How-To: Use Your Own Node Widget Class

Goal (optional): build skill tree nodes on your widget base class - your project's button base, a CommonUI button, anything - instead of subclassing this plugin's UCrimsonSkillTreeWidget_Node. You keep whatever your base already gives you (focus handling, styling, pooling, analytics) and lose nothing from the skill tree.

You do not need this page to ship
Subclassing UCrimsonSkillTreeWidget_Node is a perfectly good default and is what Quick Start does - it already implements everything below. Come here only when you want your nodes to inherit from something else.

1. Implement the contract

A node is anything that is a UUserWidget and implements `ICrimsonSkillTreeNodeWidget`. That is the whole requirement - there is no base class to inherit and no registration step.

FunctionYou mustWhat it is for
InitializeSkillNode(Node)Implement itThe host hands you the data node once, right after spawn. Store it, bind OnNodeStateChanged to stay reactive, and paint the initial look.
GetSkillNodeConnectionAnchorInset()Only if your shape does not fill its slotInset from the slot rect to the visible shape, so connection lines meet the shape edge instead of the padding. Defaults to zero.
Both have default implementations
Neither function has to be stubbed. A minimal node implements InitializeSkillNode and nothing else.

In your Widget Blueprint: Class Settings -> Interfaces -> Add -> Crimson Skill Tree Node Widget. Initialize Skill Node then appears in the My Blueprint panel under Interfaces - double-click to implement it.

Class Settings -> Interfaces -> Add -> Crimson Skill Tree Node Widget.
  1. In Initialize Skill Node, promote the In Node Data pin to a variable (e.g. SkillNodeData).
  2. From that variable, Bind Event to On Node State Changed and route it to a custom event that repaints your visuals.
  3. Call that repaint event once immediately, so the node starts in the right state instead of waiting for the first change.
  4. To act on the node, call Request Skill Node Action (from Crimson Skill Tree UI Statics) with your stored node and an action type.
Unbind on destruct
Whatever you bind in Initialize Skill Node, unbind in Event Destruct. A node widget outliving its binding is the usual cause of stale visuals after a respec or a level change.

2. Assign it

Nothing changes here - your class appears wherever a node widget class is picked, because those pickers filter by the interface, not by a base class. Precedence is per-node override, then node-type tag map, then the asset default.

WhereEffect
GraphConfig.DefaultNodeWidgetClassEvery node in the tree
GraphConfig.NodeTypeToWidgetClassMapEvery node carrying a given NodeTypeTag
A Visual node's Preview Widget ClassThat one node only
Verify
If your class does not show up in the picker, it does not implement the interface yet. Compiling the asset also reports it: validation raises an error naming the class.

3. Set the anchor inset as a class default

If your node's visible shape is smaller than its slot - a hexagon or a circle inside padding - set ConnectionAnchorInset so lines meet the shape. It must be a class default (EditDefaultsOnly), because the Visual Graph reads it off the Class Default Object to draw editor-side connections. A value that depends on per-instance runtime state will make the editor and the game disagree about where lines attach.

Anchoring treats the inset rect as an ellipse
So a circular node usually wants an even inset on all four sides rather than a tight bounding box.

4. What you take over

The convenience base does a few extra things for free. On your own base you own them - each is a few lines, and none is required for the tree to work.

BehaviourOn your own widget
ReactivityYou bind OnNodeStateChanged yourself (step 1)
TooltipsSpawn whatever tooltip you like on hover - there is no required tooltip type
Hover visualsNativeOnMouseEnter / NativeOnMouseLeave, or your base's own hover/focus hooks
Requesting actionsUCrimsonSkillTreeUIStatics::RequestSkillNodeAction

Worked example

UCrimsonCoreSkillTreeNodeButton (in the CrimsonCore plugin) is exactly this pattern: it derives from UCrimsonButtonBase rather than from any CrimsonSkillTree class, and gets gamepad focus, navigation and per-input-method styling from that base - none of which the plugin's own node base provides. It reports selection and activation outward and lets the owning screen decide what a press means, which is the recommended split.

Let the screen resolve actions
Prefer reporting that the node was pressed over calling RequestSkillNodeAction directly from the widget. Which action a press maps to - activate, increment, refund - is a game rule that depends on the node's current state, and keeping it in one place stops the mouse and gamepad paths from drifting apart.

See also: How-To: Display the Skill Tree (hosting the graph and wiring input), How-To: Drive Node Actions (what the manager does with a request), and the API Reference entry for ICrimsonSkillTreeNodeWidget.