Widget Library¶
Summary: The Widget Library provides reusable UI components following MVVM patterns. Key widgets include
UPlayerHUDWidgetfor in-game HUD,UItemWidgetfor item display with drag-drop,UItemTooltipWidgetfor detailed tooltips, andUSkillSlotWidgetfor ability slots. Widgets useBindWidgetfor child discovery andFieldNotifyfor ViewModel binding.
Table of Contents¶
- Widget Categories
- HUD Widgets
- Status Effect Widgets
- Granted Powers Widgets
- Inventory Widgets
- Tooltip Widgets
- Crafting Widgets
- Character Widgets
- Glyph Widgets
- Modal Widgets
- Utility Widgets
- Common Patterns
- API Reference
- Source References
- Related Systems
- Recent Changes
Widget Categories¶
Overview Table¶
| Category | Base Class | Purpose | Example Widgets |
|---|---|---|---|
| HUD | UUserWidget | In-game display | PlayerHUD, Skillbar, ResourceBar |
| Status | UUserWidget | Buff/debuff icons, owner panel + enemy overhead | StatusEffectPanel, StatusEffectEntry, Status |
| Granted Powers | UCommonUserWidget | Echo mods, blessings, procs, conditionals | GrantedPowersTray, PowerList, PowerCard |
| Inventory | UCommonUserWidget | Item management | Inventory, Item, ItemDropCatcher |
| Tooltip | UCommonUserWidget + IToolTip | Hover information | ItemTooltip, SkillTooltip |
| Crafting | UUserWidget | Recipe/result + greed | Crafting, CraftingModifierEntry |
| Character | UUserWidget | Character stats | Character, StatEntry |
| Game Menu | UUserWidget | In-game ESC menu | GameMenu |
| Glyph | UCommonUserWidget | Glyph socketing | GlyphWall, StonePlate, GlyphSocket |
| Modal | UUserWidget | Blocking prompts / paged sequences | Modal |
| Utility | UCommonUserWidget | Reusable components | BezierCurve, ModifierEntry |
Widget Hierarchy¶
AEternalGameHUD
|
v
+---------------------------+
| UEternalUISubsystem |
+---------------------------+
|
+-- UHUDController
| |
| v
| PlayerHUDWidget
| +-- ScreenEffectsWidget (full-screen overlay, ZOrder -10)
| +-- SkillbarWidget
| | +-- SkillSlotWidget (x5)
| +-- StatusEffectPanelWidget
| | +-- StatusEffectEntryWidget (dynamic)
| +-- GrantedPowersTrayWidget
| | +-- GrantedPowerEntryWidget (dynamic)
| +-- Vitals bars (health / stamina / poise, in-spec, no widget class)
|
+-- UStatusController (world-space, per enemy)
| |
| v
| StatusWidget (health + trailing bar, stance bar on Elite+)
| +-- StatusEffectEntryWidget (overhead status icons)
|
+-- UInventoryController
| |
| v
| InventoryWidget
| +-- ItemWidget (grid)
|
+-- USkillTooltipController
| |
| v
| SkillTooltipWidget
| +-- EffectEntryWidget (dynamic)
|
+-- UItemTooltipController
| |
| v
| ItemTooltipWidget
| +-- ModifierEntryWidget (dynamic)
| +-- ScalingAttributeEntryWidget (dynamic)
|
+-- UGlyphController
|
v
GlyphWallWidget
+-- StonePlateWidget
+-- GlyphSocketWidget
+-- ItemWidget (glyph)
HUD Widgets¶
UPlayerHUDWidget¶
Main HUD container compositing all in-game UI elements.
+------------------------------------------------+
| PlayerHUDWidget |
+------------------------------------------------+
| |
| [Health Bar] [Stamina Bar] [Poise Bar] |
| |
| [Status Effect Icons...] |
| |
| |
| |
| +-------------------+ |
| | Skillbar | |
| | [Q] [LMB] [RMB] [E] [Consumable] |
| +-------------------+ |
+------------------------------------------------+
| Child Widget | Purpose |
|---|---|
| Skillbar | Directional skill layout |
| StatusEffectPanel | Active buff/debuff icons |
| NotificationContainer | Floating messages |
| ScreenEffects | Low-health vignette + big-hit pulse overlay (hit-test invisible, below HUD chrome) |
USkillbarWidget¶
Directional skill layout for abilities.
+---------------------------------------+
| SkillbarWidget |
+---------------------------------------+
| |
| [TopSlot - Q] |
| |
| [LeftSlot - LMB] [RightSlot - RMB]|
| |
| [BottomSlot - E] |
| |
| [ConsumableSlot - 1] |
+---------------------------------------+
| Slot | Input | Typical Use |
|---|---|---|
| LeftSlot | LMB | Primary attack |
| RightSlot | RMB | Secondary attack |
| TopSlot | Q | Ability 1 |
| BottomSlot | E | Ability 2 |
| ConsumableSlot | 1 | Health potion |
USkillSlotWidget¶
Individual skill slot with icon, input label, and cooldown.
+------------------+
| SkillSlotWidget |
+------------------+
| +----------+ |
| | SkillIcon| |
| +----------+ |
| |
| [InputLabel] |
| |
| [CooldownBar] | (optional)
+------------------+
| Element | Purpose |
|---|---|
| SkillIcon | Ability texture |
| InputLabel | Key binding text (Q, LMB, etc.) |
| CooldownBar | Visual cooldown progress |
The cooldown sweep reads the ability's own GetCooldownTimeRemainingAndDuration, which USkillAbility overrides for
every slotted skill — see Universal Skill Cooldowns.
A skill whose class does not override those virtuals renders no sweep, however its CooldownDuration is authored.
Vitals bars¶
There is no UResourceBarWidget class. The HUD v3 rebuild replaced the per-resource widget with bars authored
directly in the W_PlayerHUD WidgetForge spec and driven by UPlayerHUDWidget from the HUD ViewModel.
+------------------------------------------------------+
| HealthBarTrack (texture: T_UI_Bar_Track_M) |
| +------------------------------------------------+ |
| | HealthBarGhost (MI_UI_BarFill_*, eases after) | |
| | HealthBarFill (MI_UI_BarFill_*, snaps) | |
| +------------------------------------------------+ |
| HealthCurrentText / HealthMaxText (in-bar) |
+------------------------------------------------------+
... same shape for Stamina, plus a Poise bar with ticks
| Aspect | How it works |
|---|---|
| Fill | A material instance whose Percent scalar the widget drives — not a UProgressBar |
| Trailing "ghost" | A second fill image behind the main one, easing toward the value in both directions (damage trail on health, spend trail on stamina) |
| Numbers | Drawn inside the bar rather than beside it |
| Poise ticks | Marks at the 10% / 40% / 70% thresholds; the last-crossed threshold's tick lights gold. The thresholds come from FPoiseConfiguration's shared constants so bar and FSM cannot drift |
| Binds | All BindWidgetOptional, so a spec that omits a bar degrades rather than failing to compile |
Moving the bars into the spec is what let the whole vitals block be re-arted (track/fill textures, bottom scrim) without touching C++.
UScreenEffectsWidget¶
Full-screen hit/status feedback overlay (ScreenHitEffects): low-health vignette + big-hit pulse. Owner-side presentation only — zero new replication; the HUD exists only on the owning client.
Owning-client ASC (health deltas, State.Health.Below35 / State.Dead loose tags)
|
v
UScreenEffectsController (HUD sub-controller: intensity curve, big-hit
| detection, death ramp, Eternal.ScreenFX.* CVars)
v
UScreenEffectsViewModel (FieldNotify: intensity, tint, pulse params,
| hit-pulse counter, death state)
v
UScreenEffectsWidget --> dynamic MID on VignetteImage (M_UI_ScreenFX_Vignette)
| Aspect | Behavior |
|---|---|
| Visibility | HitTestInvisible enforced in NativeConstruct; ZOrder -10 (under HUD chrome, above world; death modal is a separate layer above) |
| Low-health channel | Continuous intensity ramp (default 50% -> 15% health ratio, curve-overridable); slow swell (< 3 Hz photosensitivity cap) gated on State.Health.Below35 |
| Big-hit channel | Single ~250 ms envelope when one health delta >= configurable fraction of MaxHealth |
| Channel composition | Max, never summed (capped intensity) |
| Death | Ramps to ceiling and freezes; pulse stops; refreshes on respawn |
| Tuning | /Game/DataAssets/UI/ScreenEffectsConfig (UScreenEffectsConfig), wired via UIConfig.ScreenEffectsConfig; null config falls back to matching code defaults |
| Accessibility | Eternal.ScreenFX.Enabled / Eternal.ScreenFX.Intensity CVars apply immediately (console-variable sink), pending a settings menu |
Status Effect Widgets¶
The same entry widget renders status effects in two places — the player's own panel and an enemy's overhead bar — so everything below is written once and shared.
UStatusEffectPanelWidget (player HUD) UStatusWidget (world-space, per enemy)
| |
+-- UStatusEffectEntryWidget <--------------+
|
+-- StatusIcon (+ a dark offset duplicate behind it, faking a drop shadow)
+-- StackText bottom-right badge
+-- CountdownText bottom-left, whole seconds rounded up
One icon per status tag¶
Entries are grouped by status tag, not by effect instance: three stacks of bleed are one icon carrying a depth
badge and one countdown (the longest remaining), never three icons. The consequence is that a group can be carried by
several different effects that disagree about what the icon should annotate — Status.Poison sits on both the
poison DoT and the corruption ground hazard, for example.
EStatusOverlayMode is that annotation policy, authored per effect:
| Mode | Shows |
|---|---|
None |
Silent icon — neither badge nor countdown |
Countdown |
Countdown only, for effects that cannot stack or whose depth is not player-actionable |
StackCount ("Stacks + Countdown") |
Both — a stacking debuff's threat is how deep it is and how long it lasts, so neither number stands alone |
Modes are folded, never picked. Both controllers used to resolve a group's mode from whichever effect they happened to look at first, so the poison icon showed or hid its countdown depending on application order. The modes form an explicit information ladder — silent, then countdown, then stacks-and-countdown — and the richest wins. The rank has to be spelled out because the enumerator order is authored-asset order, not information order; the enumerator names themselves are load-bearing for the same reason (they are what serializes into effect assets, so renaming one silently drops those assets to the default).
Two presentation rules fall out of the small icon size: a lone "1" is noise, so the badge stays empty until a group actually carries more than one; and a durationless effect gets no countdown rather than a frozen number.
Tooltips resolve the owner, not the local player¶
A status tooltip pairs the hovered entry's effect handle with an ability system component — and the entry widget backs both the player's panel and an enemy's overhead bar. Pairing the handle with the local player's ASC produced an empty tooltip frame whenever the hovered icon belonged to an enemy. The handle carries its own owner, so the tooltip asks it. (Handle ids come from a process-global counter and never collide, so this was always a blank tooltip, never another effect's data.)
Tooltips are also rebuilt on hover rather than on open: UWidget::SetToolTip wraps a UUserWidget in an
SToolTip, so a widget implementing IToolTip never receives OnOpening. A tooltip built once at bind time goes
stale — or blank — the moment the effect instance it was built from expires while the group lives on.
Validation¶
A status-panel effect must name one status (Status.*), not a category. A registered but non-Status tag passes
tag validation and does render, but the panel keys one icon per status tag, so a second effect under the same
category tag would silently collapse into the first. The ability validator now rejects it. See
Content Validation.
The world-space status widget¶
UStatusWidget is the floating readout over an enemy: health with a trailing bar, its status icons, and — on Elite
tier and above — the stance bar that fills toward a poise break. It is created per target by UStatusController
and lives on a UStatusViewModel registered per enemy under a GUID-derived name, which sidesteps the tag-keyed
registration collision the shared status entries use.
| Concern | Behavior |
|---|---|
| Spawn | SpawnStatus (damage number + widget) or RevealStatus (widget only, no number) |
| Stance data | The replicated Poise / MaxPoise attributes — not GetPoisePercentage(), which reads the server-only poise component and returns a flat 1.0 on clients |
| Broken state | The break label replaces the bar rather than stacking on it |
| Tier gate | Elite+; AEternalEnemy.ThreatTier had to start replicating for clients to apply it at all |
| Lifetime | Refreshed on each spawn/reveal, self-expiring afterwards |
RevealStatus exists because a parry deals no health damage: the widget's only other spawn path is the damage-number
cue, which returns early on zero magnitude, so the entire parried-attacker feedback loop was invisible. See
Poise System.
Granted Powers Widgets¶
The player's non-ability powers — Echo Mods, Greed Blessings, procs, conditionals, chants — surface on two surfaces
sharing one snapshot type (FPowerEntry, an immutable snapshot owned by the list ViewModel).
| Widget | Where | Job |
|---|---|---|
UGrantedPowersTrayWidget |
HUD | Compact live tray; flashes and plays a proc sound when a power fires, collapses the tail into an overflow count |
UGrantedPowerEntryWidget |
HUD tray | One tray entry: icon, proc flash, hover tooltip |
UPowerListWidget |
Character sheet | One list in the Modifiers section (Granted Powers or Triggered Effects); rebuilds a fixed-column grid on entry-set change |
UPowerCardWidget |
Character sheet | One card: name, type tag, effect line, source line, and a Desire meter when sealed |
Cards take no ViewModel. The list owns the entries and pushes each snapshot in through SetEntry, so a card is a
pure renderer — which is why applying the card's WidgetForge spec does not invalidate the panels that use it (they
reference it as a class, not as an embedded instance).
Card presentation rules¶
- Type is carried by colour, one per entry type, applied to the name, the top-right tag and the rim light.
- Sealed cards are not dimmed. Dimming made the SEALED tag itself illegible; instead the type colour is replaced by a neutral grey. The absence of a type colour is what reads as sealed, and the tag stays readable saying so.
- The rim light is an additive UI material, not a tinted image. A tinted
UImagecan never blow out — alpha blending caps at the source colour — so a lamp/bloom look needs an additive material driven through a dynamic material instance. Colour genuinely varies per card; brightness does not, which is why brightness belongs on the material rather than being written from C++ everySetEntry. - A styled
CommonTextBlockcannot take an inline colour override — it re-derives from its style everySynchronizeProperties. Contrast problems are fixed on the style asset or with a new style, never inline.
Inventory Widgets¶
UInventoryWidget¶
Main inventory container with item grid.
+--------------------------------+
| InventoryWidget |
+--------------------------------+
| Item Level: 45 |
+--------------------------------+
| +----+ +----+ +----+ +----+ |
| |Item| |Item| |Item| |Item| |
| +----+ +----+ +----+ +----+ |
| +----+ +----+ +----+ +----+ |
| |Item| |Item| | | | | |
| +----+ +----+ +----+ +----+ |
+--------------------------------+
| Element | Purpose |
|---|---|
| ItemLevel | Average equipped item level |
| ItemGrid | UniformGridPanel of ItemWidgets |
UItemWidget¶
Reusable item display with drag-drop and tooltips.
+------------------+
| ItemWidget |
+------------------+
| +------------+ |
| | | |
| | ItemIcon | |
| | | |
| +------------+ |
| |
| [x5] | Stack count (if stackable)
+------------------+
| Feature | How It Works |
|---|---|
| Tooltip | Creates ItemTooltipWidget on hover |
| Drag | Creates UItemDragDropPayload |
| Stack Display | Shows count for stackable items |
| Detailed Mode | Toggle with modifier key |
| Cursor Pickup | Plain LMB release (armed a drag but never dragged, no modifiers) → TryPickUpToCursor() → InventoryController::PickUpItem |
| Cursor Follow | SetCursorFollowMode(true) makes the widget the held-item visual: NativeTick calls SnapToCursor() (centre under cursor), HitTestInvisible, added straight to the viewport by the controller (no separate overlay class) |
UItemDropCatcherWidget¶
Full-screen widget catching item drops to world.
+------------------------------------------+
| ItemDropCatcherWidget |
| (Covers entire viewport) |
| |
| Drop item here = spawn in world |
| |
+------------------------------------------+
| Behavior | Description |
|---|---|
| Z-Order | Lowest, behind all panels |
| OnDrop | Extracts payload, calls InventoryController |
| OnItemDroppedToWorld | Blueprint event for VFX |
| Cursor Hold | Shared with the cursor-pickup model: NativeOnMouseButtonDown LMB → DropHeldItemToWorld, RMB → cancel |
Cursor Pickup (Held-Item) Support¶
The click-to-hold ("item on cursor") model reuses the existing widgets rather than adding new panels. See the Inventory System — Cursor Pickup Model for the server-side design; the UI side is:
| Widget | Role |
|---|---|
UItemWidget (cursor-follow mode) |
Doubles as the held-item visual — no dedicated overlay widget class |
UItemGridWidget (preview-phase routing) |
Intercepts clicks while a hold is active and dispatches place/swap/merge |
UItemGridWidget routes held clicks in the tunnel (preview) phase via
NativeOnPreviewMouseButtonDown, so the grid sees the click before any child ItemWidget — occupied and empty
cells route identically, and drag vs. hold are mutually exclusive by construction:
NativeOnPreviewMouseButtonDown (only while Controller->IsHoldingItem())
RMB → Controller->CancelHeldItem() (item reappears at its anchor)
LMB → HandleHeldClick(geometry, event)
ComputeDropTopLeft(FPointerEvent) [shared snap math, generalized from FDragDropEvent]
ResolveDropAction(.., bDisplaceToCursor = true) [shared with the drag path]
dispatch → PlaceHeldItemInContainer | PlaceHeldItemOntoOccupant | MergeHeldItemInto
| PlaceHeldItemInEquipmentSlot | keep-holding (Reject)
NativeOnMouseMove calls UpdateHeldHoverPreview while holding, reusing the drag preview paint (green/yellow/red;
equipment slots highlight the whole slot; merge highlights the target stack).
Tooltip Widgets¶
UItemTooltipWidget¶
Comprehensive item information tooltip.
+--------------------------------+
| ItemTooltipWidget |
+--------------------------------+
| [Icon] ITEM NAME |
| Item Level 45 |
| One-Handed Sword |
| Tainted <- greed tint| (collapsed when Clean)
+--------------------------------+
| Damage: 120-180 |
| Attack Speed: 1.4 |
+--------------------------------+
| IMPLICIT MODIFIERS |
| +15% Critical Strike |
+--------------------------------+
| PREFIX MODIFIERS |
| +25 Strength |
| +12% Physical Damage |
+--------------------------------+
| SUFFIX MODIFIERS |
| +12% Fire Resistance |
+--------------------------------+
| BLESSING (Consumed, Alt mode) |
| +8% Fire Damage |
+--------------------------------+
| SCALING: [S] Str [B] Dex |
+--------------------------------+
| Section | Widget / Element | When Shown |
|---|---|---|
| Modifier Entries | ModifierEntryWidget (dynamic) | Always (per modifier list) |
| Scaling Attributes | ScalingAttributeEntryWidget (dynamic) | Weapons |
| Greed State | GreedStateText (tinted line) |
Collapsed when Clean; C++ tints + shows it for Touched/Tainted/Corrupting/Consumed |
| Blessing | BlessingHeaderText + BlessingModifiersBox |
Consumed items only, in detailed/Alt mode (mirrors the Echo section) |
The greed line and Blessing header tints are read from the shared FGreedStateColors table, so they match the crafting window exactly. See the Greed System for the state model.
USkillTooltipWidget¶
Ability information tooltip.
+--------------------------------+
| SkillTooltipWidget |
+--------------------------------+
| [Icon] ABILITY NAME |
| Melee Attack |
+--------------------------------+
| Deals 150-200 physical damage |
| to enemies in front of you. |
+--------------------------------+
| Stamina Cost: 25 |
| Cooldown: 0.5s |
+--------------------------------+
| COMBO CHAIN (detailed mode) |
| 1: 100% damage, 100% stamina |
| 2: 120% damage, 110% stamina |
| 3: 150% damage, 120% stamina |
+--------------------------------+
| Property | Source |
|---|---|
| Static Data | UpdateFromAbility() - name, icon, base description |
| Dynamic Data | RefreshDynamicValues() - damage, costs, combos |
UModifierEntryWidget¶
Individual modifier display line.
+--------------------------------+
| ModifierEntryWidget |
+--------------------------------+
| +25% Critical Strike Chance |
| [T3] | (detailed mode)
+--------------------------------+
UScalingAttributeEntryWidget¶
Weapon scaling rank display.
| Rank | Meaning |
|---|---|
| S | Exceptional scaling |
| A | Very good scaling |
| B | Good scaling |
| C | Average scaling |
| D | Poor scaling |
| E | Minimal scaling |
UStatEntryTooltipWidget¶
Breakdown tooltip for a character-sheet stat row ("where on my gear does this stat come from"). It has no
ViewModel — the owning UStatEntryWidget pushes content on hover via SetupTooltip(name, value, description,
sources, footer). The per-source lines come from UStatEntryViewModel::GetStatSources(), a TArray<FStatSourceEntry>
that the CharacterController pre-formats (see Character Widgets).
+--------------------------------+
| StatEntryTooltipWidget |
+--------------------------------+
| Strength 125 | Header (name + total)
| Raw physical power... | Description
+--------------------------------+
| Base +10 |
| Vessel Plate +37 | Source rows (StatSourceRowWidget)
| Curse of Weakness -7 | (negative → NegativeValueColor)
+--------------------------------+
| From gear and attributes | Context footer
+--------------------------------+
| Element | Bound As | Notes |
|---|---|---|
| HeaderNameText / HeaderValueText | BindWidget |
Stat name + total |
| DescriptionText | BindWidget |
Stat description |
| SourcesSection | BindWidget |
Divider + rows + footer; collapsed when the stat has no gear sources |
| SourcesBox | BindWidget |
Container the per-source rows are created into |
| FooterText | BindWidget |
Context line ("from your gear" / "from your attributes") |
| SourceRowClass | EditDefaultsOnly |
TSubclassOf<UStatSourceRowWidget> for one line |
UStatSourceRowWidget¶
One line of the breakdown: contributor name + its pre-formatted contribution. No ViewModel — rows are transient
display children created by UStatEntryTooltipWidget. SetSource(FStatSourceEntry) fills SourceNameText and
SourceValueText; a negative contribution (bNegative) uses NegativeValueColor (the value is a plain UTextBlock
so the per-row color override sticks).
Crafting Widgets¶
UCraftingWidget¶
Crafting window bound to UCraftingViewModel for recipe state, resource display, and the result preview. Beyond
the recipe/result surface, it renders the greed state of the target item so the player sees corruption build
up as they craft.
+--------------------------------+
| CraftingWidget |
+--------------------------------+
| Recipe: Lure of Cinders |
| Cost: 75 / 120 |
| [ Tainted ] <- greed badge | (collapsed when Clean)
+--------------------------------+
| +~~~~~~~~~~~~~~~~~~~~~~~~~~+ |
| | GreedStateImage overlay | | corruption MID, eased between states
| | (paint-mask band) | |
| +~~~~~~~~~~~~~~~~~~~~~~~~~~+ |
| | RESULT PREVIEW | |
| +-------------------------+ |
| |
| [ Craft ] [ X ] |
+--------------------------------+
The greed surface is two bound widgets: a text badge and an image overlay. Both are optional binds, so a Blueprint that omits them simply shows no greed feedback.
| Property | Purpose |
|---|---|
GreedStateText |
Greed badge (Touched/Tainted/Corrupting/Consumed); collapsed when Clean. Tinted from the shared color table. |
GreedStateImage |
Corruption overlay image layered over the panel; its dynamic material is driven by the greed state. |
GreedOverlayMaterial |
EditAnywhere class-default material applied to GreedStateImage at NativeConstruct. Falls back to the image brush's own material when left unset. |
GreedStateMID |
Cached UMaterialInstanceDynamic created from the overlay material; the live target of every parameter write. |
GreedTweenTime |
Seconds to ease between greed states (default 0.45); a hard cut at 0 would snap. |
Overlay material contract. GreedStateMID exposes two parameters the widget drives:
| Parameter | Type | Meaning |
|---|---|---|
Progress |
Scalar | Corruption fill amount (0 = Clean, adds nothing; rises toward 1 with greed) |
StateColor |
Vector | Tint for the current greed state, from FGreedStateColors |
Easing & lifecycle. When the greed state changes the widget sets new Progress/StateColor targets and starts
a self-stopping FTSTicker (TickGreedTween) that eases GreedFillCurrent/GreedColorCurrent toward them, then
removes itself once settled — no per-frame NativeTick cost while idle. NativeDestruct resets the tween state
(bGreedInitialized, current/target fill and color, ticker handle), so reopening the window on a different item
snaps to that item's state instead of animating from the previous one.
| Element | Bound As | Notes |
|---|---|---|
| RecipeNameText / RecipeDescriptionText | BindWidget |
Selected recipe summary |
| ResourceCostText / PlayerResourceText | BindWidget |
Cost vs. available |
| CraftButton / CloseButton | BindWidget |
Execute / dismiss |
| ModifiersPanel / ModifierEntriesContainer | BindWidgetOptional |
ModifyExisting result preview |
| ItemDescriptionPanel | BindWidgetOptional |
CreateNew / TransformItem result preview |
| GreedStateText / GreedStateImage | BindWidgetOptional |
Greed badge + corruption overlay |
Character Widgets¶
UStatEntryWidget¶
Reusable stat row display.
+--------------------------------+
| StatEntryWidget |
+--------------------------------+
| Strength: 125 |
+--------------------------------+
| Feature | Behavior |
|---|---|
| Hover | Shows stat tooltip with breakdown |
| Tooltip | StatEntryTooltipWidget (per-source breakdown) |
Spec Ownership (WidgetForge)¶
Every widget with a spec in Tools/WidgetForge/specs/ is co-authored through the WidgetForge pipeline — the
directory is the list, so consult it rather than any enumeration kept here. Declarative specs live in
Tools/WidgetForge/specs/*.spec.json (see
Tools/WidgetForge/SPEC_FORMAT.md and Documentation/11_Tools/05_WidgetForge.md). Applying a spec
(WidgetForge.Apply) rebuilds the whole widget tree from scratch (deterministic / idempotent; the Blueprint
graph and animations are untouched). The widget asset is the source of truth: editing these widgets by hand in
the UMG designer is safe — Apply refuses (status:"stale") whenever the widget changed since the spec was last
exported, so designer edits can never be silently clobbered. The agent workflow is Export-first: refresh the spec
from the widget, edit the spec, then Apply.
Glyph Widgets¶
UGlyphWallWidget¶
Container for stone plate display.
+--------------------------------+
| GlyphWallWidget |
+--------------------------------+
| +------------------------+ |
| | StonePlateWidget | |
| | [O] [O] [O] | | Glyph sockets
| +------------------------+ |
| |
| INDIVIDUAL EFFECTS: |
| +10% Fire Damage |
| +5% Cast Speed |
| |
| CHANT EFFECTS: |
| (Complete patterns for bonus)|
+--------------------------------+
| [Close Button] |
+--------------------------------+
UGlyphSocketWidget¶
Socket on stone plate for glyph placement.
+-----------+
| Socket |
| [O] | Empty
+-----------+
+-----------+
| Socket |
| [Glyph] | Filled with ItemWidget
+-----------+
| Delegate | Triggered When |
|---|---|
| OnGlyphDroppedOnSocket | Glyph dragged onto socket |
| OnGlyphRemovedFromSocket | Right-click to remove |
Modal Widgets¶
W_Modal¶
The single reusable blocking prompt: build disclaimer, game intro, death screen, and any future confirm.
Content is pushed by UModalController — single page or a paged sequence — so the widget itself is pure chrome.
+--------------------------------------------------+
| (scrim dims everything behind) |
| +----------------------------------+ |
| | TITLE (TS_ModalTitle)| |
| | | |
| | Body copy, rich-text markup | |
| | supported. (TS_ModalBody) | |
| | | |
| | [ Cancel ] [ Confirm ] | |
| +----------------------------------+ |
+--------------------------------------------------+
| Aspect | Detail |
|---|---|
| Authoring | WidgetForge-managed — Tools/WidgetForge/specs/W_Modal.spec.json |
| Chrome | Matches the GameMenu look: scrim, T_UI_Panel_TornDark body plate, T_UI_ButtonPlate_M buttons |
| Typography | TS_ModalTitle / TS_ModalBody / TS_ModalButton CommonTextStyles, declared in Tools/WidgetForge/specs/TextStyles.styles.json |
| Buttons | Empty Confirm/Cancel text hides that button; on any page but the last, a button advances the sequence instead of closing |
The earlier RetainerBox + MI_UI_Modal material layer was dropped — the torn-plate textures carry the look
directly, so the modal no longer pays for an offscreen render target.
See UI Architecture § Modal Content for where the text comes from.
Utility Widgets¶
UBezierCurveWidget¶
Custom painting widget for visual connections.
+--------------------------------+
| BezierCurveWidget |
+--------------------------------+
| |
| O-----. |
| '----. |
| '----O |
| |
+--------------------------------+
| Property | Purpose |
|---|---|
| Connections | Array of FBezierConnection |
| StartPoint/EndPoint | Connection endpoints |
| Color | Line color |
| Thickness | Line width |
Common Patterns¶
ViewModel Binding Pattern¶
Binding Flow:
+--------------------------------+
| NativeConstruct() |
| Find ViewModel from MVVM |
| BindViewModel() |
+--------------------------------+
|
v
+--------------------------------+
| BindViewModel() |
| AddFieldValueChangedDelegate |
| for each property needed |
| Store FDelegateHandle |
+--------------------------------+
|
v
+--------------------------------+
| OnPropertyChanged() |
| Read new value from VM |
| Update widget display |
+--------------------------------+
|
v
+--------------------------------+
| NativeDestruct() |
| UnbindViewModel() |
| RemoveFieldValueChanged... |
+--------------------------------+
Dynamic Child Creation Pattern¶
When child widgets need to be created based on data:
Dynamic Creation Flow:
+--------------------------------+
| OnPropertyChanged() |
| ClearChildren() |
| Clear widget map |
+--------------------------------+
|
v
+--------------------------------+
| For each item in array: |
| CreateWidget<ChildType>() |
| BindToViewModel() |
| AddChild() |
| Add to widget map |
+--------------------------------+
Tooltip Display Pattern¶
Tooltip Flow:
+--------------------------------+
| NativeOnMouseEnter() |
| Create TooltipWidget |
| InitializeFromData() |
| SetToolTip(Widget) |
+--------------------------------+
|
v
+--------------------------------+
| NativeOnMouseLeave() |
| SetToolTip(nullptr) |
+--------------------------------+
State-color source¶
Greed tints are never hand-picked per widget. Both the item tooltip (greed line + Blessing
header) and the crafting window (badge + overlay StateColor) read the single shared
FGreedStateColors table, which UGreedSubsystem populates from UGreedConfigDataAsset (DA_GreedConfig) on
init. Designers retune colors in that one data asset (no recompile) and every greed surface updates together — the
tooltip and crafting window can never drift apart.
DA_GreedConfig (designer-tunable)
|
v (UGreedSubsystem, on init)
FGreedStateColors <- single shared lookup: EGreedState -> FLinearColor
|
+--> ItemTooltipWidget (greed line / Blessing header tint)
+--> CraftingWidget (greed badge + overlay StateColor)
See the Greed System for the state model and config asset.
API Reference¶
Widget Lifecycle Methods¶
| Method | When Called | Purpose |
|---|---|---|
| NativeConstruct() | Widget added to viewport | Bind ViewModel, setup delegates |
| NativeDestruct() | Widget removed from viewport | Unbind, cleanup |
| NativeTick() | Every frame | Animations, interpolation |
Common Widget Properties¶
| Widget | Key Properties |
|---|---|
| UPlayerHUDWidget | Skillbar, StatusEffectPanel, NotificationContainer |
| USkillSlotWidget | SkillIcon, InputLabel, CooldownBar |
| UItemWidget | ItemIconImage, StackCountText, TooltipWidgetClass |
| UItemTooltipWidget | ItemNameText, ModifiersBox, ScalingAttributesBox, GreedStateText, BlessingHeaderText, BlessingModifiersBox |
| USkillTooltipWidget | AbilityNameText, AbilityDescriptionText, CostText |
| UStatEntryTooltipWidget | HeaderNameText, HeaderValueText, DescriptionText, SourcesSection, SourcesBox, FooterText, SourceRowClass |
| UCraftingWidget | RecipeNameText, CraftButton, GreedStateText, GreedStateImage, GreedOverlayMaterial |
Widget Delegates¶
| Widget | Delegate | Purpose |
|---|---|---|
| UGlyphSocketWidget | OnGlyphDroppedOnSocket | Glyph placed |
| UGlyphSocketWidget | OnGlyphRemovedFromSocket | Glyph removed |
| USkillSlotWidget | OnTooltipRequested | Show ability tooltip |
Source References¶
| Widget | File |
|---|---|
| UPlayerHUDWidget | Source/ProjectEternal/Public/UI/Widgets/HUD/PlayerHUDWidget.h |
| USkillbarWidget | Source/ProjectEternal/Public/UI/Widgets/HUD/SkillbarWidget.h |
| USkillSlotWidget | Source/ProjectEternal/Public/UI/Widgets/HUD/SkillSlotWidget.h |
| Vitals bars (health/stamina/poise fills, ghosts, in-bar numbers, poise ticks) | Source/ProjectEternal/Public/UI/Widgets/HUD/PlayerHUDWidget.h |
| UScreenEffectsWidget | Source/ProjectEternal/Public/UI/Widgets/HUD/ScreenEffectsWidget.h |
| UScreenEffectsController / UScreenEffectsViewModel | Source/ProjectEternal/Public/UI/Controllers/ScreenEffectsController.h, Source/ProjectEternal/Public/UI/ViewModels/HUD/ScreenEffectsViewModel.h |
| UScreenEffectsConfig | Source/ProjectEternal/Public/UI/Data/ScreenEffectsConfig.h |
| UStatusEffectPanelWidget | Source/ProjectEternal/Public/UI/Widgets/HUD/StatusEffectPanelWidget.h |
| UStatusEffectEntryWidget | Source/ProjectEternal/Public/UI/Widgets/HUD/StatusEffectEntryWidget.h |
| UStatusEffectEntryViewModel (stack badge, countdown) | Source/ProjectEternal/Public/UI/ViewModels/HUD/StatusEffectEntryViewModel.h |
StatusEffectDisplay helpers (MergeOverlayModes, BuildStackText, BuildCountdownText) |
Source/ProjectEternal/Public/UI/StatusEffectDisplayHelper.h |
EStatusOverlayMode, per-effect display block |
Source/ProjectEternal/Public/Abilities/EternalGameplayEffect.h |
| UStatusWidget (world-space health + stance bar) | Source/ProjectEternal/Public/UI/Widgets/HUD/StatusWidget.h |
UStatusController (SpawnStatus / RevealStatus / BindTargetStance) |
Source/ProjectEternal/Public/UI/Controllers/StatusController.h |
| UGrantedPowersTrayWidget / UGrantedPowerEntryWidget | Source/ProjectEternal/Public/UI/Widgets/HUD/GrantedPowersTrayWidget.h, Source/ProjectEternal/Public/UI/Widgets/HUD/GrantedPowerEntryWidget.h |
| UPowerListWidget / UPowerCardWidget | Source/ProjectEternal/Public/UI/Widgets/Character/PowerListWidget.h, Source/ProjectEternal/Public/UI/Widgets/Character/PowerCardWidget.h |
| UInventoryWidget | Source/ProjectEternal/Public/UI/Widgets/Inventory/InventoryWidget.h |
| UItemGridWidget | Source/ProjectEternal/Public/UI/Widgets/Inventory/ItemGridWidget.h |
| UItemWidget | Source/ProjectEternal/Public/UI/Widgets/Items/ItemWidget.h |
| UItemTooltipWidget | Source/ProjectEternal/Public/UI/Widgets/Tooltip/ItemTooltipWidget.h |
| UItemTooltipWidget greed binds (GreedStateText, BlessingHeaderText, BlessingModifiersBox) | Source/ProjectEternal/Public/UI/Widgets/Tooltip/ItemTooltipWidget.h |
| USkillTooltipWidget | Source/ProjectEternal/Public/UI/Widgets/Tooltip/SkillTooltipWidget.h |
| UStatEntryTooltipWidget | Source/ProjectEternal/Public/UI/Widgets/Tooltip/StatEntryTooltipWidget.h |
| UStatSourceRowWidget | Source/ProjectEternal/Public/UI/Widgets/Tooltip/StatSourceRowWidget.h |
| UStatEntryViewModel / FStatSourceEntry | Source/ProjectEternal/Public/UI/ViewModels/Character/StatEntryViewModel.h |
| UCraftingWidget | Source/ProjectEternal/Public/UI/Widgets/Crafting/CraftingWidget.h |
| FGreedStateColors (shared tint table) | Source/ProjectEternal/Public/Greed/Types/GreedStateColors.h |
| UGlyphWallWidget | Source/ProjectEternal/Public/UI/Widgets/Glyph/GlyphWallWidget.h |
| UGlyphSocketWidget | Source/ProjectEternal/Public/UI/Widgets/Glyph/GlyphSocketWidget.h |
| UBezierCurveWidget | Source/ProjectEternal/Public/UI/Widgets/Common/BezierCurveWidget.h |
Related Systems¶
- UI Architecture - Subsystem and controller setup
- MVVM Framework - ViewModel binding
- CommonUI Integration - Input handling
- Item System - Item data for widgets
- Greed System - State model and shared color config behind the tooltip/crafting greed surfaces
Recent Changes¶
| Date | Change | Impact |
|---|---|---|
| 2024-12 | SkillSlotWidget supports dynamic tooltip refresh | Calls controller's RefreshTooltipValues() when tooltip becomes visible |
| 2024-12 | SkillTooltipWidget displays combo and charge info | Detailed mode shows combo step multipliers and charge level bonuses |
| 2024-12 | Ability display types moved to AbilityDisplayTypes.h | ESkillType, FComboStepDisplayInfo, FChargeLevelDisplayInfo now in Abilities module |
| 2026-06-17 | Greed presentation in tooltip + crafting window | ItemTooltipWidget gains a tinted greed-state line (collapsed when Clean) and a Consumed-only Blessing section (Alt mode); new Crafting Widgets section documents UCraftingWidget's greed badge + corruption overlay (GreedOverlayMaterial/GreedStateMID, Progress/StateColor params, GreedTweenTime FTSTicker easing, NativeDestruct reset). Both surfaces read the shared FGreedStateColors table sourced from DA_GreedConfig |
| 2026-07-02 | Cursor-pickup UI (click-to-hold) | UItemWidget doubles as the held-item cursor visual (SetCursorFollowMode/TryPickUpToCursor); UItemGridWidget routes held clicks in the tunnel phase (NativeOnPreviewMouseButtonDown → HandleHeldClick, shared ComputeDropTopLeft/ResolveDropAction); ItemDropCatcherWidget LMB→drop / RMB→cancel while holding. See Inventory System doc for the server model |
| 2026-07-02 | Stat-source breakdown tooltip | New UStatEntryTooltipWidget + UStatSourceRowWidget (no ViewModel; content pushed by UStatEntryWidget on hover via SetupTooltip); UStatEntryViewModel exposes GetStatSources (FStatSourceEntry) + GetSourcesFooter, pre-formatted by CharacterController |
| 2026-07-02 | WidgetForge spec ownership | W_Character / W_StatEntry / W_StatEntryTooltip are authored from Tools/WidgetForge/specs/*.spec.json (SPEC_FORMAT.md); applying a spec rebuilds the widget tree from scratch, so manual editor edits to these widgets are overwritten on the next compile |
| 2026-07-02 | WidgetForge co-authoring | Widget-as-truth model shipped: full-fidelity Export, staleness gate (Apply refuses stale when the widget changed since the spec's export; -Force to override), PIE guard, token lint, renamedFrom rename safety. Manual UMG edits to spec-managed widgets are now safe; see Documentation/11_Tools/05_WidgetForge.md |
| 2026-07-03 | ScreenHitEffects overlay (Tiers 1+2) | New UScreenEffectsWidget in W_PlayerHUD (ZOrder -10, HitTestInvisible): low-health vignette (continuous ramp + sub-3Hz swell) and big-hit pulse, driven by UScreenEffectsController -> UScreenEffectsViewModel over the owning ASC only. Tuned via ScreenEffectsConfig data asset; Eternal.ScreenFX.Enabled/.Intensity accessibility CVars. W_PlayerHUD + W_ScreenEffects are WidgetForge-managed (Tools/WidgetForge/specs/). Tier 3 per-ailment accents deferred |
| 2026-07-20 | Granted Powers display shipped | HUD tray (UGrantedPowersTrayWidget + entries, reactive proc flash, tray-level proc sound, overflow count, hover tooltips) and character-sheet sections (UPowerListWidget + UPowerCardWidget, entries pushed as immutable FPowerEntry snapshots — cards carry no ViewModel of their own) |
| 2026-07-21 | HUD v3 art pass | Bar track/fill + bottom-scrim textures, menu-strip band plate and glyphs, redrawn attack-mark stamps for 22px legibility, corner marks on weapon skill slots, and a single controller-side icon resolution for skill slots (item art wins over ability art; desired sizes pre-fit so icons no longer flash oversized on spawn). W_SkillPanel / W_SkillSlot adopted into WidgetForge |
| 2026-07-29 | Game Menu widget | W_GameMenu — ESC overlay with Change Preset (inline confirm, returns to character select) and Resume; UGameMenuController + UGameMenuViewModel; GameMenuWidgetClass on UIConfig |
| 2026-07-27 | Status overlay pass (FSH-445/446/447/448) | Status entries gained a stack badge and a whole-second countdown driven by the authored EStatusOverlayMode; group modes are folded on an information ladder instead of resolved by application order; tooltips resolve the ASC that owns the hovered effect (an enemy's overhead icon produced a blank frame before) and rebuild on hover, because UMG wraps tooltips in SToolTip so IToolTip::OnOpening never fires |
| 2026-07-27 | Power card + HUD icon sizing | Torn-plate card header, additive rim-light material driven per entry type through a dynamic material instance, sealed cards no longer dimmed (neutral colour carries the state so the SEALED tag stays legible), DesireBar onto the shared bar textures, and a contrast bump on the micro/note text styles (a styled CommonTextBlock re-derives colour from its style, so overrides must be made on the style asset) |
| 2026-08-06 | W_Modal rebuilt on GameMenu chrome | Scrim + T_UI_Panel_TornDark + T_UI_ButtonPlate_M; the RetainerBox/MI_UI_Modal layer was dropped; type moved onto TS_ModalTitle/TS_ModalBody/TS_ModalButton CommonTextStyles declared in TextStyles.styles.json. New Modal Widgets section |
| 2026-08-06 | Preset card selected state | UPresetCardWidget lifts the selected card onto a bright torn-parchment plate with dark ink text; unselected cards keep the dark plate and recede behind a scrim |
| 2026-08-06 | Menu strip unseen-item badge | UMenuStripWidget pulses a badge on the inventory glyph while the inventory reports unseen items, bound to OnUnseenCountChanged; the pulse is held at 1 Hz, well under the photosensitivity cap |
| 2026-08-06 | Spec ownership list de-enumerated | The hand-maintained widget list drifted; Tools/WidgetForge/specs/ is now cited as the list itself |
| 2026-07-27 | World-space stance bar | UStatusWidget gains a poise/stance bar on Elite+ targets, bound to the replicated Poise/MaxPoise attributes; the break label replaces the bar rather than stacking on it. UStatusController::RevealStatus spawns the widget without a damage number so a parry can reveal it |