From 161496a305899423b59199e3d9174894b2d01b94 Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Fri, 24 Apr 2026 22:37:56 +0200 Subject: [PATCH] fix(shaders_lab): Output connectable after drop + working color picker Two bugs: 1. Dropped nodes were pushed to the end of the pipeline, but the Output node already sat there from startup. The cycle validator and the compiler only look for sources at indices strictly lower than the target, so new nodes were invisible to the Output. Fix: insert dropped nodes before the first Output; topo_sort also stable-moves Output nodes to the back. 2. ColorEdit3 with default flags rendered RGB text inputs alongside the swatch; clicking them dragged the node instead of opening the picker. Fix: NoInputs + NoLabel leaves only the swatch (a single item), and ed::Suspend/Resume wraps the call so the popup isn't clipped to the node or captured by the canvas. Co-Authored-By: Claude Opus 4.7 (1M context) --- cpp/functions/gfx/dag_node_editor.cpp | 28 +++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/cpp/functions/gfx/dag_node_editor.cpp b/cpp/functions/gfx/dag_node_editor.cpp index 2685d02b..0c7538c4 100644 --- a/cpp/functions/gfx/dag_node_editor.cpp +++ b/cpp/functions/gfx/dag_node_editor.cpp @@ -117,6 +117,14 @@ static bool topo_sort(std::vector& pipeline) { std::vector sorted; sorted.reserve(static_cast(n)); for (int idx : order) sorted.push_back(pipeline[static_cast(idx)]); + + // Force Output nodes to the back so all their dependencies live at smaller + // indices (compiler and cycle validator search strictly before the target). + std::stable_partition(sorted.begin(), sorted.end(), [](const DagStep& s) { + const DagNodeDef* d = dag_find(s.name); + return !(d && d->kind == DagKind::Output); + }); + pipeline = std::move(sorted); return true; } @@ -174,7 +182,15 @@ bool dag_node_editor(std::vector& pipeline) { ImVec2 canvas_pos = ed::ScreenToCanvas(s_pending_add_pos); step.editor_pos_x = canvas_pos.x; step.editor_pos_y = canvas_pos.y; - pipeline.push_back(step); + // Insert before the Output node so the Output stays at the back; + // otherwise new nodes can never be wired into it (compiler and + // cycle check only search indices strictly before the target). + auto insert_it = pipeline.end(); + for (auto it = pipeline.begin(); it != pipeline.end(); ++it) { + const DagNodeDef* d = dag_find(it->name); + if (d && d->kind == DagKind::Output) { insert_it = it; break; } + } + pipeline.insert(insert_it, step); changed = true; } s_pending_add = false; @@ -246,7 +262,15 @@ bool dag_node_editor(std::vector& pipeline) { } else if (ctrl.kind == DagControl::Kind::Color) { int pr = ctrl.param_idx[0]; if (pr >= 0 && pr + 2 < 4) { - ImGui::ColorEdit3(uid_lbl, &step.params[static_cast(pr)]); + // Suspend the node editor while the color picker popup is + // open so its clicks don't reach the canvas (and so the + // popup isn't clipped to the node bounds). + ed::Suspend(); + ImGui::ColorEdit3(uid_lbl, &step.params[static_cast(pr)], + ImGuiColorEditFlags_NoInputs | + ImGuiColorEditFlags_NoLabel | + ImGuiColorEditFlags_AlphaBar); + ed::Resume(); } } }