Skip to content

CommonUI Integration

Summary: Project Eternal uses CommonUI for consistent input handling and widget patterns. Widgets extend UCommonUserWidget for gamepad navigation and input consistency. The IToolTip interface integrates with Unreal's native tooltip system. Input routing uses Enhanced Input through UEternalInputSubsystem, with mouse events broadcast as delegates for widget subscription.

Table of Contents


Why CommonUI

Design Goals

  1. Input Consistency: Same interaction patterns across mouse, keyboard, gamepad
  2. Platform Parity: Widgets work correctly on PC, console, and handheld
  3. Native Tooltip Support: IToolTip interface hooks into Unreal's tooltip system
  4. Focus Management: CommonUI handles focus chains and navigation

Key Tradeoffs

Decision Benefit Cost
CommonUI for Interactive Consistent input handling Additional dependency
UUserWidget for Containers Lighter weight for layout-only Manual input handling if needed
IToolTip Interface Native tooltip timing and positioning Must implement all interface methods
Delegate Broadcasts Widgets decouple from input system Extra subscription boilerplate

Widget Hierarchy

+---------------------------+
|       UUserWidget         |   Unreal Base
+---------------------------+
            |
            +------------------------+
            |                        |
            v                        v
+---------------------------+   +---------------------------+
|    UCommonUserWidget      |   |       UUserWidget         |
|       (CommonUI)          |   |       (Standard)          |
+---------------------------+   +---------------------------+
|  - Gamepad navigation     |   |  - Layout containers      |
|  - Input consistency      |   |  - Non-interactive        |
|  - Platform awareness     |   |  - Lighter weight         |
+---------------------------+   +---------------------------+
            |                                |
            v                                v
+---------------------------+   +---------------------------+
| Interactive Widgets:      |   | Container Widgets:        |
|  - ItemWidget             |   |  - PlayerHUDWidget        |
|  - ItemDropCatcherWidget  |   |  - InventoryWidget        |
|  - GlyphWallWidget        |   |  - CharacterWidget        |
|  - GlyphSocketWidget      |   |  - CraftingWidget         |
|  - StonePlateWidget       |   |  - SkillbarWidget         |
|  - AutomapWidget          |   +---------------------------+
|  - ItemTooltipWidget      |
|  - SkillTooltipWidget     |
|  - StatusEffectTooltipWidget
+---------------------------+

Selection Criteria

Use CommonUserWidget When Use Standard UserWidget When
Widget handles mouse/touch input Widget is layout-only
Widget needs gamepad navigation Widget contains other widgets
Widget implements IToolTip Widget has no direct interaction
Widget supports drag-drop Widget is purely visual

Input Handling

Input Flow Architecture

Player Input Device
        |
        v
+------------------------+
| Enhanced Input System  |
+------------------------+
        |
        v
+------------------------+
| UEternalInputSubsystem |
| (LocalPlayerSubsystem) |
+------------------------+
        |
        +---> UI Toggles --> Controllers --> Widgets
        |
        +---> Mouse Clicks --> Broadcast Delegates
        |                           |
        |                           v
        |                    [Subscribed Widgets]
        |                      - ItemWidget
        |                      - CraftingWidget
        |                      - etc.
        |
        +---> Gameplay --> PlayerController/Pawn

Mouse Event Broadcasting

Why Broadcasts? Widgets don't need direct coupling to input system. They subscribe to what they care about.

Input Event Flow:
+---------------------------+
| Player clicks LMB         |
+---------------------------+
            |
            v
+---------------------------+
| EnhancedInputComponent    |
| triggers LMB action       |
+---------------------------+
            |
            v
+---------------------------+
| EternalInputSubsystem     |
| HandleLeftMouseClick()    |
+---------------------------+
            |
            v
+---------------------------+
| OnLeftMouseClick          |
| .Broadcast()              |
+---------------------------+
            |
     +------+------+
     |             |
     v             v
[Widget A]    [Widget B]
OnLeftClick   OnLeftClick

Widget Mouse Subscription

Widgets subscribe in NativeConstruct, unsubscribe in NativeDestruct:

Widget Subscription Lifecycle:
+---------------------------+
| NativeConstruct()         |
|   Get LocalPlayer         |
|   Get InputSubsystem      |
|   AddDynamic(OnLeftClick) |
|   AddDynamic(OnRightClick)|
+---------------------------+
            |
            v
      [Widget Active]
            |
            v
+---------------------------+
| NativeDestruct()          |
|   RemoveDynamic(...)      |
+---------------------------+

Tooltip System

IToolTip Interface

Tooltip widgets implement Unreal's native IToolTip interface for proper timing and positioning.

+---------------------------+
|    UCommonUserWidget      |
|           +               |
|        IToolTip           |
+---------------------------+
            |
            v
+---------------------------+
|   Tooltip Widget Types    |
|                           |
|  - ItemTooltipWidget      |
|  - SkillTooltipWidget     |
|  - StatusEffectTooltipWidget
+---------------------------+

IToolTip Interface Methods

Method Purpose Typical Implementation
AsWidget() Return Slate widget TakeWidget()
GetContentWidget() Return content for sizing Same as AsWidget or cached
SetContentWidget() Set external content Store in member
IsEmpty() Check if has data TooltipData == nullptr
IsInteractive() Allow mouse interaction Usually false
OnOpening() Called when shown Refresh display
OnClosed() Called when hidden Optional cleanup

Tooltip Display Flow

Mouse enters widget
        |
        v
+---------------------------+
| UItemWidget               |
| NativeOnMouseEnter()      |
+---------------------------+
        |
        v
+---------------------------+
| UpdateTooltip()           |
|   Create TooltipWidget    |
|   InitializeFromData()    |
|   SetToolTip(Widget)      |
+---------------------------+
        |
        v
+---------------------------+
| Unreal Tooltip System     |
|   Applies delay           |
|   Positions tooltip       |
|   Calls OnOpening()       |
+---------------------------+
        |
        v
+---------------------------+
| TooltipWidget visible     |
+---------------------------+

Mouse leaves widget
        |
        v
+---------------------------+
| NativeOnMouseLeave()      |
|   SetToolTip(nullptr)     |
+---------------------------+
        |
        v
+---------------------------+
| Unreal calls OnClosed()   |
+---------------------------+

Drag-Drop Support

Drag-Drop Architecture

+---------------------------+
|     Drag Source           |  (e.g., ItemWidget)
+---------------------------+
            |
            | Create UDragDropOperation
            | with Payload
            v
+---------------------------+
|   UDragDropOperation      |
|   - Payload: UItemDragDropPayload
|   - DefaultDragVisual     |
|   - Pivot                 |
+---------------------------+
            |
            v
+---------------------------+
|     Drop Target           |  (e.g., GlyphSocketWidget,
|                           |   ItemDropCatcherWidget)
+---------------------------+
            |
            v
+---------------------------+
| NativeOnDrop()            |
|   Extract payload         |
|   Validate drop           |
|   Execute action          |
+---------------------------+

UItemDragDropPayload

Carries item data during drag operations:

Property Type Purpose
Item UItemObject* The dragged item
ItemContainerComponent UItemContainerComponent* Source container
TopLeftIndex int32 Grid position in source

Drop Targets

Widget Accepts Action
UItemDropCatcherWidget Any item Drop item to world
UGlyphSocketWidget Glyph items Socket glyph in plate
InventorySlot Any item Move/swap items
EquipmentSlot Equipment Equip item

Full-Screen Drop Catcher

The ItemDropCatcherWidget covers the viewport to catch drops that miss valid targets:

+------------------------------------------+
|           ItemDropCatcherWidget           |
|  (Full viewport, lowest Z-order)         |
|                                          |
|    +---------------------------+         |
|    |    Inventory Panel       |         |
|    |    (Higher Z-order)      |         |
|    |                          |         |
|    |  [Item] [Item] [Item]    |         |
|    |  [Item] [Item] [Item]    |         |
|    +---------------------------+         |
|                                          |
|  Drop here = item goes to world          |
+------------------------------------------+

Widget Binding Patterns

BindWidget Meta Specifier

Widgets declare child widget references with automatic UMG binding:

Meta Specifier Behavior
BindWidget Required - widget must exist in UMG
BindWidgetOptional Optional - can be null

Child Widget Binding

Widget Class Hierarchy:
+--------------------------------+
| C++ Widget Class              |
|   UPROPERTY(BindWidget)       |
|   UTextBlock* ItemNameText;   |
+--------------------------------+
            |
            v
+--------------------------------+
| Blueprint Widget (BP_MyWidget)|
|   [ItemNameText] TextBlock    |
|   (Name matches property)     |
+--------------------------------+

Button Event Binding Pattern

Binding Flow:
+--------------------------------+
| NativeConstruct()              |
|   If (CraftButton)             |
|     CraftButton->OnClicked     |
|       .AddDynamic(this,        |
|         &OnCraftButtonClicked) |
+--------------------------------+
            |
            v
+--------------------------------+
| User clicks button             |
+--------------------------------+
            |
            v
+--------------------------------+
| OnCraftButtonClicked()         |
|   Call controller method       |
+--------------------------------+
            |
            v
+--------------------------------+
| NativeDestruct()               |
|   (Delegates auto-cleaned)     |
+--------------------------------+

API Reference

UCommonUserWidget Overrides

Method When Called Purpose
NativeOnMouseEnter() Mouse enters bounds Start hover effects, show tooltip
NativeOnMouseLeave() Mouse exits bounds End hover, hide tooltip
NativeOnMouseButtonDown() Mouse button pressed Start drag, hide tooltip
NativeOnMouseButtonUp() Mouse button released End drag
NativeOnDrop() Drop operation completes Handle dropped item
NativeOnDragEnter() Drag enters bounds Show drop preview
NativeOnDragLeave() Drag exits bounds Hide drop preview

IToolTip Interface

Method Return Purpose
AsWidget() TSharedRef Return Slate widget
GetContentWidget() TSharedRef Return content widget
SetContentWidget() void Set external content
IsEmpty() bool Check if tooltip has data
IsInteractive() bool Allow mouse over tooltip
OnOpening() void Called when tooltip shown
OnClosed() void Called when tooltip hidden

UEternalInputSubsystem Delegates

Delegate Signature Purpose
OnLeftMouseClick FOnLeftMouseClick LMB press
OnRightMouseClick FOnRightMouseClick RMB press

Common Widget Properties

Type Properties
UCommonTextBlock Text rendering with CommonUI styling
UCommonRichTextBlock Formatted text with styling support
UCommonButtonBase Button with CommonUI input handling

Source References

Class File Line
UItemWidget Source/ProjectEternal/Public/UI/Widgets/Items/ItemWidget.h 1
UItemDropCatcherWidget Source/ProjectEternal/Public/UI/Widgets/Items/ItemDropCatcherWidget.h 1
UItemTooltipWidget Source/ProjectEternal/Public/UI/Widgets/Tooltip/ItemTooltipWidget.h 1
USkillTooltipWidget Source/ProjectEternal/Public/UI/Widgets/Tooltip/SkillTooltipWidget.h 1
UGlyphSocketWidget Source/ProjectEternal/Public/UI/Widgets/Glyph/GlyphSocketWidget.h 1
UEternalInputSubsystem Source/ProjectEternal/Public/Input/EternalInputSubsystem.h 1
UItemDragDropPayload Source/ProjectEternal/Public/UI/Widgets/Items/ItemDragDropPayload.h 1


Recent Changes

Date Change Impact
2024-12 SkillTooltipController uses SetCombatComponent() for dependency injection Controller no longer discovers CombatComponent from pawn; must be explicitly provided
2024-12 SkillTooltipViewModel RefreshDynamicValues() Dynamic data (damage, costs) recalculated on demand, not on every hover