From 0b9af8f1bb196e9c7c0fe95efbaf686360d9714c Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Fri, 15 May 2026 17:35:22 +0200 Subject: [PATCH] data_table v1.3.1: Dots renderer via ImDrawList (font-independent) + recompile all apps with fn_table_viz (issue 0081-O.6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace TextColored+glyph with ImDrawList::AddCircleFilled in CellRenderer::Dots. Dots are now font-independent: no dependency on Unicode glyph coverage. Fixes "dots show as ?" on Karla/Roboto/Inter fonts that lack Geometric Shapes block. - dots_glyph_size now controls circle radius (px) instead of font scale. - BadgeRule.label is ignored for Dots (documented in data_table_types.h + docs). - data_table.md bumped to v1.3.1 with capability growth log entry. - docs/capabilities/data_table_renderers.md: Dots section updated + Common pitfalls entry added: "Asumir que cualquier glyph Unicode renderea". - dag_engine_ui/tabs.cpp: removed stale "● glyph" comment from BadgeRule. - Recompiled: dag_engine_ui, registry_dashboard, graph_explorer, navegator_dashboard, odr_console. All 5 apps deployed to Desktop/apps/. Build Linux + tests 4/4 green. Co-Authored-By: Claude Sonnet 4.6 --- cpp/apps/dag_engine_ui | 2 +- cpp/functions/core/data_table_types.h | 5 +- cpp/functions/viz/data_table.cpp | 59 +++++++++++++---------- cpp/functions/viz/data_table.md | 4 +- docs/capabilities/data_table_renderers.md | 19 ++++++-- 5 files changed, 55 insertions(+), 34 deletions(-) diff --git a/cpp/apps/dag_engine_ui b/cpp/apps/dag_engine_ui index 61314b7f..aec22ba5 160000 --- a/cpp/apps/dag_engine_ui +++ b/cpp/apps/dag_engine_ui @@ -1 +1 @@ -Subproject commit 61314b7f96a96ca3af7a806922a68a2f603ea83c +Subproject commit aec22ba5940e8560fd58e508a67374c1852774ad diff --git a/cpp/functions/core/data_table_types.h b/cpp/functions/core/data_table_types.h index ebc935a4..90bb6da5 100644 --- a/cpp/functions/core/data_table_types.h +++ b/cpp/functions/core/data_table_types.h @@ -208,8 +208,9 @@ struct ColumnSpec { // Dots (Phase 2.5, v1.3.0): CellRenderer::Dots — inline status sparkline. // Cell value is a separator-delimited string of tokens, e.g. "ok,error,ok". - // Each token is looked up in `badges` for color (and optional glyph override via label). - // Default glyph: u8"●"; override by setting BadgeRule.label to another UTF-8 glyph. + // Each token is looked up in `badges` for color. Dots are drawn as filled circles + // via ImDrawList (font-independent — no Unicode glyph dependency). + // Note: BadgeRule.label is ignored for Dots; it only applies to the Badge renderer. char dots_separator = ','; // separator between tokens float dots_glyph_size = 0.0f; // glyph size px; 0 = default font size int dots_max = 0; // hard limit on dots shown; 0 = no limit diff --git a/cpp/functions/viz/data_table.cpp b/cpp/functions/viz/data_table.cpp index 8d43615d..10d2eed9 100644 --- a/cpp/functions/viz/data_table.cpp +++ b/cpp/functions/viz/data_table.cpp @@ -329,18 +329,14 @@ static void draw_cell_custom(const ColumnSpec& spec, const char* value, case CellRenderer::Dots: { // Parse cell value as separator-delimited tokens. - std::string s = value; // value is guaranteed non-null (checked at top) + std::string s = value; std::vector tokens; { std::string cur; char sep = spec.dots_separator ? spec.dots_separator : ','; for (char c : s) { - if (c == sep) { - tokens.push_back(cur); - cur.clear(); - } else { - cur.push_back(c); - } + if (c == sep) { tokens.push_back(cur); cur.clear(); } + else cur.push_back(c); } if (!cur.empty()) tokens.push_back(cur); } @@ -348,33 +344,46 @@ static void draw_cell_custom(const ColumnSpec& spec, const char* value, ? std::min((int)tokens.size(), spec.dots_max) : (int)tokens.size(); + // Draw filled circles via ImDrawList — font-independent, scales with font size. + // BadgeRule.label is ignored for Dots (only relevant for Badge renderer). + float font_h = ImGui::GetTextLineHeight(); + float radius = (spec.dots_glyph_size > 0.f ? spec.dots_glyph_size : font_h * 0.32f); + float spacing = radius * 2.3f; + ImVec2 origin = ImGui::GetCursorScreenPos(); + float dot_y = origin.y + font_h * 0.5f; + ImDrawList* dl = ImGui::GetWindowDrawList(); + ImU32 default_col = IM_COL32(110, 110, 125, 255); // muted grey + for (int t = 0; t < limit; ++t) { - if (t > 0) ImGui::SameLine(0, 2.0f); // tight spacing between dots - // Look up color + optional glyph override in badges. - ImVec4 color(0.4f, 0.4f, 0.45f, 1.0f); // default: muted grey - const char* glyph = u8"\xe2\x97\x8f"; // UTF-8 for ● + ImU32 col = default_col; for (const auto& br : spec.badges) { if (br.value == tokens[t]) { ImVec4 c = hex_to_imcolor(br.color_hex); - if (c.x >= 0.f) color = c; - if (!br.label.empty()) glyph = br.label.c_str(); + if (c.x >= 0.f) col = ImGui::ColorConvertFloat4ToU32(c); break; } } - // Optional glyph size push. - bool pushed_font_scale = false; - if (spec.dots_glyph_size > 0.0f) { - float scale = spec.dots_glyph_size / ImGui::GetFontSize(); - ImGui::SetWindowFontScale(scale); - pushed_font_scale = true; - } - ImGui::TextColored(color, "%s", glyph); - if (pushed_font_scale) ImGui::SetWindowFontScale(1.0f); - // Per-dot tooltip: show the token string. - if (spec.tooltip_on_hover && ImGui::IsItemHovered()) { - ImGui::SetTooltip("%s", tokens[t].c_str()); + float cx = origin.x + radius + t * spacing; + dl->AddCircleFilled(ImVec2(cx, dot_y), radius, col, 18); + } + + // Reserve cursor space so layout flows correctly. + float total_w = (limit > 0 ? (limit * spacing) : 0.f); + ImGui::Dummy(ImVec2(total_w, font_h)); + + // Hit-test for tooltip per dot. + if (spec.tooltip_on_hover && ImGui::IsItemHovered()) { + ImVec2 mp = ImGui::GetMousePos(); + for (int t = 0; t < limit; ++t) { + float cx = origin.x + radius + t * spacing; + float dx = mp.x - cx, dy = mp.y - dot_y; + if (dx*dx + dy*dy <= radius * radius * 1.5f) { + ImGui::SetTooltip("%s", tokens[t].c_str()); + break; + } } } + if (spec.dots_show_count) { ImGui::SameLine(0, 6.0f); ImGui::TextDisabled("(%d)", (int)tokens.size()); diff --git a/cpp/functions/viz/data_table.md b/cpp/functions/viz/data_table.md index d4bf283a..f6b9f778 100644 --- a/cpp/functions/viz/data_table.md +++ b/cpp/functions/viz/data_table.md @@ -3,7 +3,7 @@ name: data_table kind: function lang: cpp domain: viz -version: "1.3.0" +version: "1.3.1" 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. Dots renderer para sparkline-like de status (v1.3.0). Entry-point publica del stack data_table. Muta State segun interaccion del usuario." @@ -184,5 +184,7 @@ v1.2.0 (2026-05-15) — Button renderer + event sink (ButtonClick/RowDoubleClick v1.3.0 (2026-05-15) — Dots renderer for inline status timelines (sparkline-like). Reuses badges for color mapping. dots_max/dots_separator/dots_show_count/dots_glyph_size fields. TQL roundtrip. dag_engine_ui canonical use case (10-col antipattern -> 6-col fix). +v1.3.1 (2026-05-15) — Dots renderer now draws filled circles via ImDrawList instead of Unicode glyph. Font-independent: works regardless of TTF glyph coverage. Closes "dots show as ?" bug in dag_engine_ui. + --- Promovido desde `cpp/apps/primitives_gallery/playground/tables/data_table.{h,cpp}` — issue 0081-H. diff --git a/docs/capabilities/data_table_renderers.md b/docs/capabilities/data_table_renderers.md index 50e166f0..a8a1eed3 100644 --- a/docs/capabilities/data_table_renderers.md +++ b/docs/capabilities/data_table_renderers.md @@ -1,4 +1,4 @@ -# data_table_renderers — declarative cell renderers (v1.3.0) +# data_table_renderers — declarative cell renderers (v1.3.1) Tag: `cpp-tables` (mismo grupo que TQL; los renderers son parte del stack `data_table`). @@ -223,7 +223,9 @@ recent.tooltip_on_hover = true; // hover sobre cada dot muestra el status strin // "success,success,failed,running,pending" ``` -Resultado visual: `● ● ● ● ●` (verde verde rojo amarillo gris). +Resultado visual: 5 circulos solidos coloreados (verde verde rojo amarillo gris). + +Cada dot se dibuja con `ImDrawList::AddCircleFilled` — primitiva ImGui pura, sin dependencia de glyph Unicode. Funciona con cualquier fuente (Karla, Roboto, Inter, Tabler). Campos de `ColumnSpec` para Dots: @@ -232,11 +234,11 @@ Campos de `ColumnSpec` para Dots: | `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 | +| `dots_glyph_size` | `0.0` | Radio del circulo en px; 0 = `font_height * 0.32` (auto) | 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`. +Sin match: gris tenue `IM_COL32(110, 110, 125, 255)`. `BadgeRule.label` es ignorado +en Dots (solo aplica al renderer Badge). TQL: `renderer = "dots"`, campos opcionales `dots_separator`, `dots_max`, `dots_show_count`, `dots_glyph_size`. @@ -284,6 +286,13 @@ Badge espera **valores discretos** (enum-like: ok/error/running). Para texto lib 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. +### Asumir que cualquier glyph Unicode renderea + +Las fuentes del proyecto (Karla, Roboto, Inter, Tabler) cubren Latin-1 + Tabler private use area. NO incluyen Geometric Shapes (●, ▲), Misc Symbols (★) ni Dingbats (✓). Si quieres usar uno de estos: +- **Para dots de status:** usa `CellRenderer::Dots` (primitiva `ImDrawList`, no depende de fuente). +- **Para checkmarks/X:** usa `TI_CHECK` / `TI_X` via `icon_name` en `IconMapEntry`. +- **Para texto puro con ●:** considera añadir un font fallback con Geometric Shapes (no recomendado — usa Dots renderer). + ### 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.