--- name: data_table kind: function lang: cpp domain: viz version: "1.2.0" purity: impure signature: "void data_table::render(const char* id, const std::vector& tables, State& st, std::vector* events_out = nullptr, bool show_chrome = true)" description: "Render UI completa de tabla TQL: chips bar, tabla, viz panels, column-stats inline, drill, color rules, joins, TQL editor, Ask AI, Button renderer, event sink (ButtonClick/RowDoubleClick/RowRightClick), tooltip per-cell, column_specs persisted in TQL. Entry-point publica del stack data_table. Muta State segun interaccion del usuario." tags: [tables, viz, ui, imgui, tql, cpp-tables] uses_functions: - compute_stage_cpp_core - compute_pipeline_cpp_core - compute_column_stats_cpp_core - auto_detect_type_cpp_core - tql_emit_cpp_core - tql_apply_cpp_core - tql_helpers_cpp_core - tql_to_sql_cpp_core - lua_engine_cpp_core - join_tables_cpp_core - viz_render_cpp_viz uses_types: - data_table_types_cpp_core - ColumnSpec_cpp_core - CellRenderer_cpp_core - BadgeRule_cpp_core - IconMapEntry_cpp_core - TableInput_cpp_core - State_cpp_core - Stage_cpp_core - StageOutput_cpp_core - ViewMode_cpp_viz - ViewConfig_cpp_viz - VizPanel_cpp_viz - Join_cpp_core - Filter_cpp_core - DrillStep_cpp_core - DerivedColumn_cpp_core - Aggregation_cpp_core - SortClause_cpp_core - ColumnType_cpp_core - TableEvent_cpp_core - TableEventKind_cpp_core returns: [] returns_optional: false error_type: "error_go_core" imports: - imgui.h - app_base.h - core/data_table_types.h - core/lua_engine.h - core/tql_apply.h - core/tql_emit.h - core/tql_helpers.h - core/tql_to_sql.h - core/compute_stage.h - core/compute_pipeline.h - core/compute_column_stats.h - core/auto_detect_type.h - core/join_tables.h - viz/viz_render.h tested: true tests: - "back-compat: TableInput without column_specs does not crash" - "Badge: TableInput with Badge column_spec compiles and links" - "Progress: TableInput with Progress column_spec compiles and links" - "Duration: TableInput with Duration column_spec compiles and links" - "Icon: TableInput with Icon column_spec compiles and links" - "Button: TableEvent struct constructible; render() with events_out links" - "Tooltip: ColumnSpec with tooltip_on_hover=true compiles and links" - "Back-compat: both render() signatures (with/without events_out) link" test_file_path: "cpp/tests/test_column_specs.cpp" file_path: "cpp/functions/viz/data_table.cpp" params: - name: id desc: "ID unico ImGui para esta instancia, ej. '##orders_table'. Debe ser estable entre frames." - name: tables desc: "Lista de TableInput materializadas en memoria. tables[0] es la main por defecto; si State.main_source no-vacio se usa por nombre. Tablas extra se exponen como joinables en la UI de joins." - name: st desc: "Estado mutable completo: pipeline de stages, joins, viz config, ui tweaks, aux_column_specs (Phase 2). Debe persistir entre frames — no declarar en el stack del frame." - name: events_out desc: "Puntero a vector de TableEvent. Si non-null, se puebla con eventos de este frame (ButtonClick, RowDoubleClick, RowRightClick). El caller limpia/lee el vector despues de cada render. Pasar nullptr para desactivar (back-compat)." - name: show_chrome desc: "Si false, oculta chips bar + breadcrumb por defecto. El usuario puede reactivar con el boton 'Show UI'. El State persiste el override del usuario entre frames." output: "void. Muta st en respuesta a la interaccion del usuario (filtros, breakouts, sorts, drill, joins, viz mode). Los cambios son visibles en st al retornar. Events emitted via events_out." --- ## Ejemplo ```cpp #include "viz/data_table.h" #include "core/data_table_types.h" // --- Setup (una vez) --- data_table::TableInput t; t.name = "orders"; t.rows = num_rows; t.cols = num_cols; t.cells = cells_ptr; // row-major flat array, owner externo t.headers = {"id", "amount", "status", "actions"}; t.types = {data_table::ColumnType::Int, data_table::ColumnType::Float, data_table::ColumnType::String, data_table::ColumnType::String}; // Phase 2: declarative renderers + tooltip t.column_specs.resize(4); t.column_specs[2].renderer = data_table::CellRenderer::Badge; t.column_specs[2].badges = {{"paid","#22c55e","Paid"},{"pending","#f59e0b",""}}; t.column_specs[2].tooltip = "auto"; t.column_specs[2].tooltip_on_hover = true; t.column_specs[3].renderer = data_table::CellRenderer::Button; t.column_specs[3].button_action = "cancel_order"; t.column_specs[3].button_label = "Cancel"; data_table::State st; // persiste entre frames std::vector events; // --- Render (cada frame) --- ImGui::Begin("Orders"); ImGui::BeginChild("##tbl", ImVec2(-1, -1)); events.clear(); data_table::render("##orders", {t}, st, &events); ImGui::EndChild(); ImGui::End(); // --- Process events --- for (const auto& ev : events) { if (ev.kind == data_table::TableEventKind::ButtonClick && ev.action_id == "cancel_order") { cancel_order(ev.row); // app handles the action } if (ev.kind == data_table::TableEventKind::RowDoubleClick) { open_order_detail(ev.row); } } ``` ## Cuando usarla Cuando una app necesita tabla con filtros + agregaciones + viz + joins sobre datos en memoria. Reemplaza `ImGui::BeginTable` inline + toda la logica TQL manual. Sustituye directamente el include del playground (`tables/data_table.h`) cambiando solo el path a `viz/data_table.h`. ## Gotchas - **ImGui + ImPlot context activos**: `render()` llama a APIs de ambas librerias. Llamar fuera de un frame activo causa UB. - **State no stack-local**: `State` contiene el historial de drill, pipeline de stages, cache de stats y buffers de UI. Declarar en el stack del frame reset todo el estado del usuario en cada frame. - **Drill-down propaga en State**: `st.active_stage` y `st.stages` se mutan por click en charts. El caller puede leer `st` tras `render()` para reaccionar. - **Thread-safety**: `render()` usa `static thread_local` para buffers intermedios. Llamar solo desde el main thread de ImGui. - **TableInput owner externo**: `cells` es un puntero raw al array del caller. Los datos deben sobrevivir durante toda la llamada a `render()`. No pasar puntero a vector que puede reallocarse. - **events_out no se limpia**: `render()` solo hace `push_back`. El caller debe llamar `events.clear()` antes de cada frame o acumulara eventos de frames anteriores. - **Button + celda vacia**: si el cell value es vacio, el boton NO se dibuja. La app controla cuando mostrar el boton poniendo un value no vacio (ej. "1" o el ID de la fila). - **RowRightClick emite evento Y abre popup interno**: la tabla de stages (stage>0) sigue abriendo su popup de drill. En el raw table (stage 0), se emite el evento pero el popup de drill antiguo tambien puede abrirse via `U.open_cell_popup`. El caller puede ignorar el popup interno y gestionar su propio menu al detectar `RowRightClick`. - **aux_column_specs merge**: si `TableInput.column_specs` esta vacio pero `State.aux_column_specs[0]` no, `render()` los aplica automaticamente. Si el caller pasa column_specs no vacios, ganan sobre los del State. - **Ask AI modal (llm_anthropic)**: el boton "Ask AI" usa un stub interno de `llm_anthropic` que retorna error por defecto. Para activar la feature real, compilar con `-DFN_LLM_ANTHROPIC=1` y proveer `infra/llm_anthropic.h` en el include path. Pendiente Wave 4: promover al registry. - **FN_TQL_DUCKDB**: modo SQL del Ask AI requiere compilar con `-DFN_TQL_DUCKDB=1` y la libreria DuckDB disponible. ## Notas No hay tests unitarios directos: `render()` requiere ImGui + ImPlot context activos (imposible sin ventana GL). Cobertura via: 1. `cpp/apps/primitives_gallery/playground/tables/` — playground original con self_test.cpp y e2e_run.sh. 2. Wave 4: migration self-tests en las apps que migren desde el playground. **Estado Wave 3.5 (issue 0081-I):** - Todos los includes del playground (`data_table_logic.h`, `tql.h`, `tql_to_sql.h`) eliminados. `data_table.cpp` compila sin el playground en el include path. - `tql::apply` firma extendida ya en `tql_apply_cpp_core` (wave anterior). Resuelto. - `tql_to_sql` promovido a `core/tql_to_sql.h`. Resuelto. - `data_table_logic` helpers (row_to_tsv, drill, view_mode, etc.) declarados como `static` en `data_table.cpp`. No son API pública. - `State::ensure_stage0/raw/active` implementados en `compute_stage.cpp`. - `ColStats` struct: usa el de `compute_column_stats_cpp_core`. Unificado. **Deuda tecnica restante (Wave 4):** - `llm_anthropic` (Ask AI modal, issue 0080): stub interno activo. Promover a `cpp/functions/infra/llm_anthropic` para activar feature real. - `FN_TQL_DUCKDB`: modo SQL del Ask AI sin soporte en stub. Requiere DuckDB + flag de compilacion. - `column_specs` TQL roundtrip (Phase 2): actualmente caller-managed. No persisten en TQL emit/apply. Planificado en issue 0081-O. ## Capability growth log v1.1.0 (2026-05-15) — declarative CellRenderer (Badge/Progress/Duration/Icon) via TableInput.column_specs sidecar. Back-compat preservado: apps existentes sin column_specs siguen funcionando sin cambios. v1.2.0 (2026-05-15) — Button renderer + event sink (ButtonClick/RowDoubleClick/RowRightClick) + tooltip per cell + column_specs persisted in TQL (aux_column_specs roundtrip). Back-compat preserved: events_out=nullptr by default; existing render() callers unchanged. --- Promovido desde `cpp/apps/primitives_gallery/playground/tables/data_table.{h,cpp}` — issue 0081-H.