Files
UnrealEngine/Engine/Source/Editor/UMGEditor/Private/UIComponentWidgetBlueprintExtension.cpp
2025-05-18 13:04:45 +08:00

224 lines
8.0 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "UIComponentWidgetBlueprintExtension.h"
#include "Extensions/UIComponent.h"
#include "Extensions/UIComponentContainer.h"
#include "Extensions/UIComponentWidgetBlueprintGeneratedClassExtension.h"
#include "Extensions/UIComponentUserWidgetExtension.h"
#include "Blueprint/WidgetTree.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "WidgetBlueprintEditor.h"
#define LOCTEXT_NAMESPACE "UIComponentWidgetBlueprintExtension"
const FName UUIComponentWidgetBlueprintExtension::MD_ComponentVariable = "GeneratedForComponent";
UUIComponentWidgetBlueprintExtension::UUIComponentWidgetBlueprintExtension(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
ComponentContainer = CreateDefaultSubobject<UUIComponentContainer>(TEXT("ComponentContainer"));
ComponentContainer->SetFlags(RF_Transactional | RF_ArchetypeObject);
}
UUIComponentContainer* UUIComponentWidgetBlueprintExtension::DuplicateContainer(UObject* Outer) const
{
ensure(ComponentContainer->HasAnyFlags(RF_ArchetypeObject));
FObjectInstancingGraph ObjectInstancingGraph;
UUIComponentContainer* NewContainer = NewObject<UUIComponentContainer>(Outer, ComponentContainer->GetClass(), NAME_None, RF_Transactional, ComponentContainer, false);
return NewContainer;
}
void UUIComponentWidgetBlueprintExtension::RemoveComponent(const UClass* ComponentClass, FName OwnerName)
{
// Remove the Component
if(ensure(ComponentContainer))
{
ComponentContainer->RemoveAllComponentsOfType(ComponentClass, OwnerName);
}
}
TArray<UUIComponent*> UUIComponentWidgetBlueprintExtension::GetComponentsFor(const UWidget* Target) const
{
TArray<UUIComponent*> Components;
if(ensure(Target))
{
ComponentContainer->ForEachComponentTarget([Target, &Components](const FUIComponentTarget& ComponentTarget){
if(ComponentTarget.GetTargetName() == Target->GetFName())
{
Components.Push(ComponentTarget.GetComponent());
}
});
}
return Components;
}
UUIComponent* UUIComponentWidgetBlueprintExtension::GetComponent(const UClass* ComponentClass, FName OwnerName) const
{
if (ComponentContainer)
{
return ComponentContainer->GetComponent(ComponentClass, OwnerName);
}
return nullptr;
}
void UUIComponentWidgetBlueprintExtension::RenameWidget(const FName& OldVarName, const FName& NewVarName)
{
if (ensure(ComponentContainer))
{
ComponentContainer->RenameWidget(OldVarName, NewVarName);
}
}
bool UUIComponentWidgetBlueprintExtension::VerifyContainer(UUserWidget* UserWidget) const
{
const UUIComponentUserWidgetExtension* UserWidgetExtension = UserWidget->GetExtension<UUIComponentUserWidgetExtension>();
if(UserWidgetExtension)
{
TArray<FUIComponentTarget> Components;
ComponentContainer->ForEachComponentTarget([&Components](const FUIComponentTarget& ComponentTarget){
Components.Push(ComponentTarget);
});
while(Components.Num() > 0)
{
FUIComponentTarget& Target = Components[Components.Num()-1];
if(!UserWidgetExtension->GetComponent(Target.GetComponent()->GetClass(), Target.GetTargetName()))
{
return false;
}
else
{
Components.RemoveAt(Components.Num()-1);
}
}
return true;
}
return false;
}
UUIComponentUserWidgetExtension* UUIComponentWidgetBlueprintExtension::GetOrCreateExtension(UUserWidget* PreviewWidget)
{
UUIComponentUserWidgetExtension* UserWidgetExtension = PreviewWidget->GetExtension<UUIComponentUserWidgetExtension>();
// If the extension do not exist, we create it which will create a copy of the component we just added.
if (!UserWidgetExtension)
{
UserWidgetExtension = PreviewWidget->AddExtension<UUIComponentUserWidgetExtension>();
UserWidgetExtension->InitializeContainer(DuplicateContainer(UserWidgetExtension));
UserWidgetExtension->Initialize();
}
else
{
VerifyContainer(PreviewWidget);
}
return UserWidgetExtension;
}
UUIComponent* UUIComponentWidgetBlueprintExtension::AddComponent( const UClass* ComponentClass, FName OwnerName)
{
// Add the Component to the WidgetBlueprint
check(ComponentContainer);
// If we already have a component of that tyype on the Widget early out
if (ComponentContainer->GetComponent(ComponentClass, OwnerName))
{
return nullptr;
}
if (UUIComponent* NewComponent = NewObject<UUIComponent>(ComponentContainer, ComponentClass, NAME_None, RF_ArchetypeObject))
{
Modify();
ComponentContainer->AddComponent(OwnerName, NewComponent);
return NewComponent;
}
return nullptr;
}
void UUIComponentWidgetBlueprintExtension::HandleBeginCompilation(FWidgetBlueprintCompilerContext& InCreationContext)
{
Super::HandleBeginCompilation(InCreationContext);
CompilerContext = &InCreationContext;
}
void UUIComponentWidgetBlueprintExtension::HandleCleanAndSanitizeClass(UWidgetBlueprintGeneratedClass* ClassToClean, UObject* OldCDO)
{
// Here we handle Widget Deletion. Go through all Widgets referenced by Components and remove them if they no longer exist.
const UWidgetTree* WidgetTree = GetWidgetBlueprint()->WidgetTree;
if (ComponentContainer && WidgetTree)
{
ComponentContainer->CleanupUIComponents(WidgetTree);
}
}
void UUIComponentWidgetBlueprintExtension::HandlePopulateGeneratedVariables(const FWidgetBlueprintCompilerContext::FPopulateGeneratedVariablesContext& Context)
{
ComponentContainer->ForEachComponentTarget([this, &Context](const FUIComponentTarget& ComponentTarget)
{
if (ensure(ComponentTarget.GetComponent()))
{
FBPVariableDescription ComponentVariableDesc;
ComponentVariableDesc.VarName = UUIComponentContainer::GetPropertyNameForComponent(ComponentTarget.GetComponent(), ComponentTarget.GetTargetName());
ComponentVariableDesc.VarGuid = FGuid::NewGuid();
ComponentVariableDesc.VarType = FEdGraphPinType(UEdGraphSchema_K2::PC_Object, NAME_None, ComponentTarget.GetComponent()->GetClass(), EPinContainerType::None, true, FEdGraphTerminalType());
ComponentVariableDesc.FriendlyName = ComponentTarget.GetComponent()->GetName();
ComponentVariableDesc.PropertyFlags = (CPF_PersistentInstance | CPF_InstancedReference | CPF_BlueprintVisible | CPF_BlueprintReadOnly | CPF_Transient | CPF_RepSkip);
ComponentVariableDesc.SetMetaData(UUIComponentWidgetBlueprintExtension::MD_ComponentVariable, TEXT("true"));
ComponentVariableDesc.SetMetaData(FBlueprintMetadata::MD_FieldNotify, TEXT("true"));
ComponentVariableDesc.Category = FText::FromString(TEXT("Component"));
Context.AddGeneratedVariable(MoveTemp(ComponentVariableDesc));
}
});
}
void UUIComponentWidgetBlueprintExtension::HandleFinishCompilingClass(UWidgetBlueprintGeneratedClass* Class)
{
Super::HandleFinishCompilingClass(Class);
// If we do not have Components, do not add the extension to the Generated Class
if(ensure(CompilerContext) && !ComponentContainer->IsEmpty())
{
if(UWidgetBlueprintGeneratedClass* NewWidgetGeneratedClass = Cast<UWidgetBlueprintGeneratedClass>(CompilerContext->NewClass))
{
UUIComponentWidgetBlueprintGeneratedClassExtension* NewExtension = NewObject<UUIComponentWidgetBlueprintGeneratedClassExtension>(NewWidgetGeneratedClass);
CompilerContext->AddExtension(NewWidgetGeneratedClass, NewExtension);
NewExtension->InitializeContainer(DuplicateContainer(NewExtension));
}
}
}
bool UUIComponentWidgetBlueprintExtension::HandleValidateGeneratedClass(UWidgetBlueprintGeneratedClass* Class)
{
const UWidgetBlueprintGeneratedClass* GeneratedClass = Class;
if ( !ensure(GeneratedClass) )
{
return false;
}
const UWidgetBlueprint* Blueprint = GetWidgetBlueprint();
if ( !ensure(Blueprint) )
{
return false;
}
// Validate with WidgetTree if all widget exists.
if(UWidgetBlueprintGeneratedClass* NewWidgetGeneratedClass = Cast<UWidgetBlueprintGeneratedClass>(CompilerContext->NewClass))
{
if (const UUIComponentWidgetBlueprintGeneratedClassExtension* Extension = NewWidgetGeneratedClass->GetExtension<UUIComponentWidgetBlueprintGeneratedClassExtension>())
{
return Extension->VerifyAllWidgetsExists(NewWidgetGeneratedClass->GetWidgetTreeArchetype());
}
}
return true;
}
void UUIComponentWidgetBlueprintExtension::HandleEndCompilation()
{
CompilerContext = nullptr;
}
#undef LOCTEXT_NAMESPACE