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

290 lines
10 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "K2Node_GenericToText.h"
#include "BlueprintActionDatabaseRegistrar.h"
#include "BlueprintNodeSpawner.h"
#include "EdGraph/EdGraph.h"
#include "EdGraphSchema_K2.h"
#include "EditorCategoryUtils.h"
#include "K2Node_CallFunction.h"
#include "Kismet/KismetTextLibrary.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Kismet2/CompilerResultsLog.h"
#include "KismetCompiler.h"
#define LOCTEXT_NAMESPACE "K2Node_GenericToText"
/////////////////////////////////////////////////////
// K2Node_GenericConvToText
namespace UE::Blueprint::Private
{
const FLazyName PC_Value = "Value";
const TArrayView<const UScriptStruct*> GetToTextSupportedScriptStruct()
{
static const UScriptStruct* List[] = { TBaseStructure<FVector>::Get()
, TBaseStructure<FVector2D>::Get()
, TBaseStructure<FRotator>::Get()
, TBaseStructure<FTransform>::Get()
, TBaseStructure<FLinearColor>::Get()
, TBaseStructure<FDateTime>::Get()
};
return MakeArrayView(List);
}
bool IsGenericNumericProperty(FName OtherPinCategory)
{
return OtherPinCategory == UEdGraphSchema_K2::PC_Boolean
|| OtherPinCategory == UEdGraphSchema_K2::PC_Byte
|| OtherPinCategory == UEdGraphSchema_K2::PC_Enum
|| OtherPinCategory == UEdGraphSchema_K2::PC_Int
|| OtherPinCategory == UEdGraphSchema_K2::PC_Int64
|| OtherPinCategory == UEdGraphSchema_K2::PC_Real
|| OtherPinCategory == UEdGraphSchema_K2::PC_Double
|| OtherPinCategory == UEdGraphSchema_K2::PC_Float;
}
}
void UK2Node_GenericToText::AllocateDefaultPins()
{
Super::AllocateDefaultPins();
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Wildcard, UE::Blueprint::Private::PC_Value.Resolve());
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Text, UEdGraphSchema_K2::PN_ReturnValue);
}
void UK2Node_GenericToText::SynchronizeArgumentPinType()
{
bool bPinTypeChanged = false;
UEdGraphPin* InputPin = FindPinChecked(UE::Blueprint::Private::PC_Value.Resolve(), EEdGraphPinDirection::EGPD_Input);
if (InputPin->LinkedTo.Num() == 0)
{
static const FEdGraphPinType WildcardPinType = FEdGraphPinType(UEdGraphSchema_K2::PC_Wildcard, NAME_None, nullptr, EPinContainerType::None, false, FEdGraphTerminalType());
// Ensure wildcard
if (InputPin->PinType != WildcardPinType)
{
InputPin->PinType = WildcardPinType;
bPinTypeChanged = true;
}
}
else
{
UEdGraphPin* SourcePin = InputPin->LinkedTo[0];
// Take the type of the connected pin
if (InputPin->PinType != SourcePin->PinType)
{
InputPin->PinType = SourcePin->PinType;
bPinTypeChanged = true;
}
}
if (bPinTypeChanged)
{
// Let the graph know to refresh
GetGraph()->NotifyNodeChanged(this);
UBlueprint* Blueprint = GetBlueprint();
if (!Blueprint->bBeingCompiled)
{
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
}
}
}
FText UK2Node_GenericToText::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
return LOCTEXT("ToText_Title", "To Text");
}
void UK2Node_GenericToText::PinConnectionListChanged(UEdGraphPin* Pin)
{
Modify();
// Potentially update an argument pin type
SynchronizeArgumentPinType();
}
void UK2Node_GenericToText::PinTypeChanged(UEdGraphPin* Pin)
{
SynchronizeArgumentPinType();
Super::PinTypeChanged(Pin);
}
void UK2Node_GenericToText::NodeConnectionListChanged()
{
SynchronizeArgumentPinType();
Super::NodeConnectionListChanged();
}
FText UK2Node_GenericToText::GetTooltipText() const
{
return LOCTEXT("NodeTooltip", "Converts numeric value, enum, string, name and some basic structure to text.\n \u2022 The basic type are Vector, Vector2D, Rotator, Transform, LinearColor, DateTime.");
}
void UK2Node_GenericToText::PostReconstructNode()
{
Super::PostReconstructNode();
if (!IsTemplate())
{
// Make sure we're not dealing with a menu node
UEdGraph* OuterGraph = GetGraph();
if (OuterGraph && OuterGraph->Schema)
{
// Potentially update an argument pin type
SynchronizeArgumentPinType();
}
}
}
void UK2Node_GenericToText::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
Super::ExpandNode(CompilerContext, SourceGraph);
UEdGraphPin* InputPin = FindPinChecked(UE::Blueprint::Private::PC_Value.Resolve(), EEdGraphPinDirection::EGPD_Input);
if (InputPin == nullptr)
{
CompilerContext.MessageLog.Error(*LOCTEXT("Error_BadInputPin", "Invalid input pin.").ToString());
return;
}
if (InputPin->LinkedTo.Num() != 1 || InputPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Wildcard)
{
CompilerContext.MessageLog.Error(*LOCTEXT("Error_BadLinkedInputPin", "Input pin is not linked to a value.").ToString());
return;
}
// Convert to the correct function call
auto CreateFunctionCall = [Self = this, &CompilerContext, SourceGraph](FName FunctionName)
{
UK2Node_CallFunction* CallFunction = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(Self, SourceGraph);
CallFunction->SetFromFunction(UKismetTextLibrary::StaticClass()->FindFunctionByName(FunctionName));
check(CallFunction->IsNodePure());
CallFunction->AllocateDefaultPins();
CompilerContext.MessageLog.NotifyIntermediateObjectCreation(CallFunction, Self);
return CallFunction;
};
UK2Node_CallFunction* CallFunction = nullptr;
UEdGraphPin* CallFunctionInputValue = nullptr;
if (UE::Blueprint::Private::IsGenericNumericProperty(InputPin->PinType.PinCategory))
{
CallFunction = CreateFunctionCall(GET_MEMBER_NAME_CHECKED(UKismetTextLibrary, Conv_NumericPropertyToText));
CallFunctionInputValue = CallFunction->FindPinChecked(TEXT("Value"));
// Set the generic type
CallFunctionInputValue->PinType = InputPin->PinType;
}
else if (InputPin->PinType.PinCategory == UEdGraphSchema_K2::PC_String)
{
CallFunction = CreateFunctionCall(GET_MEMBER_NAME_CHECKED(UKismetTextLibrary, Conv_StringToText));
CallFunctionInputValue = CallFunction->FindPinChecked(TEXT("InString"));
}
else if (InputPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Name)
{
CallFunction = CreateFunctionCall(GET_MEMBER_NAME_CHECKED(UKismetTextLibrary, Conv_NameToText));
CallFunctionInputValue = CallFunction->FindPinChecked(TEXT("InName"));
}
else if (InputPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Struct)
{
const UObject* PinSubCategoryObject = InputPin->PinType.PinSubCategoryObject.Get();
if (PinSubCategoryObject == TBaseStructure<FVector>::Get())
{
CallFunction = CreateFunctionCall(GET_MEMBER_NAME_CHECKED(UKismetTextLibrary, Conv_VectorToText));
CallFunctionInputValue = CallFunction->FindPinChecked(TEXT("InVec"));
}
else if (PinSubCategoryObject == TBaseStructure<FVector2D>::Get())
{
CallFunction = CreateFunctionCall(GET_MEMBER_NAME_CHECKED(UKismetTextLibrary, Conv_Vector2dToText));
CallFunctionInputValue = CallFunction->FindPinChecked(TEXT("InVec"));
}
else if (PinSubCategoryObject == TBaseStructure<FRotator>::Get())
{
CallFunction = CreateFunctionCall(GET_MEMBER_NAME_CHECKED(UKismetTextLibrary, Conv_RotatorToText));
CallFunctionInputValue = CallFunction->FindPinChecked(TEXT("InRot"));
}
else if (PinSubCategoryObject == TBaseStructure<FTransform>::Get())
{
CallFunction = CreateFunctionCall(GET_MEMBER_NAME_CHECKED(UKismetTextLibrary, Conv_TransformToText));
CallFunctionInputValue = CallFunction->FindPinChecked(TEXT("InTrans"));
}
else if (PinSubCategoryObject == TBaseStructure<FLinearColor>::Get())
{
CallFunction = CreateFunctionCall(GET_MEMBER_NAME_CHECKED(UKismetTextLibrary, Conv_ColorToText));
CallFunctionInputValue = CallFunction->FindPinChecked(TEXT("InColor"));
}
else if (PinSubCategoryObject == TBaseStructure<FDateTime>::Get())
{
CallFunction = CreateFunctionCall(GET_MEMBER_NAME_CHECKED(UKismetTextLibrary, AsDateTime_DateTime));
CallFunctionInputValue = CallFunction->FindPinChecked(TEXT("In"));
}
else
{
check(!UE::Blueprint::Private::GetToTextSupportedScriptStruct().Contains(Cast<UScriptStruct>(PinSubCategoryObject)));
}
}
if (CallFunction == nullptr || CallFunctionInputValue == nullptr)
{
CompilerContext.MessageLog.Error(*LOCTEXT("Error_BadPinCategory", "The input pin could not generate a valid ToText.").ToString());
return;
}
// Move connection of ToText's to the generated function.
CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(UEdGraphSchema_K2::PN_ReturnValue), *CallFunction->GetReturnValuePin());
CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(TEXT("Value")), *CallFunctionInputValue);
BreakAllNodeLinks();
}
bool UK2Node_GenericToText::IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const
{
UEdGraphPin* InputPin = FindPinChecked(UE::Blueprint::Private::PC_Value.Resolve(), EEdGraphPinDirection::EGPD_Input);
if (InputPin == MyPin && MyPin->Direction == EGPD_Input)
{
const FName OtherPinCategory = OtherPin->PinType.PinCategory;
bool bIsValidType = UE::Blueprint::Private::IsGenericNumericProperty(OtherPinCategory)
|| OtherPinCategory == UEdGraphSchema_K2::PC_String
|| OtherPinCategory == UEdGraphSchema_K2::PC_Name;
if (!bIsValidType)
{
const UObject* PinSubCategoryObject = OtherPin->PinType.PinSubCategoryObject.Get();
const bool bIsScriptStruct = OtherPinCategory == UEdGraphSchema_K2::PC_Struct
&& UE::Blueprint::Private::GetToTextSupportedScriptStruct().Contains(Cast<UScriptStruct>(PinSubCategoryObject));
bIsValidType = bIsScriptStruct;
}
if (!bIsValidType)
{
OutReason = LOCTEXT("Error_InvalidArgumentType", "To Text arguments may only be Byte, Integer, Int64, Float, Double, Text, String, Name, Boolean, Enum, and basic struct.").ToString();
return true;
}
}
return Super::IsConnectionDisallowed(MyPin, OtherPin, OutReason);
}
void UK2Node_GenericToText::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
{
UClass* ActionKey = GetClass();
if (ActionRegistrar.IsOpenForRegistration(ActionKey))
{
UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass());
ActionRegistrar.AddBlueprintAction(ActionKey, NodeSpawner);
}
}
FText UK2Node_GenericToText::GetMenuCategory() const
{
return FEditorCategoryUtils::GetCommonCategory(FCommonEditorCategory::Text);
}
#undef LOCTEXT_NAMESPACE