You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
256 lines
11 KiB
C++
256 lines
11 KiB
C++
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "BlueprintGraphPrivatePCH.h"
|
|
#include "BlueprintComponentNodeSpawner.h"
|
|
#include "K2Node_AddComponent.h"
|
|
#include "ClassIconFinder.h" // for FindIconNameForClass()
|
|
#include "BlueprintNodeTemplateCache.h" // for IsTemplateOuter()
|
|
#include "ComponentAssetBroker.h" // for GetComponentsForAsset()/AssignAssetToComponent()
|
|
#include "BlueprintActionFilter.h" // for FBlueprintActionContext
|
|
#include "ComponentTypeRegistry.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "BlueprintComponenetNodeSpawner"
|
|
|
|
/*******************************************************************************
|
|
* Static UBlueprintComponentNodeSpawner Helpers
|
|
******************************************************************************/
|
|
|
|
namespace BlueprintComponentNodeSpawnerImpl
|
|
{
|
|
static FText GetDefaultMenuCategory(TSubclassOf<UActorComponent> const ComponentClass);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static FText BlueprintComponentNodeSpawnerImpl::GetDefaultMenuCategory(TSubclassOf<UActorComponent> const ComponentClass)
|
|
{
|
|
FText ClassGroup;
|
|
TArray<FString> ClassGroupNames;
|
|
ComponentClass->GetClassGroupNames(ClassGroupNames);
|
|
|
|
static FText const DefaultClassGroup(LOCTEXT("DefaultClassGroup", "Common"));
|
|
// 'Common' takes priority over other class groups
|
|
if (ClassGroupNames.Contains(DefaultClassGroup.ToString()) || (ClassGroupNames.Num() == 0))
|
|
{
|
|
ClassGroup = DefaultClassGroup;
|
|
}
|
|
else
|
|
{
|
|
ClassGroup = FText::FromString(ClassGroupNames[0]);
|
|
}
|
|
return FText::Format(LOCTEXT("ComponentCategory", "Add Component|{0}"), ClassGroup);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* UBlueprintComponentNodeSpawner
|
|
******************************************************************************/
|
|
|
|
//------------------------------------------------------------------------------
|
|
UBlueprintComponentNodeSpawner* UBlueprintComponentNodeSpawner::Create(const FComponentTypeEntry& Entry)
|
|
{
|
|
UClass* ComponentClass = Entry.ComponentClass;
|
|
if (ComponentClass == nullptr)
|
|
{
|
|
// unloaded class, must be blueprint created. Create an entry. We'll load the class when we spawn the node:
|
|
|
|
UBlueprintComponentNodeSpawner* NodeSpawner = NewObject<UBlueprintComponentNodeSpawner>(GetTransientPackage());
|
|
NodeSpawner->ComponentClass = nullptr;
|
|
NodeSpawner->NodeClass = UK2Node_AddComponent::StaticClass();
|
|
NodeSpawner->ComponentName = Entry.ComponentName;
|
|
NodeSpawner->ComponentAssetName = Entry.ComponentAssetName;
|
|
|
|
FBlueprintActionUiSpec& MenuSignature = NodeSpawner->DefaultMenuSignature;
|
|
FText const ComponentTypeName = FText::FromString(Entry.ComponentName);
|
|
MenuSignature.MenuName = FText::Format(LOCTEXT("AddComponentMenuName", "Add {0}"), ComponentTypeName);
|
|
MenuSignature.Category = LOCTEXT("BlueprintComponentCategory", "Custom");
|
|
MenuSignature.Tooltip = FText::Format(LOCTEXT("AddComponentTooltip", "Spawn a {0}"), ComponentTypeName);
|
|
// add at least one character, so that PrimeDefaultUiSpec() doesn't
|
|
// attempt to query the template node
|
|
if (MenuSignature.Keywords.IsEmpty())
|
|
{
|
|
MenuSignature.Keywords = FText::FromString(TEXT(" "));
|
|
}
|
|
MenuSignature.IconName = FClassIconFinder::FindIconNameForClass(nullptr);
|
|
MenuSignature.DocLink = TEXT("Shared/GraphNodes/Blueprint/UK2Node_AddComponent");
|
|
MenuSignature.DocExcerptTag = TEXT("AddComponent");
|
|
|
|
return NodeSpawner;
|
|
}
|
|
|
|
if (ComponentClass->HasAnyClassFlags(CLASS_Abstract) || !ComponentClass->HasMetaData(FBlueprintMetadata::MD_BlueprintSpawnableComponent))
|
|
{
|
|
// loaded class that is marked as abstract or not spawnable, don't create an entry:
|
|
return nullptr;
|
|
}
|
|
|
|
UClass* const AuthoritativeClass = ComponentClass->GetAuthoritativeClass();
|
|
|
|
UBlueprintComponentNodeSpawner* NodeSpawner = NewObject<UBlueprintComponentNodeSpawner>(GetTransientPackage());
|
|
NodeSpawner->ComponentClass = AuthoritativeClass;
|
|
NodeSpawner->NodeClass = UK2Node_AddComponent::StaticClass();
|
|
|
|
FBlueprintActionUiSpec& MenuSignature = NodeSpawner->DefaultMenuSignature;
|
|
FText const ComponentTypeName = AuthoritativeClass->GetDisplayNameText();
|
|
MenuSignature.MenuName = FText::Format(LOCTEXT("AddComponentMenuName", "Add {0}"), ComponentTypeName);
|
|
MenuSignature.Category = BlueprintComponentNodeSpawnerImpl::GetDefaultMenuCategory(AuthoritativeClass);
|
|
MenuSignature.Tooltip = FText::Format(LOCTEXT("AddComponentTooltip", "Spawn a {0}"), ComponentTypeName);
|
|
MenuSignature.Keywords = AuthoritativeClass->GetMetaDataText(*FBlueprintMetadata::MD_FunctionKeywords.ToString(), TEXT("UObjectKeywords"), AuthoritativeClass->GetFullGroupName(false));
|
|
// add at least one character, so that PrimeDefaultUiSpec() doesn't
|
|
// attempt to query the template node
|
|
if (MenuSignature.Keywords.IsEmpty())
|
|
{
|
|
MenuSignature.Keywords = FText::FromString(TEXT(" "));
|
|
}
|
|
MenuSignature.IconName = FClassIconFinder::FindIconNameForClass(AuthoritativeClass);
|
|
MenuSignature.DocLink = TEXT("Shared/GraphNodes/Blueprint/UK2Node_AddComponent");
|
|
MenuSignature.DocExcerptTag = AuthoritativeClass->GetName();
|
|
|
|
return NodeSpawner;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
UBlueprintComponentNodeSpawner::UBlueprintComponentNodeSpawner(FObjectInitializer const& ObjectInitializer)
|
|
: Super(ObjectInitializer)
|
|
{
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
FBlueprintNodeSignature UBlueprintComponentNodeSpawner::GetSpawnerSignature() const
|
|
{
|
|
FBlueprintNodeSignature SpawnerSignature(NodeClass);
|
|
SpawnerSignature.AddSubObject(ComponentClass);
|
|
return SpawnerSignature;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Evolved from a combination of FK2ActionMenuBuilder::CreateAddComponentAction()
|
|
// and FEdGraphSchemaAction_K2AddComponent::PerformAction().
|
|
UEdGraphNode* UBlueprintComponentNodeSpawner::Invoke(UEdGraph* ParentGraph, FBindingSet const& Bindings, FVector2D const Location) const
|
|
{
|
|
auto PostSpawnLambda = [](UEdGraphNode* NewNode, bool bIsTemplateNode, FCustomizeNodeDelegate UserDelegate)
|
|
{
|
|
UK2Node_AddComponent* AddCompNode = CastChecked<UK2Node_AddComponent>(NewNode);
|
|
UBlueprint* Blueprint = AddCompNode->GetBlueprint();
|
|
|
|
UFunction* AddComponentFunc = FindFieldChecked<UFunction>(AActor::StaticClass(), UK2Node_AddComponent::GetAddComponentFunctionName());
|
|
AddCompNode->FunctionReference.SetFromField<UFunction>(AddComponentFunc, !bIsTemplateNode && FBlueprintEditorUtils::IsActorBased(Blueprint));
|
|
|
|
UserDelegate.ExecuteIfBound(NewNode, bIsTemplateNode);
|
|
};
|
|
|
|
FCustomizeNodeDelegate PostSpawnDelegate = FCustomizeNodeDelegate::CreateStatic(PostSpawnLambda, CustomizeNodeDelegate);
|
|
// let SpawnNode() allocate default pins (so we can modify them)
|
|
UK2Node_AddComponent* NewNode = Super::SpawnNode<UK2Node_AddComponent>(NodeClass, ParentGraph, FBindingSet(), Location, PostSpawnDelegate);
|
|
|
|
// set the return type to be the type of the template
|
|
UEdGraphPin* ReturnPin = NewNode->GetReturnValuePin();
|
|
if (ReturnPin != nullptr)
|
|
{
|
|
if (ComponentClass != nullptr)
|
|
{
|
|
ReturnPin->PinType.PinSubCategoryObject = *ComponentClass;
|
|
}
|
|
else
|
|
{
|
|
ReturnPin->PinType.PinSubCategoryObject = UActorComponent::StaticClass();
|
|
}
|
|
}
|
|
|
|
bool const bIsTemplateNode = FBlueprintNodeTemplateCache::IsTemplateOuter(ParentGraph);
|
|
if (!bIsTemplateNode)
|
|
{
|
|
TSubclassOf<UActorComponent> Class = ComponentClass;
|
|
if (Class == nullptr)
|
|
{
|
|
const ELoadFlags LoadFlags = LOAD_None;
|
|
UBlueprint* LoadedObject = LoadObject<UBlueprint>(NULL, *ComponentAssetName, NULL, LoadFlags, NULL);
|
|
if (LoadedObject == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
Class = TSubclassOf<UActorComponent>(Cast<UBlueprintGeneratedClass>(LoadedObject->GeneratedClass));
|
|
if (Class == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
UBlueprint* Blueprint = NewNode->GetBlueprint();
|
|
|
|
FName DesiredComponentName = MakeUniqueObjectName(Blueprint->GeneratedClass, Class, *Class->GetDisplayNameText().ToString());
|
|
UActorComponent* ComponentTemplate = NewObject<UActorComponent>(Blueprint->GeneratedClass, Class, DesiredComponentName, RF_ArchetypeObject | RF_Public);
|
|
|
|
Blueprint->ComponentTemplates.Add(ComponentTemplate);
|
|
|
|
// set the name of the template as the default for the TemplateName param
|
|
UEdGraphPin* TemplateNamePin = NewNode->GetTemplateNamePinChecked();
|
|
if (TemplateNamePin != nullptr)
|
|
{
|
|
TemplateNamePin->DefaultValue = ComponentTemplate->GetName();
|
|
}
|
|
NewNode->ReconstructNode();
|
|
}
|
|
|
|
// apply bindings, after we've setup the template pin
|
|
ApplyBindings(NewNode, Bindings);
|
|
|
|
return NewNode;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
FBlueprintActionUiSpec UBlueprintComponentNodeSpawner::GetUiSpec(FBlueprintActionContext const& Context, FBindingSet const& Bindings) const
|
|
{
|
|
UEdGraph* TargetGraph = (Context.Graphs.Num() > 0) ? Context.Graphs[0] : nullptr;
|
|
FBlueprintActionUiSpec MenuSignature = PrimeDefaultUiSpec(TargetGraph);
|
|
|
|
if (Bindings.Num() > 0)
|
|
{
|
|
FText AssetName;
|
|
if (UObject* AssetBinding = Bindings.CreateConstIterator()->Get())
|
|
{
|
|
AssetName = FText::FromName(AssetBinding->GetFName());
|
|
}
|
|
|
|
FText const ComponentTypeName = FText::FromName(ComponentClass->GetFName());
|
|
MenuSignature.MenuName = FText::Format(LOCTEXT("AddBoundComponentMenuName", "Add {0} (as {1})"), AssetName, ComponentTypeName);
|
|
MenuSignature.Tooltip = FText::Format(LOCTEXT("AddBoundComponentTooltip", "Spawn {0} using {1}"), ComponentTypeName, AssetName);
|
|
}
|
|
DynamicUiSignatureGetter.ExecuteIfBound(Context, Bindings, &MenuSignature);
|
|
return MenuSignature;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool UBlueprintComponentNodeSpawner::IsBindingCompatible(UObject const* BindingCandidate) const
|
|
{
|
|
bool bCanBindWith = false;
|
|
if (BindingCandidate->IsAsset())
|
|
{
|
|
TArray< TSubclassOf<UActorComponent> > ComponentClasses = FComponentAssetBrokerage::GetComponentsForAsset(BindingCandidate);
|
|
bCanBindWith = ComponentClasses.Contains(ComponentClass);
|
|
}
|
|
return bCanBindWith;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool UBlueprintComponentNodeSpawner::BindToNode(UEdGraphNode* Node, UObject* Binding) const
|
|
{
|
|
bool bSuccessfulBinding = false;
|
|
UK2Node_AddComponent* AddCompNode = CastChecked<UK2Node_AddComponent>(Node);
|
|
|
|
UActorComponent* ComponentTemplate = AddCompNode->GetTemplateFromNode();
|
|
if (ComponentTemplate != nullptr)
|
|
{
|
|
bSuccessfulBinding = FComponentAssetBrokerage::AssignAssetToComponent(ComponentTemplate, Binding);
|
|
AddCompNode->ReconstructNode();
|
|
}
|
|
return bSuccessfulBinding;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
TSubclassOf<UActorComponent> UBlueprintComponentNodeSpawner::GetComponentClass() const
|
|
{
|
|
return ComponentClass;
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|