// Copyright Epic Games, Inc. All Rights Reserved. #include "K2Node_SwitchEnum.h" #include "BlueprintActionDatabaseRegistrar.h" #include "BlueprintFieldNodeSpawner.h" #include "Containers/EnumAsByte.h" #include "Containers/UnrealString.h" #include "CoreTypes.h" #include "Delegates/Delegate.h" #include "EdGraphSchema_K2.h" #include "FindInBlueprintManager.h" #include "HAL/PlatformCrt.h" #include "Internationalization/Internationalization.h" #include "Kismet/KismetMathLibrary.h" #include "Misc/AssertionMacros.h" #include "Templates/Casts.h" #include "Templates/SubclassOf.h" #include "UObject/Field.h" #include "UObject/NameTypes.h" #include "UObject/WeakObjectPtr.h" #include "UObject/WeakObjectPtrTemplates.h" class UBlueprintNodeSpawner; #define LOCTEXT_NAMESPACE "K2Node" UK2Node_SwitchEnum::UK2Node_SwitchEnum(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { bHasDefaultPin = false; OrphanedPinSaveMode = ESaveOrphanPinMode::SaveAll; FunctionName = TEXT("NotEqual_ByteByte"); FunctionClass = UKismetMathLibrary::StaticClass(); } void UK2Node_SwitchEnum::SetEnum(UEnum* InEnum) { Enum = InEnum; // regenerate enum name list EnumEntries.Empty(); EnumFriendlyNames.Empty(); if (Enum) { PreloadObject(Enum); // When on async loading thread, postload happens later on GT unless it's possible // to do it right now safely. if (IsInGameThread() || Enum->IsPostLoadThreadSafe()) { Enum->ConditionalPostLoad(); } for (int32 EnumIndex = 0; EnumIndex < Enum->NumEnums() - 1; ++EnumIndex) { bool const bShouldBeHidden = Enum->HasMetaData(TEXT("Hidden"), EnumIndex ) || Enum->HasMetaData(TEXT("Spacer"), EnumIndex ); if (!bShouldBeHidden) { FString const EnumValueName = Enum->GetNameStringByIndex(EnumIndex); EnumEntries.Add( FName(*EnumValueName) ); FText EnumFriendlyName = Enum->GetDisplayNameTextByIndex(EnumIndex); EnumFriendlyNames.Add( EnumFriendlyName ); } } } } void UK2Node_SwitchEnum::PreloadRequiredAssets() { PreloadObject(Enum); Super::PreloadRequiredAssets(); } FText UK2Node_SwitchEnum::GetNodeTitle(ENodeTitleType::Type TitleType) const { if (Enum == nullptr) { return LOCTEXT("SwitchEnum_BadEnumTitle", "Switch on (bad enum)"); } else if (CachedNodeTitle.IsOutOfDate(this)) { FFormatNamedArguments Args; Args.Add(TEXT("EnumName"), FText::FromString(Enum->GetName())); // FText::Format() is slow, so we cache this to save on performance CachedNodeTitle.SetCachedText(FText::Format(NSLOCTEXT("K2Node", "Switch_Enum", "Switch on {EnumName}"), Args), this); } return CachedNodeTitle; } FText UK2Node_SwitchEnum::GetTooltipText() const { return NSLOCTEXT("K2Node", "SwitchEnum_ToolTip", "Selects an output that matches the input value"); } void UK2Node_SwitchEnum::AddPinSearchMetaDataInfo(const UEdGraphPin* Pin, TArray& OutTaggedMetaData) const { Super::AddPinSearchMetaDataInfo(Pin, OutTaggedMetaData); const UEdGraphSchema_K2* K2Schema = GetDefault(); if (Enum != nullptr && K2Schema->IsExecPin(*Pin) && Pin->Direction == EGPD_Output && Enum->IsNative() && EnumEntries.Contains(Pin->GetFName())) { // Allow native enum switch pins to be searchable by C++ enum name OutTaggedMetaData.Add(FSearchTagDataPair(FFindInBlueprintSearchTags::FiB_NativeName, FText::FromString(Pin->GetName()))); } } void UK2Node_SwitchEnum::ValidateNodeDuringCompilation(FCompilerResultsLog& MessageLog) const { if (!Enum) { MessageLog.Error(*LOCTEXT("MissingSwitchEnum", "@@ must have a valid enum - check log for 'Failed import' add a redirector or recreate the enum").ToString(), this); } return Super::ValidateNodeDuringCompilation(MessageLog); } bool UK2Node_SwitchEnum::IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const { const UEnum* SubCategoryObject = Cast( OtherPin->PinType.PinSubCategoryObject.Get() ); if( SubCategoryObject ) { if( SubCategoryObject != Enum ) { return true; } } return false; } void UK2Node_SwitchEnum::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const { struct GetMenuActions_Utils { static void SetNodeEnum(UEdGraphNode* NewNode, FFieldVariant /*EnumField*/, TWeakObjectPtr NonConstEnumPtr) { UK2Node_SwitchEnum* EnumNode = CastChecked(NewNode); EnumNode->Enum = NonConstEnumPtr.Get(); } }; UClass* NodeClass = GetClass(); ActionRegistrar.RegisterEnumActions( FBlueprintActionDatabaseRegistrar::FMakeEnumSpawnerDelegate::CreateLambda([NodeClass](const UEnum* InEnum)->UBlueprintNodeSpawner* { UBlueprintFieldNodeSpawner* NodeSpawner = UBlueprintFieldNodeSpawner::Create(NodeClass, const_cast(InEnum)); check(NodeSpawner != nullptr); TWeakObjectPtr NonConstEnumPtr = MakeWeakObjectPtr(const_cast(InEnum)); NodeSpawner->SetNodeFieldDelegate = UBlueprintFieldNodeSpawner::FSetNodeFieldDelegate::CreateStatic(GetMenuActions_Utils::SetNodeEnum, NonConstEnumPtr); return NodeSpawner; }) ); } void UK2Node_SwitchEnum::CreateSelectionPin() { UEdGraphPin* Pin = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Byte, Enum, TEXT("Selection")); GetDefault()->SetPinAutogeneratedDefaultValueBasedOnType(Pin); } FEdGraphPinType UK2Node_SwitchEnum::GetPinType() const { FEdGraphPinType EnumPinType; EnumPinType.PinCategory = UEdGraphSchema_K2::PC_Byte; EnumPinType.PinSubCategoryObject = Enum; return EnumPinType; } void UK2Node_SwitchEnum::CreateCasePins() { if (Enum) { SetEnum(Enum); } const bool bShouldUseAdvancedView = (EnumEntries.Num() > 5); if(bShouldUseAdvancedView && (ENodeAdvancedPins::NoPins == AdvancedPinDisplay)) { AdvancedPinDisplay = ENodeAdvancedPins::Hidden; } for (int32 Index = 0; Index < EnumEntries.Num(); ++Index) { UEdGraphPin* NewPin = CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, EnumEntries[Index]); if (EnumFriendlyNames.IsValidIndex(Index)) { NewPin->PinFriendlyName = EnumFriendlyNames[Index]; } if(bShouldUseAdvancedView && (Index > 2)) { NewPin->bAdvancedView = true; } } } UK2Node::ERedirectType UK2Node_SwitchEnum::DoPinsMatchForReconstruction(const UEdGraphPin* NewPin, int32 NewPinIndex, const UEdGraphPin* OldPin, int32 OldPinIndex) const { UK2Node::ERedirectType ReturnValue = Super::DoPinsMatchForReconstruction(NewPin, NewPinIndex, OldPin, OldPinIndex); if (ReturnValue == UK2Node::ERedirectType_None && Enum && OldPinIndex > 2 && NewPinIndex > 2) { const int64 OldValue = Enum->GetValueByName(OldPin->PinName); const int64 NewValue = Enum->GetValueByName(NewPin->PinName); // This handles redirects properly if (OldValue == NewValue && OldValue != INDEX_NONE) { ReturnValue = UK2Node::ERedirectType_Name; } } return ReturnValue; } void UK2Node_SwitchEnum::AddPinToSwitchNode() { // first try to restore unconnected pin, since connected one is always visible for(auto PinIt = Pins.CreateIterator(); PinIt; ++PinIt) { UEdGraphPin* Pin = *PinIt; if(Pin && (0 == Pin->LinkedTo.Num()) && Pin->bAdvancedView) { Pin->Modify(); Pin->bAdvancedView = false; return; } } for(auto PinIt = Pins.CreateIterator(); PinIt; ++PinIt) { UEdGraphPin* Pin = *PinIt; if(Pin && Pin->bAdvancedView) { Pin->Modify(); Pin->bAdvancedView = false; return; } } } void UK2Node_SwitchEnum::RemovePinFromSwitchNode(UEdGraphPin* Pin) { if(Pin) { if(!Pin->bAdvancedView) { Pin->Modify(); Pin->bAdvancedView = true; } Pin->BreakAllPinLinks(); } } void UK2Node_SwitchEnum::ReloadEnum(class UEnum* InEnum) { SetEnum(InEnum); } #undef LOCTEXT_NAMESPACE