Concept: Policy & Layout Architecture

CrimsonUI follows a three-tier pattern: a manager subsystem owns a policy, the policy owns one root layout per local player, and everything you ever see is a widget pushed onto a tag-keyed layer inside a root layout. The base types live in CrimsonCommon so any plugin can push UI; CrimsonUI supplies the concrete manager and settings that boot them.

The chain, start to pixel

StageType (plugin)What happens
1. Game instance initUCrimsonUIManagerSubsystem (CrimsonUI)Initialize loads UCrimsonUISettings::DefaultUIPolicyClass and creates the policy. It also subscribes to the game instance's player-added/removed events - which only exist on UCrimsonCommonGameInstance.
2. Player joinsUCrimsonGameUIManagerSubsystem (CrimsonCommon)NotifyPlayerAdded fires per local player - but only for UCrimsonCommonLocalPlayer subclasses - and forwards to the policy.
3. Layout creationUCrimsonGameUIPolicy (CrimsonCommon)Instantiates LayoutClass for the player and adds it to the viewport, honoring LocalMultiplayerInteractionMode.
4. Layer registrationUCrimsonPrimaryGameLayout (CrimsonCommon)Your subclass registers a UCommonActivatableWidgetContainerBase (usually a stack) per UI.Layer.* tag.
5. ScreensUCrimsonUIExtensions / PushWidgetToLayerStackEverything else is a push onto a registered layer.

Layers are a convention, not a fixed set

A layer is any tag under UI.Layer you register a stack for. The whole base set ships as native tags in CrimsonCommon (CrimsonUILayerTags.h; UI.Layer.ContextMenu sits with the other context-menu tags in CrimsonContextMenuTags.h), so the tags exist - and appear in every UI.Layer picker - whenever any Crimson plugin is present. Their contracts:

LayerContractDefined by
UI.Layer.GameThe HUD layout; exactly one widget, pushed at startup, never popped.CrimsonCommon (native, CrimsonUILayerTags.h)
UI.Layer.GameMenuIn-game screens that coexist with gameplay (inventory, map).CrimsonCommon (native, CrimsonUILayerTags.h)
UI.Layer.MenuPause/system menus. The escape handler pushes here.CrimsonCommon (native, CrimsonUILayerTags.h)
UI.Layer.ModalDialogs and errors. The messaging subsystem pushes here.CrimsonCommon (native, CrimsonUILayerTags.h)
UI.Layer.ContextMenuContext menus. The context-menu subsystem pushes here.CrimsonCommon (native, CrimsonContextMenuTags.h)

Because layers are separate stacks, pushing a menu does not disturb the HUD below it, and each stack restores its previous top widget when the current one deactivates. Stacking order on screen is simply the order of the stacks in your root layout's widget tree.

Local multiplayer

ECrimsonLocalMultiplayerInteractionMode on the policy: PrimaryOnly gives only the first local player a layout; SingleToggle keeps one interactive layout that can be handed between players (RequestPrimaryControl); Simultaneous gives every local player a full layout (split-screen co-op). Each layout is added to that player's own viewport slot.

Odds and ends worth knowing

  • The manager runs a ticker mirroring AHUD::bShowHUD onto the root layout's visibility - the console command ShowHUD therefore hides the whole CommonUI stack too.
  • The policy class is loaded once at game-instance init; editing DefaultUIPolicyClass requires a PIE restart.
  • GetPrimaryGameLayout and GetCurrentUIPolicy are C++-only; Blueprint goes through UCrimsonUIExtensions.
  • Dedicated servers create no layouts - every entry point above is local-player driven.