data_table v1.3.0: Dots renderer for status timelines + fix dag_engine_ui antipattern + pitfalls doc (issue 0081-O.5)
PARTE A - CellRenderer::Dots (v1.3.0): - Add Dots=8 to CellRenderer enum (data_table_types.h) - Add dots_separator/dots_max/dots_show_count/dots_glyph_size fields to ColumnSpec - Implement draw_cell_custom case Dots in data_table.cpp - Parses comma-separated cell value into tokens - Looks up each token in badges for color + optional glyph override - Per-dot tooltip via tooltip_on_hover - tql_emit: serialize renderer="dots" + dots_max/dots_show_count/dots_glyph_size/dots_separator - tql_apply: deserialize all Dots fields - tql_emit_test: +6 assertions (58 total, 0 failed) - tql_apply_test: +8 assertions (114 total, 0 failed) - test_column_specs: +2 tests (10/10 pass) PARTE B - dag_engine_ui fix: 10 cols -> 6 cols (submodule commit 61314b7) PARTE C - docs/capabilities/data_table_renderers.md: - Update to v1.3.0 - Add decision tree for renderer selection - Add CellRenderer::Dots section with canonical example - Add Common pitfalls section (multiple columns, badge for free-text, etc.) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,21 +1,45 @@
|
||||
# data_table_renderers — declarative cell renderers (v1.2.0)
|
||||
# data_table_renderers — declarative cell renderers (v1.3.0)
|
||||
|
||||
Tag: `cpp-tables` (mismo grupo que TQL; los renderers son parte del stack `data_table`).
|
||||
|
||||
Extiende `data_table_cpp_viz` con una API declarativa para renderizar columnas con
|
||||
Badge, Progress, Duration, Icon y **Button** (Phase 2), emitir eventos de interaccion
|
||||
(ButtonClick, RowDoubleClick, RowRightClick), mostrar tooltips por celda y persistir
|
||||
los specs en TQL (`aux_column_specs` roundtrip). Back-compat 100%: apps sin
|
||||
`column_specs` ni `events_out` no necesitan cambios.
|
||||
Badge, Progress, Duration, Icon, **Button** (Phase 2) y **Dots** (Phase 2.5), emitir
|
||||
eventos de interaccion (ButtonClick, RowDoubleClick, RowRightClick), mostrar tooltips
|
||||
por celda y persistir los specs en TQL (`aux_column_specs` roundtrip). Back-compat
|
||||
100%: apps sin `column_specs` ni `events_out` no necesitan cambios.
|
||||
|
||||
## Decision tree: que renderer elegir?
|
||||
|
||||
```
|
||||
Que muestra la celda?
|
||||
|
|
||||
+-- Texto libre arbitrario -> Text (default)
|
||||
|
|
||||
+-- Enum-like status discreto (1 val)
|
||||
| |
|
||||
| +-- Solo color de fondo -> Badge
|
||||
| +-- Necesita icono -> Icon
|
||||
| +-- Es accion clickable -> Button
|
||||
|
|
||||
+-- Lista/secuencia de status -> Dots
|
||||
|
|
||||
+-- Numero 0..1 o 0..100 -> Progress
|
||||
|
|
||||
+-- Milisegundos con threshold -> Duration
|
||||
|
|
||||
+-- Editable inline -> TextInput (Fase 3)
|
||||
|
|
||||
+-- Widget propio fuera del set -> Custom (Fase 3, escape hatch)
|
||||
```
|
||||
|
||||
## Tipos nuevos / actualizados en `data_table_types.h`
|
||||
|
||||
| Tipo | Que es |
|
||||
|---|---|
|
||||
| `CellRenderer` | enum class: `Text=0`, `Badge=1`, `Progress=2`, `Duration=3`, `Icon=4`, **`Button=5`** |
|
||||
| `BadgeRule` | value (exact match) + color_hex + label opcional |
|
||||
| `CellRenderer` | enum class: `Text=0`, `Badge=1`, `Progress=2`, `Duration=3`, `Icon=4`, **`Button=5`**, **`Dots=8`** |
|
||||
| `BadgeRule` | value (exact match) + color_hex + label opcional (usado como glyph override en Dots) |
|
||||
| `IconMapEntry` | value + icon_name (ej. `"TI_BOLT"`) + color_hex opcional |
|
||||
| `ColumnSpec` | id + renderer + badges / progress / duration / icon_map / **button_action, button_label, button_color_hex** / **tooltip, tooltip_on_hover** |
|
||||
| `ColumnSpec` | id + renderer + badges / progress / duration / icon_map / button_action, button_label, button_color_hex / tooltip, tooltip_on_hover / **dots_separator, dots_max, dots_show_count, dots_glyph_size** |
|
||||
| `TableInput::column_specs` | `std::vector<ColumnSpec>` sidecar opcional (size 0 o == cols) |
|
||||
| **`TableEventKind`** | enum class: ButtonClick=1, RowDoubleClick=2, RowRightClick=3, CellEdit=4 (reservado) |
|
||||
| **`TableEvent`** | kind + row + col + column_id + action_id + value |
|
||||
@@ -182,7 +206,88 @@ data_table::render("##t", {t}, res.state, &events);
|
||||
- `events_out` no se limpia en `render()` — el caller debe llamar `events.clear()` antes de cada frame.
|
||||
- `aux_column_specs` solo se persiste para `tables[0]` (el main table). Specs para tablas extra deben gestionarse por el caller.
|
||||
|
||||
## CellRenderer::Dots — timeline inline
|
||||
|
||||
Para sparkline-like de status (ultimas N ejecuciones, salud de checks, eventos):
|
||||
|
||||
```cpp
|
||||
ColumnSpec recent;
|
||||
recent.id = "recent";
|
||||
recent.renderer = CellRenderer::Dots;
|
||||
recent.badges = { {"success", "#22c55e"}, {"failed", "#ef4444"},
|
||||
{"running", "#eab308"}, {"pending", "#94a3b8"} };
|
||||
recent.dots_max = 5;
|
||||
recent.tooltip_on_hover = true; // hover sobre cada dot muestra el status string
|
||||
|
||||
// Cell value: comma-separated status strings (most-recent first)
|
||||
// "success,success,failed,running,pending"
|
||||
```
|
||||
|
||||
Resultado visual: `● ● ● ● ●` (verde verde rojo amarillo gris).
|
||||
|
||||
Campos de `ColumnSpec` para Dots:
|
||||
|
||||
| Campo | Default | Descripcion |
|
||||
|---|---|---|
|
||||
| `dots_separator` | `','` | Caracter separador de tokens en el cell value |
|
||||
| `dots_max` | `0` (sin limite) | Limite hard de dots a mostrar |
|
||||
| `dots_show_count` | `false` | Si true, añade ` (N)` al final con el total de tokens |
|
||||
| `dots_glyph_size` | `0.0` | Tamaño en px del glyph; 0 = tamaño de fuente por defecto |
|
||||
|
||||
Color lookup: igual que Badge — cada token se busca exactamente en `badges.value`.
|
||||
Sin match: gris tenue `#666673`. Glyph por defecto: `●` (U+25CF). Para overridearlo,
|
||||
poner el glyph UTF-8 en `BadgeRule.label`.
|
||||
|
||||
TQL: `renderer = "dots"`, campos opcionales `dots_separator`, `dots_max`, `dots_show_count`, `dots_glyph_size`.
|
||||
|
||||
## Notas
|
||||
|
||||
- Tests: `cpp/tests/test_column_specs.cpp` (8 tests: 1 back-compat + 4 renderer types + 3 Phase 2). Smoke/linker; no requieren ImGui context.
|
||||
- Tests: `cpp/tests/test_column_specs.cpp` (10 tests: 1 back-compat + 4 renderer types + 3 Phase 2 + 2 Dots Phase 2.5). Smoke/linker; no requieren ImGui context.
|
||||
- TQL roundtrip implementado en Phase 2 (issue 0081-O, v1.2.0): `tql_emit_test` (3 tests) + `tql_apply_test` (9 tests nuevos).
|
||||
- Dots TQL roundtrip cubierto en `test_column_specs.cpp` test 10 (struct + aux_column_specs).
|
||||
|
||||
## Common pitfalls
|
||||
|
||||
### Multiple columns for a status timeline
|
||||
|
||||
**Incorrecto:**
|
||||
|
||||
```cpp
|
||||
ti.headers = {"Name", "R1", "R2", "R3", "R4", "R5", ...};
|
||||
for (int i = 1; i <= 5; ++i)
|
||||
ti.column_specs[i].renderer = CellRenderer::Badge;
|
||||
```
|
||||
|
||||
Problemas:
|
||||
- 5 columnas separadas = 5x sort headers + 5x filter chips + filas desalineadas.
|
||||
- Cells `"-"` para rellenar cuando hay menos de 5 runs.
|
||||
- Imposible filtrar por "muestra DAGs con >=1 failure en ultimas 5".
|
||||
- Bug real: dag_engine_ui v1 (ver issue 0081-O.5).
|
||||
|
||||
**Correcto:**
|
||||
|
||||
```cpp
|
||||
ColumnSpec recent;
|
||||
recent.renderer = CellRenderer::Dots;
|
||||
recent.badges = { {"success","#22c55e"}, {"failed","#ef4444"}, ... };
|
||||
ti.headers = {"Name", "Recent", ...};
|
||||
// Cell value: "success,failed,success,running,pending"
|
||||
```
|
||||
|
||||
Una sola columna, N dots inline. Tooltip por dot muestra el status concreto. Filtrable como string (`Recent contains failed`).
|
||||
|
||||
### Badge para texto libre
|
||||
|
||||
Badge espera **valores discretos** (enum-like: ok/error/running). Para texto libre con muchos valores unicos: Text + opcionalmente Custom renderer (Fase 3).
|
||||
|
||||
### Buttons para navegacion cuando RowDoubleClick es suficiente
|
||||
|
||||
Si la accion es "abrir detalle del row", no uses Button por row. Usa `RowDoubleClick` event + handler comun. Mas limpio y sin IDs de ImGui por fila.
|
||||
|
||||
### Progress con valores fuera de 0..1
|
||||
|
||||
Progress espera `0..1`. Si tu valor es `0..100`, set `progress_scale_100 = true` o normaliza antes.
|
||||
|
||||
### Color hardcodeado fuera de column_specs
|
||||
|
||||
El coloreado debe vivir en `column_specs[i].badges`, no en `if (status=="ok") PushStyleColor(GREEN)` antes de `TextUnformatted`. Esto compila pero rompe la promesa declarativa: el TQL serializado no tendra la info y Ask AI no podra razonar sobre la tabla.
|
||||
|
||||
Reference in New Issue
Block a user