// 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(TEXT("ComponentContainer")); ComponentContainer->SetFlags(RF_Transactional | RF_ArchetypeObject); } UUIComponentContainer* UUIComponentWidgetBlueprintExtension::DuplicateContainer(UObject* Outer) const { ensure(ComponentContainer->HasAnyFlags(RF_ArchetypeObject)); FObjectInstancingGraph ObjectInstancingGraph; UUIComponentContainer* NewContainer = NewObject(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 UUIComponentWidgetBlueprintExtension::GetComponentsFor(const UWidget* Target) const { TArray 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(); if(UserWidgetExtension) { TArray 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(); // 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(); 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(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(CompilerContext->NewClass)) { UUIComponentWidgetBlueprintGeneratedClassExtension* NewExtension = NewObject(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(CompilerContext->NewClass)) { if (const UUIComponentWidgetBlueprintGeneratedClassExtension* Extension = NewWidgetGeneratedClass->GetExtension()) { return Extension->VerifyAllWidgetsExists(NewWidgetGeneratedClass->GetWidgetTreeArchetype()); } } return true; } void UUIComponentWidgetBlueprintExtension::HandleEndCompilation() { CompilerContext = nullptr; } #undef LOCTEXT_NAMESPACE