From c682f72afa6869c63972d3ed027078bcdde23462 Mon Sep 17 00:00:00 2001 From: PHILIP WHITE Date: Mon, 20 Nov 2023 21:25:36 +0000 Subject: [PATCH] Add Generic Graph Plugin for Creating Custom Graph --- .../Plugins/GenericGraph/.gitignore | 14 + .../Plugins/GenericGraph/GenericGraph.uplugin | 30 + EndlessVendetta/Plugins/GenericGraph/LICENSE | 21 + .../Resources/AutoArrangeIcon.png | 3 + .../GenericGraph/Resources/Icon128.png | 3 + .../GenericGraphEditor.Build.cs | 62 ++ .../Private/AssetTypeActions_GenericGraph.cpp | 48 + .../Private/AutoLayout/AutoLayoutStrategy.cpp | 96 ++ .../ForceDirectedLayoutStrategy.cpp | 162 ++++ .../Private/AutoLayout/TreeLayoutStrategy.cpp | 241 ++++++ .../AssetEditorToolbar_GenericGraph.cpp | 46 + .../AssetEditor_GenericGraph.cpp | 819 ++++++++++++++++++ .../AssetGraphSchema_GenericGraph.cpp | 476 ++++++++++ .../ConnectionDrawingPolicy_GenericGraph.cpp | 149 ++++ .../EdGraph_GenericGraph.cpp | 205 +++++ .../EdNode_GenericGraphEdge.cpp | 97 +++ .../EdNode_GenericGraphNode.cpp | 84 ++ .../EditorCommands_GenericGraph.cpp | 11 + .../GenericGraphDragConnection.cpp | 325 +++++++ .../GenericGraphEditorStyle.cpp | 54 ++ .../SEdNode_GenericGraphEdge.cpp | 199 +++++ .../SEdNode_GenericGraphNode.cpp | 336 +++++++ .../Settings_GenericGraphEditor.cpp | 24 + .../Private/GenericGraphEditor.cpp | 55 ++ .../Private/GenericGraphFactory.cpp | 117 +++ .../Private/GenericGraphNodeFactory.cpp | 20 + .../Public/AssetTypeActions_GenericGraph.h | 19 + .../Public/AutoLayout/AutoLayoutStrategy.h | 40 + .../AutoLayout/ForceDirectedLayoutStrategy.h | 24 + .../Public/AutoLayout/TreeLayoutStrategy.h | 29 + .../AssetEditorToolbar_GenericGraph.h | 24 + .../AssetEditor_GenericGraph.h | 137 +++ .../AssetGraphSchema_GenericGraph.h | 90 ++ .../Colors_GenericGraph.h | 45 + .../ConnectionDrawingPolicy_GenericGraph.h | 27 + .../EdGraph_GenericGraph.h | 39 + .../EdNode_GenericGraphEdge.h | 42 + .../EdNode_GenericGraphNode.h | 42 + .../EditorCommands_GenericGraph.h | 18 + .../GenericGraphDragConnection.h | 52 ++ .../GenericGraphEditorStyle.h | 16 + .../SEdNode_GenericGraphEdge.h | 40 + .../SEdNode_GenericGraphNode.h | 32 + .../Settings_GenericGraphEditor.h | 42 + .../Public/GenericGraphEditor.h | 23 + .../Public/GenericGraphEditorModule.h | 34 + .../Public/GenericGraphEditorPCH.h | 13 + .../Public/GenericGraphFactory.h | 22 + .../Public/GenericGraphNodeFactory.h | 8 + .../GenericGraphRuntime.Build.cs | 51 ++ .../Private/GenericGraph.cpp | 136 +++ .../Private/GenericGraphEdge.cpp | 23 + .../Private/GenericGraphNode.cpp | 90 ++ .../Private/GenericGraphRuntime.cpp | 29 + .../Private/GenericGraphRuntimePCH.h | 12 + .../GenericGraphRuntime/Public/GenericGraph.h | 60 ++ .../Public/GenericGraphEdge.h | 48 + .../Public/GenericGraphNode.h | 94 ++ .../Public/IGenericGraphRuntime.h | 36 + .../GenericGraph/docs/images/GenericGraph.png | 3 + .../docs/images/ability-graph.png | 3 + .../docs/images/dialogue/dialogue01.png | 3 + .../docs/images/dialogue/dialogue02.png | 3 + .../docs/images/dialogue/dialogue03.png | 3 + .../Plugins/GenericGraph/readme.rst | 51 ++ 65 files changed, 5200 insertions(+) create mode 100644 EndlessVendetta/Plugins/GenericGraph/.gitignore create mode 100644 EndlessVendetta/Plugins/GenericGraph/GenericGraph.uplugin create mode 100644 EndlessVendetta/Plugins/GenericGraph/LICENSE create mode 100644 EndlessVendetta/Plugins/GenericGraph/Resources/AutoArrangeIcon.png create mode 100644 EndlessVendetta/Plugins/GenericGraph/Resources/Icon128.png create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/GenericGraphEditor.Build.cs create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/AssetTypeActions_GenericGraph.cpp create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/AutoLayout/AutoLayoutStrategy.cpp create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/AutoLayout/ForceDirectedLayoutStrategy.cpp create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/AutoLayout/TreeLayoutStrategy.cpp create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/AssetEditorToolbar_GenericGraph.cpp create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/AssetEditor_GenericGraph.cpp create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/AssetGraphSchema_GenericGraph.cpp create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/ConnectionDrawingPolicy_GenericGraph.cpp create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/EdGraph_GenericGraph.cpp create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/EdNode_GenericGraphEdge.cpp create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/EdNode_GenericGraphNode.cpp create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/EditorCommands_GenericGraph.cpp create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/GenericGraphDragConnection.cpp create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/GenericGraphEditorStyle.cpp create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/SEdNode_GenericGraphEdge.cpp create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/SEdNode_GenericGraphNode.cpp create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/Settings_GenericGraphEditor.cpp create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphEditor.cpp create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphFactory.cpp create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphNodeFactory.cpp create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/AssetTypeActions_GenericGraph.h create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/AutoLayout/AutoLayoutStrategy.h create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/AutoLayout/ForceDirectedLayoutStrategy.h create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/AutoLayout/TreeLayoutStrategy.h create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/AssetEditorToolbar_GenericGraph.h create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/AssetEditor_GenericGraph.h create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/AssetGraphSchema_GenericGraph.h create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/Colors_GenericGraph.h create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/ConnectionDrawingPolicy_GenericGraph.h create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/EdGraph_GenericGraph.h create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/EdNode_GenericGraphEdge.h create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/EdNode_GenericGraphNode.h create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/EditorCommands_GenericGraph.h create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/GenericGraphDragConnection.h create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/GenericGraphEditorStyle.h create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/SEdNode_GenericGraphEdge.h create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/SEdNode_GenericGraphNode.h create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/Settings_GenericGraphEditor.h create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphEditor.h create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphEditorModule.h create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphEditorPCH.h create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphFactory.h create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphNodeFactory.h create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/GenericGraphRuntime.Build.cs create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Private/GenericGraph.cpp create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Private/GenericGraphEdge.cpp create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Private/GenericGraphNode.cpp create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Private/GenericGraphRuntime.cpp create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Private/GenericGraphRuntimePCH.h create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Public/GenericGraph.h create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Public/GenericGraphEdge.h create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Public/GenericGraphNode.h create mode 100644 EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Public/IGenericGraphRuntime.h create mode 100644 EndlessVendetta/Plugins/GenericGraph/docs/images/GenericGraph.png create mode 100644 EndlessVendetta/Plugins/GenericGraph/docs/images/ability-graph.png create mode 100644 EndlessVendetta/Plugins/GenericGraph/docs/images/dialogue/dialogue01.png create mode 100644 EndlessVendetta/Plugins/GenericGraph/docs/images/dialogue/dialogue02.png create mode 100644 EndlessVendetta/Plugins/GenericGraph/docs/images/dialogue/dialogue03.png create mode 100644 EndlessVendetta/Plugins/GenericGraph/readme.rst diff --git a/EndlessVendetta/Plugins/GenericGraph/.gitignore b/EndlessVendetta/Plugins/GenericGraph/.gitignore new file mode 100644 index 00000000..7b887157 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/.gitignore @@ -0,0 +1,14 @@ +Binaries +DerivedDataCache +Intermediate +Saved +Build +*.sdf +*.sln +*.suo +*.opensdf +*.opendb +*.db +/docs/sphinx-build-result +/.vs +NoCommit \ No newline at end of file diff --git a/EndlessVendetta/Plugins/GenericGraph/GenericGraph.uplugin b/EndlessVendetta/Plugins/GenericGraph/GenericGraph.uplugin new file mode 100644 index 00000000..68e4ef88 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/GenericGraph.uplugin @@ -0,0 +1,30 @@ +{ + "FileVersion" : 3, + "Version" : 1, + "VersionName" : "1.0", + "FriendlyName" : "Generic Graph Plugin", + "Description" : "", + "Category" : "GenericGraph", + "CreatedBy" : "jinyuliao", + "CreatedByURL" : "", + "DocsURL" : "", + "MarketplaceURL" : "", + "SupportURL" : "", + "EnabledByDefault" : true, + "CanContainContent" : true, + "IsBetaVersion" : false, + "Installed" : false, + "Modules" : + [ + { + "Name" : "GenericGraphRuntime", + "Type" : "Runtime", + "LoadingPhase" : "PreDefault" + }, + { + "Name" : "GenericGraphEditor", + "Type" : "Editor", + "LoadingPhase" : "Default" + } + ] +} \ No newline at end of file diff --git a/EndlessVendetta/Plugins/GenericGraph/LICENSE b/EndlessVendetta/Plugins/GenericGraph/LICENSE new file mode 100644 index 00000000..9c189b15 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 jinyuliao + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/EndlessVendetta/Plugins/GenericGraph/Resources/AutoArrangeIcon.png b/EndlessVendetta/Plugins/GenericGraph/Resources/AutoArrangeIcon.png new file mode 100644 index 00000000..b5e6c3b1 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Resources/AutoArrangeIcon.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:71dff338a731ee7bf2def0d378b0a866615d2ee72aa591ab4ed7ad1105049806 +size 5528 diff --git a/EndlessVendetta/Plugins/GenericGraph/Resources/Icon128.png b/EndlessVendetta/Plugins/GenericGraph/Resources/Icon128.png new file mode 100644 index 00000000..57ee2a55 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Resources/Icon128.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9eb6f69963ee569b0a8a4446e21f69d0a93205caf07b30cc37b1fddb62599602 +size 15543 diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/GenericGraphEditor.Build.cs b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/GenericGraphEditor.Build.cs new file mode 100644 index 00000000..aa57f0ce --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/GenericGraphEditor.Build.cs @@ -0,0 +1,62 @@ +using UnrealBuildTool; + +public class GenericGraphEditor : ModuleRules +{ + public GenericGraphEditor(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + bLegacyPublicIncludePaths = false; + ShadowVariableWarningLevel = WarningLevel.Error; + + PublicIncludePaths.AddRange( + new string[] { + // ... add public include paths required here ... + } + ); + + PrivateIncludePaths.AddRange( + new string[] { + // ... add other private include paths required here ... + "GenericGraphEditor/Private", + "GenericGraphEditor/Public", + } + ); + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + "CoreUObject", + "Engine", + "UnrealEd", + // ... add other public dependencies that you statically link with here ... + } + ); + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "GenericGraphRuntime", + "AssetTools", + "Slate", + "InputCore", + "SlateCore", + "GraphEditor", + "PropertyEditor", + "EditorStyle", + "Kismet", + "KismetWidgets", + "ApplicationCore", + "ToolMenus", + // ... add private dependencies that you statically link with here ... + } + ); + + DynamicallyLoadedModuleNames.AddRange( + new string[] + { + // ... add any modules that your module loads dynamically here ... + } + ); + } +} diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/AssetTypeActions_GenericGraph.cpp b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/AssetTypeActions_GenericGraph.cpp new file mode 100644 index 00000000..643911c7 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/AssetTypeActions_GenericGraph.cpp @@ -0,0 +1,48 @@ +#include "AssetTypeActions_GenericGraph.h" +#include "GenericGraphEditorPCH.h" +#include "GenericGraphAssetEditor/AssetEditor_GenericGraph.h" + +#define LOCTEXT_NAMESPACE "AssetTypeActions_GenericGraph" + +FAssetTypeActions_GenericGraph::FAssetTypeActions_GenericGraph(EAssetTypeCategories::Type InAssetCategory) + : MyAssetCategory(InAssetCategory) +{ +} + +FText FAssetTypeActions_GenericGraph::GetName() const +{ + return LOCTEXT("FGenericGraphAssetTypeActionsName", "Generic Graph"); +} + +FColor FAssetTypeActions_GenericGraph::GetTypeColor() const +{ + return FColor(129, 196, 115); +} + +UClass* FAssetTypeActions_GenericGraph::GetSupportedClass() const +{ + return UGenericGraph::StaticClass(); +} + +void FAssetTypeActions_GenericGraph::OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor) +{ + const EToolkitMode::Type Mode = EditWithinLevelEditor.IsValid() ? EToolkitMode::WorldCentric : EToolkitMode::Standalone; + + for (auto ObjIt = InObjects.CreateConstIterator(); ObjIt; ++ObjIt) + { + if (UGenericGraph* Graph = Cast(*ObjIt)) + { + TSharedRef NewGraphEditor(new FAssetEditor_GenericGraph()); + NewGraphEditor->InitGenericGraphAssetEditor(Mode, EditWithinLevelEditor, Graph); + } + } +} + +uint32 FAssetTypeActions_GenericGraph::GetCategories() +{ + return MyAssetCategory; +} + +////////////////////////////////////////////////////////////////////////// + +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/AutoLayout/AutoLayoutStrategy.cpp b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/AutoLayout/AutoLayoutStrategy.cpp new file mode 100644 index 00000000..4d03bd87 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/AutoLayout/AutoLayoutStrategy.cpp @@ -0,0 +1,96 @@ +#include "AutoLayout/AutoLayoutStrategy.h" +#include "Kismet/KismetMathLibrary.h" +#include "GenericGraphAssetEditor/EdNode_GenericGraphNode.h" +#include "GenericGraphAssetEditor/SEdNode_GenericGraphNode.h" + +UAutoLayoutStrategy::UAutoLayoutStrategy() +{ + Settings = nullptr; + MaxIteration = 50; + OptimalDistance = 150; +} + +UAutoLayoutStrategy::~UAutoLayoutStrategy() +{ + +} + +FBox2D UAutoLayoutStrategy::GetNodeBound(UEdGraphNode* EdNode) +{ + int32 NodeWidth = GetNodeWidth(Cast(EdNode)); + int32 NodeHeight = GetNodeHeight(Cast(EdNode)); + FVector2D Min(EdNode->NodePosX, EdNode->NodePosY); + FVector2D Max(EdNode->NodePosX + NodeWidth, EdNode->NodePosY + NodeHeight); + return FBox2D(Min, Max); +} + +FBox2D UAutoLayoutStrategy::GetActualBounds(UGenericGraphNode* RootNode) +{ + int Level = 0; + TArray CurrLevelNodes = { RootNode }; + TArray NextLevelNodes; + + FBox2D Rtn = GetNodeBound(EdGraph->NodeMap[RootNode]); + + while (CurrLevelNodes.Num() != 0) + { + for (int i = 0; i < CurrLevelNodes.Num(); ++i) + { + UGenericGraphNode* Node = CurrLevelNodes[i]; + check(Node != nullptr); + + Rtn += GetNodeBound(EdGraph->NodeMap[Node]); + + for (int j = 0; j < Node->ChildrenNodes.Num(); ++j) + { + NextLevelNodes.Add(Node->ChildrenNodes[j]); + } + } + + CurrLevelNodes = NextLevelNodes; + NextLevelNodes.Reset(); + ++Level; + } + return Rtn; +} + +void UAutoLayoutStrategy::RandomLayoutOneTree(UGenericGraphNode* RootNode, const FBox2D& Bound) +{ + int Level = 0; + TArray CurrLevelNodes = { RootNode }; + TArray NextLevelNodes; + + while (CurrLevelNodes.Num() != 0) + { + for (int i = 0; i < CurrLevelNodes.Num(); ++i) + { + UGenericGraphNode* Node = CurrLevelNodes[i]; + check(Node != nullptr); + + UEdNode_GenericGraphNode* EdNode_Node = EdGraph->NodeMap[Node]; + + EdNode_Node->NodePosX = UKismetMathLibrary::RandomFloatInRange(Bound.Min.X, Bound.Max.X); + EdNode_Node->NodePosY = UKismetMathLibrary::RandomFloatInRange(Bound.Min.Y, Bound.Max.Y); + + for (int j = 0; j < Node->ChildrenNodes.Num(); ++j) + { + NextLevelNodes.Add(Node->ChildrenNodes[j]); + } + } + + CurrLevelNodes = NextLevelNodes; + NextLevelNodes.Reset(); + ++Level; + } +} + +int32 UAutoLayoutStrategy::GetNodeWidth(UEdNode_GenericGraphNode* EdNode) +{ + return EdNode->SEdNode->GetCachedGeometry().GetLocalSize().X; +} + +int32 UAutoLayoutStrategy::GetNodeHeight(UEdNode_GenericGraphNode* EdNode) +{ + return EdNode->SEdNode->GetCachedGeometry().GetLocalSize().Y; +} + diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/AutoLayout/ForceDirectedLayoutStrategy.cpp b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/AutoLayout/ForceDirectedLayoutStrategy.cpp new file mode 100644 index 00000000..8d66bfcb --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/AutoLayout/ForceDirectedLayoutStrategy.cpp @@ -0,0 +1,162 @@ +#include "AutoLayout/ForceDirectedLayoutStrategy.h" + +static inline float CoolDown(float Temp, float CoolDownRate) +{ + if (Temp < .01) return .01; + return Temp - (Temp / CoolDownRate); +} + +static inline float GetAttractForce(float X, float K) +{ + return (X * X) / K; +} + +static inline float GetRepulseForce(float X, float k) +{ + return X != 0 ? k * k / X : TNumericLimits::Max(); +} + +UForceDirectedLayoutStrategy::UForceDirectedLayoutStrategy() +{ + bRandomInit = false; + CoolDownRate = 10; + InitTemperature = 10.f; +} + +UForceDirectedLayoutStrategy::~UForceDirectedLayoutStrategy() +{ + +} + +void UForceDirectedLayoutStrategy::Layout(UEdGraph* _EdGraph) +{ + EdGraph = Cast(_EdGraph); + check(EdGraph != nullptr); + + EdGraph->RebuildGenericGraph(); + Graph = EdGraph->GetGenericGraph(); + check(Graph != nullptr); + + if (Settings != nullptr) + { + OptimalDistance = Settings->OptimalDistance; + MaxIteration = Settings->MaxIteration; + bRandomInit = Settings->bRandomInit; + } + + FBox2D PreTreeBound(ForceInitToZero); + for (int32 i = 0; i < Graph->RootNodes.Num(); ++i) + { + PreTreeBound = LayoutOneTree(Graph->RootNodes[i], PreTreeBound); + } +} + +FBox2D UForceDirectedLayoutStrategy::LayoutOneTree(UGenericGraphNode* RootNode, const FBox2D& PreTreeBound) +{ + float Temp = InitTemperature; + FBox2D TreeBound = GetActualBounds(RootNode); + TreeBound.Min.X += PreTreeBound.Max.X + OptimalDistance; + TreeBound.Max.X += PreTreeBound.Max.X + OptimalDistance; + + if (bRandomInit) + { + RandomLayoutOneTree(RootNode, TreeBound); + } + + float RepulseForce, AttractForce, Distance; + FVector2D Diff; + + TMap NodeToDisplacement; + + for (int32 i = 0; i < EdGraph->Nodes.Num(); ++i) + { + NodeToDisplacement.Add(EdGraph->Nodes[i], FVector2D(0.f, 0.f)); + } + + for (int32 IterrationNum = 0; IterrationNum < MaxIteration; ++IterrationNum) + { + // Calculate the repulsive forces. + for (int32 i = 0; i < EdGraph->Nodes.Num(); ++i) + { + for (int32 j = 0; j < EdGraph->Nodes.Num(); ++j) + { + if (i == j) + continue; + Diff.X = EdGraph->Nodes[i]->NodePosX - EdGraph->Nodes[j]->NodePosX; + Diff.Y = EdGraph->Nodes[i]->NodePosY - EdGraph->Nodes[j]->NodePosY; + Distance = Diff.Size(); + Diff.Normalize(); + + RepulseForce = Distance > 2 * OptimalDistance ? 0 : GetRepulseForce(Distance, OptimalDistance); + + NodeToDisplacement[EdGraph->Nodes[i]] += Diff * RepulseForce; + } + } + + // Calculate the attractive forces. + int Level = 0; + TArray CurrLevelNodes = { RootNode }; + TArray NextLevelNodes; + + while (CurrLevelNodes.Num() != 0) + { + for (int32 i = 0; i < CurrLevelNodes.Num(); ++i) + { + UGenericGraphNode* Node = CurrLevelNodes[i]; + check(Node != nullptr); + + UEdNode_GenericGraphNode* EdNode_ParentNode = EdGraph->NodeMap[Node]; + + for (int32 j = 0; j < Node->ChildrenNodes.Num(); ++j) + { + NextLevelNodes.Add(Node->ChildrenNodes[j]); + + UEdNode_GenericGraphNode* EdNode_ChildNode = EdGraph->NodeMap[Node->ChildrenNodes[j]]; + + Diff.X = EdNode_ChildNode->NodePosX - EdNode_ParentNode->NodePosY; + Diff.Y = EdNode_ChildNode->NodePosY - EdNode_ParentNode->NodePosY; + Distance = Diff.Size(); + Diff.Normalize(); + + AttractForce = GetAttractForce(Distance, OptimalDistance); + + NodeToDisplacement[EdNode_ParentNode] += Distance * Diff; + NodeToDisplacement[EdNode_ChildNode] -= Distance * Diff; + } + } + + CurrLevelNodes = NextLevelNodes; + NextLevelNodes.Reset(); + ++Level; + } + + for (int32 i = 0; i < EdGraph->Nodes.Num(); ++i) + { + UEdGraphNode* EdNode = EdGraph->Nodes[i]; + Distance = NodeToDisplacement[EdNode].Size(); + NodeToDisplacement[EdNode].Normalize(); + + float Minimum = Distance < Temp ? Distance : Temp; + EdNode->NodePosX += NodeToDisplacement[EdNode].X * Minimum; + EdNode->NodePosY += NodeToDisplacement[EdNode].Y * Minimum; + } + + Temp = CoolDown(Temp, CoolDownRate); + } + + FBox2D ActualBound = GetActualBounds(RootNode); + + FVector2D Center = ActualBound.GetCenter(); + FVector2D TreeCenter = TreeBound.GetCenter(); + + FVector2D Scale = (TreeBound.Max - TreeBound.Min) / (ActualBound.Max - ActualBound.Min); + + for (int32 i = 0; i < EdGraph->Nodes.Num(); ++i) + { + UEdGraphNode* EdNode = EdGraph->Nodes[i]; + EdNode->NodePosX = TreeCenter.X + Scale.X * (EdNode->NodePosX - Center.X); + EdNode->NodePosY = TreeCenter.Y + Scale.Y * (EdNode->NodePosY - Center.Y); + } + + return TreeBound; +} diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/AutoLayout/TreeLayoutStrategy.cpp b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/AutoLayout/TreeLayoutStrategy.cpp new file mode 100644 index 00000000..93163208 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/AutoLayout/TreeLayoutStrategy.cpp @@ -0,0 +1,241 @@ +#include "AutoLayout/TreeLayoutStrategy.h" +#include "GenericGraphEditorPCH.h" +#include "GenericGraphAssetEditor/SEdNode_GenericGraphNode.h" + +UTreeLayoutStrategy::UTreeLayoutStrategy() +{ +} + +UTreeLayoutStrategy::~UTreeLayoutStrategy() +{ + +} + +void UTreeLayoutStrategy::Layout(UEdGraph* _EdGraph) +{ + EdGraph = Cast(_EdGraph); + check(EdGraph != nullptr); + + EdGraph->RebuildGenericGraph(); + Graph = EdGraph->GetGenericGraph(); + check(Graph != nullptr); + + bool bFirstPassOnly = false; + + if (Settings != nullptr) + { + OptimalDistance = Settings->OptimalDistance; + MaxIteration = Settings->MaxIteration; + bFirstPassOnly = Settings->bFirstPassOnly; + } + + FVector2D Anchor(0.f, 0.f); + for (int32 i = 0; i < Graph->RootNodes.Num(); ++i) + { + UGenericGraphNode* RootNode = Graph->RootNodes[i]; + InitPass(RootNode, Anchor); + + if (!bFirstPassOnly) + { + for (int32 j = 0; j < MaxIteration; ++j) + { + bool HasConflict = ResolveConflictPass(RootNode); + if (!HasConflict) + { + break; + } + } + } + } + + for (int32 i = 0; i < Graph->RootNodes.Num(); ++i) + { + for (int32 j = 0; j < i; ++j) + { + ResolveConflict(Graph->RootNodes[j], Graph->RootNodes[i]); + } + } +} + +void UTreeLayoutStrategy::InitPass(UGenericGraphNode* RootNode, const FVector2D& Anchor) +{ + UEdNode_GenericGraphNode* EdNode_RootNode = EdGraph->NodeMap[RootNode]; + + FVector2D ChildAnchor(FVector2D(0.f, GetNodeHeight(EdNode_RootNode) + OptimalDistance + Anchor.Y)); + for (int32 i = 0; i < RootNode->ChildrenNodes.Num(); ++i) + { + UGenericGraphNode* Child = RootNode->ChildrenNodes[i]; + UEdNode_GenericGraphNode* EdNode_ChildNode = EdGraph->NodeMap[Child]; + if (i > 0) + { + UGenericGraphNode* PreChild = RootNode->ChildrenNodes[i - 1]; + UEdNode_GenericGraphNode* EdNode_PreChildNode = EdGraph->NodeMap[PreChild]; + ChildAnchor.X += OptimalDistance + GetNodeWidth(EdNode_PreChildNode) / 2; + } + ChildAnchor.X += GetNodeWidth(EdNode_ChildNode) / 2; + InitPass(Child, ChildAnchor); + } + + float NodeWidth = GetNodeWidth(EdNode_RootNode); + + EdNode_RootNode->NodePosY = Anchor.Y; + if (RootNode->ChildrenNodes.Num() == 0) + { + EdNode_RootNode->NodePosX = Anchor.X - NodeWidth / 2; + } + else + { + UpdateParentNodePosition(RootNode); + } +} + +bool UTreeLayoutStrategy::ResolveConflictPass(UGenericGraphNode* Node) +{ + bool HasConflict = false; + for (int32 i = 0; i < Node->ChildrenNodes.Num(); ++i) + { + UGenericGraphNode* Child = Node->ChildrenNodes[i]; + if (ResolveConflictPass(Child)) + { + HasConflict = true; + } + } + + for (int32 i = 0; i < Node->ParentNodes.Num(); ++i) + { + UGenericGraphNode* ParentNode = Node->ParentNodes[i]; + for (int32 j = 0; j < ParentNode->ChildrenNodes.Num(); ++j) + { + UGenericGraphNode* LeftSibling = ParentNode->ChildrenNodes[j]; + if (LeftSibling == Node) + break; + if (ResolveConflict(LeftSibling, Node)) + { + HasConflict = true; + } + } + } + + return HasConflict; +} + +bool UTreeLayoutStrategy::ResolveConflict(UGenericGraphNode* LRoot, UGenericGraphNode* RRoot) +{ + TArray RightContour, LeftContour; + + GetRightContour(LRoot, 0, RightContour); + GetLeftContour(RRoot, 0, LeftContour); + + int32 MaxOverlapDistance = 0; + int32 Num = FMath::Min(LeftContour.Num(), RightContour.Num()); + for (int32 i = 0; i < Num; ++i) + { + if (RightContour.Contains(LeftContour[i]) || LeftContour.Contains(RightContour[i])) + break; + + int32 RightBound = RightContour[i]->NodePosX + GetNodeWidth(RightContour[i]); + int32 LeftBound = LeftContour[i]->NodePosX; + int32 Distance = RightBound + OptimalDistance - LeftBound; + if (Distance > MaxOverlapDistance) + { + MaxOverlapDistance = Distance; + } + } + + if (MaxOverlapDistance > 0) + { + ShiftSubTree(RRoot, FVector2D(MaxOverlapDistance, 0.f)); + + TArray ParentNodes = RRoot->ParentNodes; + TArray NextParentNodes; + while (ParentNodes.Num() != 0) + { + for (int32 i = 0; i < ParentNodes.Num(); ++i) + { + UpdateParentNodePosition(ParentNodes[i]); + + NextParentNodes.Append(ParentNodes[i]->ParentNodes); + } + + ParentNodes = NextParentNodes; + NextParentNodes.Reset(); + } + + return true; + } + else + { + return false; + } +} + +void UTreeLayoutStrategy::GetLeftContour(UGenericGraphNode* RootNode, int32 Level, TArray& Contour) +{ + UEdNode_GenericGraphNode* EdNode_Node = EdGraph->NodeMap[RootNode]; + if (Level >= Contour.Num()) + { + Contour.Add(EdNode_Node); + } + else if (EdNode_Node->NodePosX < Contour[Level]->NodePosX) + { + Contour[Level] = EdNode_Node; + } + + for (int32 i = 0; i < RootNode->ChildrenNodes.Num(); ++i) + { + GetLeftContour(RootNode->ChildrenNodes[i], Level + 1, Contour); + } +} + +void UTreeLayoutStrategy::GetRightContour(UGenericGraphNode* RootNode, int32 Level, TArray& Contour) +{ + UEdNode_GenericGraphNode* EdNode_Node = EdGraph->NodeMap[RootNode]; + if (Level >= Contour.Num()) + { + Contour.Add(EdNode_Node); + } + else if (EdNode_Node->NodePosX + GetNodeWidth(EdNode_Node) > Contour[Level]->NodePosX + GetNodeWidth(Contour[Level])) + { + Contour[Level] = EdNode_Node; + } + + for (int32 i = 0; i < RootNode->ChildrenNodes.Num(); ++i) + { + GetRightContour(RootNode->ChildrenNodes[i], Level + 1, Contour); + } +} + +void UTreeLayoutStrategy::ShiftSubTree(UGenericGraphNode* RootNode, const FVector2D& Offset) +{ + UEdNode_GenericGraphNode* EdNode_Node = EdGraph->NodeMap[RootNode]; + EdNode_Node->NodePosX += Offset.X; + EdNode_Node->NodePosY += Offset.Y; + + for (int32 i = 0; i < RootNode->ChildrenNodes.Num(); ++i) + { + UGenericGraphNode* Child = RootNode->ChildrenNodes[i]; + + if (Child->ParentNodes[0] == RootNode) + { + ShiftSubTree(RootNode->ChildrenNodes[i], Offset); + } + } +} + +void UTreeLayoutStrategy::UpdateParentNodePosition(UGenericGraphNode* ParentNode) +{ + UEdNode_GenericGraphNode* EdNode_ParentNode = EdGraph->NodeMap[ParentNode]; + if (ParentNode->ChildrenNodes.Num() % 2 == 0) + { + UEdNode_GenericGraphNode* FirstChild = EdGraph->NodeMap[ParentNode->ChildrenNodes[0]]; + UEdNode_GenericGraphNode* LastChild = EdGraph->NodeMap[ParentNode->ChildrenNodes.Last()]; + float LeftBound = FirstChild->NodePosX; + float RightBound = LastChild->NodePosX + GetNodeWidth(LastChild); + EdNode_ParentNode->NodePosX = (LeftBound + RightBound) / 2 - GetNodeWidth(EdNode_ParentNode) / 2; + } + else + { + UEdNode_GenericGraphNode* MidChild = EdGraph->NodeMap[ParentNode->ChildrenNodes[ParentNode->ChildrenNodes.Num() / 2]]; + EdNode_ParentNode->NodePosX = MidChild->NodePosX + GetNodeWidth(MidChild) / 2 - GetNodeWidth(EdNode_ParentNode) / 2; + } +} diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/AssetEditorToolbar_GenericGraph.cpp b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/AssetEditorToolbar_GenericGraph.cpp new file mode 100644 index 00000000..1d9127cc --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/AssetEditorToolbar_GenericGraph.cpp @@ -0,0 +1,46 @@ +#include "GenericGraphAssetEditor/AssetEditorToolbar_GenericGraph.h" +#include "GenericGraphAssetEditor/AssetEditor_GenericGraph.h" +#include "GenericGraphAssetEditor/EditorCommands_GenericGraph.h" +#include "GenericGraphAssetEditor/GenericGraphEditorStyle.h" + +#define LOCTEXT_NAMESPACE "AssetEditorToolbar_GenericGraph" + +void FAssetEditorToolbar_GenericGraph::AddGenericGraphToolbar(TSharedPtr Extender) +{ + check(GenericGraphEditor.IsValid()); + TSharedPtr GenericGraphEditorPtr = GenericGraphEditor.Pin(); + + TSharedPtr ToolbarExtender = MakeShareable(new FExtender); + ToolbarExtender->AddToolBarExtension("Asset", EExtensionHook::After, GenericGraphEditorPtr->GetToolkitCommands(), FToolBarExtensionDelegate::CreateSP( this, &FAssetEditorToolbar_GenericGraph::FillGenericGraphToolbar )); + GenericGraphEditorPtr->AddToolbarExtender(ToolbarExtender); +} + +void FAssetEditorToolbar_GenericGraph::FillGenericGraphToolbar(FToolBarBuilder& ToolbarBuilder) +{ + check(GenericGraphEditor.IsValid()); + TSharedPtr GenericGraphEditorPtr = GenericGraphEditor.Pin(); + + ToolbarBuilder.BeginSection("Generic Graph"); + { + ToolbarBuilder.AddToolBarButton(FEditorCommands_GenericGraph::Get().GraphSettings, + NAME_None, + LOCTEXT("GraphSettings_Label", "Graph Settings"), + LOCTEXT("GraphSettings_ToolTip", "Show the Graph Settings"), + FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.GameSettings")); + } + ToolbarBuilder.EndSection(); + + ToolbarBuilder.BeginSection("Util"); + { + ToolbarBuilder.AddToolBarButton(FEditorCommands_GenericGraph::Get().AutoArrange, + NAME_None, + LOCTEXT("AutoArrange_Label", "Auto Arrange"), + LOCTEXT("AutoArrange_ToolTip", "Auto arrange nodes, not perfect, but still handy"), + FSlateIcon(FGenericGraphEditorStyle::GetStyleSetName(), "GenericGraphEditor.AutoArrange")); + } + ToolbarBuilder.EndSection(); + +} + + +#undef LOCTEXT_NAMESPACE diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/AssetEditor_GenericGraph.cpp b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/AssetEditor_GenericGraph.cpp new file mode 100644 index 00000000..68507d39 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/AssetEditor_GenericGraph.cpp @@ -0,0 +1,819 @@ +#include "GenericGraphAssetEditor/AssetEditor_GenericGraph.h" +#include "GenericGraphEditorPCH.h" +#include "GenericGraphAssetEditor/AssetEditorToolbar_GenericGraph.h" +#include "GenericGraphAssetEditor/AssetGraphSchema_GenericGraph.h" +#include "GenericGraphAssetEditor/EditorCommands_GenericGraph.h" +#include "GenericGraphAssetEditor/EdGraph_GenericGraph.h" +#include "AssetToolsModule.h" +#include "HAL/PlatformApplicationMisc.h" +#include "Framework/Commands/GenericCommands.h" +#include "GraphEditorActions.h" +#include "IDetailsView.h" +#include "PropertyEditorModule.h" +#include "Editor/UnrealEd/Public/Kismet2/BlueprintEditorUtils.h" +#include "Kismet2/KismetEditorUtilities.h" +#include "EdGraphUtilities.h" +#include "GenericGraphAssetEditor/EdGraph_GenericGraph.h" +#include "GenericGraphAssetEditor/EdNode_GenericGraphNode.h" +#include "GenericGraphAssetEditor/EdNode_GenericGraphEdge.h" +#include "AutoLayout/TreeLayoutStrategy.h" +#include "AutoLayout/ForceDirectedLayoutStrategy.h" + +#define LOCTEXT_NAMESPACE "AssetEditor_GenericGraph" + +const FName GenericGraphEditorAppName = FName(TEXT("GenericGraphEditorApp")); + +struct FGenericGraphAssetEditorTabs +{ + // Tab identifiers + static const FName GenericGraphPropertyID; + static const FName ViewportID; + static const FName GenericGraphEditorSettingsID; +}; + +////////////////////////////////////////////////////////////////////////// + +const FName FGenericGraphAssetEditorTabs::GenericGraphPropertyID(TEXT("GenericGraphProperty")); +const FName FGenericGraphAssetEditorTabs::ViewportID(TEXT("Viewport")); +const FName FGenericGraphAssetEditorTabs::GenericGraphEditorSettingsID(TEXT("GenericGraphEditorSettings")); + +////////////////////////////////////////////////////////////////////////// + +FAssetEditor_GenericGraph::FAssetEditor_GenericGraph() +{ + EditingGraph = nullptr; + + GenricGraphEditorSettings = NewObject(UGenericGraphEditorSettings::StaticClass()); + +#if ENGINE_MAJOR_VERSION < 5 + OnPackageSavedDelegateHandle = UPackage::PackageSavedEvent.AddRaw(this, &FAssetEditor_GenericGraph::OnPackageSaved); +#else // #if ENGINE_MAJOR_VERSION < 5 + OnPackageSavedDelegateHandle = UPackage::PackageSavedWithContextEvent.AddRaw(this, &FAssetEditor_GenericGraph::OnPackageSavedWithContext); +#endif // #else // #if ENGINE_MAJOR_VERSION < 5 +} + +FAssetEditor_GenericGraph::~FAssetEditor_GenericGraph() +{ +#if ENGINE_MAJOR_VERSION < 5 + UPackage::PackageSavedEvent.Remove(OnPackageSavedDelegateHandle); +#else // #if ENGINE_MAJOR_VERSION < 5 + UPackage::PackageSavedWithContextEvent.Remove(OnPackageSavedDelegateHandle); +#endif // #else // #if ENGINE_MAJOR_VERSION < 5 +} + +void FAssetEditor_GenericGraph::InitGenericGraphAssetEditor(const EToolkitMode::Type Mode, const TSharedPtr< IToolkitHost >& InitToolkitHost, UGenericGraph* Graph) +{ + EditingGraph = Graph; + CreateEdGraph(); + + FGenericCommands::Register(); + FGraphEditorCommands::Register(); + FEditorCommands_GenericGraph::Register(); + + if (!ToolbarBuilder.IsValid()) + { + ToolbarBuilder = MakeShareable(new FAssetEditorToolbar_GenericGraph(SharedThis(this))); + } + + BindCommands(); + + CreateInternalWidgets(); + + TSharedPtr ToolbarExtender = MakeShareable(new FExtender); + + ToolbarBuilder->AddGenericGraphToolbar(ToolbarExtender); + + // Layout + const TSharedRef StandaloneDefaultLayout = FTabManager::NewLayout("Standalone_GenericGraphEditor_Layout_v1") + ->AddArea + ( + FTabManager::NewPrimaryArea()->SetOrientation(Orient_Vertical) +#if ENGINE_MAJOR_VERSION < 5 + ->Split + ( + FTabManager::NewStack() + ->SetSizeCoefficient(0.1f) + ->AddTab(GetToolbarTabId(), ETabState::OpenedTab)->SetHideTabWell(true) + ) +#endif // #if ENGINE_MAJOR_VERSION < 5 + ->Split + ( + FTabManager::NewSplitter()->SetOrientation(Orient_Horizontal)->SetSizeCoefficient(0.9f) + ->Split + ( + FTabManager::NewStack() + ->SetSizeCoefficient(0.65f) + ->AddTab(FGenericGraphAssetEditorTabs::ViewportID, ETabState::OpenedTab)->SetHideTabWell(true) + ) + ->Split + ( + FTabManager::NewSplitter()->SetOrientation(Orient_Vertical) + ->Split + ( + FTabManager::NewStack() + ->SetSizeCoefficient(0.7f) + ->AddTab(FGenericGraphAssetEditorTabs::GenericGraphPropertyID, ETabState::OpenedTab)->SetHideTabWell(true) + ) + ->Split + ( + FTabManager::NewStack() + ->SetSizeCoefficient(0.3f) + ->AddTab(FGenericGraphAssetEditorTabs::GenericGraphEditorSettingsID, ETabState::OpenedTab) + ) + ) + ) + ); + + const bool bCreateDefaultStandaloneMenu = true; + const bool bCreateDefaultToolbar = true; + FAssetEditorToolkit::InitAssetEditor(Mode, InitToolkitHost, GenericGraphEditorAppName, StandaloneDefaultLayout, bCreateDefaultStandaloneMenu, bCreateDefaultToolbar, EditingGraph, false); + + RegenerateMenusAndToolbars(); +} + +void FAssetEditor_GenericGraph::RegisterTabSpawners(const TSharedRef& InTabManager) +{ + WorkspaceMenuCategory = InTabManager->AddLocalWorkspaceMenuCategory(LOCTEXT("WorkspaceMenu_GenericGraphEditor", "Generic Graph Editor")); + auto WorkspaceMenuCategoryRef = WorkspaceMenuCategory.ToSharedRef(); + + FAssetEditorToolkit::RegisterTabSpawners(InTabManager); + + InTabManager->RegisterTabSpawner(FGenericGraphAssetEditorTabs::ViewportID, FOnSpawnTab::CreateSP(this, &FAssetEditor_GenericGraph::SpawnTab_Viewport)) + .SetDisplayName(LOCTEXT("GraphCanvasTab", "Viewport")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "GraphEditor.EventGraph_16x")); + + InTabManager->RegisterTabSpawner(FGenericGraphAssetEditorTabs::GenericGraphPropertyID, FOnSpawnTab::CreateSP(this, &FAssetEditor_GenericGraph::SpawnTab_Details)) + .SetDisplayName(LOCTEXT("DetailsTab", "Property")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.Tabs.Details")); + + InTabManager->RegisterTabSpawner(FGenericGraphAssetEditorTabs::GenericGraphEditorSettingsID, FOnSpawnTab::CreateSP(this, &FAssetEditor_GenericGraph::SpawnTab_EditorSettings)) + .SetDisplayName(LOCTEXT("EditorSettingsTab", "Generic Graph Editor Setttings")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.Tabs.Details")); +} + +void FAssetEditor_GenericGraph::UnregisterTabSpawners(const TSharedRef& InTabManager) +{ + FAssetEditorToolkit::UnregisterTabSpawners(InTabManager); + + InTabManager->UnregisterTabSpawner(FGenericGraphAssetEditorTabs::ViewportID); + InTabManager->UnregisterTabSpawner(FGenericGraphAssetEditorTabs::GenericGraphPropertyID); + InTabManager->UnregisterTabSpawner(FGenericGraphAssetEditorTabs::GenericGraphEditorSettingsID); +} + +FName FAssetEditor_GenericGraph::GetToolkitFName() const +{ + return FName("FGenericGraphEditor"); +} + +FText FAssetEditor_GenericGraph::GetBaseToolkitName() const +{ + return LOCTEXT("GenericGraphEditorAppLabel", "Generic Graph Editor"); +} + +FText FAssetEditor_GenericGraph::GetToolkitName() const +{ + const bool bDirtyState = EditingGraph->GetOutermost()->IsDirty(); + + FFormatNamedArguments Args; + Args.Add(TEXT("GenericGraphName"), FText::FromString(EditingGraph->GetName())); + Args.Add(TEXT("DirtyState"), bDirtyState ? FText::FromString(TEXT("*")) : FText::GetEmpty()); + return FText::Format(LOCTEXT("GenericGraphEditorToolkitName", "{GenericGraphName}{DirtyState}"), Args); +} + +FText FAssetEditor_GenericGraph::GetToolkitToolTipText() const +{ + return FAssetEditorToolkit::GetToolTipTextForObject(EditingGraph); +} + +FLinearColor FAssetEditor_GenericGraph::GetWorldCentricTabColorScale() const +{ + return FLinearColor::White; +} + +FString FAssetEditor_GenericGraph::GetWorldCentricTabPrefix() const +{ + return TEXT("GenericGraphEditor"); +} + +FString FAssetEditor_GenericGraph::GetDocumentationLink() const +{ + return TEXT(""); +} + +void FAssetEditor_GenericGraph::SaveAsset_Execute() +{ + if (EditingGraph != nullptr) + { + RebuildGenericGraph(); + } + + FAssetEditorToolkit::SaveAsset_Execute(); +} + +void FAssetEditor_GenericGraph::AddReferencedObjects(FReferenceCollector& Collector) +{ + Collector.AddReferencedObject(EditingGraph); + Collector.AddReferencedObject(EditingGraph->EdGraph); +} + +UGenericGraphEditorSettings* FAssetEditor_GenericGraph::GetSettings() const +{ + return GenricGraphEditorSettings; +} + +TSharedRef FAssetEditor_GenericGraph::SpawnTab_Viewport(const FSpawnTabArgs& Args) +{ + check(Args.GetTabId() == FGenericGraphAssetEditorTabs::ViewportID); + + TSharedRef SpawnedTab = SNew(SDockTab) + .Label(LOCTEXT("ViewportTab_Title", "Viewport")); + + if (ViewportWidget.IsValid()) + { + SpawnedTab->SetContent(ViewportWidget.ToSharedRef()); + } + + return SpawnedTab; +} + +TSharedRef FAssetEditor_GenericGraph::SpawnTab_Details(const FSpawnTabArgs& Args) +{ + check(Args.GetTabId() == FGenericGraphAssetEditorTabs::GenericGraphPropertyID); + + return SNew(SDockTab) +#if ENGINE_MAJOR_VERSION < 5 + .Icon(FAppStyle::GetBrush("LevelEditor.Tabs.Details")) +#endif // #if ENGINE_MAJOR_VERSION < 5 + .Label(LOCTEXT("Details_Title", "Property")) + [ + PropertyWidget.ToSharedRef() + ]; +} + +TSharedRef FAssetEditor_GenericGraph::SpawnTab_EditorSettings(const FSpawnTabArgs& Args) +{ + check(Args.GetTabId() == FGenericGraphAssetEditorTabs::GenericGraphEditorSettingsID); + + return SNew(SDockTab) +#if ENGINE_MAJOR_VERSION < 5 + .Icon(FAppStyle::GetBrush("LevelEditor.Tabs.Details")) +#endif // #if ENGINE_MAJOR_VERSION < 5 + .Label(LOCTEXT("EditorSettings_Title", "Generic Graph Editor Setttings")) + [ + EditorSettingsWidget.ToSharedRef() + ]; +} + +void FAssetEditor_GenericGraph::CreateInternalWidgets() +{ + ViewportWidget = CreateViewportWidget(); + + FDetailsViewArgs Args; + Args.bHideSelectionTip = true; + Args.NotifyHook = this; + + FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor"); + PropertyWidget = PropertyModule.CreateDetailView(Args); + PropertyWidget->SetObject(EditingGraph); + PropertyWidget->OnFinishedChangingProperties().AddSP(this, &FAssetEditor_GenericGraph::OnFinishedChangingProperties); + + EditorSettingsWidget = PropertyModule.CreateDetailView(Args); + EditorSettingsWidget->SetObject(GenricGraphEditorSettings); +} + +TSharedRef FAssetEditor_GenericGraph::CreateViewportWidget() +{ + FGraphAppearanceInfo AppearanceInfo; + AppearanceInfo.CornerText = LOCTEXT("AppearanceCornerText_GenericGraph", "Generic Graph"); + + CreateCommandList(); + + SGraphEditor::FGraphEditorEvents InEvents; + InEvents.OnSelectionChanged = SGraphEditor::FOnSelectionChanged::CreateSP(this, &FAssetEditor_GenericGraph::OnSelectedNodesChanged); + InEvents.OnNodeDoubleClicked = FSingleNodeEvent::CreateSP(this, &FAssetEditor_GenericGraph::OnNodeDoubleClicked); + + return SNew(SGraphEditor) + .AdditionalCommands(GraphEditorCommands) + .IsEditable(true) + .Appearance(AppearanceInfo) + .GraphToEdit(EditingGraph->EdGraph) + .GraphEvents(InEvents) + .AutoExpandActionMenu(true) + .ShowGraphStateOverlay(false); +} + +void FAssetEditor_GenericGraph::BindCommands() +{ + ToolkitCommands->MapAction(FEditorCommands_GenericGraph::Get().GraphSettings, + FExecuteAction::CreateSP(this, &FAssetEditor_GenericGraph::GraphSettings), + FCanExecuteAction::CreateSP(this, &FAssetEditor_GenericGraph::CanGraphSettings) + ); + + ToolkitCommands->MapAction(FEditorCommands_GenericGraph::Get().AutoArrange, + FExecuteAction::CreateSP(this, &FAssetEditor_GenericGraph::AutoArrange), + FCanExecuteAction::CreateSP(this, &FAssetEditor_GenericGraph::CanAutoArrange) + ); +} + +void FAssetEditor_GenericGraph::CreateEdGraph() +{ + if (EditingGraph->EdGraph == nullptr) + { + EditingGraph->EdGraph = CastChecked(FBlueprintEditorUtils::CreateNewGraph(EditingGraph, NAME_None, UEdGraph_GenericGraph::StaticClass(), UAssetGraphSchema_GenericGraph::StaticClass())); + EditingGraph->EdGraph->bAllowDeletion = false; + + // Give the schema a chance to fill out any required nodes (like the results node) + const UEdGraphSchema* Schema = EditingGraph->EdGraph->GetSchema(); + Schema->CreateDefaultNodesForGraph(*EditingGraph->EdGraph); + } +} + +void FAssetEditor_GenericGraph::CreateCommandList() +{ + if (GraphEditorCommands.IsValid()) + { + return; + } + + GraphEditorCommands = MakeShareable(new FUICommandList); + + // Can't use CreateSP here because derived editor are already implementing TSharedFromThis + // however it should be safe, since commands are being used only within this editor + // if it ever crashes, this function will have to go away and be reimplemented in each derived class + + GraphEditorCommands->MapAction(FEditorCommands_GenericGraph::Get().GraphSettings, + FExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::GraphSettings), + FCanExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::CanGraphSettings)); + + GraphEditorCommands->MapAction(FEditorCommands_GenericGraph::Get().AutoArrange, + FExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::AutoArrange), + FCanExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::CanAutoArrange)); + + GraphEditorCommands->MapAction(FGenericCommands::Get().SelectAll, + FExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::SelectAllNodes), + FCanExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::CanSelectAllNodes) + ); + + GraphEditorCommands->MapAction(FGenericCommands::Get().Delete, + FExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::DeleteSelectedNodes), + FCanExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::CanDeleteNodes) + ); + + GraphEditorCommands->MapAction(FGenericCommands::Get().Copy, + FExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::CopySelectedNodes), + FCanExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::CanCopyNodes) + ); + + GraphEditorCommands->MapAction(FGenericCommands::Get().Cut, + FExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::CutSelectedNodes), + FCanExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::CanCutNodes) + ); + + GraphEditorCommands->MapAction(FGenericCommands::Get().Paste, + FExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::PasteNodes), + FCanExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::CanPasteNodes) + ); + + GraphEditorCommands->MapAction(FGenericCommands::Get().Duplicate, + FExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::DuplicateNodes), + FCanExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::CanDuplicateNodes) + ); + + GraphEditorCommands->MapAction(FGenericCommands::Get().Rename, + FExecuteAction::CreateSP(this, &FAssetEditor_GenericGraph::OnRenameNode), + FCanExecuteAction::CreateSP(this, &FAssetEditor_GenericGraph::CanRenameNodes) + ); +} + +TSharedPtr FAssetEditor_GenericGraph::GetCurrGraphEditor() const +{ + return ViewportWidget; +} + +FGraphPanelSelectionSet FAssetEditor_GenericGraph::GetSelectedNodes() const +{ + FGraphPanelSelectionSet CurrentSelection; + TSharedPtr FocusedGraphEd = GetCurrGraphEditor(); + if (FocusedGraphEd.IsValid()) + { + CurrentSelection = FocusedGraphEd->GetSelectedNodes(); + } + + return CurrentSelection; +} + +void FAssetEditor_GenericGraph::RebuildGenericGraph() +{ + if (EditingGraph == nullptr) + { + LOG_WARNING(TEXT("FGenericGraphAssetEditor::RebuildGenericGraph EditingGraph is nullptr")); + return; + } + + UEdGraph_GenericGraph* EdGraph = Cast(EditingGraph->EdGraph); + check(EdGraph != nullptr); + + EdGraph->RebuildGenericGraph(); +} + +void FAssetEditor_GenericGraph::SelectAllNodes() +{ + TSharedPtr CurrentGraphEditor = GetCurrGraphEditor(); + if (CurrentGraphEditor.IsValid()) + { + CurrentGraphEditor->SelectAllNodes(); + } +} + +bool FAssetEditor_GenericGraph::CanSelectAllNodes() +{ + return true; +} + +void FAssetEditor_GenericGraph::DeleteSelectedNodes() +{ + TSharedPtr CurrentGraphEditor = GetCurrGraphEditor(); + 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(*NodeIt); + if (EdNode == nullptr || !EdNode->CanUserDeleteNode()) + continue;; + + if (UEdNode_GenericGraphNode* EdNode_Node = Cast(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(); + } + } +} + +bool FAssetEditor_GenericGraph::CanDeleteNodes() +{ + // If any of the nodes can be deleted then we should allow deleting + const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes(); + for (FGraphPanelSelectionSet::TConstIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter) + { + UEdGraphNode* Node = Cast(*SelectedIter); + if (Node != nullptr && Node->CanUserDeleteNode()) + { + return true; + } + } + + return false; +} + +void FAssetEditor_GenericGraph::DeleteSelectedDuplicatableNodes() +{ + TSharedPtr CurrentGraphEditor = GetCurrGraphEditor(); + if (!CurrentGraphEditor.IsValid()) + { + return; + } + + const FGraphPanelSelectionSet OldSelectedNodes = CurrentGraphEditor->GetSelectedNodes(); + CurrentGraphEditor->ClearSelectionSet(); + + for (FGraphPanelSelectionSet::TConstIterator SelectedIter(OldSelectedNodes); SelectedIter; ++SelectedIter) + { + UEdGraphNode* Node = Cast(*SelectedIter); + if (Node && Node->CanDuplicateNode()) + { + CurrentGraphEditor->SetNodeSelection(Node, true); + } + } + + // Delete the duplicatable nodes + DeleteSelectedNodes(); + + CurrentGraphEditor->ClearSelectionSet(); + + for (FGraphPanelSelectionSet::TConstIterator SelectedIter(OldSelectedNodes); SelectedIter; ++SelectedIter) + { + if (UEdGraphNode* Node = Cast(*SelectedIter)) + { + CurrentGraphEditor->SetNodeSelection(Node, true); + } + } +} + +void FAssetEditor_GenericGraph::CutSelectedNodes() +{ + CopySelectedNodes(); + DeleteSelectedDuplicatableNodes(); +} + +bool FAssetEditor_GenericGraph::CanCutNodes() +{ + return CanCopyNodes() && CanDeleteNodes(); +} + +void FAssetEditor_GenericGraph::CopySelectedNodes() +{ + // Export the selected nodes and place the text on the clipboard + FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes(); + + FString ExportedText; + + for (FGraphPanelSelectionSet::TIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter) + { + UEdGraphNode* Node = Cast(*SelectedIter); + if (Node == nullptr) + { + SelectedIter.RemoveCurrent(); + continue; + } + + if (UEdNode_GenericGraphEdge* EdNode_Edge = Cast(*SelectedIter)) + { + UEdNode_GenericGraphNode* StartNode = EdNode_Edge->GetStartNode(); + UEdNode_GenericGraphNode* 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_GenericGraph::CanCopyNodes() +{ + // If any of the nodes can be duplicated then we should allow copying + const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes(); + for (FGraphPanelSelectionSet::TConstIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter) + { + UEdGraphNode* Node = Cast(*SelectedIter); + if (Node && Node->CanDuplicateNode()) + { + return true; + } + } + + return false; +} + +void FAssetEditor_GenericGraph::PasteNodes() +{ + TSharedPtr CurrentGraphEditor = GetCurrGraphEditor(); + if (CurrentGraphEditor.IsValid()) + { + PasteNodesHere(CurrentGraphEditor->GetPasteLocation()); + } +} + +void FAssetEditor_GenericGraph::PasteNodesHere(const FVector2D& Location) +{ + // Find the graph editor with focus + TSharedPtr CurrentGraphEditor = GetCurrGraphEditor(); + 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 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::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; + + for (TSet::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(); + } + } + + // Update UI + CurrentGraphEditor->NotifyGraphChanged(); + + UObject* GraphOwner = EdGraph->GetOuter(); + if (GraphOwner) + { + GraphOwner->PostEditChange(); + GraphOwner->MarkPackageDirty(); + } +} + +bool FAssetEditor_GenericGraph::CanPasteNodes() +{ + TSharedPtr CurrentGraphEditor = GetCurrGraphEditor(); + if (!CurrentGraphEditor.IsValid()) + { + return false; + } + + FString ClipboardContent; + FPlatformApplicationMisc::ClipboardPaste(ClipboardContent); + + return FEdGraphUtilities::CanImportNodesFromText(CurrentGraphEditor->GetCurrentGraph(), ClipboardContent); +} + +void FAssetEditor_GenericGraph::DuplicateNodes() +{ + CopySelectedNodes(); + PasteNodes(); +} + +bool FAssetEditor_GenericGraph::CanDuplicateNodes() +{ + return CanCopyNodes(); +} + +void FAssetEditor_GenericGraph::GraphSettings() +{ + PropertyWidget->SetObject(EditingGraph); +} + +bool FAssetEditor_GenericGraph::CanGraphSettings() const +{ + return true; +} + +void FAssetEditor_GenericGraph::AutoArrange() +{ + UEdGraph_GenericGraph* EdGraph = Cast(EditingGraph->EdGraph); + check(EdGraph != nullptr); + + const FScopedTransaction Transaction(LOCTEXT("GenericGraphEditorAutoArrange", "Generic Graph Editor: Auto Arrange")); + + EdGraph->Modify(); + + UAutoLayoutStrategy* LayoutStrategy = nullptr; + switch (GenricGraphEditorSettings->AutoLayoutStrategy) + { + case EAutoLayoutStrategy::Tree: + LayoutStrategy = NewObject(EdGraph, UTreeLayoutStrategy::StaticClass()); + break; + case EAutoLayoutStrategy::ForceDirected: + LayoutStrategy = NewObject(EdGraph, UForceDirectedLayoutStrategy::StaticClass()); + break; + default: + break; + } + + if (LayoutStrategy != nullptr) + { + LayoutStrategy->Settings = GenricGraphEditorSettings; + LayoutStrategy->Layout(EdGraph); + LayoutStrategy->ConditionalBeginDestroy(); + } + else + { + LOG_ERROR(TEXT("FAssetEditor_GenericGraph::AutoArrange LayoutStrategy is null.")); + } +} + +bool FAssetEditor_GenericGraph::CanAutoArrange() const +{ + return EditingGraph != nullptr && Cast(EditingGraph->EdGraph) != nullptr; +} + +void FAssetEditor_GenericGraph::OnRenameNode() +{ + TSharedPtr CurrentGraphEditor = GetCurrGraphEditor(); + if (CurrentGraphEditor.IsValid()) + { + const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes(); + for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt) + { + UEdGraphNode* SelectedNode = Cast(*NodeIt); + if (SelectedNode != NULL && SelectedNode->bCanRenameNode) + { + CurrentGraphEditor->IsNodeTitleVisible(SelectedNode, true); + break; + } + } + } +} + +bool FAssetEditor_GenericGraph::CanRenameNodes() const +{ + UEdGraph_GenericGraph* EdGraph = Cast(EditingGraph->EdGraph); + check(EdGraph != nullptr); + + UGenericGraph* Graph = EdGraph->GetGenericGraph(); + check(Graph != nullptr) + + return Graph->bCanRenameNode && GetSelectedNodes().Num() == 1; +} + +void FAssetEditor_GenericGraph::OnSelectedNodesChanged(const TSet& NewSelection) +{ + TArray Selection; + + for (UObject* SelectionEntry : NewSelection) + { + Selection.Add(SelectionEntry); + } + + if (Selection.Num() == 0) + { + PropertyWidget->SetObject(EditingGraph); + + } + else + { + PropertyWidget->SetObjects(Selection); + } +} + +void FAssetEditor_GenericGraph::OnNodeDoubleClicked(UEdGraphNode* Node) +{ + +} + +void FAssetEditor_GenericGraph::OnFinishedChangingProperties(const FPropertyChangedEvent& PropertyChangedEvent) +{ + if (EditingGraph == nullptr) + return; + + EditingGraph->EdGraph->GetSchema()->ForceVisualizationCacheClear(); +} + +#if ENGINE_MAJOR_VERSION < 5 +void FAssetEditor_GenericGraph::OnPackageSaved(const FString& PackageFileName, UObject* Outer) +{ + RebuildGenericGraph(); +} +#else // #if ENGINE_MAJOR_VERSION < 5 +void FAssetEditor_GenericGraph::OnPackageSavedWithContext(const FString& PackageFileName, UPackage* Package, FObjectPostSaveContext ObjectSaveContext) +{ + RebuildGenericGraph(); +} +#endif // #else // #if ENGINE_MAJOR_VERSION < 5 + +void FAssetEditor_GenericGraph::RegisterToolbarTab(const TSharedRef& InTabManager) +{ + FAssetEditorToolkit::RegisterTabSpawners(InTabManager); +} + + +#undef LOCTEXT_NAMESPACE + diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/AssetGraphSchema_GenericGraph.cpp b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/AssetGraphSchema_GenericGraph.cpp new file mode 100644 index 00000000..8c873f8d --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/AssetGraphSchema_GenericGraph.cpp @@ -0,0 +1,476 @@ +#include "GenericGraphAssetEditor/AssetGraphSchema_GenericGraph.h" +#include "ToolMenus.h" +#include "GenericGraphEditorPCH.h" +#include "GenericGraphAssetEditor/EdNode_GenericGraphNode.h" +#include "GenericGraphAssetEditor/EdNode_GenericGraphEdge.h" +#include "GenericGraphAssetEditor/ConnectionDrawingPolicy_GenericGraph.h" +#include "GraphEditorActions.h" +#include "Framework/Commands/GenericCommands.h" +#include "AutoLayout/ForceDirectedLayoutStrategy.h" +#include "AutoLayout/TreeLayoutStrategy.h" + +#define LOCTEXT_NAMESPACE "AssetSchema_GenericGraph" + +int32 UAssetGraphSchema_GenericGraph::CurrentCacheRefreshID = 0; + + +class FNodeVisitorCycleChecker +{ +public: + /** Check whether a loop in the graph would be caused by linking the passed-in nodes */ + bool CheckForLoop(UEdGraphNode* StartNode, UEdGraphNode* EndNode) + { + + VisitedNodes.Add(StartNode); + + return TraverseNodes(EndNode); + } + +private: + bool TraverseNodes(UEdGraphNode* Node) + { + VisitedNodes.Add(Node); + + for (auto MyPin : Node->Pins) + { + if (MyPin->Direction == EGPD_Output) + { + for (auto OtherPin : MyPin->LinkedTo) + { + UEdGraphNode* OtherNode = OtherPin->GetOwningNode(); + if (VisitedNodes.Contains(OtherNode)) + { + // Only an issue if this is a back-edge + return false; + } + else if (!FinishedNodes.Contains(OtherNode)) + { + // Only should traverse if this node hasn't been traversed + if (!TraverseNodes(OtherNode)) + return false; + } + } + } + } + + VisitedNodes.Remove(Node); + FinishedNodes.Add(Node); + return true; + }; + + + TSet VisitedNodes; + TSet FinishedNodes; +}; + +UEdGraphNode* FAssetSchemaAction_GenericGraph_NewNode::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode /*= true*/) +{ + UEdGraphNode* ResultNode = nullptr; + + if (NodeTemplate != nullptr) + { + const FScopedTransaction Transaction(LOCTEXT("GenericGraphEditorNewNode", "Generic Graph Editor: New Node")); + ParentGraph->Modify(); + if (FromPin != nullptr) + FromPin->Modify(); + + NodeTemplate->Rename(nullptr, ParentGraph); + ParentGraph->AddNode(NodeTemplate, true, bSelectNewNode); + + NodeTemplate->CreateNewGuid(); + NodeTemplate->PostPlacedNewNode(); + NodeTemplate->AllocateDefaultPins(); + NodeTemplate->AutowireNewNode(FromPin); + + NodeTemplate->NodePosX = Location.X; + NodeTemplate->NodePosY = Location.Y; + + NodeTemplate->GenericGraphNode->SetFlags(RF_Transactional); + NodeTemplate->SetFlags(RF_Transactional); + + ResultNode = NodeTemplate; + } + + return ResultNode; +} + +void FAssetSchemaAction_GenericGraph_NewNode::AddReferencedObjects(FReferenceCollector& Collector) +{ + FEdGraphSchemaAction::AddReferencedObjects(Collector); + Collector.AddReferencedObject(NodeTemplate); +} + +UEdGraphNode* FAssetSchemaAction_GenericGraph_NewEdge::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode /*= true*/) +{ + UEdGraphNode* ResultNode = nullptr; + + if (NodeTemplate != nullptr) + { + const FScopedTransaction Transaction(LOCTEXT("GenericGraphEditorNewEdge", "Generic Graph Editor: New Edge")); + ParentGraph->Modify(); + if (FromPin != nullptr) + FromPin->Modify(); + + NodeTemplate->Rename(nullptr, ParentGraph); + ParentGraph->AddNode(NodeTemplate, true, bSelectNewNode); + + NodeTemplate->CreateNewGuid(); + NodeTemplate->PostPlacedNewNode(); + NodeTemplate->AllocateDefaultPins(); + NodeTemplate->AutowireNewNode(FromPin); + + NodeTemplate->NodePosX = Location.X; + NodeTemplate->NodePosY = Location.Y; + + NodeTemplate->GenericGraphEdge->SetFlags(RF_Transactional); + NodeTemplate->SetFlags(RF_Transactional); + + ResultNode = NodeTemplate; + } + + return ResultNode; +} + +void FAssetSchemaAction_GenericGraph_NewEdge::AddReferencedObjects(FReferenceCollector& Collector) +{ + FEdGraphSchemaAction::AddReferencedObjects(Collector); + Collector.AddReferencedObject(NodeTemplate); +} + +void UAssetGraphSchema_GenericGraph::GetBreakLinkToSubMenuActions(UToolMenu* Menu, UEdGraphPin* InGraphPin) +{ + // Make sure we have a unique name for every entry in the list + TMap< FString, uint32 > LinkTitleCount; + + FToolMenuSection& Section = Menu->FindOrAddSection("GenericGraphAssetGraphSchemaPinActions"); + + // Add all the links we could break from + for (TArray::TConstIterator Links(InGraphPin->LinkedTo); Links; ++Links) + { + UEdGraphPin* Pin = *Links; + FString TitleString = Pin->GetOwningNode()->GetNodeTitle(ENodeTitleType::ListView).ToString(); + FText Title = FText::FromString(TitleString); + if (Pin->PinName != TEXT("")) + { + TitleString = FString::Printf(TEXT("%s (%s)"), *TitleString, *Pin->PinName.ToString()); + + // Add name of connection if possible + FFormatNamedArguments Args; + Args.Add(TEXT("NodeTitle"), Title); + Args.Add(TEXT("PinName"), Pin->GetDisplayName()); + Title = FText::Format(LOCTEXT("BreakDescPin", "{NodeTitle} ({PinName})"), Args); + } + + uint32& Count = LinkTitleCount.FindOrAdd(TitleString); + + FText Description; + FFormatNamedArguments Args; + Args.Add(TEXT("NodeTitle"), Title); + Args.Add(TEXT("NumberOfNodes"), Count); + + if (Count == 0) + { + Description = FText::Format(LOCTEXT("BreakDesc", "Break link to {NodeTitle}"), Args); + } + else + { + Description = FText::Format(LOCTEXT("BreakDescMulti", "Break link to {NodeTitle} ({NumberOfNodes})"), Args); + } + ++Count; + + Section.AddMenuEntry(NAME_None, Description, Description, FSlateIcon(), FUIAction( + FExecuteAction::CreateUObject(this, &UAssetGraphSchema_GenericGraph::BreakSinglePinLink, const_cast(InGraphPin), *Links))); + } +} + +EGraphType UAssetGraphSchema_GenericGraph::GetGraphType(const UEdGraph* TestEdGraph) const +{ + return GT_StateMachine; +} + +void UAssetGraphSchema_GenericGraph::GetGraphContextActions(FGraphContextMenuBuilder& ContextMenuBuilder) const +{ + UGenericGraph* Graph = CastChecked(ContextMenuBuilder.CurrentGraph->GetOuter()); + + if (Graph->NodeType == nullptr) + { + return; + } + + const bool bNoParent = (ContextMenuBuilder.FromPin == NULL); + + const FText AddToolTip = LOCTEXT("NewGenericGraphNodeTooltip", "Add node here"); + + TSet > Visited; + + FText Desc = Graph->NodeType.GetDefaultObject()->ContextMenuName; + + if (Desc.IsEmpty()) + { + FString Title = Graph->NodeType->GetName(); + Title.RemoveFromEnd("_C"); + Desc = FText::FromString(Title); + } + + if (!Graph->NodeType->HasAnyClassFlags(CLASS_Abstract)) + { + TSharedPtr NewNodeAction(new FAssetSchemaAction_GenericGraph_NewNode(LOCTEXT("GenericGraphNodeAction", "Generic Graph Node"), Desc, AddToolTip, 0)); + NewNodeAction->NodeTemplate = NewObject(ContextMenuBuilder.OwnerOfTemporaries); + NewNodeAction->NodeTemplate->GenericGraphNode = NewObject(NewNodeAction->NodeTemplate, Graph->NodeType); + NewNodeAction->NodeTemplate->GenericGraphNode->Graph = Graph; + ContextMenuBuilder.AddAction(NewNodeAction); + + Visited.Add(Graph->NodeType); + } + + for (TObjectIterator It; It; ++It) + { + if (It->IsChildOf(Graph->NodeType) && !It->HasAnyClassFlags(CLASS_Abstract) && !Visited.Contains(*It)) + { + TSubclassOf NodeType = *It; + + if (It->GetName().StartsWith("REINST") || It->GetName().StartsWith("SKEL")) + continue; + + if (!Graph->GetClass()->IsChildOf(NodeType.GetDefaultObject()->CompatibleGraphType)) + continue; + + Desc = NodeType.GetDefaultObject()->ContextMenuName; + + if (Desc.IsEmpty()) + { + FString Title = NodeType->GetName(); + Title.RemoveFromEnd("_C"); + Desc = FText::FromString(Title); + } + + TSharedPtr Action(new FAssetSchemaAction_GenericGraph_NewNode(LOCTEXT("GenericGraphNodeAction", "Generic Graph Node"), Desc, AddToolTip, 0)); + Action->NodeTemplate = NewObject(ContextMenuBuilder.OwnerOfTemporaries); + Action->NodeTemplate->GenericGraphNode = NewObject(Action->NodeTemplate, NodeType); + Action->NodeTemplate->GenericGraphNode->Graph = Graph; + ContextMenuBuilder.AddAction(Action); + + Visited.Add(NodeType); + } + } +} + +void UAssetGraphSchema_GenericGraph::GetContextMenuActions(UToolMenu* Menu, UGraphNodeContextMenuContext* Context) const +{ + if (Context->Pin) + { + { + FToolMenuSection& Section = Menu->AddSection("GenericGraphAssetGraphSchemaNodeActions", LOCTEXT("PinActionsMenuHeader", "Pin Actions")); + // Only display the 'Break Links' option if there is a link to break! + if (Context->Pin->LinkedTo.Num() > 0) + { + Section.AddMenuEntry(FGraphEditorCommands::Get().BreakPinLinks); + + // add sub menu for break link to + if (Context->Pin->LinkedTo.Num() > 1) + { + Section.AddSubMenu( + "BreakLinkTo", + LOCTEXT("BreakLinkTo", "Break Link To..."), + LOCTEXT("BreakSpecificLinks", "Break a specific link..."), + FNewToolMenuDelegate::CreateUObject((UAssetGraphSchema_GenericGraph* const)this, &UAssetGraphSchema_GenericGraph::GetBreakLinkToSubMenuActions, const_cast(Context->Pin))); + } + else + { + ((UAssetGraphSchema_GenericGraph* const)this)->GetBreakLinkToSubMenuActions(Menu, const_cast(Context->Pin)); + } + } + } + } + else if (Context->Node) + { + { + FToolMenuSection& Section = Menu->AddSection("GenericGraphAssetGraphSchemaNodeActions", LOCTEXT("ClassActionsMenuHeader", "Node Actions")); + Section.AddMenuEntry(FGenericCommands::Get().Delete); + Section.AddMenuEntry(FGenericCommands::Get().Cut); + Section.AddMenuEntry(FGenericCommands::Get().Copy); + Section.AddMenuEntry(FGenericCommands::Get().Duplicate); + + Section.AddMenuEntry(FGraphEditorCommands::Get().BreakNodeLinks); + } + } + + Super::GetContextMenuActions(Menu, Context); +} + +const FPinConnectionResponse UAssetGraphSchema_GenericGraph::CanCreateConnection(const UEdGraphPin* A, const UEdGraphPin* B) const +{ + // Make sure the pins are not on the same node + if (A->GetOwningNode() == B->GetOwningNode()) + { + return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("PinErrorSameNode", "Can't connect node to itself")); + } + + const UEdGraphPin *Out = A; + const UEdGraphPin *In = B; + + UEdNode_GenericGraphNode* EdNode_Out = Cast(Out->GetOwningNode()); + UEdNode_GenericGraphNode* EdNode_In = Cast(In->GetOwningNode()); + + if (EdNode_Out == nullptr || EdNode_In == nullptr) + { + return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("PinError", "Not a valid UGenericGraphEdNode")); + } + + //Determine if we can have cycles or not + bool bAllowCycles = false; + auto EdGraph = Cast(Out->GetOwningNode()->GetGraph()); + if (EdGraph != nullptr) + { + bAllowCycles = EdGraph->GetGenericGraph()->bCanBeCyclical; + } + + // check for cycles + FNodeVisitorCycleChecker CycleChecker; + if (!bAllowCycles && !CycleChecker.CheckForLoop(Out->GetOwningNode(), In->GetOwningNode())) + { + return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("PinErrorCycle", "Can't create a graph cycle")); + } + + FText ErrorMessage; + if (!EdNode_Out->GenericGraphNode->CanCreateConnectionTo(EdNode_In->GenericGraphNode, EdNode_Out->GetOutputPin()->LinkedTo.Num(), ErrorMessage)) + { + return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, ErrorMessage); + } + if (!EdNode_In->GenericGraphNode->CanCreateConnectionFrom(EdNode_Out->GenericGraphNode, EdNode_In->GetInputPin()->LinkedTo.Num(), ErrorMessage)) + { + return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, ErrorMessage); + } + + + if (EdNode_Out->GenericGraphNode->GetGraph()->bEdgeEnabled) + { + return FPinConnectionResponse(CONNECT_RESPONSE_MAKE_WITH_CONVERSION_NODE, LOCTEXT("PinConnect", "Connect nodes with edge")); + } + else + { + return FPinConnectionResponse(CONNECT_RESPONSE_MAKE, LOCTEXT("PinConnect", "Connect nodes")); + } +} + +bool UAssetGraphSchema_GenericGraph::TryCreateConnection(UEdGraphPin* A, UEdGraphPin* B) const +{ + // We don't actually care about the pin, we want the node that is being dragged between + UEdNode_GenericGraphNode* NodeA = Cast(A->GetOwningNode()); + UEdNode_GenericGraphNode* NodeB = Cast(B->GetOwningNode()); + + // Check that this edge doesn't already exist + for (UEdGraphPin *TestPin : NodeA->GetOutputPin()->LinkedTo) + { + UEdGraphNode* ChildNode = TestPin->GetOwningNode(); + if (UEdNode_GenericGraphEdge* EdNode_Edge = Cast(ChildNode)) + { + ChildNode = EdNode_Edge->GetEndNode(); + } + + if (ChildNode == NodeB) + return false; + } + + if (NodeA && NodeB) + { + // Always create connections from node A to B, don't allow adding in reverse + Super::TryCreateConnection(NodeA->GetOutputPin(), NodeB->GetInputPin()); + return true; + } + else + { + return false; + } +} + +bool UAssetGraphSchema_GenericGraph::CreateAutomaticConversionNodeAndConnections(UEdGraphPin* A, UEdGraphPin* B) const +{ + UEdNode_GenericGraphNode* NodeA = Cast(A->GetOwningNode()); + UEdNode_GenericGraphNode* NodeB = Cast(B->GetOwningNode()); + + // Are nodes and pins all valid? + if (!NodeA || !NodeA->GetOutputPin() || !NodeB || !NodeB->GetInputPin()) + return false; + + UGenericGraph* Graph = NodeA->GenericGraphNode->GetGraph(); + + FVector2D InitPos((NodeA->NodePosX + NodeB->NodePosX) / 2, (NodeA->NodePosY + NodeB->NodePosY) / 2); + + FAssetSchemaAction_GenericGraph_NewEdge Action; + Action.NodeTemplate = NewObject(NodeA->GetGraph()); + Action.NodeTemplate->SetEdge(NewObject(Action.NodeTemplate, Graph->EdgeType)); + UEdNode_GenericGraphEdge* EdgeNode = Cast(Action.PerformAction(NodeA->GetGraph(), nullptr, InitPos, false)); + + // Always create connections from node A to B, don't allow adding in reverse + EdgeNode->CreateConnections(NodeA, NodeB); + + return true; +} + +class FConnectionDrawingPolicy* UAssetGraphSchema_GenericGraph::CreateConnectionDrawingPolicy(int32 InBackLayerID, int32 InFrontLayerID, float InZoomFactor, const FSlateRect& InClippingRect, class FSlateWindowElementList& InDrawElements, class UEdGraph* InGraphObj) const +{ + return new FConnectionDrawingPolicy_GenericGraph(InBackLayerID, InFrontLayerID, InZoomFactor, InClippingRect, InDrawElements, InGraphObj); +} + +FLinearColor UAssetGraphSchema_GenericGraph::GetPinTypeColor(const FEdGraphPinType& PinType) const +{ + return FColor::White; +} + +void UAssetGraphSchema_GenericGraph::BreakNodeLinks(UEdGraphNode& TargetNode) const +{ + const FScopedTransaction Transaction(NSLOCTEXT("UnrealEd", "GraphEd_BreakNodeLinks", "Break Node Links")); + + Super::BreakNodeLinks(TargetNode); +} + +void UAssetGraphSchema_GenericGraph::BreakPinLinks(UEdGraphPin& TargetPin, bool bSendsNodeNotifcation) const +{ + const FScopedTransaction Transaction(NSLOCTEXT("UnrealEd", "GraphEd_BreakPinLinks", "Break Pin Links")); + + Super::BreakPinLinks(TargetPin, bSendsNodeNotifcation); +} + +void UAssetGraphSchema_GenericGraph::BreakSinglePinLink(UEdGraphPin* SourcePin, UEdGraphPin* TargetPin) const +{ + const FScopedTransaction Transaction(NSLOCTEXT("UnrealEd", "GraphEd_BreakSinglePinLink", "Break Pin Link")); + + Super::BreakSinglePinLink(SourcePin, TargetPin); +} + +UEdGraphPin* UAssetGraphSchema_GenericGraph::DropPinOnNode(UEdGraphNode* InTargetNode, const FName& InSourcePinName, const FEdGraphPinType& InSourcePinType, EEdGraphPinDirection InSourcePinDirection) const +{ + UEdNode_GenericGraphNode* EdNode = Cast(InTargetNode); + switch (InSourcePinDirection) + { + case EGPD_Input: + return EdNode->GetOutputPin(); + case EGPD_Output: + return EdNode->GetInputPin(); + default: + return nullptr; + } +} + +bool UAssetGraphSchema_GenericGraph::SupportsDropPinOnNode(UEdGraphNode* InTargetNode, const FEdGraphPinType& InSourcePinType, EEdGraphPinDirection InSourcePinDirection, FText& OutErrorMessage) const +{ + return Cast(InTargetNode) != nullptr; +} + +bool UAssetGraphSchema_GenericGraph::IsCacheVisualizationOutOfDate(int32 InVisualizationCacheID) const +{ + return CurrentCacheRefreshID != InVisualizationCacheID; +} + +int32 UAssetGraphSchema_GenericGraph::GetCurrentVisualizationCacheID() const +{ + return CurrentCacheRefreshID; +} + +void UAssetGraphSchema_GenericGraph::ForceVisualizationCacheClear() const +{ + ++CurrentCacheRefreshID; +} + +#undef LOCTEXT_NAMESPACE diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/ConnectionDrawingPolicy_GenericGraph.cpp b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/ConnectionDrawingPolicy_GenericGraph.cpp new file mode 100644 index 00000000..a727293d --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/ConnectionDrawingPolicy_GenericGraph.cpp @@ -0,0 +1,149 @@ +#include "GenericGraphAssetEditor/ConnectionDrawingPolicy_GenericGraph.h" +#include "GenericGraphAssetEditor/EdNode_GenericGraphNode.h" +#include "GenericGraphAssetEditor/EdNode_GenericGraphEdge.h" + +FConnectionDrawingPolicy_GenericGraph::FConnectionDrawingPolicy_GenericGraph(int32 InBackLayerID, int32 InFrontLayerID, float ZoomFactor, const FSlateRect& InClippingRect, FSlateWindowElementList& InDrawElements, UEdGraph* InGraphObj) + : FConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements) + , GraphObj(InGraphObj) +{ +} + +void FConnectionDrawingPolicy_GenericGraph::DetermineWiringStyle(UEdGraphPin* OutputPin, UEdGraphPin* InputPin, /*inout*/ FConnectionParams& Params) +{ + Params.AssociatedPin1 = OutputPin; + Params.AssociatedPin2 = InputPin; + Params.WireThickness = 1.5f; + + const bool bDeemphasizeUnhoveredPins = HoveredPins.Num() > 0; + if (bDeemphasizeUnhoveredPins) + { + ApplyHoverDeemphasis(OutputPin, InputPin, /*inout*/ Params.WireThickness, /*inout*/ Params.WireColor); + } +} + +void FConnectionDrawingPolicy_GenericGraph::Draw(TMap, 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 ChildNode = StaticCastSharedRef(CurWidget.Widget); + NodeWidgetMap.Add(ChildNode->GetNodeObj(), NodeIndex); + } + + // Now draw + FConnectionDrawingPolicy::Draw(InPinGeometries, ArrangedNodes); +} + +void FConnectionDrawingPolicy_GenericGraph::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); + } +} + +void FConnectionDrawingPolicy_GenericGraph::DrawSplineWithArrow(const FVector2D& StartAnchorPoint, const FVector2D& EndAnchorPoint, const FConnectionParams& Params) +{ + // bUserFlag1 indicates that we need to reverse the direction of connection (used by debugger) + const FVector2D& P0 = Params.bUserFlag1 ? EndAnchorPoint : StartAnchorPoint; + const FVector2D& P1 = Params.bUserFlag1 ? StartAnchorPoint : EndAnchorPoint; + + Internal_DrawLineWithArrow(P0, P1, Params); +} + +void FConnectionDrawingPolicy_GenericGraph::Internal_DrawLineWithArrow(const FVector2D& StartAnchorPoint, const FVector2D& EndAnchorPoint, const FConnectionParams& Params) +{ + //@TODO: Should this be scaled by zoom factor? + 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 + 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(), + FSlateDrawElement::RelativeToElement, + Params.WireColor + ); +} + +void FConnectionDrawingPolicy_GenericGraph::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); + + DrawSplineWithArrow(StartAnchorPoint, EndAnchorPoint, Params); +} + +FVector2D FConnectionDrawingPolicy_GenericGraph::ComputeSplineTangent(const FVector2D& Start, const FVector2D& End) const +{ + const FVector2D Delta = End - Start; + const FVector2D NormDelta = Delta.GetSafeNormal(); + + return NormDelta; +} + +void FConnectionDrawingPolicy_GenericGraph::DetermineLinkGeometry(FArrangedChildren& ArrangedNodes, TSharedRef& OutputPinWidget, + UEdGraphPin* OutputPin, UEdGraphPin* InputPin, FArrangedWidget*& StartWidgetGeometry, FArrangedWidget*& EndWidgetGeometry) +{ + if (UEdNode_GenericGraphEdge* EdgeNode = Cast(InputPin->GetOwningNode())) + { + UEdNode_GenericGraphNode* Start = EdgeNode->GetStartNode(); + UEdNode_GenericGraphNode* 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* pTargetWidget = PinToPinWidgetMap.Find(InputPin)) + { + TSharedRef InputWidget = (*pTargetWidget).ToSharedRef(); + EndWidgetGeometry = PinGeometries->Find(InputWidget); + } + } +} + diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/EdGraph_GenericGraph.cpp b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/EdGraph_GenericGraph.cpp new file mode 100644 index 00000000..f730756f --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/EdGraph_GenericGraph.cpp @@ -0,0 +1,205 @@ +#include "GenericGraphAssetEditor/EdGraph_GenericGraph.h" +#include "GenericGraphEditorPCH.h" +#include "GenericGraph.h" +#include "GenericGraphAssetEditor/EdNode_GenericGraphNode.h" +#include "GenericGraphAssetEditor/EdNode_GenericGraphEdge.h" + +UEdGraph_GenericGraph::UEdGraph_GenericGraph() +{ + +} + +UEdGraph_GenericGraph::~UEdGraph_GenericGraph() +{ + +} + +void UEdGraph_GenericGraph::RebuildGenericGraph() +{ + LOG_INFO(TEXT("UGenericGraphEdGraph::RebuildGenericGraph has been called")); + + UGenericGraph* Graph = GetGenericGraph(); + + Clear(); + + for (int i = 0; i < Nodes.Num(); ++i) + { + if (UEdNode_GenericGraphNode* EdNode = Cast(Nodes[i])) + { + if (EdNode->GenericGraphNode == nullptr) + continue; + + UGenericGraphNode* GenericGraphNode = EdNode->GenericGraphNode; + + NodeMap.Add(GenericGraphNode, EdNode); + + Graph->AllNodes.Add(GenericGraphNode); + + for (int PinIdx = 0; PinIdx < EdNode->Pins.Num(); ++PinIdx) + { + UEdGraphPin* Pin = EdNode->Pins[PinIdx]; + + if (Pin->Direction != EEdGraphPinDirection::EGPD_Output) + continue; + + for (int LinkToIdx = 0; LinkToIdx < Pin->LinkedTo.Num(); ++LinkToIdx) + { + UGenericGraphNode* ChildNode = nullptr; + if (UEdNode_GenericGraphNode* EdNode_Child = Cast(Pin->LinkedTo[LinkToIdx]->GetOwningNode())) + { + ChildNode = EdNode_Child->GenericGraphNode; + } + else if (UEdNode_GenericGraphEdge* EdNode_Edge = Cast(Pin->LinkedTo[LinkToIdx]->GetOwningNode())) + { + UEdNode_GenericGraphNode* Child = EdNode_Edge->GetEndNode();; + if (Child != nullptr) + { + ChildNode = Child->GenericGraphNode; + } + } + + if (ChildNode != nullptr) + { + GenericGraphNode->ChildrenNodes.Add(ChildNode); + + ChildNode->ParentNodes.Add(GenericGraphNode); + } + else + { + LOG_ERROR(TEXT("UEdGraph_GenericGraph::RebuildGenericGraph can't find child node")); + } + } + } + } + else if (UEdNode_GenericGraphEdge* EdgeNode = Cast(Nodes[i])) + { + UEdNode_GenericGraphNode* StartNode = EdgeNode->GetStartNode(); + UEdNode_GenericGraphNode* EndNode = EdgeNode->GetEndNode(); + UGenericGraphEdge* Edge = EdgeNode->GenericGraphEdge; + + if (StartNode == nullptr || EndNode == nullptr || Edge == nullptr) + { + LOG_ERROR(TEXT("UEdGraph_GenericGraph::RebuildGenericGraph add edge failed.")); + continue; + } + + EdgeMap.Add(Edge, EdgeNode); + + Edge->Graph = Graph; + Edge->Rename(nullptr, Graph, REN_DontCreateRedirectors | REN_DoNotDirty); + Edge->StartNode = StartNode->GenericGraphNode; + Edge->EndNode = EndNode->GenericGraphNode; + Edge->StartNode->Edges.Add(Edge->EndNode, Edge); + } + } + + for (int i = 0; i < Graph->AllNodes.Num(); ++i) + { + UGenericGraphNode* Node = Graph->AllNodes[i]; + if (Node->ParentNodes.Num() == 0) + { + Graph->RootNodes.Add(Node); + + SortNodes(Node); + } + + Node->Graph = Graph; + Node->Rename(nullptr, Graph, REN_DontCreateRedirectors | REN_DoNotDirty); + } + + Graph->RootNodes.Sort([&](const UGenericGraphNode& L, const UGenericGraphNode& R) + { + UEdNode_GenericGraphNode* EdNode_LNode = NodeMap[&L]; + UEdNode_GenericGraphNode* EdNode_RNode = NodeMap[&R]; + return EdNode_LNode->NodePosX < EdNode_RNode->NodePosX; + }); +} + +UGenericGraph* UEdGraph_GenericGraph::GetGenericGraph() const +{ + return CastChecked(GetOuter()); +} + +bool UEdGraph_GenericGraph::Modify(bool bAlwaysMarkDirty /*= true*/) +{ + bool Rtn = Super::Modify(bAlwaysMarkDirty); + + GetGenericGraph()->Modify(); + + for (int32 i = 0; i < Nodes.Num(); ++i) + { + Nodes[i]->Modify(); + } + + return Rtn; +} + +void UEdGraph_GenericGraph::Clear() +{ + UGenericGraph* Graph = GetGenericGraph(); + + Graph->ClearGraph(); + NodeMap.Reset(); + EdgeMap.Reset(); + + for (int i = 0; i < Nodes.Num(); ++i) + { + if (UEdNode_GenericGraphNode* EdNode = Cast(Nodes[i])) + { + UGenericGraphNode* GenericGraphNode = EdNode->GenericGraphNode; + if (GenericGraphNode) + { + GenericGraphNode->ParentNodes.Reset(); + GenericGraphNode->ChildrenNodes.Reset(); + GenericGraphNode->Edges.Reset(); + } + } + } +} + +void UEdGraph_GenericGraph::SortNodes(UGenericGraphNode* RootNode) +{ + int Level = 0; + TArray CurrLevelNodes = { RootNode }; + TArray NextLevelNodes; + TSet Visited; + + while (CurrLevelNodes.Num() != 0) + { + int32 LevelWidth = 0; + for (int i = 0; i < CurrLevelNodes.Num(); ++i) + { + UGenericGraphNode* Node = CurrLevelNodes[i]; + Visited.Add(Node); + + auto Comp = [&](const UGenericGraphNode& L, const UGenericGraphNode& R) + { + UEdNode_GenericGraphNode* EdNode_LNode = NodeMap[&L]; + UEdNode_GenericGraphNode* EdNode_RNode = NodeMap[&R]; + return EdNode_LNode->NodePosX < EdNode_RNode->NodePosX; + }; + + Node->ChildrenNodes.Sort(Comp); + Node->ParentNodes.Sort(Comp); + + for (int j = 0; j < Node->ChildrenNodes.Num(); ++j) + { + UGenericGraphNode* ChildNode = Node->ChildrenNodes[j]; + if(!Visited.Contains(ChildNode)) + NextLevelNodes.Add(Node->ChildrenNodes[j]); + } + } + + CurrLevelNodes = NextLevelNodes; + NextLevelNodes.Reset(); + ++Level; + } +} + +void UEdGraph_GenericGraph::PostEditUndo() +{ + Super::PostEditUndo(); + + NotifyGraphChanged(); +} + diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/EdNode_GenericGraphEdge.cpp b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/EdNode_GenericGraphEdge.cpp new file mode 100644 index 00000000..636ce9c4 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/EdNode_GenericGraphEdge.cpp @@ -0,0 +1,97 @@ +#include "GenericGraphAssetEditor/EdNode_GenericGraphEdge.h" +#include "GenericGraphEdge.h" +#include "GenericGraphAssetEditor/EdNode_GenericGraphNode.h" + +#define LOCTEXT_NAMESPACE "EdNode_GenericGraphEdge" + +UEdNode_GenericGraphEdge::UEdNode_GenericGraphEdge() +{ + bCanRenameNode = true; +} + +void UEdNode_GenericGraphEdge::SetEdge(UGenericGraphEdge* Edge) +{ + GenericGraphEdge = Edge; +} + +void UEdNode_GenericGraphEdge::AllocateDefaultPins() +{ + UEdGraphPin* Inputs = CreatePin(EGPD_Input, TEXT("Edge"), FName(), TEXT("In")); + Inputs->bHidden = true; + UEdGraphPin* Outputs = CreatePin(EGPD_Output, TEXT("Edge"), FName(), TEXT("Out")); + Outputs->bHidden = true; +} + +FText UEdNode_GenericGraphEdge::GetNodeTitle(ENodeTitleType::Type TitleType) const +{ + if (GenericGraphEdge) + { + return GenericGraphEdge->GetNodeTitle(); + } + return FText(); +} + +void UEdNode_GenericGraphEdge::PinConnectionListChanged(UEdGraphPin* Pin) +{ + if (Pin->LinkedTo.Num() == 0) + { + // Commit suicide; transitions must always have an input and output connection + Modify(); + + // Our parent graph will have our graph in SubGraphs so needs to be modified to record that. + if (UEdGraph* ParentGraph = GetGraph()) + { + ParentGraph->Modify(); + } + + DestroyNode(); + } +} + +void UEdNode_GenericGraphEdge::PrepareForCopying() +{ + GenericGraphEdge->Rename(nullptr, this, REN_DontCreateRedirectors | REN_DoNotDirty); +} + +void UEdNode_GenericGraphEdge::CreateConnections(UEdNode_GenericGraphNode* Start, UEdNode_GenericGraphNode* End) +{ + Pins[0]->Modify(); + Pins[0]->LinkedTo.Empty(); + + Start->GetOutputPin()->Modify(); + Pins[0]->MakeLinkTo(Start->GetOutputPin()); + + // This to next + Pins[1]->Modify(); + Pins[1]->LinkedTo.Empty(); + + End->GetInputPin()->Modify(); + Pins[1]->MakeLinkTo(End->GetInputPin()); +} + +UEdNode_GenericGraphNode* UEdNode_GenericGraphEdge::GetStartNode() +{ + if (Pins[0]->LinkedTo.Num() > 0) + { + return Cast(Pins[0]->LinkedTo[0]->GetOwningNode()); + } + else + { + return nullptr; + } +} + +UEdNode_GenericGraphNode* UEdNode_GenericGraphEdge::GetEndNode() +{ + if (Pins[1]->LinkedTo.Num() > 0) + { + return Cast(Pins[1]->LinkedTo[0]->GetOwningNode()); + } + else + { + return nullptr; + } +} + +#undef LOCTEXT_NAMESPACE + diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/EdNode_GenericGraphNode.cpp b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/EdNode_GenericGraphNode.cpp new file mode 100644 index 00000000..9a487cf6 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/EdNode_GenericGraphNode.cpp @@ -0,0 +1,84 @@ +#include "GenericGraphAssetEditor/EdNode_GenericGraphNode.h" +#include "GenericGraphAssetEditor/EdGraph_GenericGraph.h" +#include "Kismet2/Kismet2NameValidators.h" +#include "Kismet2/BlueprintEditorUtils.h" + +#define LOCTEXT_NAMESPACE "EdNode_GenericGraph" + +UEdNode_GenericGraphNode::UEdNode_GenericGraphNode() +{ + bCanRenameNode = true; +} + +UEdNode_GenericGraphNode::~UEdNode_GenericGraphNode() +{ + +} + +void UEdNode_GenericGraphNode::AllocateDefaultPins() +{ + CreatePin(EGPD_Input, "MultipleNodes", FName(), TEXT("In")); + CreatePin(EGPD_Output, "MultipleNodes", FName(), TEXT("Out")); +} + +UEdGraph_GenericGraph* UEdNode_GenericGraphNode::GetGenericGraphEdGraph() +{ + return Cast(GetGraph()); +} + +FText UEdNode_GenericGraphNode::GetNodeTitle(ENodeTitleType::Type TitleType) const +{ + if (GenericGraphNode == nullptr) + { + return Super::GetNodeTitle(TitleType); + } + else + { + return GenericGraphNode->GetNodeTitle(); + } +} + +void UEdNode_GenericGraphNode::PrepareForCopying() +{ + GenericGraphNode->Rename(nullptr, this, REN_DontCreateRedirectors | REN_DoNotDirty); +} + +void UEdNode_GenericGraphNode::AutowireNewNode(UEdGraphPin* FromPin) +{ + Super::AutowireNewNode(FromPin); + + if (FromPin != nullptr) + { + if (GetSchema()->TryCreateConnection(FromPin, GetInputPin())) + { + FromPin->GetOwningNode()->NodeConnectionListChanged(); + } + } +} + +void UEdNode_GenericGraphNode::SetGenericGraphNode(UGenericGraphNode* InNode) +{ + GenericGraphNode = InNode; +} + +FLinearColor UEdNode_GenericGraphNode::GetBackgroundColor() const +{ + return GenericGraphNode == nullptr ? FLinearColor::Black : GenericGraphNode->GetBackgroundColor(); +} + +UEdGraphPin* UEdNode_GenericGraphNode::GetInputPin() const +{ + return Pins[0]; +} + +UEdGraphPin* UEdNode_GenericGraphNode::GetOutputPin() const +{ + return Pins[1]; +} + +void UEdNode_GenericGraphNode::PostEditUndo() +{ + UEdGraphNode::PostEditUndo(); +} + +#undef LOCTEXT_NAMESPACE diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/EditorCommands_GenericGraph.cpp b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/EditorCommands_GenericGraph.cpp new file mode 100644 index 00000000..8cd338f5 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/EditorCommands_GenericGraph.cpp @@ -0,0 +1,11 @@ +#include "GenericGraphAssetEditor/EditorCommands_GenericGraph.h" + +#define LOCTEXT_NAMESPACE "EditorCommands_GenericGraph" + +void FEditorCommands_GenericGraph::RegisterCommands() +{ + UI_COMMAND(GraphSettings, "Graph Settings", "Graph Settings", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(AutoArrange, "Auto Arrange", "Auto Arrange", EUserInterfaceActionType::Button, FInputChord()); +} + +#undef LOCTEXT_NAMESPACE diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/GenericGraphDragConnection.cpp b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/GenericGraphDragConnection.cpp new file mode 100644 index 00000000..c8c3999d --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/GenericGraphDragConnection.cpp @@ -0,0 +1,325 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + + +#include "GenericGraphAssetEditor/GenericGraphDragConnection.h" +#include "Widgets/SBoxPanel.h" +#include "Framework/Application/SlateApplication.h" +#include "Widgets/Images/SImage.h" +#include "EdGraph/EdGraph.h" +#include "SGraphPanel.h" +#include "ScopedTransaction.h" + +TSharedRef FGenericGraphDragConnection::New(const TSharedRef& GraphPanel, const FDraggedPinTable& DraggedPins) +{ + TSharedRef Operation = MakeShareable(new FGenericGraphDragConnection(GraphPanel, DraggedPins)); + Operation->Construct(); + + return Operation; +} + +void FGenericGraphDragConnection::OnDrop(bool bDropWasHandled, const FPointerEvent& MouseEvent) +{ + GraphPanel->OnStopMakingConnection(); + + Super::OnDrop(bDropWasHandled, MouseEvent); +} + +void FGenericGraphDragConnection::OnDragged(const class FDragDropEvent& DragDropEvent) +{ + FVector2D TargetPosition = DragDropEvent.GetScreenSpacePosition(); + + // Reposition the info window wrt to the drag + CursorDecoratorWindow->MoveWindowTo(DragDropEvent.GetScreenSpacePosition() + DecoratorAdjust); + // Request the active panel to scroll if required + GraphPanel->RequestDeferredPan(TargetPosition); +} + +void FGenericGraphDragConnection::HoverTargetChanged() +{ + TArray UniqueMessages; + + if (UEdGraphPin* TargetPinObj = GetHoveredPin()) + { + TArray ValidSourcePins; + ValidateGraphPinList(/*out*/ ValidSourcePins); + + // Check the schema for connection responses + for (UEdGraphPin* StartingPinObj : ValidSourcePins) + { + // The Graph object in which the pins reside. + UEdGraph* GraphObj = StartingPinObj->GetOwningNode()->GetGraph(); + + // Determine what the schema thinks about the wiring action + const FPinConnectionResponse Response = GraphObj->GetSchema()->CanCreateConnection(StartingPinObj, TargetPinObj); + + if (Response.Response == ECanCreateConnectionResponse::CONNECT_RESPONSE_DISALLOW) + { + TSharedPtr NodeWidget = TargetPinObj->GetOwningNode()->DEPRECATED_NodeWidget.Pin(); + if (NodeWidget.IsValid()) + { + NodeWidget->NotifyDisallowedPinConnection(StartingPinObj, TargetPinObj); + } + } + + UniqueMessages.AddUnique(Response); + } + } + else if (UEdNode_GenericGraphNode* TargetNodeObj = Cast(GetHoveredNode())) + { + TArray ValidSourcePins; + ValidateGraphPinList(/*out*/ ValidSourcePins); + + // Check the schema for connection responses + for (UEdGraphPin* StartingPinObj : ValidSourcePins) + { + FPinConnectionResponse Response; + FText ResponseText; + + const UEdGraphSchema *Schema = StartingPinObj->GetSchema(); + UEdGraphPin *TargetPin = TargetNodeObj->GetInputPin(); + + if (Schema && TargetPin) + { + Response = Schema->CanCreateConnection(StartingPinObj, TargetPin); + if (Response.Response == ECanCreateConnectionResponse::CONNECT_RESPONSE_DISALLOW) + { + TSharedPtr NodeWidget = TargetPin->GetOwningNode()->DEPRECATED_NodeWidget.Pin(); + if (NodeWidget.IsValid()) + { + NodeWidget->NotifyDisallowedPinConnection(StartingPinObj, TargetPinObj); + } + } + } + else + { +#define LOCTEXT_NAMESPACE "AssetSchema_GenericGraph" + Response = FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("PinError", "Not a valid UGenericGraphEdNode")); +#undef LOCTEXT_NAMESPACE + } + + UniqueMessages.AddUnique(Response); + } + } + else if (UEdGraph* CurrentHoveredGraph = GetHoveredGraph()) + { + TArray ValidSourcePins; + ValidateGraphPinList(/*out*/ ValidSourcePins); + + for (UEdGraphPin* StartingPinObj : ValidSourcePins) + { + // Let the schema describe the connection we might make + FPinConnectionResponse Response = CurrentHoveredGraph->GetSchema()->CanCreateNewNodes(StartingPinObj); + if (!Response.Message.IsEmpty()) + { + UniqueMessages.AddUnique(Response); + } + } + } + + // Let the user know the status of dropping now + if (UniqueMessages.Num() == 0) + { + // Display the place a new node icon, we're not over a valid pin and have no message from the schema + SetSimpleFeedbackMessage( + FAppStyle::GetBrush(TEXT("Graph.ConnectorFeedback.NewNode")), + FLinearColor::White, + NSLOCTEXT("GraphEditor.Feedback", "PlaceNewNode", "Place a new node.")); + } + else + { + // Take the unique responses and create visual feedback for it + TSharedRef FeedbackBox = SNew(SVerticalBox); + for (auto ResponseIt = UniqueMessages.CreateConstIterator(); ResponseIt; ++ResponseIt) + { + // Determine the icon + const FSlateBrush* StatusSymbol = NULL; + + switch (ResponseIt->Response) + { + case CONNECT_RESPONSE_MAKE: + case CONNECT_RESPONSE_BREAK_OTHERS_A: + case CONNECT_RESPONSE_BREAK_OTHERS_B: + case CONNECT_RESPONSE_BREAK_OTHERS_AB: + StatusSymbol = FAppStyle::GetBrush(TEXT("Graph.ConnectorFeedback.OK")); + break; + + case CONNECT_RESPONSE_MAKE_WITH_CONVERSION_NODE: + StatusSymbol = FAppStyle::GetBrush(TEXT("Graph.ConnectorFeedback.ViaCast")); + break; + + case CONNECT_RESPONSE_DISALLOW: + default: + StatusSymbol = FAppStyle::GetBrush(TEXT("Graph.ConnectorFeedback.Error")); + break; + } + + // Add a new message row + FeedbackBox->AddSlot() + .AutoHeight() + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + .Padding(3.0f) + .VAlign(VAlign_Center) + [ + SNew(SImage).Image(StatusSymbol) + ] + + SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Center) + [ + SNew(STextBlock).Text(ResponseIt->Message) + ] + ]; + } + + SetFeedbackMessage(FeedbackBox); + } +} + +FGenericGraphDragConnection::FGenericGraphDragConnection(const TSharedRef& GraphPanelIn, const FDraggedPinTable& DraggedPinsIn) + : GraphPanel(GraphPanelIn) + , DraggingPins(DraggedPinsIn) + , DecoratorAdjust(FSlateApplication::Get().GetCursorSize()) +{ + if (DraggingPins.Num() > 0) + { + const UEdGraphPin* PinObj = FDraggedPinTable::TConstIterator(DraggedPinsIn)->GetPinObj(*GraphPanelIn); + if (PinObj && PinObj->Direction == EGPD_Input) + { + DecoratorAdjust *= FVector2D(-1.0f, 1.0f); + } + } + + for (const FGraphPinHandle& DraggedPin : DraggedPinsIn) + { + GraphPanelIn->OnBeginMakingConnection(DraggedPin); + } +} + +FReply FGenericGraphDragConnection::DroppedOnPin(FVector2D ScreenPosition, FVector2D GraphPosition) +{ + TArray ValidSourcePins; + ValidateGraphPinList(/*out*/ ValidSourcePins); + + const FScopedTransaction Transaction(NSLOCTEXT("UnrealEd", "GraphEd_CreateConnection", "Create Pin Link")); + + UEdGraphPin* PinB = GetHoveredPin(); + bool bError = false; + TSet NodeList; + + for (UEdGraphPin* PinA : ValidSourcePins) + { + if ((PinA != NULL) && (PinB != NULL)) + { + UEdGraph* MyGraphObj = PinA->GetOwningNode()->GetGraph(); + + if (MyGraphObj->GetSchema()->TryCreateConnection(PinA, PinB)) + { + if (!PinA->IsPendingKill()) + { + NodeList.Add(PinA->GetOwningNode()); + } + if (!PinB->IsPendingKill()) + { + NodeList.Add(PinB->GetOwningNode()); + } + } + } + else + { + bError = true; + } + } + + // Send all nodes that received a new pin connection a notification + for (auto It = NodeList.CreateConstIterator(); It; ++It) + { + UEdGraphNode* Node = (*It); + Node->NodeConnectionListChanged(); + } + + if (bError) + { + return FReply::Unhandled(); + } + + return FReply::Handled(); +} + +FReply FGenericGraphDragConnection::DroppedOnNode(FVector2D ScreenPosition, FVector2D GraphPosition) +{ + bool bHandledPinDropOnNode = false; + UEdGraphNode* NodeOver = GetHoveredNode(); + + if (NodeOver) + { + // Gather any source drag pins + TArray ValidSourcePins; + ValidateGraphPinList(/*out*/ ValidSourcePins); + + if (ValidSourcePins.Num()) + { + for (UEdGraphPin* SourcePin : ValidSourcePins) + { + // Check for pin drop support + FText ResponseText; + if (SourcePin->GetOwningNode() != NodeOver && SourcePin->GetSchema()->SupportsDropPinOnNode(NodeOver, SourcePin->PinType, SourcePin->Direction, ResponseText)) + { + bHandledPinDropOnNode = true; + + // Find which pin name to use and drop the pin on the node + const FName PinName = SourcePin->PinFriendlyName.IsEmpty() ? SourcePin->PinName : *SourcePin->PinFriendlyName.ToString(); + + const FScopedTransaction Transaction((SourcePin->Direction == EGPD_Output) ? NSLOCTEXT("UnrealEd", "AddInParam", "Add In Parameter") : NSLOCTEXT("UnrealEd", "AddOutParam", "Add Out Parameter")); + + UEdGraphPin* EdGraphPin = NodeOver->GetSchema()->DropPinOnNode(GetHoveredNode(), PinName, SourcePin->PinType, SourcePin->Direction); + + // This can invalidate the source pin due to node reconstruction, abort in that case + if (SourcePin->GetOwningNodeUnchecked() && EdGraphPin) + { + SourcePin->Modify(); + EdGraphPin->Modify(); + SourcePin->GetSchema()->TryCreateConnection(SourcePin, EdGraphPin); + } + } + + // If we have not handled the pin drop on node and there is an error message, do not let other actions occur. + if (!bHandledPinDropOnNode && !ResponseText.IsEmpty()) + { + bHandledPinDropOnNode = true; + } + } + } + } + return bHandledPinDropOnNode ? FReply::Handled() : FReply::Unhandled(); +} + +FReply FGenericGraphDragConnection::DroppedOnPanel(const TSharedRef< SWidget >& Panel, FVector2D ScreenPosition, FVector2D GraphPosition, UEdGraph& Graph) +{ + // Gather any source drag pins + TArray PinObjects; + ValidateGraphPinList(/*out*/ PinObjects); + + // Create a context menu + TSharedPtr WidgetToFocus = GraphPanel->SummonContextMenu(ScreenPosition, GraphPosition, NULL, NULL, PinObjects); + + // Give the context menu focus + return (WidgetToFocus.IsValid()) + ? FReply::Handled().SetUserFocus(WidgetToFocus.ToSharedRef(), EFocusCause::SetDirectly) + : FReply::Handled(); +} + + +void FGenericGraphDragConnection::ValidateGraphPinList(TArray& OutValidPins) +{ + OutValidPins.Empty(DraggingPins.Num()); + for (const FGraphPinHandle& PinHandle : DraggingPins) + { + if (UEdGraphPin* GraphPin = PinHandle.GetPinObj(*GraphPanel)) + { + OutValidPins.Add(GraphPin); + } + } +} diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/GenericGraphEditorStyle.cpp b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/GenericGraphEditorStyle.cpp new file mode 100644 index 00000000..6870128f --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/GenericGraphEditorStyle.cpp @@ -0,0 +1,54 @@ +#include "GenericGraphAssetEditor/GenericGraphEditorStyle.h" +#include "Styling/SlateStyleRegistry.h" +#include "Styling/SlateTypes.h" +#include "Misc/Paths.h" + +TSharedPtr FGenericGraphEditorStyle::StyleSet = nullptr; + +#define IMAGE_BRUSH( RelativePath, ... ) FSlateImageBrush( StyleSet->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ ) +#define BOX_BRUSH( RelativePath, ... ) FSlateBoxBrush( StyleSet->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ ) +#define BORDER_BRUSH( RelativePath, ... ) FSlateBorderBrush( StyleSet->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ ) +#define TTF_FONT( RelativePath, ... ) FSlateFontInfo( StyleSet->RootToContentDir( RelativePath, TEXT(".ttf") ), __VA_ARGS__ ) +#define OTF_FONT( RelativePath, ... ) FSlateFontInfo( StyleSet->RootToContentDir( RelativePath, TEXT(".otf") ), __VA_ARGS__ ) + +void FGenericGraphEditorStyle::Initialize() +{ + const FVector2D Icon20x20(20.0f, 20.0f); + const FVector2D Icon40x40(40.0f, 40.0f); + const FVector2D Icon64x64(64.0f, 64.0f); + + if (StyleSet.IsValid()) + { + return; + } + + StyleSet = MakeShareable(new FSlateStyleSet("GenericGraphEditorStyle")); + + StyleSet->SetContentRoot(FPaths::ProjectPluginsDir() / TEXT("GenericGraph/Resources")); + + StyleSet->Set("GenericGraphEditor.AutoArrange", new IMAGE_BRUSH("AutoArrangeIcon", Icon40x40)); + StyleSet->Set("GenericGraphEditor.AutoArrange.Small", new IMAGE_BRUSH( "AutoArrangeIcon", Icon20x20 ) ); + + FSlateStyleRegistry::RegisterSlateStyle(*StyleSet.Get()); +} + +void FGenericGraphEditorStyle::Shutdown() +{ + if (StyleSet.IsValid()) + { + FSlateStyleRegistry::UnRegisterSlateStyle(*StyleSet.Get()); + ensure(StyleSet.IsUnique()); + StyleSet.Reset(); + } +} + +const FName& FGenericGraphEditorStyle::GetStyleSetName() +{ + return StyleSet->GetStyleSetName(); +} + +#undef IMAGE_BRUSH +#undef BOX_BRUSH +#undef BORDER_BRUSH +#undef TTF_FONT +#undef OTF_FONT diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/SEdNode_GenericGraphEdge.cpp b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/SEdNode_GenericGraphEdge.cpp new file mode 100644 index 00000000..d46b0777 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/SEdNode_GenericGraphEdge.cpp @@ -0,0 +1,199 @@ +#include "GenericGraphAssetEditor/SEdNode_GenericGraphEdge.h" +#include "Widgets/SBoxPanel.h" +#include "Widgets/Images/SImage.h" +#include "Widgets/Text/SInlineEditableTextBlock.h" +#include "Widgets/SToolTip.h" +#include "SGraphPanel.h" +#include "EdGraphSchema_K2.h" +#include "GenericGraphAssetEditor/EdNode_GenericGraphNode.h" +#include "GenericGraphAssetEditor/EdNode_GenericGraphEdge.h" +#include "GenericGraphAssetEditor/ConnectionDrawingPolicy_GenericGraph.h" + +#define LOCTEXT_NAMESPACE "SGenericGraphEdge" + +void SEdNode_GenericGraphEdge::Construct(const FArguments& InArgs, UEdNode_GenericGraphEdge* InNode) +{ + this->GraphNode = InNode; + this->UpdateGraphNode(); +} + +bool SEdNode_GenericGraphEdge::RequiresSecondPassLayout() const +{ + return true; +} + +void SEdNode_GenericGraphEdge::PerformSecondPassLayout(const TMap< UObject*, TSharedRef >& NodeToWidgetLookup) const +{ + UEdNode_GenericGraphEdge* EdgeNode = CastChecked(GraphNode); + + FGeometry StartGeom; + FGeometry EndGeom; + + UEdNode_GenericGraphNode* Start = EdgeNode->GetStartNode(); + UEdNode_GenericGraphNode* End = EdgeNode->GetEndNode(); + if (Start != nullptr && End != nullptr) + { + const TSharedRef* pFromWidget = NodeToWidgetLookup.Find(Start); + const TSharedRef* pToWidget = NodeToWidgetLookup.Find(End); + if (pFromWidget != nullptr && pToWidget != nullptr) + { + const TSharedRef& FromWidget = *pFromWidget; + const TSharedRef& 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 SEdNode_GenericGraphEdge::OnNameTextCommited(const FText& InText, ETextCommit::Type CommitInfo) +{ + SGraphNode::OnNameTextCommited(InText, CommitInfo); + + UEdNode_GenericGraphEdge* MyNode = CastChecked(GraphNode); + + if (MyNode != nullptr && MyNode->GenericGraphEdge != nullptr) + { + const FScopedTransaction Transaction(LOCTEXT("GenericGraphEditorRenameEdge", "Generic Graph Editor: Rename Edge")); + MyNode->Modify(); + MyNode->GenericGraphEdge->SetNodeTitle(InText); + UpdateGraphNode(); + } +} + +void SEdNode_GenericGraphEdge::UpdateGraphNode() +{ + InputPins.Empty(); + OutputPins.Empty(); + + RightNodeBox.Reset(); + LeftNodeBox.Reset(); + + TSharedPtr NodeTitle = SNew(SNodeTitle, GraphNode); + + 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, &SEdNode_GenericGraphEdge::GetEdgeColor) + ] + + SOverlay::Slot() + [ + SNew(SImage) + .Image(this, &SEdNode_GenericGraphEdge::GetEdgeImage) + .Visibility(this, &SEdNode_GenericGraphEdge::GetEdgeImageVisibility) + ] + + + SOverlay::Slot() + .Padding(FMargin(4.0f, 4.0f, 4.0f, 4.0f)) + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .HAlign(HAlign_Center) + .AutoHeight() + [ + SAssignNew(InlineEditableText, SInlineEditableTextBlock) + .ColorAndOpacity(FLinearColor::Black) + .Visibility(this, &SEdNode_GenericGraphEdge::GetEdgeTitleVisbility) + .Font(FCoreStyle::GetDefaultFontStyle("Regular", 12)) + .Text(NodeTitle.Get(), &SNodeTitle::GetHeadTitle) + .OnTextCommitted(this, &SEdNode_GenericGraphEdge::OnNameTextCommited) + ] + + SVerticalBox::Slot() + .AutoHeight() + [ + NodeTitle.ToSharedRef() + ] + + ] + ]; +} + +void SEdNode_GenericGraphEdge::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 + // towards the PrevStateNode and +1 is 1 towards the NextStateNode. + + const float MutliNodeSpace = 0.2f; // Space between multiple transition nodes (in units of ) + 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 SEdNode_GenericGraphEdge::GetEdgeColor() const +{ + UEdNode_GenericGraphEdge* EdgeNode = CastChecked(GraphNode); + if (EdgeNode != nullptr && EdgeNode->GenericGraphEdge != nullptr) + { + return EdgeNode->GenericGraphEdge->GetEdgeColour(); + } + return FLinearColor(0.9f, 0.9f, 0.9f, 1.0f); +} + +const FSlateBrush* SEdNode_GenericGraphEdge::GetEdgeImage() const +{ + return FAppStyle::GetBrush("Graph.TransitionNode.Icon"); +} + +EVisibility SEdNode_GenericGraphEdge::GetEdgeImageVisibility() const +{ + UEdNode_GenericGraphEdge* EdgeNode = CastChecked(GraphNode); + if (EdgeNode && EdgeNode->GenericGraphEdge && EdgeNode->GenericGraphEdge->bShouldDrawTitle) + return EVisibility::Hidden; + + return EVisibility::Visible; +} + +EVisibility SEdNode_GenericGraphEdge::GetEdgeTitleVisbility() const +{ + UEdNode_GenericGraphEdge* EdgeNode = CastChecked(GraphNode); + if (EdgeNode && EdgeNode->GenericGraphEdge && EdgeNode->GenericGraphEdge->bShouldDrawTitle) + return EVisibility::Visible; + + return EVisibility::Collapsed; +} + +#undef LOCTEXT_NAMESPACE diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/SEdNode_GenericGraphNode.cpp b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/SEdNode_GenericGraphNode.cpp new file mode 100644 index 00000000..d69e7eea --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/SEdNode_GenericGraphNode.cpp @@ -0,0 +1,336 @@ +#include "GenericGraphAssetEditor/SEdNode_GenericGraphNode.h" +#include "GenericGraphEditorPCH.h" +#include "GenericGraphAssetEditor/Colors_GenericGraph.h" +#include "SLevelOfDetailBranchNode.h" +#include "Widgets/Text/SInlineEditableTextBlock.h" +#include "SCommentBubble.h" +#include "SlateOptMacros.h" +#include "SGraphPin.h" +#include "GraphEditorSettings.h" +#include "GenericGraphAssetEditor/EdNode_GenericGraphNode.h" +#include "GenericGraphAssetEditor/GenericGraphDragConnection.h" + +#define LOCTEXT_NAMESPACE "EdNode_GenericGraph" + +////////////////////////////////////////////////////////////////////////// +class SGenericGraphPin : public SGraphPin +{ +public: + SLATE_BEGIN_ARGS(SGenericGraphPin) {} + SLATE_END_ARGS() + + void Construct(const FArguments& InArgs, UEdGraphPin* InPin) + { + this->SetCursor(EMouseCursor::Default); + + bShowLabel = true; + + GraphPinObj = InPin; + check(GraphPinObj != nullptr); + + const UEdGraphSchema* Schema = GraphPinObj->GetSchema(); + check(Schema); + + SBorder::Construct(SBorder::FArguments() + .BorderImage(this, &SGenericGraphPin::GetPinBorder) + .BorderBackgroundColor(this, &SGenericGraphPin::GetPinColor) + .OnMouseButtonDown(this, &SGenericGraphPin::OnPinMouseDown) + .Cursor(this, &SGenericGraphPin::GetPinCursor) + .Padding(FMargin(5.0f)) + ); + } + +protected: + virtual FSlateColor GetPinColor() const override + { + return GenericGraphColors::Pin::Default; + } + + virtual TSharedRef GetDefaultValueWidget() override + { + return SNew(STextBlock); + } + + const FSlateBrush* GetPinBorder() const + { + return FAppStyle::GetBrush(TEXT("Graph.StateNode.Body")); + } + + virtual TSharedRef SpawnPinDragEvent(const TSharedRef& InGraphPanel, const TArray< TSharedRef >& InStartingPins) override + { + FGenericGraphDragConnection::FDraggedPinTable PinHandles; + PinHandles.Reserve(InStartingPins.Num()); + // since the graph can be refreshed and pins can be reconstructed/replaced + // behind the scenes, the DragDropOperation holds onto FGraphPinHandles + // instead of direct widgets/graph-pins + for (const TSharedRef& PinWidget : InStartingPins) + { + PinHandles.Add(PinWidget->GetPinObj()); + } + + return FGenericGraphDragConnection::New(InGraphPanel, PinHandles); + } + +}; + + +////////////////////////////////////////////////////////////////////////// +void SEdNode_GenericGraphNode::Construct(const FArguments& InArgs, UEdNode_GenericGraphNode* InNode) +{ + GraphNode = InNode; + UpdateGraphNode(); + InNode->SEdNode = this; +} + +BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION +void SEdNode_GenericGraphNode::UpdateGraphNode() +{ + const FMargin NodePadding = FMargin(5); + const FMargin NamePadding = FMargin(2); + + InputPins.Empty(); + OutputPins.Empty(); + + // Reset variables that are going to be exposed, in case we are refreshing an already setup node. + RightNodeBox.Reset(); + LeftNodeBox.Reset(); + + const FSlateBrush *NodeTypeIcon = GetNameIcon(); + + FLinearColor TitleShadowColor(0.6f, 0.6f, 0.6f); + TSharedPtr ErrorText; + TSharedPtr NodeBody; + TSharedPtr NodeTitle = SNew(SNodeTitle, GraphNode); + + this->ContentScale.Bind(this, &SGraphNode::GetContentScale); + this->GetOrAddSlot(ENodeZone::Center) + .HAlign(HAlign_Fill) + .VAlign(VAlign_Center) + [ + SNew(SBorder) + .BorderImage(FAppStyle::GetBrush("Graph.StateNode.Body")) + .Padding(0.0f) + .BorderBackgroundColor(this, &SEdNode_GenericGraphNode::GetBorderBackgroundColor) + [ + SNew(SOverlay) + + + SOverlay::Slot() + .HAlign(HAlign_Fill) + .VAlign(VAlign_Fill) + [ + SNew(SVerticalBox) + + // Input Pin Area + + SVerticalBox::Slot() + .FillHeight(1) + [ + SAssignNew(LeftNodeBox, SVerticalBox) + ] + + // Output Pin Area + + SVerticalBox::Slot() + .FillHeight(1) + [ + SAssignNew(RightNodeBox, SVerticalBox) + ] + ] + + + SOverlay::Slot() + .HAlign(HAlign_Center) + .VAlign(VAlign_Center) + .Padding(8.0f) + [ + SNew(SBorder) + .BorderImage(FAppStyle::GetBrush("Graph.StateNode.ColorSpill")) + .BorderBackgroundColor(TitleShadowColor) + .HAlign(HAlign_Center) + .VAlign(VAlign_Center) + .Visibility(EVisibility::SelfHitTestInvisible) + .Padding(6.0f) + [ + SAssignNew(NodeBody, SVerticalBox) + + // Title + + SVerticalBox::Slot() + .AutoHeight() + [ + SNew(SHorizontalBox) + + // Error message + + SHorizontalBox::Slot() + .AutoWidth() + [ + SAssignNew(ErrorText, SErrorText) + .BackgroundColor(this, &SEdNode_GenericGraphNode::GetErrorColor) + .ToolTipText(this, &SEdNode_GenericGraphNode::GetErrorMsgToolTip) + ] + + // Icon + +SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Center) + [ + SNew(SImage) + .Image(NodeTypeIcon) + ] + + // Node Title + + SHorizontalBox::Slot() + .Padding(FMargin(4.0f, 0.0f, 4.0f, 0.0f)) + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .AutoHeight() + [ + SAssignNew(InlineEditableText, SInlineEditableTextBlock) + .Style(FAppStyle::Get(), "Graph.StateNode.NodeTitleInlineEditableText") + .Text(NodeTitle.Get(), &SNodeTitle::GetHeadTitle) + .OnVerifyTextChanged(this, &SEdNode_GenericGraphNode::OnVerifyNameTextChanged) + .OnTextCommitted(this, &SEdNode_GenericGraphNode::OnNameTextCommited) + .IsReadOnly(this, &SEdNode_GenericGraphNode::IsNameReadOnly) + .IsSelected(this, &SEdNode_GenericGraphNode::IsSelectedExclusively) + ] + + SVerticalBox::Slot() + .AutoHeight() + [ + NodeTitle.ToSharedRef() + ] + ] + ] + ] + ] + ] + ]; + + // Create comment bubble + TSharedPtr CommentBubble; + const FSlateColor CommentColor = GetDefault()->DefaultCommentNodeTitleColor; + + SAssignNew(CommentBubble, SCommentBubble) + .GraphNode(GraphNode) + .Text(this, &SGraphNode::GetNodeComment) + .OnTextCommitted(this, &SGraphNode::OnCommentTextCommitted) + .ColorAndOpacity(CommentColor) + .AllowPinning(true) + .EnableTitleBarBubble(true) + .EnableBubbleCtrls(true) + .GraphLOD(this, &SGraphNode::GetCurrentLOD) + .IsGraphNodeHovered(this, &SGraphNode::IsHovered); + + GetOrAddSlot(ENodeZone::TopCenter) + .SlotOffset(TAttribute(CommentBubble.Get(), &SCommentBubble::GetOffset)) + .SlotSize(TAttribute(CommentBubble.Get(), &SCommentBubble::GetSize)) + .AllowScaling(TAttribute(CommentBubble.Get(), &SCommentBubble::IsScalingAllowed)) + .VAlign(VAlign_Top) + [ + CommentBubble.ToSharedRef() + ]; + + ErrorReporting = ErrorText; + ErrorReporting->SetError(ErrorMsg); + CreatePinWidgets(); +} + +void SEdNode_GenericGraphNode::CreatePinWidgets() +{ + UEdNode_GenericGraphNode* StateNode = CastChecked(GraphNode); + + for (int32 PinIdx = 0; PinIdx < StateNode->Pins.Num(); PinIdx++) + { + UEdGraphPin* MyPin = StateNode->Pins[PinIdx]; + if (!MyPin->bHidden) + { + TSharedPtr NewPin = SNew(SGenericGraphPin, MyPin); + + AddPin(NewPin.ToSharedRef()); + } + } +} + +void SEdNode_GenericGraphNode::AddPin(const TSharedRef& PinToAdd) +{ + PinToAdd->SetOwner(SharedThis(this)); + + const UEdGraphPin* PinObj = PinToAdd->GetPinObj(); + const bool bAdvancedParameter = PinObj && PinObj->bAdvancedView; + if (bAdvancedParameter) + { + PinToAdd->SetVisibility( TAttribute(PinToAdd, &SGraphPin::IsPinVisibleAsAdvanced) ); + } + + TSharedPtr PinBox; + if (PinToAdd->GetDirection() == EEdGraphPinDirection::EGPD_Input) + { + PinBox = LeftNodeBox; + InputPins.Add(PinToAdd); + } + else // Direction == EEdGraphPinDirection::EGPD_Output + { + PinBox = RightNodeBox; + OutputPins.Add(PinToAdd); + } + + if (PinBox) + { + PinBox->AddSlot() + .HAlign(HAlign_Fill) + .VAlign(VAlign_Fill) + .FillHeight(1.0f) + //.Padding(6.0f, 0.0f) + [ + PinToAdd + ]; + } +} + +bool SEdNode_GenericGraphNode::IsNameReadOnly() const +{ + UEdNode_GenericGraphNode* EdNode_Node = Cast(GraphNode); + check(EdNode_Node != nullptr); + + UGenericGraph* GenericGraph = EdNode_Node->GenericGraphNode->Graph; + check(GenericGraph != nullptr); + + return (!GenericGraph->bCanRenameNode || !EdNode_Node->GenericGraphNode->IsNameEditable()) || SGraphNode::IsNameReadOnly(); +} + +END_SLATE_FUNCTION_BUILD_OPTIMIZATION + +void SEdNode_GenericGraphNode::OnNameTextCommited(const FText& InText, ETextCommit::Type CommitInfo) +{ + SGraphNode::OnNameTextCommited(InText, CommitInfo); + + UEdNode_GenericGraphNode* MyNode = CastChecked(GraphNode); + + if (MyNode != nullptr && MyNode->GenericGraphNode != nullptr) + { + const FScopedTransaction Transaction(LOCTEXT("GenericGraphEditorRenameNode", "Generic Graph Editor: Rename Node")); + MyNode->Modify(); + MyNode->GenericGraphNode->Modify(); + MyNode->GenericGraphNode->SetNodeTitle(InText); + UpdateGraphNode(); + } +} + +FSlateColor SEdNode_GenericGraphNode::GetBorderBackgroundColor() const +{ + UEdNode_GenericGraphNode* MyNode = CastChecked(GraphNode); + return MyNode ? MyNode->GetBackgroundColor() : GenericGraphColors::NodeBorder::HighlightAbortRange0; +} + +FSlateColor SEdNode_GenericGraphNode::GetBackgroundColor() const +{ + return GenericGraphColors::NodeBody::Default; +} + +EVisibility SEdNode_GenericGraphNode::GetDragOverMarkerVisibility() const +{ + return EVisibility::Visible; +} + +const FSlateBrush* SEdNode_GenericGraphNode::GetNameIcon() const +{ + return FAppStyle::GetBrush(TEXT("BTEditor.Graph.BTNode.Icon")); +} + +#undef LOCTEXT_NAMESPACE diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/Settings_GenericGraphEditor.cpp b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/Settings_GenericGraphEditor.cpp new file mode 100644 index 00000000..a5180a8d --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphAssetEditor/Settings_GenericGraphEditor.cpp @@ -0,0 +1,24 @@ +#include "GenericGraphAssetEditor/Settings_GenericGraphEditor.h" + +UGenericGraphEditorSettings::UGenericGraphEditorSettings() +{ + AutoLayoutStrategy = EAutoLayoutStrategy::Tree; + + bFirstPassOnly = false; + + bRandomInit = false; + + OptimalDistance = 100.f; + + MaxIteration = 50; + + InitTemperature = 10.f; + + CoolDownRate = 10.f; +} + +UGenericGraphEditorSettings::~UGenericGraphEditorSettings() +{ + +} + diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphEditor.cpp b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphEditor.cpp new file mode 100644 index 00000000..b83d1f6d --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphEditor.cpp @@ -0,0 +1,55 @@ +#include "GenericGraphEditor.h" +#include "GenericGraphNodeFactory.h" +#include "AssetTypeActions_GenericGraph.h" +#include "GenericGraphAssetEditor/GenericGraphEditorStyle.h" + +DEFINE_LOG_CATEGORY(GenericGraphEditor) + +#define LOCTEXT_NAMESPACE "Editor_GenericGraph" + +void FGenericGraphEditor::StartupModule() +{ + FGenericGraphEditorStyle::Initialize(); + + GraphPanelNodeFactory_GenericGraph = MakeShareable(new FGraphPanelNodeFactory_GenericGraph()); + FEdGraphUtilities::RegisterVisualNodeFactory(GraphPanelNodeFactory_GenericGraph); + + IAssetTools& AssetTools = FModuleManager::LoadModuleChecked("AssetTools").Get(); + + GenericGraphAssetCategoryBit = AssetTools.RegisterAdvancedAssetCategory(FName(TEXT("GenericGraph")), LOCTEXT("GenericGraphAssetCategory", "GenericGraph")); + + RegisterAssetTypeAction(AssetTools, MakeShareable(new FAssetTypeActions_GenericGraph(GenericGraphAssetCategoryBit))); +} + + +void FGenericGraphEditor::ShutdownModule() +{ + // Unregister all the asset types that we registered + if (FModuleManager::Get().IsModuleLoaded("AssetTools")) + { + IAssetTools& AssetTools = FModuleManager::GetModuleChecked("AssetTools").Get(); + for (int32 Index = 0; Index < CreatedAssetTypeActions.Num(); ++Index) + { + AssetTools.UnregisterAssetTypeActions(CreatedAssetTypeActions[Index].ToSharedRef()); + } + } + + if (GraphPanelNodeFactory_GenericGraph.IsValid()) + { + FEdGraphUtilities::UnregisterVisualNodeFactory(GraphPanelNodeFactory_GenericGraph); + GraphPanelNodeFactory_GenericGraph.Reset(); + } + + FGenericGraphEditorStyle::Shutdown(); +} + +void FGenericGraphEditor::RegisterAssetTypeAction(IAssetTools& AssetTools, TSharedRef Action) +{ + AssetTools.RegisterAssetTypeActions(Action); + CreatedAssetTypeActions.Add(Action); +} + +IMPLEMENT_MODULE(FGenericGraphEditor, GenericGraphEditor) + +#undef LOCTEXT_NAMESPACE + diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphFactory.cpp b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphFactory.cpp new file mode 100644 index 00000000..4c9fc7bb --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphFactory.cpp @@ -0,0 +1,117 @@ +#include "GenericGraphFactory.h" +#include "GenericGraph.h" + +#include "ClassViewerModule.h" +#include "ClassViewerFilter.h" +#include "Kismet2/KismetEditorUtilities.h" +#include "Kismet2/SClassPickerDialog.h" + +#define LOCTEXT_NAMESPACE "GenericGraphFactory" + +class FAssetClassParentFilter : public IClassViewerFilter +{ +public: + FAssetClassParentFilter() + : DisallowedClassFlags(CLASS_None), bDisallowBlueprintBase(false) + {} + + /** All children of these classes will be included unless filtered out by another setting. */ + TSet< const UClass* > AllowedChildrenOfClasses; + + /** Disallowed class flags. */ + EClassFlags DisallowedClassFlags; + + /** Disallow blueprint base classes. */ + bool bDisallowBlueprintBase; + + virtual bool IsClassAllowed(const FClassViewerInitializationOptions& InInitOptions, const UClass* InClass, TSharedRef< FClassViewerFilterFuncs > InFilterFuncs) override + { + bool bAllowed= !InClass->HasAnyClassFlags(DisallowedClassFlags) + && InFilterFuncs->IfInChildOfClassesSet(AllowedChildrenOfClasses, InClass) != EFilterReturn::Failed; + + if (bAllowed && bDisallowBlueprintBase) + { + if (FKismetEditorUtilities::CanCreateBlueprintOfClass(InClass)) + { + return false; + } + } + + return bAllowed; + } + + virtual bool IsUnloadedClassAllowed(const FClassViewerInitializationOptions& InInitOptions, const TSharedRef< const IUnloadedBlueprintData > InUnloadedClassData, TSharedRef< FClassViewerFilterFuncs > InFilterFuncs) override + { + if (bDisallowBlueprintBase) + { + return false; + } + + return !InUnloadedClassData->HasAnyClassFlags(DisallowedClassFlags) + && InFilterFuncs->IfInChildOfClassesSet(AllowedChildrenOfClasses, InUnloadedClassData) != EFilterReturn::Failed; + } +}; + + +UGenericGraphFactory::UGenericGraphFactory() +{ + bCreateNew = true; + bEditAfterNew = true; + SupportedClass = UGenericGraph::StaticClass(); +} + +UGenericGraphFactory::~UGenericGraphFactory() +{ + +} + +bool UGenericGraphFactory::ConfigureProperties() +{ + // nullptr the GenericGraphClass so we can check for selection + GenericGraphClass = nullptr; + + // Load the classviewer module to display a class picker + FClassViewerModule& ClassViewerModule = FModuleManager::LoadModuleChecked("ClassViewer"); + + // Fill in options + FClassViewerInitializationOptions Options; + Options.Mode = EClassViewerMode::ClassPicker; + +#if ENGINE_MAJOR_VERSION < 5 + TSharedPtr Filter = MakeShareable(new FAssetClassParentFilter); + Options.ClassFilter = Filter; +#else // #if ENGINE_MAJOR_VERSION < 5 + TSharedRef Filter = MakeShareable(new FAssetClassParentFilter); + Options.ClassFilters.Add(Filter); +#endif // #else // #if ENGINE_MAJOR_VERSION < 5 + + Filter->DisallowedClassFlags = CLASS_Abstract | CLASS_Deprecated | CLASS_NewerVersionExists | CLASS_HideDropDown; + Filter->AllowedChildrenOfClasses.Add(UGenericGraph::StaticClass()); + + const FText TitleText = LOCTEXT("CreateGenericGraphAssetOptions", "Pick Generic Graph Class"); + UClass* ChosenClass = nullptr; + const bool bPressedOk = SClassPickerDialog::PickClass(TitleText, Options, ChosenClass, UGenericGraph::StaticClass()); + + if ( bPressedOk ) + { + GenericGraphClass = ChosenClass; + } + + return bPressedOk; +} + +UObject* UGenericGraphFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) +{ + if (GenericGraphClass != nullptr) + { + return NewObject(InParent, GenericGraphClass, Name, Flags | RF_Transactional); + } + else + { + check(Class->IsChildOf(UGenericGraph::StaticClass())); + return NewObject(InParent, Class, Name, Flags | RF_Transactional); + } + +} + +#undef LOCTEXT_NAMESPACE diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphNodeFactory.cpp b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphNodeFactory.cpp new file mode 100644 index 00000000..e79dd2cf --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Private/GenericGraphNodeFactory.cpp @@ -0,0 +1,20 @@ +#include "GenericGraphNodeFactory.h" +#include +#include "GenericGraphAssetEditor/SEdNode_GenericGraphEdge.h" +#include "GenericGraphAssetEditor/EdNode_GenericGraphNode.h" +#include "GenericGraphAssetEditor/SEdNode_GenericGraphNode.h" +#include "GenericGraphAssetEditor/EdNode_GenericGraphEdge.h" + +TSharedPtr FGraphPanelNodeFactory_GenericGraph::CreateNode(UEdGraphNode* Node) const +{ + if (UEdNode_GenericGraphNode* EdNode_GraphNode = Cast(Node)) + { + return SNew(SEdNode_GenericGraphNode, EdNode_GraphNode); + } + else if (UEdNode_GenericGraphEdge* EdNode_Edge = Cast(Node)) + { + return SNew(SEdNode_GenericGraphEdge, EdNode_Edge); + } + return nullptr; +} + diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/AssetTypeActions_GenericGraph.h b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/AssetTypeActions_GenericGraph.h new file mode 100644 index 00000000..8d7ba935 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/AssetTypeActions_GenericGraph.h @@ -0,0 +1,19 @@ +#pragma once + +#include "CoreMinimal.h" +#include "AssetTypeActions_Base.h" + +class GENERICGRAPHEDITOR_API FAssetTypeActions_GenericGraph : public FAssetTypeActions_Base +{ +public: + FAssetTypeActions_GenericGraph(EAssetTypeCategories::Type InAssetCategory); + + virtual FText GetName() const override; + virtual FColor GetTypeColor() const override; + virtual UClass* GetSupportedClass() const override; + virtual void OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor = TSharedPtr()) override; + virtual uint32 GetCategories() override; + +private: + EAssetTypeCategories::Type MyAssetCategory; +}; \ No newline at end of file diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/AutoLayout/AutoLayoutStrategy.h b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/AutoLayout/AutoLayoutStrategy.h new file mode 100644 index 00000000..52a5efb3 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/AutoLayout/AutoLayoutStrategy.h @@ -0,0 +1,40 @@ +#pragma once + +#include "CoreMinimal.h" +#include "EdGraph/EdGraph.h" +#include "GenericGraph.h" +#include "GenericGraphAssetEditor/EdGraph_GenericGraph.h" +#include "GenericGraphAssetEditor/EdNode_GenericGraphNode.h" +#include "GenericGraphAssetEditor/EdNode_GenericGraphEdge.h" +#include "GenericGraphAssetEditor/Settings_GenericGraphEditor.h" +#include "AutoLayoutStrategy.generated.h" + +UCLASS(abstract) +class GENERICGRAPHEDITOR_API UAutoLayoutStrategy : public UObject +{ + GENERATED_BODY() +public: + UAutoLayoutStrategy(); + virtual ~UAutoLayoutStrategy(); + + virtual void Layout(UEdGraph* G) {}; + + class UGenericGraphEditorSettings* Settings; + +protected: + int32 GetNodeWidth(UEdNode_GenericGraphNode* EdNode); + + int32 GetNodeHeight(UEdNode_GenericGraphNode* EdNode); + + FBox2D GetNodeBound(UEdGraphNode* EdNode); + + FBox2D GetActualBounds(UGenericGraphNode* RootNode); + + virtual void RandomLayoutOneTree(UGenericGraphNode* RootNode, const FBox2D& Bound); + +protected: + UGenericGraph* Graph; + UEdGraph_GenericGraph* EdGraph; + int32 MaxIteration; + int32 OptimalDistance; +}; diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/AutoLayout/ForceDirectedLayoutStrategy.h b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/AutoLayout/ForceDirectedLayoutStrategy.h new file mode 100644 index 00000000..e5c40c95 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/AutoLayout/ForceDirectedLayoutStrategy.h @@ -0,0 +1,24 @@ +#pragma once + +#include "CoreMinimal.h" +#include "AutoLayoutStrategy.h" +#include "ForceDirectedLayoutStrategy.generated.h" + +UCLASS() +class GENERICGRAPHEDITOR_API UForceDirectedLayoutStrategy : public UAutoLayoutStrategy +{ + GENERATED_BODY() +public: + UForceDirectedLayoutStrategy(); + virtual ~UForceDirectedLayoutStrategy(); + + virtual void Layout(UEdGraph* EdGraph) override; + +protected: + virtual FBox2D LayoutOneTree(UGenericGraphNode* RootNode, const FBox2D& PreTreeBound); + +protected: + bool bRandomInit; + float InitTemperature; + float CoolDownRate; +}; diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/AutoLayout/TreeLayoutStrategy.h b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/AutoLayout/TreeLayoutStrategy.h new file mode 100644 index 00000000..27c7ff42 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/AutoLayout/TreeLayoutStrategy.h @@ -0,0 +1,29 @@ +#pragma once + +#include "CoreMinimal.h" +#include "AutoLayoutStrategy.h" +#include "TreeLayoutStrategy.generated.h" + +UCLASS() +class GENERICGRAPHEDITOR_API UTreeLayoutStrategy : public UAutoLayoutStrategy +{ + GENERATED_BODY() +public: + UTreeLayoutStrategy(); + virtual ~UTreeLayoutStrategy(); + + virtual void Layout(UEdGraph* EdGraph) override; + +protected: + void InitPass(UGenericGraphNode* RootNode, const FVector2D& Anchor); + bool ResolveConflictPass(UGenericGraphNode* Node); + + bool ResolveConflict(UGenericGraphNode* LRoot, UGenericGraphNode* RRoot); + + void GetLeftContour(UGenericGraphNode* RootNode, int32 Level, TArray& Contour); + void GetRightContour(UGenericGraphNode* RootNode, int32 Level, TArray& Contour); + + void ShiftSubTree(UGenericGraphNode* RootNode, const FVector2D& Offset); + + void UpdateParentNodePosition(UGenericGraphNode* RootNode); +}; diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/AssetEditorToolbar_GenericGraph.h b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/AssetEditorToolbar_GenericGraph.h new file mode 100644 index 00000000..37e5d092 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/AssetEditorToolbar_GenericGraph.h @@ -0,0 +1,24 @@ + +#pragma once + +#include "CoreMinimal.h" + +class FAssetEditor_GenericGraph; +class FExtender; +class FToolBarBuilder; + +class GENERICGRAPHEDITOR_API FAssetEditorToolbar_GenericGraph : public TSharedFromThis +{ +public: + FAssetEditorToolbar_GenericGraph(TSharedPtr InGenericGraphEditor) + : GenericGraphEditor(InGenericGraphEditor) {} + + void AddGenericGraphToolbar(TSharedPtr Extender); + +private: + void FillGenericGraphToolbar(FToolBarBuilder& ToolbarBuilder); + +protected: + /** Pointer back to the blueprint editor tool that owns us */ + TWeakPtr GenericGraphEditor; +}; diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/AssetEditor_GenericGraph.h b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/AssetEditor_GenericGraph.h new file mode 100644 index 00000000..f1cc415b --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/AssetEditor_GenericGraph.h @@ -0,0 +1,137 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Settings_GenericGraphEditor.h" +#include "GenericGraph.h" + +#if ENGINE_MAJOR_VERSION == 5 +#include "UObject/ObjectSaveContext.h" +#endif // #if ENGINE_MAJOR_VERSION == 5 + +class FGGAssetEditorToolbar; + +class GENERICGRAPHEDITOR_API FAssetEditor_GenericGraph : public FAssetEditorToolkit, public FNotifyHook, public FGCObject +{ +public: + FAssetEditor_GenericGraph(); + virtual ~FAssetEditor_GenericGraph(); + + void InitGenericGraphAssetEditor(const EToolkitMode::Type Mode, const TSharedPtr< IToolkitHost >& InitToolkitHost, UGenericGraph* Graph); + + // IToolkit interface + virtual void RegisterTabSpawners(const TSharedRef& TabManager) override; + virtual void UnregisterTabSpawners(const TSharedRef& TabManager) override; + // End of IToolkit interface + + // FAssetEditorToolkit + 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; + // End of FAssetEditorToolkit + + //Toolbar + void UpdateToolbar(); + TSharedPtr GetToolbarBuilder() { return ToolbarBuilder; } + void RegisterToolbarTab(const TSharedRef& TabManager); + + + // FSerializableObject interface + virtual void AddReferencedObjects(FReferenceCollector& Collector) override; + // End of FSerializableObject interface + +#if ENGINE_MAJOR_VERSION == 5 + // FGCObject interface + virtual FString GetReferencerName() const + { + return TEXT("FAssetEditor_LTGenericGraph"); + } + // ~FGCObject interface +#endif // #if ENGINE_MAJOR_VERSION == 5 + + UGenericGraphEditorSettings* GetSettings() const; + +protected: + TSharedRef SpawnTab_Viewport(const FSpawnTabArgs& Args); + TSharedRef SpawnTab_Details(const FSpawnTabArgs& Args); + TSharedRef SpawnTab_EditorSettings(const FSpawnTabArgs& Args); + + void CreateInternalWidgets(); + TSharedRef CreateViewportWidget(); + + + void BindCommands(); + + void CreateEdGraph(); + + void CreateCommandList(); + + TSharedPtr GetCurrGraphEditor() const; + + FGraphPanelSelectionSet GetSelectedNodes() const; + + void RebuildGenericGraph(); + + // Delegates for graph editor commands + 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 GraphSettings(); + bool CanGraphSettings() const; + + void AutoArrange(); + bool CanAutoArrange() const; + + void OnRenameNode(); + bool CanRenameNodes() const; + + ////////////////////////////////////////////////////////////////////////// + // graph editor event + void OnSelectedNodesChanged(const TSet& NewSelection); + + void OnNodeDoubleClicked(UEdGraphNode* Node); + + void OnFinishedChangingProperties(const FPropertyChangedEvent& PropertyChangedEvent); + +#if ENGINE_MAJOR_VERSION < 5 + void OnPackageSaved(const FString& PackageFileName, UObject* Outer); +#else // #if ENGINE_MAJOR_VERSION < 5 + void OnPackageSavedWithContext(const FString& PackageFileName, UPackage* Package, FObjectPostSaveContext ObjectSaveContext); +#endif // #else // #if ENGINE_MAJOR_VERSION < 5 + +protected: + UGenericGraphEditorSettings* GenricGraphEditorSettings; + + UGenericGraph* EditingGraph; + + //Toolbar + TSharedPtr ToolbarBuilder; + + /** Handle to the registered OnPackageSave delegate */ + FDelegateHandle OnPackageSavedDelegateHandle; + + TSharedPtr ViewportWidget; + TSharedPtr PropertyWidget; + TSharedPtr EditorSettingsWidget; + + /** The command list for this editor */ + TSharedPtr GraphEditorCommands; +}; + + diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/AssetGraphSchema_GenericGraph.h b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/AssetGraphSchema_GenericGraph.h new file mode 100644 index 00000000..faf74b9c --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/AssetGraphSchema_GenericGraph.h @@ -0,0 +1,90 @@ +#pragma once + +#include "CoreMinimal.h" +#include "GenericGraph.h" +#include "GenericGraphNode.h" +#include "GenericGraphEdge.h" +#include "AssetGraphSchema_GenericGraph.generated.h" + +class UEdNode_GenericGraphNode; +class UEdNode_GenericGraphEdge; +class UAutoLayoutStrategy; + +/** Action to add a node to the graph */ +USTRUCT() +struct GENERICGRAPHEDITOR_API FAssetSchemaAction_GenericGraph_NewNode : public FEdGraphSchemaAction +{ + GENERATED_USTRUCT_BODY(); + +public: + FAssetSchemaAction_GenericGraph_NewNode(): NodeTemplate(nullptr) {} + + FAssetSchemaAction_GenericGraph_NewNode(const FText& InNodeCategory, const FText& InMenuDesc, const FText& InToolTip, const int32 InGrouping) + : FEdGraphSchemaAction(InNodeCategory, InMenuDesc, InToolTip, InGrouping), NodeTemplate(nullptr) {} + + virtual UEdGraphNode* PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode = true) override; + virtual void AddReferencedObjects(FReferenceCollector& Collector) override; + + UEdNode_GenericGraphNode* NodeTemplate; +}; + +USTRUCT() +struct GENERICGRAPHEDITOR_API FAssetSchemaAction_GenericGraph_NewEdge : public FEdGraphSchemaAction +{ + GENERATED_USTRUCT_BODY(); + +public: + FAssetSchemaAction_GenericGraph_NewEdge(): NodeTemplate(nullptr){} + + FAssetSchemaAction_GenericGraph_NewEdge(const FText& InNodeCategory, const FText& InMenuDesc, const FText& InToolTip, const int32 InGrouping) + : FEdGraphSchemaAction(InNodeCategory, InMenuDesc, InToolTip, InGrouping), NodeTemplate(nullptr) {} + + virtual UEdGraphNode* PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode = true) override; + virtual void AddReferencedObjects(FReferenceCollector& Collector) override; + + UEdNode_GenericGraphEdge* NodeTemplate; +}; + +UCLASS(MinimalAPI) +class UAssetGraphSchema_GenericGraph : public UEdGraphSchema +{ + GENERATED_BODY() + +public: + void GetBreakLinkToSubMenuActions(class UToolMenu* Menu, class UEdGraphPin* InGraphPin); + + virtual EGraphType GetGraphType(const UEdGraph* TestEdGraph) const override; + + virtual void GetGraphContextActions(FGraphContextMenuBuilder& ContextMenuBuilder) const override; + + virtual void GetContextMenuActions(class UToolMenu* Menu, class UGraphNodeContextMenuContext* Context) const override; + + virtual const FPinConnectionResponse CanCreateConnection(const UEdGraphPin* A, const UEdGraphPin* B) const override; + + virtual bool TryCreateConnection(UEdGraphPin* A, UEdGraphPin* B) const override; + virtual bool CreateAutomaticConversionNodeAndConnections(UEdGraphPin* A, UEdGraphPin* B) const override; + + virtual class FConnectionDrawingPolicy* CreateConnectionDrawingPolicy(int32 InBackLayerID, int32 InFrontLayerID, float InZoomFactor, const FSlateRect& InClippingRect, class FSlateWindowElementList& InDrawElements, class UEdGraph* InGraphObj) const override; + + virtual FLinearColor GetPinTypeColor(const FEdGraphPinType& PinType) const override; + + virtual void BreakNodeLinks(UEdGraphNode& TargetNode) const override; + + virtual void BreakPinLinks(UEdGraphPin& TargetPin, bool bSendsNodeNotifcation) const override; + + virtual void BreakSinglePinLink(UEdGraphPin* SourcePin, UEdGraphPin* TargetPin) const override; + + virtual UEdGraphPin* DropPinOnNode(UEdGraphNode* InTargetNode, const FName& InSourcePinName, const FEdGraphPinType& InSourcePinType, EEdGraphPinDirection InSourcePinDirection) const override; + + virtual bool SupportsDropPinOnNode(UEdGraphNode* InTargetNode, const FEdGraphPinType& InSourcePinType, EEdGraphPinDirection InSourcePinDirection, FText& OutErrorMessage) const override; + + virtual bool IsCacheVisualizationOutOfDate(int32 InVisualizationCacheID) const override; + + virtual int32 GetCurrentVisualizationCacheID() const override; + + virtual void ForceVisualizationCacheClear() const override; + +private: + static int32 CurrentCacheRefreshID; +}; + diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/Colors_GenericGraph.h b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/Colors_GenericGraph.h new file mode 100644 index 00000000..5258d1a8 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/Colors_GenericGraph.h @@ -0,0 +1,45 @@ +#pragma once + +#include "CoreMinimal.h" + +namespace GenericGraphColors +{ + namespace NodeBody + { + const FLinearColor Default(0.1f, 0.1f, 0.1f); + const FLinearColor Root(0.5f, 0.5f, 0.5f, 0.1f); + const FLinearColor Error(1.0f, 0.0f, 0.0f); + } + + namespace NodeBorder + { + const FLinearColor Inactive(0.08f, 0.08f, 0.08f); + const FLinearColor Root(0.2f, 0.2f, 0.2f, 0.2f); + const FLinearColor Selected(1.00f, 0.08f, 0.08f); + const FLinearColor ActiveDebugging(1.0f, 1.0f, 0.0f); + const FLinearColor InactiveDebugging(0.4f, 0.4f, 0.0f); + const FLinearColor HighlightAbortRange0(0.0f, 0.22f, 0.4f); + const FLinearColor HighlightAbortRange1(0.0f, 0.4f, 0.22f); + const FLinearColor Disconnected(0.f, 0.f, 0.f); + const FLinearColor BrokenWithParent(1.f, 0.f, 1.f); + const FLinearColor QuickFind(0.f, 0.8f, 0.f); + } + + namespace Pin + { + const FLinearColor Diff(0.9f, 0.2f, 0.15f); + const FLinearColor Hover(1.0f, 0.7f, 0.0f); + const FLinearColor Default(0.02f, 0.02f, 0.02f); + const FLinearColor SingleNode(0.02f, 0.02f, 0.02f); + } + + namespace Connection + { + const FLinearColor Default(1.0f, 1.0f, 1.0f); + } + + namespace Action + { + const FLinearColor DragMarker(1.0f, 1.0f, 0.2f); + } +} diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/ConnectionDrawingPolicy_GenericGraph.h b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/ConnectionDrawingPolicy_GenericGraph.h new file mode 100644 index 00000000..e375e816 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/ConnectionDrawingPolicy_GenericGraph.h @@ -0,0 +1,27 @@ +#pragma once + +#include "CoreMinimal.h" +#include "ConnectionDrawingPolicy.h" + +class GENERICGRAPHEDITOR_API FConnectionDrawingPolicy_GenericGraph : public FConnectionDrawingPolicy +{ +protected: + UEdGraph* GraphObj; + TMap NodeWidgetMap; + +public: + FConnectionDrawingPolicy_GenericGraph(int32 InBackLayerID, int32 InFrontLayerID, float ZoomFactor, const FSlateRect& InClippingRect, FSlateWindowElementList& InDrawElements, UEdGraph* InGraphObj); + + // FConnectionDrawingPolicy interface + virtual void DetermineWiringStyle(UEdGraphPin* OutputPin, UEdGraphPin* InputPin, /*inout*/ FConnectionParams& Params) override; + virtual void Draw(TMap, FArrangedWidget>& PinGeometries, 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& 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); +}; diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/EdGraph_GenericGraph.h b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/EdGraph_GenericGraph.h new file mode 100644 index 00000000..5cb92af3 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/EdGraph_GenericGraph.h @@ -0,0 +1,39 @@ +#pragma once + +#include "CoreMinimal.h" +#include "EdGraph/EdGraph.h" +#include "EdGraph_GenericGraph.generated.h" + +class UGenericGraph; +class UGenericGraphNode; +class UGenericGraphEdge; +class UEdNode_GenericGraphNode; +class UEdNode_GenericGraphEdge; + +UCLASS() +class GENERICGRAPHEDITOR_API UEdGraph_GenericGraph : public UEdGraph +{ + GENERATED_BODY() + +public: + UEdGraph_GenericGraph(); + virtual ~UEdGraph_GenericGraph(); + + virtual void RebuildGenericGraph(); + + UGenericGraph* GetGenericGraph() const; + + virtual bool Modify(bool bAlwaysMarkDirty = true) override; + virtual void PostEditUndo() override; + + UPROPERTY(Transient) + TMap NodeMap; + + UPROPERTY(Transient) + TMap EdgeMap; + +protected: + void Clear(); + + void SortNodes(UGenericGraphNode* RootNode); +}; diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/EdNode_GenericGraphEdge.h b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/EdNode_GenericGraphEdge.h new file mode 100644 index 00000000..ac480d8f --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/EdNode_GenericGraphEdge.h @@ -0,0 +1,42 @@ +#pragma once + +#include "CoreMinimal.h" +#include "EdGraph/EdGraphNode.h" +#include "EdNode_GenericGraphEdge.generated.h" + +class UGenericGraphNode; +class UGenericGraphEdge; +class UEdNode_GenericGraphNode; + +UCLASS(MinimalAPI) +class UEdNode_GenericGraphEdge : public UEdGraphNode +{ + GENERATED_BODY() + +public: + UEdNode_GenericGraphEdge(); + + UPROPERTY() + class UEdGraph* Graph; + + UPROPERTY(VisibleAnywhere, Instanced, Category = "GenericGraph") + UGenericGraphEdge* GenericGraphEdge; + + void SetEdge(UGenericGraphEdge* Edge); + + virtual void AllocateDefaultPins() override; + + virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override; + + virtual void PinConnectionListChanged(UEdGraphPin* Pin) override; + + virtual void PrepareForCopying() override; + + virtual UEdGraphPin* GetInputPin() const { return Pins[0]; } + virtual UEdGraphPin* GetOutputPin() const { return Pins[1]; } + + void CreateConnections(UEdNode_GenericGraphNode* Start, UEdNode_GenericGraphNode* End); + + UEdNode_GenericGraphNode* GetStartNode(); + UEdNode_GenericGraphNode* GetEndNode(); +}; diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/EdNode_GenericGraphNode.h b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/EdNode_GenericGraphNode.h new file mode 100644 index 00000000..258a089c --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/EdNode_GenericGraphNode.h @@ -0,0 +1,42 @@ +#pragma once + +#include "CoreMinimal.h" +#include "EdGraph/EdGraphNode.h" +#include "GenericGraphNode.h" +#include "EdNode_GenericGraphNode.generated.h" + +class UEdNode_GenericGraphEdge; +class UEdGraph_GenericGraph; +class SEdNode_GenericGraphNode; + +UCLASS(MinimalAPI) +class UEdNode_GenericGraphNode : public UEdGraphNode +{ + GENERATED_BODY() + +public: + UEdNode_GenericGraphNode(); + virtual ~UEdNode_GenericGraphNode(); + + UPROPERTY(VisibleAnywhere, Instanced, Category = "GenericGraph") + UGenericGraphNode* GenericGraphNode; + + void SetGenericGraphNode(UGenericGraphNode* InNode); + UEdGraph_GenericGraph* GetGenericGraphEdGraph(); + + SEdNode_GenericGraphNode* SEdNode; + + virtual void AllocateDefaultPins() override; + virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override; + virtual void PrepareForCopying() override; + virtual void AutowireNewNode(UEdGraphPin* FromPin) override; + + virtual FLinearColor GetBackgroundColor() const; + virtual UEdGraphPin* GetInputPin() const; + virtual UEdGraphPin* GetOutputPin() const; + +#if WITH_EDITOR + virtual void PostEditUndo() override; +#endif + +}; diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/EditorCommands_GenericGraph.h b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/EditorCommands_GenericGraph.h new file mode 100644 index 00000000..ff6c5bd9 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/EditorCommands_GenericGraph.h @@ -0,0 +1,18 @@ +#pragma once + +#include "CoreMinimal.h" + +class GENERICGRAPHEDITOR_API FEditorCommands_GenericGraph : public TCommands +{ +public: + /** Constructor */ + FEditorCommands_GenericGraph() + : TCommands("GenericGraphEditor", NSLOCTEXT("Contexts", "GenericGraphEditor", "Generic Graph Editor"), NAME_None, FAppStyle::GetAppStyleSetName()) + { + } + + TSharedPtr GraphSettings; + TSharedPtr AutoArrange; + + virtual void RegisterCommands() override; +}; diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/GenericGraphDragConnection.h b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/GenericGraphDragConnection.h new file mode 100644 index 00000000..bd0a91af --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/GenericGraphDragConnection.h @@ -0,0 +1,52 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Input/DragAndDrop.h" +#include "Input/Reply.h" +#include "Widgets/SWidget.h" +#include "SGraphPin.h" +#include "GraphEditorDragDropAction.h" + +class SGraphPanel; +class UEdGraph; + +class FGenericGraphDragConnection : public FGraphEditorDragDropAction +{ +public: + DRAG_DROP_OPERATOR_TYPE(FDragConnection, FGraphEditorDragDropAction) + + typedef TArray FDraggedPinTable; + static TSharedRef New(const TSharedRef& InGraphPanel, const FDraggedPinTable& InStartingPins); + + // FDragDropOperation interface + virtual void OnDrop(bool bDropWasHandled, const FPointerEvent& MouseEvent) override; + // End of FDragDropOperation interface + + // FGraphEditorDragDropAction interface + virtual void HoverTargetChanged() override; + virtual FReply DroppedOnPin(FVector2D ScreenPosition, FVector2D GraphPosition) override; + virtual FReply DroppedOnNode(FVector2D ScreenPosition, FVector2D GraphPosition) override; + virtual FReply DroppedOnPanel(const TSharedRef< SWidget >& Panel, FVector2D ScreenPosition, FVector2D GraphPosition, UEdGraph& Graph) override; + virtual void OnDragged(const class FDragDropEvent& DragDropEvent) override; + // End of FGraphEditorDragDropAction interface + + /* + * Function to check validity of graph pins in the StartPins list. This check helps to prevent processing graph pins which are outdated. + */ + virtual void ValidateGraphPinList(TArray& OutValidPins); + +protected: + typedef FGraphEditorDragDropAction Super; + + // Constructor: Make sure to call Construct() after factorying one of these + FGenericGraphDragConnection(const TSharedRef& GraphPanel, const FDraggedPinTable& DraggedPins); + +protected: + TSharedPtr GraphPanel; + FDraggedPinTable DraggingPins; + + /** Offset information for the decorator widget */ + FVector2D DecoratorAdjust; +}; diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/GenericGraphEditorStyle.h b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/GenericGraphEditorStyle.h new file mode 100644 index 00000000..577312f3 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/GenericGraphEditorStyle.h @@ -0,0 +1,16 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Styling/SlateStyle.h" + +class GENERICGRAPHEDITOR_API FGenericGraphEditorStyle +{ +public: + static void Initialize(); + static void Shutdown(); + + static const FName& GetStyleSetName(); + +private: + static TSharedPtr StyleSet; +}; diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/SEdNode_GenericGraphEdge.h b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/SEdNode_GenericGraphEdge.h new file mode 100644 index 00000000..8505a5f6 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/SEdNode_GenericGraphEdge.h @@ -0,0 +1,40 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Styling/SlateColor.h" +#include "Widgets/DeclarativeSyntaxSupport.h" +#include "Widgets/SWidget.h" +#include "SNodePanel.h" +#include "SGraphNode.h" + +class SToolTip; +class UEdNode_GenericGraphEdge; + +class GENERICGRAPHEDITOR_API SEdNode_GenericGraphEdge : public SGraphNode +{ +public: + SLATE_BEGIN_ARGS(SEdNode_GenericGraphEdge){} + SLATE_END_ARGS() + + void Construct(const FArguments& InArgs, UEdNode_GenericGraphEdge* InNode); + + virtual bool RequiresSecondPassLayout() const override; + virtual void PerformSecondPassLayout(const TMap< UObject*, TSharedRef >& 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; + + void OnNameTextCommited(const FText& InText, ETextCommit::Type CommitInfo); + +protected: + FSlateColor GetEdgeColor() const; + + const FSlateBrush* GetEdgeImage() const; + + EVisibility GetEdgeImageVisibility() const; + EVisibility GetEdgeTitleVisbility() const; +private: + TSharedPtr TextEntryWidget; +}; diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/SEdNode_GenericGraphNode.h b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/SEdNode_GenericGraphNode.h new file mode 100644 index 00000000..1d4d9b99 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/SEdNode_GenericGraphNode.h @@ -0,0 +1,32 @@ +#pragma once + +#include "CoreMinimal.h" +#include "SGraphNode.h" + +class UEdNode_GenericGraphNode; + +class GENERICGRAPHEDITOR_API SEdNode_GenericGraphNode : public SGraphNode +{ +public: + SLATE_BEGIN_ARGS(SEdNode_GenericGraphNode) {} + SLATE_END_ARGS() + + void Construct(const FArguments& InArgs, UEdNode_GenericGraphNode* InNode); + + virtual void UpdateGraphNode() override; + virtual void CreatePinWidgets() override; + virtual void AddPin(const TSharedRef& PinToAdd) override; + virtual bool IsNameReadOnly() const override; + + void OnNameTextCommited(const FText& InText, ETextCommit::Type CommitInfo); + + virtual FSlateColor GetBorderBackgroundColor() const; + virtual FSlateColor GetBackgroundColor() const; + + virtual EVisibility GetDragOverMarkerVisibility() const; + + virtual const FSlateBrush* GetNameIcon() const; + +protected: +}; + diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/Settings_GenericGraphEditor.h b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/Settings_GenericGraphEditor.h new file mode 100644 index 00000000..c1e39156 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphAssetEditor/Settings_GenericGraphEditor.h @@ -0,0 +1,42 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Settings_GenericGraphEditor.generated.h" + +UENUM(BlueprintType) +enum class EAutoLayoutStrategy : uint8 +{ + Tree, + ForceDirected, +}; + +UCLASS() +class GENERICGRAPHEDITOR_API UGenericGraphEditorSettings : public UObject +{ + GENERATED_BODY() + +public: + UGenericGraphEditorSettings(); + virtual ~UGenericGraphEditorSettings(); + + UPROPERTY(EditDefaultsOnly, Category = "AutoArrange") + float OptimalDistance; + + UPROPERTY(EditDefaultsOnly, AdvancedDisplay, Category = "AutoArrange") + EAutoLayoutStrategy AutoLayoutStrategy; + + UPROPERTY(EditDefaultsOnly, AdvancedDisplay, Category = "AutoArrange") + int32 MaxIteration; + + UPROPERTY(EditDefaultsOnly, AdvancedDisplay, Category = "AutoArrange") + bool bFirstPassOnly; + + UPROPERTY(EditDefaultsOnly, AdvancedDisplay, Category = "AutoArrange") + bool bRandomInit; + + UPROPERTY(EditDefaultsOnly, AdvancedDisplay, Category = "AutoArrange") + float InitTemperature; + + UPROPERTY(EditDefaultsOnly, AdvancedDisplay, Category = "AutoArrange") + float CoolDownRate; +}; diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphEditor.h b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphEditor.h new file mode 100644 index 00000000..f710fc2c --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphEditor.h @@ -0,0 +1,23 @@ +#pragma once +#include "Modules/ModuleManager.h" +#include "GenericGraphEditorModule.h" +#include +#include + +class FGenericGraphEditor : public IGenericGraphEditor +{ + /** IModuleInterface implementation */ + virtual void StartupModule() override; + virtual void ShutdownModule() override; + + +private: + void RegisterAssetTypeAction(IAssetTools& AssetTools, TSharedRef Action); + +private: + TArray< TSharedPtr > CreatedAssetTypeActions; + + EAssetTypeCategories::Type GenericGraphAssetCategoryBit; + + TSharedPtr GraphPanelNodeFactory_GenericGraph; +}; \ No newline at end of file diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphEditorModule.h b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphEditorModule.h new file mode 100644 index 00000000..c042dc1f --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphEditorModule.h @@ -0,0 +1,34 @@ +#include"Modules/ModuleManager.h" + +DECLARE_LOG_CATEGORY_EXTERN(GenericGraphEditor, Log, All); + +/** + * The public interface to this module + */ +class IGenericGraphEditor : public IModuleInterface +{ + +public: + + /** + * Singleton-like access to this module's interface. This is just for convenience! + * Beware of calling this during the shutdown phase, though. Your module might have been unloaded already. + * + * @return Returns singleton instance, loading the module on demand if needed + */ + static IGenericGraphEditor& Get() + { + return FModuleManager::LoadModuleChecked< IGenericGraphEditor >("GenericGraphEditor"); + } + + /** + * Checks to see if this module is loaded and ready. It is only valid to call Get() if IsAvailable() returns true. + * + * @return True if the module is loaded and ready to use + */ + static bool IsAvailable() + { + return FModuleManager::Get().IsModuleLoaded("GenericGraphEditor"); + } +}; + diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphEditorPCH.h b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphEditorPCH.h new file mode 100644 index 00000000..f0385f7d --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphEditorPCH.h @@ -0,0 +1,13 @@ +#pragma once + +#include "GenericGraph.h" +#include "GenericGraphNode.h" +#include "GenericGraphEdge.h" + +// You should place include statements to your module's private header files here. You only need to +// add includes for headers that are used in most of your module's source files though. +#include "GenericGraphEditor.h" + +#define LOG_INFO(FMT, ...) UE_LOG(GenericGraphEditor, Display, (FMT), ##__VA_ARGS__) +#define LOG_WARNING(FMT, ...) UE_LOG(GenericGraphEditor, Warning, (FMT), ##__VA_ARGS__) +#define LOG_ERROR(FMT, ...) UE_LOG(GenericGraphEditor, Error, (FMT), ##__VA_ARGS__) diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphFactory.h b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphFactory.h new file mode 100644 index 00000000..00cc44d4 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphFactory.h @@ -0,0 +1,22 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Factories/Factory.h" +#include "GenericGraph.h" +#include "GenericGraphFactory.generated.h" + +UCLASS() +class GENERICGRAPHEDITOR_API UGenericGraphFactory : public UFactory +{ + GENERATED_BODY() + +public: + UGenericGraphFactory(); + virtual ~UGenericGraphFactory(); + + UPROPERTY(EditAnywhere, Category=DataAsset) + TSubclassOf GenericGraphClass; + + virtual bool ConfigureProperties() override; + virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; +}; diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphNodeFactory.h b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphNodeFactory.h new file mode 100644 index 00000000..5feb3345 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphEditor/Public/GenericGraphNodeFactory.h @@ -0,0 +1,8 @@ +#pragma once +#include +#include + +class FGraphPanelNodeFactory_GenericGraph : public FGraphPanelNodeFactory +{ + virtual TSharedPtr CreateNode(UEdGraphNode* Node) const override; +}; \ No newline at end of file diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/GenericGraphRuntime.Build.cs b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/GenericGraphRuntime.Build.cs new file mode 100644 index 00000000..e3d5c5d9 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/GenericGraphRuntime.Build.cs @@ -0,0 +1,51 @@ +using UnrealBuildTool; + +public class GenericGraphRuntime : ModuleRules +{ + public GenericGraphRuntime(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + bLegacyPublicIncludePaths = false; + ShadowVariableWarningLevel = WarningLevel.Error; + + PublicIncludePaths.AddRange( + new string[] { + // ... add public include paths required here ... + } + ); + + PrivateIncludePaths.AddRange( + new string[] { + "GenericGraphRuntime/Private", + // ... add other private include paths required here ... + } + ); + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + "CoreUObject", + "Engine", + // ... add other public dependencies that you statically link with here ... + } + ); + + PrivateDependencyModuleNames.AddRange( + new string[] + { + // ... add private dependencies that you statically link with here ... + "Slate", + "SlateCore", + "GameplayTags" + } + ); + + DynamicallyLoadedModuleNames.AddRange( + new string[] + { + // ... add any modules that your module loads dynamically here ... + } + ); + } +} \ No newline at end of file diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Private/GenericGraph.cpp b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Private/GenericGraph.cpp new file mode 100644 index 00000000..c2bbb302 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Private/GenericGraph.cpp @@ -0,0 +1,136 @@ +#include "GenericGraph.h" +#include "GenericGraphRuntimePCH.h" +#include "Engine/Engine.h" + +#define LOCTEXT_NAMESPACE "GenericGraph" + +UGenericGraph::UGenericGraph() +{ + NodeType = UGenericGraphNode::StaticClass(); + EdgeType = UGenericGraphEdge::StaticClass(); + + bEdgeEnabled = true; + +#if WITH_EDITORONLY_DATA + EdGraph = nullptr; + + bCanRenameNode = true; +#endif +} + +UGenericGraph::~UGenericGraph() +{ + +} + +void UGenericGraph::Print(bool ToConsole /*= true*/, bool ToScreen /*= true*/) +{ + int Level = 0; + TArray CurrLevelNodes = RootNodes; + TArray NextLevelNodes; + + while (CurrLevelNodes.Num() != 0) + { + for (int i = 0; i < CurrLevelNodes.Num(); ++i) + { + UGenericGraphNode* Node = CurrLevelNodes[i]; + check(Node != nullptr); + + FString Message = FString::Printf(TEXT("%s, Level %d"), *Node->GetDescription().ToString(), Level); + + if (ToConsole) + { + LOG_INFO(TEXT("%s"), *Message); + } + + if (ToScreen && GEngine != nullptr) + { + GEngine->AddOnScreenDebugMessage(-1, 15.f, FColor::Blue, Message); + } + + for (int j = 0; j < Node->ChildrenNodes.Num(); ++j) + { + NextLevelNodes.Add(Node->ChildrenNodes[j]); + } + } + + CurrLevelNodes = NextLevelNodes; + NextLevelNodes.Reset(); + ++Level; + } +} + +int UGenericGraph::GetLevelNum() const +{ + int Level = 0; + TArray CurrLevelNodes = RootNodes; + TArray NextLevelNodes; + + while (CurrLevelNodes.Num() != 0) + { + for (int i = 0; i < CurrLevelNodes.Num(); ++i) + { + UGenericGraphNode* Node = CurrLevelNodes[i]; + check(Node != nullptr); + + for (int j = 0; j < Node->ChildrenNodes.Num(); ++j) + { + NextLevelNodes.Add(Node->ChildrenNodes[j]); + } + } + + CurrLevelNodes = NextLevelNodes; + NextLevelNodes.Reset(); + ++Level; + } + + return Level; +} + +void UGenericGraph::GetNodesByLevel(int Level, TArray& Nodes) +{ + int CurrLEvel = 0; + TArray NextLevelNodes; + + Nodes = RootNodes; + + while (Nodes.Num() != 0) + { + if (CurrLEvel == Level) + break; + + for (int i = 0; i < Nodes.Num(); ++i) + { + UGenericGraphNode* Node = Nodes[i]; + check(Node != nullptr); + + for (int j = 0; j < Node->ChildrenNodes.Num(); ++j) + { + NextLevelNodes.Add(Node->ChildrenNodes[j]); + } + } + + Nodes = NextLevelNodes; + NextLevelNodes.Reset(); + ++CurrLEvel; + } +} + +void UGenericGraph::ClearGraph() +{ + for (int i = 0; i < AllNodes.Num(); ++i) + { + UGenericGraphNode* Node = AllNodes[i]; + if (Node) + { + Node->ParentNodes.Empty(); + Node->ChildrenNodes.Empty(); + Node->Edges.Empty(); + } + } + + AllNodes.Empty(); + RootNodes.Empty(); +} + +#undef LOCTEXT_NAMESPACE diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Private/GenericGraphEdge.cpp b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Private/GenericGraphEdge.cpp new file mode 100644 index 00000000..6d0118c7 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Private/GenericGraphEdge.cpp @@ -0,0 +1,23 @@ +#include "GenericGraphEdge.h" + +UGenericGraphEdge::UGenericGraphEdge() +{ + +} + +UGenericGraphEdge::~UGenericGraphEdge() +{ + +} + +UGenericGraph* UGenericGraphEdge::GetGraph() const +{ + return Graph; +} + +#if WITH_EDITOR +void UGenericGraphEdge::SetNodeTitle(const FText& NewTitle) +{ + NodeTitle = NewTitle; +} +#endif // #if WITH_EDITOR \ No newline at end of file diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Private/GenericGraphNode.cpp b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Private/GenericGraphNode.cpp new file mode 100644 index 00000000..548cdc3b --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Private/GenericGraphNode.cpp @@ -0,0 +1,90 @@ +#include "GenericGraphNode.h" +#include "GenericGraph.h" + +#define LOCTEXT_NAMESPACE "GenericGraphNode" + +UGenericGraphNode::UGenericGraphNode() +{ +#if WITH_EDITORONLY_DATA + CompatibleGraphType = UGenericGraph::StaticClass(); + + BackgroundColor = FLinearColor::Black; +#endif +} + +UGenericGraphNode::~UGenericGraphNode() +{ +} + +UGenericGraphEdge* UGenericGraphNode::GetEdge(UGenericGraphNode* ChildNode) +{ + return Edges.Contains(ChildNode) ? Edges.FindChecked(ChildNode) : nullptr; +} + +FText UGenericGraphNode::GetDescription_Implementation() const +{ + return LOCTEXT("NodeDesc", "Generic Graph Node"); +} + +#if WITH_EDITOR +bool UGenericGraphNode::IsNameEditable() const +{ + return true; +} + +FLinearColor UGenericGraphNode::GetBackgroundColor() const +{ + return BackgroundColor; +} + +FText UGenericGraphNode::GetNodeTitle() const +{ + return NodeTitle.IsEmpty() ? GetDescription() : NodeTitle; +} + +void UGenericGraphNode::SetNodeTitle(const FText& NewTitle) +{ + NodeTitle = NewTitle; +} + +bool UGenericGraphNode::CanCreateConnection(UGenericGraphNode* Other, FText& ErrorMessage) +{ + return true; +} + +bool UGenericGraphNode::CanCreateConnectionTo(UGenericGraphNode* Other, int32 NumberOfChildrenNodes, FText& ErrorMessage) +{ + if (ChildrenLimitType == ENodeLimit::Limited && NumberOfChildrenNodes >= ChildrenLimit) + { + ErrorMessage = FText::FromString("Children limit exceeded"); + return false; + } + + return CanCreateConnection(Other, ErrorMessage); +} + +bool UGenericGraphNode::CanCreateConnectionFrom(UGenericGraphNode* Other, int32 NumberOfParentNodes, FText& ErrorMessage) +{ + if (ParentLimitType == ENodeLimit::Limited && NumberOfParentNodes >= ParentLimit) + { + ErrorMessage = FText::FromString("Parent limit exceeded"); + return false; + } + + return true; +} + + +#endif + +bool UGenericGraphNode::IsLeafNode() const +{ + return ChildrenNodes.Num() == 0; +} + +UGenericGraph* UGenericGraphNode::GetGraph() const +{ + return Graph; +} + +#undef LOCTEXT_NAMESPACE diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Private/GenericGraphRuntime.cpp b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Private/GenericGraphRuntime.cpp new file mode 100644 index 00000000..bfc33a53 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Private/GenericGraphRuntime.cpp @@ -0,0 +1,29 @@ +#include "GenericGraphRuntimePCH.h" + +DEFINE_LOG_CATEGORY(GenericGraphRuntime) + +class FGenericGraphRuntime : public IGenericGraphRuntime +{ + /** IModuleInterface implementation */ + virtual void StartupModule() override; + virtual void ShutdownModule() override; +}; + +IMPLEMENT_MODULE( FGenericGraphRuntime, GenericGraphRuntime ) + + + +void FGenericGraphRuntime::StartupModule() +{ + // This code will execute after your module is loaded into memory (but after global variables are initialized, of course.) +} + + +void FGenericGraphRuntime::ShutdownModule() +{ + // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, + // we call this function before unloading the module. +} + + + diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Private/GenericGraphRuntimePCH.h b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Private/GenericGraphRuntimePCH.h new file mode 100644 index 00000000..18feed8f --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Private/GenericGraphRuntimePCH.h @@ -0,0 +1,12 @@ +#pragma once + +// #include "CoreUObject.h" +// #include "Engine.h" + +// You should place include statements to your module's private header files here. You only need to +// add includes for headers that are used in most of your module's source files though. +#include "IGenericGraphRuntime.h" + +#define LOG_INFO(FMT, ...) UE_LOG(GenericGraphRuntime, Display, (FMT), ##__VA_ARGS__) +#define LOG_WARNING(FMT, ...) UE_LOG(GenericGraphRuntime, Warning, (FMT), ##__VA_ARGS__) +#define LOG_ERROR(FMT, ...) UE_LOG(GenericGraphRuntime, Error, (FMT), ##__VA_ARGS__) diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Public/GenericGraph.h b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Public/GenericGraph.h new file mode 100644 index 00000000..1ac0d39d --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Public/GenericGraph.h @@ -0,0 +1,60 @@ +#pragma once + +#include "CoreMinimal.h" +#include "GenericGraphNode.h" +#include "GenericGraphEdge.h" +#include "GameplayTagContainer.h" +#include "GenericGraph.generated.h" + +UCLASS(Blueprintable) +class GENERICGRAPHRUNTIME_API UGenericGraph : public UObject +{ + GENERATED_BODY() + +public: + UGenericGraph(); + virtual ~UGenericGraph(); + + UPROPERTY(EditDefaultsOnly, Category = "GenericGraph") + FString Name; + + UPROPERTY(EditDefaultsOnly, Category = "GenericGraph") + TSubclassOf NodeType; + + UPROPERTY(EditDefaultsOnly, Category = "GenericGraph") + TSubclassOf EdgeType; + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "GenericGraph") + FGameplayTagContainer GraphTags; + + UPROPERTY(BlueprintReadOnly, Category = "GenericGraph") + TArray RootNodes; + + UPROPERTY(BlueprintReadOnly, Category = "GenericGraph") + TArray AllNodes; + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "GenericGraph") + bool bEdgeEnabled; + + UFUNCTION(BlueprintCallable, Category = "GenericGraph") + void Print(bool ToConsole = true, bool ToScreen = true); + + UFUNCTION(BlueprintCallable, Category = "GenericGraph") + int GetLevelNum() const; + + UFUNCTION(BlueprintCallable, Category = "GenericGraph") + void GetNodesByLevel(int Level, TArray& Nodes); + + void ClearGraph(); + +#if WITH_EDITORONLY_DATA + UPROPERTY() + class UEdGraph* EdGraph; + + UPROPERTY(EditDefaultsOnly, Category = "GenericGraph_Editor") + bool bCanRenameNode; + + UPROPERTY(EditDefaultsOnly, Category = "GenericGraph_Editor") + bool bCanBeCyclical; +#endif +}; diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Public/GenericGraphEdge.h b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Public/GenericGraphEdge.h new file mode 100644 index 00000000..3df297b4 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Public/GenericGraphEdge.h @@ -0,0 +1,48 @@ +#pragma once + +#include "CoreMinimal.h" +#include "GenericGraphNode.h" +#include "GenericGraphEdge.generated.h" + +class UGenericGraph; + +UCLASS(Blueprintable) +class GENERICGRAPHRUNTIME_API UGenericGraphEdge : public UObject +{ + GENERATED_BODY() + +public: + UGenericGraphEdge(); + virtual ~UGenericGraphEdge(); + + UPROPERTY(VisibleAnywhere, Category = "GenericGraphNode") + UGenericGraph* Graph; + + UPROPERTY(BlueprintReadOnly, Category = "GenericGraphEdge") + UGenericGraphNode* StartNode; + + UPROPERTY(BlueprintReadOnly, Category = "GenericGraphEdge") + UGenericGraphNode* EndNode; + + UFUNCTION(BlueprintPure, Category = "GenericGraphEdge") + UGenericGraph* GetGraph() const; + +#if WITH_EDITORONLY_DATA + UPROPERTY(EditDefaultsOnly, Category = "GenericGraphNode_Editor") + bool bShouldDrawTitle = false; + + UPROPERTY(EditDefaultsOnly, Category = "GenericGraphNode_Editor") + FText NodeTitle; + + UPROPERTY(EditDefaultsOnly, Category = "GenericGraphEdge") + FLinearColor EdgeColour = FLinearColor(0.9f, 0.9f, 0.9f, 1.0f); +#endif + +#if WITH_EDITOR + virtual FText GetNodeTitle() const { return NodeTitle; } + FLinearColor GetEdgeColour() { return EdgeColour; } + + virtual void SetNodeTitle(const FText& NewTitle); +#endif + +}; diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Public/GenericGraphNode.h b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Public/GenericGraphNode.h new file mode 100644 index 00000000..965d109e --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Public/GenericGraphNode.h @@ -0,0 +1,94 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Templates/SubclassOf.h" +#include "GenericGraphNode.generated.h" + +class UGenericGraph; +class UGenericGraphEdge; + +UENUM(BlueprintType) +enum class ENodeLimit : uint8 +{ + Unlimited, + Limited +}; + + +UCLASS(Blueprintable) +class GENERICGRAPHRUNTIME_API UGenericGraphNode : public UObject +{ + GENERATED_BODY() + +public: + UGenericGraphNode(); + virtual ~UGenericGraphNode(); + + UPROPERTY(VisibleDefaultsOnly, Category = "GenericGraphNode") + UGenericGraph* Graph; + + UPROPERTY(BlueprintReadOnly, Category = "GenericGraphNode") + TArray ParentNodes; + + UPROPERTY(BlueprintReadOnly, Category = "GenericGraphNode") + TArray ChildrenNodes; + + UPROPERTY(BlueprintReadOnly, Category = "GenericGraphNode") + TMap Edges; + + UFUNCTION(BlueprintCallable, Category = "GenericGraphNode") + virtual UGenericGraphEdge* GetEdge(UGenericGraphNode* ChildNode); + + UFUNCTION(BlueprintCallable, Category = "GenericGraphNode") + bool IsLeafNode() const; + + UFUNCTION(BlueprintCallable, Category = "GenericGraphNode") + UGenericGraph* GetGraph() const; + + UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "GenericGraphNode") + FText GetDescription() const; + virtual FText GetDescription_Implementation() const; + + ////////////////////////////////////////////////////////////////////////// +#if WITH_EDITORONLY_DATA + UPROPERTY(EditDefaultsOnly, Category = "GenericGraphNode_Editor") + FText NodeTitle; + + UPROPERTY(VisibleDefaultsOnly, Category = "GenericGraphNode_Editor") + TSubclassOf CompatibleGraphType; + + UPROPERTY(EditDefaultsOnly, Category = "GenericGraphNode_Editor") + FLinearColor BackgroundColor; + + UPROPERTY(EditDefaultsOnly, Category = "GenericGraphNode_Editor") + FText ContextMenuName; + + UPROPERTY(EditDefaultsOnly, Category = "GenericGraphNode_Editor") + ENodeLimit ParentLimitType; + + UPROPERTY(EditDefaultsOnly, Category = "GenericGraphNode_Editor" ,meta = (ClampMin = "0",EditCondition = "ParentLimitType == ENodeLimit::Limited", EditConditionHides)) + int32 ParentLimit; + + UPROPERTY(EditDefaultsOnly, Category = "GenericGraphNode_Editor") + ENodeLimit ChildrenLimitType; + + UPROPERTY(EditDefaultsOnly, Category = "GenericGraphNode_Editor" ,meta = (ClampMin = "0",EditCondition = "ChildrenLimitType == ENodeLimit::Limited", EditConditionHides)) + int32 ChildrenLimit; + +#endif + +#if WITH_EDITOR + virtual bool IsNameEditable() const; + + virtual FLinearColor GetBackgroundColor() const; + + virtual FText GetNodeTitle() const; + + virtual void SetNodeTitle(const FText& NewTitle); + + virtual bool CanCreateConnection(UGenericGraphNode* Other, FText& ErrorMessage); + + virtual bool CanCreateConnectionTo(UGenericGraphNode* Other, int32 NumberOfChildrenNodes, FText& ErrorMessage); + virtual bool CanCreateConnectionFrom(UGenericGraphNode* Other, int32 NumberOfParentNodes, FText& ErrorMessage); +#endif +}; diff --git a/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Public/IGenericGraphRuntime.h b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Public/IGenericGraphRuntime.h new file mode 100644 index 00000000..f978a2b6 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/Source/GenericGraphRuntime/Public/IGenericGraphRuntime.h @@ -0,0 +1,36 @@ +#pragma once + +#include "Modules/ModuleManager.h" + +DECLARE_LOG_CATEGORY_EXTERN(GenericGraphRuntime, Log, All); + +/** + * The public interface to this module + */ +class IGenericGraphRuntime : public IModuleInterface +{ + +public: + + /** + * Singleton-like access to this module's interface. This is just for convenience! + * Beware of calling this during the shutdown phase, though. Your module might have been unloaded already. + * + * @return Returns singleton instance, loading the module on demand if needed + */ + static IGenericGraphRuntime& Get() + { + return FModuleManager::LoadModuleChecked< IGenericGraphRuntime >("GenericGraphRuntime"); + } + + /** + * Checks to see if this module is loaded and ready. It is only valid to call Get() if IsAvailable() returns true. + * + * @return True if the module is loaded and ready to use + */ + static bool IsAvailable() + { + return FModuleManager::Get().IsModuleLoaded( "GenericGraphRuntime" ); + } +}; + diff --git a/EndlessVendetta/Plugins/GenericGraph/docs/images/GenericGraph.png b/EndlessVendetta/Plugins/GenericGraph/docs/images/GenericGraph.png new file mode 100644 index 00000000..0d899979 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/docs/images/GenericGraph.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:092185ad7a9989a69ba9543793326cf0f893fcc1a0895de65db3ca24ee7c970a +size 89534 diff --git a/EndlessVendetta/Plugins/GenericGraph/docs/images/ability-graph.png b/EndlessVendetta/Plugins/GenericGraph/docs/images/ability-graph.png new file mode 100644 index 00000000..b65adf2a --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/docs/images/ability-graph.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e97bb76a079e7e37c685cfa0f480c4028be755577939d8ae3d51be6e5571112c +size 114519 diff --git a/EndlessVendetta/Plugins/GenericGraph/docs/images/dialogue/dialogue01.png b/EndlessVendetta/Plugins/GenericGraph/docs/images/dialogue/dialogue01.png new file mode 100644 index 00000000..7fbb5326 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/docs/images/dialogue/dialogue01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:95ab320c78c5ed1a79b78686a9e453bf459805b71ca6b855305f2ac328103d9e +size 98959 diff --git a/EndlessVendetta/Plugins/GenericGraph/docs/images/dialogue/dialogue02.png b/EndlessVendetta/Plugins/GenericGraph/docs/images/dialogue/dialogue02.png new file mode 100644 index 00000000..c03fde96 --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/docs/images/dialogue/dialogue02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cbbb01dc0b5af841164b8baeeebfb5aa769cb0710482f6accd984d02e0586eb9 +size 94706 diff --git a/EndlessVendetta/Plugins/GenericGraph/docs/images/dialogue/dialogue03.png b/EndlessVendetta/Plugins/GenericGraph/docs/images/dialogue/dialogue03.png new file mode 100644 index 00000000..ec53962e --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/docs/images/dialogue/dialogue03.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a51f6bd700c3346982c523d3c6e535cadb7ec8f0dc6c806bd5c481e961e8969c +size 95655 diff --git a/EndlessVendetta/Plugins/GenericGraph/readme.rst b/EndlessVendetta/Plugins/GenericGraph/readme.rst new file mode 100644 index 00000000..f76c40ba --- /dev/null +++ b/EndlessVendetta/Plugins/GenericGraph/readme.rst @@ -0,0 +1,51 @@ +GenericGraphPlugin +================== + +Generic graph data structure plugin for ue4 + +.. image:: docs/images/GenericGraph.png + +Feature +------- + +* Custom asset type +* UE4 BehaviorTree-like asset editor +* Extendable graph node type +* Extendable graph edge type +* Extendable graph type(new asset type with generic graph editor, C++ only) + +Usage +----- + +* Ability system +* Dialogue system +* Quest system +* Etc + +Install +------- + +#. Clone this project to ${YourProject}/Plugins/ +#. Generate project file +#. Compile + +Tutorial +-------- + +`Dialogue System`_ (WIP) + +Example +------- + +Dialogue System and ability system: SRPGTemplate_ + +.. image:: docs/images/dialogue/dialogue01.png + +.. image:: docs/images/dialogue/dialogue02.png + +.. image:: docs/images/dialogue/dialogue03.png + +.. image:: docs/images/ability-graph.png + +.. _Dialogue System: https://jinyuliao.github.io/blog/html/2017/12/15/ue4_dialogue_system_part1.html +.. _SRPGTemplate: https://github.com/jinyuliao/SRPGTemplate