// Copyright Epic Games, Inc. All Rights Reserved. #include "MVVMBlueprintViewEvent.h" #include "MVVMBlueprintView.h" #include "Bindings/MVVMConversionFunctionHelper.h" #include "Bindings/MVVMBindingHelper.h" #include "Engine/Blueprint.h" #include "Kismet2/BlueprintEditorUtils.h" #include "MVVMDeveloperProjectSettings.h" #include "MVVMWidgetBlueprintExtension_View.h" #include "WidgetBlueprint.h" #include "EdGraphSchema_K2.h" #include "EdGraph/EdGraphPin.h" #include "K2Node_CallFunction.h" #include "K2Node_FunctionEntry.h" #include "K2Node_FunctionResult.h" #include "K2Node_VariableSet.h" #include "KismetCompiler.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(MVVMBlueprintViewEvent) #define LOCTEXT_NAMESPACE "MVVMBlueprintViewEvent" void UMVVMBlueprintViewEvent::SetEventPath(FMVVMBlueprintPropertyPath InEventPath) { if (InEventPath == EventPath) { return; } RemoveWrapperGraph(); EventPath = MoveTemp(InEventPath); GraphName = FName(); if (!EventPath.IsEmpty()) { TStringBuilder<256> StringBuilder; StringBuilder << TEXT("__"); StringBuilder << FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphensLower); GraphName = StringBuilder.ToString(); } CreateWrapperGraphInternal(); SavePinValues(); } void UMVVMBlueprintViewEvent::SetDestinationPath(FMVVMBlueprintPropertyPath InDestinationPath) { if (InDestinationPath == DestinationPath) { return; } RemoveWrapperGraph(); DestinationPath = MoveTemp(InDestinationPath); CreateWrapperGraphInternal(); SavePinValues(); } UEdGraph* UMVVMBlueprintViewEvent::GetOrCreateWrapperGraph() { if (CachedWrapperGraph) { return CachedWrapperGraph; } CreateWrapperGraphInternal(); return CachedWrapperGraph; } void UMVVMBlueprintViewEvent::RemoveWrapperGraph() { if (CachedWrapperGraph) { FBlueprintEditorUtils::RemoveGraph(GetWidgetBlueprintInternal(), CachedWrapperGraph); CachedWrapperGraph = nullptr; CachedWrapperNode = nullptr; } Messages.Empty(); SavedPins.Empty(); } UEdGraphPin* UMVVMBlueprintViewEvent::GetOrCreateGraphPin(FName PinName) { GetOrCreateWrapperGraph(); if (CachedWrapperNode) { return CachedWrapperNode->FindPin(PinName); } return nullptr; } void UMVVMBlueprintViewEvent::SavePinValues() { if (CachedWrapperNode) { SavedPins.Reset(); UWidgetBlueprint* Blueprint = GetWidgetBlueprintInternal(); 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)); } } } } FMVVMBlueprintPropertyPath UMVVMBlueprintViewEvent::GetPinPath(FName PinName) const { const FMVVMBlueprintPin* ViewPin = SavedPins.FindByPredicate([PinName](const FMVVMBlueprintPin& Other) { return PinName == Other.GetName(); }); return ViewPin ? ViewPin->GetPath() : FMVVMBlueprintPropertyPath(); } void UMVVMBlueprintViewEvent::SetPinPath(FName PinName, const FMVVMBlueprintPropertyPath& Path) { UEdGraphPin* GraphPin = GetOrCreateGraphPin(PinName); if (GraphPin) { UBlueprint* Blueprint = GetWidgetBlueprintInternal(); // Set the value and make the blueprint as dirty before creating the pin. FMVVMBlueprintPin* ViewPin = SavedPins.FindByPredicate([PinName](const FMVVMBlueprintPin& Other) { return PinName == Other.GetName(); }); if (!ViewPin) { ViewPin = &SavedPins.Add_GetRef(FMVVMBlueprintPin::CreateFromPin(Blueprint, GraphPin)); } //A property (viewmodel or widget) may not be created yet and the skeletal needs to be recreated. FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Blueprint); UE::MVVM::ConversionFunctionHelper::SetPropertyPathForPin(Blueprint, Path, GraphPin); // Take the path built in BP, it may had some errors ViewPin->SetPath(UE::MVVM::ConversionFunctionHelper::GetPropertyPathForPin(Blueprint, GraphPin, false)); } } UWidgetBlueprint* UMVVMBlueprintViewEvent::GetWidgetBlueprintInternal() const { return GetOuterUMVVMBlueprintView()->GetOuterUMVVMWidgetBlueprintExtension_View()->GetWidgetBlueprint(); } bool UMVVMBlueprintViewEvent::Supports(const UWidgetBlueprint* WidgetBlueprint, const FMVVMBlueprintPropertyPath& PropertyPath) { return GetDefault()->bAllowBindingEvent && GetEventSignature(WidgetBlueprint, PropertyPath) != nullptr; } const UFunction* UMVVMBlueprintViewEvent::GetEventSignature() const { return GetEventSignature(GetWidgetBlueprintInternal(), EventPath); } const UFunction* UMVVMBlueprintViewEvent::GetEventSignature(const UWidgetBlueprint* WidgetBlueprint, const FMVVMBlueprintPropertyPath& PropertyPath) { if (!PropertyPath.IsEmpty() && PropertyPath.GetFieldPaths().Num() > 0) { const FMVVMBlueprintFieldPath& LastPath = PropertyPath.GetFieldPaths().Last(); UE::MVVM::FMVVMConstFieldVariant LastField = LastPath.GetField(WidgetBlueprint->SkeletonGeneratedClass); if (LastField.IsProperty()) { if (const FMulticastDelegateProperty* Property = CastField(LastField.GetProperty())) { return Property->SignatureFunction.Get(); } } } return nullptr; } UEdGraph* UMVVMBlueprintViewEvent::CreateWrapperGraphInternal() { if (GraphName.IsNone() || DestinationPath.IsEmpty() || EventPath.IsEmpty()) { return nullptr; } const UFunction* DelegateSignature = GetEventSignature(); if (DelegateSignature == nullptr) { return nullptr; } if (DestinationPath.GetFieldPaths().Num() > 0) { const FMVVMBlueprintFieldPath& LastPath = DestinationPath.GetFieldPaths().Last(); UE::MVVM::FMVVMConstFieldVariant LastField = LastPath.GetField(GetWidgetBlueprintInternal()->SkeletonGeneratedClass); if (LastField.IsFunction() && LastField.GetFunction() != nullptr) { return CreateWrapperGraphInternal(DelegateSignature, LastField.GetFunction()); } else if (LastField.IsProperty() && LastField.GetProperty() != nullptr) { return CreateWrapperGraphInternal(DelegateSignature, LastField.GetProperty()); } } else if (DestinationPath.GetViewModelId().IsValid()) { const FMVVMBlueprintViewModelContext* Context = GetOuterUMVVMBlueprintView()->FindViewModel(DestinationPath.GetViewModelId()); const FProperty* ViewModel = Context ? GetWidgetBlueprintInternal()->SkeletonGeneratedClass->FindPropertyByName(Context->GetViewModelName()) : nullptr; if (ViewModel) { return CreateWrapperGraphInternal(DelegateSignature, ViewModel); } } return nullptr; } UEdGraph* UMVVMBlueprintViewEvent::CreateWrapperGraphInternal(const UFunction* DelegateSignature, const UFunction* Function) { UWidgetBlueprint* WidgetBlueprint = GetWidgetBlueprintInternal(); bool bTransient = true; TPair Result = UE::MVVM::ConversionFunctionHelper::CreateGraph(WidgetBlueprint, GraphName, DelegateSignature, Function, bTransient); CachedWrapperGraph = Result.Get<0>(); CachedWrapperNode = Result.Get<1>(); // Add SelfNode if needed if (CachedWrapperNode) { if (UEdGraphPin* SelfPin = CachedWrapperNode->FindPin(UEdGraphSchema_K2::PSC_Self)) { FMVVMBlueprintPropertyPath PathToSet = DestinationPath; TArray FieldPaths = PathToSet.GetFields(WidgetBlueprint->SkeletonGeneratedClass); PathToSet.ResetPropertyPath(); for (int32 Index = 0; Index < FieldPaths.Num() - 1; ++Index) { PathToSet.AppendPropertyPath(WidgetBlueprint, FieldPaths[Index]); } ; UE::MVVM::ConversionFunctionHelper::SetPropertyPathForPin(WidgetBlueprint, PathToSet, SelfPin); } } LoadPinValuesInternal(); return CachedWrapperGraph; } UEdGraph* UMVVMBlueprintViewEvent::CreateWrapperGraphInternal(const UFunction* DelegateSignature, const FProperty* Property) { UWidgetBlueprint* WidgetBlueprint = GetWidgetBlueprintInternal(); bool bTransient = true; TPair Result = UE::MVVM::ConversionFunctionHelper::CreateGraph(WidgetBlueprint, GraphName, DelegateSignature, UK2Node_VariableSet::StaticClass(), bTransient, [WidgetBlueprint, Property](UK2Node* NewNode) { UK2Node_VariableSet* VariableNode = CastChecked(NewNode); UEdGraphSchema_K2::ConfigureVarNode(VariableNode, Property->GetFName(), Property->GetOwnerStruct(), WidgetBlueprint); }); CachedWrapperGraph = Result.Get<0>(); CachedWrapperNode = Result.Get<1>(); // Add SelfNode if (CachedWrapperNode) { FMVVMBlueprintPropertyPath PathToSet = DestinationPath; TArray FieldPaths = PathToSet.GetFields(WidgetBlueprint->SkeletonGeneratedClass); PathToSet.ResetPropertyPath(); for (int32 Index = 0; Index < FieldPaths.Num() - 1; ++Index) { PathToSet.AppendPropertyPath(WidgetBlueprint, FieldPaths[Index]); } UEdGraphPin* SelfPin = CachedWrapperNode->FindPinChecked(UEdGraphSchema_K2::PSC_Self); UE::MVVM::ConversionFunctionHelper::SetPropertyPathForPin(WidgetBlueprint, PathToSet, SelfPin); } LoadPinValuesInternal(); return CachedWrapperGraph; } void UMVVMBlueprintViewEvent::LoadPinValuesInternal() { if (CachedWrapperNode) { TArray AllPins; AllPins.Reserve(CachedWrapperNode->Pins.Num()); for (UEdGraphPin* Pin : CachedWrapperNode->Pins) { if (Pin->PinName != UEdGraphSchema_K2::PN_Self && Pin->PinName != UEdGraphSchema_K2::PN_Execute && Pin->Direction == EGPD_Input) { AllPins.Add(Pin); } } UBlueprint* Blueprint = GetWidgetBlueprintInternal(); for (int32 Index = SavedPins.Num() - 1; Index >= 0; --Index) { const FMVVMBlueprintPin& Pin = SavedPins[Index]; if (UEdGraphPin* GraphPin = CachedWrapperNode->FindPin(Pin.GetName())) { AllPins.RemoveSingleSwap(GraphPin); Pin.CopyTo(Blueprint, GraphPin); } else { // pin doesn't exist anymore SavedPins.RemoveAt(Index); } } // Create the reminding pin. for (UEdGraphPin* Pin : AllPins) { SavedPins.Add(FMVVMBlueprintPin::CreateFromPin(Blueprint, Pin)); } } } TArray UMVVMBlueprintViewEvent::GetCompilationMessages(EMessageType InMessageType) const { TArray Result; Result.Reset(Messages.Num()); for (const FMessage& Msg : Messages) { if (Msg.MessageType == InMessageType) { Result.Add(Msg.MessageText); } } return Result; } bool UMVVMBlueprintViewEvent::HasCompilationMessage(EMessageType InMessageType) const { return Messages.ContainsByPredicate([InMessageType](const FMessage& Other) { return Other.MessageType == InMessageType; }); } void UMVVMBlueprintViewEvent::AddCompilationToBinding(FMessage MessageToAdd) { Messages.Add(MoveTemp(MessageToAdd)); } void UMVVMBlueprintViewEvent::ResetCompilationMessages() { Messages.Reset(); } FText UMVVMBlueprintViewEvent::GetDisplayName(bool bUseDisplayName) const { TArray JoinArgs; for (const FMVVMBlueprintPin& Pin : GetPins()) { if (Pin.UsedPathAsValue()) { JoinArgs.Add(Pin.GetPath().ToText(GetWidgetBlueprintInternal(), bUseDisplayName)); } } return FText::Format(LOCTEXT("BlueprintViewEventDisplayNameFormat", "{0} => {1}({2})") , EventPath.ToText(GetWidgetBlueprintInternal(), bUseDisplayName) , DestinationPath.ToText(GetWidgetBlueprintInternal(), bUseDisplayName) , FText::Join(LOCTEXT("PathDelimiter", ", "), JoinArgs) ); } FString UMVVMBlueprintViewEvent::GetSearchableString() const { TStringBuilder<256> Builder; Builder << EventPath.ToString(GetWidgetBlueprintInternal(), true, true); Builder << TEXT(' '); Builder << DestinationPath.ToString(GetWidgetBlueprintInternal(), true, true); Builder << TEXT('('); bool bFirst = true; for (const FMVVMBlueprintPin& Pin : GetPins()) { if (!bFirst) { Builder << TEXT(", "); } if (Pin.UsedPathAsValue()) { Builder << Pin.GetPath().ToString(GetWidgetBlueprintInternal(), true, true); } bFirst = false; } Builder << TEXT(')'); return Builder.ToString(); } void UMVVMBlueprintViewEvent::PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChainEvent) { Super::PostEditChangeChainProperty(PropertyChainEvent); GetOuterUMVVMBlueprintView()->OnViewModelsUpdated.Broadcast(); } #undef LOCTEXT_NAMESPACE