Inventory UI

Requires every Crimson plugin
CrimsonCore is free to download, but compiles only with all Crimson plugins installed. The inventory UI below bridges 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.

Show Plugin Content
Plugin assets are hidden in the Content Browser until you tick Settings -> Show Plugin Content. Every /CrimsonCore/... path on this page needs that toggle.
W_Inventory_Screen in PIE: the inventory panel (search, category tabs, Sort, groups, capacity meters, currencies) beside the equipment paper doll, with the CommonUI action bar showing the verb prompts.

What ships

LayerC++ base (CrimsonCore)Shipped assetRole
ScreensUCrimsonCoreInventoryScreenBase -> UCrimsonCoreInventoryScreenScreens/W_Inventory_ScreenThe player's inventory beside the equipment paper doll. GA_ShowWidget_Inventory pushes it on UI.Layer.Menu.
ScreensUCrimsonCoreContainerScreenScreens/W_Container_ScreenTwo-pane transfer screen (player pane + chest / stash pane). One widget serves every container scope.
ScreensUCrimsonCoreLootWindowScreens/W_Loot_WindowLoot list for a multi-item pickup; take one row or Take All. Closes itself when the pickup empties.
PanelsUCrimsonCoreInventoryPanelWidgetPanels/W_Inventory_PanelOne inventory: search box, category tabs, Sort button, one group panel per layout group, capacity meters, currency row.
PanelsUCrimsonCoreInventoryGroupPanelPanels/W_Inventory_GroupPanelOne layout group rendered as Flat, Grid, or Weight. Creates a real slot widget per cell so gamepad focus navigation works.
PanelsUCrimsonCoreEquipmentPanelPanels/W_Equipment_PanelPaper doll. Collects the UCrimsonCoreEquipmentSlotWidgets you placed and binds them by SlotTag / SlotIndex.
WidgetsUCrimsonCoreInventorySlotWidgetWidgets/W_Inventory_Slot, Widgets/W_Inventory_SlotOverlayOne cell (or one grid item overlay when bIsGridItemOverlay is set). Click / focus / drag / drop / double-click all route to the owning screen.
WidgetsUCrimsonCoreEquipmentSlotWidgetWidgets/W_Equipment_SlotOne paper-doll slot. Author SlotTag (Equipment.Slot.*) and SlotIndex on the instance.
WidgetsUCrimsonCoreLootEntryWidgetWidgets/W_Loot_EntryOne loot row (IUserObjectListEntry) bound to a UCrimsonCoreLootEntryViewModel.
WidgetsUCrimsonCoreInventoryDragDropOperation-Mouse drag payload: entry VM, source inventory, EntryId, Count, Rotation. Derives from UCrimsonSlotBindingDragDropOperation (CrimsonUI) so an item can also be dropped on W_ActionBar.
DialogsUCrimsonCoreSplitStackDialogDialogs/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.
TooltipsUCrimsonCoreItemTooltipWidget + _Consumable / _Equipment / _BagTooltips/W_ItemTooltip, W_ItemTooltip_Consumable, W_ItemTooltip_Equipment, W_ItemTooltip_BagFocus / hover tooltip. The class is resolved per item (see Tooltip resolution below).
HUDUCrimsonCoreInventoryHUDWidgetHUD/W_Inventory_HUDAlways-on capacity meters (Box_Capacities) and currency totals (Box_Currencies). Rebinds to the player on a timer (RebindInterval) so late possession is handled.
HUDUCrimsonCoreInventoryToastWidgetHUD/W_Inventory_ToastQueued 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.
SettingsUCrimsonCoreInventoryUISettingsConfig/DefaultCrimsonCore.iniProject Settings -> Crimson -> Crimson Inventory UI. Tooltip classes, split dialog, drag visual, drop confirmation, DropDistance.
HelpersUCrimsonCoreInventoryUIStatics-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_WipeThe 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 modelBound toWhat it exposes
UCrimsonCoreInventoryViewModelOne 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.
UCrimsonCoreInventoryGroupViewModelOne layout group (FCrimsonLayoutGroupInfo), including nested bag groupsGroupId, ParentGroupId, OwnerEntryId, Depth, DisplayName, LayoutKind (ECrimsonCoreInventoryLayoutKind::Flat / Grid / Weight), GridSize, Capacity, Used, bActive, bSupportsRotation, and the entries placed in it.
UCrimsonCoreInventoryEntryViewModelOne FCrimsonInventoryEntryViewEntryId, LayoutGroupId, placement, DisplayName, Description, Icon, StackCount, CountText, Footprint, bAllowRotation, Weight, bLocked, bEquipped, bIsBag, bIsEquippable, bFiltered, bMarked, RarityColor, Durability01, ItemType.
UCrimsonCoreEquipmentViewModel + UCrimsonCoreEquipmentSlotViewModelThe pawn's ICrimsonEquipmentHost (CrimsonEquipment; implemented by UCrimsonEquipmentManagerComponent)One slot VM per SlotTag / SlotIndex: SlotLabel, bOccupied, DisplayName, Icon, RarityColor, Durability01, bDropHighlighted, GetItemInstance().
UCrimsonCoreLootViewModel + UCrimsonCoreLootEntryViewModelOne ACrimsonPickupActorBase plus the destination inventory (BindPickup)Title, bIsEmpty, one entry VM per pickup slot; RequestTake(Entry), RequestTakeAll().
Do not add UMG view bindings to these widgets
The C++ bases already subscribe to every 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.

InputGamepadMouse / keyboardWhat happens
ActivateAClickEmpty 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).
CancelB (Back)EscapeCancel the carry; with nothing carried, close the screen. bBackClosesWhileCarrying closes instead of canceling.
Secondary-Right clickContext menu for the focused item via UCrimsonCoreContextMenuSubsystem - see Context Menu. On gamepad, the Use verb below runs the item's default action directly.
Default actionX (tap, the Use verb)Double clickThe item's default context action from ItemTypeActionSet_Core (use / equip) via ExecuteDefaultActionForItem.
Drag-Drag a slotUCrimsonCoreInventoryDragDropOperation; 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).

Hold verbs need one console variable
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.
VerbInput Action (/CrimsonCore/Input/Inventory/)Gamepad (IMC_UI_Inventory)KeyboardDoes
SortIA_UI_Inventory_SortLTTRequestSort on the focused inventory using SortTags (empty = by type, then name).
ActionsIA_UI_Inventory_UseX (tap)FOpens the focused item's context menu, where Use lives (ContextMenuAction).
SplitIA_UI_Inventory_SplitX (hold)XShows SplitStackDialogClass through UCrimsonMessagingSubsystem::ShowTextInputWithClass with a UCrimsonCoreSplitStackDescriptor; a Confirmed result carries the amount, which calls RequestSplit.
RotateIA_UI_Inventory_RotateRTRRotates the carried item (grid groups with bSupportsRotation).
LockIA_UI_Inventory_LockR3 (right stick click)LRequestLock toggle; locked items refuse drop / sort.
DropIA_UI_Inventory_DropViewDeleteRequestDrop (asks first when bConfirmDrop); lands DropDistance in front of the pawn.
MarkIA_UI_Inventory_MarkL3 (left stick click)MToggleMarked on the focused item; the prompt flips between Mark and Unmark. MarkedCount and GetMarkedEntryIds expose the selection for your own bulk actions.
Take AllIA_UI_Inventory_TakeAllYFLoot window only: takes every row.
Next panelIA_UI_Inventory_NextPanel- (RB comes from the shared UI context's Next action)TabMoves focus to the next registered panel root (RegisterPanelRoot).
Previous panelIA_UI_Inventory_PrevPanel- (LB comes from the shared UI context's Previous action)not mapped in the shipped contextMoves focus to the previous panel root.
Where the keys live
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 = one FCrimsonEquipmentSlotDefinition per Equipment.Slot.* tag (Head, Chest, Legs, Feet, MainHand, OffHand, Ring), bAddEquipmentManager = true.
Verify
Play in editor, open the World Outliner, select your PlayerState: it has a 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).

Verify
Press the mapped key: the screen opens with your inventory groups on the left and the paper doll on the right. Focus lands on the first slot; the action bar shows Sort and the Next / Previous panel prompts.

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.

Verify
Walk onto a B_CrimsonItemSpawnerPad: the HUD meter ticks up and a toast slides in with the item icon, name, and count.
Where the toast signal comes from
The toast listens for 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.

ini
[/Script/CrimsonCore.CrimsonCoreInventoryUISettings]
DefaultTooltipWidgetClass=/CrimsonCore/UserInterface/Inventory/Tooltips/W_ItemTooltip.W_ItemTooltip_C
TooltipClassByItemType=(((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
Project Settings -> Crimson -> Crimson Inventory UI with the shipped tooltip map, split dialog, and drop settings.
SettingDefaultEffect
DefaultTooltipWidgetClassW_ItemTooltipTooltip when neither the item's Tooltip fragment nor the type map names one.
TooltipClassByItemTypeConsumable, Weapon, Armor, BagTooltip class per Item.Type.* tag; exact match first, then parent tags.
SplitStackDialogClassW_SplitStack_DialogDialog pushed by the Split verb.
DialogLayerTagunsetLayer the split dialog is pushed onto.
DragVisualClassunsetWidget that follows the cursor during a mouse drag. Unset = an icon ghost built in code.
bConfirmDroptrueAsk before the Drop verb destroys / drops an item.
DropDistance150Distance in front of the pawn where dropped items land.
Verify
Focus a potion: the consumable tooltip appears. Focus a sword: the equipment tooltip with the slot row appears. Press Split on a stack: the slider dialog opens.

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 widgetTypeDriven by
Image_IconUImageIcon
Image_RarityUImageRarityColor
Text_CountUCommonTextBlockCountText (hidden for 1)
Image_LockUImagebLocked
Image_EquippedUImagebEquipped
Image_MarkedUImagebMarked
Image_BagUImagebIsBag
Bar_DurabilityUProgressBarDurability01 (hidden when the item has no durability)
Image_FocusUImageKeyboard / 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

ClassEvent / functionUse
UCrimsonCoreInventoryScreenBaseOn Carry Changed, On Focused Entry Changed, On Operation FailedPresentation for carry start / end, focus, and routed failures.
UCrimsonCoreInventoryScreenBaseBeginCarry, CancelCarry, PlaceCarried, EquipCarried, PreviewDrop, Do* verbs, RegisterPanelRoot, FocusPanelDrive the controller from your own widgets (a custom button that sorts, a panel you added to the bumper cycle).
UCrimsonCoreInventoryPanelWidgetOn Groups Rebuilt, On Currencies Changed; CategoryTags, CategoryTabButtonClass, GroupPanelClass, CapacityMeterClass, CurrencyTextStyleWhich Item.Type.* tabs appear, and the classes used for tabs, groups, and meters.
UCrimsonCoreInventoryGroupPanelOn Group Rebuilt; FindSlotForTarget, FindItemOverlay, GetFirstSlot, RefreshAllDecorate a rebuilt group; locate a slot for custom highlights.
UCrimsonCoreEquipmentPanelOn Slots Bound (bHasHost)Show an "equipment unavailable" state when the pawn has no ICrimsonEquipmentHost.
UCrimsonCoreInventorySlotWidget / UCrimsonCoreEquipmentSlotWidget / UCrimsonCoreLootEntryWidgetOn Appearance ChangedAdditive visuals after the C++ pass (animations, glow).
UCrimsonCoreInventoryToastWidgetShow Toast (Toast), then call NotifyToastFinishedPlay your toast animation; the queue advances when you call NotifyToastFinished.
UCrimsonCoreInventoryHUDWidgetOn Currencies Changed, RebindToPlayerCurrency presentation; force a rebind after a pawn swap.
UCrimsonCoreSplitStackDialogOn Amount Changed (NewAmount, MaxAmount); SetAmount, Step; Confirm / Cancel are the descriptor buttons (AcceptTextInput / CancelTextInput)Custom stepper visuals.

Multiplayer

  • Server: UCrimsonInventoryManagerComponent on the PlayerState owns the items and replicates them; every Request* ends in a Server RPC on UCrimsonInventoryRoutingComponent, which validates and applies. The UI never mutates state.
  • Owning client: the screens, carry mode, drag-drop, tooltips, HUD strip, and toasts. PreviewDrop is 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 RebindInterval until it finds the player's inventory.
Verify (Play As Client)
Run with Net Mode = Play As Client and two players. Move an item on the client: the slot updates after the server round-trip, and the host's view of that client's inventory (World Outliner -> PlayerState -> component) matches. A failed move (locked target) shows the error toast on the client only.

See also

  • Pickup Actor & Loot Window
  • Inventory Showcases
  • Context Menu
  • GameFeatureActions
  • CrimsonInventory -> Overview
  • CrimsonUI -> Overview