ComboActionGraph classes can now be added in the content browser. No asset editor yet though.

This commit is contained in:
Jamie Greunbaum
2023-09-29 01:33:09 -04:00
parent 8b6cfafc45
commit de9865758a
14 changed files with 1492 additions and 62 deletions
@@ -0,0 +1,978 @@
// Copyright Dominik Pavlicek 2023. 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 "EditorCommands/FComboActionGraphEditorCommands.h"
//#include "AssetEditor/FAssetEditorToolbarComboActionGraph.h"
#include "Ed/EdComboActionGraph.h"
#include "Ed/EdComboActionGraphEdge.h"
#include "Ed/EdComboActionGraphNode.h"
#include "Settings/FComboActionGraphEditorStyle.h"
#include "ComboActionGraphSchema.h"
//#include "Layout/AssetEditorTabs.h"
//#include "Helpers/ComboActionGraphHelpers.h"
//#include "Helpers/ComboActionSystemEditorBFC.h"
//#include "Layout/ForceDirectedSolveLayoutStrategy.h"
//#include "Layout/ComboActionGraphLayoutStrategy.h"
//#include "Layout/TreeSolveLayoutStrategy.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->AddMounteaDialogueGraphToolbar(ToolbarExtender);
// Layout
//const TSharedRef<FTabManager::FLayout> StandaloneDefaultLayout = FTabManager::NewLayout("Standalone_MounteaDialogueGraphEditor_LayoutV0.3")
// ->AddArea
// (
// FTabManager::NewPrimaryArea()->SetOrientation(Orient_Vertical)
// ->Split
// (
// FTabManager::NewSplitter()->SetOrientation(Orient_Horizontal)->SetSizeCoefficient(0.9f)
// ->Split
// (
// FTabManager::NewStack()
// ->SetSizeCoefficient(3.f)
// ->AddTab(FAssetEditorTabs_MounteaDialogueGraph::ViewportID, ETabState::OpenedTab)->SetHideTabWell(true)
// )
// ->Split
// (
// FTabManager::NewSplitter()->SetOrientation(Orient_Vertical)
// ->Split
// (
// FTabManager::NewSplitter()->SetOrientation(Orient_Vertical)
// ->Split
// (
// FTabManager::NewStack()
// ->SetSizeCoefficient(0.9f)
// ->AddTab(FAssetEditorTabs_MounteaDialogueGraph::MounteaDialogueGraphPropertyID, ETabState::OpenedTab)->SetHideTabWell(true)
// )
// ->Split
// (
// FTabManager::NewStack()
// ->SetSizeCoefficient(0.3f)
// ->AddTab(FAssetEditorTabs_MounteaDialogueGraph::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)
{
//WorkspaceMenuCategory = InTabManager->AddLocalWorkspaceMenuCategory(LOCTEXT("WorkspaceMenu_MounteaDialogueTreeEditor", "Mountea Dialogue Tree Editor"));
//auto WorkspaceMenuCategoryRef = 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::MounteaDialogueGraphPropertyID, 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()
{
const bool bSatisfied = FAssetEditorToolkit::CloseWindow();
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().ComboActionGraphEditorStyleSet->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)
//);
//
//this->ToolkitCommands->MapAction
//(
// FComboActionGraphEditorCommands::Get().FindInDialogue,
// FExecuteAction::CreateLambda([this] { this->SummonSearchUI(); })
//);
}
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)
);
//this->ToolkitCommands->MapAction
//(
// FComboActionGraphEditorCommands::Get().FindInDialogue,
// FExecuteAction::CreateLambda([this] { SummonSearchUI(); })
//);
}
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::SummonSearchUI(FString NewSearch, bool bSelectFirstResult)
{
//TSharedPtr<SMounteaDialogueSearch> FindResultsToUse;
//FindResultsToUse = FindResultsView;
//FMounteaDialogueSearchHelpers::InvokeTab(TabManager, FAssetEditorTabs_MounteaDialogueGraph::SearchToolbarID);
//if (FindResultsToUse.IsValid())
//{
// FMounteaDialogueSearchFilter Filter;
// Filter.SearchString = NewSearch;
// FindResultsToUse->FocusForUse(Filter, bSelectFirstResult);
//}
}
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, UTreeSolveLayoutStrategy::StaticClass());
// break;
// case EComboActionAutoLayoutStrategyType::ForceDirected:
// LayoutStrategy = NewObject<UComboActionGraphLayoutStrategy>(EdGraph, UForceDirectedSolveLayoutStrategy::StaticClass());
// break;
// default:
// break;
//}
//if (LayoutStrategy != nullptr)
//{
// LayoutStrategy->Layout(EdGraph);
// LayoutStrategy->ConditionalBeginDestroy();
//}
//else
//{
// LOG_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;
}
TSharedRef<SDockTab> FAssetEditor_ComboActionGraph::SpawnTab_Search(const FSpawnTabArgs& Args)
{
//check(Args.GetTabId() == FAssetEditorTabs_MounteaDialogueGraph::SearchToolbarID);
//auto DockTab = SNew(SDockTab)
// .Label(LOCTEXT("Search_Title", "Search"))
// [
// this->FindResultsView.ToSharedRef()
// ];
//DockTab->SetTabIcon(FAppStyle::GetBrush("Kismet.Tabs.FindResults"));
//return DockTab;
return SNew(SDockTab);
}
#undef LOCTEXT_NAMESPACE
@@ -0,0 +1,167 @@
// Copyright Dominik Pavlicek 2023. All Rights Reserved.
#pragma once
//#include "Search/SComboActionSearch.h"
DECLARE_LOG_CATEGORY_EXTERN(LogAssetEditorComboActionGraph, Log, All);
class COMBOINPUTEDITOR_API FAssetEditor_ComboActionGraph : public FAssetEditorToolkit, public FNotifyHook, public FGCObject
{
public:
FAssetEditor_ComboActionGraph();
virtual ~FAssetEditor_ComboActionGraph() override;
void OnPackageSaved(const FString &String, UPackage *Package, FObjectPostSaveContext ObjectPostSaveContext);
void InitComboActionGraphAssetEditor(const EToolkitMode::Type Mode, const TSharedPtr<IToolkitHost> &InitToolkitHost, class UComboActionGraph *Graph);
class UComboActionGraphEditorSettings *GetSettings() const;
#pragma region ToolkitInterface
public:
// IToolkit interface
virtual void RegisterTabSpawners(const TSharedRef<FTabManager> &InTabManager) override;
virtual void UnregisterTabSpawners(const TSharedRef<FTabManager> &InTabManager) override;
virtual bool CloseWindow() override;
// End of IToolkit interface
#pragma endregion
#pragma region AssetEditorToolkit
virtual FName GetToolkitFName() const override;
virtual FText GetBaseToolkitName() const override;
virtual FText GetToolkitName() const override;
virtual FText GetToolkitToolTipText() const override;
virtual FLinearColor GetWorldCentricTabColorScale() const override;
virtual FString GetWorldCentricTabPrefix() const override;
virtual FString GetDocumentationLink() const override;
virtual void SaveAsset_Execute() override;
#pragma endregion
#pragma region Toolbar
void UpdateToolbar();
//TSharedPtr<FAssetEditorToolbarComboActionGraph> GetToolbarBuilder() { return ToolbarBuilder; }
void RegisterToolbarTab(const TSharedRef<class FTabManager> &InTabManager);
#pragma endregion
#pragma region SerializableObjectInterface
virtual void AddReferencedObjects(FReferenceCollector &Collector) override;
#pragma endregion
#pragma region FGCObject
virtual FString GetReferencerName() const override;
#pragma endregion
// Gets/Sets the dialogue being edited
class UComboActionGraph *GetEditingGraphSafe()
{
check(EditingGraph);
return EditingGraph;
}
void SetDialogueBeingEdited(class UComboActionGraph *NewDialogue);
virtual void JumpToNode(const class UEdGraphNode *JumpToMe, bool bRequestRename = false, bool bSelectNode = true)
{
if (this->ViewportWidget.IsValid())
{
this->ViewportWidget->JumpToNode(JumpToMe, bRequestRename, bSelectNode);
}
}
private:
void CreateInternalWidgets();
TSharedRef<SGraphEditor> CreateViewportWidget();
void BindCommands();
void CreateEdGraph();
void CreateCommandList();
TSharedPtr<SGraphEditor> GetCurrentGraphEditor() const;
FGraphPanelSelectionSet GetSelectedNodes() const;
void RebuildComboActionGraph();
void SummonSearchUI(FString NewSearch = FString(), bool bSelectFirstResult = false);
#pragma region GraphEditorCommands
void SelectAllNodes();
bool CanSelectAllNodes();
void DeleteSelectedNodes();
bool CanDeleteNodes();
void DeleteSelectedDuplicatableNodes();
void CutSelectedNodes();
bool CanCutNodes();
void CopySelectedNodes();
bool CanCopyNodes();
void PasteNodes();
void PasteNodesHere(const FVector2D &Location);
bool CanPasteNodes();
void DuplicateNodes();
bool CanDuplicateNodes();
void AutoArrange();
bool CanAutoArrange() const;
void ValidateGraph();
bool CanValidateGraph() const;
void OnRenameNode();
bool CanRenameNodes() const;
#pragma endregion
#pragma region GraphEditorEvents
void OnSelectedNodesChanged(const TSet<class UObject*> &NewSelection);
void OnNodeDoubleClicked(UEdGraphNode *Node);
void OnFinishedChangingProperties(const FPropertyChangedEvent &PropertyChangedEvent);
//void OnPackageSaved(const FString &PackageFileName, UObject *Outer);
#pragma endregion
#pragma region Variables
private:
TSharedRef<SDockTab> SpawnTab_Viewport(const FSpawnTabArgs &Args);
TSharedRef<SDockTab> SpawnTab_Details(const FSpawnTabArgs &Args);
TSharedRef<SDockTab> SpawnTab_Search(const FSpawnTabArgs &Args);
class UComboActionGraphEditorSettings *ComboActionGraphEditorSettings;
class UComboActionGraph *EditingGraph;
//Toolbar
//TSharedPtr<FAssetEditorToolbarComboActionGraph> ToolbarBuilder;
/** Handle to the registered OnPackageSave delegate */
FDelegateHandle OnPackageSavedDelegateHandle;
TSharedPtr<SGraphEditor> ViewportWidget;
TSharedPtr<class IDetailsView> PropertyWidget;
//TSharedPtr<SComboActionSearch> FindResultsView;
/** The command list for this editor */
TSharedPtr<FUICommandList> GraphEditorCommands;
TSharedPtr<SWindow> ValidationWindow;
#pragma endregion
};
@@ -3,6 +3,7 @@
#include "Ed/EdComboActionGraph.h"
#include "ComboActionGraph.h"
#include "Ed/AssetEditor_ComboActionGraph.h"
#include "Ed/EdComboActionGraphEdge.h"
#include "Ed/EdComboActionGraphNode.h"
#include "Nodes/ComboActionGraphEdge.h"
@@ -20,18 +21,18 @@ void UEdComboActionGraph::RebuildComboActionGraph()
UComboActionGraph *Graph = GetComboActionGraph();
Clear();
this->Clear();
for (int i = 0; i < Nodes.Num(); ++i)
for (const TObjectPtr<UEdGraphNode> &Node : this->Nodes)
{
if (UEdComboActionGraphNode *EdNode = Cast<UEdComboActionGraphNode>(Nodes[i]))
if (UEdComboActionGraphNode *EdNode = Cast<UEdComboActionGraphNode>(Node))
{
if (EdNode->ComboActionGraphNode == nullptr)
continue;
UComboActionGraphNode *ComboActionGraphNode = EdNode->ComboActionGraphNode;
NodeMap.Add(ComboActionGraphNode, EdNode);
this->NodeMap.Add(ComboActionGraphNode, EdNode);
Graph->AllNodes.Add(ComboActionGraphNode);
@@ -73,7 +74,7 @@ void UEdComboActionGraph::RebuildComboActionGraph()
}
}
}
else if (UEdComboActionGraphEdge *EdgeNode = Cast<UEdComboActionGraphEdge>(Nodes[i]))
else if (UEdComboActionGraphEdge *EdgeNode = Cast<UEdComboActionGraphEdge>(Node))
{
UEdComboActionGraphNode *StartNode = EdgeNode->GetStartNode();
UEdComboActionGraphNode *EndNode = EdgeNode->GetEndNode();
@@ -81,11 +82,11 @@ void UEdComboActionGraph::RebuildComboActionGraph()
if (StartNode == nullptr || EndNode == nullptr || Edge == nullptr)
{
UE_LOG(LogEdComboActionGraph, Error, TEXT("[RebuildMounteaDialogueGraph] Add edge failed."));
UE_LOG(LogEdComboActionGraph, Error, TEXT("[RebuildComboActionGraph] Add edge failed."));
continue;
}
EdgeMap.Add(Edge, EdgeNode);
this->EdgeMap.Add(Edge, EdgeNode);
Edge->Graph = Graph;
Edge->Rename(nullptr, Graph, REN_DontCreateRedirectors | REN_DoNotDirty);
@@ -95,9 +96,8 @@ void UEdComboActionGraph::RebuildComboActionGraph()
}
}
for (int i = 0; i < Graph->AllNodes.Num(); ++i)
for (UComboActionGraphNode *Node : Graph->AllNodes)
{
UComboActionGraphNode *Node = Graph->AllNodes[i];
if (Node->ParentNodes.Num() == 0)
{
Graph->RootNodes.Add(Node);
@@ -111,8 +111,8 @@ void UEdComboActionGraph::RebuildComboActionGraph()
Graph->RootNodes.Sort([&](const UComboActionGraphNode &L, const UComboActionGraphNode &R)
{
UEdComboActionGraphNode* EdNode_LNode = NodeMap[&L];
UEdComboActionGraphNode* EdNode_RNode = NodeMap[&R];
UEdComboActionGraphNode *EdNode_LNode = this->NodeMap[&L];
UEdComboActionGraphNode *EdNode_RNode = this->NodeMap[&R];
return EdNode_LNode->NodePosX < EdNode_RNode->NodePosX;
});
}
@@ -128,9 +128,9 @@ bool UEdComboActionGraph::Modify(bool bAlwaysMarkDirty)
this->GetComboActionGraph()->Modify();
for (int32 i = 0; i < Nodes.Num(); ++i)
for (TObjectPtr<UEdGraphNode> &Node : this->Nodes)
{
Nodes[i]->Modify();
Node->Modify();
}
return Rtn;
@@ -138,20 +138,21 @@ bool UEdComboActionGraph::Modify(bool bAlwaysMarkDirty)
void UEdComboActionGraph::PostEditUndo()
{
NotifyGraphChanged();
this->NotifyGraphChanged();
Super::PostEditUndo();
}
//void UEdComboActionGraph::SetDialogueEditorPtr(TWeakPtr<FAssetEditor_MounteaDialogueGraph> NewPtr)
//{
// DialogueEditorPtr = NewPtr;
//}
void UEdComboActionGraph::SetDialogueEditorPtr(TWeakPtr<FAssetEditor_ComboActionGraph> NewPtr)
{
this->ComboActionEditorPtr = NewPtr;
}
//bool UEdComboActionGraph::JumpToNode(const UMounteaDialogueGraphNode* Node)
//{
// return FComboActionGraphEditorUtilities::OpenEditorAndJumpToGraphNode(DialogueEditorPtr, *NodeMap.Find(Node));
//}
bool UEdComboActionGraph::JumpToNode(const UComboActionGraphNode *Node)
{
//return FComboActionGraphEditorUtilities::OpenEditorAndJumpToGraphNode(this->ComboActionEditorPtr, *NodeMap.Find(Node));
return false;
}
void UEdComboActionGraph::Clear()
{
@@ -23,14 +23,13 @@ public:
virtual bool Modify(bool bAlwaysMarkDirty) override;
virtual void PostEditUndo() override;
//TWeakPtr<FAssetEditor_ComboActionGraph> GetDialogueEditorPtr() const { return DialogueEditorPtr; }
//void SetDialogueEditorPtr(TWeakPtr<FAssetEditor_ComboActionGraph> NewPtr);
//void ResetDialogueEditorPtr() { DialogueEditorPtr.Reset(); }
TWeakPtr<class FAssetEditor_ComboActionGraph> GetComboActionEditorPtr() const { return this->ComboActionEditorPtr; }
void SetDialogueEditorPtr(TWeakPtr<class FAssetEditor_ComboActionGraph> NewPtr);
void ResetDialogueEditorPtr() { this->ComboActionEditorPtr.Reset(); }
//bool JumpToNode(const class UComboActionGraphNode *Node);
bool JumpToNode(const class UComboActionGraphNode *Node);
public:
UPROPERTY(Transient)
TMap<class UComboActionGraphNode*, class UEdComboActionGraphNode*> NodeMap;
@@ -38,12 +37,10 @@ public:
TMap<class UComboActionGraphEdge*, class UEdComboActionGraphEdge*> EdgeMap;
protected:
void Clear();
void SortNodes(UComboActionGraphNode *RootNode);
private:
/** Pointer back to the Dialogue editor that owns us */
//TWeakPtr<FAssetEditor_ComboActionGraph> DialogueEditorPtr;
/** Pointer back to the combo action editor that owns us */
TWeakPtr<FAssetEditor_ComboActionGraph> ComboActionEditorPtr;
};
@@ -23,6 +23,19 @@ UEdComboActionGraphNode::UEdComboActionGraphNode()
bAllowPaste = true;
}
void UEdComboActionGraphNode::SetComboActionGraphNode(UComboActionGraphNode *NewNode)
{
this->ComboActionGraphNode = NewNode;
if (this->ComboActionGraphNode)
{
this->bAllowCopy = this->ComboActionGraphNode->bAllowCopy;
this->bAllowDelete = this->ComboActionGraphNode->bAllowDelete;
this->bAllowDuplicate = this->ComboActionGraphNode->bAllowPaste;
this->bAllowPaste = this->ComboActionGraphNode->bAllowPaste;
}
}
UEdComboActionGraph *UEdComboActionGraphNode::GetEdComboActionGraph() const
{
return Cast<UEdComboActionGraph>(this->GetGraph());
@@ -22,7 +22,8 @@ class UEdComboActionGraphNode : public UEdGraphNode
public:
UEdComboActionGraphNode();
void SetComboActionGraphNode(UComboActionGraphNode *NewNode);
class UEdComboActionGraph *GetEdComboActionGraph() const;
virtual void AllocateDefaultPins() override;
@@ -0,0 +1,210 @@
// ©2023 Batty Bovine Productions, LLC. All Rights Reserved.
#include "FConnectionDrawingPolicy_ComboActionGraph.h"
#include "ComboInputEditor.h"
#include "Ed/EdComboActionGraphEdge.h"
#include "Ed/EdComboActionGraphNode.h"
#include "Settings/ComboActionGraphEditorSettings.h"
#include "Settings/FComboActionGraphEditorStyle.h"
DEFINE_LOG_CATEGORY(LogConnectionDrawingPolicy_ComboActionGraph);
FConnectionDrawingPolicy_ComboActionGraph::FConnectionDrawingPolicy_ComboActionGraph(int32 InBackLayerID, int32 InFrontLayerID, float ZoomFactor, const FSlateRect &InClippingRect, FSlateWindowElementList &InDrawElements, UEdGraph *InGraphObj)
: FKismetConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements, InGraphObj)
, GraphObj(InGraphObj)
{
if (const UComboActionGraphEditorSettings* GraphEditorSettings = GetMutableDefault<UComboActionGraphEditorSettings>())
{
FComboInputEditorModule &ComboInputEditorModule = FComboInputEditorModule::Get();
switch (GraphEditorSettings->GetArrowType())
{
case EComboActionArrowType::SimpleArrow:
ArrowImage = ComboInputEditorModule.ComboActionGraphEditorStyleSet->GetBrush(TEXT("MDSStyleSet.Graph.SimpleArrow"));
break;
case EComboActionArrowType::HollowArrow:
ArrowImage = ComboInputEditorModule.ComboActionGraphEditorStyleSet->GetBrush(TEXT("MDSStyleSet.Graph.HollowArrow"));
break;
case EComboActionArrowType::FancyArrow:
ArrowImage = ComboInputEditorModule.ComboActionGraphEditorStyleSet->GetBrush(TEXT("MDSStyleSet.Graph.FancyArrow"));
break;
case EComboActionArrowType::Bubble:
ArrowImage = ComboInputEditorModule.ComboActionGraphEditorStyleSet->GetBrush(TEXT("MDSStyleSet.Graph.Bubble"));
break;
case EComboActionArrowType::None:
default:
ArrowImage = nullptr;
}
}
else
{
ArrowImage = FAppStyle::GetBrush( TEXT("GenericPlay") );
}
ArrowRadius = ArrowImage ? ArrowImage->ImageSize * ZoomFactor * 0.5f : FVector2D(0.f);
MidpointImage = nullptr;
MidpointRadius = FVector2D::ZeroVector;
HoverDeemphasisDarkFraction = 0.8f;
BubbleImage = FAppStyle::GetBrush( TEXT("Graph.Arrow") );
}
void FConnectionDrawingPolicy_ComboActionGraph::DetermineWiringStyle(UEdGraphPin *OutputPin, UEdGraphPin *InputPin, FConnectionParams &Params)
{
Params.AssociatedPin1 = OutputPin;
Params.AssociatedPin2 = InputPin;
const UComboActionGraphEditorSettings *MounteaDialogueGraphEditorSettings = GetDefault<UComboActionGraphEditorSettings>();
if (MounteaDialogueGraphEditorSettings)
{
Params.WireThickness = MounteaDialogueGraphEditorSettings->GetWireWidth();
}
else
{
Params.WireThickness = 1.f;
}
const bool bDeemphasizeUnhoveredPins = HoveredPins.Num() > 0;
if (bDeemphasizeUnhoveredPins)
{
ApplyHoverDeemphasis(OutputPin, InputPin, /*inout*/ Params.WireThickness, /*inout*/ Params.WireColor);
}
}
void FConnectionDrawingPolicy_ComboActionGraph::Draw(TMap<TSharedRef<SWidget>, FArrangedWidget> &InPinGeometries, FArrangedChildren &ArrangedNodes)
{
// Build an acceleration structure to quickly find geometry for the nodes
NodeWidgetMap.Empty();
for (int32 NodeIndex = 0; NodeIndex < ArrangedNodes.Num(); ++NodeIndex)
{
FArrangedWidget& CurWidget = ArrangedNodes[NodeIndex];
TSharedRef<SGraphNode> ChildNode = StaticCastSharedRef<SGraphNode>(CurWidget.Widget);
NodeWidgetMap.Add(ChildNode->GetNodeObj(), NodeIndex);
}
// Now draw
FConnectionDrawingPolicy::Draw(InPinGeometries, ArrangedNodes);
}
void FConnectionDrawingPolicy_ComboActionGraph::DrawSplineWithArrow(const FGeometry &StartGeom, const FGeometry &EndGeom, const FConnectionParams &Params)
{
// Get a reasonable seed point (halfway between the boxes)
const FVector2D StartCenter = FGeometryHelper::CenterOf(StartGeom);
const FVector2D EndCenter = FGeometryHelper::CenterOf(EndGeom);
const FVector2D SeedPoint = (StartCenter + EndCenter) * 0.5f;
// Find the (approximate) closest points between the two boxes
const FVector2D StartAnchorPoint = FGeometryHelper::FindClosestPointOnGeom(StartGeom, SeedPoint);
const FVector2D EndAnchorPoint = FGeometryHelper::FindClosestPointOnGeom(EndGeom, SeedPoint);
this->DrawSplineWithArrow(StartAnchorPoint, EndAnchorPoint, Params);
}
void FConnectionDrawingPolicy_ComboActionGraph::DrawSplineWithArrow(const FVector2D &StartPoint, const FVector2D &EndPoint, const FConnectionParams &Params)
{
// bUserFlag1 indicates that we need to reverse the direction of connection (used by debugger)
const FVector2D &P0 = Params.bUserFlag1 ? EndPoint : StartPoint;
const FVector2D &P1 = Params.bUserFlag1 ? StartPoint : EndPoint;
UE_LOG(LogConnectionDrawingPolicy_ComboActionGraph, Verbose, TEXT("%s I %s"), *P0.ToString(), *P1.ToString());
FConnectionParams NewParams = Params;
//NewParams.bDrawBubbles = true;
if (const UComboActionGraphEditorSettings *MounteaDialogueGraphEditorSettings = GetMutableDefault<UComboActionGraphEditorSettings>())
{
NewParams.WireThickness = MounteaDialogueGraphEditorSettings->GetWireWidth();
}
Internal_DrawLineWithArrow(P0, P1, NewParams);
}
void FConnectionDrawingPolicy_ComboActionGraph::DrawPreviewConnector(const FGeometry &PinGeometry, const FVector2D &StartPoint, const FVector2D &EndPoint, UEdGraphPin* Pin)
{
FConnectionParams Params;
DetermineWiringStyle(Pin, nullptr, /*inout*/ Params);
if (Pin->Direction == EEdGraphPinDirection::EGPD_Output)
{
DrawSplineWithArrow(FGeometryHelper::FindClosestPointOnGeom(PinGeometry, EndPoint), EndPoint, Params);
}
else
{
DrawSplineWithArrow(FGeometryHelper::FindClosestPointOnGeom(PinGeometry, StartPoint), StartPoint, Params);
}
}
FVector2D FConnectionDrawingPolicy_ComboActionGraph::ComputeSplineTangent(const FVector2D &Start, const FVector2D &End) const
{
const FVector2D Delta = End - Start;
const FVector2D NormDelta = Delta.GetSafeNormal();
return NormDelta;
}
void FConnectionDrawingPolicy_ComboActionGraph::DetermineLinkGeometry(FArrangedChildren &ArrangedNodes, TSharedRef<SWidget> &OutputPinWidget, UEdGraphPin *OutputPin, UEdGraphPin *InputPin, FArrangedWidget *&StartWidgetGeometry, FArrangedWidget *&EndWidgetGeometry)
{
if (UEdComboActionGraphEdge *EdgeNode = Cast<UEdComboActionGraphEdge>(InputPin->GetOwningNode()))
{
UEdComboActionGraphNode* Start = EdgeNode->GetStartNode();
UEdComboActionGraphNode* End = EdgeNode->GetEndNode();
if (Start != nullptr && End != nullptr)
{
int32* StartNodeIndex = NodeWidgetMap.Find(Start);
int32* EndNodeIndex = NodeWidgetMap.Find(End);
if (StartNodeIndex != nullptr && EndNodeIndex != nullptr)
{
StartWidgetGeometry = &(ArrangedNodes[*StartNodeIndex]);
EndWidgetGeometry = &(ArrangedNodes[*EndNodeIndex]);
}
}
}
else
{
StartWidgetGeometry = PinGeometries->Find(OutputPinWidget);
if (TSharedPtr<SGraphPin>* pTargetWidget = PinToPinWidgetMap.Find(InputPin))
{
TSharedRef<SGraphPin> InputWidget = (*pTargetWidget).ToSharedRef();
EndWidgetGeometry = PinGeometries->Find(InputWidget);
}
}
}
void FConnectionDrawingPolicy_ComboActionGraph::Internal_DrawLineWithArrow(const FVector2D &StartAnchorPoint, const FVector2D &EndAnchorPoint, const FConnectionParams &Params)
{
const float LineSeparationAmount = 4.5f;
const FVector2D DeltaPos = EndAnchorPoint - StartAnchorPoint;
const FVector2D UnitDelta = DeltaPos.GetSafeNormal();
const FVector2D Normal = FVector2D(DeltaPos.Y, -DeltaPos.X).GetSafeNormal();
// Come up with the final start/end points
const FVector2D DirectionBias = Normal * LineSeparationAmount;
const FVector2D LengthBias = ArrowRadius.X * UnitDelta;
const FVector2D StartPoint = StartAnchorPoint + DirectionBias + LengthBias;
const FVector2D EndPoint = EndAnchorPoint + DirectionBias - LengthBias;
// Draw a line/spline
DrawConnection(WireLayerID, StartPoint, EndPoint, Params);
// Draw the arrow
if (ArrowImage)
{
const FVector2D ArrowDrawPos = EndPoint - ArrowRadius;
const float AngleInRadians = FMath::Atan2(DeltaPos.Y, DeltaPos.X);
FSlateDrawElement::MakeRotatedBox(
DrawElementsList,
ArrowLayerID,
FPaintGeometry(ArrowDrawPos, ArrowImage->ImageSize * ZoomFactor, ZoomFactor),
ArrowImage,
ESlateDrawEffect::None,
AngleInRadians,
TOptional<FVector2D>(),
FSlateDrawElement::RelativeToElement,
Params.WireColor
);
}
}
@@ -0,0 +1,36 @@
// ©2023 Batty Bovine Productions, LLC. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "BlueprintConnectionDrawingPolicy.h"
#include "ConnectionDrawingPolicy.h"
DECLARE_LOG_CATEGORY_EXTERN(LogConnectionDrawingPolicy_ComboActionGraph, Log, All);
class FConnectionDrawingPolicy_ComboActionGraph : public FKismetConnectionDrawingPolicy
{
public:
FConnectionDrawingPolicy_ComboActionGraph(int32 InBackLayerID, int32 InFrontLayerID, float ZoomFactor, const FSlateRect& InClippingRect, FSlateWindowElementList& InDrawElements, UEdGraph* InGraphObj);
virtual ~FConnectionDrawingPolicy_ComboActionGraph() {};
// FConnectionDrawingPolicy interface
virtual void DetermineWiringStyle(UEdGraphPin* OutputPin, UEdGraphPin* InputPin, /*inout*/ FConnectionParams& Params) override;
virtual void Draw(TMap<TSharedRef<SWidget>, FArrangedWidget>& InPinGeometries, FArrangedChildren& ArrangedNodes) override;
virtual void DrawSplineWithArrow(const FGeometry& StartGeom, const FGeometry& EndGeom, const FConnectionParams& Params) override;
virtual void DrawSplineWithArrow(const FVector2D& StartPoint, const FVector2D& EndPoint, const FConnectionParams& Params) override;
virtual void DrawPreviewConnector(const FGeometry& PinGeometry, const FVector2D& StartPoint, const FVector2D& EndPoint, UEdGraphPin* Pin) override;
virtual FVector2D ComputeSplineTangent(const FVector2D& Start, const FVector2D& End) const override;
virtual void DetermineLinkGeometry(FArrangedChildren& ArrangedNodes, TSharedRef<SWidget>& OutputPinWidget, UEdGraphPin* OutputPin, UEdGraphPin* InputPin, FArrangedWidget*& StartWidgetGeometry, FArrangedWidget*& EndWidgetGeometry) override;
// End of FConnectionDrawingPolicy interface
protected:
void Internal_DrawLineWithArrow(const FVector2D& StartAnchorPoint, const FVector2D& EndAnchorPoint, const FConnectionParams& Params);
protected:
UEdGraph *GraphObj;
TMap<UEdGraphNode*, int32> NodeWidgetMap;
};
@@ -0,0 +1,131 @@
#include "SEdComboActionGraphEdge.h"
#include "ConnectionDrawingPolicy.h"
#include "SGraphPanel.h"
#include "Ed/EdComboActionGraphEdge.h"
#include "Ed/EdComboActionGraphNode.h"
#include "Widgets/Images/SImage.h"
#define LOCTEXT_NAMESPACE "SEdComboActionGraphEdge"
void SEdComboActionGraphEdge::Construct(const FArguments &InArgs, UEdComboActionGraphEdge *InNode)
{
this->GraphNode = InNode;
this->UpdateGraphNode();
}
bool SEdComboActionGraphEdge::RequiresSecondPassLayout() const
{
return true;
}
void SEdComboActionGraphEdge::PerformSecondPassLayout(const TMap<UObject*, TSharedRef<SNode>>& NodeToWidgetLookup) const
{
UEdComboActionGraphEdge *EdgeNode = CastChecked<UEdComboActionGraphEdge>(GraphNode);
FGeometry StartGeom;
FGeometry EndGeom;
UEdComboActionGraphNode *Start = EdgeNode->GetStartNode();
UEdComboActionGraphNode *End = EdgeNode->GetEndNode();
if (Start != nullptr && End != nullptr)
{
const TSharedRef<SNode> *pFromWidget = NodeToWidgetLookup.Find(Start);
const TSharedRef<SNode> *pToWidget = NodeToWidgetLookup.Find(End);
if (pFromWidget != nullptr && pToWidget != nullptr)
{
const TSharedRef<SNode>& FromWidget = *pFromWidget;
const TSharedRef<SNode>& ToWidget = *pToWidget;
StartGeom = FGeometry(FVector2D(Start->NodePosX, Start->NodePosY), FVector2D::ZeroVector, FromWidget->GetDesiredSize(), 1.0f);
EndGeom = FGeometry(FVector2D(End->NodePosX, End->NodePosY), FVector2D::ZeroVector, ToWidget->GetDesiredSize(), 1.0f);
}
}
PositionBetweenTwoNodesWithOffset(StartGeom, EndGeom, 0, 1);
}
void SEdComboActionGraphEdge::UpdateGraphNode()
{
InputPins.Empty();
OutputPins.Empty();
RightNodeBox.Reset();
LeftNodeBox.Reset();
this->ContentScale.Bind( this, &SGraphNode::GetContentScale );
this->GetOrAddSlot( ENodeZone::Center )
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
[
SNew(SOverlay)
/*+ SOverlay::Slot()
[
SNew(SImage)
.Image(FAppStyle::GetBrush("Graph.TransitionNode.ColorSpill"))
.ColorAndOpacity(this, &SEdComboActionGraphEdge::GetEdgeColor)
]*/
+ SOverlay::Slot()
[
SNew(SImage)
.Image(FAppStyle::GetBrush("GraphEditor.RefPinIcon"))
.ColorAndOpacity(this, &SEdComboActionGraphEdge::GetEdgeColor)
]
];
}
void SEdComboActionGraphEdge::PositionBetweenTwoNodesWithOffset(const FGeometry& StartGeom, const FGeometry& EndGeom, int32 NodeIndex, int32 MaxNodes) const
{
// Get a reasonable seed point (halfway between the boxes)
const FVector2D StartCenter = FGeometryHelper::CenterOf(StartGeom);
const FVector2D EndCenter = FGeometryHelper::CenterOf(EndGeom);
const FVector2D SeedPoint = (StartCenter + EndCenter) * 0.5f;
// Find the (approximate) closest points between the two boxes
const FVector2D StartAnchorPoint = FGeometryHelper::FindClosestPointOnGeom(StartGeom, SeedPoint);
const FVector2D EndAnchorPoint = FGeometryHelper::FindClosestPointOnGeom(EndGeom, SeedPoint);
// Position ourselves halfway along the connecting line between the nodes, elevated away perpendicular to the direction of the line
const float Height = 30.0f;
const FVector2D DesiredNodeSize = GetDesiredSize();
FVector2D DeltaPos(EndAnchorPoint - StartAnchorPoint);
if (DeltaPos.IsNearlyZero())
{
DeltaPos = FVector2D(10.0f, 0.0f);
}
const FVector2D Normal = FVector2D(DeltaPos.Y, -DeltaPos.X).GetSafeNormal();
const FVector2D NewCenter = StartAnchorPoint + (0.5f * DeltaPos) + (Height * Normal);
FVector2D DeltaNormal = DeltaPos.GetSafeNormal();
// Calculate node offset in the case of multiple transitions between the same two nodes
// MultiNodeOffset: the offset where 0 is the centre of the transition, -1 is 1 <size of node>
// towards the PrevStateNode and +1 is 1 <size of node> towards the NextStateNode.
const float MutliNodeSpace = 0.2f; // Space between multiple transition nodes (in units of <size of node> )
const float MultiNodeStep = (1.f + MutliNodeSpace); //Step between node centres (Size of node + size of node spacer)
const float MultiNodeStart = -((MaxNodes - 1) * MultiNodeStep) / 2.f;
const float MultiNodeOffset = MultiNodeStart + (NodeIndex * MultiNodeStep);
// Now we need to adjust the new center by the node size, zoom factor and multi node offset
const FVector2D NewCorner = NewCenter - (0.5f * DesiredNodeSize) + (DeltaNormal * MultiNodeOffset * DesiredNodeSize.Size());
GraphNode->NodePosX = NewCorner.X;
GraphNode->NodePosY = NewCorner.Y;
}
FSlateColor SEdComboActionGraphEdge::GetEdgeColor() const
{
return FLinearColor(0.9f, 0.9f, 0.9f, 1.0f);
}
#undef LOCTEXT_NAMESPACE
@@ -0,0 +1,33 @@
#pragma once
#include "CoreMinimal.h"
#include "Styling/SlateColor.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Widgets/SWidget.h"
#include "SNodePanel.h"
#include "SGraphNode.h"
class SEdComboActionGraphEdge : public SGraphNode
{
public:
SLATE_BEGIN_ARGS(SEdComboActionGraphEdge){}
SLATE_END_ARGS()
void Construct(const FArguments &InArgs, class UEdComboActionGraphEdge *InNode);
virtual bool RequiresSecondPassLayout() const override;
virtual void PerformSecondPassLayout(const TMap<UObject*, TSharedRef<SNode> > &NodeToWidgetLookup) const override;
virtual void UpdateGraphNode() override;
// Calculate position for multiple nodes to be placed between a start and end point, by providing this nodes index and max expected nodes
void PositionBetweenTwoNodesWithOffset(const FGeometry &StartGeom, const FGeometry &EndGeom, int32 NodeIndex, int32 MaxNodes) const;
protected:
FSlateColor GetEdgeColor() const;
private:
TSharedPtr<STextEntryPopup> TextEntryWidget;
};