UI Showcases

CrimsonCore is the aggregator layer — the only place allowed to mix CrimsonInput with a feature plugin's UI in one class. The widgets here are reference implementations that demonstrate the recommended wiring pattern; subclass them in Blueprint to use as-is, or copy them as a starting point for project-specific UIs.

Cardinal rule preserved
CrimsonSkillTree (and every other Tier-1 plugin) is free of any CrimsonInput dependency. The cross-plugin wiring lives here, where it's allowed.

UCrimsonCoreSkillTreeScreen

The ready-made skill tree screen. It is a UCrimsonActivatableWidget (CrimsonUI) that hosts a UCrimsonCoreSkillTreeGraph canvas, resolves the player's UCrimsonSkillTreeManager, drives a UCrimsonCoreSkillTreeViewModel for the detail panel, and owns all input. It lives here rather than in CrimsonSkillTree because it needs CrimsonUI, CrimsonInput and the MVVM stack at once - which only CrimsonCore is allowed to combine.

Input binds on Enhanced Input, not CommonUI UI-actions
Every binding goes on the owning controller's UEnhancedInputComponent. That gives one consistent path for analog axis values and press/hold events while the menu holds mouse capture. CommonUI remains the activation/screen framework but is out of the input path, and the screen deliberately does not take focus - CapturePermanently keeps focus on the viewport so the capture cannot collapse on a click.

Configurable properties

PropertyCategoryPurpose
UIInputConfigInputTagged input config. Its UIInputActions array must contain an entry per tag below.
ZoomTagInputAxis1D action - wheel delta.
PanMoveTagInputAxis2D action - pointer delta. Only applied while pan is active.
PanToggleTagInputDigital action - held to enable panning.
PrimaryTagInputDigital action - runs RequestPrimaryActionOnTarget.
SecondaryTagInputDigital action - runs RequestSecondaryActionOnTarget.
DefaultSkillTreeTypeTagSkill TreeWhich configured tree to display on activation.
ZoomSpeedFactorZoomPer-notch zoom step (default 0.07).
MinZoomScale / MaxZoomScaleZoomClamp range (defaults 0.75 / 1.75).
InitialCanvasScaleZoomScale applied once the graph reports ready. 0 leaves the engine default.
Required BindWidget
The Blueprint's widget tree must contain a UCrimsonCoreSkillTreeGraph named `SkillTreeGraph`. It is a hard BindWidget, so the Blueprint will not compile without it.

Step 1 - Author UI input actions

In your UCrimsonInputConfig asset, add five entries to the UI Input Actions array, each tagged with a UIInputTag. The screen resolves its UInputAction objects from these tags at activation, so rebinding is pure data - any key, mouse button, wheel axis or gamepad control works.

text
DA_InputConfig_PlayerUI
UIInputActions[0].InputTag = UIInputTag.SkillTree.Zoom # Axis1D (mouse wheel)
UIInputActions[0].InputAction = IA_SkillTree_Zoom
UIInputActions[1].InputTag = UIInputTag.SkillTree.PanMove # Axis2D (mouse XY)
UIInputActions[1].InputAction = IA_SkillTree_PanMove
UIInputActions[2].InputTag = UIInputTag.SkillTree.PanToggle # digital (RMB hold)
UIInputActions[2].InputAction = IA_SkillTree_PanToggle
UIInputActions[3].InputTag = UIInputTag.SkillTree.Primary # digital (LMB / gamepad accept)
UIInputActions[3].InputAction = IA_SkillTree_Primary
UIInputActions[4].InputTag = UIInputTag.SkillTree.Secondary # digital (RMB click / gamepad back)
UIInputActions[4].InputAction = IA_SkillTree_Secondary

Step 2 - Create the widget Blueprint

Right-click in the Content Browser -> User Widget -> parent class UCrimsonCoreSkillTreeScreen. Place a UCrimsonCoreSkillTreeGraph in the widget tree and name it SkillTreeGraph. In Class Defaults, set the Input Config plus the five tags, and the default skill tree type tag. Push the screen onto your CommonUI Menu layer.

That is the whole integration. NativeOnActivated binds the Enhanced Input actions and NativeOnDeactivated unbinds them; the screen resolves the manager, populates the canvas and binds the view model on its own.

How pan works

Pan is a held gesture expressed as two bindings rather than a polling timer. PanToggle (Started / Completed) flips an active flag, and PanMove delivers an Axis2D pointer delta which is applied only while that flag is set. Because the delta arrives with the input event, there is no cursor polling and no tick rate to tune.

BindingTriggerHandlerEffect
PanToggleTagStartedHandlePanPressedMarks pan active.
PanToggleTagCompletedHandlePanReleasedMarks pan inactive.
PanMoveTagTriggered (Axis2D)HandlePanMoveEIApplies the delta via PanByViewportDelta, ignored unless pan is active.

How zoom works

UCrimsonCoreSkillTreeGraph::ApplyZoom is a pure math primitive - a multiplier plus a screen-space anchor, with no speed or clamp policy. The screen owns the policy: it converts the wheel delta into a multiplier using ZoomSpeedFactor, clamps the resulting scale between MinZoomScale and MaxZoomScale, then passes the effective multiplier down.

cpp
const float WheelDelta = Value.Get<float>();
const float CurrentScale = SkillTreeGraph->GetCanvasRenderScale();
const float RawMultiplier = (WheelDelta > 0.f) ? (1.f + ZoomSpeedFactor)
: (1.f / (1.f + ZoomSpeedFactor));
const float Desired = FMath::Clamp(CurrentScale * RawMultiplier, MinZoomScale, MaxZoomScale);
const float Effective = Desired / CurrentScale;
ZoomAtScreenPosition(Effective, MousePos);

InitialCanvasScale is applied separately through SetCanvasRenderScale when the graph broadcasts OnGraphReady.

Node widgets: any base class you like

The graph spawns node and decoration widgets as plain UUserWidget and talks to them only through CrimsonSkillTree's ICrimsonSkillTreeNodeWidget / ICrimsonSkillTreeDecorationWidget contracts, so a project may build nodes on any widget base. See CrimsonSkillTree -> How-To: Use Your Own Node Widget Class.

`UCrimsonCoreSkillTreeNodeButton` is the worked example shipped here: a node built on UCrimsonButtonBase instead of on any CrimsonSkillTree class, which is what gives it gamepad focus, navigation and per-input-method styling for free. Assign it like any other node widget class, via GraphConfig.DefaultNodeWidgetClass or NodeTypeToWidgetClassMap.

It reports, the screen decides
The button broadcasts OnSkillNodeSelected / OnSkillNodeActivated and resolves no action itself. The graph forwards those to OnNodeWidgetSelected / OnNodeWidgetActivated, and the screen turns them into SetTargetNode + RequestPrimaryActionOnTarget. Action resolution stays in one place, so the mouse and gamepad paths cannot drift apart.

Non-CommonUI projects

The action API is input-agnostic and public: ZoomAtScreenPosition, PanByViewportDelta, RecenterOnRoot, RecenterOnContent, SetTargetNode, RequestPrimaryActionOnTarget, RequestSecondaryActionOnTarget and RequestActionOnNode are all BlueprintCallable. If you would rather not use this screen, host UCrimsonCoreSkillTreeGraph in your own widget and drive that same API from wherever your input lives.

Source location
Plugins/CrimsonCore/Source/CrimsonCore/Public/UserInterface/SkillTree/ - Screens/CrimsonCoreSkillTreeScreen.h, Graph/CrimsonCoreSkillTreeGraph.h, Nodes/CrimsonCoreSkillTreeNodeButton.h, ViewModels/CrimsonCoreSkillTreeViewModel.h, with the matching .cpp files under Private/UserInterface/SkillTree/.