refactor(advanced_table): Extraer result.zig y state_helpers.zig de state.zig
- Extraer AdvancedTableResult a result.zig (73 LOC) - Extraer funciones de map shifting a state_helpers.zig (141 LOC con tests) - Reducir state.zig de 1235 LOC a 1123 LOC - Añadir swapMapEntries para operaciones de move row - Mantener re-exports para compatibilidad
This commit is contained in:
parent
042ff96141
commit
7d4d4190b8
3 changed files with 234 additions and 132 deletions
73
src/widgets/advanced_table/result.zig
Normal file
73
src/widgets/advanced_table/result.zig
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
//! AdvancedTable Result - Return type from advancedTable() call
|
||||
//!
|
||||
//! Struct independiente que contiene el resultado de una llamada a advancedTable().
|
||||
//! Extraído de state.zig para mejorar modularidad.
|
||||
|
||||
const table_core = @import("../table_core/table_core.zig");
|
||||
const types = @import("types.zig");
|
||||
|
||||
pub const SortDirection = types.SortDirection;
|
||||
pub const CRUDAction = types.CRUDAction;
|
||||
|
||||
/// Result returned from advancedTable() call
|
||||
pub const AdvancedTableResult = struct {
|
||||
// Selection
|
||||
selection_changed: bool = false,
|
||||
selected_row: ?usize = null,
|
||||
selected_col: ?usize = null,
|
||||
|
||||
// Editing
|
||||
edit_started: bool = false,
|
||||
edit_ended: bool = false,
|
||||
cell_edited: bool = false,
|
||||
|
||||
// Sorting
|
||||
sort_changed: bool = false,
|
||||
sort_column: ?usize = null,
|
||||
sort_direction: SortDirection = .none,
|
||||
|
||||
// Row operations
|
||||
row_inserted: bool = false,
|
||||
row_deleted: bool = false,
|
||||
row_moved: bool = false,
|
||||
|
||||
// Auto-CRUD
|
||||
crud_action: ?CRUDAction = null,
|
||||
crud_success: bool = true,
|
||||
|
||||
// Lookup (Phase 7)
|
||||
lookup_success: ?bool = null, // null = no lookup, true = found, false = not found
|
||||
|
||||
// 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];
|
||||
}
|
||||
};
|
||||
|
|
@ -6,6 +6,8 @@ const std = @import("std");
|
|||
const types = @import("types.zig");
|
||||
const schema_mod = @import("schema.zig");
|
||||
const table_core = @import("../table_core/table_core.zig");
|
||||
const state_helpers = @import("state_helpers.zig");
|
||||
const result_mod = @import("result.zig");
|
||||
|
||||
pub const CellValue = types.CellValue;
|
||||
pub const RowState = types.RowState;
|
||||
|
|
@ -15,6 +17,9 @@ pub const Row = types.Row;
|
|||
pub const TableSchema = schema_mod.TableSchema;
|
||||
pub const MAX_EDIT_BUFFER = types.MAX_EDIT_BUFFER;
|
||||
|
||||
// Re-export AdvancedTableResult desde result.zig
|
||||
pub const AdvancedTableResult = result_mod.AdvancedTableResult;
|
||||
|
||||
// =============================================================================
|
||||
// AdvancedTable State
|
||||
// =============================================================================
|
||||
|
|
@ -906,148 +911,31 @@ pub const AdvancedTableState = struct {
|
|||
}
|
||||
|
||||
// =========================================================================
|
||||
// Internal Helpers
|
||||
// Internal Helpers (delegados a state_helpers.zig)
|
||||
// =========================================================================
|
||||
|
||||
const MAX_STATE_ENTRIES = 64; // Maximum entries we expect in state maps
|
||||
|
||||
/// Shift row indices down (after insert)
|
||||
fn shiftRowIndicesDown(self: *AdvancedTableState, insert_index: usize) void {
|
||||
shiftMapIndicesDown(&self.dirty_rows, insert_index);
|
||||
shiftMapIndicesDown(&self.new_rows, insert_index);
|
||||
shiftMapIndicesDown(&self.deleted_rows, insert_index);
|
||||
shiftMapIndicesDown(&self.validation_errors, insert_index);
|
||||
state_helpers.shiftMapIndicesDown(&self.dirty_rows, insert_index);
|
||||
state_helpers.shiftMapIndicesDown(&self.new_rows, insert_index);
|
||||
state_helpers.shiftMapIndicesDown(&self.deleted_rows, insert_index);
|
||||
state_helpers.shiftMapIndicesDown(&self.validation_errors, insert_index);
|
||||
}
|
||||
|
||||
/// Shift row indices up (after delete)
|
||||
fn shiftRowIndicesUp(self: *AdvancedTableState, delete_index: usize) void {
|
||||
shiftMapIndicesUp(&self.dirty_rows, delete_index);
|
||||
shiftMapIndicesUp(&self.new_rows, delete_index);
|
||||
shiftMapIndicesUp(&self.deleted_rows, delete_index);
|
||||
shiftMapIndicesUp(&self.validation_errors, delete_index);
|
||||
}
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
// Map Shifting Helpers (standalone functions to avoid allocator issues)
|
||||
// =============================================================================
|
||||
|
||||
const Entry = struct { key: usize, value: bool };
|
||||
|
||||
fn shiftMapIndicesDown(map: *std.AutoHashMap(usize, bool), insert_index: usize) void {
|
||||
// Use bounded array to avoid allocation
|
||||
var entries: [AdvancedTableState.MAX_STATE_ENTRIES]Entry = undefined;
|
||||
var count: usize = 0;
|
||||
|
||||
// Collect entries that need shifting
|
||||
var iter = map.iterator();
|
||||
while (iter.next()) |entry| {
|
||||
if (count >= AdvancedTableState.MAX_STATE_ENTRIES) break;
|
||||
if (entry.key_ptr.* >= insert_index) {
|
||||
entries[count] = .{ .key = entry.key_ptr.* + 1, .value = entry.value_ptr.* };
|
||||
} else {
|
||||
entries[count] = .{ .key = entry.key_ptr.*, .value = entry.value_ptr.* };
|
||||
}
|
||||
count += 1;
|
||||
state_helpers.shiftMapIndicesUp(&self.dirty_rows, delete_index);
|
||||
state_helpers.shiftMapIndicesUp(&self.new_rows, delete_index);
|
||||
state_helpers.shiftMapIndicesUp(&self.deleted_rows, delete_index);
|
||||
state_helpers.shiftMapIndicesUp(&self.validation_errors, delete_index);
|
||||
}
|
||||
|
||||
// Rebuild map
|
||||
map.clearRetainingCapacity();
|
||||
for (entries[0..count]) |e| {
|
||||
map.put(e.key, e.value) catch {};
|
||||
}
|
||||
}
|
||||
|
||||
fn shiftMapIndicesUp(map: *std.AutoHashMap(usize, bool), delete_index: usize) void {
|
||||
// Use bounded array to avoid allocation
|
||||
var entries: [AdvancedTableState.MAX_STATE_ENTRIES]Entry = undefined;
|
||||
var count: usize = 0;
|
||||
|
||||
// Collect entries, skipping deleted and shifting down
|
||||
var iter = map.iterator();
|
||||
while (iter.next()) |entry| {
|
||||
if (count >= AdvancedTableState.MAX_STATE_ENTRIES) break;
|
||||
if (entry.key_ptr.* == delete_index) {
|
||||
continue; // Skip deleted index
|
||||
} else if (entry.key_ptr.* > delete_index) {
|
||||
entries[count] = .{ .key = entry.key_ptr.* - 1, .value = entry.value_ptr.* };
|
||||
} else {
|
||||
entries[count] = .{ .key = entry.key_ptr.*, .value = entry.value_ptr.* };
|
||||
}
|
||||
count += 1;
|
||||
}
|
||||
|
||||
// Rebuild map
|
||||
map.clearRetainingCapacity();
|
||||
for (entries[0..count]) |e| {
|
||||
map.put(e.key, e.value) catch {};
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Result Type
|
||||
// =============================================================================
|
||||
|
||||
/// Result returned from advancedTable() call
|
||||
pub const AdvancedTableResult = struct {
|
||||
// Selection
|
||||
selection_changed: bool = false,
|
||||
selected_row: ?usize = null,
|
||||
selected_col: ?usize = null,
|
||||
|
||||
// Editing
|
||||
edit_started: bool = false,
|
||||
edit_ended: bool = false,
|
||||
cell_edited: bool = false,
|
||||
|
||||
// Sorting
|
||||
sort_changed: bool = false,
|
||||
sort_column: ?usize = null,
|
||||
sort_direction: SortDirection = .none,
|
||||
|
||||
// Row operations
|
||||
row_inserted: bool = false,
|
||||
row_deleted: bool = false,
|
||||
row_moved: bool = false,
|
||||
|
||||
// Auto-CRUD
|
||||
crud_action: ?CRUDAction = null,
|
||||
crud_success: bool = true,
|
||||
|
||||
// Lookup (Phase 7)
|
||||
lookup_success: ?bool = null, // null = no lookup, true = found, false = not found
|
||||
|
||||
// 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];
|
||||
/// Swap state map entries between two row indices
|
||||
fn swapRowStates(self: *AdvancedTableState, idx_a: usize, idx_b: usize) void {
|
||||
state_helpers.swapMapEntries(&self.dirty_rows, idx_a, idx_b);
|
||||
state_helpers.swapMapEntries(&self.new_rows, idx_a, idx_b);
|
||||
state_helpers.swapMapEntries(&self.deleted_rows, idx_a, idx_b);
|
||||
state_helpers.swapMapEntries(&self.validation_errors, idx_a, idx_b);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
141
src/widgets/advanced_table/state_helpers.zig
Normal file
141
src/widgets/advanced_table/state_helpers.zig
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
//! AdvancedTable State Helpers - Map shifting utilities
|
||||
//!
|
||||
//! Funciones auxiliares para manipular los state maps (dirty_rows, new_rows, etc.)
|
||||
//! cuando se insertan o eliminan filas.
|
||||
//!
|
||||
//! Extraído de state.zig para mejorar modularidad.
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
/// Maximum entries we expect in state maps
|
||||
pub const MAX_STATE_ENTRIES = 64;
|
||||
|
||||
/// Entry for temporary storage during map rebuild
|
||||
const Entry = struct { key: usize, value: bool };
|
||||
|
||||
/// Shift row indices down (after insert)
|
||||
/// All indices >= insert_index are incremented by 1
|
||||
pub fn shiftMapIndicesDown(map: *std.AutoHashMap(usize, bool), insert_index: usize) void {
|
||||
// Use bounded array to avoid allocation
|
||||
var entries: [MAX_STATE_ENTRIES]Entry = undefined;
|
||||
var count: usize = 0;
|
||||
|
||||
// Collect entries that need shifting
|
||||
var iter = map.iterator();
|
||||
while (iter.next()) |entry| {
|
||||
if (count >= MAX_STATE_ENTRIES) break;
|
||||
if (entry.key_ptr.* >= insert_index) {
|
||||
entries[count] = .{ .key = entry.key_ptr.* + 1, .value = entry.value_ptr.* };
|
||||
} else {
|
||||
entries[count] = .{ .key = entry.key_ptr.*, .value = entry.value_ptr.* };
|
||||
}
|
||||
count += 1;
|
||||
}
|
||||
|
||||
// Rebuild map
|
||||
map.clearRetainingCapacity();
|
||||
for (entries[0..count]) |e| {
|
||||
map.put(e.key, e.value) catch {};
|
||||
}
|
||||
}
|
||||
|
||||
/// Shift row indices up (after delete)
|
||||
/// All indices > delete_index are decremented by 1
|
||||
/// The entry at delete_index is removed
|
||||
pub fn shiftMapIndicesUp(map: *std.AutoHashMap(usize, bool), delete_index: usize) void {
|
||||
// Use bounded array to avoid allocation
|
||||
var entries: [MAX_STATE_ENTRIES]Entry = undefined;
|
||||
var count: usize = 0;
|
||||
|
||||
// Collect entries, skipping deleted and shifting down
|
||||
var iter = map.iterator();
|
||||
while (iter.next()) |entry| {
|
||||
if (count >= MAX_STATE_ENTRIES) break;
|
||||
if (entry.key_ptr.* == delete_index) {
|
||||
continue; // Skip deleted index
|
||||
} else if (entry.key_ptr.* > delete_index) {
|
||||
entries[count] = .{ .key = entry.key_ptr.* - 1, .value = entry.value_ptr.* };
|
||||
} else {
|
||||
entries[count] = .{ .key = entry.key_ptr.*, .value = entry.value_ptr.* };
|
||||
}
|
||||
count += 1;
|
||||
}
|
||||
|
||||
// Rebuild map
|
||||
map.clearRetainingCapacity();
|
||||
for (entries[0..count]) |e| {
|
||||
map.put(e.key, e.value) catch {};
|
||||
}
|
||||
}
|
||||
|
||||
/// Swap entries between two indices in a map
|
||||
/// Used when swapping rows (move up/down)
|
||||
pub fn swapMapEntries(map: *std.AutoHashMap(usize, bool), idx_a: usize, idx_b: usize) void {
|
||||
const val_a = map.get(idx_a);
|
||||
const val_b = map.get(idx_b);
|
||||
|
||||
if (val_a != null and val_b != null) {
|
||||
// Both exist - no change needed (both stay)
|
||||
} else if (val_a) |v| {
|
||||
// Only a exists - move to b
|
||||
_ = map.remove(idx_a);
|
||||
map.put(idx_b, v) catch {};
|
||||
} else if (val_b) |v| {
|
||||
// Only b exists - move to a
|
||||
_ = map.remove(idx_b);
|
||||
map.put(idx_a, v) catch {};
|
||||
}
|
||||
// Neither exists - nothing to do
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Tests
|
||||
// =============================================================================
|
||||
|
||||
test "shiftMapIndicesDown" {
|
||||
var map = std.AutoHashMap(usize, bool).init(std.testing.allocator);
|
||||
defer map.deinit();
|
||||
|
||||
try map.put(0, true);
|
||||
try map.put(2, true);
|
||||
try map.put(5, true);
|
||||
|
||||
// Insert at index 2 - indices 2 and 5 should shift to 3 and 6
|
||||
shiftMapIndicesDown(&map, 2);
|
||||
|
||||
try std.testing.expect(map.get(0) != null);
|
||||
try std.testing.expect(map.get(2) == null); // Shifted to 3
|
||||
try std.testing.expect(map.get(3) != null);
|
||||
try std.testing.expect(map.get(5) == null); // Shifted to 6
|
||||
try std.testing.expect(map.get(6) != null);
|
||||
}
|
||||
|
||||
test "shiftMapIndicesUp" {
|
||||
var map = std.AutoHashMap(usize, bool).init(std.testing.allocator);
|
||||
defer map.deinit();
|
||||
|
||||
try map.put(0, true);
|
||||
try map.put(2, true);
|
||||
try map.put(5, true);
|
||||
|
||||
// Delete at index 2 - index 5 should shift to 4, index 2 is removed
|
||||
shiftMapIndicesUp(&map, 2);
|
||||
|
||||
try std.testing.expect(map.get(0) != null);
|
||||
try std.testing.expect(map.get(2) == null); // Deleted
|
||||
try std.testing.expect(map.get(4) != null); // Shifted from 5
|
||||
try std.testing.expect(map.get(5) == null);
|
||||
}
|
||||
|
||||
test "swapMapEntries" {
|
||||
var map = std.AutoHashMap(usize, bool).init(std.testing.allocator);
|
||||
defer map.deinit();
|
||||
|
||||
try map.put(1, true);
|
||||
|
||||
// Swap 1 and 3 (only 1 exists)
|
||||
swapMapEntries(&map, 1, 3);
|
||||
|
||||
try std.testing.expect(map.get(1) == null);
|
||||
try std.testing.expect(map.get(3) != null);
|
||||
}
|
||||
Loading…
Reference in a new issue