# Sistema de Focus - Resolucion Final (2025-12-11) > **ESTADO**: RESUELTO > **Fecha resolucion**: 2025-12-11 ~19:00 > **Probado en**: zsimifactu (tabla lista clientes + panel detalle) --- ## RESUMEN EJECUTIVO El sistema de focus de zcatgui tenia dos bugs que impedian la navegacion por teclado al iniciar una aplicacion: 1. **Bug 1**: `selected_col` se inicializaba a -1, causando que `selectedCell()` retornara null 2. **Bug 2**: Sin celda seleccionada valida, los cambios de seleccion no se propagaban **Solucion**: Inicializar `selected_row` y `selected_col` a 0 cuando la tabla tiene datos. --- ## ANALISIS DEL PROBLEMA ### Sintoma - Al iniciar el programa, la tabla mostraba focus visual (borde azul) - Las flechas del teclado NO movian la seleccion - Al hacer clic con raton, todo funcionaba correctamente ### Investigacion (debug prints) ``` TABLE handleKeyboard: nav=down, selected_row=0 DOWN: MOVED to row 1 AFTER tableRect[frame=7]: selection_changed=true, selected_row=1, selected_col=-1 SELECTION_CHANGED[frame=7]: selectedCell() returned null! <-- AQUI EL BUG SYNC[frame=8]: DM idx=0, table row=1 -> forcing to 0 <-- DM no se actualizo ``` ### Causa raiz 1. `TableState` inicializa `selected_row = -1` y `selected_col = -1` 2. El teclado solo modifica `selected_row`, no `selected_col` 3. `selectedCell()` retorna `null` si cualquiera es < 0 4. Sin celda valida, el DataManager no se notifica del cambio 5. En el siguiente frame, el SYNC del DataManager restaura `selected_row` a 0 ### Por que funcionaba con clic El clic llama a `selectCell(row, col)` que inicializa AMBOS valores correctamente. --- ## SOLUCION IMPLEMENTADA ### Archivo: `zcatgui/src/widgets/table.zig` En `tableRectFull()`, despues de verificar que hay datos: ```zig // Ensure valid selection if table has data // Without this, selected_row/col stay at -1 until user clicks, // which breaks keyboard navigation and selectedCell() returns null if (state.row_count > 0 and columns.len > 0) { if (state.selected_row < 0) state.selected_row = 0; if (state.selected_col < 0) state.selected_col = 0; } ``` ### Por que esta solucion es correcta - Es logica: si una tabla tiene datos, debe tener una celda seleccionada por defecto - Se hace en la libreria, no en la aplicacion (principio de resolver en origen) - No rompe compatibilidad: las apps que ya usaban clic siguen funcionando - Beneficia a todas las apps que usen zcatgui --- ## OTROS CAMBIOS REALIZADOS (durante investigacion) ### 1. Focus implicito (conservar) **Archivo**: `zcatgui/src/core/focus.zig` `hasFocus()` y `getFocused()` ahora retornan el primer widget si `focused_index == null`: - Permite que widgets respondan al teclado desde el primer frame - `endFrame()` convierte el focus implicito en explicito ### 2. Separacion registration_group / active_group (conservar) **Archivos**: `focus.zig`, `context.zig` - `registration_group`: donde se registran widgets durante draw - `active_group`: grupo con focus de teclado (solo cambia con F6/clic) ### 3. Test de input corregido (conservar) **Archivo**: `zcatgui/src/core/input.zig` El test `navKeyPressed` usaba `setKeyState()` pero debia usar `handleKeyEvent()`. --- ## ESTADO DE PRUEBAS | Funcionalidad | Estado | Notas | |---------------|--------|-------| | Focus visual exclusivo | OK | Solo un panel con borde azul | | F6 cambia entre paneles | OK | | | Clic cambia focus | OK | | | Flechas mueven seleccion tabla | OK | Ahora funciona desde inicio | | Tab entre TextInputs | OK | | | Seleccion se propaga a DataManager | OK | Panel detalle se actualiza | | CPU idle ~0% | OK | SDL_WaitEventTimeout | --- ## ARQUITECTURA FINAL DEL SISTEMA DE FOCUS ``` FocusSystem | +---------------+---------------+ | | FocusGroup 1 FocusGroup 2 (Panel Lista) (Panel Detalle) | | Table widget TextInput widgets | | - registerFocusable() - registerFocusable() - hasFocus() -> true/false - hasFocus() -> true/false ``` ### Flujo por frame: 1. `beginFrame()` - limpia registros de widgets 2. `setRegistrationGroup(1)` - panel 1 registra sus widgets 3. `setRegistrationGroup(2)` - panel 2 registra sus widgets 4. Widgets preguntan `hasFocus()` durante draw 5. `endFrame()` - valida focus, procesa Tab pendiente ### Invariantes: - Solo UN grupo tiene `active_group` a la vez - El primer widget del grupo activo tiene focus implicito si `focused_index == null` - `selectedCell()` requiere `selected_row >= 0` Y `selected_col >= 0` --- ## LECCION APRENDIDA **Siempre inicializar estado completo, no parcial.** El bug surgio porque `selected_row` se manejaba correctamente pero `selected_col` se ignoraba. Cualquier funcion que dependa de multiples campos (como `selectedCell()`) fallara si alguno no esta inicializado. --- *Documento cerrado: 2025-12-11 ~19:00* *Autores: Arno + Claude (Opus 4.5)*