Loading...
Loading...
Use this skill when working with UMG, UI, widget, UserWidget, Slate, HUD, BindWidget, Common UI, menu, or UMG binding in Unreal Engine. See references/widget-types.md for widget type reference and references/common-ui-setup.md for Common UI plugin setup. For Slate in editor tools, see ue-editor-tools. For input mode management, see ue-input-system.
npx skill4agent add quodsoler/unreal-engine-skills ue-ui-umg-slate.agents/ue-project-context.md// MyWidget.h
UCLASS()
class MYGAME_API UMyWidget : public UUserWidget
{
GENERATED_BODY()
protected:
virtual void NativeConstruct() override; // Bind delegates here — BindWidget refs valid
virtual void NativeDestruct() override;
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
};
// MyWidget.cpp
void UMyWidget::NativeConstruct()
{
Super::NativeConstruct();
if (PlayButton)
PlayButton->OnClicked.AddDynamic(this, &UMyWidget::HandlePlayClicked);
}// Creation — always pass the owning PlayerController for player-UI
UMyWidget* Widget = CreateWidget<UMyWidget>(GetOwningPlayer(), MyWidgetClass);
Widget->AddToViewport(0); // ZOrder: higher = on top. Roots the widget (won't GC).
Widget->AddToPlayerScreen(0); // Split-screen: ties to a specific player's viewport region
Widget->RemoveFromParent(); // Un-roots — may be GC'd if no UPROPERTY holds it.
UPROPERTY() TObjectPtr<UMyWidget> CachedWidget; // Strong ref keeps alive after RemoveFromParent
// Visibility
Widget->SetVisibility(ESlateVisibility::Collapsed); // Not drawn, takes no space
Widget->SetVisibility(ESlateVisibility::Hidden); // Not drawn, takes space
Widget->SetVisibility(ESlateVisibility::Visible); // Drawn, receives input
// HitTestInvisible: drawn, passes input through; SelfHitTestInvisible: children receive inputUCLASS()
class MYGAME_API UMyWidget : public UUserWidget
{
GENERATED_BODY()
protected:
UPROPERTY(meta=(BindWidget)) // Required — compiler warning if absent in UMG BP
TObjectPtr<UButton> PlayButton;
UPROPERTY(meta=(BindWidgetOptional)) // Optional — always null-check before use
TObjectPtr<UTextBlock> SubtitleText;
UPROPERTY(meta=(BindWidgetAnim), Transient) // Transient is required for anim bindings
TObjectPtr<UWidgetAnimation> IntroAnim;
UPROPERTY(meta=(BindWidget)) TObjectPtr<UTextBlock> ScoreText;
UPROPERTY(meta=(BindWidget)) TObjectPtr<UImage> PlayerAvatar;
UPROPERTY(meta=(BindWidget)) TObjectPtr<UProgressBar> HealthBar;
UPROPERTY(meta=(BindWidget)) TObjectPtr<UListView> ItemList;
UPROPERTY(meta=(BindWidget)) TObjectPtr<UScrollBox> ContentScroll; // Scrollable container
};// Delegates: OnClicked, OnPressed, OnReleased, OnHovered, OnUnhovered
PlayButton->OnClicked.AddDynamic(this, &UMyWidget::HandlePlayClicked);
PlayButton->OnHovered.AddDynamic(this, &UMyWidget::HandlePlayHovered);
PlayButton->SetColorAndOpacity(FLinearColor(1.f, 0.8f, 0.f, 1.f));
PlayButton->SetIsEnabled(false);
// UE5.2+: WidgetStyle, ColorAndOpacity, ClickMethod direct access deprecated — use setters// SetText wipes any Blueprint binding on Text
ScoreText->SetText(FText::Format(NSLOCTEXT("HUD", "ScoreFmt", "Score: {0}"), FText::AsNumber(Score)));
ScoreText->SetColorAndOpacity(FSlateColor(FLinearColor::White));
ScoreText->SetTextTransformPolicy(ETextTransformPolicy::ToUpper);
ScoreText->SetTextOverflowPolicy(ETextOverflowPolicy::Ellipsis);PlayerAvatar->SetBrushFromTexture(AvatarTexture, /*bMatchSize=*/false);
PlayerAvatar->SetBrushFromMaterial(IconMaterial);
UMaterialInstanceDynamic* MID = PlayerAvatar->GetDynamicMaterial();
MID->SetScalarParameterValue(TEXT("Opacity"), 0.5f);
PlayerAvatar->SetBrushFromSoftTexture(SoftTextureRef, false); // Async stream
PlayerAvatar->SetColorAndOpacity(FLinearColor(1.f, 1.f, 1.f, 0.5f));HealthBar->SetPercent(CurrentHealth / MaxHealth); // 0.0–1.0
HealthBar->SetFillColorAndOpacity(FLinearColor(0.f, 1.f, 0.f, 1.f));
LoadingBar->SetIsMarquee(true); // Indeterminate animationUScrollBoxContentScroll->ScrollToEnd();
ContentScroll->SetScrollOffset(0.f);
ContentScroll->SetOrientation(Orient_Vertical); // defaultUListViewUTileViewIUserObjectListEntry// Entry widget must implement the interface
UCLASS()
class UItemEntryWidget : public UUserWidget, public IUserObjectListEntry
{
GENERATED_BODY()
UPROPERTY(meta=(BindWidget)) TObjectPtr<UTextBlock> NameText;
protected:
virtual void NativeOnListItemObjectSet(UObject* ListItemObject) override
{
IUserObjectListEntry::NativeOnListItemObjectSet(ListItemObject);
if (UItemData* Data = Cast<UItemData>(ListItemObject))
NameText->SetText(FText::FromString(Data->ItemName));
}
};
// Populate
ItemList->ClearListItems();
for (const FItemInfo& Info : Items)
{
UItemData* Data = NewObject<UItemData>(this);
Data->ItemName = Info.Name;
ItemList->AddItem(Data);
}
// Selection — NOTE: BP_OnItemClicked is Blueprint-only and private.
// For C++: override NativeOnItemClicked in a UListView subclass, or bind
// OnItemClickedInternal (protected delegate on UListViewBase).
UItemData* Selected = ItemList->GetSelectedItem<UItemData>();// UI-only — full-screen menus. Game input blocked.
FInputModeUIOnly UIMode;
UIMode.SetWidgetToFocus(Widget->TakeWidget());
GetOwningPlayer()->SetInputMode(UIMode);
GetOwningPlayer()->SetShowMouseCursor(true);
// Game-only — restore gameplay.
GetOwningPlayer()->SetInputMode(FInputModeGameOnly());
GetOwningPlayer()->SetShowMouseCursor(false);
// Game and UI — HUD tooltips, keeps game input active.
FInputModeGameAndUI GameUIMode;
GameUIMode.SetLockMouseToViewportBehavior(EMouseLockMode::LockOnCapture);
GetOwningPlayer()->SetInputMode(GameUIMode);
GetOwningPlayer()->SetShowMouseCursor(true);AddToViewportNativeConstructreferences/common-ui-setup.mdCommonUICommonInputUCommonGameViewportClientUCommonGameViewportClientUCommonInputSubsystemUCLASS()
class MYGAME_API UMyMenuScreen : public UCommonActivatableWidget
{
GENERATED_BODY()
protected:
virtual void NativeOnActivated() override;
virtual void NativeOnDeactivated() override;
virtual UWidget* NativeGetDesiredFocusTarget() const override { return CloseButton; }
virtual TOptional<FUIInputConfig> GetDesiredInputConfig() const override
{
return FUIInputConfig(ECommonInputMode::Menu, EMouseCaptureMode::NoCapture);
}
UPROPERTY(meta=(BindWidget)) TObjectPtr<UCommonButtonBase> CloseButton;
};
void UMyMenuScreen::NativeOnActivated()
{
Super::NativeOnActivated(); // Always call Super
CloseButton->OnClicked().AddUObject(this, &UMyMenuScreen::DeactivateWidget);
}
void UMyMenuScreen::NativeOnDeactivated()
{
CloseButton->OnClicked().RemoveAll(this);
Super::NativeOnDeactivated(); // Always call Super
}UCommonActivatableWidgetContainerBaseUCommonActivatableWidgetStackUCommonActivatableWidgetSwitcherUCommonAnimatedSwitcherUWidgetSwitcherUCommonActivatableWidgetQueueUPROPERTY(meta=(BindWidget)) TObjectPtr<UCommonActivatableWidgetStack> MenuLayer;
UPROPERTY(meta=(BindWidget)) TObjectPtr<UCommonActivatableWidgetQueue> NotificationQueue;
// Push screen onto the stack — AddWidget creates the widget internally
UMyMenuScreen* Screen = Cast<UMyMenuScreen>(MenuLayer->AddWidget(UMyMenuScreen::StaticClass()));
Screen->ActivateWidget();
// Go back — deactivate re-activates the previous widget in the stack
Screen->DeactivateWidget();
// Queue a notification — plays after the current one finishes
NotificationQueue->AddWidget(UMyNotificationWidget::StaticClass());FCommonButtonEventDECLARE_EVENTAddUObjectAddDynamicMyButton->OnClicked().AddUObject(this, &UMyScreen::HandleClick);
MyButton->OnClicked().RemoveAll(this); // In NativeOnDeactivatedGetDesiredInputConfig#include "Input/CommonUIActionRouterBase.h"
// Get() takes a const UWidget& — call from within a widget (or pass any valid UWidget in scope):
UCommonUIActionRouterBase* Router = UCommonUIActionRouterBase::Get(*ContextWidget); // ContextWidget: const UWidget&
if (Router) { /* query active input config */ }UCommonButtonBaseUCommonActivatableWidget::NativeGetDesiredFocusTarget()GetDesiredInputConfigECommonInputMode::MenuSWidgetSCompoundWidget// Custom widget — SMyWidget.h
class SMyWidget : public SCompoundWidget
{
public:
SLATE_BEGIN_ARGS(SMyWidget)
: _LabelText(FText::GetEmpty()), _OnClicked() {}
SLATE_ATTRIBUTE(FText, LabelText)
SLATE_EVENT(FSimpleDelegate, OnClicked)
SLATE_END_ARGS()
void Construct(const FArguments& InArgs);
private:
TAttribute<FText> LabelText;
FSimpleDelegate OnClicked;
};
// SMyWidget.cpp
void SMyWidget::Construct(const FArguments& InArgs)
{
LabelText = InArgs._LabelText;
OnClicked = InArgs._OnClicked;
ChildSlot
[
SNew(SButton)
.OnClicked_Lambda([this]() -> FReply { OnClicked.ExecuteIfBound(); return FReply::Handled(); })
[ SNew(STextBlock).Text(LabelText) ]
];
}
// Instantiation
TSharedRef<SMyWidget> W = SNew(SMyWidget).LabelText(NSLOCTEXT("UI", "Btn", "Go"));
TSharedPtr<SButton> Btn;
SAssignNew(Btn, SButton).OnClicked(FOnClicked::CreateUObject(this, &UMyClass::Handle));STextBlockSButtonSVerticalBoxSHorizontalBoxSOverlaySBorderSBoxSScrollBoxSImageSEditableTextBox// ViewModel — MyGameViewModel.h
UCLASS()
class MYGAME_API UMyGameViewModel : public UMVVMViewModelBase
{
GENERATED_BODY()
public:
// UE_MVVM_SET_PROPERTY_VALUE: checks equality, sets, broadcasts field change
void SetScore(int32 NewScore) { UE_MVVM_SET_PROPERTY_VALUE(Score, NewScore); }
void SetPlayerName(FText NewName) { UE_MVVM_SET_PROPERTY_VALUE(PlayerName, NewName); }
int32 GetScore() const { return Score; }
FText GetPlayerName() const { return PlayerName; }
private:
UPROPERTY(BlueprintReadOnly, FieldNotify, Getter, meta=(AllowPrivateAccess))
int32 Score = 0;
UPROPERTY(BlueprintReadOnly, FieldNotify, Getter, meta=(AllowPrivateAccess))
FText PlayerName;
};
// From game code — UI updates automatically via field notifications:
void AMyPlayerState::OnScoreChanged(int32 NewScore) { ViewModel->SetScore(NewScore); }NewObject<UMyGameViewModel>(this)UMVVMView| Anti-pattern | Correct approach |
|---|---|
| |
| |
| Use |
| |
Binding delegates in | Bind once in |
Skipping | Always call Super — it routes focus and input |
| One widget for all players in split-screen | |
No UPROPERTY reference after | Hold a |
| Z-order conflicts | Multiple widgets at same Z-order causes undefined draw order |
| Invisible widgets still tick | Hidden widgets with |
PublicDependencyModuleNames.AddRange(new string[] { "UMG", "Slate", "SlateCore" });
PrivateDependencyModuleNames.Add("CommonUI"); // If using CommonUI
PrivateDependencyModuleNames.Add("CommonInput"); // If using CommonInput
PrivateDependencyModuleNames.Add("ModelViewViewModel"); // If using MVVMue-cpp-foundationsue-input-systemue-editor-tools