ComboInput/Source/ComboInputEditor/Private/Ed/AssetEditor_ComboActionGraph.cpp
2023-09-29 15:11:48 -04:00

936 lines
29 KiB
C++

// ©2023 Batty Bovine Productions, LLC. All Rights Reserved.
#include "AssetEditor_ComboActionGraph.h"
#include "GraphEditorActions.h"
#include "Framework/Commands/GenericCommands.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "HAL/PlatformApplicationMisc.h"
#include "EdGraphUtilities.h"
#include "ComboInputEditor.h"
#include "ComboActionGraph.h"
#include "Nodes/ComboActionGraphNode.h"
#include "Ed/EdComboActionGraph.h"
#include "Ed/EdComboActionGraphEdge.h"
#include "Ed/EdComboActionGraphNode.h"
#include "Ed/FAssetEditorToolbarComboActionGraph.h"
#include "Ed/FComboActionGraphEditorCommands.h"
#include "ComboActionGraphSchema.h"
//#include "Helpers/ComboActionGraphHelpers.h"
//#include "Helpers/ComboActionSystemEditorBFC.h"
#include "Layout/AssetEditorTabs.h"
#include "Layout/ComboActionGraphLayoutStrategy.h"
#include "Layout/ComboActionForceDirectedSolveLayoutStrategy.h"
#include "Layout/ComboActionTreeSolveLayoutStrategy.h"
//#include "Popups/MDSPopup_GraphValidation.h"
//#include "Search/ComboActionSearchUtils.h"
#include "Settings/ComboActionGraphEditorSettings.h"
#include "UObject/ObjectSaveContext.h"
#define LOCTEXT_NAMESPACE "AssetEditor_ComboActionGraph"
DEFINE_LOG_CATEGORY(LogAssetEditorComboActionGraph);
#pragma region ConstantNames
const FName ComboActionGraphEditorAppName = FName(TEXT("ComboActionGraphEditorApp"));
const FName FAssetEditorTabs_ComboActionGraph::ComboActionGraphPropertyID(TEXT("ComboActionGraphProperty"));
const FName FAssetEditorTabs_ComboActionGraph::ViewportID(TEXT("Viewport"));
const FName FAssetEditorTabs_ComboActionGraph::SearchToolbarID(TEXT("Search"));
#pragma endregion
FAssetEditor_ComboActionGraph::FAssetEditor_ComboActionGraph()
{
this->EditingGraph = nullptr;
this->ComboActionGraphEditorSettings = GetMutableDefault<UComboActionGraphEditorSettings>();
this->OnPackageSavedDelegateHandle = UPackage::PackageSavedWithContextEvent.AddRaw(this, &FAssetEditor_ComboActionGraph::OnPackageSaved);
}
FAssetEditor_ComboActionGraph::~FAssetEditor_ComboActionGraph()
{
this->EditingGraph = nullptr;
UPackage::PackageSavedWithContextEvent.Remove(this->OnPackageSavedDelegateHandle);
FGenericCommands::Unregister();
FGraphEditorCommands::Unregister();
FComboActionGraphEditorCommands::Unregister();
this->ToolbarBuilder.Reset();
}
void FAssetEditor_ComboActionGraph::OnPackageSaved(const FString& String, UPackage* Package, FObjectPostSaveContext ObjectPostSaveContext)
{
this->RebuildComboActionGraph();
}
void FAssetEditor_ComboActionGraph::InitComboActionGraphAssetEditor(const EToolkitMode::Type Mode, const TSharedPtr<IToolkitHost>& InitToolkitHost, UComboActionGraph *Graph)
{
this->EditingGraph = Graph;
this->CreateEdGraph();
FGenericCommands::Register();
FGraphEditorCommands::Register();
FComboActionGraphEditorCommands::Register();
if (!this->ToolbarBuilder.IsValid())
{
this->ToolbarBuilder = MakeShareable(new FAssetEditorToolbarComboActionGraph(SharedThis(this)));
}
this->BindCommands();
this->CreateInternalWidgets();
TSharedPtr<FExtender> ToolbarExtender = MakeShareable(new FExtender);
this->ToolbarBuilder->AddComboActionGraphToolbar(ToolbarExtender);
// Layout
const TSharedRef<FTabManager::FLayout> StandaloneDefaultLayout = FTabManager::NewLayout("Standalone_ComboActionGraphEditor_LayoutV0.3")
->AddArea
(
FTabManager::NewPrimaryArea()->SetOrientation(EOrientation::Orient_Vertical)
->Split
(
FTabManager::NewSplitter()->SetOrientation(EOrientation::Orient_Horizontal)->SetSizeCoefficient(0.9f)
->Split
(
FTabManager::NewStack()
->SetSizeCoefficient(3.f)
->AddTab(FAssetEditorTabs_ComboActionGraph::ViewportID, ETabState::OpenedTab)->SetHideTabWell(true)
)
->Split
(
FTabManager::NewSplitter()->SetOrientation(EOrientation::Orient_Vertical)
->Split
(
FTabManager::NewSplitter()->SetOrientation(EOrientation::Orient_Vertical)
->Split
(
FTabManager::NewStack()
->SetSizeCoefficient(0.9f)
->AddTab(FAssetEditorTabs_ComboActionGraph::ComboActionGraphPropertyID, ETabState::OpenedTab)->SetHideTabWell(true)
)
->Split
(
FTabManager::NewStack()
->SetSizeCoefficient(0.3f)
->AddTab(FAssetEditorTabs_ComboActionGraph::SearchToolbarID, ETabState::OpenedTab)
)
)
)
)
);
const bool bCreateDefaultStandaloneMenu = true;
const bool bCreateDefaultToolbar = true;
FAssetEditorToolkit::InitAssetEditor
(
Mode,
InitToolkitHost,
ComboActionGraphEditorAppName,
StandaloneDefaultLayout,
bCreateDefaultStandaloneMenu,
bCreateDefaultToolbar,
EditingGraph,
false
);
this->RegenerateMenusAndToolbars();
}
UComboActionGraphEditorSettings *FAssetEditor_ComboActionGraph::GetSettings() const
{
return this->ComboActionGraphEditorSettings;
}
void FAssetEditor_ComboActionGraph::RegisterTabSpawners(const TSharedRef<FTabManager> &InTabManager)
{
this->WorkspaceMenuCategory = InTabManager->AddLocalWorkspaceMenuCategory(LOCTEXT("WorkspaceMenu_ComboActionTreeEditor", "Combo Action Tree Editor"));
auto WorkspaceMenuCategoryRef = this->WorkspaceMenuCategory.ToSharedRef();
FAssetEditorToolkit::RegisterTabSpawners(InTabManager);
InTabManager->RegisterTabSpawner(FAssetEditorTabs_ComboActionGraph::ViewportID, FOnSpawnTab::CreateSP(this, &FAssetEditor_ComboActionGraph::SpawnTab_Viewport))
.SetDisplayName(LOCTEXT("GraphCanvasTab", "Viewport"))
.SetGroup(WorkspaceMenuCategoryRef)
.SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "GraphEditor.EventGraph_16x"));
InTabManager->RegisterTabSpawner(FAssetEditorTabs_ComboActionGraph::ComboActionGraphPropertyID, FOnSpawnTab::CreateSP(this, &FAssetEditor_ComboActionGraph::SpawnTab_Details))
.SetDisplayName(LOCTEXT("DetailsTab", "Property"))
.SetGroup(WorkspaceMenuCategoryRef)
.SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.Tabs.Details"));
//InTabManager->RegisterTabSpawner(FAssetEditorTabs_ComboActionGraph::SearchToolbarID, FOnSpawnTab::CreateSP(this, &FAssetEditor_ComboActionGraph::SpawnTab_Search))
// .SetDisplayName(LOCTEXT("SearchTab", "Search"))
// .SetGroup(WorkspaceMenuCategoryRef)
// .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.Tabs.FindResults"));
}
void FAssetEditor_ComboActionGraph::UnregisterTabSpawners(const TSharedRef<FTabManager>& InTabManager)
{
FAssetEditorToolkit::UnregisterTabSpawners(InTabManager);
InTabManager->UnregisterTabSpawner(FAssetEditorTabs_ComboActionGraph::ViewportID);
InTabManager->UnregisterTabSpawner(FAssetEditorTabs_ComboActionGraph::ComboActionGraphPropertyID);
InTabManager->UnregisterTabSpawner(FAssetEditorTabs_ComboActionGraph::SearchToolbarID);
}
bool FAssetEditor_ComboActionGraph::CloseWindow(EAssetEditorCloseReason InCloseReason)
{
const bool bSatisfied = FAssetEditorToolkit::CloseWindow(InCloseReason);
if (this->EditingGraph)
{
if (this->EditingGraph->EdGraph)
{
UEdComboActionGraph *EdGraph = Cast<UEdComboActionGraph>(this->EditingGraph->EdGraph);
if (EdGraph->GetComboActionEditorPtr().HasSameObject(this)) EdGraph->ResetDialogueEditorPtr();
}
}
return bSatisfied;
}
FName FAssetEditor_ComboActionGraph::GetToolkitFName() const
{
return FName("FComboActionGraphEditor");
}
FText FAssetEditor_ComboActionGraph::GetBaseToolkitName() const
{
return LOCTEXT("ComboActionGraphEditorAppLabel", "Combo Action Graph Editor");
}
FText FAssetEditor_ComboActionGraph::GetToolkitName() const
{
const bool bDirtyState = this->EditingGraph->GetOutermost()->IsDirty();
FFormatNamedArguments Args;
Args.Add(TEXT("ComboActionGraphName"), FText::FromString(this->EditingGraph->GetName()));
Args.Add(TEXT("DirtyState"), bDirtyState ? FText::FromString(TEXT("*")) : FText::GetEmpty());
return FText::Format(LOCTEXT("ComboActionGraphEditorToolkitName", "{ComboActionGraphName}{DirtyState}"), Args);
}
FText FAssetEditor_ComboActionGraph::GetToolkitToolTipText() const
{
return FAssetEditorToolkit::GetToolTipTextForObject(EditingGraph);
}
FLinearColor FAssetEditor_ComboActionGraph::GetWorldCentricTabColorScale() const
{
return FLinearColor::Gray;
}
FString FAssetEditor_ComboActionGraph::GetWorldCentricTabPrefix() const
{
return TEXT("ComboActionGraphEditor");
}
FString FAssetEditor_ComboActionGraph::GetDocumentationLink() const
{
return TEXT("There should be a link to the documentation here someday.");
}
void FAssetEditor_ComboActionGraph::SaveAsset_Execute()
{
if (this->EditingGraph != nullptr)
{
this->RebuildComboActionGraph();
}
FAssetEditorToolkit::SaveAsset_Execute();
}
void FAssetEditor_ComboActionGraph::UpdateToolbar()
{
// TODO
}
void FAssetEditor_ComboActionGraph::RegisterToolbarTab(const TSharedRef<FTabManager>& InTabManager)
{
FAssetEditorToolkit::RegisterTabSpawners(InTabManager);
}
void FAssetEditor_ComboActionGraph::AddReferencedObjects(FReferenceCollector& Collector)
{
Collector.AddReferencedObject(this->EditingGraph);
Collector.AddReferencedObject(this->EditingGraph->EdGraph);
}
FString FAssetEditor_ComboActionGraph::GetReferencerName() const
{
return TEXT("FAssetEditor_ComboActionGraph");
}
void FAssetEditor_ComboActionGraph::SetDialogueBeingEdited(UComboActionGraph *NewDialogue)
{
if (NewDialogue == nullptr) return;
if (NewDialogue == this->EditingGraph) return;
UComboActionGraph *Previous = this->EditingGraph;
this->EditingGraph = NewDialogue;
this->RemoveEditingObject(Previous);
this->AddEditingObject(NewDialogue);
}
void FAssetEditor_ComboActionGraph::CreateInternalWidgets()
{
this->ViewportWidget = this->CreateViewportWidget();
FDetailsViewArgs Args; //( false, false, true, FDetailsViewArgs::HideNameArea, false );
Args.bUpdatesFromSelection = false;
Args.bLockable = false;
Args.bAllowSearch = true;
Args.NameAreaSettings = FDetailsViewArgs::HideNameArea;
Args.bHideSelectionTip = false;
Args.bShowObjectLabel = false;
FPropertyEditorModule &PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
this->PropertyWidget = PropertyModule.CreateDetailView(Args);
this->PropertyWidget->SetObject(EditingGraph);
this->PropertyWidget->OnFinishedChangingProperties().AddSP(this, &FAssetEditor_ComboActionGraph::OnFinishedChangingProperties);
//this->FindResultsView = SNew(SComboActionSearch, SharedThis(this));
}
TSharedRef<SGraphEditor> FAssetEditor_ComboActionGraph::CreateViewportWidget()
{
const FComboInputEditorModule &Module = FModuleManager::GetModuleChecked<FComboInputEditorModule>("ComboInputEditor");
FGraphAppearanceInfo AppearanceInfo;
AppearanceInfo.CornerText = LOCTEXT("AppearanceCornerText_ComboActionGraph", "Combo Action Tree");
AppearanceInfo.CornerImage = Module.Get().GetComboInputEditorStyleSet()->GetBrush(TEXT("MDSStyleSet.Graph.CornerImage"));
AppearanceInfo.InstructionText = LOCTEXT("InstructionText_ComboActionGraph", "Place action nodes by right clicking.");
this->CreateCommandList();
SGraphEditor::FGraphEditorEvents InEvents;
InEvents.OnSelectionChanged = SGraphEditor::FOnSelectionChanged::CreateSP(this, &FAssetEditor_ComboActionGraph::OnSelectedNodesChanged);
InEvents.OnNodeDoubleClicked = FSingleNodeEvent::CreateSP(this, &FAssetEditor_ComboActionGraph::OnNodeDoubleClicked);
return SNew(SGraphEditor)
.AdditionalCommands(GraphEditorCommands)
.IsEditable(true)
.Appearance(AppearanceInfo)
.GraphToEdit(EditingGraph->EdGraph)
.GraphEvents(InEvents)
.AutoExpandActionMenu(true)
.ShowGraphStateOverlay(false);
}
void FAssetEditor_ComboActionGraph::BindCommands()
{
this->ToolkitCommands->MapAction
(
FComboActionGraphEditorCommands::Get().AutoArrange,
FExecuteAction::CreateSP(this, &FAssetEditor_ComboActionGraph::AutoArrange),
FCanExecuteAction::CreateSP(this, &FAssetEditor_ComboActionGraph::CanAutoArrange)
);
this->ToolkitCommands->MapAction
(
FComboActionGraphEditorCommands::Get().ValidateGraph,
FExecuteAction::CreateSP(this, &FAssetEditor_ComboActionGraph::ValidateGraph),
FCanExecuteAction::CreateSP(this, &FAssetEditor_ComboActionGraph::CanValidateGraph)
);
}
void FAssetEditor_ComboActionGraph::CreateEdGraph()
{
if (this->EditingGraph->EdGraph == nullptr)
{
this->EditingGraph->EdGraph = CastChecked<UEdComboActionGraph>(FBlueprintEditorUtils::CreateNewGraph(EditingGraph, NAME_None, UEdComboActionGraph::StaticClass(), UComboActionGraphSchema::StaticClass()));
this->EditingGraph->EdGraph->bAllowDeletion = false;
// Give the schema a chance to fill out any required nodes (like the results node)
const UEdGraphSchema *Schema = this->EditingGraph->EdGraph->GetSchema();
Schema->CreateDefaultNodesForGraph(*this->EditingGraph->EdGraph);
UEdComboActionGraph *ComboActionGraph = Cast<UEdComboActionGraph>(this->EditingGraph->EdGraph);
UEdComboActionGraphNode *NewNode = ComboActionGraph->CreateIntermediateNode<UEdComboActionGraphNode>();
NewNode->SetComboActionGraphNode(this->EditingGraph->StartNode);
NewNode->CreateNewGuid();
NewNode->PostPlacedNewNode();
NewNode->AllocateDefaultPins();
NewNode->AutowireNewNode(nullptr);
NewNode->NodePosX = 0;
NewNode->NodePosY = 0;
NewNode->ComboActionGraphNode->SetFlags(EObjectFlags::RF_Transactional);
NewNode->SetFlags(EObjectFlags::RF_Transactional);
ComboActionGraph->RebuildComboActionGraph();
}
if (UEdComboActionGraph *EdComboActionGraph = Cast<UEdComboActionGraph>(this->EditingGraph->EdGraph))
{
EdComboActionGraph->SetDialogueEditorPtr(SharedThis(this));
EdComboActionGraph->RebuildComboActionGraph();
}
}
void FAssetEditor_ComboActionGraph::CreateCommandList()
{
if (this->GraphEditorCommands.IsValid())
{
return;
}
this->GraphEditorCommands = MakeShareable(new FUICommandList);
this->GraphEditorCommands->MapAction(FComboActionGraphEditorCommands::Get().AutoArrange,
FExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::AutoArrange),
FCanExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CanAutoArrange));
this->GraphEditorCommands->MapAction(FComboActionGraphEditorCommands::Get().ValidateGraph,
FExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::ValidateGraph),
FCanExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CanValidateGraph));
this->GraphEditorCommands->MapAction(FGenericCommands::Get().SelectAll,
FExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::SelectAllNodes),
FCanExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CanSelectAllNodes)
);
this->GraphEditorCommands->MapAction(FGenericCommands::Get().Delete,
FExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::DeleteSelectedNodes),
FCanExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CanDeleteNodes)
);
this->GraphEditorCommands->MapAction(FGenericCommands::Get().Copy,
FExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CopySelectedNodes),
FCanExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CanCopyNodes)
);
this->GraphEditorCommands->MapAction(FGenericCommands::Get().Cut,
FExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CutSelectedNodes),
FCanExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CanCutNodes)
);
this->GraphEditorCommands->MapAction(FGenericCommands::Get().Paste,
FExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::PasteNodes),
FCanExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CanPasteNodes)
);
this->GraphEditorCommands->MapAction(FGenericCommands::Get().Duplicate,
FExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::DuplicateNodes),
FCanExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CanDuplicateNodes)
);
this->GraphEditorCommands->MapAction(FGenericCommands::Get().Rename,
FExecuteAction::CreateSP(this, &FAssetEditor_ComboActionGraph::OnRenameNode),
FCanExecuteAction::CreateSP(this, &FAssetEditor_ComboActionGraph::CanRenameNodes)
);
}
TSharedPtr<SGraphEditor> FAssetEditor_ComboActionGraph::GetCurrentGraphEditor() const
{
return this->ViewportWidget;
}
FGraphPanelSelectionSet FAssetEditor_ComboActionGraph::GetSelectedNodes() const
{
FGraphPanelSelectionSet CurrentSelection;
TSharedPtr<SGraphEditor> FocusedGraphEd = this->GetCurrentGraphEditor();
if (FocusedGraphEd.IsValid())
{
CurrentSelection = FocusedGraphEd->GetSelectedNodes();
}
return CurrentSelection;
}
void FAssetEditor_ComboActionGraph::RebuildComboActionGraph()
{
if (this->EditingGraph == nullptr)
{
UE_LOG(LogAssetEditorComboActionGraph, Warning, TEXT("[RebuildComboActionGraph] EditingGraph is nullptr"));
return;
}
UEdComboActionGraph *EdGraph = Cast<UEdComboActionGraph>(this->EditingGraph->EdGraph);
check(EdGraph != nullptr);
EdGraph->RebuildComboActionGraph();
}
void FAssetEditor_ComboActionGraph::SelectAllNodes()
{
TSharedPtr<SGraphEditor> CurrentGraphEditor = this->GetCurrentGraphEditor();
if (CurrentGraphEditor.IsValid())
{
CurrentGraphEditor->SelectAllNodes();
}
}
bool FAssetEditor_ComboActionGraph::CanSelectAllNodes()
{
return true;
}
void FAssetEditor_ComboActionGraph::DeleteSelectedNodes()
{
TSharedPtr<SGraphEditor> CurrentGraphEditor = this->GetCurrentGraphEditor();
if (!CurrentGraphEditor.IsValid())
{
return;
}
const FScopedTransaction Transaction(FGenericCommands::Get().Delete->GetDescription());
CurrentGraphEditor->GetCurrentGraph()->Modify();
const FGraphPanelSelectionSet SelectedNodes = CurrentGraphEditor->GetSelectedNodes();
CurrentGraphEditor->ClearSelectionSet();
for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt)
{
UEdGraphNode* EdNode = Cast<UEdGraphNode>(*NodeIt);
if (EdNode == nullptr || !EdNode->CanUserDeleteNode())
continue;;
if (UEdComboActionGraphNode *EdNode_Node = Cast<UEdComboActionGraphNode>(EdNode))
{
EdNode_Node->Modify();
const UEdGraphSchema *Schema = EdNode_Node->GetSchema();
if (Schema != nullptr)
{
Schema->BreakNodeLinks(*EdNode_Node);
}
EdNode_Node->DestroyNode();
}
else
{
EdNode->Modify();
EdNode->DestroyNode();
}
}
// Update UI
CurrentGraphEditor->NotifyGraphChanged();
UEdGraph *EdGraph = CurrentGraphEditor->GetCurrentGraph();
UObject *GraphOwner = EdGraph->GetOuter();
if (GraphOwner)
{
GraphOwner->PostEditChange();
GraphOwner->MarkPackageDirty();
}
this->RebuildComboActionGraph();
}
bool FAssetEditor_ComboActionGraph::CanDeleteNodes()
{
// If any of the nodes can be deleted then we should allow deleting
const FGraphPanelSelectionSet SelectedNodes = this->GetSelectedNodes();
for (FGraphPanelSelectionSet::TConstIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter)
{
UEdGraphNode* Node = Cast<UEdGraphNode>(*SelectedIter);
if (Node != nullptr && Node->CanUserDeleteNode())
{
return true;
}
}
return false;
}
void FAssetEditor_ComboActionGraph::DeleteSelectedDuplicatableNodes()
{
TSharedPtr<SGraphEditor> CurrentGraphEditor = this->GetCurrentGraphEditor();
if (!CurrentGraphEditor.IsValid())
{
return;
}
const FGraphPanelSelectionSet OldSelectedNodes = CurrentGraphEditor->GetSelectedNodes();
CurrentGraphEditor->ClearSelectionSet();
for (FGraphPanelSelectionSet::TConstIterator SelectedIter(OldSelectedNodes); SelectedIter; ++SelectedIter)
{
UEdGraphNode *Node = Cast<UEdGraphNode>(*SelectedIter);
if (Node && Node->CanDuplicateNode())
{
CurrentGraphEditor->SetNodeSelection(Node, true);
}
}
// Delete the duplicatable nodes
this->DeleteSelectedNodes();
CurrentGraphEditor->ClearSelectionSet();
for (FGraphPanelSelectionSet::TConstIterator SelectedIter(OldSelectedNodes); SelectedIter; ++SelectedIter)
{
if (UEdGraphNode* Node = Cast<UEdGraphNode>(*SelectedIter))
{
CurrentGraphEditor->SetNodeSelection(Node, true);
}
}
}
void FAssetEditor_ComboActionGraph::CutSelectedNodes()
{
this->CopySelectedNodes();
this->DeleteSelectedDuplicatableNodes();
}
bool FAssetEditor_ComboActionGraph::CanCutNodes()
{
return this->CanCopyNodes() && this->CanDeleteNodes();
}
void FAssetEditor_ComboActionGraph::CopySelectedNodes()
{
// Export the selected nodes and place the text on the clipboard
FGraphPanelSelectionSet SelectedNodes = this->GetSelectedNodes();
FString ExportedText;
for (FGraphPanelSelectionSet::TIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter)
{
UEdGraphNode *Node = Cast<UEdGraphNode>(*SelectedIter);
if (Node == nullptr)
{
SelectedIter.RemoveCurrent();
continue;
}
if (UEdComboActionGraphEdge *EdNode_Edge = Cast<UEdComboActionGraphEdge>(*SelectedIter))
{
UEdComboActionGraphNode *StartNode = EdNode_Edge->GetStartNode();
UEdComboActionGraphNode *EndNode = EdNode_Edge->GetEndNode();
if (!SelectedNodes.Contains(StartNode) || !SelectedNodes.Contains(EndNode))
{
SelectedIter.RemoveCurrent();
continue;
}
}
Node->PrepareForCopying();
}
FEdGraphUtilities::ExportNodesToText(SelectedNodes, ExportedText);
FPlatformApplicationMisc::ClipboardCopy(*ExportedText);
}
bool FAssetEditor_ComboActionGraph::CanCopyNodes()
{
// If any of the nodes can be duplicated then we should allow copying
const FGraphPanelSelectionSet SelectedNodes = this->GetSelectedNodes();
for (FGraphPanelSelectionSet::TConstIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter)
{
UEdGraphNode *Node = Cast<UEdGraphNode>(*SelectedIter);
if (Node && Node->CanDuplicateNode() == false)
{
return false;
}
}
return true;
}
void FAssetEditor_ComboActionGraph::PasteNodes()
{
TSharedPtr<SGraphEditor> CurrentGraphEditor = this->GetCurrentGraphEditor();
if (CurrentGraphEditor.IsValid())
{
PasteNodesHere(CurrentGraphEditor->GetPasteLocation());
}
}
void FAssetEditor_ComboActionGraph::PasteNodesHere(const FVector2D& Location)
{
// Find the graph editor with focus
TSharedPtr<SGraphEditor> CurrentGraphEditor = this->GetCurrentGraphEditor();
if (!CurrentGraphEditor.IsValid())
{
return;
}
// Select the newly pasted stuff
UEdGraph *EdGraph = CurrentGraphEditor->GetCurrentGraph();
{
const FScopedTransaction Transaction(FGenericCommands::Get().Paste->GetDescription());
EdGraph->Modify();
// Clear the selection set (newly pasted stuff will be selected)
CurrentGraphEditor->ClearSelectionSet();
// Grab the text to paste from the clipboard.
FString TextToImport;
FPlatformApplicationMisc::ClipboardPaste(TextToImport);
// Import the nodes
TSet<UEdGraphNode*> PastedNodes;
FEdGraphUtilities::ImportNodesFromText(EdGraph, TextToImport, PastedNodes);
//Average position of nodes so we can move them while still maintaining relative distances to each other
FVector2D AvgNodePosition(0.0f, 0.0f);
for (TSet<UEdGraphNode*>::TIterator It(PastedNodes); It; ++It)
{
UEdGraphNode* Node = *It;
AvgNodePosition.X += Node->NodePosX;
AvgNodePosition.Y += Node->NodePosY;
}
float InvNumNodes = 1.0f / float(PastedNodes.Num());
AvgNodePosition.X *= InvNumNodes;
AvgNodePosition.Y *= InvNumNodes;
// 0 is always Start Node!
int32 SharedIndex = EditingGraph->GetAllNodes().Num();
for (TSet<UEdGraphNode*>::TIterator It(PastedNodes); It; ++It)
{
UEdGraphNode* Node = *It;
CurrentGraphEditor->SetNodeSelection(Node, true);
Node->NodePosX = (Node->NodePosX - AvgNodePosition.X) + Location.X;
Node->NodePosY = (Node->NodePosY - AvgNodePosition.Y) + Location.Y;
Node->SnapToGrid(16);
// Give new node a different Guid from the old one
Node->CreateNewGuid();
if (UEdComboActionGraphNode *MounteaNode = Cast<UEdComboActionGraphNode>(Node))
{
if (MounteaNode->ComboActionGraphNode)
{
MounteaNode->ComboActionGraphNode->OnPasted();
//MounteaNode->SetDialogueNodeIndex(SharedIndex);
}
SharedIndex++;
}
}
}
// Update UI
CurrentGraphEditor->NotifyGraphChanged();
UObject *GraphOwner = EdGraph->GetOuter();
if (GraphOwner)
{
GraphOwner->PostEditChange();
GraphOwner->MarkPackageDirty();
}
this->RebuildComboActionGraph();
}
bool FAssetEditor_ComboActionGraph::CanPasteNodes()
{
const FGraphPanelSelectionSet SelectedNodes = this->GetSelectedNodes();
for (FGraphPanelSelectionSet::TConstIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter)
{
const UEdComboActionGraphNode *Node = Cast<UEdComboActionGraphNode>(*SelectedIter);
if (Node && Node->CanUserPasteNodes() == false)
{
return false;
}
}
TSharedPtr<SGraphEditor> CurrentGraphEditor = this->GetCurrentGraphEditor();
if (!CurrentGraphEditor.IsValid())
{
return false;
}
FString ClipboardContent;
FPlatformApplicationMisc::ClipboardPaste(ClipboardContent);
return FEdGraphUtilities::CanImportNodesFromText(CurrentGraphEditor->GetCurrentGraph(), ClipboardContent);
}
void FAssetEditor_ComboActionGraph::DuplicateNodes()
{
this->CopySelectedNodes();
this->PasteNodes();
}
bool FAssetEditor_ComboActionGraph::CanDuplicateNodes()
{
return this->CanCopyNodes();
}
void FAssetEditor_ComboActionGraph::AutoArrange()
{
UEdComboActionGraph *EdGraph = Cast<UEdComboActionGraph>(this->EditingGraph->EdGraph);
check(EdGraph != nullptr);
const FScopedTransaction Transaction(LOCTEXT("ComboActionGraphEditorAutoArrange", "Combo Action Graph Editor: Auto Arrange all Nodes"));
EdGraph->Modify(true);
UComboActionGraphLayoutStrategy *LayoutStrategy = nullptr;
switch (this->ComboActionGraphEditorSettings->GetAutoLayoutStrategy())
{
case EComboActionAutoLayoutStrategyType::Tree:
LayoutStrategy = NewObject<UComboActionGraphLayoutStrategy>(EdGraph, UComboActionTreeSolveLayoutStrategy::StaticClass());
break;
case EComboActionAutoLayoutStrategyType::ForceDirected:
LayoutStrategy = NewObject<UComboActionGraphLayoutStrategy>(EdGraph, UComboActionForceDirectedSolveLayoutStrategy::StaticClass());
break;
default:
break;
}
if (LayoutStrategy != nullptr)
{
LayoutStrategy->Layout(EdGraph);
LayoutStrategy->ConditionalBeginDestroy();
}
else
{
UE_LOG(LogAssetEditorComboActionGraph, Error, TEXT("[AutoArrange] LayoutStrategy is null."));
}
}
bool FAssetEditor_ComboActionGraph::CanAutoArrange() const
{
return this->EditingGraph != nullptr && Cast<UEdComboActionGraph>(this->EditingGraph->EdGraph) != nullptr;
}
void FAssetEditor_ComboActionGraph::ValidateGraph()
{
if (this->ValidationWindow.IsValid())
{
this->ValidationWindow->RequestDestroyWindow();
}
UEdComboActionGraph *EdGraph = Cast<UEdComboActionGraph>(this->EditingGraph->EdGraph);
check(EdGraph != nullptr);
const FScopedTransaction Transaction(LOCTEXT("ComboActionGraphEditorValidateGraph", "Combo Action Graph Editor: Validate Graph."));
UComboActionGraph *ComboActionGraph = EdGraph->GetComboActionGraph();
check(ComboActionGraph != nullptr);
this->RebuildComboActionGraph();
//TArray<FText> ValidationMessages;
//if (ComboActionGraph->ValidateGraph(ValidationMessages, true) == false)
//{
// ValidationWindow = MDSPopup_GraphValidation::Open(ValidationMessages);
//}
//else
//{
// ValidationMessages.Empty();
// ValidationWindow = MDSPopup_GraphValidation::Open(ValidationMessages);
//}
}
bool FAssetEditor_ComboActionGraph::CanValidateGraph() const
{
return true;
}
void FAssetEditor_ComboActionGraph::OnRenameNode()
{
TSharedPtr<SGraphEditor> CurrentGraphEditor = this->GetCurrentGraphEditor();
if (CurrentGraphEditor.IsValid())
{
const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes();
for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt)
{
UEdGraphNode *SelectedNode = Cast<UEdGraphNode>(*NodeIt);
if (SelectedNode != nullptr && SelectedNode->bCanRenameNode)
{
CurrentGraphEditor->IsNodeTitleVisible(SelectedNode, true);
break;
}
}
}
}
bool FAssetEditor_ComboActionGraph::CanRenameNodes() const
{
check(this->GetSettings() != nullptr);
return this->GetSettings()->AllowRenameNodes() == true && this->GetSelectedNodes().Num() == 1;
}
void FAssetEditor_ComboActionGraph::OnSelectedNodesChanged(const TSet<UObject*> &NewSelection)
{
TArray<UObject*> Selection;
for (UObject* SelectionEntry : NewSelection)
{
Selection.Add(SelectionEntry);
}
if (Selection.Num() == 1)
{
// When just one node is selected, add it to PropertyWidget
this->PropertyWidget->SetObjects(Selection);
this->PropertyWidget->ShowAllAdvancedProperties();
//UComboActionEditorBFC::TriggerPreviewRefresh(Selection);
}
else
{
this->PropertyWidget->SetObject(this->EditingGraph);
}
this->RebuildComboActionGraph();
}
void FAssetEditor_ComboActionGraph::OnNodeDoubleClicked(UEdGraphNode *Node)
{
this->GraphEditorCommands->TryExecuteAction(FGenericCommands::Get().Rename.ToSharedRef());
}
void FAssetEditor_ComboActionGraph::OnFinishedChangingProperties(const FPropertyChangedEvent& PropertyChangedEvent)
{
if (this->EditingGraph == nullptr)
return;
this->EditingGraph->EdGraph->GetSchema()->ForceVisualizationCacheClear();
this->RebuildComboActionGraph();
}
TSharedRef<SDockTab> FAssetEditor_ComboActionGraph::SpawnTab_Viewport(const FSpawnTabArgs &Args)
{
check(Args.GetTabId() == FAssetEditorTabs_ComboActionGraph::ViewportID);
TSharedRef<SDockTab> SpawnedTab = SNew(SDockTab)
.Label(LOCTEXT("ViewportTab_Title", "Viewport"));
if (this->ViewportWidget.IsValid())
{
SpawnedTab->SetContent(this->ViewportWidget.ToSharedRef());
}
return SpawnedTab;
}
TSharedRef<SDockTab> FAssetEditor_ComboActionGraph::SpawnTab_Details(const FSpawnTabArgs& Args)
{
check(Args.GetTabId() == FAssetEditorTabs_ComboActionGraph::ComboActionGraphPropertyID);
auto DockTab = SNew(SDockTab)
.Label(LOCTEXT("Details_Title", "Property"))
[
PropertyWidget.ToSharedRef()
];
DockTab->SetTabIcon(FAppStyle::GetBrush("LevelEditor.Tabs.Details"));
return DockTab;
}
#undef LOCTEXT_NAMESPACE