10 KiB
10 KiB
Parameter Node Shortcuts Feature Plan
Overview
Parameter nodes can now act as "sources" and have "shortcut" clones that reference them. This allows creating multiple convenient access points to the same parameter value across the graph without physical links.
Core Concepts
- Source Node: A parameter node marked as a source (
m_IsSource = true) that acts as the master copy - Shortcut/Clone: A parameter node that references a source node (
m_SourceIDset) and syncs its value from the source - Value Synchronization: When a shortcut's value/name/type is edited, it updates the source, and all shortcuts automatically receive updates from the source
Feature Requirements
1. Source Node Toggle
- Context menu option "As Source" toggles
m_IsSourceflag - Only visible for parameter nodes
- When enabled, shows additional menu item "Create Shortcut"
2. Create Shortcut
- Only visible when node is a source (
m_IsSource == true) - Creates a new parameter node (clone) with:
- Same type as source
- Same value as source (retrieved via
Run()) - Same name as source (with optional suffix for uniqueness)
m_SourceIDset to source node's ID- Positioned next to source node (offset right by ~200px)
3. Visual Differentiation
- Source nodes: Render with different border color/style (e.g., gold/bright border, or "★" indicator)
- Shortcut nodes: Render with different background color/style (e.g., slightly dimmed, or "📋" indicator)
- Visual cues should be subtle but clear
4. Value Synchronization
- Shortcut → Source: When shortcut's value/name/type is edited, update source immediately
- Source → Shortcuts: When source's value/name/type is edited, all shortcuts sync (via
Run()mechanism) - Use existing
Run()infrastructure for value propagation
5. Source Deletion Handling
- When source node is deleted, shortcuts remain but become independent
- Set
m_SourceID = 0on all affected shortcuts - Clear
m_IsSource = falseif somehow a shortcut was marked as source
6. Save/Load
- Save
m_IsSourceflag - Save
m_SourceID(0 if not a shortcut) - On load, validate source still exists; if not, clear
m_SourceIDandm_IsSource
Affected Files & Classes
Core Parameter Node Files
-
examples/blueprints-example/blocks/parameter_node.h- Add
m_IsSourcemember (bool) - Add
m_SourceIDmember (int, 0 if not a shortcut) - Add getter/setter methods for source management
- Add method to find source node via App*
- Add method to create shortcut from this node
- Add
-
examples/blueprints-example/blocks/parameter_node.cpp- Modify
OnMenu()to add "As Source" toggle - Modify
OnMenu()to add "Create Shortcut" (conditional onm_IsSource) - Modify
Render*()methods to show visual differentiation - Modify
SetBool/Int/Float/String()to sync to source if this is a shortcut - Modify
SetName()to sync to source if this is a shortcut - Modify
Run()to check if source exists, sync from source if shortcut - Modify
SaveState()to savem_IsSourceandm_SourceID - Modify
LoadState()to loadm_IsSourceandm_SourceID, validate source exists - Add helper method
GetSourceNode()to find source via App* - Add helper method
SyncValueToSource()for shortcut→source updates - Add helper method
SyncValueFromSource()for source→shortcut updates - Add helper method
CreateShortcut()to spawn clone at offset position
- Modify
App Logic Files
examples/blueprints-example/app-logic.cpp- Modify
LoadGraph()to validate source nodes exist when loading shortcuts - Add helper method
FindAllShortcutsForSource()(optional, for cleanup) - May need to handle source deletion in node removal code
- Modify
App Render Files
examples/blueprints-example/app-render.cpp- Context menu already calls
ParameterInstance->OnMenu(), no changes needed - Node deletion already handled, but may want to add cleanup hook
- Context menu already calls
Node Types/Structure
examples/blueprints-example/types.h(if needed)- May need to add source-related fields to Node struct (optional, prefer keeping in ParameterNode)
Save/Load JSON Format
- JSON Structure Addition:
{ "node_type": "parameter", "param_type": 2, "is_source": true, // NEW "source_id": 0, // NEW (0 = not a shortcut, >0 = source node ID) "value": 42, "display_mode": 1 }
Implementation Tasks
Phase 1: Core Data Structures
- Add
m_IsSourceboolean member toParameterNodeclass - Add
m_SourceIDint member toParameterNodeclass (default 0) - Initialize both members in constructor
- Add getter/setter methods:
IsSource(),SetIsSource(),GetSourceID(),SetSourceID()
Phase 2: Context Menu Integration
- Modify
ParameterNode::OnMenu()to add "As Source" menu item (checkbox style) - Toggle
m_IsSourcewhen menu item clicked - Add conditional "Create Shortcut" menu item (only if
m_IsSource == true) - Implement shortcut creation logic when menu item clicked
Phase 3: Shortcut Creation
- Create
ParameterNode::CreateShortcut()helper method - Get source node's position using
ed::GetNodePosition() - Calculate offset position (source.x + 200, source.y)
- Call
App::SpawnParameterNode()with source's type and calculated position - Set new node's
m_SourceIDto source node's ID - Copy source's value to new node via
Set*()methods - Set new node's name to source's name (or add suffix like "Copy")
- Set new node's position using
ed::SetNodePosition()
Phase 4: Value Synchronization (Shortcut → Source)
- Modify
ParameterNode::SetBool()to check ifm_SourceID > 0 - If shortcut, find source node via
App::FindNode() - Update source's value and sync to source's
ParameterInstance - Similarly modify
SetInt(),SetFloat(),SetString() - Modify
SetName()to sync to source if shortcut - Note: Type changes should propagate too, but may require node recreation (advanced)
Phase 5: Value Synchronization (Source → Shortcuts)
- Add helper method
SyncAllShortcuts()inParameterNode(called from source) - In source's
SetBool/Int/Float/String(), ifm_IsSource == true, find all shortcuts - Iterate through all nodes in container, find ones with
m_SourceID == this->m_ID - Update each shortcut's value via
Set*()methods (avoid recursion by checking source) - Alternative: Use existing
Run()mechanism - shortcuts callRun()which reads from source
Phase 6: Visual Differentiation
- Modify
RenderNameOnly()to show indicator for source/shortcut - Modify
RenderNameAndValue()to show indicator and/or different border color - Modify
RenderSmallBox()to show indicator - Source nodes: Gold/bright border or "★" icon
- Shortcut nodes: Dimmed background or "📋" icon or different border style
Phase 7: Save/Load
- Modify
ParameterNode::SaveState()to savem_IsSourceas boolean - Modify
ParameterNode::SaveState()to savem_SourceID(0 if not a shortcut) - Modify
ParameterNode::LoadState()to loadm_IsSource - Modify
ParameterNode::LoadState()to loadm_SourceID - In
LoadState(), validate source node exists:app->FindNode(ed::NodeId(m_SourceID)) - If source doesn't exist, clear
m_SourceID = 0andm_IsSource = false(orphaned shortcut)
Phase 8: Source Deletion Handling
- When node is deleted, check if it's a source (
m_IsSource == true) - Find all shortcuts: iterate nodes, find ones with
m_SourceID == deleted_node_id - Clear
m_SourceID = 0andm_IsSource = falseon all found shortcuts - Implementation: Hook into node deletion in
app-render.cppor `app-logic.cpp - Alternative: Check in
ParameterNode::Run()- if source not found, clearm_SourceID
Phase 9: Edge Cases & Polish
- Handle circular references (source→shortcut→source, prevent infinite loops)
- Handle source being a shortcut itself (prevent chains, keep flat)
- Update tooltips or help text to explain source/shortcut concept
- Test save/load with source nodes
- Test save/load with shortcuts
- Test source deletion with active shortcuts
- Test creating shortcuts from shortcuts (should reference original source)
Technical Notes
Source ID Management
m_SourceID = 0means "not a shortcut"m_SourceID > 0means "shortcut, source node ID is m_SourceID"- Source nodes have
m_IsSource = trueandm_SourceID = 0(they are not shortcuts of themselves)
Value Sync Direction
- Shortcut edits → Source: Direct update via
Set*()methods - Source edits → Shortcuts: Can use existing
Run()mechanism or direct updates - Recommendation: Use
Run()for source→shortcut (already handles connected inputs), direct update for shortcut→source
Position Calculation
- Offset new shortcut to the right of source:
sourcePos.x + 200.0f, sourcePos.y - Use
ed::GetNodePosition()anded::SetNodePosition()for positioning - Consider node size for better spacing
Finding Shortcuts
- When source updates, need to find all shortcuts
- Iterate through
container->GetNodes(app)orapp->GetActiveRootContainer()->GetNodes(app) - Check each node:
node->Type == NodeType::Parameter && node->ParameterInstance && node->ParameterInstance->GetSourceID() == source_id
Testing Checklist
- Create parameter node, mark as source
- Create shortcut from source
- Edit source value, verify shortcut updates
- Edit shortcut value, verify source updates
- Create multiple shortcuts from same source
- Edit source name, verify shortcuts sync (if implemented)
- Delete source, verify shortcuts become independent
- Save graph with source and shortcuts
- Load graph with source and shortcuts
- Load graph where source no longer exists (orphaned shortcuts)
- Visual indicators show correctly for source/shortcut
- Context menu shows/hides correctly
Future Enhancements (Out of Scope)
- Type change propagation (requires node recreation)
- Name sync from source to shortcuts
- Batch operations (create multiple shortcuts at once)
- Shortcut manager UI (list all shortcuts for a source)
- Shortcut-to-source navigation (jump to source)
- Breaking shortcut connection (make shortcut independent)