9.2 KiB
Group Block Implementation Plan - Spatial Grouping
Overview
This document outlines the implementation plan for adding spatial grouping functionality to Group blocks in Expanded mode. This allows Group blocks to act as containers that automatically adopt nodes when they are dragged into their bounds, similar to the "Test Comment" nodes in the old demo.
Core Principle: Groups work through spatial containment - any node whose bounds fall within a group's m_GroupBounds becomes a child. The group can be dragged by its header to move all children together, and can be resized to adjust its containment area.
Reference Implementation
Based on ref/old_demo.cpp and ref/old_editor.cpp, the old comment/group nodes worked as follows:
ed::Group(size): Called duringed::BeginNode()/ed::EndNode()to mark the node as a group and set its group bounds- Spatial Containment: Nodes within
m_GroupBoundsare automatically considered children GetGroupedNodes(): Recursively finds all nodes within bounds usingFindNodesInRect()- Header Dragging: Dragging the group header moves the group and all children
- Resizing: Groups can be resized by dragging edges/corners, updating
m_GroupBounds - Group Hints:
BeginGroupHint()/EndGroupHint()allow rendering title bar outside bounds
Architecture Notes
- The imgui_node_editor uses internal
NodeType::Groupined::Node(separate from our applicationNodeType::Group) - Our Group blocks use application-level
NodeType::Groupbut need to integrate with editor's group system - The editor's group system is based on spatial containment - no explicit parent/child references
- Groups are sorted separately from regular nodes for rendering order
Implementation Tasks
Phase 1: Basic Group Infrastructure
Task 1.1: Integrate Group Block with Editor's Group System
Files: examples/blueprints-example/blocks/group_block.cpp
- Update
RenderExpanded()to called::Group(size)during node rendering- Call
ed::Group(groupSize)withined::BeginNode()/ed::EndNode() - Store group size in GroupBlock (initial size from node bounds or default)
- This marks the node as a group in the editor's internal system
- Call
- Add group size tracking to GroupBlock
- Add
ImVec2 m_GroupSizemember variable - Initialize with default size (e.g., 400x300) or based on node bounds
- Save/load group size in
SaveState()/LoadState()
- Add
- Verify node is recognized as group by editor
- Editor should automatically recognize it via
ed::Group()call - Check that
IsGroup()returns true for our nodes
- Editor should automatically recognize it via
Dependencies: None
Task 1.2: Implement Group Hints (Title Bar Rendering)
Files: examples/blueprints-example/blocks/group_block.cpp
- Render group title bar using
ed::BeginGroupHint()/ed::EndGroupHint()- Display group name in the hint
- Position title bar at top of group bounds (using
ed::GetGroupMin()) - Style similar to old comment nodes (semi-transparent background)
- Make title bar draggable
- Editor automatically handles dragging when
GetRegion()returnsNodeRegion::Header - Verify dragging group header moves the group
- Editor automatically handles dragging when
Dependencies: Task 1.1
Phase 2: Spatial Containment & Child Management
Task 2.1: Automatic Child Detection
Files: examples/blueprints-example/blocks/group_block.cpp, examples/blueprints-example/app-render.cpp
- Verify automatic child detection works
- Editor's
GetGroupedNodes()automatically finds nodes withinm_GroupBounds - Uses
FindNodesInRect(m_GroupBounds)internally - Test: Drag a block into group bounds → should become child
- Editor's
- Optional: Store child node IDs for quick access
- Not strictly necessary (editor handles this via
GetGroupedNodes()) - Could cache for performance if needed later
- Not strictly necessary (editor handles this via
Dependencies: Task 1.1
Task 2.2: Dragging Group Header Moves Children
Files: examples/blueprints-example/app-render.cpp (if needed)
- Verify editor automatically handles child movement
- Editor's
DragActioncallsGetGroupedNodes()when dragging group - Children are included in drag operation automatically
- Test: Drag group header → all children should move
- Editor's
- Handle edge cases
- Prevent dragging children out of group when dragging group
- Or allow it (editor might handle automatically)
Dependencies: Task 1.1, 2.1
Phase 3: Group Resizing
Task 3.1: Enable Group Resizing
Files: examples/blueprints-example/blocks/group_block.cpp, examples/blueprints-example/app-render.cpp
- Verify editor handles resizing automatically
- Editor's
SizeActionhandles group resizing whenIsGroup()is true - User can drag edges/corners to resize
- Updates both
m_Boundsandm_GroupBounds
- Editor's
- Update group size tracking
- When group is resized, update
m_GroupSizein GroupBlock - Save updated size in
SaveState()
- When group is resized, update
- Call
ed::SetGroupSize()if needed for initial size- May need to call this after creating group to set initial bounds
Dependencies: Task 1.1
Task 3.2: Auto-Resize Based on Children
Files: examples/blueprints-example/blocks/group_block.cpp
- Optional: Auto-expand group when nodes are added
- Calculate bounding box of all child nodes
- Expand
m_GroupBoundsto encompass all children (with padding) - Update
m_GroupSizeaccordingly
- Optional: Auto-shrink when nodes removed
- Recalculate bounds when child count decreases
- Shrink group to fit remaining children
Dependencies: Task 3.1, 2.1
Phase 4: Visual Polish
Task 4.1: Group Visual Styling
Files: examples/blueprints-example/blocks/group_block.cpp
- Style group background (in Expanded mode)
- Semi-transparent background (similar to old comment nodes)
- Border styling
- Visual distinction from regular blocks
- Style group title bar (hint)
- Background color
- Text styling
- Padding and spacing
Dependencies: Task 1.1, 1.2
Task 4.2: Visual Feedback for Child Nodes
Files: examples/blueprints-example/app-render.cpp (if needed)
- Optional: Visual indication of group membership
- Slight visual change for nodes inside groups
- Border color change or subtle background tint
- Or keep same (spatial containment is self-evident)
Dependencies: Task 2.1
Phase 5: Edge Cases & Validation
Task 5.1: Prevent Group Nesting Issues
Files: examples/blueprints-example/app-logic.cpp
- Validate group operations
- Prevent groups from being dragged into themselves (recursive nesting)
- Handle groups being moved into other groups (nested groups)
- Editor should handle this, but verify behavior
- Handle group deletion
- When group is deleted, children should become top-level nodes
- Or delete children too (decide on behavior)
Dependencies: Task 2.1
Task 5.2: Save/Load Group Bounds
Files: examples/blueprints-example/blocks/group_block.cpp
- Save group size to JSON
- Already added in Task 1.1, verify it works
- Load group size from JSON
- Restore size when loading saved graph
- Call
ed::SetGroupSize()if needed after loading
Dependencies: Task 1.1
Phase 6: Collapsed Mode (Future)
Task 6.1: Collapsed Mode Rendering
Files: examples/blueprints-example/blocks/group_block.cpp
- Implement
RenderCollapsed()- Show compact representation (just title, maybe pin count)
- Hide all child nodes visually
- Children still exist but not rendered
- Toggle between modes
- User can switch via context menu
- State persists across save/load
Dependencies: Task 1.1, 4.1
Key API Functions
From imgui_node_editor:
ed::Group(ImVec2 size)- Mark node as group and set group boundsed::SetGroupSize(NodeId, ImVec2)- Set group size externallyed::BeginGroupHint(NodeId)/ed::EndGroupHint()- Render title bared::GetGroupMin()/ed::GetGroupMax()- Get group bounds (in hint context)node->GetGroupedNodes(vector)- Find all nodes within bounds- Editor automatically handles dragging groups with children via
GetGroupedNodes()
Testing Checklist
- Create Group block → should show as group with title bar
- Drag block into group → should become child
- Drag group header → should move group and all children
- Resize group → should update bounds, children still contained
- Drag child out of group → should work
- Save/load → group size should persist
- Toggle display mode → should switch between expanded/collapsed
Notes
- The editor's group system is spatial-based, not reference-based
- No explicit parent/child links needed - containment is automatic
ed::Group()must be called during node rendering, not after- Group bounds (
m_GroupBounds) are separate from node bounds (m_Bounds)m_Bounds= the actual node rectangle (for header/title bar)m_GroupBounds= the area that contains child nodes (usually larger)