Files
UnrealEngine/Engine/Source/Editor/Kismet/Private/SFixupSelfContextDlg.cpp
2025-05-18 13:04:45 +08:00

333 lines
8.7 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SFixupSelfContextDlg.h"
#include "BlueprintEditor.h"
#include "Containers/BitArray.h"
#include "Containers/Set.h"
#include "Containers/SparseArray.h"
#include "Containers/UnrealString.h"
#include "Delegates/Delegate.h"
#include "EdGraph/EdGraph.h"
#include "Editor.h"
#include "Editor/EditorEngine.h"
#include "Engine/Blueprint.h"
#include "Framework/Views/ITypedTableView.h"
#include "HAL/Platform.h"
#include "HAL/PlatformCrt.h"
#include "Internationalization/Internationalization.h"
#include "Internationalization/Text.h"
#include "K2Node_CallFunction.h"
#include "K2Node_CustomEvent.h"
#include "K2Node_Event.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Layout/Children.h"
#include "Layout/Margin.h"
#include "Math/Color.h"
#include "Misc/AssertionMacros.h"
#include "Misc/Attribute.h"
#include "Misc/Optional.h"
#include "SlotBase.h"
#include "Styling/AppStyle.h"
#include "Styling/SlateColor.h"
#include "Templates/SubclassOf.h"
#include "Types/SlateEnums.h"
#include "Types/SlateStructs.h"
#include "Widgets/Input/SButton.h"
#include "Widgets/Input/STextComboBox.h"
#include "Widgets/Layout/SBorder.h"
#include "Widgets/Layout/SBox.h"
#include "Widgets/SBoxPanel.h"
#include "Widgets/SWindow.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/Views/SListView.h"
#include "Widgets/Views/STableRow.h"
class ITableRow;
class STableViewBase;
class SWidget;
class UFunction;
#define LOCTEXT_NAMESPACE "FixupContextDialog"
TSharedRef<SWidget> FFixupSelfContextItem::CreateWidget(TArray<TSharedPtr<FString>>& InFixupOptions)
{
return SNew(SHorizontalBox)
+SHorizontalBox::Slot()
[
SNew(STextBlock)
.Text(FText::FromName(FuncName))
]
+SHorizontalBox::Slot()
[
SAssignNew(ComboBox, STextComboBox)
.OptionsSource(&InFixupOptions)
.InitiallySelectedItem(InFixupOptions[0])
];
}
void SFixupSelfContextDialog::Construct(const FArguments& InArgs, const TArray< UK2Node_CallFunction* >& InNodesToFixup, UBlueprint* InFromBP, const FBlueprintEditor* InBlueprintEditorPtr, bool bInOtherPastedNodes)
{
NodesToFixup = InNodesToFixup;
BlueprintEditor = InBlueprintEditorPtr;
bOtherNodes = bInOtherPastedNodes;
FromBP = InFromBP;
Options.Add(MakeShared<FString>(LOCTEXT("DoNothing", "Do Nothing").ToString()));
Options.Add(MakeShared<FString>(LOCTEXT("CreateMatchingFunction", "Create Matching Function in Blueprint").ToString()));
Options.Add(MakeShared<FString>(LOCTEXT("RemoveNodes", "Remove Node(s)").ToString()));
for (UK2Node_CallFunction* Node : NodesToFixup)
{
bool bIsNew = true;
for (FListViewItem Func : FunctionsToFixup)
{
if (Func->FuncName == Node->GetFunctionName())
{
bIsNew = false;
Func->Nodes.Add(Node);
}
}
if (bIsNew)
{
FunctionsToFixup.Add(MakeShared<FFixupSelfContextItem>(Node->GetFunctionName()));
FunctionsToFixup.Last()->Nodes.Add(Node);
}
}
ChildSlot
[
SNew(SVerticalBox)
+SVerticalBox::Slot()
.AutoHeight()
[
SNew(STextBlock)
.Text(LOCTEXT("FixupDescription", "Some function references could not be resolved in the new context. How would you like to fix them?"))
.AutoWrapText(true)
]
+SVerticalBox::Slot()
.Padding(5.0f, 5.0f)
[
SNew(SBox)
.MinDesiredHeight(100.0f)
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("Menu.Background"))
[
SNew(SListView<FListViewItem>)
.ListItemsSource(&FunctionsToFixup)
.SelectionMode(ESelectionMode::None)
.OnGenerateRow(this, &SFixupSelfContextDialog::OnGenerateRow)
]
]
]
+SVerticalBox::Slot()
.AutoHeight()
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
.Padding(5.0f, 3.0f)
[
SNew(STextBlock)
.Text(LOCTEXT("NoNodesWarning", "WARNING: Nothing will be pasted!"))
.ColorAndOpacity(FSlateColor(FLinearColor::Yellow))
.Visibility(this, &SFixupSelfContextDialog::GetNoneWarningVisibility)
]
+SHorizontalBox::Slot()
.FillWidth(1.0f)
.HAlign(HAlign_Right)
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.AutoWidth()
.HAlign(HAlign_Right)
.VAlign(VAlign_Bottom)
.Padding(5.0f, 3.0f)
[
SNew(SButton)
.Text(LOCTEXT("Cancel", "Cancel"))
.OnClicked(this, &SFixupSelfContextDialog::CloseWindow, false)
]
+SHorizontalBox::Slot()
.AutoWidth()
.HAlign(HAlign_Right)
.VAlign(VAlign_Bottom)
.Padding(5.0f, 3.0f)
[
SNew(SButton)
.Text(LOCTEXT("Confirm", "Confirm"))
.OnClicked(this, &SFixupSelfContextDialog::CloseWindow, true)
]
]
]
];
}
bool SFixupSelfContextDialog::CreateModal(const TArray<UK2Node_CallFunction*>& NodesToFixup, UBlueprint* InFromBP, const FBlueprintEditor* BlueprintEditor, bool bOtherPastedNodes)
{
TSharedPtr<SWindow> Window;
TSharedPtr<SFixupSelfContextDialog> Widget;
Window = SNew(SWindow)
.Title(LOCTEXT("FixupReferencesTitle", "Fix Self Context Function References"))
.SizingRule(ESizingRule::UserSized)
.ClientSize(FVector2D(400.f, 300.f))
.SupportsMaximize(true)
.SupportsMinimize(false)
.HasCloseButton(false)
[
SNew(SBorder)
.Padding(4.f)
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
[
SAssignNew(Widget, SFixupSelfContextDialog, NodesToFixup, InFromBP, BlueprintEditor, bOtherPastedNodes)
]
];
Widget->MyWindow = Window;
GEditor->EditorAddModalWindow(Window.ToSharedRef());
return Widget->bOutConfirmed;
}
TSharedRef<ITableRow> SFixupSelfContextDialog::OnGenerateRow(FListViewItem Item, const TSharedRef<STableViewBase>& OwnerTable)
{
return SNew(STableRow<FListViewItem>, OwnerTable)
[
Item->CreateWidget(Options)
];
}
EVisibility SFixupSelfContextDialog::GetNoneWarningVisibility() const
{
if (bOtherNodes)
{
return EVisibility::Hidden;
}
for (FListViewItem Item : FunctionsToFixup)
{
if (Item->ComboBox.IsValid())
{
int32 Strategy;
Options.Find(Item->ComboBox->GetSelectedItem(), Strategy);
if (FFixupSelfContextItem::EFixupStrategy(Strategy) != FFixupSelfContextItem::EFixupStrategy::RemoveNode)
{
return EVisibility::Hidden;
}
}
}
return EVisibility::Visible;
}
FReply SFixupSelfContextDialog::CloseWindow(bool bConfirmed)
{
if (bConfirmed)
{
for (FListViewItem ItemPtr : FunctionsToFixup)
{
if (!ItemPtr.IsValid() || !ItemPtr->ComboBox.IsValid())
{
continue;
}
int32 Strategy;
Options.Find(ItemPtr->ComboBox->GetSelectedItem(), Strategy);
switch (FFixupSelfContextItem::EFixupStrategy(Strategy))
{
case FFixupSelfContextItem::EFixupStrategy::CreateNewFunction:
CreateMissingFunctions(ItemPtr);
break;
case FFixupSelfContextItem::EFixupStrategy::RemoveNode:
for (UK2Node_CallFunction* Node : ItemPtr->Nodes)
{
UEdGraph* Graph = Node ? Node->GetGraph() : nullptr;
if (Graph)
{
Graph->RemoveNode(Node);
}
}
break;
case FFixupSelfContextItem::EFixupStrategy::DoNothing:
default:
break;
}
}
}
bOutConfirmed = bConfirmed;
if (MyWindow.IsValid())
{
MyWindow->RequestDestroyWindow();
}
return FReply::Handled();
}
void SFixupSelfContextDialog::CreateMissingFunctions(FListViewItem FuncToFix)
{
if (!BlueprintEditor || !FuncToFix.IsValid())
{
return;
}
// Search the FromBP for an Event with the name we are interested in
UFunction* OriginalEventFunc = nullptr;
if(FromBP && FromBP->SkeletonGeneratedClass)
{
TArray<UK2Node_Event*> AllEvents;
FBlueprintEditorUtils::GetAllNodesOfClass(FromBP, AllEvents);
for (UK2Node_Event* EventNode : AllEvents)
{
if (EventNode->GetFunctionName() == FuncToFix->FuncName)
{
// Attempt to find the UFunction by name on the given blueprint instead of the EventReference because
// the EventReference will always be empty, giving a nullptr every time.
OriginalEventFunc = FromBP->SkeletonGeneratedClass->FindFunctionByName(FuncToFix->FuncName);
break;
}
}
}
if(OriginalEventFunc)
{
// Spawn a new event node in the BP we are copying to
UBlueprint* BP = BlueprintEditor->GetBlueprintObj();
UEdGraph* EventGraph = FBlueprintEditorUtils::FindEventGraph(BP);
UK2Node_CustomEvent* NewNode = UK2Node_CustomEvent::CreateFromFunction(
EventGraph->GetGoodPlaceForNewNode(),
EventGraph,
FuncToFix->FuncName.ToString(),
OriginalEventFunc
);
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(BP);
}
else
{
// Otherwise just spawn a regular function graph
check(FuncToFix->Nodes.Num() > 0);
FBlueprintEditorUtils::CreateMatchingFunction(FuncToFix->Nodes[0], BlueprintEditor->GetDefaultSchema());
}
// Reconstruct all the nodes so that they get the newly created function reference
for (UK2Node_CallFunction* Node : FuncToFix->Nodes)
{
Node->ReconstructNode();
}
}
#undef LOCTEXT_NAMESPACE