Files
UnrealEngineUWP/Engine/Source/Editor/BlueprintGraph/Private/K2Node_BreakStruct.cpp
Maciej Mroz 4e272a43ac UDS can be based on TableRowBase - first version.
Better handling CPF_BlueprintVisible.
DataTable uses DisplayName.

[CL 2248538 by Maciej Mroz in Main branch]
2014-08-08 11:13:17 -04:00

333 lines
11 KiB
C++

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
#include "BlueprintGraphPrivatePCH.h"
#include "KismetCompiler.h"
#include "BlueprintNodeSpawner.h"
#include "EditorCategoryUtils.h"
#define LOCTEXT_NAMESPACE "K2Node_BreakStruct"
//////////////////////////////////////////////////////////////////////////
// FKCHandler_BreakStruct
class FKCHandler_BreakStruct : public FNodeHandlingFunctor
{
public:
FKCHandler_BreakStruct(FKismetCompilerContext& InCompilerContext)
: FNodeHandlingFunctor(InCompilerContext)
{
}
FBPTerminal* RegisterInputTerm(FKismetFunctionContext& Context, UK2Node_BreakStruct* Node)
{
check(NULL != Node);
if(NULL == Node->StructType)
{
CompilerContext.MessageLog.Error(*LOCTEXT("BreakStruct_UnknownStructure_Error", "Unknown structure to break for @@").ToString(), Node);
return NULL;
}
//Find input pin
UEdGraphPin* InputPin = NULL;
for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex)
{
UEdGraphPin* Pin = Node->Pins[PinIndex];
if(Pin && (EGPD_Input == Pin->Direction))
{
InputPin = Pin;
break;
}
}
check(NULL != InputPin);
//Find structure source net
UEdGraphPin* Net = FEdGraphUtilities::GetNetFromPin(InputPin);
check(NULL != Net);
FBPTerminal** FoundTerm = Context.NetMap.Find(Net);
FBPTerminal* Term = FoundTerm ? *FoundTerm : NULL;
if(NULL == Term)
{
// Dont allow literal
if ((Net->Direction == EGPD_Input) && (Net->LinkedTo.Num() == 0))
{
CompilerContext.MessageLog.Error(*LOCTEXT("InvalidNoInputStructure_Error", "No input structure to break for @@").ToString(), Net);
return NULL;
}
// standard register net
else
{
Term = new (Context.IsEventGraph() ? Context.EventGraphLocals : Context.Locals) FBPTerminal();
Term->CopyFromPin(Net, Context.NetNameMap->MakeValidName(Net));
}
Context.NetMap.Add(Net, Term);
}
UStruct* StructInTerm = Cast<UStruct>(Term->Type.PinSubCategoryObject.Get());
if(NULL == StructInTerm || !StructInTerm->IsChildOf(Node->StructType))
{
CompilerContext.MessageLog.Error(*LOCTEXT("BreakStruct_NoMatch_Error", "Structures don't match for @@").ToString(), Node);
}
return Term;
}
void RegisterOutputTerm(FKismetFunctionContext& Context, UScriptStruct* StructType, UEdGraphPin* Net, FBPTerminal* ContextTerm)
{
UProperty* BoundProperty = FindField<UProperty>(StructType, *(Net->PinName));
if (BoundProperty != NULL)
{
FBPTerminal* Term = new (Context.IsEventGraph() ? Context.EventGraphLocals : Context.Locals) FBPTerminal();
Term->CopyFromPin(Net, Net->PinName);
Term->AssociatedVarProperty = BoundProperty;
Context.NetMap.Add(Net, Term);
Term->Context = ContextTerm;
if (BoundProperty->HasAnyPropertyFlags(CPF_BlueprintReadOnly))
{
Term->bIsConst = true;
}
}
else
{
CompilerContext.MessageLog.Error(TEXT("Failed to find a struct member for @@"), Net);
}
}
virtual void RegisterNets(FKismetFunctionContext& Context, UEdGraphNode* InNode) override
{
UK2Node_BreakStruct* Node = Cast<UK2Node_BreakStruct>(InNode);
check(NULL != Node);
if(!UK2Node_BreakStruct::CanBeBroken(Node->StructType))
{
CompilerContext.MessageLog.Warning(*LOCTEXT("BreakStruct_NoBreak_Error", "The structure cannot be broken using generic 'break' node @@. Try use specialized 'break' function if available.").ToString(), Node);
}
if(FBPTerminal* StructContextTerm = RegisterInputTerm(Context, Node))
{
for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex)
{
UEdGraphPin* Pin = Node->Pins[PinIndex];
if(NULL != Pin && EGPD_Output == Pin->Direction)
{
RegisterOutputTerm(Context, Node->StructType, Pin, StructContextTerm);
}
}
}
}
};
UK2Node_BreakStruct::UK2Node_BreakStruct(const class FPostConstructInitializeProperties& PCIP) : Super(PCIP)
{
}
bool UK2Node_BreakStruct::CanCreatePinForProperty(const UProperty* Property)
{
const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
FEdGraphPinType DumbGraphPinType;
const bool bConvertable = Schema->ConvertPropertyToPinType(Property, /*out*/ DumbGraphPinType);
//TODO: remove CPF_Edit in a next release.
const bool bVisible = Property ? Property->HasAnyPropertyFlags(CPF_Edit | CPF_BlueprintVisible) : false;
return bVisible && bConvertable;
}
bool UK2Node_BreakStruct::CanBeBroken(const UScriptStruct* Struct)
{
const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
if(Struct && Schema && !Struct->HasMetaData(TEXT("HasNativeBreak")))
{
for (TFieldIterator<UProperty> It(Struct); It; ++It)
{
if (CanCreatePinForProperty(*It))
{
return true;
}
}
}
return false;
}
void UK2Node_BreakStruct::AllocateDefaultPins()
{
const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
if(Schema && StructType)
{
CreatePin(EGPD_Input, Schema->PC_Struct, TEXT(""), StructType, false, true, StructType->GetName(), true);
struct FBreakStructPinManager : public FStructOperationOptionalPinManager
{
virtual bool CanTreatPropertyAsOptional(UProperty* TestProperty) const override
{
return UK2Node_BreakStruct::CanCreatePinForProperty(TestProperty);
}
};
{
FBreakStructPinManager OptionalPinManager;
OptionalPinManager.RebuildPropertyList(ShowPinForProperties, StructType);
OptionalPinManager.CreateVisiblePins(ShowPinForProperties, StructType, EGPD_Output, this);
}
// When struct has a lot of fields, mark their pins as advanced
if(Pins.Num() > 5)
{
if(ENodeAdvancedPins::NoPins == AdvancedPinDisplay)
{
AdvancedPinDisplay = ENodeAdvancedPins::Hidden;
}
for(int32 PinIndex = 3; PinIndex < Pins.Num(); ++PinIndex)
{
if(UEdGraphPin * EdGraphPin = Pins[PinIndex])
{
EdGraphPin->bAdvancedView = true;
}
}
}
}
}
FText UK2Node_BreakStruct::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
FFormatNamedArguments Args;
Args.Add(TEXT("StructName"), FText::FromString(StructType ? StructType->GetName() : FString()));
return FText::Format(LOCTEXT("BreakNodeTitle", "Break {StructName}"), Args);
}
FString UK2Node_BreakStruct::GetTooltip() const
{
return FString::Printf(
*LOCTEXT("MakeStruct_Tooltip", "Adds a node that breaks a '%s' into its member fields").ToString(),
*(StructType ? StructType->GetName() : FString())
);
}
void UK2Node_BreakStruct::ValidateNodeDuringCompilation(class FCompilerResultsLog& MessageLog) const
{
if(!StructType)
{
MessageLog.Error(*LOCTEXT("NoStruct_Error", "No Struct in @@").ToString(), this);
}
else
{
bool bHasAnyBlueprintVisibleProperty = false;
for (TFieldIterator<UProperty> It(StructType); It; ++It)
{
const UProperty* Property = *It;
if (CanCreatePinForProperty(Property))
{
const bool bIsBlueprintVisible = Property->HasAnyPropertyFlags(CPF_BlueprintVisible) || (Property->GetOwnerStruct() && Property->GetOwnerStruct()->IsA<UUserDefinedStruct>());
bHasAnyBlueprintVisibleProperty |= bIsBlueprintVisible;
const UEdGraphPin* Pin = Property ? FindPin(Property->GetName()) : NULL;
const bool bIsLinked = Pin && Pin->LinkedTo.Num();
if (!bIsBlueprintVisible && bIsLinked)
{
MessageLog.Warning(*LOCTEXT("PropertyIsNotBPVisible_Warning", "@@ - the native property is not tagged as BlueprintReadWrite or BlueprintReadOnly, the pin will be removed in a future release.").ToString(), Pin);
}
if ((Property->ArrayDim > 1) && bIsLinked)
{
MessageLog.Warning(*LOCTEXT("StaticArray_Warning", "@@ - the native property is a static array, which is not supported by blueprints").ToString(), Pin);
}
}
}
if (!bHasAnyBlueprintVisibleProperty)
{
MessageLog.Warning(*LOCTEXT("StructHasNoBPVisibleProperties_Warning", "@@ has no property tagged as BlueprintReadWrite or BlueprintReadOnly. The node will be removed in a future release.").ToString(), this);
}
}
}
FLinearColor UK2Node_BreakStruct::GetNodeTitleColor() const
{
if(const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>())
{
FEdGraphPinType PinType;
PinType.PinCategory = K2Schema->PC_Struct;
PinType.PinSubCategoryObject = StructType;
return K2Schema->GetPinTypeColor(PinType);
}
return UK2Node::GetNodeTitleColor();
}
UK2Node::ERedirectType UK2Node_BreakStruct::DoPinsMatchForReconstruction(const UEdGraphPin* NewPin, int32 NewPinIndex, const UEdGraphPin* OldPin, int32 OldPinIndex) const
{
ERedirectType Result = UK2Node::DoPinsMatchForReconstruction(NewPin, NewPinIndex, OldPin, OldPinIndex);
if ((ERedirectType_None == Result) && NewPin && OldPin)
{
if ((EGPD_Input == NewPin->Direction) && (EGPD_Input == OldPin->Direction))
{
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
if (K2Schema->ArePinTypesCompatible( NewPin->PinType, OldPin->PinType))
{
Result = ERedirectType_Custom;
}
}
else if ((EGPD_Output == NewPin->Direction) && (EGPD_Output == OldPin->Direction))
{
TMap<FName, FName>* StructRedirects = UStruct::TaggedPropertyRedirects.Find(StructType->GetFName());
if (StructRedirects)
{
FName* PropertyRedirect = StructRedirects->Find(FName(*OldPin->PinName));
if (PropertyRedirect)
{
Result = ((FCString::Stricmp(*PropertyRedirect->ToString(), *NewPin->PinName) != 0) ? ERedirectType_None : ERedirectType_Name);
}
}
}
}
return Result;
}
FNodeHandlingFunctor* UK2Node_BreakStruct::CreateNodeHandler(class FKismetCompilerContext& CompilerContext) const
{
return new FKCHandler_BreakStruct(CompilerContext);
}
void UK2Node_BreakStruct::GetMenuActions(TArray<UBlueprintNodeSpawner*>& ActionListOut) const
{
for (TObjectIterator<UScriptStruct> StructIt; StructIt; ++StructIt)
{
UScriptStruct const* Struct = (*StructIt);
// we only want to add autonomous structs here; those belonging to a
// certain class should instead be associated with that class (so when
// the class is modified we can easily handle any structs that were changed).
bool bIsStandaloneStruct = Struct->GetOuter()->IsA(UPackage::StaticClass());
if (!bIsStandaloneStruct || !UEdGraphSchema_K2::IsAllowableBlueprintVariableType(Struct) || !CanBeBroken(Struct))
{
continue;
}
auto CustomizeBreakNodeLambda = [](UEdGraphNode* NewNode, bool bIsTemplateNode, TWeakObjectPtr<UScriptStruct> StructPtr)
{
UK2Node_BreakStruct* BreakNode = CastChecked<UK2Node_BreakStruct>(NewNode);
if (StructPtr.IsValid())
{
BreakNode->StructType = StructPtr.Get();
}
};
UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass());
check(NodeSpawner != nullptr);
ActionListOut.Add(NodeSpawner);
TWeakObjectPtr<UScriptStruct> StructPtr = Struct;
NodeSpawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(CustomizeBreakNodeLambda, StructPtr);
}
}
FText UK2Node_BreakStruct::GetMenuCategory() const
{
return FEditorCategoryUtils::GetCommonCategory(FCommonEditorCategory::Struct);
}
#undef LOCTEXT_NAMESPACE