diff --git a/src/widgets/advanced_table/advanced_table.zig b/src/widgets/advanced_table/advanced_table.zig index 9ec26aa..bac7da3 100644 --- a/src/widgets/advanced_table/advanced_table.zig +++ b/src/widgets/advanced_table/advanced_table.zig @@ -569,8 +569,11 @@ fn drawEditingOverlay( } // ============================================================================= -// Keyboard Handling +// Keyboard Handling (Brain-in-Core pattern) // ============================================================================= +// +// Arquitectura: TODA la lógica de decisión está en table_core.processTableEvents() +// Este handler solo aplica flags y maneja lógica específica de AdvancedTable. fn handleKeyboard( ctx: *Context, @@ -585,120 +588,109 @@ fn handleKeyboard( const config = table_schema.config; const col_count = table_schema.columns.len; - // Use navKeyPressed for navigation (includes key repeats) - if (ctx.input.navKeyPressed()) |nav_key| { - switch (nav_key) { - .up => { - if (table_state.selected_row > 0) { - const new_row: usize = @intCast(table_state.selected_row - 1); - const new_col: usize = @intCast(@max(0, table_state.selected_col)); - table_state.selectCell(new_row, new_col); - result.selection_changed = true; - result.selected_row = new_row; - result.selected_col = new_col; - } - }, - .down => { - if (table_state.selected_row < @as(i32, @intCast(row_count)) - 1) { - const new_row: usize = @intCast(table_state.selected_row + 1); - const new_col: usize = @intCast(@max(0, table_state.selected_col)); - table_state.selectCell(new_row, new_col); - result.selection_changed = true; - result.selected_row = new_row; - result.selected_col = new_col; - } - }, - .left => { - if (table_state.selected_col > 0) { - const new_row: usize = @intCast(@max(0, table_state.selected_row)); - const new_col: usize = @intCast(table_state.selected_col - 1); - table_state.selectCell(new_row, new_col); - result.selection_changed = true; - result.selected_row = new_row; - result.selected_col = new_col; - } - }, - .right => { - if (table_state.selected_col < @as(i32, @intCast(col_count)) - 1) { - const new_row: usize = @intCast(@max(0, table_state.selected_row)); - const new_col: usize = @intCast(table_state.selected_col + 1); - table_state.selectCell(new_row, new_col); - result.selection_changed = true; - result.selected_row = new_row; - result.selected_col = new_col; - } - }, - .page_up => { - const new_row: usize = @intCast(@max(0, table_state.selected_row - @as(i32, @intCast(visible_rows)))); - const new_col: usize = @intCast(@max(0, table_state.selected_col)); - table_state.selectCell(new_row, new_col); - result.selection_changed = true; - result.selected_row = new_row; - result.selected_col = new_col; - }, - .page_down => { - const new_row: usize = @intCast(@min( - @as(i32, @intCast(row_count)) - 1, - table_state.selected_row + @as(i32, @intCast(visible_rows)), - )); - const new_col: usize = @intCast(@max(0, table_state.selected_col)); - table_state.selectCell(new_row, new_col); - result.selection_changed = true; - result.selected_row = new_row; - result.selected_col = new_col; - }, - .home => { - var new_row: usize = undefined; - var new_col: usize = undefined; - if (ctx.input.modifiers.ctrl) { - // Ctrl+Home: first cell - new_row = 0; - new_col = 0; - } else { - // Home: first column - new_row = @intCast(@max(0, table_state.selected_row)); - new_col = 0; - } - table_state.selectCell(new_row, new_col); - result.selection_changed = true; - result.selected_row = new_row; - result.selected_col = new_col; - }, - .end => { - var new_row: usize = undefined; - var new_col: usize = undefined; - if (ctx.input.modifiers.ctrl) { - // Ctrl+End: last cell - new_row = if (row_count > 0) row_count - 1 else 0; - new_col = col_count - 1; - } else { - // End: last column - new_row = @intCast(@max(0, table_state.selected_row)); - new_col = col_count - 1; - } - table_state.selectCell(new_row, new_col); - result.selection_changed = true; - result.selected_row = new_row; - result.selected_col = new_col; - }, - .tab => { - if (config.handle_tab) { - // Tab navigation handled below - } - }, - .enter => { - // Enter to start editing handled below - }, - .escape => {}, - else => {}, - } + // ========================================================================= + // BRAIN-IN-CORE: Delegar toda la lógica de decisión al Core + // ========================================================================= + const events = table_core.processTableEvents(ctx, table_state.isEditing()); + + // ========================================================================= + // Aplicar navegación desde el Core + // ========================================================================= + if (events.move_up and table_state.selected_row > 0) { + const new_row: usize = @intCast(table_state.selected_row - 1); + const new_col: usize = @intCast(@max(0, table_state.selected_col)); + table_state.selectCell(new_row, new_col); + result.selection_changed = true; + result.selected_row = new_row; + result.selected_col = new_col; } - // Tab navigation (if handle_tab is enabled) - if (config.handle_tab and ctx.input.keyPressed(.tab)) { - const shift = ctx.input.modifiers.shift; + if (events.move_down and table_state.selected_row < @as(i32, @intCast(row_count)) - 1) { + const new_row: usize = @intCast(table_state.selected_row + 1); + const new_col: usize = @intCast(@max(0, table_state.selected_col)); + table_state.selectCell(new_row, new_col); + result.selection_changed = true; + result.selected_row = new_row; + result.selected_col = new_col; + } - if (shift) { + if (events.move_left and table_state.selected_col > 0) { + const new_row: usize = @intCast(@max(0, table_state.selected_row)); + const new_col: usize = @intCast(table_state.selected_col - 1); + table_state.selectCell(new_row, new_col); + result.selection_changed = true; + result.selected_row = new_row; + result.selected_col = new_col; + } + + if (events.move_right and table_state.selected_col < @as(i32, @intCast(col_count)) - 1) { + const new_row: usize = @intCast(@max(0, table_state.selected_row)); + const new_col: usize = @intCast(table_state.selected_col + 1); + table_state.selectCell(new_row, new_col); + result.selection_changed = true; + result.selected_row = new_row; + result.selected_col = new_col; + } + + if (events.page_up) { + const new_row: usize = @intCast(@max(0, table_state.selected_row - @as(i32, @intCast(visible_rows)))); + const new_col: usize = @intCast(@max(0, table_state.selected_col)); + table_state.selectCell(new_row, new_col); + result.selection_changed = true; + result.selected_row = new_row; + result.selected_col = new_col; + } + + if (events.page_down) { + const new_row: usize = @intCast(@min( + @as(i32, @intCast(row_count)) - 1, + table_state.selected_row + @as(i32, @intCast(visible_rows)), + )); + const new_col: usize = @intCast(@max(0, table_state.selected_col)); + table_state.selectCell(new_row, new_col); + result.selection_changed = true; + result.selected_row = new_row; + result.selected_col = new_col; + } + + if (events.go_to_first_col) { + const new_row: usize = if (events.go_to_first_row) 0 else @intCast(@max(0, table_state.selected_row)); + table_state.selectCell(new_row, 0); + result.selection_changed = true; + result.selected_row = new_row; + result.selected_col = 0; + } else if (events.go_to_first_row) { + const new_col: usize = @intCast(@max(0, table_state.selected_col)); + table_state.selectCell(0, new_col); + result.selection_changed = true; + result.selected_row = 0; + result.selected_col = new_col; + } + + if (events.go_to_last_col) { + const new_row: usize = if (events.go_to_last_row) + (if (row_count > 0) row_count - 1 else 0) + else + @intCast(@max(0, table_state.selected_row)); + const new_col: usize = col_count - 1; + table_state.selectCell(new_row, new_col); + result.selection_changed = true; + result.selected_row = new_row; + result.selected_col = new_col; + } else if (events.go_to_last_row) { + const new_row: usize = if (row_count > 0) row_count - 1 else 0; + const new_col: usize = @intCast(@max(0, table_state.selected_col)); + table_state.selectCell(new_row, new_col); + result.selection_changed = true; + result.selected_row = new_row; + result.selected_col = new_col; + } + + // ========================================================================= + // Tab navigation (usando lógica del Core + config local) + // ========================================================================= + 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( @@ -707,7 +699,6 @@ fn handleKeyboard( ); result.selection_changed = true; } else if (table_state.selected_row > 0 and config.wrap_navigation) { - // Wrap to end of previous row table_state.selectCell( @intCast(table_state.selected_row - 1), col_count - 1, @@ -723,7 +714,6 @@ fn handleKeyboard( ); result.selection_changed = true; } else if (table_state.selected_row < @as(i32, @intCast(row_count)) - 1 and config.wrap_navigation) { - // Wrap to beginning of next row table_state.selectCell( @intCast(table_state.selected_row + 1), 0, @@ -733,38 +723,38 @@ fn handleKeyboard( } } - // Start editing with F2, Enter, or Space (empty) - if (config.allow_edit) { - const start_edit_key = ctx.input.keyPressed(.f2) or ctx.input.keyPressed(.enter); - const start_empty = ctx.input.keyPressed(.space); - - if (start_edit_key or start_empty) { - if (table_state.selected_row >= 0 and table_state.selected_col >= 0) { - const col_idx: usize = @intCast(table_state.selected_col); - if (col_idx < table_schema.columns.len and table_schema.columns[col_idx].editable) { - if (table_state.getRow(@intCast(table_state.selected_row))) |row| { - const value = row.get(table_schema.columns[col_idx].name); - if (start_empty) { - // Espacio: empezar vacío - table_state.startEditing(""); - } else { - // F2/Enter: empezar con valor actual - var format_buf: [128]u8 = undefined; - const text = value.format(&format_buf); - table_state.startEditing(text); - } - table_state.original_value = value; - result.edit_started = true; + // ========================================================================= + // Inicio de edición (F2, Space, o tecla alfanumérica desde el Core) + // ========================================================================= + if (events.start_editing and config.allow_edit) { + if (table_state.selected_row >= 0 and table_state.selected_col >= 0) { + const col_idx: usize = @intCast(table_state.selected_col); + if (col_idx < table_schema.columns.len and table_schema.columns[col_idx].editable) { + if (table_state.getRow(@intCast(table_state.selected_row))) |row| { + const value = row.get(table_schema.columns[col_idx].name); + if (events.initial_char) |ch| { + // Tecla alfanumérica: empezar con ese caracter + var char_buf: [1]u8 = .{ch}; + table_state.startEditing(&char_buf); + } else { + // F2/Space: empezar con valor actual + var format_buf: [128]u8 = undefined; + const text = value.format(&format_buf); + table_state.startEditing(text); } + table_state.original_value = value; + result.edit_started = true; } } } } - // Row operations (if allowed) + // ========================================================================= + // Operaciones CRUD (Ctrl+N, Ctrl+Delete, Ctrl+B desde el Core) + // ========================================================================= if (config.allow_row_operations) { - // Ctrl+N: Insert row at current position - if (ctx.input.keyPressed(.n) and ctx.input.modifiers.ctrl) { + // Ctrl+N: Insert row + if (events.insert_row) { const insert_idx: usize = if (table_state.selected_row >= 0) @intCast(table_state.selected_row) else @@ -776,8 +766,8 @@ fn handleKeyboard( } else |_| {} } - // Ctrl+Delete: Delete current row - if (ctx.input.keyPressed(.delete) and ctx.input.modifiers.ctrl) { + // Ctrl+Delete o Ctrl+B: Delete row + if (events.delete_row) { if (table_state.selected_row >= 0) { const delete_idx: usize = @intCast(table_state.selected_row); table_state.deleteRow(delete_idx); @@ -795,20 +785,23 @@ fn handleKeyboard( } } - // Ctrl+A: Select all rows (if multi-select enabled) + // ========================================================================= + // Ctrl+A: Select all (lógica específica de AdvancedTable) + // ========================================================================= if (config.allow_multi_select and ctx.input.keyPressed(.a) and ctx.input.modifiers.ctrl) { table_state.selectAllRows(); result.selection_changed = true; } - // Type-to-edit or incremental search - // Behavior: If current cell is editable → start editing with typed char - // If not editable → incremental search in first column - if (!ctx.input.modifiers.ctrl and !ctx.input.modifiers.alt) { + // ========================================================================= + // Búsqueda incremental (solo si NO se inició edición) + // Solo para celdas no editables - lógica específica de AdvancedTable + // ========================================================================= + if (!events.start_editing and !ctx.input.modifiers.ctrl and !ctx.input.modifiers.alt) { if (ctx.input.text_input_len > 0) { const text = ctx.input.text_input[0..ctx.input.text_input_len]; - // Check if current cell is editable + // Solo búsqueda si la celda actual NO es editable const current_cell_editable = blk: { if (!config.allow_edit) break :blk false; if (table_state.selected_row < 0 or table_state.selected_col < 0) break :blk false; @@ -817,19 +810,7 @@ fn handleKeyboard( break :blk table_schema.columns[col_idx].editable; }; - if (current_cell_editable) { - // Start editing with first typed character - if (text.len > 0 and text[0] >= 32 and text[0] < 127) { - if (table_state.getRow(@intCast(table_state.selected_row))) |row| { - const col_idx: usize = @intCast(table_state.selected_col); - const value = row.get(table_schema.columns[col_idx].name); - // Start with the typed text (replaces content) - table_state.startEditing(text); - table_state.original_value = value; - result.edit_started = true; - } - } - } else { + if (!current_cell_editable) { // Incremental search (type-to-search) in first column for (text) |char| { if (char >= 32 and char < 127) {