Files
2025-05-18 13:04:45 +08:00

588 lines
25 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "K2Node_EnhancedInputAction.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "BlueprintActionDatabaseRegistrar.h"
#include "BlueprintEditorSettings.h"
#include "BlueprintNodeSpawner.h"
#include "EdGraphSchema_K2_Actions.h"
#include "Editor.h"
#include "EditorCategoryUtils.h"
#include "GraphEditorSettings.h"
#include "InputAction.h"
#include "K2Node_AssignmentStatement.h"
#include "K2Node_EnhancedInputActionEvent.h"
#include "K2Node_GetInputActionValue.h"
#include "K2Node_TemporaryVariable.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "KismetCompiler.h"
#include "Misc/PackageName.h"
#include "Subsystems/AssetEditorSubsystem.h"
#include "Modules/ModuleManager.h"
#include "Styling/AppStyle.h"
#include "EnhancedInputEditorSettings.h"
#include "WidgetBlueprint.h"
#include "Blueprint/UserWidget.h"
#include "BlueprintNodeTemplateCache.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(K2Node_EnhancedInputAction)
#define LOCTEXT_NAMESPACE "K2Node_EnhancedInputAction"
static const FName InputActionPinName = TEXT("InputAction");
static const FName ElapsedSecondsPinName = TEXT("ElapsedSeconds");
static const FName TriggeredSecondsPinName = TEXT("TriggeredSeconds");
static const FName ActionValuePinName = TEXT("ActionValue");
namespace UE::Input
{
static bool bShouldWarnOnUnsupportedInputPin = false;
static FAutoConsoleVariableRef CVarShouldWarnOnUnsupportedInputPin(
TEXT("enhancedInput.bp.bShouldWarnOnUnsupportedInputPin"),
bShouldWarnOnUnsupportedInputPin,
TEXT("Should the Enhanced Input event node throw a warning if a \"Unsuported\" pin has a connection?"),
ECVF_Default);
}
UK2Node_EnhancedInputAction::UK2Node_EnhancedInputAction(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void ForEachEventPinName(TFunctionRef<bool(ETriggerEvent Event, FName PinName)> PinLambda)
{
UEnum* EventEnum = StaticEnum<ETriggerEvent>();
for (int32 i = 0; i < EventEnum->NumEnums() - 1; ++i)
{
if (!EventEnum->HasMetaData(TEXT("Hidden"), i))
{
if (!PinLambda(ETriggerEvent(EventEnum->GetValueByIndex(i)), *EventEnum->GetNameStringByIndex(i)))
{
break;
}
}
}
}
UInputActionEventNodeSpawner* UInputActionEventNodeSpawner::Create(TSubclassOf<UEdGraphNode> const NodeClass, TObjectPtr<const UInputAction> InAction)
{
check(NodeClass != nullptr);
check(NodeClass->IsChildOf<UEdGraphNode>());
check(InAction != nullptr);
UInputActionEventNodeSpawner* NodeSpawner = NewObject<UInputActionEventNodeSpawner>(GetTransientPackage());
NodeSpawner->NodeClass = NodeClass;
NodeSpawner->WeakActionPtr = InAction;
return NodeSpawner;
}
UEdGraphNode* UInputActionEventNodeSpawner::Invoke(UEdGraph* ParentGraph, FBindingSet const& Bindings, FVector2D const Location) const
{
check(ParentGraph != nullptr);
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraphChecked(ParentGraph);
if (!FBlueprintNodeTemplateCache::IsTemplateOuter(ParentGraph))
{
// Look to see if a node for this input action already exists. If it does, just return that
// which will jump the focus to it.
if (UK2Node* PreExistingNode = FindExistingNode(Blueprint))
{
return PreExistingNode;
}
}
return Super::Invoke(ParentGraph, Bindings, Location);
}
UK2Node* UInputActionEventNodeSpawner::FindExistingNode(const UBlueprint* Blueprint) const
{
// We don't want references to node spawners to be keeping any input action assets from GC
// if you unload a plugin for example, so we keep it as a weak pointer.
TStrongObjectPtr<const UInputAction> ActionPtr = WeakActionPtr.Pin();
if (!ActionPtr)
{
return nullptr;
}
TArray<UK2Node_EnhancedInputAction*> AllInputActionNodes;
FBlueprintEditorUtils::GetAllNodesOfClass<UK2Node_EnhancedInputAction>(Blueprint, AllInputActionNodes);
for (UK2Node_EnhancedInputAction* Node : AllInputActionNodes)
{
if (Node->InputAction == ActionPtr)
{
return Node;
}
}
return nullptr;
}
void UK2Node_EnhancedInputAction::AllocateDefaultPins()
{
PreloadObject((UObject*)InputAction);
const ETriggerEventsSupported SupportedTriggerEvents = GetDefault<UBlueprintEditorSettings>()->bEnableInputTriggerSupportWarnings && InputAction ? InputAction->GetSupportedTriggerEvents() : ETriggerEventsSupported::All;
ForEachEventPinName([this, SupportedTriggerEvents](ETriggerEvent Event, FName PinName)
{
static const UEnum* EventEnum = StaticEnum<ETriggerEvent>();
UEdGraphPin* NewPin = CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, PinName);
// Mark all triggering exec pins as advanced view except for the triggered pin. Most of the time, triggered is what users should be using.
// More advanced input set ups can use the more advanced pins when they want to!
NewPin->bAdvancedView = !(GetDefault<UEnhancedInputEditorSettings>()->VisibleEventPinsByDefault & static_cast<uint8>(Event));
NewPin->PinToolTip = EventEnum->GetToolTipTextByIndex(EventEnum->GetIndexByValue(static_cast<uint8>(Event))).ToString();
// Add a special tooltip and display name for pins that are unsupported
if (UE::Input::bShouldWarnOnUnsupportedInputPin && !UInputTrigger::IsSupportedTriggerEvent(SupportedTriggerEvents, Event))
{
static const FText UnsuportedTooltip = LOCTEXT("UnsupportedTooltip", "\n\nThis trigger event is not supported by the action! Add a supported trigger to enable this pin.");
NewPin->PinToolTip += UnsuportedTooltip.ToString();
NewPin->PinFriendlyName = FText::Format(LOCTEXT("UnsupportedPinFriendlyName", "(Unsupported) {0}"), FText::FromName(NewPin->GetFName()));
}
// Continue iterating
return true;
});
HideEventPins(nullptr);
const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
AdvancedPinDisplay = ENodeAdvancedPins::Hidden;
UEdGraphPin* ValuePin = CreatePin(EGPD_Output, UK2Node_GetInputActionValue::GetValueCategory(InputAction), UK2Node_GetInputActionValue::GetValueSubCategory(InputAction), UK2Node_GetInputActionValue::GetValueSubCategoryObject(InputAction), ActionValuePinName);
Schema->SetPinAutogeneratedDefaultValueBasedOnType(ValuePin);
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Real, UEdGraphSchema_K2::PC_Double, ElapsedSecondsPinName)->bAdvancedView = true;
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Real, UEdGraphSchema_K2::PC_Double, TriggeredSecondsPinName)->bAdvancedView = true;
if(InputAction)
{
UEdGraphPin* ActionPin = CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Object, InputAction->GetClass(), InputActionPinName);
ActionPin->DefaultObject = const_cast<UObject*>(Cast<UObject>(InputAction));
ActionPin->DefaultValue = InputAction->GetName();
ActionPin->bAdvancedView = true;
Schema->ConstructBasicPinTooltip(*ActionPin, LOCTEXT("InputActionPinDescription", "The input action that caused this event to fire"), ActionPin->PinToolTip);
}
Super::AllocateDefaultPins();
}
void UK2Node_EnhancedInputAction::HideEventPins(UEdGraphPin* RetainPin)
{
// Gather pins
const ETriggerEventsSupported SupportedTriggerEvents = GetDefault<UBlueprintEditorSettings>()->bEnableInputTriggerSupportWarnings && InputAction ? InputAction->GetSupportedTriggerEvents() : ETriggerEventsSupported::All;
// Hide any event pins that are not supported by this Action's triggers in the advanced view
ForEachEventPinName([this, SupportedTriggerEvents](ETriggerEvent Event, FName PinName)
{
if (UEdGraphPin* Pin = FindPin(PinName))
{
const bool bIsSupported = UInputTrigger::IsSupportedTriggerEvent(SupportedTriggerEvents, Event);
Pin->bAdvancedView = !(GetDefault<UEnhancedInputEditorSettings>()->VisibleEventPinsByDefault & static_cast<uint8>(Event)) || !bIsSupported;
}
// Continue iterating
return true;
});
}
const ETriggerEvent UK2Node_EnhancedInputAction::GetTriggerTypeFromExecPin(const UEdGraphPin* ExecPin) const
{
static const UEnum* EventEnum = StaticEnum<ETriggerEvent>();
if(ensure(ExecPin && ExecPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec))
{
return (ETriggerEvent)(EventEnum->GetValueByName(ExecPin->PinName));
}
return ETriggerEvent::None;
}
void UK2Node_EnhancedInputAction::PostReconstructNode()
{
Super::PostReconstructNode();
HideEventPins(nullptr);
}
void UK2Node_EnhancedInputAction::PinConnectionListChanged(UEdGraphPin* Pin)
{
Super::PinConnectionListChanged(Pin);
HideEventPins(Pin);
}
bool UK2Node_EnhancedInputAction::IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const
{
if(MyPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec && InputAction)
{
const ETriggerEvent Event = GetTriggerTypeFromExecPin(MyPin);
const ETriggerEventsSupported SupportedEvents = GetDefault<UBlueprintEditorSettings>()->bEnableInputTriggerSupportWarnings ? InputAction->GetSupportedTriggerEvents() : ETriggerEventsSupported::All;
if(!UInputTrigger::IsSupportedTriggerEvent(SupportedEvents, Event))
{
FFormatNamedArguments Args;
Args.Add(TEXT("ActionName"), FText::FromName(InputAction->GetFName()));
Args.Add(TEXT("PinName"), FText::FromName(MyPin->PinName));
OutReason = FText::Format(LOCTEXT("UnsupportedEventType_DragTooltip", "WARNING: '{ActionName}' does not support the '{PinName}' trigger event."), Args).ToString();
}
}
return Super::IsConnectionDisallowed(MyPin, OtherPin, OutReason);
}
FLinearColor UK2Node_EnhancedInputAction::GetNodeTitleColor() const
{
return GetDefault<UGraphEditorSettings>()->EventNodeTitleColor;
}
FName UK2Node_EnhancedInputAction::GetActionName() const
{
return InputAction ? InputAction->GetFName() : FName();
}
FText UK2Node_EnhancedInputAction::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
// TODO: Is Using InputAction->GetFName okay here? Full Asset path would be better for disambiguation.
if (TitleType == ENodeTitleType::MenuTitle)
{
return FText::FromName(GetActionName());
}
else if (CachedNodeTitle.IsOutOfDate(this))
{
FFormatNamedArguments Args;
Args.Add(TEXT("InputActionName"), FText::FromName(GetActionName()));
FText LocFormat = LOCTEXT("EnhancedInputAction_Name", "EnhancedInputAction {InputActionName}");
// FText::Format() is slow, so we cache this to save on performance
CachedNodeTitle.SetCachedText(FText::Format(LocFormat, Args), this);
}
return CachedNodeTitle;
}
FText UK2Node_EnhancedInputAction::GetTooltipText() const
{
if (CachedTooltip.IsOutOfDate(this))
{
// FText::Format() is slow, so we cache this to save on performance
FString ActionPath = InputAction ? InputAction->GetFullName() : TEXT("");
CachedTooltip.SetCachedText(
FText::Format(
LOCTEXT("EnhancedInputAction_Tooltip", "Event for when '{0}' triggers.\n\nNote: This is not guaranteed to fire every frame, only when the Action is triggered and the current Input Mode includes 'Game'.\n\n{1}\n\n{2}"),
FText::FromString(ActionPath),
LOCTEXT("EnhancedInputAction_Node_Tooltip_Tip", "Tip: Use the 'showdebug enhancedinput' command while playing to see debug information about Enhanced Input."),
LOCTEXT("EnhancedInputAction_Node_SettingsTooltip", "You can change what execution pins are visible by default in the Enhanced Input Editor Preferences.")),
this);
}
return CachedTooltip;
}
FSlateIcon UK2Node_EnhancedInputAction::GetIconAndTint(FLinearColor& OutColor) const
{
static FSlateIcon Icon(FAppStyle::GetAppStyleSetName(), "GraphEditor.Event_16x");
return Icon;
}
bool UK2Node_EnhancedInputAction::IsCompatibleWithGraph(UEdGraph const* Graph) const
{
// This node expands into event nodes and must be placed in a Ubergraph
EGraphType const GraphType = Graph->GetSchema()->GetGraphType(Graph);
bool bIsCompatible = (GraphType == EGraphType::GT_Ubergraph);
if (bIsCompatible)
{
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(Graph);
UEdGraphSchema_K2 const* K2Schema = Cast<UEdGraphSchema_K2>(Graph->GetSchema());
bool const bIsConstructionScript = (K2Schema != nullptr) ? UEdGraphSchema_K2::IsConstructionScript(Graph) : false;
bIsCompatible = (Blueprint != nullptr) && Blueprint->SupportsInputEvents() && !bIsConstructionScript && Super::IsCompatibleWithGraph(Graph);
}
return bIsCompatible;
}
UObject* UK2Node_EnhancedInputAction::GetJumpTargetForDoubleClick() const
{
return const_cast<UObject*>(Cast<UObject>(InputAction));
}
void UK2Node_EnhancedInputAction::JumpToDefinition() const
{
GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->OpenEditorForAsset(GetJumpTargetForDoubleClick());
}
void UK2Node_EnhancedInputAction::ValidateNodeDuringCompilation(class FCompilerResultsLog& MessageLog) const
{
Super::ValidateNodeDuringCompilation(MessageLog);
if (!InputAction)
{
MessageLog.Error(*LOCTEXT("EnhancedInputAction_ErrorFmt", "EnhancedInputActionEvent references invalid 'null' action for @@").ToString(), this);
return;
}
// There are no supported triggers on this action, we should put a note down
// This would only be the case if the user has added a custom UInputTrigger that uses ETriggeredEventsSupported::None
if(InputAction->GetSupportedTriggerEvents() == ETriggerEventsSupported::None)
{
MessageLog.Warning(
*LOCTEXT("EnhancedInputAction_NoTriggersOnAction",
"@@ may not be triggered. There are no triggers supported on this action! Add a trigger to this action to resolve this warning.").ToString(),
this);
}
}
void UK2Node_EnhancedInputAction::ExpandNode(FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
Super::ExpandNode(CompilerContext, SourceGraph);
if(!InputAction)
{
static const FText InvalidActionWarning = LOCTEXT("InvalidInputActionDuringExpansion", "@@ does not have a valid Input Action asset!!");
CompilerContext.MessageLog.Warning(*InvalidActionWarning.ToString(), this);
return;
}
// Establish active pins
struct ActivePinData
{
ActivePinData(UEdGraphPin* InPin, ETriggerEvent InTriggerEvent) : Pin(InPin), TriggerEvent(InTriggerEvent) {}
UEdGraphPin* Pin;
ETriggerEvent TriggerEvent;
};
const ETriggerEventsSupported SupportedTriggerEvents = GetDefault<UBlueprintEditorSettings>()->bEnableInputTriggerSupportWarnings ? InputAction->GetSupportedTriggerEvents() : ETriggerEventsSupported::All;
TArray<ActivePinData> ActivePins;
ForEachActiveEventPin([this, &ActivePins, &SupportedTriggerEvents, &CompilerContext](ETriggerEvent Event, UEdGraphPin& InputActionPin)
{
ActivePins.Add(ActivePinData(&InputActionPin, Event));
// Check if this exec pin is supported!
if (UE::Input::bShouldWarnOnUnsupportedInputPin && !UInputTrigger::IsSupportedTriggerEvent(SupportedTriggerEvents, Event))
{
CompilerContext.MessageLog.Warning(*FText::Format(LOCTEXT("UnsuportedEventTypeOnAction", "'{0}'on @@ may not be executed because it is not a supported trigger on this action!"), InputActionPin.GetDisplayName()).ToString(), this);
}
// Continue iterating
return true;
});
if (ActivePins.Num() == 0)
{
return;
}
// Bind all active pins to their action delegate
const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema();
auto CreateInputActionEvent = [this, &CompilerContext, &SourceGraph](UEdGraphPin* Pin, ETriggerEvent TriggerEvent) -> UK2Node_EnhancedInputActionEvent*
{
if (!InputAction)
{
return nullptr;
}
UK2Node_EnhancedInputActionEvent* InputActionEvent = CompilerContext.SpawnIntermediateNode<UK2Node_EnhancedInputActionEvent>(this, SourceGraph);
InputActionEvent->CustomFunctionName = FName(*FString::Printf(TEXT("InpActEvt_%s_%s"), *GetActionName().ToString(), *InputActionEvent->GetName()));
InputActionEvent->InputAction = InputAction;
InputActionEvent->TriggerEvent = TriggerEvent;
InputActionEvent->EventReference.SetExternalDelegateMember(FName(TEXT("EnhancedInputActionHandlerDynamicSignature__DelegateSignature")));
InputActionEvent->AllocateDefaultPins();
return InputActionEvent;
};
// Widget blueprints require the bAutomaticallyRegisterInputOnConstruction to be set to true in order to receive callbacks
if (GetBlueprint()->IsA<UWidgetBlueprint>())
{
CompilerContext.AddPostCDOCompiledStep([](const UObject::FPostCDOCompiledContext& Context, UObject* NewCDO)
{
UUserWidget* Widget = CastChecked<UUserWidget>(NewCDO);
Widget->bAutomaticallyRegisterInputOnConstruction = true;
});
}
// Create temporary variables to copy ActionValue and ElapsedSeconds into
UK2Node_TemporaryVariable* ActionValueVar = CompilerContext.SpawnIntermediateNode<UK2Node_TemporaryVariable>(this, SourceGraph);
ActionValueVar->VariableType.PinCategory = UK2Node_GetInputActionValue::GetValueCategory(InputAction);
ActionValueVar->VariableType.PinSubCategory = UK2Node_GetInputActionValue::GetValueSubCategory(InputAction);
ActionValueVar->VariableType.PinSubCategoryObject = UK2Node_GetInputActionValue::GetValueSubCategoryObject(InputAction);
ActionValueVar->AllocateDefaultPins();
UK2Node_TemporaryVariable* ElapsedSecondsVar = CompilerContext.SpawnIntermediateNode<UK2Node_TemporaryVariable>(this, SourceGraph);
ElapsedSecondsVar->VariableType.PinCategory = UEdGraphSchema_K2::PC_Real;
ElapsedSecondsVar->VariableType.PinSubCategory = UEdGraphSchema_K2::PC_Double;
ElapsedSecondsVar->AllocateDefaultPins();
UK2Node_TemporaryVariable* TriggeredSecondsVar = CompilerContext.SpawnIntermediateNode<UK2Node_TemporaryVariable>(this, SourceGraph);
TriggeredSecondsVar->VariableType.PinCategory = UEdGraphSchema_K2::PC_Real;
TriggeredSecondsVar->VariableType.PinSubCategory = UEdGraphSchema_K2::PC_Double;
TriggeredSecondsVar->AllocateDefaultPins();
UK2Node_TemporaryVariable* InputActionVar = CompilerContext.SpawnIntermediateNode<UK2Node_TemporaryVariable>(this, SourceGraph);
InputActionVar->VariableType.PinCategory = UEdGraphSchema_K2::PC_Object;
InputActionVar->VariableType.PinSubCategoryObject = InputAction->GetClass();
InputActionVar->AllocateDefaultPins();
for (ActivePinData& PinData : ActivePins)
{
UEdGraphPin* EachPin = PinData.Pin;
UK2Node_EnhancedInputActionEvent* InputActionEvent = CreateInputActionEvent(EachPin, PinData.TriggerEvent);
if (!InputActionEvent)
{
continue;
}
// Create assignment nodes to assign the action value
UK2Node_AssignmentStatement* ActionValueInitialize = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(this, SourceGraph);
ActionValueInitialize->AllocateDefaultPins();
Schema->TryCreateConnection(ActionValueVar->GetVariablePin(), ActionValueInitialize->GetVariablePin());
Schema->TryCreateConnection(ActionValueInitialize->GetValuePin(), InputActionEvent->FindPinChecked(ActionValuePinName));
// Connect the events to the assign location nodes
Schema->TryCreateConnection(Schema->FindExecutionPin(*InputActionEvent, EGPD_Output), ActionValueInitialize->GetExecPin());
// Create assignment nodes to assign the elapsed timers and input action
UK2Node_AssignmentStatement* ElapsedSecondsInitialize = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(this, SourceGraph);
ElapsedSecondsInitialize->AllocateDefaultPins();
Schema->TryCreateConnection(ElapsedSecondsVar->GetVariablePin(), ElapsedSecondsInitialize->GetVariablePin());
Schema->TryCreateConnection(ElapsedSecondsInitialize->GetValuePin(), InputActionEvent->FindPinChecked(TEXT("ElapsedTime")));
UK2Node_AssignmentStatement* TriggeredSecondsInitialize = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(this, SourceGraph);
TriggeredSecondsInitialize->AllocateDefaultPins();
Schema->TryCreateConnection(TriggeredSecondsVar->GetVariablePin(), TriggeredSecondsInitialize->GetVariablePin());
Schema->TryCreateConnection(TriggeredSecondsInitialize->GetValuePin(), InputActionEvent->FindPinChecked(TEXT("TriggeredTime")));
UK2Node_AssignmentStatement* InputActionInitialize = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(this, SourceGraph);
InputActionInitialize->AllocateDefaultPins();
Schema->TryCreateConnection(InputActionVar->GetVariablePin(), InputActionInitialize->GetVariablePin());
Schema->TryCreateConnection(InputActionInitialize->GetValuePin(), InputActionEvent->FindPinChecked(TEXT("SourceAction")));
// Connect the assign location to the assign elapsed time nodes
Schema->TryCreateConnection(ActionValueInitialize->GetThenPin(), ElapsedSecondsInitialize->GetExecPin());
Schema->TryCreateConnection(ElapsedSecondsInitialize->GetThenPin(), TriggeredSecondsInitialize->GetExecPin());
Schema->TryCreateConnection(TriggeredSecondsInitialize->GetThenPin(), InputActionInitialize->GetExecPin());
// Move the original event connections to the then pin of the Input Action assign
CompilerContext.MovePinLinksToIntermediate(*EachPin, *InputActionInitialize->GetThenPin());
// Move the original event variable connections to the intermediate nodes
CompilerContext.MovePinLinksToIntermediate(*FindPin(ActionValuePinName), *ActionValueVar->GetVariablePin());
CompilerContext.MovePinLinksToIntermediate(*FindPin(ElapsedSecondsPinName), *ElapsedSecondsVar->GetVariablePin());
CompilerContext.MovePinLinksToIntermediate(*FindPin(TriggeredSecondsPinName), *TriggeredSecondsVar->GetVariablePin());
CompilerContext.MovePinLinksToIntermediate(*FindPin(InputActionPinName), *InputActionVar->GetVariablePin());
}
}
void UK2Node_EnhancedInputAction::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
{
auto CustomizeInputNodeLambda = [](UEdGraphNode* NewNode, bool bIsTemplateNode, TWeakObjectPtr<const UInputAction> Action)
{
UK2Node_EnhancedInputAction* InputNode = CastChecked<UK2Node_EnhancedInputAction>(NewNode);
InputNode->InputAction = Action.Get();
};
// Do a first time registration using the node's class to pull in all existing actions
if (ActionRegistrar.IsOpenForRegistration(GetClass()))
{
IAssetRegistry& AssetRegistry = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry")).Get();
static bool bRegisterOnce = true;
if (bRegisterOnce)
{
bRegisterOnce = false;
if (AssetRegistry.IsLoadingAssets())
{
AssetRegistry.OnFilesLoaded().AddLambda([]() { FBlueprintActionDatabase::Get().RefreshClassActions(StaticClass()); });
}
}
TArray<FAssetData> ActionAssets;
AssetRegistry.GetAssetsByClass(UInputAction::StaticClass()->GetClassPathName(), ActionAssets, true);
for (const FAssetData& ActionAsset : ActionAssets)
{
if (FPackageName::GetPackageMountPoint(ActionAsset.PackageName.ToString()) != NAME_None)
{
if (const UInputAction* Action = Cast<const UInputAction>(ActionAsset.GetAsset()))
{
UBlueprintNodeSpawner* NodeSpawner = UInputActionEventNodeSpawner::Create(GetClass(), Action);
check(NodeSpawner != nullptr);
NodeSpawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(CustomizeInputNodeLambda, TWeakObjectPtr<const UInputAction>(Action));
ActionRegistrar.AddBlueprintAction(Action, NodeSpawner);
}
}
}
}
else if (const UInputAction* Action = Cast<const UInputAction>(ActionRegistrar.GetActionKeyFilter()))
{
// If this is a specific UInputAction asset update it.
UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass());
check(NodeSpawner != nullptr);
NodeSpawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(CustomizeInputNodeLambda, TWeakObjectPtr<const UInputAction>(Action));
ActionRegistrar.AddBlueprintAction(Action, NodeSpawner);
}
}
FText UK2Node_EnhancedInputAction::GetMenuCategory() const
{
static FNodeTextCache CachedCategory;
if (CachedCategory.IsOutOfDate(this))
{
// FText::Format() is slow, so we cache this to save on performance
CachedCategory.SetCachedText(FEditorCategoryUtils::BuildCategoryString(FCommonEditorCategory::Input, LOCTEXT("ActionMenuCategory", "Enhanced Action Events")), this); // TODO: Rename Action Events once old action system is removed
}
return CachedCategory;
}
FBlueprintNodeSignature UK2Node_EnhancedInputAction::GetSignature() const
{
FBlueprintNodeSignature NodeSignature = Super::GetSignature();
NodeSignature.AddKeyValue(GetActionName().ToString());
return NodeSignature;
}
TSharedPtr<FEdGraphSchemaAction> UK2Node_EnhancedInputAction::GetEventNodeAction(const FText& ActionCategory)
{
// TODO: Custom EdGraphSchemaAction required?
TSharedPtr<FEdGraphSchemaAction_K2InputAction> EventNodeAction = MakeShareable(new FEdGraphSchemaAction_K2InputAction(ActionCategory, GetNodeTitle(ENodeTitleType::EditableTitle), GetTooltipText(), 0));
EventNodeAction->NodeTemplate = this;
return EventNodeAction;
}
bool UK2Node_EnhancedInputAction::HasAnyConnectedEventPins() const
{
bool bHasAnyConnectedEventPins = false;
ForEachActiveEventPin([&bHasAnyConnectedEventPins](ETriggerEvent, UEdGraphPin& Pin)
{
// Stop iterating on the first active pin
bHasAnyConnectedEventPins = true;
return false;
});
return bHasAnyConnectedEventPins;
}
void UK2Node_EnhancedInputAction::ForEachActiveEventPin(TFunctionRef<bool(ETriggerEvent, UEdGraphPin&)> Predicate) const
{
ForEachEventPinName([this, Predicate](ETriggerEvent Event, FName PinName)
{
UEdGraphPin* InputActionPin = FindPin(PinName, EEdGraphPinDirection::EGPD_Output);
if (InputActionPin && InputActionPin->LinkedTo.Num() > 0)
{
return Predicate(Event, *InputActionPin);
}
return true;
});
}
#undef LOCTEXT_NAMESPACE