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:
@@ -327,15 +327,72 @@ static void draw_cell_custom(const ColumnSpec& spec, const char* value,
|
||||
break;
|
||||
}
|
||||
|
||||
case CellRenderer::Dots: {
|
||||
// Parse cell value as separator-delimited tokens.
|
||||
std::string s = value; // value is guaranteed non-null (checked at top)
|
||||
std::vector<std::string> 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 (!cur.empty()) tokens.push_back(cur);
|
||||
}
|
||||
int limit = (spec.dots_max > 0)
|
||||
? std::min((int)tokens.size(), spec.dots_max)
|
||||
: (int)tokens.size();
|
||||
|
||||
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 ●
|
||||
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();
|
||||
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());
|
||||
}
|
||||
}
|
||||
if (spec.dots_show_count) {
|
||||
ImGui::SameLine(0, 6.0f);
|
||||
ImGui::TextDisabled("(%d)", (int)tokens.size());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// CellRenderer::Text or unknown — plain text.
|
||||
ImGui::TextUnformatted(value);
|
||||
break;
|
||||
}
|
||||
|
||||
// Tooltip: show on hover if tooltip_on_hover is set.
|
||||
// Tooltip: show on hover if tooltip_on_hover is set (non-Dots renderers).
|
||||
// For Dots, per-dot tooltips are handled inline above.
|
||||
// "auto" shows the raw cell value (useful for truncated text columns).
|
||||
if (spec.tooltip_on_hover && ImGui::IsItemHovered()) {
|
||||
if (spec.renderer != CellRenderer::Dots &&
|
||||
spec.tooltip_on_hover && ImGui::IsItemHovered()) {
|
||||
const char* tip = (spec.tooltip == "auto") ? value : spec.tooltip.c_str();
|
||||
if (tip && tip[0]) ImGui::SetTooltip("%s", tip);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user