// Copyright Epic Games, Inc. All Rights Reserved. #include "K2Node_MacroInstance.h" #include "Engine/Blueprint.h" #include "Framework/Commands/UIAction.h" #include "ToolMenus.h" #include "EdGraphSchema_K2.h" #include "Kismet2/BlueprintEditorUtils.h" #include "EditorStyleSet.h" #include "Editor.h" #include "EditorCategoryUtils.h" #include "BlueprintActionFilter.h" #include "Classes/EditorStyleSettings.h" #define LOCTEXT_NAMESPACE "K2Node_MacroInstance" UK2Node_MacroInstance::UK2Node_MacroInstance(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { bReconstructNode = false; } void UK2Node_MacroInstance::Serialize(FArchive& Ar) { Super::Serialize(Ar); if (Ar.UEVer() < VER_UE4_K2NODE_REFERENCEGUIDS) { MacroGraphReference.SetGraph(MacroGraph_DEPRECATED); } } bool UK2Node_MacroInstance::IsActionFilteredOut(FBlueprintActionFilter const& Filter) { bool bIsFilteredOut = false; FBlueprintActionContext const& FilterContext = Filter.Context; for (UEdGraph* Graph : FilterContext.Graphs) { if (!CanPasteHere(Graph)) { bIsFilteredOut = true; break; } } return bIsFilteredOut; } void UK2Node_MacroInstance::PostPasteNode() { const UBlueprint* InstanceOwner = GetBlueprint(); // Find the owner of the macro graph if (const UEdGraph* MacroGraph = MacroGraphReference.GetGraph()) { UObject* MacroOwner = MacroGraph->GetOuter(); UBlueprint* MacroOwnerBP = nullptr; while (MacroOwner) { MacroOwnerBP = Cast(MacroOwner); if (MacroOwnerBP) { break; } MacroOwner = MacroOwner->GetOuter(); } if ((MacroOwnerBP != nullptr) && (MacroOwnerBP->BlueprintType != BPTYPE_MacroLibrary) && (MacroOwnerBP != InstanceOwner)) { // If this is a graph from another blueprint that is NOT a library, disallow the connection! MacroGraphReference.SetGraph(nullptr); } } else { // Can't find the referenced macro, fully clear this reference MacroGraphReference.SetGraph(nullptr); } Super::PostPasteNode(); } void UK2Node_MacroInstance::AllocateDefaultPins() { UK2Node::AllocateDefaultPins(); const UEdGraphSchema_K2* Schema = GetDefault(); PreloadObject(MacroGraphReference.GetBlueprint()); UEdGraph* MacroGraph = MacroGraphReference.GetGraph(); if (MacroGraph != nullptr) { PreloadObject(MacroGraph); // Preload the macro graph, if needed, so that we can get the proper pins if (MacroGraph->HasAnyFlags(RF_NeedLoad)) { PreloadObject(MacroGraph); FBlueprintEditorUtils::PreloadMembers(MacroGraph); } for (TArray::TIterator NodeIt(MacroGraph->Nodes); NodeIt; ++NodeIt) { if (UK2Node_Tunnel* TunnelNode = Cast(*NodeIt)) { // Only want exact tunnel nodes, more specific nodes like composites or macro instances shouldn't be grabbed. if (TunnelNode->GetClass() == UK2Node_Tunnel::StaticClass()) { for (TArray::TIterator PinIt(TunnelNode->Pins); PinIt; ++PinIt) { UEdGraphPin* PortPin = *PinIt; // We're not interested in any pins that have been expanded internally on the macro if (PortPin->ParentPin == nullptr) { UEdGraphPin* NewLocalPin = CreatePin(UEdGraphPin::GetComplementaryDirection(PortPin->Direction), PortPin->PinType, PortPin->PinName); Schema->SetPinAutogeneratedDefaultValue(NewLocalPin, PortPin->GetDefaultAsString()); } } } } } } CacheWildcardPins(); } void UK2Node_MacroInstance::PreloadRequiredAssets() { PreloadObject(MacroGraphReference.GetBlueprint()); UEdGraph* MacroGraph = MacroGraphReference.GetGraph(); PreloadObject(MacroGraph); Super::PreloadRequiredAssets(); } FText UK2Node_MacroInstance::GetTooltipText() const { UEdGraph* MacroGraph = MacroGraphReference.GetGraph(); if (FKismetUserDeclaredFunctionMetadata* Metadata = GetAssociatedGraphMetadata(MacroGraph)) { if (!Metadata->ToolTip.IsEmpty()) { return Metadata->ToolTip; } } if (MacroGraph == nullptr) { return NSLOCTEXT("K2Node", "Macro_Tooltip", "Macro"); } else if (CachedTooltip.IsOutOfDate(this)) { // FText::Format() is slow, so we cache this to save on performance CachedTooltip.SetCachedText(FText::Format(NSLOCTEXT("K2Node", "MacroGraphInstance_Tooltip", "{0} instance"), FText::FromName(MacroGraph->GetFName())), this); } return CachedTooltip; } FText UK2Node_MacroInstance::GetKeywords() const { FText Keywords = GetAssociatedGraphMetadata(GetMacroGraph())->Keywords; // If the Macro has Compact Node Title data, append the compact node title as a Keyword so it can be searched. if (ShouldDrawCompact()) { Keywords = FText::Format(FText::FromString(TEXT("{0} {1}")), Keywords, GetCompactNodeTitle()); } return Keywords; } FText UK2Node_MacroInstance::GetNodeTitle(ENodeTitleType::Type TitleType) const { UEdGraph* MacroGraph = MacroGraphReference.GetGraph(); FText Result = NSLOCTEXT("K2Node", "MacroInstance", "Macro instance"); if (MacroGraph) { Result = FText::FromString(MacroGraph->GetName()); if ((GEditor != NULL) && (GetDefault()->bShowFriendlyNames)) { Result = FText::FromString(FName::NameToDisplayString(Result.ToString(), false)); } } return Result; } FLinearColor UK2Node_MacroInstance::GetNodeTitleColor() const { UEdGraph* MacroGraph = MacroGraphReference.GetGraph(); if (FKismetUserDeclaredFunctionMetadata* Metadata = GetAssociatedGraphMetadata(MacroGraph)) { return Metadata->InstanceTitleColor.ToFColor(false); } return FLinearColor::White; } void UK2Node_MacroInstance::GetNodeContextMenuActions(UToolMenu* Menu, UGraphNodeContextMenuContext* Context) const { if ( Context->Pin == nullptr ) { { FToolMenuSection& Section = Menu->AddSection("K2NodeMacroInstance", NSLOCTEXT("K2Node", "MacroInstanceHeader", "Macro Instance")); Section.AddMenuEntry( "MacroInstanceFindInContentBrowser", NSLOCTEXT("K2Node", "MacroInstanceFindInContentBrowser", "Find in Content Browser"), NSLOCTEXT("K2Node", "MacroInstanceFindInContentBrowserTooltip", "Finds the Blueprint Macro Library that contains this Macro in the Content Browser"), FSlateIcon(FEditorStyle::GetStyleSetName(), "Icons.Search"), FUIAction( FExecuteAction::CreateStatic( &UK2Node_MacroInstance::FindInContentBrowser, MakeWeakObjectPtr(const_cast(this)) ) ) ); } } } FKismetUserDeclaredFunctionMetadata* UK2Node_MacroInstance::GetAssociatedGraphMetadata(const UEdGraph* AssociatedMacroGraph) { if (AssociatedMacroGraph) { // Look at the graph for the entry node, to get the default title color TArray TunnelNodes; AssociatedMacroGraph->GetNodesOfClass(TunnelNodes); for (int32 i = 0; i < TunnelNodes.Num(); i++) { UK2Node_Tunnel* Node = TunnelNodes[i]; if (Node->IsEditable()) { if (Node->bCanHaveOutputs) { return &(Node->MetaData); } } } } return nullptr; } void UK2Node_MacroInstance::FindInContentBrowser(TWeakObjectPtr MacroInstance) { if ( MacroInstance.IsValid() ) { UEdGraph* InstanceMacroGraph = MacroInstance.Get()->MacroGraphReference.GetGraph(); if ( InstanceMacroGraph ) { UBlueprint* BlueprintToSync = FBlueprintEditorUtils::FindBlueprintForGraph(InstanceMacroGraph); if ( BlueprintToSync ) { TArray ObjectsToSync; ObjectsToSync.Add( BlueprintToSync ); GEditor->SyncBrowserToObjects(ObjectsToSync); } } } } void UK2Node_MacroInstance::NotifyPinConnectionListChanged(UEdGraphPin* ChangedPin) { Super::NotifyPinConnectionListChanged(ChangedPin); // added a link? if (ChangedPin->LinkedTo.Num() > 0) { // ... to a wildcard pin? bool const bIsWildcardPin = ChangedPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Wildcard; if (bIsWildcardPin) { // get type of pin we just got linked to FEdGraphPinType const LinkedPinType = ChangedPin->LinkedTo[0]->PinType; // change all other wildcard pins to the new type // note we're assuming only one wildcard type per Macro node, for now for(int32 PinIdx=0; PinIdxPinType.PinCategory == UEdGraphSchema_K2::PC_Wildcard) { // only copy the category stuff to preserve array and ref status TmpPin->PinType.PinCategory = LinkedPinType.PinCategory; TmpPin->PinType.PinSubCategory = LinkedPinType.PinSubCategory; TmpPin->PinType.PinSubCategoryObject = LinkedPinType.PinSubCategoryObject; } } } ResolvedWildcardType = LinkedPinType; bReconstructNode = true; } } else { // reconstruct on disconnects so we can revert back to wildcards if necessary bReconstructNode = true; } } void UK2Node_MacroInstance::NodeConnectionListChanged() { Super::NodeConnectionListChanged(); if (bReconstructNode) { ReconstructNode(); UBlueprint* const Blueprint = GetBlueprint(); if (Blueprint && !Blueprint->bBeingCompiled) { FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint); Blueprint->BroadcastChanged(); } } } FString UK2Node_MacroInstance::GetDocumentationLink() const { return TEXT("Shared/GraphNodes/Blueprint/UK2Node_MacroInstance"); } FString UK2Node_MacroInstance::GetDocumentationExcerptName() const { UEdGraph* MacroGraph = MacroGraphReference.GetGraph(); if (MacroGraph != nullptr) { return MacroGraph->GetName(); } return Super::GetDocumentationExcerptName(); } void UK2Node_MacroInstance::PostReconstructNode() { bReconstructNode = false; Super::PostReconstructNode(); } FName UK2Node_MacroInstance::GetCornerIcon() const { if (UEdGraph* MacroGraph = MacroGraphReference.GetGraph()) { FBlueprintMacroCosmeticInfo CosmeticInfo = FBlueprintEditorUtils::GetCosmeticInfoForMacro(MacroGraph); if (CosmeticInfo.bContainsLatentNodes) { return TEXT("Graph.Latent.LatentIcon"); } } return Super::GetCornerIcon(); } FSlateIcon UK2Node_MacroInstance::GetIconAndTint(FLinearColor& OutColor) const { const char* IconName = "GraphEditor.Macro_16x"; // Special case handling for standard macros // @TODO Change this to a SlateBurushAsset pointer on the graph or something similar, to allow any macro to have an icon UEdGraph* MacroGraph = MacroGraphReference.GetGraph(); if(MacroGraph != nullptr && MacroGraph->GetOuter()->GetName() == TEXT("StandardMacros")) { FName MacroName = FName(*MacroGraph->GetName()); if( MacroName == TEXT("ForLoop" ) || MacroName == TEXT("ForLoopWithBreak") || MacroName == TEXT("WhileLoop") ) { IconName = "GraphEditor.Macro.Loop_16x"; } else if( MacroName == TEXT("Gate") ) { IconName = "GraphEditor.Macro.Gate_16x"; } else if( MacroName == TEXT("Do N") ) { IconName = "GraphEditor.Macro.DoN_16x"; } else if (MacroName == TEXT("DoOnce")) { IconName = "GraphEditor.Macro.DoOnce_16x"; } else if (MacroName == TEXT("IsValid")) { IconName = "GraphEditor.Macro.IsValid_16x"; } else if (MacroName == TEXT("FlipFlop")) { IconName = "GraphEditor.Macro.FlipFlop_16x"; } else if ( MacroName == TEXT("ForEachLoop") || MacroName == TEXT("ForEachLoopWithBreak") ) { IconName = "GraphEditor.Macro.ForEach_16x"; } } return FSlateIcon("EditorStyle", IconName); } FText UK2Node_MacroInstance::GetCompactNodeTitle() const { FText ResultText; if (FKismetUserDeclaredFunctionMetadata* MacroGraphMetadata = GetAssociatedGraphMetadata(GetMacroGraph())) { ResultText = MacroGraphMetadata->CompactNodeTitle; } return ResultText; } bool UK2Node_MacroInstance::ShouldDrawCompact() const { return !GetCompactNodeTitle().IsEmpty(); } bool UK2Node_MacroInstance::CanPasteHere(const UEdGraph* TargetGraph) const { bool bCanPaste = false; UBlueprint* MacroBlueprint = GetSourceBlueprint(); UBlueprint* TargetBlueprint = FBlueprintEditorUtils::FindBlueprintForGraph(TargetGraph); if ((MacroBlueprint != nullptr) && (TargetBlueprint != nullptr)) { // Only allow "local" macro instances or instances from a macro library blueprint with the same parent class check(MacroBlueprint->ParentClass != nullptr && TargetBlueprint->ParentClass != nullptr); bCanPaste = (MacroBlueprint == TargetBlueprint) || (MacroBlueprint->BlueprintType == BPTYPE_MacroLibrary && TargetBlueprint->ParentClass->IsChildOf(MacroBlueprint->ParentClass)); } // Macro Instances are not allowed in it's own graph UEdGraph* MacroGraph = GetMacroGraph(); bCanPaste &= (MacroGraph != TargetGraph); // nor in Function graphs if the macro has latent functions in it bool const bIsTargetFuncGraph = (TargetGraph->GetSchema()->GetGraphType(TargetGraph) == GT_Function); bCanPaste &= (!bIsTargetFuncGraph || !FBlueprintEditorUtils::CheckIfGraphHasLatentFunctions(MacroGraph)); return bCanPaste && Super::CanPasteHere(TargetGraph); } void UK2Node_MacroInstance::PostFixupAllWildcardPins(bool bInAllWildcardPinsUnlinked) { if (bInAllWildcardPinsUnlinked) { // Reset the type to a wildcard because there are no longer any wildcard pins linked to determine a type with ResolvedWildcardType.ResetToDefaults(); // Collapse any wildcard pins that are split and set their type back to wildcard for (UEdGraphPin* Pin : WildcardPins) { GetSchema()->RecombinePin(Pin); Pin->PinType.PinCategory = UEdGraphSchema_K2::PC_Wildcard; Pin->PinType.PinSubCategory = NAME_None; Pin->PinType.PinSubCategoryObject = nullptr; } } } bool UK2Node_MacroInstance::HasExternalDependencies(TArray* OptionalOutput) const { UBlueprint* OtherBlueprint = MacroGraphReference.GetBlueprint(); const bool bResult = OtherBlueprint && (OtherBlueprint != GetBlueprint()); if (bResult && OptionalOutput) { if (UClass* OtherClass = *OtherBlueprint->GeneratedClass) { OptionalOutput->AddUnique(OtherClass); } for (UEdGraphPin* Pin : Pins) { if (Pin->PinType.PinSubCategoryObject.IsValid()) { if (UStruct* Struct = Cast(Pin->PinType.PinSubCategoryObject.Get())) { OptionalOutput->AddUnique(Struct); } else { OptionalOutput->AddUnique(Pin->PinType.PinSubCategoryObject.Get()->GetClass()); } } } } const bool bSuperResult = Super::HasExternalDependencies(OptionalOutput); return bSuperResult || bResult; } void UK2Node_MacroInstance::GetNodeAttributes( TArray>& OutNodeAttributes ) const { FString MacroName( TEXT( "InvalidMacro" )); if( UEdGraph* MacroGraph = GetMacroGraph() ) { MacroName = MacroGraph->GetName(); } OutNodeAttributes.Add( TKeyValuePair( TEXT( "Type" ), TEXT( "Macro" ) )); OutNodeAttributes.Add( TKeyValuePair( TEXT( "Class" ), GetClass()->GetName() )); OutNodeAttributes.Add( TKeyValuePair( TEXT( "Name" ), MacroName )); } FText UK2Node_MacroInstance::GetMenuCategory() const { FText MenuCategory = FEditorCategoryUtils::GetCommonCategory(FCommonEditorCategory::Utilities); if (UEdGraph* MacroGraph = GetMacroGraph()) { FKismetUserDeclaredFunctionMetadata* MacroGraphMetadata = UK2Node_MacroInstance::GetAssociatedGraphMetadata(MacroGraph); if ((MacroGraphMetadata != nullptr) && !MacroGraphMetadata->Category.IsEmpty()) { MenuCategory = MacroGraphMetadata->Category; } } return MenuCategory; } FBlueprintNodeSignature UK2Node_MacroInstance::GetSignature() const { FBlueprintNodeSignature NodeSignature = Super::GetSignature(); NodeSignature.AddSubObject(GetMacroGraph()); return NodeSignature; } #undef LOCTEXT_NAMESPACE