// Copyright Epic Games, Inc. All Rights Reserved. #include "Components/Widget.h" #include "Misc/ConfigCacheIni.h" #include "Misc/UObjectToken.h" #include "CoreGlobals.h" #include "Widgets/SNullWidget.h" #include "Types/NavigationMetaData.h" #include "Widgets/IToolTip.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "Widgets/SOverlay.h" #include "UObject/ObjectSaveContext.h" #include "UObject/UObjectHash.h" #include "UObject/UObjectIterator.h" #include "UObject/Package.h" #include "Engine/LocalPlayer.h" #include "Engine/UserInterfaceSettings.h" #include "Framework/Application/SlateApplication.h" #include "Widgets/Layout/SBorder.h" #include "Widgets/Layout/SSpacer.h" #include "Widgets/SToolTip.h" #include "Binding/PropertyBinding.h" #include "Binding/States/WidgetStateBitfield.h" #include "Binding/States/WidgetStateSettings.h" #include "Binding/States/WidgetStateRegistration.h" #include "Binding/WidgetFieldNotificationExtension.h" #include "Logging/MessageLog.h" #include "Blueprint/GameViewportSubsystem.h" #include "Blueprint/UserWidget.h" #include "Blueprint/UserWidgetBlueprint.h" #include "Blueprint/WidgetBlueprintGeneratedClass.h" #include "Slate/SObjectWidget.h" #include "Blueprint/WidgetTree.h" #include "UMGStyle.h" #include "Types/ReflectionMetadata.h" #include "Trace/SlateMemoryTags.h" #include "Serialization/PropertyLocalizationDataGathering.h" #include "Components/NamedSlotInterface.h" #include "ProfilingDebugging/AssetMetadataTrace.h" #include "HAL/LowLevelMemStats.h" #include "Extensions/UIComponentContainer.h" #include "Extensions/UIComponent.h" #include "Extensions/UIComponentUserWidgetExtension.h" #include "Types/InvisibleToWidgetReflectorMetaData.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(Widget) #define LOCTEXT_NAMESPACE "UMG" DECLARE_DWORD_ACCUMULATOR_STAT(TEXT("Total Created UWidgets"), STAT_SlateUTotalWidgets, STATGROUP_SlateMemory); /** * Interface for tool tips. */ class FDelegateToolTip : public IToolTip { public: /** * Gets the widget that this tool tip represents. * * @return The tool tip widget. */ virtual TSharedRef AsWidget() override { return GetContentWidget(); } /** * Gets the tool tip's content widget. * * @return The content widget. */ virtual TSharedRef GetContentWidget() override { if ( CachedToolTip.IsValid() ) { return CachedToolTip.ToSharedRef(); } UWidget* Widget = ToolTipWidgetDelegate.Execute(); if ( Widget ) { CachedToolTip = Widget->TakeWidget(); return CachedToolTip.ToSharedRef(); } return SNullWidget::NullWidget; } /** * Sets the tool tip's content widget. * * @param InContentWidget The new content widget to set. */ virtual void SetContentWidget(const TSharedRef& InContentWidget) override { CachedToolTip = InContentWidget; } /** * Checks whether this tool tip has no content to display right now. * * @return true if the tool tip has no content to display, false otherwise. */ virtual bool IsEmpty() const override { return !ToolTipWidgetDelegate.IsBound(); } /** * Checks whether this tool tip can be made interactive by the user (by holding Ctrl). * * @return true if it is an interactive tool tip, false otherwise. */ virtual bool IsInteractive() const override { return false; } virtual void OnClosed() override { //TODO Notify interface implementing widget of closure CachedToolTip.Reset(); } virtual void OnOpening() override { //TODO Notify interface implementing widget of opening } public: UWidget::FGetWidget ToolTipWidgetDelegate; private: TSharedPtr CachedToolTip; }; #if WITH_EDITORONLY_DATA namespace { void GatherWidgetForLocalization(const UObject* const Object, FPropertyLocalizationDataGatherer& PropertyLocalizationDataGatherer, const EPropertyLocalizationGathererTextFlags GatherTextFlags) { const UWidget* const Widget = CastChecked(Object); EPropertyLocalizationGathererTextFlags WidgetGatherTextFlags = GatherTextFlags; // If we've instanced this widget from another asset, then we only want to process the widget itself (to process any overrides against the archetype), but skip all of its children if (UObject* WidgetGenerator = Widget->WidgetGeneratedBy.Get()) { if (WidgetGenerator->GetOutermost() != Widget->GetOutermost()) { WidgetGatherTextFlags |= EPropertyLocalizationGathererTextFlags::SkipSubObjects; } } PropertyLocalizationDataGatherer.GatherLocalizationDataFromObject(Widget, WidgetGatherTextFlags); } } #endif ///////////////////////////////////////////////////// // UWidget TArray> UWidget::BinderClasses; ///////////////////////////////////////////////////// void UWidget::FFieldNotificationClassDescriptor::ForEachField(const UClass* Class, TFunctionRef Callback) const { for (int32 Index = 0; Index < Max_IndexOf_; ++Index) { if (!Callback(*AllFields[Index])) { return; } } if (const UBlueprintGeneratedClass* BPClass = Cast(Class)) { BPClass->ForEachFieldNotify(Callback, true); } } UE_FIELD_NOTIFICATION_IMPLEMENT_CLASS_DESCRIPTOR_ThreeFields(UWidget, ToolTipText, Visibility, bIsEnabled); UWidget::UWidget(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { PRAGMA_DISABLE_DEPRECATION_WARNINGS bIsEnabled = true; bIsVariable = true; bIsManagedByGameViewportSubsystem = false; #if WITH_EDITOR DesignerFlags = static_cast(EWidgetDesignFlags::None); #endif Visibility = ESlateVisibility::Visible; RenderOpacity = 1.0f; RenderTransformPivot = FVector2D(0.5f, 0.5f); Cursor = EMouseCursor::Default; PixelSnapping = EWidgetPixelSnapping::Inherit; #if WITH_EDITORONLY_DATA bOverrideAccessibleDefaults = false; AccessibleBehavior = ESlateAccessibleBehavior::NotAccessible; AccessibleSummaryBehavior = ESlateAccessibleBehavior::Auto; bCanChildrenBeAccessible = true; #endif AccessibleWidgetData = nullptr; bShouldBroadcastState = true; bWidgetStateInitialized = false; bWrappedByComponent = false; PRAGMA_ENABLE_DEPRECATION_WARNINGS #if WITH_EDITORONLY_DATA { static const FAutoRegisterLocalizationDataGatheringCallback AutomaticRegistrationOfLocalizationGatherer(UWidget::StaticClass(), &GatherWidgetForLocalization); } #endif INC_DWORD_STAT(STAT_SlateUTotalWidgets); } PRAGMA_DISABLE_DEPRECATION_WARNINGS const FWidgetTransform& UWidget::GetRenderTransform() const { return RenderTransform; } void UWidget::SetRenderTransform(FWidgetTransform Transform) { RenderTransform = Transform; UpdateRenderTransform(); } void UWidget::SetRenderScale(FVector2D Scale) { RenderTransform.Scale = Scale; UpdateRenderTransform(); } void UWidget::SetRenderShear(FVector2D Shear) { RenderTransform.Shear = Shear; UpdateRenderTransform(); } void UWidget::SetRenderTransformAngle(float Angle) { RenderTransform.Angle = Angle; UpdateRenderTransform(); } float UWidget::GetRenderTransformAngle() const { return RenderTransform.Angle; } void UWidget::SetRenderTranslation(FVector2D Translation) { RenderTransform.Translation = Translation; UpdateRenderTransform(); } void UWidget::UpdateRenderTransform() { TSharedPtr SafeWidget = GetCachedWidget(); if (SafeWidget.IsValid()) { if (RenderTransform.IsIdentity()) { SafeWidget->SetRenderTransform(TOptional()); } else { SafeWidget->SetRenderTransform(RenderTransform.ToSlateRenderTransform()); } } } FVector2D UWidget::GetRenderTransformPivot() const { return RenderTransformPivot; } void UWidget::SetRenderTransformPivot(FVector2D Pivot) { RenderTransformPivot = Pivot; TSharedPtr SafeWidget = GetCachedWidget(); if (SafeWidget.IsValid()) { SafeWidget->SetRenderTransformPivot(Pivot); } } EFlowDirectionPreference UWidget::GetFlowDirectionPreference() const { return FlowDirectionPreference; } void UWidget::SetFlowDirectionPreference(EFlowDirectionPreference FlowDirection) { FlowDirectionPreference = FlowDirection; if (TSharedPtr SafeWidget = GetCachedWidget()) { SafeWidget->SetFlowDirectionPreference(FlowDirectionPreference); } } bool UWidget::GetIsEnabled() const { TSharedPtr SafeWidget = GetCachedWidget(); return SafeWidget.IsValid() ? SafeWidget->IsEnabled() : bIsEnabled; } void UWidget::SetIsEnabled(bool bInIsEnabled) { bool bValueChanged = false; if (bIsEnabled != bInIsEnabled) { bIsEnabled = bInIsEnabled; bValueChanged = true; } TSharedPtr SafeWidget = GetCachedWidget(); if (SafeWidget.IsValid()) { SafeWidget->SetEnabled(bInIsEnabled); } if (bValueChanged) { BroadcastFieldValueChanged(FFieldNotificationClassDescriptor::bIsEnabled); // Note: State is disabled, so we broadcast !bIsEnabled BroadcastBinaryPostStateChange(UWidgetDisabledStateRegistration::Bit, !bIsEnabled); } } bool UWidget::IsInViewport() const { if (bIsManagedByGameViewportSubsystem) { if (UGameViewportSubsystem* Subsystem = UGameViewportSubsystem::Get(GetWorld())) { return Subsystem->IsWidgetAdded(this); } } return false; } EMouseCursor::Type UWidget::GetCursor() const { return Cursor; } void UWidget::SetCursor(EMouseCursor::Type InCursor) { bOverride_Cursor = true; Cursor = InCursor; TSharedPtr SafeWidget = GetCachedWidget(); if ( SafeWidget.IsValid() ) { SafeWidget->SetCursor(Cursor); } } void UWidget::ResetCursor() { bOverride_Cursor = false; TSharedPtr SafeWidget = GetCachedWidget(); if ( SafeWidget.IsValid() ) { SafeWidget->SetCursor(TOptional()); } } bool UWidget::IsRendered() const { TSharedPtr SafeWidget = GetCachedWidget(); if ( SafeWidget.IsValid() ) { return SafeWidget->GetVisibility().IsVisible() && SafeWidget->GetRenderOpacity() > 0.0f; } return false; } bool UWidget::IsVisible() const { TSharedPtr SafeWidget = GetCachedWidget(); if ( SafeWidget.IsValid() ) { return SafeWidget->GetVisibility().IsVisible(); } return false; } ESlateVisibility UWidget::GetVisibility() const { TSharedPtr SafeWidget = GetCachedWidget(); if (SafeWidget.IsValid()) { return UWidget::ConvertRuntimeToSerializedVisibility(SafeWidget->GetVisibility()); } return Visibility; } void UWidget::SetVisibility(ESlateVisibility InVisibility) { SetVisibilityInternal(InVisibility); } void UWidget::SetVisibilityInternal(ESlateVisibility InVisibility) { const bool bVisibilityChanged = Visibility != InVisibility; if (bVisibilityChanged) { Visibility = InVisibility; } TSharedPtr SafeWidget = GetCachedWidget(); if (SafeWidget.IsValid()) { EVisibility SlateVisibility = UWidget::ConvertSerializedVisibilityToRuntime(InVisibility); SafeWidget->SetVisibility(SlateVisibility); } if (bVisibilityChanged) { BroadcastFieldValueChanged(FFieldNotificationClassDescriptor::Visibility); } } float UWidget::GetRenderOpacity() const { TSharedPtr SafeWidget = GetCachedWidget(); if (SafeWidget.IsValid()) { return SafeWidget->GetRenderOpacity(); } return RenderOpacity; } void UWidget::SetRenderOpacity(float InRenderOpacity) { RenderOpacity = InRenderOpacity; TSharedPtr SafeWidget = GetCachedWidget(); if (SafeWidget.IsValid()) { SafeWidget->SetRenderOpacity(InRenderOpacity); } } EWidgetClipping UWidget::GetClipping() const { TSharedPtr SafeWidget = GetCachedWidget(); if (SafeWidget.IsValid()) { return SafeWidget->GetClipping(); } return Clipping; } void UWidget::SetClipping(EWidgetClipping InClipping) { Clipping = InClipping; TSharedPtr SafeWidget = GetCachedWidget(); if (SafeWidget.IsValid()) { SafeWidget->SetClipping(InClipping); } } PRAGMA_ENABLE_DEPRECATION_WARNINGS EWidgetPixelSnapping UWidget::GetPixelSnapping() const { TSharedPtr SafeWidget = GetCachedWidget(); if (SafeWidget.IsValid()) { return SafeWidget->GetPixelSnapping(); } return PixelSnapping; } void UWidget::SetPixelSnapping(EWidgetPixelSnapping InPixelSnappingMethod) { PixelSnapping = InPixelSnappingMethod; TSharedPtr SafeWidget = GetCachedWidget(); if (SafeWidget.IsValid()) { SafeWidget->SetPixelSnapping(InPixelSnappingMethod); } } void UWidget::ForceVolatile(bool bForce) { bIsVolatile = bForce; TSharedPtr SafeWidget = GetCachedWidget(); if ( SafeWidget.IsValid() ) { SafeWidget->ForceVolatile(bForce); } } PRAGMA_DISABLE_DEPRECATION_WARNINGS FText UWidget::GetToolTipText() const { return ToolTipText; } void UWidget::SetToolTipText(const FText& InToolTipText) { ToolTipText = InToolTipText; BroadcastFieldValueChanged(FFieldNotificationClassDescriptor::ToolTipText); TSharedPtr SafeWidget = GetCachedWidget(); if (SafeWidget.IsValid()) { SafeWidget->SetToolTipText(InToolTipText); } } UWidget* UWidget::GetToolTip() const { return ToolTipWidget; } void UWidget::SetToolTip(UWidget* InToolTipWidget) { ToolTipWidget = InToolTipWidget; TSharedPtr SafeWidget = GetCachedWidget(); if ( SafeWidget.IsValid() ) { if ( ToolTipWidget ) { TSharedRef ToolTip = SNew(SToolTip) .TextMargin(FMargin(0)) .BorderImage(nullptr) [ ToolTipWidget->TakeWidget() ]; SafeWidget->SetToolTip(ToolTip); } else { SafeWidget->SetToolTip(TSharedPtr()); } } } PRAGMA_ENABLE_DEPRECATION_WARNINGS bool UWidget::IsHovered() const { TSharedPtr SafeWidget = GetCachedWidget(); if (SafeWidget.IsValid()) { return SafeWidget->IsHovered(); } return false; } bool UWidget::HasKeyboardFocus() const { TSharedPtr SafeWidget = GetCachedWidget(); if (SafeWidget.IsValid()) { return SafeWidget->HasKeyboardFocus(); } return false; } bool UWidget::HasMouseCapture() const { TSharedPtr SafeWidget = GetCachedWidget(); if (SafeWidget.IsValid()) { return SafeWidget->HasMouseCapture(); } return false; } bool UWidget::HasMouseCaptureByUser(int32 UserIndex, int32 PointerIndex) const { TSharedPtr SafeWidget = GetCachedWidget(); if (SafeWidget.IsValid()) { return SafeWidget->HasMouseCaptureByUser(UserIndex, PointerIndex >= 0 ? PointerIndex : TOptional()); } return false; } void UWidget::SetKeyboardFocus() { TSharedPtr SafeWidget = GetCachedWidget(); if (SafeWidget.IsValid()) { #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) if ( !SafeWidget->SupportsKeyboardFocus() ) { FMessageLog("PIE").Warning(FText::Format(LOCTEXT("ThisWidgetDoesntSupportFocus", "The widget {0} does not support focus. If this is a UserWidget, you should set bIsFocusable to true."), FText::FromString(GetNameSafe(this)))); } #endif if ( !FSlateApplication::Get().SetKeyboardFocus(SafeWidget) ) { if ( UWorld* World = GetWorld() ) { if ( ULocalPlayer* LocalPlayer = World->GetFirstLocalPlayerFromController() ) { LocalPlayer->GetSlateOperations().SetUserFocus(SafeWidget.ToSharedRef(), EFocusCause::SetDirectly); } } } } } bool UWidget::HasUserFocus(APlayerController* PlayerController) const { if (PlayerController == nullptr || PlayerController->Player == nullptr || !PlayerController->IsLocalPlayerController()) { return false; } TSharedPtr SafeWidget = GetCachedWidget(); if ( SafeWidget.IsValid() ) { FLocalPlayerContext Context(PlayerController); if ( ULocalPlayer* LocalPlayer = Context.GetLocalPlayer() ) { TOptional UserIndex = FSlateApplication::Get().GetUserIndexForController(LocalPlayer->GetControllerId()); if (UserIndex.IsSet()) { TOptional FocusCause = SafeWidget->HasUserFocus(UserIndex.GetValue()); return FocusCause.IsSet(); } } } return false; } bool UWidget::HasAnyUserFocus() const { TSharedPtr SafeWidget = GetCachedWidget(); if ( SafeWidget.IsValid() ) { TOptional FocusCause = SafeWidget->HasAnyUserFocus(); return FocusCause.IsSet(); } return false; } bool UWidget::HasFocusedDescendants() const { TSharedPtr SafeWidget = GetCachedWidget(); if ( SafeWidget.IsValid() ) { return SafeWidget->HasFocusedDescendants(); } return false; } bool UWidget::HasUserFocusedDescendants(APlayerController* PlayerController) const { if ( PlayerController == nullptr || !PlayerController->IsLocalPlayerController() ) { return false; } TSharedPtr SafeWidget = GetCachedWidget(); if ( SafeWidget.IsValid() ) { FLocalPlayerContext Context(PlayerController); if ( ULocalPlayer* LocalPlayer = Context.GetLocalPlayer() ) { TOptional UserIndex = FSlateApplication::Get().GetUserIndexForController(LocalPlayer->GetControllerId()); if (UserIndex.IsSet()) { return SafeWidget->HasUserFocusedDescendants(UserIndex.GetValue()); } } } return false; } void UWidget::SetFocus() { SetUserFocus(GetOwningPlayer()); } void UWidget::SetUserFocus(APlayerController* PlayerController) { if ( PlayerController == nullptr || !PlayerController->IsLocalPlayerController() || PlayerController->Player == nullptr ) { #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) FMessageLog("PIE").Error()->AddToken(FTextToken::Create(LOCTEXT("NoPlayerControllerToFocus", "The PlayerController is not a valid local player so it can't focus on ")))->AddToken(FUObjectToken::Create(this)); #endif return; } TSharedPtr SafeWidget = GetCachedWidget(); if ( SafeWidget.IsValid() ) { #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) if ( !SafeWidget->SupportsKeyboardFocus() ) { TSharedRef Message = FMessageLog("PIE").Warning()->AddToken(FUObjectToken::Create(this)); #if WITH_EDITORONLY_DATA if(UObject* GeneratedBy = WidgetGeneratedBy.Get()) { Message->AddToken(FTextToken::Create(FText::FromString(TEXT(" in "))))->AddToken(FUObjectToken::Create(GeneratedBy)); } #endif if (IsA(UUserWidget::StaticClass())) { Message->AddToken(FTextToken::Create(LOCTEXT("UserWidgetDoesntSupportFocus", " does not support focus, you should set bIsFocusable to true."))); } else { Message->AddToken(FTextToken::Create(LOCTEXT("NonUserWidgetDoesntSupportFocus", " does not support focus."))); } } #endif if ( ULocalPlayer* LocalPlayer = PlayerController->GetLocalPlayer() ) { TOptional UserIndex = FSlateApplication::Get().GetUserIndexForController(LocalPlayer->GetControllerId()); if (UserIndex.IsSet()) { FReply& DelayedSlateOperations = LocalPlayer->GetSlateOperations(); if (FSlateApplication::Get().SetUserFocus(UserIndex.GetValue(), SafeWidget)) { DelayedSlateOperations.CancelFocusRequest(); } else { DelayedSlateOperations.SetUserFocus(SafeWidget.ToSharedRef()); } } } } } void UWidget::ForceLayoutPrepass() { TSharedPtr SafeWidget = GetCachedWidget(); if (SafeWidget.IsValid()) { SafeWidget->MarkPrepassAsDirty(); SafeWidget->SlatePrepass(SafeWidget->GetTickSpaceGeometry().Scale); } } void UWidget::InvalidateLayoutAndVolatility() { TSharedPtr SafeWidget = GetCachedWidget(); if ( SafeWidget.IsValid() ) { SafeWidget->Invalidate(EInvalidateWidgetReason::LayoutAndVolatility); } } FVector2D UWidget::GetDesiredSize() const { TSharedPtr SafeWidget = GetCachedWidget(); if (SafeWidget.IsValid()) { return SafeWidget->GetDesiredSize(); } return FVector2D(0, 0); } void UWidget::SetNavigationRuleInternal(EUINavigation Direction, EUINavigationRule Rule, FName WidgetToFocus/* = NAME_None*/, UWidget* InWidget/* = nullptr*/, FCustomWidgetNavigationDelegate InCustomDelegate/* = FCustomWidgetNavigationDelegate()*/) { if (Navigation == nullptr) { Navigation = NewObject(this); } FWidgetNavigationData NavigationData; NavigationData.Rule = Rule; NavigationData.WidgetToFocus = WidgetToFocus; NavigationData.Widget = InWidget; NavigationData.CustomDelegate = InCustomDelegate; switch(Direction) { case EUINavigation::Up: Navigation->Up = NavigationData; break; case EUINavigation::Down: Navigation->Down = NavigationData; break; case EUINavigation::Left: Navigation->Left = NavigationData; break; case EUINavigation::Right: Navigation->Right = NavigationData; break; case EUINavigation::Next: Navigation->Next = NavigationData; break; case EUINavigation::Previous: Navigation->Previous = NavigationData; break; default: break; } } void UWidget::SetNavigationRule(EUINavigation Direction, EUINavigationRule Rule, FName WidgetToFocus) { SetNavigationRuleInternal(Direction, Rule, WidgetToFocus); BuildNavigation(); } void UWidget::SetNavigationRuleBase(EUINavigation Direction, EUINavigationRule Rule) { if (Rule == EUINavigationRule::Explicit || Rule == EUINavigationRule::Custom || Rule == EUINavigationRule::CustomBoundary) { #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) FMessageLog("PIE").Error(LOCTEXT("SetNavigationRuleBaseWrongRule", "Cannot use SetNavigationRuleBase with an Explicit or a Custom or a CustomBoundary Rule.")); #endif return; } SetNavigationRuleInternal(Direction, Rule); BuildNavigation(); } void UWidget::SetNavigationRuleExplicit(EUINavigation Direction, UWidget* InWidget) { SetNavigationRuleInternal(Direction, EUINavigationRule::Explicit, NAME_None, InWidget); BuildNavigation(); } void UWidget::SetNavigationRuleCustom(EUINavigation Direction, FCustomWidgetNavigationDelegate InCustomDelegate) { SetNavigationRuleInternal(Direction, EUINavigationRule::Custom, NAME_None, nullptr, InCustomDelegate); BuildNavigation(); } void UWidget::SetNavigationRuleCustomBoundary(EUINavigation Direction, FCustomWidgetNavigationDelegate InCustomDelegate) { SetNavigationRuleInternal(Direction, EUINavigationRule::CustomBoundary, NAME_None, nullptr, InCustomDelegate); BuildNavigation(); } void UWidget::SetAllNavigationRules(EUINavigationRule Rule, FName WidgetToFocus) { SetNavigationRuleInternal(EUINavigation::Up, Rule, WidgetToFocus); SetNavigationRuleInternal(EUINavigation::Down, Rule, WidgetToFocus); SetNavigationRuleInternal(EUINavigation::Left, Rule, WidgetToFocus); SetNavigationRuleInternal(EUINavigation::Right, Rule, WidgetToFocus); SetNavigationRuleInternal(EUINavigation::Next, Rule, WidgetToFocus); SetNavigationRuleInternal(EUINavigation::Previous, Rule, WidgetToFocus); BuildNavigation(); } UPanelWidget* UWidget::GetParent() const { if ( Slot ) { return Slot->Parent; } return nullptr; } void UWidget::RemoveFromParent() { if (!HasAnyFlags(RF_BeginDestroyed)) { if (bIsManagedByGameViewportSubsystem) { if (UGameViewportSubsystem* Subsystem = UGameViewportSubsystem::Get(GetWorld())) { Subsystem->RemoveWidget(this); } } else if (UPanelWidget* CurrentParent = GetParent()) { CurrentParent->RemoveChild(this); } else { #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) if (GetCachedWidget().IsValid() && GetCachedWidget()->GetParentWidget().IsValid() && !IsDesignTime()) { FText WarningMessage = FText::Format(LOCTEXT("RemoveFromParentWithNoParent", "UWidget::RemoveFromParent() called on '{0}' which has no UMG parent (if it was added directly to a native Slate widget via TakeWidget() then it must be removed explicitly rather than via RemoveFromParent())"), FText::AsCultureInvariant(GetPathName())); // @todo: nickd - we need to switch this back to a warning in engine, but info for games FMessageLog("PIE").Info(WarningMessage); } #endif } } } const FGeometry& UWidget::GetCachedGeometry() const { return GetTickSpaceGeometry(); } const FGeometry& UWidget::GetTickSpaceGeometry() const { TSharedPtr SafeWidget = GetCachedWidget(); if ( SafeWidget.IsValid() ) { return SafeWidget->GetTickSpaceGeometry(); } return SNullWidget::NullWidget->GetTickSpaceGeometry(); } const FGeometry& UWidget::GetPaintSpaceGeometry() const { TSharedPtr SafeWidget = GetCachedWidget(); if (SafeWidget.IsValid()) { return SafeWidget->GetPaintSpaceGeometry(); } return SNullWidget::NullWidget->GetPaintSpaceGeometry(); } #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) void UWidget::VerifySynchronizeProperties() { ensureMsgf(bRoutedSynchronizeProperties, TEXT("%s failed to route SynchronizeProperties. Please call Super::SynchronizeProperties() in your ::SynchronizeProperties() function."), *GetFullName()); } #endif void UWidget::OnWidgetRebuilt() { } TSharedRef UWidget::TakeWidget() { LLM_SCOPE_BYTAG(UI_UMG); #if WIDGET_INCLUDE_RELFECTION_METADATA UObject* SourceAsset = GetSourceAssetOrClass(); UClass* WidgetClass = GetClass(); if(SourceAsset && WidgetClass) { LLM_SCOPE_DYNAMIC_STAT_OBJECTPATH(SourceAsset->GetPackage(), ELLMTagSet::Assets); LLM_SCOPE_DYNAMIC_STAT_OBJECTPATH(WidgetClass, ELLMTagSet::AssetClasses); UE_TRACE_METADATA_SCOPE_ASSET(SourceAsset, WidgetClass); return TakeWidget_Private([](UUserWidget* Widget, TSharedRef Content) -> TSharedPtr { return SNew(SObjectWidget, Widget)[Content]; }); } #endif return TakeWidget_Private([](UUserWidget* Widget, TSharedRef Content) -> TSharedPtr { return SNew(SObjectWidget, Widget)[Content]; }); } TSharedRef UWidget::TakeWidget_Private(ConstructMethodType ConstructMethod) { bool bNewlyCreated = false; TSharedPtr PublicWidget; // If the underlying widget doesn't exist we need to construct and cache the widget for the first run. if (!MyWidget.IsValid()) { PublicWidget = RebuildWidget(); #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) ensureMsgf(PublicWidget.Get() != &SNullWidget::NullWidget.Get(), TEXT("Don't return SNullWidget from RebuildWidget, because we mutate the state of the return. Return a SSpacer if you need to return a no-op widget.")); #endif MyWidget = PublicWidget; bNewlyCreated = true; } else { PublicWidget = MyWidget.Pin(); } // If it is a user widget wrap it in a SObjectWidget to keep the instance from being GC'ed if (IsA(UUserWidget::StaticClass())) { TSharedPtr SafeGCWidget = MyGCWidget.Pin(); // If the GC Widget is still valid we still exist in the slate hierarchy, so just return the GC Widget. if (SafeGCWidget.IsValid()) { ensure(bNewlyCreated == false); PublicWidget = SafeGCWidget; } else // Otherwise we need to recreate the wrapper widget { SafeGCWidget = ConstructMethod(Cast(this), PublicWidget.ToSharedRef()); MyGCWidget = SafeGCWidget; PublicWidget = SafeGCWidget; } } if(bWrappedByComponent) { // Now, if the Widget instance is wrapped by a component, add it to the hierarchy const UWidgetTree* WidgetTree = Cast(GetOuter()); if (const UUserWidget* UserWidget = WidgetTree ? Cast(WidgetTree->GetOuter()) : nullptr) { if (UUIComponentUserWidgetExtension* Extension = UserWidget->GetExtension()) { for (UUIComponent* Component : Extension->GetComponentsFor(this)) { if (bNewlyCreated) { TSharedPtr SafeComponentWidget = Component->RebuildWidgetWithContent(PublicWidget.ToSharedRef()); // If the Widget is the same of null, we do nothing if (SafeComponentWidget != PublicWidget) { ComponentWrapperWidget = SafeComponentWidget; PublicWidget = SafeComponentWidget; } } else if (ComponentWrapperWidget.IsValid()) { PublicWidget = ComponentWrapperWidget.Pin(); } } } } } #if WITH_EDITOR if (IsDesignTime()) { if (bNewlyCreated) { TSharedPtr SafeDesignWidget = RebuildDesignWidget(PublicWidget.ToSharedRef()); if (SafeDesignWidget != PublicWidget) { DesignWrapperWidget = SafeDesignWidget; PublicWidget = SafeDesignWidget; } } else if (DesignWrapperWidget.IsValid()) { PublicWidget = DesignWrapperWidget.Pin(); } } #endif if (bNewlyCreated) { #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) bRoutedSynchronizeProperties = false; #endif #if WIDGET_INCLUDE_RELFECTION_METADATA UObject* SourceAsset = GetSourceAssetOrClass(); UClass* WidgetClass = GetClass(); // We only need to do this once, when the slate widget is created. PublicWidget->AddMetadata(MakeShared(GetFName(), WidgetClass, this, SourceAsset)); #endif SynchronizeProperties(); VerifySynchronizeProperties(); OnWidgetRebuilt(); } return PublicWidget.ToSharedRef(); } TSharedPtr UWidget::GetCachedWidget() const { #if WITH_EDITOR if (DesignWrapperWidget.IsValid()) { return DesignWrapperWidget.Pin(); } #endif if (ComponentWrapperWidget.IsValid()) { return ComponentWrapperWidget.Pin(); } if (MyGCWidget.IsValid()) { return MyGCWidget.Pin(); } return MyWidget.Pin(); } bool UWidget::IsConstructed() const { const TSharedPtr& SafeWidget = GetCachedWidget(); return SafeWidget.IsValid(); } #if WITH_EDITOR TSharedRef UWidget::RebuildDesignWidget(TSharedRef Content) { return Content; } TSharedRef UWidget::CreateDesignerOutline(TSharedRef Content) const { return SNew(SOverlay) + SOverlay::Slot() .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) [ Content ] + SOverlay::Slot() .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) [ SNew(SBorder) .AddMetaData(FInvisibleToWidgetReflectorMetaData()) .DesiredSizeScale(FVector2D(0.0f, 0.0f)) .Visibility(HasAnyDesignerFlags(EWidgetDesignFlags::ShowOutline) ? EVisibility::HitTestInvisible : EVisibility::Collapsed) .BorderImage(FUMGStyle::Get().GetBrush("MarchingAnts")) ]; } #endif UGameInstance* UWidget::GetGameInstance() const { if (UWorld* World = GetWorld()) { return World->GetGameInstance(); } return nullptr; } APlayerController* UWidget::GetOwningPlayer() const { UWidgetTree* WidgetTree = Cast(GetOuter()); if (UUserWidget* UserWidget = WidgetTree ? Cast(WidgetTree->GetOuter()) : nullptr) { return UserWidget->GetOwningPlayer(); } return nullptr; } ULocalPlayer* UWidget::GetOwningLocalPlayer() const { UWidgetTree* WidgetTree = Cast(GetOuter()); if (UUserWidget* UserWidget = WidgetTree ? Cast(WidgetTree->GetOuter()) : nullptr) { return UserWidget->GetOwningLocalPlayer(); } return nullptr; } #if WITH_EDITOR #undef LOCTEXT_NAMESPACE #define LOCTEXT_NAMESPACE "UMGEditor" void UWidget::SetDesignerFlags(EWidgetDesignFlags NewFlags) { DesignerFlags = static_cast(GetDesignerFlags() | NewFlags); INamedSlotInterface* NamedSlotWidget = Cast(this); if (NamedSlotWidget) { NamedSlotWidget->SetNamedSlotDesignerFlags(NewFlags); } } void UWidget::SetDisplayLabel(const FString& InDisplayLabel) { DisplayLabel = InDisplayLabel; } const FString& UWidget::GetCategoryName() const { return CategoryName; } void UWidget::SetCategoryName(const FString& InValue) { CategoryName = InValue; } bool UWidget::IsGeneratedName() const { if (!DisplayLabel.IsEmpty()) { return false; } FString Name = GetName(); if (Name == GetClass()->GetName() || Name.StartsWith(GetClass()->GetName() + TEXT("_"))) { return true; } else if (GetClass()->ClassGeneratedBy != nullptr) { FString BaseNameForBP = GetClass()->GetName(); BaseNameForBP.RemoveFromEnd(TEXT("_C"), ESearchCase::CaseSensitive); if (Name == BaseNameForBP || Name.StartsWith(BaseNameForBP + TEXT("_"))) { return true; } } return false; } FString UWidget::GetLabelMetadata() const { return TEXT(""); } FText UWidget::GetLabelText() const { return GetDisplayNameBase(); } FText UWidget::GetLabelTextWithMetadata() const { FText Label = GetDisplayNameBase(); if (!bIsVariable || !GetLabelMetadata().IsEmpty()) { FFormatNamedArguments Args; Args.Add(TEXT("BaseName"), Label); Args.Add(TEXT("Metadata"), FText::FromString(GetLabelMetadata())); Label = FText::Format(LOCTEXT("NonVariableLabelFormat", "[{BaseName}]{Metadata}"), Args); } return Label; } FText UWidget::GetDisplayNameBase() const { const bool bHasDisplayLabel = !DisplayLabel.IsEmpty(); if (IsGeneratedName() && !bIsVariable) { return GetClass()->GetDisplayNameText(); } else { return FText::FromString(bHasDisplayLabel ? DisplayLabel : GetName()); } } const FText UWidget::GetPaletteCategory() { return LOCTEXT("Uncategorized", "Uncategorized"); } void UWidget::CreatedFromPalette() { // Allowing the variable creation if the setting allows it. const UUserInterfaceSettings* UISettings = GetDefault(); if (!UISettings->bAuthorizeAutomaticWidgetVariableCreation) { bIsVariable = false; } OnCreationFromPalette(); } EVisibility UWidget::GetVisibilityInDesigner() const { return bHiddenInDesigner ? EVisibility::Collapsed : EVisibility::Visible; } bool UWidget::IsEditorWidget() const { if (UWidgetTree* WidgetTree = Cast(GetOuter())) { //@TODO: DarenC - This is a bit dirty, can't find a cleaner alternative yet though. bool bIsEditorWidgetPreview = WidgetTree->RootWidget && WidgetTree->RootWidget->WidgetGeneratedBy.IsValid(); UObject* WidgetBPObject = bIsEditorWidgetPreview ? WidgetTree->RootWidget->WidgetGeneratedBy.Get() : WidgetTree->GetOuter(); if (UUserWidgetBlueprint* WidgetBP = Cast(WidgetBPObject)) { return WidgetBP->AllowEditorWidget(); } else if (UUserWidget* UserWidget = Cast(WidgetBPObject)) { return UserWidget->IsEditorUtility(); } } return false; } bool UWidget::IsVisibleInDesigner() const { if (bHiddenInDesigner) { return false; } UWidget* Parent = GetParent(); while (Parent != nullptr) { if (Parent->bHiddenInDesigner) { return false; } Parent = Parent->GetParent(); } return true; } void UWidget::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { Super::PostEditChangeProperty(PropertyChangedEvent); TSharedPtr SafeWidget = GetCachedWidget(); if (SafeWidget.IsValid()) { SynchronizeProperties(); } else { SynchronizeAccessibleData(); } } void UWidget::SelectByDesigner() { OnSelectedByDesigner(); UWidget* Parent = GetParent(); while ( Parent != nullptr ) { Parent->OnDescendantSelectedByDesigner(this); Parent = Parent->GetParent(); } } void UWidget::DeselectByDesigner() { OnDeselectedByDesigner(); UWidget* Parent = GetParent(); while ( Parent != nullptr ) { Parent->OnDescendantDeselectedByDesigner(this); Parent = Parent->GetParent(); } } #undef LOCTEXT_NAMESPACE #define LOCTEXT_NAMESPACE "UMG" #endif // WITH_EDITOR void UWidget::PreSave(FObjectPreSaveContext ObjectSaveContext) { Super::PreSave(ObjectSaveContext); // This is a failsafe to make sure all the accessibility data is copied over in case // some rare instance isn't handled by SynchronizeProperties. It might not be necessary. SynchronizeAccessibleData(); } #if WITH_EDITOR bool UWidget::Modify(bool bAlwaysMarkDirty) { bool Modified = Super::Modify(bAlwaysMarkDirty); if ( Slot ) { Slot->SetFlags(RF_Transactional); Modified |= Slot->Modify(bAlwaysMarkDirty); } return Modified; } #endif bool UWidget::IsChildOf(UWidget* PossibleParent) { UPanelWidget* Parent = GetParent(); if ( Parent == nullptr ) { return false; } else if ( Parent == PossibleParent ) { return true; } return Parent->IsChildOf(PossibleParent); } TSharedRef UWidget::RebuildWidget() { ensureMsgf(false, TEXT("You must implement RebuildWidget() in your child class")); return SNew(SSpacer); } void UWidget::SynchronizeProperties() { #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) bRoutedSynchronizeProperties = true; #endif // Always sync accessible data even if the SWidget doesn't exist SynchronizeAccessibleData(); // We want to apply the bindings to the cached widget, which could be the SWidget, or the SObjectWidget, // in the case where it's a user widget. We always want to prefer the SObjectWidget so that bindings to // visibility and enabled status are not stomping values setup in the root widget in the User Widget. TSharedPtr SafeWidget = GetCachedWidget(); if ( !SafeWidget.IsValid() ) { return; } #if WITH_EDITOR TSharedPtr SafeContentWidget = MyGCWidget.IsValid() ? MyGCWidget.Pin() : MyWidget.Pin(); #endif PRAGMA_DISABLE_DEPRECATION_WARNINGS #if WITH_EDITOR // Always use an enabled and visible state in the designer. if ( IsDesignTime() ) { SafeWidget->SetEnabled(true); SafeWidget->SetVisibility(BIND_UOBJECT_ATTRIBUTE(EVisibility, GetVisibilityInDesigner)); } else #endif { if ( bOverride_Cursor /*|| CursorDelegate.IsBound()*/ ) { SafeWidget->SetCursor(Cursor);// PROPERTY_BINDING(EMouseCursor::Type, Cursor)); } SafeWidget->SetEnabled(BITFIELD_PROPERTY_BINDING( bIsEnabled )); SafeWidget->SetVisibility(OPTIONAL_BINDING_CONVERT(ESlateVisibility, Visibility, EVisibility, ConvertVisibility)); } #if WITH_EDITOR // In the designer, we need to apply the clip to bounds flag to the real widget, not the designer outline. // because we may be changing a critical default set on the base that not actually set on the outline. // An example of this, would be changing the clipping bounds on a scrollbox. The outline never clipped to bounds // so unless we tweak the -actual- value on the SScrollBox, the user won't see a difference in how the widget clips. SafeContentWidget->SetClipping(Clipping); #else SafeWidget->SetClipping(Clipping); #endif SafeWidget->SetPixelSnapping(PixelSnapping); SafeWidget->SetFlowDirectionPreference(FlowDirectionPreference); SafeWidget->ForceVolatile(bIsVolatile); SafeWidget->SetRenderOpacity(RenderOpacity); UpdateRenderTransform(); SafeWidget->SetRenderTransformPivot(RenderTransformPivot); if ( ToolTipWidgetDelegate.IsBound() && !IsDesignTime() ) { TSharedRef ToolTip = MakeShareable(new FDelegateToolTip()); ToolTip->ToolTipWidgetDelegate = ToolTipWidgetDelegate; SafeWidget->SetToolTip(ToolTip); } else if ( ToolTipWidget != nullptr ) { TSharedRef ToolTip = SNew(SToolTip) .TextMargin(FMargin(0)) .BorderImage(nullptr) [ ToolTipWidget->TakeWidget() ]; SafeWidget->SetToolTip(ToolTip); } else if ( !ToolTipText.IsEmpty() || ToolTipTextDelegate.IsBound() ) { SafeWidget->SetToolTipText(PROPERTY_BINDING(FText, ToolTipText)); } #if WITH_ACCESSIBILITY if (AccessibleWidgetData) { TSharedPtr AccessibleWidget = GetAccessibleWidget(); if (AccessibleWidget.IsValid()) { AccessibleWidget->SetAccessibleBehavior((EAccessibleBehavior)AccessibleWidgetData->AccessibleBehavior, AccessibleWidgetData->CreateAccessibleTextAttribute(), EAccessibleType::Main); AccessibleWidget->SetAccessibleBehavior((EAccessibleBehavior)AccessibleWidgetData->AccessibleSummaryBehavior, AccessibleWidgetData->CreateAccessibleSummaryTextAttribute(), EAccessibleType::Summary); AccessibleWidget->SetCanChildrenBeAccessible(AccessibleWidgetData->bCanChildrenBeAccessible); } } #endif PRAGMA_ENABLE_DEPRECATION_WARNINGS } void UWidget::SynchronizeAccessibleData() { #if WITH_EDITORONLY_DATA if (bOverrideAccessibleDefaults) { if (!AccessibleWidgetData) { AccessibleWidgetData = NewObject(this); } AccessibleWidgetData->bCanChildrenBeAccessible = bCanChildrenBeAccessible; AccessibleWidgetData->AccessibleBehavior = AccessibleBehavior; AccessibleWidgetData->AccessibleText = AccessibleText; AccessibleWidgetData->AccessibleTextDelegate = AccessibleTextDelegate; AccessibleWidgetData->AccessibleSummaryBehavior = AccessibleSummaryBehavior; AccessibleWidgetData->AccessibleSummaryText = AccessibleSummaryText; AccessibleWidgetData->AccessibleSummaryTextDelegate = AccessibleSummaryTextDelegate; } else if (AccessibleWidgetData) { AccessibleWidgetData = nullptr; } #endif } #if WITH_ACCESSIBILITY TSharedPtr UWidget::GetAccessibleWidget() const { return GetCachedWidget(); } #endif FText UWidget::GetAccessibleText() const { #if WITH_ACCESSIBILITY TSharedPtr AccessibleWidget = GetAccessibleWidget(); if (AccessibleWidget.IsValid()) { return AccessibleWidget->GetAccessibleText(); } #endif return FText::GetEmpty(); } FText UWidget::GetAccessibleSummaryText() const { #if WITH_ACCESSIBILITY TSharedPtr AccessibleWidget = GetAccessibleWidget(); if (AccessibleWidget.IsValid()) { return AccessibleWidget->GetAccessibleSummary(); } #endif return FText::GetEmpty(); } UObject* UWidget::GetSourceAssetOrClass() const { UObject* SourceAsset = nullptr; #if WITH_EDITOR // In editor builds we add metadata to the widget so that once hit with the widget reflector it can report // where it comes from, what blueprint, what the name of the widget was...etc. SourceAsset = WidgetGeneratedBy.Get(); #else #if UE_HAS_WIDGET_GENERATED_BY_CLASS SourceAsset = WidgetGeneratedByClass.Get(); #endif #endif if (!SourceAsset) { if (UWidget* WidgetOuter = GetTypedOuter()) { return WidgetOuter->GetSourceAssetOrClass(); } } return SourceAsset; } void UWidget::BuildNavigation() { if ( Navigation != nullptr ) { TSharedPtr SafeWidget = GetCachedWidget(); if ( SafeWidget.IsValid() ) { TSharedPtr MetaData = SafeWidget->GetMetaData(); if ( !MetaData.IsValid() ) { MetaData = MakeShared(); SafeWidget->AddMetadata(MetaData.ToSharedRef()); } Navigation->UpdateMetaData(MetaData.ToSharedRef()); } } } UWorld* UWidget::GetWorld() const { // UWidget's are given world scope by their owning user widget. We can get that through the widget tree that should // be the outer of this widget. if ( UWidgetTree* OwningTree = Cast(GetOuter()) ) { return OwningTree->GetWorld(); } return Super::GetWorld(); } void UWidget::BeginDestroy() { if (bIsManagedByGameViewportSubsystem) { if (UGameViewportSubsystem* Subsystem = UGameViewportSubsystem::Get(GetWorld())) { Subsystem->RemoveWidget(this); } } Super::BeginDestroy(); } void UWidget::FinishDestroy() { Super::FinishDestroy(); DEC_DWORD_STAT(STAT_SlateUTotalWidgets); } EVisibility UWidget::ConvertSerializedVisibilityToRuntime(ESlateVisibility Input) { switch ( Input ) { case ESlateVisibility::Visible: return EVisibility::Visible; case ESlateVisibility::Collapsed: return EVisibility::Collapsed; case ESlateVisibility::Hidden: return EVisibility::Hidden; case ESlateVisibility::HitTestInvisible: return EVisibility::HitTestInvisible; case ESlateVisibility::SelfHitTestInvisible: return EVisibility::SelfHitTestInvisible; default: check(false); return EVisibility::Visible; } } ESlateVisibility UWidget::ConvertRuntimeToSerializedVisibility(const EVisibility& Input) { if ( Input == EVisibility::Visible ) { return ESlateVisibility::Visible; } else if ( Input == EVisibility::Collapsed ) { return ESlateVisibility::Collapsed; } else if ( Input == EVisibility::Hidden ) { return ESlateVisibility::Hidden; } else if ( Input == EVisibility::HitTestInvisible ) { return ESlateVisibility::HitTestInvisible; } else if ( Input == EVisibility::SelfHitTestInvisible ) { return ESlateVisibility::SelfHitTestInvisible; } else { check(false); return ESlateVisibility::Visible; } } FSizeParam UWidget::ConvertSerializedSizeParamToRuntime(const FSlateChildSize& Input) { switch ( Input.SizeRule ) { default: case ESlateSizeRule::Automatic: return FAuto(); case ESlateSizeRule::Fill: return FStretch(Input.Value); } } UWidget* UWidget::FindChildContainingDescendant(UWidget* Root, UWidget* Descendant) { if ( Root == nullptr ) { return nullptr; } UWidget* Parent = Descendant->GetParent(); while ( Parent != nullptr ) { // If the Descendant's parent is the root, then the child containing the descendant is the descendant. if ( Parent == Root ) { return Descendant; } Descendant = Parent; Parent = Parent->GetParent(); } return nullptr; } // TODO: Clean this up to, move it to a user interface setting, don't use a config. FString UWidget::GetDefaultFontName() { FString DefaultFontName = TEXT("/Engine/EngineFonts/Roboto"); GConfig->GetString(TEXT("SlateStyle"), TEXT("DefaultFontName"), DefaultFontName, GEngineIni); return DefaultFontName; } //bool UWidget::BindProperty(const FName& DestinationProperty, UObject* SourceObject, const FName& SourceProperty) //{ // FDelegateProperty* DelegateProperty = FindFProperty(GetClass(), FName(*( DestinationProperty.ToString() + TEXT("Delegate") ))); // // if ( DelegateProperty ) // { // FDynamicPropertyPath BindingPath(SourceProperty.ToString()); // return AddBinding(DelegateProperty, SourceObject, BindingPath); // } // // return false; //} TSubclassOf UWidget::FindBinderClassForDestination(FProperty* Property) { if (BinderClasses.IsEmpty()) { TArray PropertyBindingClasses; GetDerivedClasses(UPropertyBinding::StaticClass(), PropertyBindingClasses); BinderClasses.Reserve(PropertyBindingClasses.Num()); for (UClass* PropertyBindingClass : PropertyBindingClasses) { BinderClasses.Emplace(PropertyBindingClass); } } for (TSubclassOf& BinderClass : BinderClasses) { if (BinderClass.GetDefaultObject()->IsSupportedDestination(Property)) { return BinderClass; } } return nullptr; } static UPropertyBinding* GenerateBinder(FDelegateProperty* DelegateProperty, UObject* Container, UObject* SourceObject, const FDynamicPropertyPath& BindingPath) { FScriptDelegate* ScriptDelegate = DelegateProperty->GetPropertyValuePtr_InContainer(Container); if ( ScriptDelegate ) { // Only delegates that take no parameters have native binders. UFunction* SignatureFunction = DelegateProperty->SignatureFunction; if ( SignatureFunction->NumParms == 1 ) { if ( FProperty* ReturnProperty = SignatureFunction->GetReturnProperty() ) { TSubclassOf BinderClass = UWidget::FindBinderClassForDestination(ReturnProperty); if ( BinderClass != nullptr ) { UPropertyBinding* Binder = NewObject(Container, BinderClass); Binder->SourceObject = SourceObject; Binder->SourcePath = BindingPath; Binder->Bind(ReturnProperty, ScriptDelegate); return Binder; } } } } return nullptr; } bool UWidget::AddBinding(FDelegateProperty* DelegateProperty, UObject* SourceObject, const FDynamicPropertyPath& BindingPath) { if ( UPropertyBinding* Binder = GenerateBinder(DelegateProperty, this, SourceObject, BindingPath) ) { // Remove any existing binding object for this property. for ( int32 BindingIndex = 0; BindingIndex < NativeBindings.Num(); BindingIndex++ ) { if ( NativeBindings[BindingIndex]->DestinationProperty == DelegateProperty->GetFName() ) { NativeBindings.RemoveAt(BindingIndex); break; } } NativeBindings.Add(Binder); // Only notify the bindings have changed if we've already create the underlying slate widget. if ( MyWidget.IsValid() ) { OnBindingChanged(DelegateProperty->GetFName()); } return true; } return false; } FDelegateHandle UWidget::RegisterPostStateListener(const FOnWidgetStateBroadcast::FDelegate& ListenerDelegate, bool bBroadcastCurrentState) { if (!bWidgetStateInitialized) { MyWidgetStateBitfield = UWidgetStateSettings::Get()->GetInitialRegistrationBitfield(this); bWidgetStateInitialized = true; } if (bBroadcastCurrentState) { ListenerDelegate.ExecuteIfBound(this, MyWidgetStateBitfield); } return PostWidgetStateChanged.Add(ListenerDelegate); } void UWidget::UnregisterPostStateListener(const FDelegateHandle& ListenerDelegate) { PostWidgetStateChanged.Remove(ListenerDelegate); } void UWidget::OnBindingChanged(const FName& Property) { } void UWidget::BroadcastBinaryPostStateChange(const FWidgetStateBitfield& StateChange, bool bInValue) { if (bShouldBroadcastState && bWidgetStateInitialized) { MyWidgetStateBitfield.SetBinaryState(StateChange, bInValue); PostWidgetStateChanged.Broadcast(this, MyWidgetStateBitfield); } } void UWidget::BroadcastEnumPostStateChange(const FWidgetStateBitfield& StateChange) { } namespace UE::UMG::Private { UWidgetFieldNotificationExtension* FindOrAddWidgetNotifyExtension(UWidget* Widget) { if (UUserWidget* UserWidget = Cast(Widget)) { if (UWidgetFieldNotificationExtension* Extension = UserWidget->GetExtension()) { return Extension; } return UserWidget->AddExtension(); } else if (UWidgetTree* WidgetTree = Cast(Widget->GetOuter())) { if (UUserWidget* InnerUserWidget = Cast(WidgetTree->GetOuter())) { if (UWidgetFieldNotificationExtension* Extension = InnerUserWidget->GetExtension()) { return Extension; } return InnerUserWidget->AddExtension(); } } return nullptr; } UWidgetFieldNotificationExtension* FindWidgetNotifyExtension(const UWidget* Widget) { if (const UUserWidget* UserWidget = Cast(Widget)) { if (UWidgetFieldNotificationExtension* Extension = UserWidget->GetExtension()) { return Extension; } } else if (const UWidgetTree* WidgetTree = Cast(Widget->GetOuter())) { if (const UUserWidget* InnerUserWidget = Cast(WidgetTree->GetOuter())) { if (UWidgetFieldNotificationExtension* Extension = InnerUserWidget->GetExtension()) { return Extension; } } } return nullptr; } } FDelegateHandle UWidget::AddFieldValueChangedDelegate(UE::FieldNotification::FFieldId InFieldId, FFieldValueChangedDelegate InNewDelegate) { FDelegateHandle Result; if (InFieldId.IsValid()) { if (UWidgetFieldNotificationExtension* Extension = UE::UMG::Private::FindOrAddWidgetNotifyExtension(this)) { Result = Extension->AddFieldValueChangedDelegate(this, InFieldId, MoveTemp(InNewDelegate)); if (Result.IsValid()) { EnabledFieldNotifications.PadToNum(InFieldId.GetIndex() + 1, false); EnabledFieldNotifications[InFieldId.GetIndex()] = true; } } } return Result; } void UWidget::K2_AddFieldValueChangedDelegate(FFieldNotificationId InFieldId, FFieldValueChangedDynamicDelegate InDelegate) { if (InFieldId.IsValid()) { const UE::FieldNotification::FFieldId FieldId = GetFieldNotificationDescriptor().GetField(GetClass(), InFieldId.FieldName); if (ensureMsgf(FieldId.IsValid(), TEXT("The field should be compiled correctly."))) { if (UWidgetFieldNotificationExtension* Extension = UE::UMG::Private::FindOrAddWidgetNotifyExtension(this)) { if (Extension->AddFieldValueChangedDelegate(this, FieldId, InDelegate).IsValid()) { EnabledFieldNotifications.PadToNum(FieldId.GetIndex() + 1, false); EnabledFieldNotifications[FieldId.GetIndex()] = true; } } } } } bool UWidget::RemoveFieldValueChangedDelegate(UE::FieldNotification::FFieldId InFieldId, FDelegateHandle InHandle) { bool bResult = false; if (InFieldId.IsValid() && InHandle.IsValid() && EnabledFieldNotifications.IsValidIndex(InFieldId.GetIndex()) && EnabledFieldNotifications[InFieldId.GetIndex()]) { UWidgetFieldNotificationExtension* Extension = UE::UMG::Private::FindWidgetNotifyExtension(this); checkf(Extension, TEXT("If the EnabledFieldNotifications is valid, then the Extension must also be valid.")); UWidgetFieldNotificationExtension::FRemoveFromResult RemoveResult = Extension->RemoveFieldValueChangedDelegate(this, InFieldId, InHandle); bResult = RemoveResult.bRemoved; EnabledFieldNotifications[InFieldId.GetIndex()] = RemoveResult.bHasOtherBoundDelegates; } return bResult; } void UWidget::K2_RemoveFieldValueChangedDelegate(FFieldNotificationId InFieldId, FFieldValueChangedDynamicDelegate InDelegate) { if (InFieldId.IsValid()) { const UE::FieldNotification::FFieldId FieldId = GetFieldNotificationDescriptor().GetField(GetClass(), InFieldId.FieldName); if (ensureMsgf(FieldId.IsValid(), TEXT("The field should be compiled correctly."))) { if (EnabledFieldNotifications.IsValidIndex(FieldId.GetIndex()) && EnabledFieldNotifications[FieldId.GetIndex()]) { UWidgetFieldNotificationExtension* Extension = UE::UMG::Private::FindWidgetNotifyExtension(this); checkf(Extension, TEXT("If the EnabledFieldNotifications is valid, then the Extension must also be valid.")); UWidgetFieldNotificationExtension::FRemoveFromResult RemoveResult = Extension->RemoveFieldValueChangedDelegate(this, FieldId, InDelegate); EnabledFieldNotifications[FieldId.GetIndex()] = RemoveResult.bHasOtherBoundDelegates; } } } } int32 UWidget::RemoveAllFieldValueChangedDelegates(FDelegateUserObjectConst InUserObject) { int32 bResult = 0; if (InUserObject) { if (UWidgetFieldNotificationExtension* Extension = UE::UMG::Private::FindWidgetNotifyExtension(this)) { UWidgetFieldNotificationExtension::FRemoveAllResult RemoveResult = Extension->RemoveAllFieldValueChangedDelegates(this, InUserObject); bResult = RemoveResult.RemoveCount; EnabledFieldNotifications = RemoveResult.HasFields; } } return bResult; } int32 UWidget::RemoveAllFieldValueChangedDelegates(UE::FieldNotification::FFieldId InFieldId, FDelegateUserObjectConst InUserObject) { int32 bResult = 0; if (InUserObject) { if (UWidgetFieldNotificationExtension* Extension = UE::UMG::Private::FindWidgetNotifyExtension(this)) { UWidgetFieldNotificationExtension::FRemoveAllResult RemoveResult = Extension->RemoveAllFieldValueChangedDelegates(this, InFieldId, InUserObject); bResult = RemoveResult.RemoveCount; EnabledFieldNotifications = RemoveResult.HasFields; } } return bResult; } void UWidget::BroadcastFieldValueChanged(UE::FieldNotification::FFieldId InFieldId) { if (InFieldId.IsValid() && EnabledFieldNotifications.IsValidIndex(InFieldId.GetIndex()) && EnabledFieldNotifications[InFieldId.GetIndex()]) { UWidgetFieldNotificationExtension* Extension = UE::UMG::Private::FindWidgetNotifyExtension(this); checkf(Extension, TEXT("If the EnabledFieldNotifications is valid, then the Extension must also be valid.")); Extension->BroadcastFieldValueChanged(this, InFieldId); } } void UWidget::K2_BroadcastFieldValueChanged(FFieldNotificationId InFieldId) { if (InFieldId.IsValid()) { const UE::FieldNotification::FFieldId FieldId = GetFieldNotificationDescriptor().GetField(GetClass(), InFieldId.FieldName); if (ensureMsgf(FieldId.IsValid(), TEXT("The field should be compiled correctly."))) { BroadcastFieldValueChanged(FieldId); } } } #undef LOCTEXT_NAMESPACE