fix: promote button funciona + placement direccional mas cercano

Dos bugs reportados tras 0036d/0037:

1. Promote button en NodeGroups window kind=Group no respondia al
   click. Causa: el Selectable con SpanAllColumns + AllowDoubleClick
   se tragaba el click destinado al SmallButton(TI_ARROW_UP) sobre
   la misma fila. ImGui tiene un flag dedicado para esto:
   AllowOverlap. Anyadido al Selectable y los botones recuperan los
   clicks. Mismo fix beneficia al kind=Table porque los botones
   "promote" y "expand" de DuckDB rows estaban en la misma situacion
   silenciosa.

2. Placement direccional de 0037 enviaba hijos hasta r=400 cuando
   habia colisiones, "muy lejos" segun el usuario. Ajustes:
   - Anillos mas cercanos: {60,90,120,150,180,210,240} en vez de
     {80,140,200,280,400}. Maximo 240px del source.
   - Mas anillos disponibles (7 vs 5) — fan-out gradual sin saltar
     bruscamente de 80 a 140.
   - Espaciado entre hermanos en arco usa cluster_min_dist=35 en
     vez de min_dist=60. Permite mas hijos por anillo dentro del
     arco de 45 grados (cap @ r=240 = 5 vs 3 antes).
   - Para 10-15 hijos tipicos los inner rings cubren todo dentro
     de 200px del source.

Build limpio. Tests WSL 102 / Windows 91 + 11 skipped.

Bonus: borrado /home/lucas/fn_registry/cpp/registry.db (vacio, 0
bytes, creado por algun binario con flag O_CREAT) — violacion de
db_locations.md (registry.db solo en raiz del repo). Era el motivo
de un test flaky de python_runtime_resolver.
This commit is contained in:
2026-05-04 01:36:54 +02:00
parent 28548e053d
commit 616c46297b
2 changed files with 21 additions and 6 deletions
+16 -5
View File
@@ -293,9 +293,17 @@ static void place_orphans_near_neighbors(GraphData& g, float min_dist,
float cam_cy = 0.0f,
float cam_radius = 120.0f) {
if (g.node_count == 0) return;
const float neighbor_radii[] = {80.0f, 140.0f, 200.0f, 280.0f, 400.0f};
// Anillos para placement direccional (issue 0037 + ajuste): radios
// mas cercanos que la version 360 original (max 240 vs 400) para
// que un grupo de 10-15 hijos quepa cerca del source y no se
// disperse media pantalla. La distancia entre hermanos en el arco
// (cluster_min_dist) es menor que el min_dist de colision general
// — los hermanos pueden estar mas apretados entre si que un nodo
// suelto, porque conceptualmente forman un cluster.
const float neighbor_radii[] = {60.0f, 90.0f, 120.0f, 150.0f, 180.0f, 210.0f, 240.0f};
const int n_neighbor_radii = (int)(sizeof(neighbor_radii) /
sizeof(neighbor_radii[0]));
const float cluster_min_dist = 35.0f; // espaciado entre hermanos en arco
// Anillos crecientes alrededor de la camara — empieza pequeno (cam_radius
// base ~viewport/zoom) para mantener los nuevos cerca del foco visual.
@@ -394,13 +402,16 @@ static void place_orphans_near_neighbors(GraphData& g, float min_dist,
}
// --- Distribucion en abanico: capacidad por anillo restringida ---
// cap_r = max(2, arc_span * r / min_dist). Para min_dist=60,
// r=80 -> cap=2; r=140 -> 2; r=200 -> 3; etc. Ajustamos a un
// minimo de 2 para que kids unitarios o duos no degeneren.
// cap_r = max(2, arc_span * r / cluster_min_dist). Con
// arc_span=45 y cluster_min_dist=35:
// r=60 ->2, r=90 ->2, r=120 ->2, r=150 ->3, r=180 ->4,
// r=210 ->4, r=240 ->5 (total 22 hijos en max 240px).
// Para 10-15 hijos tipicos los inner rings cubren todo,
// los hijos quedan visiblemente cerca del source.
size_t n = kids.size();
size_t accum = 0;
int ri = 0;
size_t cap_r = (size_t)std::max(2.0f, arc_span * neighbor_radii[ri] / min_dist);
size_t cap_r = (size_t)std::max(2.0f, arc_span * neighbor_radii[ri] / cluster_min_dist);
for (size_t k = 0; k < n; ++k) {
// Avanzar de anillo cuando el actual se ha llenado.
while (k >= accum + cap_r && ri < n_neighbor_radii - 1) {
+5 -1
View File
@@ -2084,8 +2084,12 @@ void views_node_groups_window(AppState& app) {
// Selectable spanning para que el doble-click y el right-click
// funcionen sobre toda la fila, no solo el texto.
// AllowOverlap obligatorio para que botones colocados despues
// (Promote en kind=Group, ver issue 0036d) reciban sus clicks
// en lugar de que el Selectable se los trague.
ImGuiSelectableFlags sf = ImGuiSelectableFlags_SpanAllColumns
| ImGuiSelectableFlags_AllowDoubleClick;
| ImGuiSelectableFlags_AllowDoubleClick
| ImGuiSelectableFlags_AllowOverlap;
ImGui::Selectable(row.id.c_str(), false, sf);
if (is_group) {