Repository navigation
[minor] Give NodeEditorRenderer a hook for host content in a node body - #443
Merged
Merged
Conversation
RenderNode drew a fixed node body — a title bar, one line per input pin, one right-aligned line per output pin — and offered no way in. A host wanting a value editable on the node face had to either fork the renderer, giving up zoom, hover highlighting, the measured position and dimension feedback and the pin offset publishing, or drive ImNodes directly and keep the engine for topology alone. Both cost the same things. DrawNodeBody is called inside each node, after its pins and before EndNode. ImNodes draws and sizes whatever it submits, which is the usual idiom for putting a widget on a node. It runs after the pins deliberately: PublishPinOffsets measures from rows RecordPinRow collects as each pin is submitted, so content drawn later cannot move the point a link is drawn to. Moving the call above the pins makes the new pin-position test fail, which is the guard on that. Fixes #441 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P6bYWX7oFj1YTAAw9M516w
SonarCloud's scan of the branch raised MSTEST0068 on the new test: CollectionAssert.AreEquivalent where Assert.AreSequenceEqual is the MSTest-native form. The quality gate passed regardless, but the warning is this branch's own and the sequence form is the stronger assertion. The renderer walks engine.Nodes, which holds them in creation order, so the order is deterministic and worth pinning rather than discarding: an equivalent-set assertion would have passed had the hook run the nodes in some other order, which would itself be a change worth noticing. 101/101 tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P6bYWX7oFj1YTAAw9M516w
|
This was referenced Sep 23, 2026
This was referenced Oct 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Fixes #441
What was wrong
RenderNodedrew a fixed node body — a title bar, oneImGui.Textper input pin, one right-alignedImGui.Textper output pin — with no callback anywhere on the type. A host wanting a value editable on the node face had two options and both were bad: fork the renderer, giving up zoom, hover highlighting, the measured position and dimension feedback and the pin offset publishing, all of which live in that class; or draw with raw ImNodes and keepNodeEditorEnginefor topology and physics alone, which loses the same things.ImNodes itself has no such restriction — anything submitted between
BeginNodeandEndNodeis drawn in the node and sized into it.The change
Invoked inside each node, after its pins and before
EndNode. Null draws nothing extra, so a renderer nobody configures draws exactly the node it always drew.The placement is the load-bearing part.
PublishPinOffsetsmeasures each pin from a rowRecordPinRowcollects as that pin is submitted, and the engine uses those offsets to measure a link between the points it is actually drawn between. Running the callback after every pin row is recorded means nothing a host draws can move the point a link is drawn to. Moving the call above the pins makesDrawNodeBody_LeavesThePinPositionsWhereTheyWerefail, which is what that test is for.The issue also sketches a per-pin
DrawPinBody. That one is left alone deliberately: whether an input pin should offer an inline editor depends on whether it is connected, which is a question about the graph rather than about drawing, and the issue says it needs the most thought. This PR is the per-node half, which stands on its own.Two things the doc comment and README state rather than enforce: the ID stack inside the callback is already the node's, since ImNodes pushes the node's ID as it begins it; and an exception thrown out of the callback escapes before
EndNodeand leaves the frame unusable, so a host that can fail should catch its own failures.Tests
Three in
NodeRenderingTests, which drives real frames throughImGuiAppHarness:DrawNodeBody_ReachesEveryNodeAndDrawsWithoutError— called once per node, a pinless node included, withImGui.GetCurrentContext().ErrorCountCurrentFrameat 0DrawNodeBody_ContentIsSizedIntoTheNodeAndSettles— the node grows to hold the body and then holds still, rather than growing every frameDrawNodeBody_LeavesThePinPositionsWhereTheyWere— the published pin screen positions do not move when a body is drawnVerified by reverting rather than assuming: removing the call site fails the first two (
Expected:<3>. Actual:<0>, and the node not widening); moving the call above the pins fails the third ("A body drawn under the pins moved an output pin").tests/ImGui.NodeEditor.Tests101/101 pass on this branch, on .NET 10.0.401 / Linux.Also adds a
NodeEditorRendererREADME row and a short usage section.🤖 Generated with Claude Code
https://claude.ai/code/session_01P6bYWX7oFj1YTAAw9M516w
Generated by Claude Code