Inventory UI
CrimsonInventory, CrimsonEquipment, CrimsonUI and CrimsonInput. The data layer it renders (UCrimsonInventoryManagerComponent, UCrimsonInventoryRoutingComponent, item definitions and fragments) lives in CrimsonInventory - see CrimsonInventory -> Overview for the standalone model.A complete, controller-first inventory UI: a player inventory screen with an equipment paper doll, a two-pane container screen, a loot window for multi-item pickups, a split-stack dialog, per-item-type tooltips, a HUD capacity / currency strip, and a toast feed. Every widget is a Blueprintable C++ base under Source/CrimsonCore/Public/UserInterface/Inventory/, and every base has a shipped Blueprint under /CrimsonCore/UserInterface/Inventory/ that you can use as-is or duplicate.
/CrimsonCore/... path on this page needs that toggle.What ships
| Layer | C++ base (CrimsonCore) | Shipped asset | Role |
|---|---|---|---|
| Screens | UCrimsonCoreInventoryScreenBase -> UCrimsonCoreInventoryScreen | Screens/W_Inventory_Screen | The player's inventory beside the equipment paper doll. GA_ShowWidget_Inventory pushes it on UI.Layer.Menu. |
| Screens | UCrimsonCoreContainerScreen | Screens/W_Container_Screen | Two-pane transfer screen (player pane + chest / stash pane). One widget serves every container scope. |
| Screens | UCrimsonCoreLootWindow | Screens/W_Loot_Window | Loot list for a multi-item pickup; take one row or Take All. Closes itself when the pickup empties. |
| Panels | UCrimsonCoreInventoryPanelWidget | Panels/W_Inventory_Panel | One inventory: search box, category tabs, Sort button, one group panel per layout group, capacity meters, currency row. |
| Panels | UCrimsonCoreInventoryGroupPanel | Panels/W_Inventory_GroupPanel | One layout group rendered as Flat, Grid, or Weight. Creates a real slot widget per cell so gamepad focus navigation works. |
| Panels | UCrimsonCoreEquipmentPanel | Panels/W_Equipment_Panel | Paper doll. Collects the UCrimsonCoreEquipmentSlotWidgets you placed and binds them by SlotTag / SlotIndex. |
| Widgets | UCrimsonCoreInventorySlotWidget | Widgets/W_Inventory_Slot, Widgets/W_Inventory_SlotOverlay | One cell (or one grid item overlay when bIsGridItemOverlay is set). Click / focus / drag / drop / double-click all route to the owning screen. |
| Widgets | UCrimsonCoreEquipmentSlotWidget | Widgets/W_Equipment_Slot | One paper-doll slot. Author SlotTag (Equipment.Slot.*) and SlotIndex on the instance. |
| Widgets | UCrimsonCoreLootEntryWidget | Widgets/W_Loot_Entry | One loot row (IUserObjectListEntry) bound to a UCrimsonCoreLootEntryViewModel. |
| Widgets | UCrimsonCoreInventoryDragDropOperation | - | Mouse drag payload: entry VM, source inventory, EntryId, Count, Rotation. Derives from UCrimsonSlotBindingDragDropOperation (CrimsonUI) so an item can also be dropped on W_ActionBar. |
| Dialogs | UCrimsonCoreSplitStackDialog | Dialogs/W_SplitStack_Dialog | "How many?" dialog for the Split verb, built on the CrimsonUI dialog workflow (UCrimsonTextInputDialog): numeric house text box, slider, Less / More steppers; Confirm / Cancel come from the descriptor. |
| Tooltips | UCrimsonCoreItemTooltipWidget + _Consumable / _Equipment / _Bag | Tooltips/W_ItemTooltip, W_ItemTooltip_Consumable, W_ItemTooltip_Equipment, W_ItemTooltip_Bag | Focus / hover tooltip. The class is resolved per item (see Tooltip resolution below). |
| HUD | UCrimsonCoreInventoryHUDWidget | HUD/W_Inventory_HUD | Always-on capacity meters (Box_Capacities) and currency totals (Box_Currencies). Rebinds to the player on a timer (RebindInterval) so late possession is handled. |
| HUD | UCrimsonCoreInventoryToastWidget | HUD/W_Inventory_Toast | Queued toasts for additions, removals, constraint failures, and failed operations (bToastAdditions, bToastRemovals, bOwnInventoryOnly, MaxQueued). Driven by Crimson.Inventory.Message.StackChanged, which every net mode receives - a listen-server host and a standalone game included. |
| Settings | UCrimsonCoreInventoryUISettings | Config/DefaultCrimsonCore.ini | Project Settings -> Crimson -> Crimson Inventory UI. Tooltip classes, split dialog, drag visual, drop confirmation, DropDistance. |
| Helpers | UCrimsonCoreInventoryUIStatics | - | GetTagLabel (label from UCrimsonTagLabelSettings, else the humanized leaf) and FillCurrencyRow (rebuilds a panel of currency texts). |
| Materials | - | instances of CrimsonUI's M_UI_ChamferBox frame master: MI_UI_InvPanel, MI_UI_InvPanel_Raised, MI_UI_InvSlot_Fill, MI_UI_InvSlot_Rim, MI_UI_InvToast_Bar, MI_UI_InvToast_Bar_Error; M_UI_InvCarry_Ghost, M_UI_InvMeter_Fill, MI_UI_InvSlot_Focus, MI_UI_InvItem_Glow, MI_UI_InvPanel_Dim, MI_UI_InvEquip_Wipe | The look. Slot rims are driven by the slot widget through the RimColor and InsetBandPx parameters of its rim material instance. |
Architecture: MVVM on the CrimsonUI bases
Widgets never touch the inventory directly. Each screen owns view models built on the CrimsonUI bases (UCrimsonScreenViewModelBase, UCrimsonViewModelBase, UCrimsonEntryViewModelBase); widgets subscribe to FieldNotify properties with FCrimsonFieldBindingTracker in C++ and expose a single BlueprintImplementableEvent (On Appearance Changed, On Group Rebuilt, On Currencies Changed, ...) for visual reactions. Every mutation is a Request* call on a view model that forwards to UCrimsonInventoryRoutingComponent on the owning PlayerController, which owns the Server RPCs.
| View model | Bound to | What it exposes |
|---|---|---|
UCrimsonCoreInventoryViewModel | One UCrimsonInventoryManagerComponent (BindInventory) | Title, Groups, Capacities, Currencies, SearchText, CategoryTag, TotalEntryCount, MarkedCount, IsBound. Rebuilds from the inventory messages (stack changed, inventory updated, layout groups changed, constraint changed, currency changed). Filters: SetSearchText, SetCategoryTag. Marks: ToggleMarked, ClearMarks, GetMarkedEntryIds. Requests: RequestMove, RequestTransfer, RequestSplit, RequestDrop, RequestLock, RequestSort. OnOperationResult (native delegate) fires on the owning client for every routed result. |
UCrimsonCoreInventoryGroupViewModel | One layout group (FCrimsonLayoutGroupInfo), including nested bag groups | GroupId, ParentGroupId, OwnerEntryId, Depth, DisplayName, LayoutKind (ECrimsonCoreInventoryLayoutKind::Flat / Grid / Weight), GridSize, Capacity, Used, bActive, bSupportsRotation, and the entries placed in it. |
UCrimsonCoreInventoryEntryViewModel | One FCrimsonInventoryEntryView | EntryId, LayoutGroupId, placement, DisplayName, Description, Icon, StackCount, CountText, Footprint, bAllowRotation, Weight, bLocked, bEquipped, bIsBag, bIsEquippable, bFiltered, bMarked, RarityColor, Durability01, ItemType. |
UCrimsonCoreEquipmentViewModel + UCrimsonCoreEquipmentSlotViewModel | The pawn's ICrimsonEquipmentHost (CrimsonEquipment; implemented by UCrimsonEquipmentManagerComponent) | One slot VM per SlotTag / SlotIndex: SlotLabel, bOccupied, DisplayName, Icon, RarityColor, Durability01, bDropHighlighted, GetItemInstance(). |
UCrimsonCoreLootViewModel + UCrimsonCoreLootEntryViewModel | One ACrimsonPickupActorBase plus the destination inventory (BindPickup) | Title, bIsEmpty, one entry VM per pickup slot; RequestTake(Entry), RequestTakeAll(). |
FieldNotify property they render. Use the Blueprint only for the On ... Changed presentation events (animations, extra decoration). A UMG binding on the same property fires alongside the C++ one in an undefined order.Controller-first interaction
UCrimsonCoreInventoryScreenBase is the interaction controller every screen shares. Focus drives everything: the focused slot owns the tooltip, the action-bar prompts are re-evaluated on every focus change, and a drop preview is drawn on the slot under focus while you carry an item. Mouse users get the same results through drag and drop; both paths end in PlaceCarried / EquipCarried.
| Input | Gamepad | Mouse / keyboard | What happens |
|---|---|---|---|
| Activate | A | Click | Empty hands: pick the focused item up (carry mode). Carrying: place it on the focused slot (move / swap / merge / transfer) or on a paper-doll slot (equip). |
| Cancel | B (Back) | Escape | Cancel the carry; with nothing carried, close the screen. bBackClosesWhileCarrying closes instead of canceling. |
| Secondary | - | Right click | Context menu for the focused item via UCrimsonCoreContextMenuSubsystem - see Context Menu. On gamepad, the Use verb below runs the item's default action directly. |
| Default action | X (tap, the Use verb) | Double click | The item's default context action from ItemTypeActionSet_Core (use / equip) via ExecuteDefaultActionForItem. |
| Drag | - | Drag a slot | UCrimsonCoreInventoryDragDropOperation; dropping on a slot, a paper-doll slot, or an action-bar slot resolves the same way as carry mode. |
The ten verbs are FCrimsonCoreUIAction properties on the screen (SortAction, SplitAction, RotateAction, LockAction, DropAction, ContextMenuAction (opens the focused item's context menu, where Use lives), TakeAllAction, MarkAction, NextPanelAction, PreviousPanelAction). Each holds an InputAction, an optional CommonUI ActionTag (UI.Action.*) used for the prompt when no Input Action is set, and a DisplayName override. The screen registers each verb as a CommonUI action-bar prompt, and that binding is what executes it: a key press, a completed hold, and a click on the bar button all run the same delegate. Nothing is bound on the Enhanced Input component - CommonUI's action router sees every key before the player controller does and consumes it, so a second binding ran every verb twice. The prompt only shows while the verb is allowed (CanSort, CanSplit, ... are re-checked per focus change).
CommonUI.bEnhancedInputDynamicallyResolveHoldKeysForInputAction=1 must be set in DefaultEngine.ini ([SystemSettings]; add it if your project does not already set it). With it the router honors the Hold trigger an Input Action carries, so Split fires once when the hold completes. A hold verb is registered for a key event the router never dispatches (FBindUIActionArgs::KeyEvent = IE_DoubleClick), which keeps it off the synthesized press the router generates when the same key is released inside its quarter-second threshold - that press runs Actions instead. A release between that threshold and the hold time does nothing, and a Tap trigger on the Actions action is not consulted. While a context menu is open the screen clears its prompts and restores them on close, so the menu's row keys get the shared buttons.| Verb | Input Action (/CrimsonCore/Input/Inventory/) | Gamepad (IMC_UI_Inventory) | Keyboard | Does |
|---|---|---|---|---|
| Sort | IA_UI_Inventory_Sort | LT | T | RequestSort on the focused inventory using SortTags (empty = by type, then name). |
| Actions | IA_UI_Inventory_Use | X (tap) | F | Opens the focused item's context menu, where Use lives (ContextMenuAction). |
| Split | IA_UI_Inventory_Split | X (hold) | X | Shows SplitStackDialogClass through UCrimsonMessagingSubsystem::ShowTextInputWithClass with a UCrimsonCoreSplitStackDescriptor; a Confirmed result carries the amount, which calls RequestSplit. |
| Rotate | IA_UI_Inventory_Rotate | RT | R | Rotates the carried item (grid groups with bSupportsRotation). |
| Lock | IA_UI_Inventory_Lock | R3 (right stick click) | L | RequestLock toggle; locked items refuse drop / sort. |
| Drop | IA_UI_Inventory_Drop | View | Delete | RequestDrop (asks first when bConfirmDrop); lands DropDistance in front of the pawn. |
| Mark | IA_UI_Inventory_Mark | L3 (left stick click) | M | ToggleMarked on the focused item; the prompt flips between Mark and Unmark. MarkedCount and GetMarkedEntryIds expose the selection for your own bulk actions. |
| Take All | IA_UI_Inventory_TakeAll | Y | F | Loot window only: takes every row. |
| Next panel | IA_UI_Inventory_NextPanel | - (RB comes from the shared UI context's Next action) | Tab | Moves focus to the next registered panel root (RegisterPanelRoot). |
| Previous panel | IA_UI_Inventory_PrevPanel | - (LB comes from the shared UI context's Previous action) | not mapped in the shipped context | Moves focus to the previous panel root. |
IMC_UI_Inventory is the screen's own mapping context (Input Mode Filter: Do Not Filter, like every UI context); the screen uses InputConfig = GameAndMenu with move / look ignored so game input keeps flowing while it is open. The D-pad and left stick belong to Slate navigation across the whole UI, so no verb can live there: Mark and Lock are the two stick clicks, Take All has Y to itself, and X carries Actions (tap) and Split (hold) on one button.Setup
These steps assume CrimsonCore is installed per Integration. Add each action below to the UCrimsonExperienceActionSet - or the UCrimsonExperienceDefinition Actions array - that your project's experience loads.
1. Give the player an inventory, routing, and equipment
Add these two actions to your own experience action set:
- `CrimsonGameFeatureAction_AddInventory` ->
InventoryList[0]:ActorClass = B_CrimsonPlayerState(/CrimsonCore/Player/, or your own player state class),Layout = DA_CrimsonInventoryLayout_FlatSlot,DefaultIntConstraints = [Crimson.Inventory.Constraint.Slots = 24],RuntimeConstraints = [CrimsonInventoryConstraint_Slot],RegisteredCurrencyDataTables = [DT_Currencies].RoutingList[0]:ActorClass = B_PlayerController(/CrimsonCore/Player/, or your own player controller class). - `CrimsonGameFeatureAction_AddEquipmentSlots` ->
SlotsList[0]:ActorClass = BP_CrimsonCharacter(/CrimsonCore/Characters/, or your own pawn class),SlotLayout= oneFCrimsonEquipmentSlotDefinitionperEquipment.Slot.*tag (Head, Chest, Legs, Feet, MainHand, OffHand, Ring),bAddEquipmentManager = true.
CrimsonInventoryManagerComponent; the PlayerController has a CrimsonInventoryRoutingComponent; the pawn has a CrimsonEquipmentSlotsComponent and a CrimsonEquipmentManagerComponent.2. Open the inventory screen
The shipped GA_ShowWidget_Inventory (/CrimsonCore/Input/Abilities/) pushes W_Inventory_Screen on UI.Layer.Menu. Grant it through your ability set and map its input tag in your UCrimsonInputConfig, exactly like the other GA_ShowWidget_* abilities. To push it yourself: Get Primary Game Layout -> Push Widget To Layer Stack (LayerName = UI.Layer.Menu, WidgetClass = W_Inventory_Screen).
3. Put the HUD strip and toasts on screen
Add two CrimsonGameFeatureAction_AddWidget entries to your experience action set: W_Inventory_HUD at HUD.Slot.Inventory and W_Inventory_Toast at HUD.Slot.Toasts (both shipped in /CrimsonCore/UserInterface/Inventory/HUD/). Your HUD layout needs a CrimsonUIExtensionPoint with each of those tags.
B_CrimsonItemSpawnerPad: the HUD meter ticks up and a toast slides in with the item icon, name, and count.Crimson.Inventory.Message.StackChanged. A remote client receives that from the inventory's FastArray replication callbacks; the authority raises its own from the catch-up sweep in FinalizeServerMutation, because replication callbacks never fire on the machine that owns the data. Test on a host as well as a client - UI driven only off replication callbacks looks correct in Play As Client and is silent in Standalone.bIsRestoreOrInitialSync on the message marks a save restore or the first replicated batch. The shipped toast skips those, so loading a save does not spray a toast per restored item. Honor that flag in any widget of your own that reacts to acquisitions.4. Review the settings
UCrimsonCoreInventoryUISettings ships with working defaults in Plugins/CrimsonCore/Config/DefaultCrimsonCore.ini. Project Settings -> Crimson -> Crimson Inventory UI edits the same values.
[/Script/CrimsonCore.CrimsonCoreInventoryUISettings]DefaultTooltipWidgetClass=/CrimsonCore/UserInterface/Inventory/Tooltips/W_ItemTooltip.W_ItemTooltip_CTooltipClassByItemType=(((TagName="Item.Type.Consumable"),"/CrimsonCore/UserInterface/Inventory/Tooltips/W_ItemTooltip_Consumable.W_ItemTooltip_Consumable_C"),((TagName="Item.Type.Weapon"),"/CrimsonCore/UserInterface/Inventory/Tooltips/W_ItemTooltip_Equipment.W_ItemTooltip_Equipment_C"),((TagName="Item.Type.Armor"),"/CrimsonCore/UserInterface/Inventory/Tooltips/W_ItemTooltip_Equipment.W_ItemTooltip_Equipment_C"),((TagName="Item.Type.Bag"),"/CrimsonCore/UserInterface/Inventory/Tooltips/W_ItemTooltip_Bag.W_ItemTooltip_Bag_C"))SplitStackDialogClass=/CrimsonCore/UserInterface/Inventory/Dialogs/W_SplitStack_Dialog.W_SplitStack_Dialog_C
| Setting | Default | Effect |
|---|---|---|
DefaultTooltipWidgetClass | W_ItemTooltip | Tooltip when neither the item's Tooltip fragment nor the type map names one. |
TooltipClassByItemType | Consumable, Weapon, Armor, Bag | Tooltip class per Item.Type.* tag; exact match first, then parent tags. |
SplitStackDialogClass | W_SplitStack_Dialog | Dialog pushed by the Split verb. |
DialogLayerTag | unset | Layer the split dialog is pushed onto. |
DragVisualClass | unset | Widget that follows the cursor during a mouse drag. Unset = an icon ghost built in code. |
bConfirmDrop | true | Ask before the Drop verb destroys / drops an item. |
DropDistance | 150 | Distance in front of the pawn where dropped items land. |
Slot widget contract
UCrimsonCoreInventorySlotWidget (on UCrimsonButtonBase) computes one ECrimsonCoreInventorySlotState per refresh and applies it in C++: Empty, Occupied, Filtered (search / category mismatch; FilteredOpacity), CarrySource (the item you are holding; CarrySourceOpacity), DropValid, DropInvalid. The rim material instance receives RimColor (FocusRimColor, DropValidRimColor, DropInvalidRimColor, EmptyRimColor) and InsetBandPx (DropPreviewInsetBand). Everything below is BindWidgetOptional - bind what your design needs; W_Inventory_Slot binds all of them.
| Optional widget | Type | Driven by |
|---|---|---|
Image_Icon | UImage | Icon |
Image_Rarity | UImage | RarityColor |
Text_Count | UCommonTextBlock | CountText (hidden for 1) |
Image_Lock | UImage | bLocked |
Image_Equipped | UImage | bEquipped |
Image_Marked | UImage | bMarked |
Image_Bag | UImage | bIsBag |
Bar_Durability | UProgressBar | Durability01 (hidden when the item has no durability) |
Image_Focus | UImage | Keyboard / gamepad focus |
Grid groups place one W_Inventory_Slot per cell in Grid_Cells and one W_Inventory_SlotOverlay (bIsGridItemOverlay = true) per item in Canvas_Items, sized by footprint and rotation. UCrimsonCoreInventoryGroupPanel exposes CellSize, FlatSlotGap, GridCellGap, FlatColumns, SlotWidgetClass, GridItemWidgetClass, and bWeightListTrailingEmptyRow; its optional widgets are Text_Title, Meter_Capacity, Switcher_Content, Widget_GridContent, Grid_Cells, Canvas_Items, Box_List.
UCrimsonCoreEquipmentSlotWidget mirrors the slot: Image_Icon, Image_Rarity, Image_EmptyGlyph, Text_Label (from GetTagLabel(SlotTag)), Bar_Durability, Image_Focus, the same rim colors, and On Appearance Changed (ViewModel, bDropHighlighted).
Tooltip resolution
UCrimsonCoreItemTooltipWidget::ResolveTooltipClass(Item) picks, in order: the item definition's UCrimsonInventoryItemFragment_Tooltip widget class (CrimsonInventory), then TooltipClassByItemType for the item's Item.Type.* tag (walking up parent tags), then DefaultTooltipWidgetClass. The screen creates one instance, parents it to Canvas_Tooltip when that optional canvas is bound (otherwise the viewport), and calls SetEntry / SetEquipmentSlot on focus. Optional widgets on the base: Text_Name, Text_Type, Text_Description, Text_Count, Text_Weight, Image_Icon, Image_RarityBar, Bar_Durability; _Equipment adds Text_Slot, Text_CurrentlyEquipped, Widget_CompareRow; _Bag adds Text_Capacity, Text_Rules. On Tooltip Refreshed (Item) is the Blueprint hook.
Blueprint hooks
| Class | Event / function | Use |
|---|---|---|
UCrimsonCoreInventoryScreenBase | On Carry Changed, On Focused Entry Changed, On Operation Failed | Presentation for carry start / end, focus, and routed failures. |
UCrimsonCoreInventoryScreenBase | BeginCarry, CancelCarry, PlaceCarried, EquipCarried, PreviewDrop, Do* verbs, RegisterPanelRoot, FocusPanel | Drive the controller from your own widgets (a custom button that sorts, a panel you added to the bumper cycle). |
UCrimsonCoreInventoryPanelWidget | On Groups Rebuilt, On Currencies Changed; CategoryTags, CategoryTabButtonClass, GroupPanelClass, CapacityMeterClass, CurrencyTextStyle | Which Item.Type.* tabs appear, and the classes used for tabs, groups, and meters. |
UCrimsonCoreInventoryGroupPanel | On Group Rebuilt; FindSlotForTarget, FindItemOverlay, GetFirstSlot, RefreshAll | Decorate a rebuilt group; locate a slot for custom highlights. |
UCrimsonCoreEquipmentPanel | On Slots Bound (bHasHost) | Show an "equipment unavailable" state when the pawn has no ICrimsonEquipmentHost. |
UCrimsonCoreInventorySlotWidget / UCrimsonCoreEquipmentSlotWidget / UCrimsonCoreLootEntryWidget | On Appearance Changed | Additive visuals after the C++ pass (animations, glow). |
UCrimsonCoreInventoryToastWidget | Show Toast (Toast), then call NotifyToastFinished | Play your toast animation; the queue advances when you call NotifyToastFinished. |
UCrimsonCoreInventoryHUDWidget | On Currencies Changed, RebindToPlayer | Currency presentation; force a rebind after a pawn swap. |
UCrimsonCoreSplitStackDialog | On Amount Changed (NewAmount, MaxAmount); SetAmount, Step; Confirm / Cancel are the descriptor buttons (AcceptTextInput / CancelTextInput) | Custom stepper visuals. |
Multiplayer
- Server:
UCrimsonInventoryManagerComponenton the PlayerState owns the items and replicates them; everyRequest*ends in a Server RPC onUCrimsonInventoryRoutingComponent, which validates and applies. The UI never mutates state. - Owning client: the screens, carry mode, drag-drop, tooltips, HUD strip, and toasts.
PreviewDropis a client-side dry run for highlighting only; the server still decides. - All clients: nothing - inventory UI is never pushed on simulated proxies.
- Late join / respawn: view models rebuild from replicated state on bind, and the HUD widget rebinds on
RebindIntervaluntil it finds the player's inventory.
See also
- Pickup Actor & Loot Window
- Inventory Showcases
- Context Menu
- GameFeatureActions
- CrimsonInventory -> Overview
- CrimsonUI -> Overview