// Copyright Epic Games, Inc. All Rights Reserved. #include "MVVMBlueprintViewConversionFunction.h" #include "MVVMDeveloperProjectSettings.h" #include "Bindings/MVVMConversionFunctionHelper.h" #include "Bindings/MVVMBindingHelper.h" #include "Engine/Blueprint.h" #include "Kismet2/BlueprintEditorUtils.h" #include "EdGraphSchema_K2.h" #include "EdGraph/EdGraphPin.h" #include "K2Node_CallFunction.h" #include "K2Node_FunctionEntry.h" #include "K2Node_FunctionResult.h" #include "KismetCompiler.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(MVVMBlueprintViewConversionFunction) namespace UE::MVVM::Private { static FLazyName DefaultConversionFunctionName = TEXT("__ConversionFunction"); } const UFunction* UMVVMBlueprintViewConversionFunction::GetCompiledFunction(const UClass* SelfContext) const { if (NeedsWrapperGraph()) { FMemberReference CompiledFunction; CompiledFunction.SetSelfMember(GraphName); return CompiledFunction.ResolveMember(const_cast(SelfContext)); } return FunctionReference.ResolveMember(const_cast(SelfContext)); } FName UMVVMBlueprintViewConversionFunction::GetCompiledFunctionName() const { if (NeedsWrapperGraph()) { return GraphName; } return FunctionReference.GetMemberName(); } TVariant> UMVVMBlueprintViewConversionFunction::GetConversionFunction(const UClass* SelfContext) const { if (FunctionNode.Get()) { return TVariant>( TInPlaceType>(), FunctionNode); } else { const UFunction* ConversionFunction = FunctionReference.ResolveMember(const_cast(SelfContext)); return TVariant>( TInPlaceType(), ConversionFunction); } //return FunctionReference.ResolveMember(WidgetBlueprint->SkeletonGeneratedClass); //UK2Node_CallFunction* CallFunctionNode = GetConversionFunctionNode(WidgetBlueprint, Binding, bSourceToDestination); //if (CallFunctionNode) //{ // return CallFunctionNode->GetTargetFunction(); //} } void UMVVMBlueprintViewConversionFunction::Reset() { FunctionReference = FMemberReference(); FunctionNode = TSubclassOf(); GraphName = FName(); bWrapperGraphTransient = false; SavedPins.Reset(); CachedWrapperGraph = nullptr; CachedWrapperNode = nullptr; } void UMVVMBlueprintViewConversionFunction::InitFromFunction(UBlueprint* InContext, const UFunction* InFunction) { InitFromFunction(InContext, InFunction, UE::MVVM::Private::DefaultConversionFunctionName.Resolve()); } void UMVVMBlueprintViewConversionFunction::InitFromFunction(UBlueprint* InContext, const UFunction* InFunction, FName InGraphName) { Reset(); if (InFunction) { UClass* OwnerClass = InFunction->GetOuterUClass(); FGuid MemberGuid; UBlueprint::GetGuidFromClassByFieldName(OwnerClass, InFunction->GetFName(), MemberGuid); bool bIsSelf = (InContext->GeneratedClass && InContext->GeneratedClass->IsChildOf(OwnerClass)) || (InContext->SkeletonGeneratedClass && InContext->SkeletonGeneratedClass->IsChildOf(OwnerClass)); if (bIsSelf) { FunctionReference.SetSelfMember(InFunction->GetFName(), MemberGuid); } else { if (UBlueprint* VariableOwnerBP = Cast(OwnerClass->ClassGeneratedBy)) { OwnerClass = VariableOwnerBP->SkeletonGeneratedClass; } FunctionReference.SetExternalMember(InFunction->GetFName(), OwnerClass, MemberGuid); } // used to be SetFromField(Function, SelfContext->SkeletonGeneratedClass); if (UE::MVVM::ConversionFunctionHelper::RequiresWrapper(InFunction)) { GraphName = InGraphName; bWrapperGraphTransient = !GetDefault()->bAllowConversionFunctionGeneratedGraphInEditor; GetOrCreateWrapperGraphInternal(InContext, InFunction); SavePinValues(InContext); } } } void UMVVMBlueprintViewConversionFunction::InitializeFromWrapperGraph(UBlueprint* SelfContext, UEdGraph* Graph) { Reset(); if (UK2Node* WrapperNode = UE::MVVM::ConversionFunctionHelper::GetWrapperNode(Graph)) { if (UK2Node_CallFunction* CallFunction = Cast(WrapperNode)) { FunctionReference = CallFunction->FunctionReference; } else { FunctionNode = WrapperNode->GetClass(); } CachedWrapperGraph = WrapperNode->GetGraph(); check(CachedWrapperGraph); GraphName = CachedWrapperGraph->GetFName(); bWrapperGraphTransient = !GetDefault()->bAllowConversionFunctionGeneratedGraphInEditor; CachedWrapperNode = WrapperNode; SavePinValues(SelfContext); if (bWrapperGraphTransient && CachedWrapperNode) { SelfContext->FunctionGraphs.RemoveSingle(CachedWrapperGraph); } } } void UMVVMBlueprintViewConversionFunction::InitializeFromMemberReference(UBlueprint* SelfContext, FMemberReference MemberReference) { Reset(); FunctionReference = MemberReference; } UEdGraph* UMVVMBlueprintViewConversionFunction::GetOrCreateIntermediateWrapperGraph(FKismetCompilerContext& Context) const { if (!NeedsWrapperGraph()) { return nullptr; } if (CachedWrapperGraph) { return CachedWrapperGraph; } if (IsWrapperGraphTransient()) { if (FunctionNode.Get()) { ensure(false); // not supported for now return nullptr; } else { const UFunction* ConversionFunction = FunctionReference.ResolveMember(Context.NewClass); return GetOrCreateWrapperGraphInternal(Context, ConversionFunction); } } else { TObjectPtr* Found = Context.Blueprint->FunctionGraphs.FindByPredicate([GraphName = GetWrapperGraphName()](const UEdGraph* Other) { return Other->GetFName() == GraphName; }); if (Found) { CachedWrapperGraph = *Found; CachedWrapperNode = UE::MVVM::ConversionFunctionHelper::GetWrapperNode(CachedWrapperGraph); } return CachedWrapperGraph; } } UEdGraph* UMVVMBlueprintViewConversionFunction::GetOrCreateWrapperGraph(UBlueprint* Blueprint) const { if (!NeedsWrapperGraph()) { return nullptr; } if (CachedWrapperGraph) { return CachedWrapperGraph; } if (IsWrapperGraphTransient()) { if (FunctionNode.Get()) { ensure(false); // not supported for now return nullptr; } else { const UFunction* ConversionFunction = FunctionReference.ResolveMember(Blueprint->SkeletonGeneratedClass); return GetOrCreateWrapperGraphInternal(Blueprint, ConversionFunction); } } else { TObjectPtr* Found = Blueprint->FunctionGraphs.FindByPredicate([GraphName = GetWrapperGraphName()](const UEdGraph* Other) { return Other->GetFName() == GraphName; }); if (Found) { CachedWrapperGraph = *Found; CachedWrapperNode = UE::MVVM::ConversionFunctionHelper::GetWrapperNode(CachedWrapperGraph); } return CachedWrapperGraph; } } UEdGraphPin* UMVVMBlueprintViewConversionFunction::GetOrCreateGraphPin(UBlueprint* Blueprint, FName PinName) const { GetOrCreateWrapperGraph(Blueprint); if (CachedWrapperNode) { return CachedWrapperNode->FindPin(PinName); } return nullptr; } UEdGraph* UMVVMBlueprintViewConversionFunction::GetOrCreateWrapperGraphInternal(FKismetCompilerContext& Context, const UFunction* Function) const { return GetOrCreateWrapperGraphInternal(Context.Blueprint, Function); } UEdGraph* UMVVMBlueprintViewConversionFunction::GetOrCreateWrapperGraphInternal(UBlueprint* Blueprint, const UFunction* Function) const { bool bConst = true; TPair Result = UE::MVVM::ConversionFunctionHelper::CreateGraph(Blueprint, GraphName, nullptr, Function, bConst, bWrapperGraphTransient); CachedWrapperGraph = Result.Get<0>(); CachedWrapperNode = Result.Get<1>(); LoadPinValuesInternal(Blueprint); return CachedWrapperGraph; } void UMVVMBlueprintViewConversionFunction::RemoveWrapperGraph(UBlueprint* Blueprint) { TObjectPtr* Result = Blueprint->FunctionGraphs.FindByPredicate([WrapperName = GraphName](const UEdGraph* GraphPtr) { return GraphPtr->GetFName() == WrapperName; }); if (Result) { FBlueprintEditorUtils::RemoveGraph(Blueprint, Result->Get()); } CachedWrapperGraph = nullptr; CachedWrapperNode = nullptr; } void UMVVMBlueprintViewConversionFunction::SetGraphPin(UBlueprint* Blueprint, FName PinName, const FMVVMBlueprintPropertyPath& Path) { if (!NeedsWrapperGraph()) { return; } UEdGraphPin* GraphPin = GetOrCreateGraphPin(Blueprint, PinName); // Set the value and make the blueprint as dirty before creating the pin. //A property may not be created yet and the skeletal needs to be recreated. FMVVMBlueprintPin* Pin = SavedPins.FindByPredicate([PinName](const FMVVMBlueprintPin& Other) { return PinName == Other.GetName(); }); if (!Pin) { Pin = &SavedPins.Add_GetRef(FMVVMBlueprintPin::CreateFromPin(Blueprint, GraphPin)); } Pin->SetPath(Path); FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Blueprint); UE::MVVM::ConversionFunctionHelper::SetPropertyPathForPin(Blueprint, Path, GraphPin); } void UMVVMBlueprintViewConversionFunction::SavePinValues(UBlueprint* Blueprint) { if (CachedWrapperNode) { SavedPins.Reset(); for (UEdGraphPin* Pin : CachedWrapperNode->Pins) { if (Pin->PinName != UEdGraphSchema_K2::PN_Self && Pin->PinName != UEdGraphSchema_K2::PN_Execute && Pin->Direction == EGPD_Input) { SavedPins.Add(FMVVMBlueprintPin::CreateFromPin(Blueprint, Pin)); } } } } void UMVVMBlueprintViewConversionFunction::LoadPinValuesInternal(UBlueprint* Blueprint) const { if (CachedWrapperNode) { for (const FMVVMBlueprintPin& Pin : SavedPins) { if (UEdGraphPin* GraphPin = CachedWrapperNode->FindPin(Pin.GetName())) { Pin.CopyTo(Blueprint, GraphPin); } } } }