fix(advanced_table): Teclado funciona - result.selected_row/col
Bug: Las flechas no movían la selección en AdvancedTable. Causa raíz: - handleKeyboard seteaba result.selection_changed = true - PERO NO seteaba result.selected_row / result.selected_col - zsimifactu sincroniza selección desde DataManager cada frame - Sin esos valores, DataManager no se actualizaba - Siguiente frame: selección se reseteaba al valor anterior Solución: - Añadir result.selected_row y result.selected_col a todas las teclas de navegación (up, down, left, right, page_up, page_down, home, end) Cambios visuales: - Celda seleccionada: borde + tinte sutil (15%) en lugar de fondo sólido azul 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
654183c0eb
commit
bd95013ffc
4 changed files with 345 additions and 139 deletions
28
CLAUDE.md
28
CLAUDE.md
|
|
@ -21,7 +21,7 @@
|
|||
### Paso 3: Leer documentación
|
||||
```
|
||||
REFERENCE.md # ⭐ MANUAL DE REFERENCIA COMPLETO
|
||||
docs/BUG_ADVANCEDTABLE_KEYBOARD_2025-12-17.md # 🔴 BUG PENDIENTE - LEER PRIMERO
|
||||
docs/BUG_ADVANCEDTABLE_KEYBOARD_2025-12-17.md # ✅ BUG RESUELTO
|
||||
docs/ADVANCED_TABLE_MERGE_PLAN.md # Plan merge Table → AdvancedTable
|
||||
docs/research/DVUI_AUDIT_2025-12-17.md # Auditoría DVUI
|
||||
docs/DEVELOPMENT_PLAN.md # Plan de desarrollo por fases
|
||||
|
|
@ -813,35 +813,27 @@ const stdout = std.fs.File.stdout(); // NO std.io.getStdOut()
|
|||
| 2025-12-17 | v0.18.0 | Paridad Visual DVUI Fase 1: RenderMode dual, esquinas redondeadas, sombras |
|
||||
| 2025-12-17 | v0.19.0 | Paridad Visual DVUI Fase 2: HoverTransition, Focus Ring AA en 9 widgets |
|
||||
| 2025-12-17 | v0.20.0 | AdvancedTable: 8 fases completas (~2,700 LOC) - Schema, CRUD, Sorting, Lookup |
|
||||
| 2025-12-17 | v0.21.0 | AdvancedTable: +990 LOC (multi-select, search, validation) - 🔴 BUG TECLADO |
|
||||
| 2025-12-17 | v0.21.0 | AdvancedTable: +990 LOC (multi-select, search, validation) |
|
||||
| 2025-12-17 | v0.21.1 | Fix: AdvancedTable teclado - result.selected_row/col en handleKeyboard |
|
||||
|
||||
---
|
||||
|
||||
## 🔴 BUG PENDIENTE: AdvancedTable Teclado
|
||||
## ✅ BUG RESUELTO: AdvancedTable Teclado
|
||||
|
||||
> **Estado:** NO RESUELTO (2025-12-17)
|
||||
> **Estado:** RESUELTO (2025-12-17 19:30)
|
||||
> **Documentación:** `docs/BUG_ADVANCEDTABLE_KEYBOARD_2025-12-17.md`
|
||||
|
||||
### Síntoma
|
||||
Las flechas ↑↓←→ no mueven la selección en AdvancedTable (zsimifactu WHO panel).
|
||||
### Causa raíz
|
||||
`handleKeyboard` seteaba `result.selection_changed = true` pero NO seteaba `result.selected_row` / `result.selected_col`. zsimifactu sincroniza selección desde DataManager cada frame, y sin esos valores no actualizaba DataManager → reset al valor anterior.
|
||||
|
||||
### Lo que sabemos
|
||||
- Click en filas funciona
|
||||
- `has_focus=true` cuando widget tiene foco
|
||||
- `navKeyPressed()` detecta teclas (probado con debug)
|
||||
- Pero `handleKeyboard()` no las procesa
|
||||
|
||||
### Pista clave
|
||||
El widget **Table original** SÍ funciona con teclado. Comparar implementaciones.
|
||||
|
||||
### Próximo paso
|
||||
Añadir debug DENTRO de `handleKeyboard()` (no antes de `if (has_focus)`).
|
||||
### Solución
|
||||
Añadir `result.selected_row` y `result.selected_col` a todas las teclas de navegación (up, down, left, right, page_up, page_down, home, end).
|
||||
|
||||
---
|
||||
|
||||
## ESTADO ACTUAL
|
||||
|
||||
**✅ PROYECTO COMPLETADO - v0.21.0** (con bug pendiente en AdvancedTable)
|
||||
**✅ PROYECTO COMPLETADO - v0.21.1**
|
||||
|
||||
> **Para detalles técnicos completos, ver `REFERENCE.md`** (1370 líneas de documentación)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Bug: AdvancedTable Keyboard Navigation No Funciona
|
||||
|
||||
> **Fecha:** 2025-12-17
|
||||
> **Estado:** 🔴 NO RESUELTO
|
||||
> **Estado:** ✅ RESUELTO
|
||||
> **Severidad:** ALTA
|
||||
|
||||
---
|
||||
|
|
@ -12,123 +12,105 @@ Las flechas de teclado no mueven la selección en AdvancedTable cuando se usa en
|
|||
|
||||
---
|
||||
|
||||
## Lo que sabemos
|
||||
## Causa Raíz (ENCONTRADA)
|
||||
|
||||
### Funciona
|
||||
- El widget se renderiza correctamente
|
||||
- El click en filas funciona (cambia selección)
|
||||
- El sorting visual funciona (click en headers)
|
||||
- `has_focus=true` cuando se hace click en la tabla
|
||||
- `keyboard_nav=true` en la configuración
|
||||
El problema era una combinación de dos factores:
|
||||
|
||||
### No funciona
|
||||
- Flechas ↑↓←→ no mueven la selección
|
||||
- No hay mensajes de `selection_changed` después de presionar flechas
|
||||
### 1. `handleKeyboard` no seteaba `result.selected_row` / `result.selected_col`
|
||||
|
||||
---
|
||||
|
||||
## Investigación realizada
|
||||
|
||||
### 1. Cambio de keyPressed a navKeyPressed
|
||||
**Hipótesis:** `keyPressed()` no incluye key repeats.
|
||||
**Cambio:** Línea 566 de `advanced_table.zig` usa `navKeyPressed()`.
|
||||
**Resultado:** No resuelve el problema.
|
||||
|
||||
### 2. Debug print consumía el evento
|
||||
**Descubrimiento:** Al añadir debug print que llamaba `navKeyPressed()` ANTES del bloque `if (has_focus)`, el evento se consumía y no llegaba a `handleKeyboard`.
|
||||
**Evidencia:** Con debug print, aparecían mensajes `navKeyPressed=.down, has_focus=true, keyboard_nav=true` pero sin movimiento.
|
||||
**Conclusión:** `navKeyPressed()` solo puede leerse UNA VEZ por frame.
|
||||
|
||||
### 3. Sin debug print, nada funciona
|
||||
**Observación:** Al quitar el debug print, ni siquiera hay mensajes en consola.
|
||||
**Implicación:** `handleKeyboard` no se está ejecutando, o `navKeyPressed()` devuelve null.
|
||||
|
||||
---
|
||||
|
||||
## Código actual relevante
|
||||
|
||||
### advanced_table.zig líneas 165-180
|
||||
En el código original:
|
||||
```zig
|
||||
// Handle keyboard
|
||||
if (has_focus) {
|
||||
if (table_state.editing) {
|
||||
handleEditingKeyboard(ctx, table_state, table_schema, &result);
|
||||
drawEditingOverlay(ctx, bounds, table_state, table_schema, header_h, state_col_w, colors);
|
||||
} else if (config.keyboard_nav) {
|
||||
handleKeyboard(ctx, table_state, table_schema, visible_rows, &result);
|
||||
.down => {
|
||||
if (table_state.selected_row < row_count - 1) {
|
||||
table_state.selectCell(...);
|
||||
result.selection_changed = true;
|
||||
// FALTABA: result.selected_row = new_row;
|
||||
// FALTABA: result.selected_col = new_col;
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
### 2. zsimifactu sincroniza la selección desde DataManager cada frame
|
||||
|
||||
En `who_list_advanced.zig` líneas 199-205:
|
||||
```zig
|
||||
// Sincronizar selección con DataManager
|
||||
if (dm.getSelectedWhoIndex()) |idx| {
|
||||
const idx_i32: i32 = @intCast(idx);
|
||||
if (self.table_state.selected_row != idx_i32) {
|
||||
self.table_state.selected_row = idx_i32; // ← RESETEA cada frame
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### handleKeyboard líneas 570-590
|
||||
### Flujo del bug:
|
||||
|
||||
1. Usuario presiona flecha abajo
|
||||
2. `handleKeyboard` detecta la tecla y ejecuta `selectCell(new_row, new_col)`
|
||||
3. `selected_row` cambia de 1 a 2 ✓
|
||||
4. `result.selection_changed = true` ✓
|
||||
5. **PERO** `result.selected_row` es `null` ✗
|
||||
6. zsimifactu verifica `if (result.selected_row) |row_idx|` → es null → no actualiza DataManager
|
||||
7. Siguiente frame: zsimifactu sincroniza desde DataManager → `selected_row` vuelve a 1
|
||||
|
||||
---
|
||||
|
||||
## Solución
|
||||
|
||||
Añadir `result.selected_row` y `result.selected_col` a todas las teclas de navegación en `handleKeyboard`:
|
||||
|
||||
```zig
|
||||
if (ctx.input.navKeyPressed()) |nav_key| {
|
||||
switch (nav_key) {
|
||||
.up => {
|
||||
if (table_state.selected_row > 0) {
|
||||
table_state.selectCell(...);
|
||||
result.selection_changed = true;
|
||||
}
|
||||
},
|
||||
.down => {
|
||||
if (table_state.selected_row < row_count - 1) {
|
||||
table_state.selectCell(...);
|
||||
result.selection_changed = true;
|
||||
}
|
||||
},
|
||||
// ... etc
|
||||
.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; // ← AÑADIDO
|
||||
result.selected_col = new_col; // ← AÑADIDO
|
||||
}
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Hipótesis pendientes de verificar
|
||||
|
||||
1. **¿`has_focus` es realmente true?**
|
||||
- El debug mostró true, pero quizás el focus se pierde entre frames
|
||||
|
||||
2. **¿`navKeyPressed()` funciona en AdvancedTable?**
|
||||
- Funciona en otros widgets (Table original, TextInput)
|
||||
- Quizás hay algo específico del contexto de AdvancedTable
|
||||
|
||||
3. **¿El evento de teclado llega al widget?**
|
||||
- zsimifactu tiene su propio event handling en main.zig
|
||||
- Quizás los eventos se consumen antes de llegar al widget
|
||||
|
||||
4. **¿Hay diferencia entre Table y AdvancedTable?**
|
||||
- Table original funciona con teclado
|
||||
- Comparar línea por línea el handling de teclado
|
||||
Teclas corregidas:
|
||||
- `.up`
|
||||
- `.down`
|
||||
- `.left`
|
||||
- `.right`
|
||||
- `.page_up`
|
||||
- `.page_down`
|
||||
- `.home` (y Ctrl+Home)
|
||||
- `.end` (y Ctrl+End)
|
||||
|
||||
---
|
||||
|
||||
## Próximos pasos sugeridos
|
||||
## Verificación
|
||||
|
||||
1. **Comparar con Table original** - El widget Table SÍ funciona con teclado. Comparar cómo maneja eventos.
|
||||
Debug añadido temporalmente mostró:
|
||||
```
|
||||
[ADV-TABLE] DOWN: after selectCell, selected_row=2 ← SÍ cambia
|
||||
[ADV-TABLE] navKey detected: down, selected_row=1 ← Pero vuelve a 1 en siguiente frame
|
||||
```
|
||||
|
||||
2. **Añadir debug DENTRO de handleKeyboard** - No antes del `if (has_focus)`, sino dentro de la función misma.
|
||||
|
||||
3. **Verificar InputState.navKeyPressed()** - Ver si devuelve algo cuando AdvancedTable tiene focus.
|
||||
|
||||
4. **Revisar el focus system** - Quizás AdvancedTable no está registrándose correctamente en el FocusSystem.
|
||||
Después del fix, las flechas funcionan correctamente.
|
||||
|
||||
---
|
||||
|
||||
## Archivos relevantes
|
||||
## Archivos modificados
|
||||
|
||||
- `/mnt/cello2/arno/re/recode/zig/zcatgui/src/widgets/advanced_table/advanced_table.zig`
|
||||
- `/mnt/cello2/arno/re/recode/zig/zcatgui/src/widgets/table/table.zig` (referencia funcional)
|
||||
- `/mnt/cello2/arno/re/recode/zig/zcatgui/src/core/input.zig` (navKeyPressed)
|
||||
- `/mnt/cello2/arno/re/recode/zig/zsimifactu/src/panels/who_list_advanced.zig`
|
||||
- `/mnt/cello2/arno/re/recode/zig/zsimifactu/src/main.zig` (event loop)
|
||||
- `src/widgets/advanced_table/advanced_table.zig` - handleKeyboard corregido
|
||||
|
||||
---
|
||||
|
||||
## Commit actual
|
||||
## Lecciones aprendidas
|
||||
|
||||
Los cambios de "merge Table → AdvancedTable" están commiteados (af1bb76) pero el bug de teclado **NO está resuelto**.
|
||||
1. **El resultado de un widget debe ser completo** - Si `selection_changed = true`, también debe informar QUÉ cambió (`selected_row`, `selected_col`).
|
||||
|
||||
2. **Cuidado con la sincronización bidireccional** - Cuando un widget y un data manager ambos pueden modificar el mismo estado, hay que asegurar que los cambios del widget se propaguen al data manager antes de que éste resetee el estado.
|
||||
|
||||
3. **Debug incremental** - Añadir debug DENTRO de la función problemática (no antes) para ver el flujo real.
|
||||
|
||||
---
|
||||
|
||||
*Documentado por: Claude Code (Opus 4.5)*
|
||||
*Fecha: 2025-12-17 ~19:00*
|
||||
*Fecha resolución: 2025-12-17 ~19:30*
|
||||
|
|
|
|||
201
docs/PROPUESTA_WIDGETS_BROWSER.md
Normal file
201
docs/PROPUESTA_WIDGETS_BROWSER.md
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
# Propuesta: Widgets Browser Especializados
|
||||
|
||||
**Fecha:** 2025-12-17
|
||||
**Origen:** Conversación zsimifactu sobre extracción de patrones reutilizables
|
||||
**Estado:** PROPUESTA - Pendiente de implementación
|
||||
|
||||
---
|
||||
|
||||
## Resumen Ejecutivo
|
||||
|
||||
Se propone crear widgets especializados para navegación de datos tabulares que encapsulen patrones comunes identificados en zsimifactu. Estos widgets combinarían tabla + controles de navegación + filtros en componentes cohesivos.
|
||||
|
||||
---
|
||||
|
||||
## Widgets Propuestos
|
||||
|
||||
### 1. TableBrowser
|
||||
|
||||
Widget compuesto que integra:
|
||||
- Tabla con scroll y selección
|
||||
- Barra de estado con posición (ej: "15/520")
|
||||
- Botones de navegación (|< < > >|)
|
||||
- Soporte para filtros (opcional)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ [Filtro: ___________] [Tipo: v] │ ← Zona filtros (opcional)
|
||||
├─────────────────────────────────────────────┤
|
||||
│ Codigo │ Nombre │ Ciudad │ ← Header tabla
|
||||
├────────┼─────────────────┼─────────────────┤
|
||||
│ C0001 │ Empresa ABC │ Valencia │
|
||||
│ C0002 │ Empresa XYZ │ Madrid │
|
||||
│ ... │ ... │ ... │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ [|<] [<] 15/520 [>] [>|] │ ← Navegación integrada
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Caso de uso:** Panel de lista en aplicaciones CRUD (WhoListPanel, DocListPanel, etc.)
|
||||
|
||||
### 2. ConfigBrowser
|
||||
|
||||
Widget especializado para visualizar/editar configuración:
|
||||
- Lista de variables agrupadas por categoría
|
||||
- Editor inline según tipo (checkbox, input, color picker)
|
||||
- Búsqueda/filtro de variables
|
||||
- Indicador de cambios pendientes
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ [Buscar: ___________] │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ ▼ General │
|
||||
│ auto_guardar_cada [15 ] minutos │
|
||||
│ backup_automatico [✓] │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ ▼ Apariencia │
|
||||
│ color_azul_empresa [■] RGB(40,80,120) │
|
||||
│ font_size [14 ] │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ ▶ Comportamiento (click para expandir) │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Caso de uso:** Panel de configuración de aplicación
|
||||
|
||||
---
|
||||
|
||||
## Justificación Técnica
|
||||
|
||||
### Patrón Repetido en zsimifactu
|
||||
|
||||
En `WhoListPanel` y futuros paneles de lista se repite:
|
||||
1. Tabla con datos
|
||||
2. Callback para obtener celdas
|
||||
3. Botones navegación (First/Prev/Next/Last)
|
||||
4. Indicador posición
|
||||
5. Manejo de selección → DataManager
|
||||
|
||||
Este código se duplicaría en cada panel de lista (DocListPanel, ProdListPanel, etc.).
|
||||
|
||||
### Beneficios de Encapsular
|
||||
|
||||
| Aspecto | Sin widget | Con TableBrowser |
|
||||
|---------|------------|------------------|
|
||||
| Líneas por panel | ~200 | ~50 |
|
||||
| Bugs de navegación | Duplicados | Corregidos una vez |
|
||||
| Consistencia UX | Manual | Automática |
|
||||
| Nuevos paneles | Copy-paste | Instanciar widget |
|
||||
|
||||
---
|
||||
|
||||
## API Propuesta
|
||||
|
||||
### TableBrowser
|
||||
|
||||
```zig
|
||||
const TableBrowser = struct {
|
||||
table_state: widgets.table.TableState,
|
||||
nav_state: NavState,
|
||||
filter_state: ?FilterState,
|
||||
|
||||
pub const Config = struct {
|
||||
columns: []const Column,
|
||||
show_navigation: bool = true,
|
||||
show_filters: bool = false,
|
||||
row_height: u16 = 18,
|
||||
};
|
||||
|
||||
pub const Callbacks = struct {
|
||||
getCellData: *const fn (row: usize, col: usize) []const u8,
|
||||
getRowCount: *const fn () usize,
|
||||
onSelectionChanged: ?*const fn (row: usize) void = null,
|
||||
};
|
||||
|
||||
pub fn init(config: Config) TableBrowser;
|
||||
pub fn draw(self: *Self, ctx: *Context, rect: Rect, callbacks: Callbacks) DrawResult;
|
||||
pub fn handleEvent(self: *Self, event: Event) bool;
|
||||
|
||||
// Navegación programática
|
||||
pub fn goFirst(self: *Self) void;
|
||||
pub fn goPrev(self: *Self) void;
|
||||
pub fn goNext(self: *Self) void;
|
||||
pub fn goLast(self: *Self) void;
|
||||
pub fn getPosition(self: *Self) struct { current: usize, total: usize };
|
||||
};
|
||||
```
|
||||
|
||||
### ConfigBrowser
|
||||
|
||||
```zig
|
||||
const ConfigBrowser = struct {
|
||||
pub const Config = struct {
|
||||
variables: []const ConfigVariable, // De zcatconfig
|
||||
show_search: bool = true,
|
||||
group_by_category: bool = true,
|
||||
};
|
||||
|
||||
pub fn init(config: Config) ConfigBrowser;
|
||||
pub fn draw(self: *Self, ctx: *Context, rect: Rect, config_ptr: anytype) DrawResult;
|
||||
pub fn handleEvent(self: *Self, event: Event) bool;
|
||||
|
||||
// Estado
|
||||
pub fn hasChanges(self: *Self) bool;
|
||||
pub fn getChangedVariables(self: *Self) []const []const u8;
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integración con zcatconfig
|
||||
|
||||
El `ConfigBrowser` se integraría naturalmente con la nueva librería `zcatconfig`:
|
||||
|
||||
```zig
|
||||
const zcatconfig = @import("zcatconfig");
|
||||
const ConfigBrowser = zcatgui.widgets.ConfigBrowser;
|
||||
|
||||
// En el proyecto consumidor:
|
||||
const variables = @import("config/variables.zig");
|
||||
const Config = @import("config/structures.zig").Config;
|
||||
|
||||
var config = Config{};
|
||||
var browser = ConfigBrowser.init(.{
|
||||
.variables = &variables.config_variables,
|
||||
});
|
||||
|
||||
// En draw:
|
||||
const result = browser.draw(ctx, rect, &config);
|
||||
if (result.value_changed) {
|
||||
try zcatconfig.save(&variables.config_variables, Config, &config, allocator, "config.txt");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Priorización Sugerida
|
||||
|
||||
| Widget | Prioridad | Razón |
|
||||
|--------|-----------|-------|
|
||||
| TableBrowser | Alta | Necesario para DocListPanel, ProdListPanel |
|
||||
| ConfigBrowser | Media | Panel config es nice-to-have, no bloqueante |
|
||||
|
||||
---
|
||||
|
||||
## Dependencias
|
||||
|
||||
- **TableBrowser**: Ninguna externa, usa widgets existentes (table, button)
|
||||
- **ConfigBrowser**: Requiere `zcatconfig` como dependencia opcional
|
||||
|
||||
---
|
||||
|
||||
## Siguiente Paso
|
||||
|
||||
1. Revisar esta propuesta
|
||||
2. Decidir si proceder con implementación
|
||||
3. Crear issue/tarea en zcatgui
|
||||
|
||||
---
|
||||
|
||||
*Documento generado desde conversación zsimifactu 2025-12-17*
|
||||
|
|
@ -86,6 +86,12 @@ pub fn advancedTableRect(
|
|||
const colors = custom_colors orelse table_schema.colors orelse &default_colors;
|
||||
const config = table_schema.config;
|
||||
|
||||
// Ensure valid selection if table has data (like Table widget does)
|
||||
if (table_state.getRowCount() > 0 and table_schema.columns.len > 0) {
|
||||
if (table_state.selected_row < 0) table_state.selected_row = 0;
|
||||
if (table_state.selected_col < 0) table_state.selected_col = 0;
|
||||
}
|
||||
|
||||
// Generate unique ID for focus system
|
||||
const widget_id: u64 = @intFromPtr(table_state);
|
||||
|
||||
|
|
@ -393,9 +399,12 @@ fn drawRow(
|
|||
const is_selected_cell = is_selected_row and table_state.selected_col == @as(i32, @intCast(col_idx));
|
||||
const cell_clicked = cell_rect.contains(mouse.x, mouse.y) and ctx.input.mousePressed(.left);
|
||||
|
||||
// Cell background for selected cell
|
||||
// Cell indicator for selected cell (outline instead of solid fill)
|
||||
if (is_selected_cell) {
|
||||
ctx.pushCommand(Command.rect(col_x, bounds.y, col.width, config.row_height, colors.selected_cell));
|
||||
// Subtle background tint
|
||||
ctx.pushCommand(Command.rect(col_x, bounds.y, col.width, config.row_height, blendColor(row_bg, colors.selected_cell, 0.15)));
|
||||
// Border outline
|
||||
ctx.pushCommand(Command.rectOutline(col_x, bounds.y, col.width, config.row_height, colors.selected_cell));
|
||||
}
|
||||
|
||||
// Get cell value
|
||||
|
|
@ -567,74 +576,96 @@ fn handleKeyboard(
|
|||
switch (nav_key) {
|
||||
.up => {
|
||||
if (table_state.selected_row > 0) {
|
||||
table_state.selectCell(
|
||||
@intCast(table_state.selected_row - 1),
|
||||
@intCast(@max(0, table_state.selected_col)),
|
||||
);
|
||||
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) {
|
||||
table_state.selectCell(
|
||||
@intCast(table_state.selected_row + 1),
|
||||
@intCast(@max(0, table_state.selected_col)),
|
||||
);
|
||||
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) {
|
||||
table_state.selectCell(
|
||||
@intCast(@max(0, table_state.selected_row)),
|
||||
@intCast(table_state.selected_col - 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;
|
||||
}
|
||||
},
|
||||
.right => {
|
||||
if (table_state.selected_col < @as(i32, @intCast(col_count)) - 1) {
|
||||
table_state.selectCell(
|
||||
@intCast(@max(0, table_state.selected_row)),
|
||||
@intCast(table_state.selected_col + 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 = @max(0, table_state.selected_row - @as(i32, @intCast(visible_rows)));
|
||||
table_state.selectCell(@intCast(new_row), @intCast(@max(0, table_state.selected_col)));
|
||||
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 = @min(
|
||||
const new_row: usize = @intCast(@min(
|
||||
@as(i32, @intCast(row_count)) - 1,
|
||||
table_state.selected_row + @as(i32, @intCast(visible_rows)),
|
||||
);
|
||||
table_state.selectCell(@intCast(new_row), @intCast(@max(0, table_state.selected_col)));
|
||||
));
|
||||
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
|
||||
table_state.selectCell(0, 0);
|
||||
new_row = 0;
|
||||
new_col = 0;
|
||||
} else {
|
||||
// Home: first column
|
||||
table_state.selectCell(@intCast(@max(0, table_state.selected_row)), 0);
|
||||
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
|
||||
const last_row = if (row_count > 0) row_count - 1 else 0;
|
||||
const last_col = col_count - 1;
|
||||
table_state.selectCell(last_row, last_col);
|
||||
new_row = if (row_count > 0) row_count - 1 else 0;
|
||||
new_col = col_count - 1;
|
||||
} else {
|
||||
// End: last column
|
||||
table_state.selectCell(@intCast(@max(0, table_state.selected_row)), col_count - 1);
|
||||
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) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue