diff --git a/issues/0036f-view-menu-open-nodegroups.md b/issues/0036f-view-menu-open-nodegroups.md new file mode 100644 index 0000000..f91a036 --- /dev/null +++ b/issues/0036f-view-menu-open-nodegroups.md @@ -0,0 +1,63 @@ +--- +id: 0036f +title: View menu accion "Open NodeGroups for selected" +status: done +priority: low +created: 2026-05-04 +parent: 0036 +depends_on: [0036b, 0036c] +--- + +## Objetivo + +Acción puntual en el menú View que abre/enfoca la NodeGroups window +del nodo seleccionado en el viewport. Sin atajo de teclado por ahora. +No es un toggle de panel (las windows son por entidad, multiples +abiertas simultaneamente). + +## Cambios + +### `views_toolbar.cpp` (o donde vive el menu View) + +Anyadir item: + +``` +[ ] View + ...existing items... + --- Drill-in --- + Open NodeGroups for selected (deshabilitado si selected + no es Table ni Group) +``` + +Comportamiento: +- Si hay un nodo seleccionado con `type_ref == "Table"` o `"Group"`: + llamar a `node_groups_open(g_app, sql_id, kind)` con kind + derivado del type_ref. Marcar `focus_request = true`. +- Si la seleccion es de otro tipo o no hay seleccion: item + deshabilitado con tooltip + `"Select a Table or Group node first"`. + +### Actualizar list de panels en menu + +`g_panels[]` array no se toca — NodeGroups no es panel toggleable. + +## Acceptance criteria + +- Sin nodo seleccionado: item disabled con tooltip explicativo. +- Selected = Url: item disabled con tooltip. +- Selected = Group: click abre window, queda al frente. +- Selected = Table: igual. +- Si la window ya existia abierta: el click la enfoca. +- No hay regresion en otros items del menu View. +- Tests pytest siguen verdes (no requiere tests UI). + +## TBD + +Branch `issue/0036f-view-menu-open-nodegroups`, merge `--no-ff` a +master. + +## Out of scope + +- Atajo de teclado (lo dejamos para otra fase si surge friccion). +- Submenu con lista de NodeGroups abiertas (fase 2 si llegan a ser + muchas). diff --git a/main.cpp b/main.cpp index 3d89d40..1cf092b 100644 --- a/main.cpp +++ b/main.cpp @@ -2431,6 +2431,58 @@ int main(int argc, char** argv) { } } + // 0036f — accion en el menu View que abre la NodeGroups window del + // nodo seleccionado. Disabled si la seleccion no es Table ni Group. + auto view_extras_cb = []() -> bool { + // Resolver seleccion actual: priorizamos el nodo del viewport; + // si no hay nada seleccionado en el canvas pero el inspector + // tiene una entidad cargada, usamos esa. + int sel_idx = -1; + if (!g_viewport.selection.empty()) { + int s = g_viewport.selection.front(); + if (s >= 0 && s < g_graph.node_count) sel_idx = s; + } + if (sel_idx < 0 + && g_app.insp_node_idx >= 0 + && g_app.insp_node_idx < g_graph.node_count) { + sel_idx = g_app.insp_node_idx; + } + + bool is_table = false; + bool is_group = false; + const char* sql_id = nullptr; + if (sel_idx >= 0) { + uint16_t tid = g_graph.nodes[sel_idx].type_id; + const char* tn = (tid < (uint16_t)g_graph.type_count + && g_graph.types[tid].name) + ? g_graph.types[tid].name : ""; + if (tn && std::strcmp(tn, "Table") == 0) is_table = true; + else if (tn && std::strcmp(tn, "Group") == 0) is_group = true; + sql_id = ge::entity_index_lookup( + g_idx, g_graph.nodes[sel_idx].user_data); + } + const bool enabled = (is_table || is_group) && sql_id != nullptr; + + ImGui::Separator(); + bool acted = false; + if (!enabled) ImGui::BeginDisabled(); + if (ImGui::MenuItem(TI_FOLDER_OPEN " Open NodeGroups for selected")) { + if (enabled) { + ge::NodeGroupsKind kind = is_group + ? ge::NodeGroupsKind::Group + : ge::NodeGroupsKind::Table; + ge::views_node_groups_open(g_app, sql_id, kind, + g_input_path.c_str()); + acted = true; + } + } + if (!enabled) ImGui::EndDisabled(); + if (!enabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { + ImGui::SetTooltip("Select a Table or Group node first"); + } + return acted; + }; + int rc = fn::run_app( {.title = "graph_explorer", .width = 1600, @@ -2439,6 +2491,7 @@ int main(int argc, char** argv) { .panels = g_panels, .panel_count = sizeof(g_panels) / sizeof(g_panels[0]), .layouts_cb = g_layout_storage ? &g_layout_cb : nullptr, + .view_extras = view_extras_cb, .init_gl_loader = true}, render);