data_table: Phase 2 — Button + events + tooltip + RightClick + TQL persist column_specs (issue 0081-O)

- CellRenderer::Button=5: renders SmallButton per cell; emits TableEvent::ButtonClick on click
- TableEventKind enum (ButtonClick/RowDoubleClick/RowRightClick/CellEdit) + TableEvent struct
- render() extended overload: adds events_out parameter (nullptr = back-compat, no events)
- RowDoubleClick and RowRightClick detection in raw table loop (stage 0)
- RowRightClick also detected in aggregated stage table (stage 1+)
- Tooltip per cell: tooltip_on_hover + tooltip fields on ColumnSpec; "auto" = show cell value
- State::aux_column_specs: TQL-persisted column specs sidecar per table
- tql_emit: serializes aux_column_specs[0] as column_specs block (badge/progress/duration/icon/button/tooltip)
- tql_apply: parses column_specs block back into state.aux_column_specs[0]
- render() merges aux_column_specs into TableInput when caller passes empty column_specs
- test_column_specs: 5->8 tests (Button struct, tooltip fields, both render() signatures link)
- tql_emit_test: 3 new tests (column_specs badge/button/tooltip emit) — 52 passed
- tql_apply_test: 3 new tests (column_specs badge/button/tooltip roundtrip) — 106 passed
- Back-compat: existing apps (graph_explorer, registry_dashboard) unchanged
- Version bump: data_table v1.1.0 -> v1.2.0

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-15 16:59:26 +02:00
parent 1c9aabd212
commit 97bd5ea056
11 changed files with 2593 additions and 40 deletions
+132 -5
View File
@@ -1,9 +1,12 @@
// test_column_specs.cpp — Smoke / back-compat tests for declarative cell renderers.
// Issue 0081-N, v1.1.0.
// Issue 0081-N, v1.1.0. Phase 2 (issue 0081-O, v1.2.0).
//
// These tests verify:
// 1. TableInput without column_specs compiles and links (back-compat).
// 2-5. TableInput with Badge/Progress/Duration/Icon column_specs compiles and links.
// 6. Button renderer: TableEvent struct is constructible; events_out pointer accepted.
// 7. Tooltip field: ColumnSpec with tooltip_on_hover=true compiles and links.
// 8. render() overload with events_out=nullptr back-compat (symbol resolution only).
//
// None of these tests call data_table::render() (requires ImGui context).
// They only verify that the new types are usable and that the symbols from
@@ -50,7 +53,11 @@ static void test_no_column_specs() {
// Verify that render symbol is still linkable (no ImGui context needed
// to take the address; the linker verifies the symbol resolves).
auto* render_fn = &data_table::render;
// Use the classic overload (without events_out) for the back-compat check.
auto* render_fn = static_cast<void(*)(const char*,
const std::vector<TableInput>&,
State&,
bool)>(&data_table::render);
(void)render_fn;
std::printf("PASS: test_no_column_specs (back-compat, column_specs empty)\n");
@@ -174,13 +181,130 @@ static void test_icon_column_spec() {
assert(t.column_specs[3].icon_map[0].value == "fn");
assert(t.column_specs[3].icon_map[0].icon_name == "TI_BOLT");
// Verify render symbol still links with column_specs populated.
auto* render_fn = &data_table::render;
// Verify render symbol still links with column_specs populated (classic overload).
auto* render_fn = static_cast<void(*)(const char*,
const std::vector<TableInput>&,
State&,
bool)>(&data_table::render);
(void)render_fn;
std::printf("PASS: test_icon_column_spec (2 entries, render symbol links)\n");
}
// ---------------------------------------------------------------------------
// Test 6: Button renderer — TableEvent struct is constructible; events_out ptr
// is accepted by the new render() overload (symbol resolution only).
// ---------------------------------------------------------------------------
static void test_button_column_spec_and_event_struct() {
TableInput t;
t.name = "t6";
t.rows = 3;
t.cols = 4;
t.cells = g_cells;
t.headers = g_headers;
t.types = g_types;
ColumnSpec cs_btn;
cs_btn.id = "actions";
cs_btn.renderer = CellRenderer::Button;
cs_btn.button_action = "cancel";
cs_btn.button_label = "Cancel";
cs_btn.button_color_hex = "#ef4444";
t.column_specs.resize(4);
t.column_specs[0] = cs_btn;
assert(t.column_specs[0].renderer == CellRenderer::Button);
assert(t.column_specs[0].button_action == "cancel");
assert(t.column_specs[0].button_label == "Cancel");
assert(t.column_specs[0].button_color_hex == "#ef4444");
// Verify TableEvent struct can be constructed and holds expected fields.
TableEvent ev;
ev.kind = TableEventKind::ButtonClick;
ev.row = 1;
ev.col = 0;
ev.column_id = "actions";
ev.action_id = "cancel";
ev.value = "ok";
assert(ev.kind == TableEventKind::ButtonClick);
assert(ev.row == 1);
assert(ev.action_id == "cancel");
// Verify the render() overload with events_out is linkable.
std::vector<TableEvent> events;
auto* render_with_events = static_cast<void(*)(const char*,
const std::vector<TableInput>&,
State&,
std::vector<TableEvent>*,
bool)>(&data_table::render);
(void)render_with_events;
std::printf("PASS: test_button_column_spec_and_event_struct "
"(Button spec + TableEvent + render overload link)\n");
}
// ---------------------------------------------------------------------------
// Test 7: Tooltip field — ColumnSpec with tooltip_on_hover=true.
// ---------------------------------------------------------------------------
static void test_tooltip_column_spec() {
TableInput t;
t.name = "t7";
t.rows = 3;
t.cols = 4;
t.cells = g_cells;
t.headers = g_headers;
t.types = g_types;
ColumnSpec cs_tip;
cs_tip.id = "status";
cs_tip.renderer = CellRenderer::Text; // tooltip works on any renderer
cs_tip.tooltip = "auto"; // "auto" -> show cell value
cs_tip.tooltip_on_hover = true;
t.column_specs.resize(4);
t.column_specs[0] = cs_tip;
assert(t.column_specs[0].tooltip == "auto");
assert(t.column_specs[0].tooltip_on_hover == true);
// Also test explicit tooltip text.
ColumnSpec cs_tip2;
cs_tip2.id = "progress";
cs_tip2.renderer = CellRenderer::Progress;
cs_tip2.tooltip = "Progress percentage (0..1)";
cs_tip2.tooltip_on_hover = true;
t.column_specs[1] = cs_tip2;
assert(t.column_specs[1].tooltip == "Progress percentage (0..1)");
assert(t.column_specs[1].tooltip_on_hover == true);
std::printf("PASS: test_tooltip_column_spec (auto + explicit, tooltip_on_hover=true)\n");
}
// ---------------------------------------------------------------------------
// Test 8: render() back-compat overload without events_out — symbol links.
// ---------------------------------------------------------------------------
static void test_render_backcompat_overload() {
// Verify both render() signatures are resolvable at link time.
// Classic (no events_out):
auto* render_classic = static_cast<void(*)(const char*,
const std::vector<TableInput>&,
State&,
bool)>(&data_table::render);
(void)render_classic;
// New (with events_out):
auto* render_events = static_cast<void(*)(const char*,
const std::vector<TableInput>&,
State&,
std::vector<TableEvent>*,
bool)>(&data_table::render);
(void)render_events;
std::printf("PASS: test_render_backcompat_overload (both render() signatures link)\n");
}
// ---------------------------------------------------------------------------
// main
// ---------------------------------------------------------------------------
@@ -191,6 +315,9 @@ int main() {
test_progress_column_spec();
test_duration_column_spec();
test_icon_column_spec();
std::printf("=== ALL TESTS PASSED (5/5) ===\n");
test_button_column_spec_and_event_struct();
test_tooltip_column_spec();
test_render_backcompat_overload();
std::printf("=== ALL TESTS PASSED (8/8) ===\n");
return 0;
}