Files
UnrealEngineUWP/Engine/Source/Editor/BlueprintGraph/Private/K2Node_CustomEvent.cpp

376 lines
12 KiB
C++
Raw Normal View History

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
#include "BlueprintGraphPrivatePCH.h"
#include "Kismet2NameValidators.h"
#include "CompilerResultsLog.h"
#include "BlueprintEventNodeSpawner.h"
#define LOCTEXT_NAMESPACE "K2Node_CustomEvent"
#define SNAP_GRID (16) // @todo ensure this is the same as SNodePanel::GetSnapGridSize()
/**
* Attempts to find a CustomEvent node associated with the specified function.
*
* @param CustomEventFunc The function you want to find an associated node for.
* @return A pointer to the found node (NULL if a corresponding node wasn't found)
*/
static UK2Node_CustomEvent const* FindCustomEventNodeFromFunction(UFunction* CustomEventFunc)
{
UK2Node_CustomEvent const* FoundEventNode = NULL;
if (CustomEventFunc != NULL)
{
UObject const* const FuncOwner = CustomEventFunc->GetOuter();
check(FuncOwner != NULL);
// if the found function is a NOT a native function (it's user generated)
if (FuncOwner->IsA(UBlueprintGeneratedClass::StaticClass()))
{
UBlueprintGeneratedClass* FuncClass = Cast<UBlueprintGeneratedClass>(CustomEventFunc->GetOuter());
check(FuncClass != NULL);
UBlueprint* FuncBlueprint = Cast<UBlueprint>(FuncClass->ClassGeneratedBy);
check(FuncBlueprint != NULL);
TArray<UK2Node_CustomEvent*> BpCustomEvents;
FBlueprintEditorUtils::GetAllNodesOfClass<UK2Node_CustomEvent>(FuncBlueprint, BpCustomEvents);
// look to see if the function that this is overriding is a custom-event
for (UK2Node_CustomEvent const* const UserEvent : BpCustomEvents)
{
if (UserEvent->CustomFunctionName == CustomEventFunc->GetFName())
{
FoundEventNode = UserEvent;
break;
}
}
}
}
return FoundEventNode;
}
/**
* Custom handler for validating CustomEvent renames
*/
class FCustomEventNameValidator : public FKismetNameValidator
{
public:
FCustomEventNameValidator(UK2Node_CustomEvent const* CustomEventIn)
: FKismetNameValidator(CustomEventIn->GetBlueprint(), CustomEventIn->CustomFunctionName)
, CustomEvent(CustomEventIn)
{
check(CustomEvent != NULL);
}
// Begin INameValidatorInterface
virtual EValidatorResult IsValid(FString const& Name, bool bOriginal = false) override
{
EValidatorResult NameValidity = FKismetNameValidator::IsValid(Name, bOriginal);
if ((NameValidity == EValidatorResult::Ok) || (NameValidity == EValidatorResult::ExistingName))
{
UBlueprint* Blueprint = CustomEvent->GetBlueprint();
check(Blueprint != NULL);
UFunction* ParentFunction = FindField<UFunction>(Blueprint->ParentClass, *Name);
// if this custom-event is overriding a function belonging to the blueprint's parent
if (ParentFunction != NULL)
{
UK2Node_CustomEvent const* OverriddenEvent = FindCustomEventNodeFromFunction(ParentFunction);
// if the function that we're overriding isn't another custom event,
// then we can't name it this (only allow custom-event to override other custom-events)
if (OverriddenEvent == NULL)
{
NameValidity = EValidatorResult::AlreadyInUse;
}
}
}
return NameValidity;
}
// End INameValidatorInterface
private:
UK2Node_CustomEvent const* CustomEvent;
};
UK2Node_CustomEvent::UK2Node_CustomEvent(const class FPostConstructInitializeProperties& PCIP)
: Super(PCIP)
{
bOverrideFunction = false;
bIsEditable = true;
bCanRenameNode = true;
Added the ability to flag custom events as CallInEditor and extended the main editor actor details view to provide a drop list in the blueprints to call valid meta tagged functions. #TTP 337860 - Expose flagged BlueprintEvents as buttons in the Details panel that can be run in-editor #Branch UE4 #Proj Engine, DetailsCustomization, Kismet, Kismetcompiler, BlueprintGraph #Change Added FName FBlueprintMetadata::MD_CallInEditor as "CallInEditor" #Change Added bool UProperty UK2Node_CustomEvent::bCallInEditor to enable call in editor states to be serialised with UK2Node_CustomEvent #Change Modified FBlueprintGraphActionDetails::CustomizeDetails to add support for the CallInEditor tickbox. #Change Modified UK2Node_CustomEvent::GetPaletteIcon to return a different node icon when call in editor is enabled. #Added new icon GraphEditor.CallInEditorEvent_16x for the node corner icons and the blutility combo menu. #Change Added FKismetUserDeclaredFunctionMetadata::bCallInEditorProperty to help propagate call in editor setting from customevent node's to compiler generated event function entry point #Change Modified FKismetCompilerContext::CreateFunctionStubForEvent to check customevent node bCallInEditor properties and mark the generated function entry nodes as CallInEditor. This was required as a transitional phase to so the setting could be observed later during meta data generation. #Change Modified FKismetCompilerContext::FinishCompilingFunction to check function entry points for call in editor flags in the attached FKismetUserDeclaredFunctionMetadata and add CallInEditor meta data if present. #Change Modified AActor::ProcessEvent to allow events to be processed by the actor instance if the Function has CallInEditor meta data set to true #Change Added a series of functions to FActorDetails to conditionally add a blutility category and a combo list ( displaying the available blutility functions from the current actor blueprint ) and a button to call the selected function on the actor selection. ReviewedBy Chris.Wood #codereview Nick.Whiting [CL 2230263 by Ben Cosh in Main branch]
2014-07-24 13:30:26 -04:00
bCallInEditor = false;
}
FText UK2Node_CustomEvent::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
if (CustomFunctionName.IsNone() && (TitleType == ENodeTitleType::ListView))
{
return LOCTEXT("ActionMenuTitle", "Custom Event...");
}
else if ((TitleType == ENodeTitleType::EditableTitle) || (TitleType == ENodeTitleType::ListView))
{
return FText::FromName(CustomFunctionName);
}
else
{
FString RPCString = UK2Node_Event::GetLocalizedNetString(FunctionFlags, false);
FFormatNamedArguments Args;
Args.Add(TEXT("FunctionName"), FText::FromName(CustomFunctionName));
Args.Add(TEXT("RPCString"), FText::FromString(RPCString));
return FText::Format(NSLOCTEXT("K2Node", "CustomEvent_Name", "{FunctionName}{RPCString}\nCustom Event"), Args);
}
}
UEdGraphPin* UK2Node_CustomEvent::CreatePinFromUserDefinition(const TSharedPtr<FUserPinInfo> NewPinInfo)
{
UEdGraphPin* NewPin = CreatePin(
EGPD_Output,
NewPinInfo->PinType.PinCategory,
NewPinInfo->PinType.PinSubCategory,
NewPinInfo->PinType.PinSubCategoryObject.Get(),
NewPinInfo->PinType.bIsArray,
NewPinInfo->PinType.bIsReference,
NewPinInfo->PinName);
NewPin->DefaultValue = NewPin->AutogeneratedDefaultValue = NewPinInfo->PinDefaultValue;
return NewPin;
}
void UK2Node_CustomEvent::RenameCustomEventCloseToName(int32 StartIndex)
{
bool bFoundName = false;
const FString& BaseName = CustomFunctionName.ToString();
for (int32 NameIndex = StartIndex; !bFoundName; ++NameIndex)
{
const FString NewName = FString::Printf(TEXT("%s_%d"), *BaseName, NameIndex);
if (Rename(*NewName, GetOuter(), REN_Test))
{
UBlueprint* Blueprint = GetBlueprint();
CustomFunctionName = FName(NewName.GetCharArray().GetData());
Rename(*NewName, GetOuter(), (Blueprint->bIsRegeneratingOnLoad ? REN_ForceNoResetLoaders : 0) | REN_DontCreateRedirectors);
bFoundName = true;
}
}
}
void UK2Node_CustomEvent::OnRenameNode(const FString& NewName)
{
CustomFunctionName = *NewName;
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(GetBlueprint());
}
TSharedPtr<class INameValidatorInterface> UK2Node_CustomEvent::MakeNameValidator() const
{
return MakeShareable(new FCustomEventNameValidator(this));
}
bool UK2Node_CustomEvent::IsOverride() const
{
UBlueprint* Blueprint = GetBlueprint();
check(Blueprint != NULL);
UFunction* ParentFunction = FindField<UFunction>(Blueprint->ParentClass, CustomFunctionName);
UK2Node_CustomEvent const* OverriddenEvent = FindCustomEventNodeFromFunction(ParentFunction);
return (OverriddenEvent != NULL);
}
uint32 UK2Node_CustomEvent::GetNetFlags() const
{
uint32 NetFlags = (FunctionFlags & FUNC_NetFuncFlags);
if (IsOverride())
{
UBlueprint* Blueprint = GetBlueprint();
check(Blueprint != NULL);
UFunction* ParentFunction = FindField<UFunction>(Blueprint->ParentClass, CustomFunctionName);
check(ParentFunction != NULL);
// inherited net flags take precedence
NetFlags = (ParentFunction->FunctionFlags & FUNC_NetFuncFlags);
}
return NetFlags;
}
void UK2Node_CustomEvent::ValidateNodeDuringCompilation(class FCompilerResultsLog& MessageLog) const
{
Super::ValidateNodeDuringCompilation(MessageLog);
UBlueprint* Blueprint = GetBlueprint();
check(Blueprint != NULL);
UFunction* ParentFunction = FindField<UFunction>(Blueprint->ParentClass, CustomFunctionName);
// if this custom-event is overriding a function belonging to the blueprint's parent
if (ParentFunction != NULL)
{
UObject const* const FuncOwner = ParentFunction->GetOuter();
check(FuncOwner != NULL);
// if this custom-event is attempting to override a native function, we can't allow that
if (!FuncOwner->IsA(UBlueprintGeneratedClass::StaticClass()))
{
MessageLog.Error(*FString::Printf(*LOCTEXT("NativeFunctionConflict", "@@ name conflicts with a native '%s' function").ToString(), *FuncOwner->GetName()), this);
}
else
{
UK2Node_CustomEvent const* OverriddenEvent = FindCustomEventNodeFromFunction(ParentFunction);
// if the function that this is attempting to override is NOT another
// custom-event, then we want to error (a custom-event shouldn't override something different)
if (OverriddenEvent == NULL)
{
MessageLog.Error(*FString::Printf(*LOCTEXT("NonCustomEventOverride", "@@ name conflicts with a '%s' function").ToString(), *FuncOwner->GetName()), this);
}
// else, we assume the user was attempting to override the parent's custom-event
// the signitures could still be off, but FKismetCompilerContext::PrecompileFunction() should catch that
}
}
}
void UK2Node_CustomEvent::GetMenuActions(TArray<UBlueprintNodeSpawner*>& ActionListOut) const
{
UBlueprintNodeSpawner* NodeSpawner = UBlueprintEventNodeSpawner::Create(GetClass(), FName());
check(NodeSpawner != nullptr);
auto SetupCustomEventNodeLambda = [](UEdGraphNode* NewNode, bool bIsTemplateNode)
{
UK2Node_CustomEvent* EventNode = CastChecked<UK2Node_CustomEvent>(NewNode);
UBlueprint* Blueprint = EventNode->GetBlueprint();
// in GetNodeTitle(), we use an empty CustomFunctionName to identify a menu entry
if (!bIsTemplateNode)
{
EventNode->CustomFunctionName = FBlueprintEditorUtils::FindUniqueCustomEventName(Blueprint);
}
EventNode->bIsEditable = true;
};
NodeSpawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(SetupCustomEventNodeLambda);
ActionListOut.Add(NodeSpawner);
}
void UK2Node_CustomEvent::ReconstructNode()
{
const UEdGraphPin* DelegateOutPin = FindPin(DelegateOutputName);
const UK2Node_BaseMCDelegate* OtherNode = (DelegateOutPin && DelegateOutPin->LinkedTo.Num() && DelegateOutPin->LinkedTo[0]) ?
Cast<const UK2Node_BaseMCDelegate>(DelegateOutPin->LinkedTo[0]->GetOwningNode()) : NULL;
const UFunction* DelegateSignature = OtherNode ? OtherNode->GetDelegateSignature() : NULL;
const bool bUseDelegateSignature = (NULL == FindEventSignatureFunction()) && DelegateSignature;
if (bUseDelegateSignature)
{
UserDefinedPins.Empty();
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
for (TFieldIterator<UProperty> PropIt(DelegateSignature); PropIt && (PropIt->PropertyFlags & CPF_Parm); ++PropIt)
{
const UProperty* Param = *PropIt;
if (!Param->HasAnyPropertyFlags(CPF_OutParm) || Param->HasAnyPropertyFlags(CPF_ReferenceParm))
{
FEdGraphPinType PinType;
K2Schema->ConvertPropertyToPinType(Param, /*out*/ PinType);
FString NewPinName = Param->GetName();
int32 Index = 1;
while ((DelegateOutputName == NewPinName) || (K2Schema->PN_Then == NewPinName))
{
++Index;
NewPinName += FString::FromInt(Index);
}
TSharedPtr<FUserPinInfo> NewPinInfo = MakeShareable( new FUserPinInfo() );
NewPinInfo->PinName = NewPinName;
NewPinInfo->PinType = PinType;
UserDefinedPins.Add(NewPinInfo);
}
}
}
Super::ReconstructNode();
}
UK2Node_CustomEvent* UK2Node_CustomEvent::CreateFromFunction(FVector2D GraphPosition, UEdGraph* ParentGraph, const FString& Name, const UFunction* Function, bool bSelectNewNode/* = true*/)
{
UK2Node_CustomEvent* CustomEventNode = NULL;
if(ParentGraph && Function)
{
CustomEventNode = NewObject<UK2Node_CustomEvent>(ParentGraph);
CustomEventNode->CustomFunctionName = FName(*Name);
CustomEventNode->SetFlags(RF_Transactional);
ParentGraph->AddNode(CustomEventNode, true, bSelectNewNode);
CustomEventNode->CreateNewGuid();
CustomEventNode->PostPlacedNewNode();
CustomEventNode->AllocateDefaultPins();
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
for (TFieldIterator<UProperty> PropIt(Function); PropIt && (PropIt->PropertyFlags & CPF_Parm); ++PropIt)
{
const UProperty* Param = *PropIt;
if (!Param->HasAnyPropertyFlags(CPF_OutParm) || Param->HasAnyPropertyFlags(CPF_ReferenceParm))
{
FEdGraphPinType PinType;
K2Schema->ConvertPropertyToPinType(Param, /*out*/ PinType);
CustomEventNode->CreateUserDefinedPin(Param->GetName(), PinType);
}
}
CustomEventNode->NodePosX = GraphPosition.X;
CustomEventNode->NodePosY = GraphPosition.Y;
CustomEventNode->SnapToGrid(SNAP_GRID);
}
return CustomEventNode;
}
bool UK2Node_CustomEvent::IsEditable() const
{
const UEdGraphPin* DelegateOutPin = FindPin(DelegateOutputName);
if(DelegateOutPin && DelegateOutPin->LinkedTo.Num())
{
return false;
}
return Super::IsEditable();
}
bool UK2Node_CustomEvent::IsUsedByAuthorityOnlyDelegate() const
{
if(const UEdGraphPin* DelegateOutPin = FindPin(DelegateOutputName))
{
for(auto PinIter = DelegateOutPin->LinkedTo.CreateConstIterator(); PinIter; ++PinIter)
{
const UEdGraphPin* LinkedPin = *PinIter;
const UK2Node_BaseMCDelegate* Node = LinkedPin ? Cast<const UK2Node_BaseMCDelegate>(LinkedPin->GetOwningNode()) : NULL;
if(Node && Node->IsAuthorityOnly())
{
return true;
}
}
}
return false;
}
FString UK2Node_CustomEvent::GetTooltip() const
{
return LOCTEXT("AddCustomEvent_Tooltip", "An event with customizable name and parameters.").ToString();
}
FString UK2Node_CustomEvent::GetDocumentationLink() const
{
// Use the main k2 node doc
return UK2Node::GetDocumentationLink();
}
FString UK2Node_CustomEvent::GetDocumentationExcerptName() const
{
return TEXT("UK2Node_CustomEvent");
}
Added the ability to flag custom events as CallInEditor and extended the main editor actor details view to provide a drop list in the blueprints to call valid meta tagged functions. #TTP 337860 - Expose flagged BlueprintEvents as buttons in the Details panel that can be run in-editor #Branch UE4 #Proj Engine, DetailsCustomization, Kismet, Kismetcompiler, BlueprintGraph #Change Added FName FBlueprintMetadata::MD_CallInEditor as "CallInEditor" #Change Added bool UProperty UK2Node_CustomEvent::bCallInEditor to enable call in editor states to be serialised with UK2Node_CustomEvent #Change Modified FBlueprintGraphActionDetails::CustomizeDetails to add support for the CallInEditor tickbox. #Change Modified UK2Node_CustomEvent::GetPaletteIcon to return a different node icon when call in editor is enabled. #Added new icon GraphEditor.CallInEditorEvent_16x for the node corner icons and the blutility combo menu. #Change Added FKismetUserDeclaredFunctionMetadata::bCallInEditorProperty to help propagate call in editor setting from customevent node's to compiler generated event function entry point #Change Modified FKismetCompilerContext::CreateFunctionStubForEvent to check customevent node bCallInEditor properties and mark the generated function entry nodes as CallInEditor. This was required as a transitional phase to so the setting could be observed later during meta data generation. #Change Modified FKismetCompilerContext::FinishCompilingFunction to check function entry points for call in editor flags in the attached FKismetUserDeclaredFunctionMetadata and add CallInEditor meta data if present. #Change Modified AActor::ProcessEvent to allow events to be processed by the actor instance if the Function has CallInEditor meta data set to true #Change Added a series of functions to FActorDetails to conditionally add a blutility category and a combo list ( displaying the available blutility functions from the current actor blueprint ) and a button to call the selected function on the actor selection. ReviewedBy Chris.Wood #codereview Nick.Whiting [CL 2230263 by Ben Cosh in Main branch]
2014-07-24 13:30:26 -04:00
FName UK2Node_CustomEvent::GetPaletteIcon(FLinearColor& OutColor) const
{
return bCallInEditor ? TEXT("GraphEditor.CallInEditorEvent_16x") : TEXT("GraphEditor.CustomEvent_16x");
}
#undef LOCTEXT_NAMESPACE