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.
UCrimsonCoreSkillTreeWidget_Display
Subclass of UCrimsonSkillTreeWidget_Display (from CrimsonSkillTree). Wires CommonUI's RegisterUIActionBinding(FBindUIActionArgs(...)) route to the base widget's input-agnostic action API. Designer sets a UCrimsonInputConfig plus four gameplay tags (zoom / pan-toggle / primary / secondary), and the skill tree responds to those actions with no project C++.
Configurable properties
| Property | Category | Purpose |
|---|---|---|
InputConfig | Input | Tagged input config. Its UIInputActions array must contain entries for the four tags below. |
ZoomTag | Input | Axis1D action — mouse wheel delta. |
PanToggleTag | Input | Digital action — typically a mouse button held to pan. |
PrimaryTag | Input | Digital action — runs RequestPrimaryActionOnHoveredNode. |
SecondaryTag | Input | Digital action — runs RequestSecondaryActionOnHoveredNode. |
PanTickInterval | Input | Cursor-follow timer rate while the pan gesture is held. Default: 1/60. |
ZoomSpeedFactor | Zoom | Per-wheel-tick rate. Cooked multiplier = 1 + ZoomSpeedFactor (in) or 1 / (1 + ZoomSpeedFactor) (out). Default: 0.07. |
MinZoomScale / MaxZoomScale | Zoom | Clamp range applied to CurrentScale × CookedMultiplier before the effective multiplier is fed to ZoomAtScreenPosition. Defaults: 0.75 / 1.75. |
InitialCanvasScale | Zoom | Applied when OnGraphReady fires. 0 = leave engine default. Default: 1.5. |
Step 1 — Author UI input actions
In your UCrimsonInputConfig asset, add four entries to the UI Input Actions array. Tag each with a UIInputTag.* gameplay tag.
DA_InputConfig_PlayerUIUIInputActions[0].InputTag = UIInputTag.SkillTree.Zoom # Axis1D (mouse wheel)UIInputActions[0].InputAction = IA_SkillTree_ZoomUIInputActions[1].InputTag = UIInputTag.SkillTree.Pan # digital (RMB hold)UIInputActions[1].InputAction = IA_SkillTree_PanUIInputActions[2].InputTag = UIInputTag.SkillTree.Primary # digital (LMB click)UIInputActions[2].InputAction = IA_SkillTree_PrimaryUIInputActions[3].InputTag = UIInputTag.SkillTree.Secondary # digital (RMB click on node)UIInputActions[3].InputAction = IA_SkillTree_Secondary
bEnableEnhancedInputSupport = true in Project Settings → Common Input → Enhanced Input Support. Without it, CommonUI ignores UInputAction-based bindings.Step 2 — Create the widget Blueprint
Right-click in the Content Browser → User Widget → parent class UCrimsonCoreSkillTreeWidget_Display. In the Class Defaults panel, fill in the five properties (Input Config + four tags). Bind the SkillTreeGraph widget slot to a UCrimsonSkillTreeWidget_Graph subclass placed in the widget tree.
That's the entire integration. NativeOnInitialized on the C++ side registers all four UI action bindings; handlers route to the base widget's action API.
How pan works
Pan is a press-and-hold gesture, which doesn't fit CommonUI's discrete event model. The showcase binds the same PanToggleAction twice via FBindUIActionArgs:
| Binding | Fires on | Handler | Effect |
|---|---|---|---|
| #1 | KeyEvent = IE_Pressed | HandlePanPressed | BeginPan(cursor) + start a 60 Hz timer. |
| #2 | KeyEvent = IE_Released | HandlePanReleased | Clear the timer + EndPan(). |
The timer's TickPan reads the cursor each tick and calls UpdatePan(cursor). PanTickInterval controls the rate (default 1/60). NativeOnDeactivated defensively clears the timer and calls EndPan() in case the release event didn't fire (e.g. widget swap mid-gesture).
How zoom works
The base plugin's ApplyZoom is a pure math primitive (multiplier + anchor, no policy). Speed cooking and clamping live in the showcase — HandleZoom does:
const float WheelDelta = /* queried from UEnhancedPlayerInput::GetActionValue(ZoomAction) */;const float CurrentScale = SkillTreeGraph->GetCanvasRenderScale();const float RawMultiplier = (WheelDelta > 0) ? (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 via SetCanvasRenderScale when the graph widget broadcasts OnGraphReady (subscribed in NativeOnInitialized).
Non-CommonUI projects
If your project doesn't use CommonUI (or has bEnableEnhancedInputSupport = false), don't use this widget. Instead, give your player controller a UCrimsonInputComponent and call the base widget's action API directly from the controller's BindNativeAction handlers. The base widget doesn't care which path you take — UCrimsonSkillTreeWidget_Display::Zoom(...) / BeginPan(...) / RequestPrimaryActionOnHoveredNode() / etc. work the same either way.
Plugins/CrimsonCore/Source/CrimsonCore/Public/UserInterface/SkillTree/CrimsonCoreSkillTreeWidget_Display.h + the matching .cpp under Private/UserInterface/SkillTree/. Copy the file pair into your own plugin if you need to diverge from the showcase.