├── .gitignore ├── GenericGraph.uplugin ├── LICENSE ├── Resources ├── AutoArrangeIcon.png └── Icon128.png ├── Source ├── GenericGraphEditor │ ├── GenericGraphEditor.Build.cs │ ├── Private │ │ ├── AssetTypeActions_GenericGraph.cpp │ │ ├── AutoLayout │ │ │ ├── AutoLayoutStrategy.cpp │ │ │ ├── ForceDirectedLayoutStrategy.cpp │ │ │ └── TreeLayoutStrategy.cpp │ │ ├── GenericGraphAssetEditor │ │ │ ├── AssetEditorToolbar_GenericGraph.cpp │ │ │ ├── AssetEditor_GenericGraph.cpp │ │ │ ├── AssetGraphSchema_GenericGraph.cpp │ │ │ ├── ConnectionDrawingPolicy_GenericGraph.cpp │ │ │ ├── EdGraph_GenericGraph.cpp │ │ │ ├── EdNode_GenericGraphEdge.cpp │ │ │ ├── EdNode_GenericGraphNode.cpp │ │ │ ├── EditorCommands_GenericGraph.cpp │ │ │ ├── GenericGraphDragConnection.cpp │ │ │ ├── GenericGraphEditorStyle.cpp │ │ │ ├── SEdNode_GenericGraphEdge.cpp │ │ │ ├── SEdNode_GenericGraphNode.cpp │ │ │ └── Settings_GenericGraphEditor.cpp │ │ ├── GenericGraphEditor.cpp │ │ ├── GenericGraphFactory.cpp │ │ └── GenericGraphNodeFactory.cpp │ └── Public │ │ ├── AssetTypeActions_GenericGraph.h │ │ ├── AutoLayout │ │ ├── AutoLayoutStrategy.h │ │ ├── ForceDirectedLayoutStrategy.h │ │ └── TreeLayoutStrategy.h │ │ ├── GenericGraphAssetEditor │ │ ├── AssetEditorToolbar_GenericGraph.h │ │ ├── AssetEditor_GenericGraph.h │ │ ├── AssetGraphSchema_GenericGraph.h │ │ ├── Colors_GenericGraph.h │ │ ├── ConnectionDrawingPolicy_GenericGraph.h │ │ ├── EdGraph_GenericGraph.h │ │ ├── EdNode_GenericGraphEdge.h │ │ ├── EdNode_GenericGraphNode.h │ │ ├── EditorCommands_GenericGraph.h │ │ ├── GenericGraphDragConnection.h │ │ ├── GenericGraphEditorStyle.h │ │ ├── SEdNode_GenericGraphEdge.h │ │ ├── SEdNode_GenericGraphNode.h │ │ └── Settings_GenericGraphEditor.h │ │ ├── GenericGraphEditor.h │ │ ├── GenericGraphEditorModule.h │ │ ├── GenericGraphEditorPCH.h │ │ ├── GenericGraphFactory.h │ │ └── GenericGraphNodeFactory.h └── GenericGraphRuntime │ ├── GenericGraphRuntime.Build.cs │ ├── Private │ ├── GenericGraph.cpp │ ├── GenericGraphEdge.cpp │ ├── GenericGraphNode.cpp │ ├── GenericGraphRuntime.cpp │ └── GenericGraphRuntimePCH.h │ └── Public │ ├── GenericGraph.h │ ├── GenericGraphEdge.h │ ├── GenericGraphNode.h │ └── IGenericGraphRuntime.h ├── docs └── images │ ├── GenericGraph.png │ ├── ability-graph.png │ └── dialogue │ ├── dialogue01.png │ ├── dialogue02.png │ └── dialogue03.png └── readme.rst /.gitignore: -------------------------------------------------------------------------------- 1 | Binaries 2 | DerivedDataCache 3 | Intermediate 4 | Saved 5 | Build 6 | *.sdf 7 | *.sln 8 | *.suo 9 | *.opensdf 10 | *.opendb 11 | *.db 12 | /docs/sphinx-build-result 13 | /.vs 14 | NoCommit -------------------------------------------------------------------------------- /GenericGraph.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion" : 3, 3 | "Version" : 1, 4 | "VersionName" : "1.0", 5 | "FriendlyName" : "Generic Graph Plugin", 6 | "Description" : "", 7 | "Category" : "Editor", 8 | "CreatedBy" : "jinyuliao", 9 | "CreatedByURL" : "", 10 | "DocsURL" : "", 11 | "MarketplaceURL" : "", 12 | "SupportURL" : "", 13 | "EnabledByDefault" : false, 14 | "CanContainContent" : true, 15 | "IsBetaVersion" : false, 16 | "Installed" : false, 17 | "Modules" : 18 | [ 19 | { 20 | "Name" : "GenericGraphRuntime", 21 | "Type" : "Runtime", 22 | "LoadingPhase" : "PreDefault" 23 | }, 24 | { 25 | "Name" : "GenericGraphEditor", 26 | "Type" : "Editor", 27 | "LoadingPhase" : "Default" 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 jinyuliao 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Resources/AutoArrangeIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProjectBorealis/GenericGraph/e9a606dfd2ff4e9ca33ca44e6d835274c708dff4/Resources/AutoArrangeIcon.png -------------------------------------------------------------------------------- /Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProjectBorealis/GenericGraph/e9a606dfd2ff4e9ca33ca44e6d835274c708dff4/Resources/Icon128.png -------------------------------------------------------------------------------- /Source/GenericGraphEditor/GenericGraphEditor.Build.cs: -------------------------------------------------------------------------------- 1 | using UnrealBuildTool; 2 | 3 | public class GenericGraphEditor : ModuleRules 4 | { 5 | public GenericGraphEditor(ReadOnlyTargetRules Target) : base(Target) 6 | { 7 | PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; 8 | bLegacyPublicIncludePaths = false; 9 | ShadowVariableWarningLevel = WarningLevel.Error; 10 | 11 | PublicIncludePaths.AddRange( 12 | new string[] { 13 | // ... add public include paths required here ... 14 | } 15 | ); 16 | 17 | PrivateIncludePaths.AddRange( 18 | new string[] { 19 | // ... add other private include paths required here ... 20 | "GenericGraphEditor/Private", 21 | "GenericGraphEditor/Public", 22 | } 23 | ); 24 | 25 | PublicDependencyModuleNames.AddRange( 26 | new string[] 27 | { 28 | "Core", 29 | "CoreUObject", 30 | "Engine", 31 | "UnrealEd", 32 | // ... add other public dependencies that you statically link with here ... 33 | } 34 | ); 35 | 36 | PrivateDependencyModuleNames.AddRange( 37 | new string[] 38 | { 39 | "GenericGraphRuntime", 40 | "AssetTools", 41 | "Slate", 42 | "InputCore", 43 | "SlateCore", 44 | "InputCore", 45 | "GraphEditor", 46 | "PropertyEditor", 47 | "EditorStyle", 48 | "Kismet", 49 | "KismetWidgets", 50 | "ApplicationCore", 51 | "ToolMenus", 52 | "Projects", 53 | // ... add private dependencies that you statically link with here ... 54 | } 55 | ); 56 | 57 | DynamicallyLoadedModuleNames.AddRange( 58 | new string[] 59 | { 60 | // ... add any modules that your module loads dynamically here ... 61 | } 62 | ); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Private/AssetTypeActions_GenericGraph.cpp: -------------------------------------------------------------------------------- 1 | #include "AssetTypeActions_GenericGraph.h" 2 | #include "GenericGraphEditorPCH.h" 3 | #include "GenericGraphAssetEditor/AssetEditor_GenericGraph.h" 4 | 5 | #define LOCTEXT_NAMESPACE "AssetTypeActions_GenericGraph" 6 | 7 | FAssetTypeActions_GenericGraph::FAssetTypeActions_GenericGraph(EAssetTypeCategories::Type InAssetCategory) 8 | : MyAssetCategory(InAssetCategory) 9 | { 10 | } 11 | 12 | FText FAssetTypeActions_GenericGraph::GetName() const 13 | { 14 | return LOCTEXT("FGenericGraphAssetTypeActionsName", "Generic Graph"); 15 | } 16 | 17 | FColor FAssetTypeActions_GenericGraph::GetTypeColor() const 18 | { 19 | return FColor(129, 196, 115); 20 | } 21 | 22 | UClass* FAssetTypeActions_GenericGraph::GetSupportedClass() const 23 | { 24 | return UGenericGraph::StaticClass(); 25 | } 26 | 27 | void FAssetTypeActions_GenericGraph::OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor) 28 | { 29 | const EToolkitMode::Type Mode = EditWithinLevelEditor.IsValid() ? EToolkitMode::WorldCentric : EToolkitMode::Standalone; 30 | 31 | for (auto ObjIt = InObjects.CreateConstIterator(); ObjIt; ++ObjIt) 32 | { 33 | if (UGenericGraph* Graph = Cast(*ObjIt)) 34 | { 35 | TSharedRef NewGraphEditor(new FAssetEditor_GenericGraph()); 36 | NewGraphEditor->InitGenericGraphAssetEditor(Mode, EditWithinLevelEditor, Graph); 37 | } 38 | } 39 | } 40 | 41 | uint32 FAssetTypeActions_GenericGraph::GetCategories() 42 | { 43 | return MyAssetCategory; 44 | } 45 | 46 | ////////////////////////////////////////////////////////////////////////// 47 | 48 | #undef LOCTEXT_NAMESPACE -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Private/AutoLayout/AutoLayoutStrategy.cpp: -------------------------------------------------------------------------------- 1 | #include "AutoLayout/AutoLayoutStrategy.h" 2 | #include "Kismet/KismetMathLibrary.h" 3 | #include "GenericGraphAssetEditor/EdNode_GenericGraphNode.h" 4 | #include "GenericGraphAssetEditor/SEdNode_GenericGraphNode.h" 5 | 6 | UAutoLayoutStrategy::UAutoLayoutStrategy() 7 | { 8 | Settings = nullptr; 9 | MaxIteration = 50; 10 | OptimalDistance = 150; 11 | } 12 | 13 | UAutoLayoutStrategy::~UAutoLayoutStrategy() 14 | { 15 | 16 | } 17 | 18 | FBox2D UAutoLayoutStrategy::GetNodeBound(UEdGraphNode* EdNode) 19 | { 20 | int32 NodeWidth = GetNodeWidth(Cast(EdNode)); 21 | int32 NodeHeight = GetNodeHeight(Cast(EdNode)); 22 | FVector2D Min(EdNode->NodePosX, EdNode->NodePosY); 23 | FVector2D Max(EdNode->NodePosX + NodeWidth, EdNode->NodePosY + NodeHeight); 24 | return FBox2D(Min, Max); 25 | } 26 | 27 | FBox2D UAutoLayoutStrategy::GetActualBounds(UGenericGraphNode* RootNode) 28 | { 29 | int Level = 0; 30 | TArray CurrLevelNodes = { RootNode }; 31 | TArray NextLevelNodes; 32 | 33 | FBox2D Rtn = GetNodeBound(EdGraph->NodeMap[RootNode]); 34 | 35 | while (CurrLevelNodes.Num() != 0) 36 | { 37 | for (int i = 0; i < CurrLevelNodes.Num(); ++i) 38 | { 39 | UGenericGraphNode* Node = CurrLevelNodes[i]; 40 | check(Node != nullptr); 41 | 42 | Rtn += GetNodeBound(EdGraph->NodeMap[Node]); 43 | 44 | for (int j = 0; j < Node->ChildrenNodes.Num(); ++j) 45 | { 46 | NextLevelNodes.Add(Node->ChildrenNodes[j]); 47 | } 48 | } 49 | 50 | CurrLevelNodes = NextLevelNodes; 51 | NextLevelNodes.Reset(); 52 | ++Level; 53 | } 54 | return Rtn; 55 | } 56 | 57 | void UAutoLayoutStrategy::RandomLayoutOneTree(UGenericGraphNode* RootNode, const FBox2D& Bound) 58 | { 59 | int Level = 0; 60 | TArray CurrLevelNodes = { RootNode }; 61 | TArray NextLevelNodes; 62 | 63 | while (CurrLevelNodes.Num() != 0) 64 | { 65 | for (int i = 0; i < CurrLevelNodes.Num(); ++i) 66 | { 67 | UGenericGraphNode* Node = CurrLevelNodes[i]; 68 | check(Node != nullptr); 69 | 70 | UEdNode_GenericGraphNode* EdNode_Node = EdGraph->NodeMap[Node]; 71 | 72 | EdNode_Node->NodePosX = UKismetMathLibrary::RandomFloatInRange(Bound.Min.X, Bound.Max.X); 73 | EdNode_Node->NodePosY = UKismetMathLibrary::RandomFloatInRange(Bound.Min.Y, Bound.Max.Y); 74 | 75 | for (int j = 0; j < Node->ChildrenNodes.Num(); ++j) 76 | { 77 | NextLevelNodes.Add(Node->ChildrenNodes[j]); 78 | } 79 | } 80 | 81 | CurrLevelNodes = NextLevelNodes; 82 | NextLevelNodes.Reset(); 83 | ++Level; 84 | } 85 | } 86 | 87 | int32 UAutoLayoutStrategy::GetNodeWidth(UEdNode_GenericGraphNode* EdNode) 88 | { 89 | return EdNode->SEdNode->GetCachedGeometry().GetLocalSize().X; 90 | } 91 | 92 | int32 UAutoLayoutStrategy::GetNodeHeight(UEdNode_GenericGraphNode* EdNode) 93 | { 94 | return EdNode->SEdNode->GetCachedGeometry().GetLocalSize().Y; 95 | } 96 | 97 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Private/AutoLayout/ForceDirectedLayoutStrategy.cpp: -------------------------------------------------------------------------------- 1 | #include "AutoLayout/ForceDirectedLayoutStrategy.h" 2 | 3 | static inline float CoolDown(float Temp, float CoolDownRate) 4 | { 5 | if (Temp < .01) return .01; 6 | return Temp - (Temp / CoolDownRate); 7 | } 8 | 9 | static inline float GetAttractForce(float X, float K) 10 | { 11 | return (X * X) / K; 12 | } 13 | 14 | static inline float GetRepulseForce(float X, float k) 15 | { 16 | return X != 0 ? k * k / X : TNumericLimits::Max(); 17 | } 18 | 19 | UForceDirectedLayoutStrategy::UForceDirectedLayoutStrategy() 20 | { 21 | bRandomInit = false; 22 | CoolDownRate = 10; 23 | InitTemperature = 10.f; 24 | } 25 | 26 | UForceDirectedLayoutStrategy::~UForceDirectedLayoutStrategy() 27 | { 28 | 29 | } 30 | 31 | void UForceDirectedLayoutStrategy::Layout(UEdGraph* _EdGraph) 32 | { 33 | EdGraph = Cast(_EdGraph); 34 | check(EdGraph != nullptr); 35 | 36 | EdGraph->RebuildGenericGraph(); 37 | Graph = EdGraph->GetGenericGraph(); 38 | check(Graph != nullptr); 39 | 40 | if (Settings != nullptr) 41 | { 42 | OptimalDistance = Settings->OptimalDistance; 43 | MaxIteration = Settings->MaxIteration; 44 | bRandomInit = Settings->bRandomInit; 45 | } 46 | 47 | FBox2D PreTreeBound(ForceInitToZero); 48 | for (int32 i = 0; i < Graph->RootNodes.Num(); ++i) 49 | { 50 | PreTreeBound = LayoutOneTree(Graph->RootNodes[i], PreTreeBound); 51 | } 52 | } 53 | 54 | FBox2D UForceDirectedLayoutStrategy::LayoutOneTree(UGenericGraphNode* RootNode, const FBox2D& PreTreeBound) 55 | { 56 | float Temp = InitTemperature; 57 | FBox2D TreeBound = GetActualBounds(RootNode); 58 | TreeBound.Min.X += PreTreeBound.Max.X + OptimalDistance; 59 | TreeBound.Max.X += PreTreeBound.Max.X + OptimalDistance; 60 | 61 | if (bRandomInit) 62 | { 63 | RandomLayoutOneTree(RootNode, TreeBound); 64 | } 65 | 66 | float RepulseForce, AttractForce, Distance; 67 | FVector2D Diff; 68 | 69 | TMap NodeToDisplacement; 70 | 71 | for (int32 i = 0; i < EdGraph->Nodes.Num(); ++i) 72 | { 73 | NodeToDisplacement.Add(EdGraph->Nodes[i], FVector2D(0.f, 0.f)); 74 | } 75 | 76 | for (int32 IterrationNum = 0; IterrationNum < MaxIteration; ++IterrationNum) 77 | { 78 | // Calculate the repulsive forces. 79 | for (int32 i = 0; i < EdGraph->Nodes.Num(); ++i) 80 | { 81 | for (int32 j = 0; j < EdGraph->Nodes.Num(); ++j) 82 | { 83 | if (i == j) 84 | continue; 85 | Diff.X = EdGraph->Nodes[i]->NodePosX - EdGraph->Nodes[j]->NodePosX; 86 | Diff.Y = EdGraph->Nodes[i]->NodePosY - EdGraph->Nodes[j]->NodePosY; 87 | Distance = Diff.Size(); 88 | Diff.Normalize(); 89 | 90 | RepulseForce = Distance > 2 * OptimalDistance ? 0 : GetRepulseForce(Distance, OptimalDistance); 91 | 92 | NodeToDisplacement[EdGraph->Nodes[i]] += Diff * RepulseForce; 93 | } 94 | } 95 | 96 | // Calculate the attractive forces. 97 | int Level = 0; 98 | TArray CurrLevelNodes = { RootNode }; 99 | TArray NextLevelNodes; 100 | 101 | while (CurrLevelNodes.Num() != 0) 102 | { 103 | for (int32 i = 0; i < CurrLevelNodes.Num(); ++i) 104 | { 105 | UGenericGraphNode* Node = CurrLevelNodes[i]; 106 | check(Node != nullptr); 107 | 108 | UEdNode_GenericGraphNode* EdNode_ParentNode = EdGraph->NodeMap[Node]; 109 | 110 | for (int32 j = 0; j < Node->ChildrenNodes.Num(); ++j) 111 | { 112 | NextLevelNodes.Add(Node->ChildrenNodes[j]); 113 | 114 | UEdNode_GenericGraphNode* EdNode_ChildNode = EdGraph->NodeMap[Node->ChildrenNodes[j]]; 115 | 116 | Diff.X = EdNode_ChildNode->NodePosX - EdNode_ParentNode->NodePosY; 117 | Diff.Y = EdNode_ChildNode->NodePosY - EdNode_ParentNode->NodePosY; 118 | Distance = Diff.Size(); 119 | Diff.Normalize(); 120 | 121 | AttractForce = GetAttractForce(Distance, OptimalDistance); 122 | 123 | NodeToDisplacement[EdNode_ParentNode] += Distance * Diff; 124 | NodeToDisplacement[EdNode_ChildNode] -= Distance * Diff; 125 | } 126 | } 127 | 128 | CurrLevelNodes = NextLevelNodes; 129 | NextLevelNodes.Reset(); 130 | ++Level; 131 | } 132 | 133 | for (int32 i = 0; i < EdGraph->Nodes.Num(); ++i) 134 | { 135 | UEdGraphNode* EdNode = EdGraph->Nodes[i]; 136 | Distance = NodeToDisplacement[EdNode].Size(); 137 | NodeToDisplacement[EdNode].Normalize(); 138 | 139 | float Minimum = Distance < Temp ? Distance : Temp; 140 | EdNode->NodePosX += NodeToDisplacement[EdNode].X * Minimum; 141 | EdNode->NodePosY += NodeToDisplacement[EdNode].Y * Minimum; 142 | } 143 | 144 | Temp = CoolDown(Temp, CoolDownRate); 145 | } 146 | 147 | FBox2D ActualBound = GetActualBounds(RootNode); 148 | 149 | FVector2D Center = ActualBound.GetCenter(); 150 | FVector2D TreeCenter = TreeBound.GetCenter(); 151 | 152 | FVector2D Scale = (TreeBound.Max - TreeBound.Min) / (ActualBound.Max - ActualBound.Min); 153 | 154 | for (int32 i = 0; i < EdGraph->Nodes.Num(); ++i) 155 | { 156 | UEdGraphNode* EdNode = EdGraph->Nodes[i]; 157 | EdNode->NodePosX = TreeCenter.X + Scale.X * (EdNode->NodePosX - Center.X); 158 | EdNode->NodePosY = TreeCenter.Y + Scale.Y * (EdNode->NodePosY - Center.Y); 159 | } 160 | 161 | return TreeBound; 162 | } 163 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Private/AutoLayout/TreeLayoutStrategy.cpp: -------------------------------------------------------------------------------- 1 | #include "AutoLayout/TreeLayoutStrategy.h" 2 | #include "GenericGraphEditorPCH.h" 3 | #include "GenericGraphAssetEditor/SEdNode_GenericGraphNode.h" 4 | 5 | UTreeLayoutStrategy::UTreeLayoutStrategy() 6 | { 7 | } 8 | 9 | UTreeLayoutStrategy::~UTreeLayoutStrategy() 10 | { 11 | 12 | } 13 | 14 | void UTreeLayoutStrategy::Layout(UEdGraph* _EdGraph) 15 | { 16 | EdGraph = Cast(_EdGraph); 17 | check(EdGraph != nullptr); 18 | 19 | EdGraph->RebuildGenericGraph(); 20 | Graph = EdGraph->GetGenericGraph(); 21 | check(Graph != nullptr); 22 | 23 | bool bFirstPassOnly = false; 24 | 25 | if (Settings != nullptr) 26 | { 27 | OptimalDistance = Settings->OptimalDistance; 28 | MaxIteration = Settings->MaxIteration; 29 | bFirstPassOnly = Settings->bFirstPassOnly; 30 | } 31 | 32 | FVector2D Anchor(0.f, 0.f); 33 | for (int32 i = 0; i < Graph->RootNodes.Num(); ++i) 34 | { 35 | UGenericGraphNode* RootNode = Graph->RootNodes[i]; 36 | InitPass(RootNode, Anchor); 37 | 38 | if (!bFirstPassOnly) 39 | { 40 | for (int32 j = 0; j < MaxIteration; ++j) 41 | { 42 | bool HasConflict = ResolveConflictPass(RootNode); 43 | if (!HasConflict) 44 | { 45 | break; 46 | } 47 | } 48 | } 49 | } 50 | 51 | for (int32 i = 0; i < Graph->RootNodes.Num(); ++i) 52 | { 53 | for (int32 j = 0; j < i; ++j) 54 | { 55 | ResolveConflict(Graph->RootNodes[j], Graph->RootNodes[i]); 56 | } 57 | } 58 | } 59 | 60 | void UTreeLayoutStrategy::InitPass(UGenericGraphNode* RootNode, const FVector2D& Anchor) 61 | { 62 | UEdNode_GenericGraphNode* EdNode_RootNode = EdGraph->NodeMap[RootNode]; 63 | 64 | FVector2D ChildAnchor(FVector2D(0.f, GetNodeHeight(EdNode_RootNode) + OptimalDistance + Anchor.Y)); 65 | for (int32 i = 0; i < RootNode->ChildrenNodes.Num(); ++i) 66 | { 67 | UGenericGraphNode* Child = RootNode->ChildrenNodes[i]; 68 | UEdNode_GenericGraphNode* EdNode_ChildNode = EdGraph->NodeMap[Child]; 69 | if (i > 0) 70 | { 71 | UGenericGraphNode* PreChild = RootNode->ChildrenNodes[i - 1]; 72 | UEdNode_GenericGraphNode* EdNode_PreChildNode = EdGraph->NodeMap[PreChild]; 73 | ChildAnchor.X += OptimalDistance + GetNodeWidth(EdNode_PreChildNode) / 2; 74 | } 75 | ChildAnchor.X += GetNodeWidth(EdNode_ChildNode) / 2; 76 | InitPass(Child, ChildAnchor); 77 | } 78 | 79 | float NodeWidth = GetNodeWidth(EdNode_RootNode); 80 | 81 | EdNode_RootNode->NodePosY = Anchor.Y; 82 | if (RootNode->ChildrenNodes.Num() == 0) 83 | { 84 | EdNode_RootNode->NodePosX = Anchor.X - NodeWidth / 2; 85 | } 86 | else 87 | { 88 | UpdateParentNodePosition(RootNode); 89 | } 90 | } 91 | 92 | bool UTreeLayoutStrategy::ResolveConflictPass(UGenericGraphNode* Node) 93 | { 94 | bool HasConflict = false; 95 | for (int32 i = 0; i < Node->ChildrenNodes.Num(); ++i) 96 | { 97 | UGenericGraphNode* Child = Node->ChildrenNodes[i]; 98 | if (ResolveConflictPass(Child)) 99 | { 100 | HasConflict = true; 101 | } 102 | } 103 | 104 | for (int32 i = 0; i < Node->ParentNodes.Num(); ++i) 105 | { 106 | UGenericGraphNode* ParentNode = Node->ParentNodes[i]; 107 | for (int32 j = 0; j < ParentNode->ChildrenNodes.Num(); ++j) 108 | { 109 | UGenericGraphNode* LeftSibling = ParentNode->ChildrenNodes[j]; 110 | if (LeftSibling == Node) 111 | break; 112 | if (ResolveConflict(LeftSibling, Node)) 113 | { 114 | HasConflict = true; 115 | } 116 | } 117 | } 118 | 119 | return HasConflict; 120 | } 121 | 122 | bool UTreeLayoutStrategy::ResolveConflict(UGenericGraphNode* LRoot, UGenericGraphNode* RRoot) 123 | { 124 | TArray RightContour, LeftContour; 125 | 126 | GetRightContour(LRoot, 0, RightContour); 127 | GetLeftContour(RRoot, 0, LeftContour); 128 | 129 | int32 MaxOverlapDistance = 0; 130 | int32 Num = FMath::Min(LeftContour.Num(), RightContour.Num()); 131 | for (int32 i = 0; i < Num; ++i) 132 | { 133 | if (RightContour.Contains(LeftContour[i]) || LeftContour.Contains(RightContour[i])) 134 | break; 135 | 136 | int32 RightBound = RightContour[i]->NodePosX + GetNodeWidth(RightContour[i]); 137 | int32 LeftBound = LeftContour[i]->NodePosX; 138 | int32 Distance = RightBound + OptimalDistance - LeftBound; 139 | if (Distance > MaxOverlapDistance) 140 | { 141 | MaxOverlapDistance = Distance; 142 | } 143 | } 144 | 145 | if (MaxOverlapDistance > 0) 146 | { 147 | ShiftSubTree(RRoot, FVector2D(MaxOverlapDistance, 0.f)); 148 | 149 | TArray ParentNodes = RRoot->ParentNodes; 150 | TArray NextParentNodes; 151 | while (ParentNodes.Num() != 0) 152 | { 153 | for (int32 i = 0; i < ParentNodes.Num(); ++i) 154 | { 155 | UpdateParentNodePosition(ParentNodes[i]); 156 | 157 | NextParentNodes.Append(ParentNodes[i]->ParentNodes); 158 | } 159 | 160 | ParentNodes = NextParentNodes; 161 | NextParentNodes.Reset(); 162 | } 163 | 164 | return true; 165 | } 166 | else 167 | { 168 | return false; 169 | } 170 | } 171 | 172 | void UTreeLayoutStrategy::GetLeftContour(UGenericGraphNode* RootNode, int32 Level, TArray& Contour) 173 | { 174 | UEdNode_GenericGraphNode* EdNode_Node = EdGraph->NodeMap[RootNode]; 175 | if (Level >= Contour.Num()) 176 | { 177 | Contour.Add(EdNode_Node); 178 | } 179 | else if (EdNode_Node->NodePosX < Contour[Level]->NodePosX) 180 | { 181 | Contour[Level] = EdNode_Node; 182 | } 183 | 184 | for (int32 i = 0; i < RootNode->ChildrenNodes.Num(); ++i) 185 | { 186 | GetLeftContour(RootNode->ChildrenNodes[i], Level + 1, Contour); 187 | } 188 | } 189 | 190 | void UTreeLayoutStrategy::GetRightContour(UGenericGraphNode* RootNode, int32 Level, TArray& Contour) 191 | { 192 | UEdNode_GenericGraphNode* EdNode_Node = EdGraph->NodeMap[RootNode]; 193 | if (Level >= Contour.Num()) 194 | { 195 | Contour.Add(EdNode_Node); 196 | } 197 | else if (EdNode_Node->NodePosX + GetNodeWidth(EdNode_Node) > Contour[Level]->NodePosX + GetNodeWidth(Contour[Level])) 198 | { 199 | Contour[Level] = EdNode_Node; 200 | } 201 | 202 | for (int32 i = 0; i < RootNode->ChildrenNodes.Num(); ++i) 203 | { 204 | GetRightContour(RootNode->ChildrenNodes[i], Level + 1, Contour); 205 | } 206 | } 207 | 208 | void UTreeLayoutStrategy::ShiftSubTree(UGenericGraphNode* RootNode, const FVector2D& Offset) 209 | { 210 | UEdNode_GenericGraphNode* EdNode_Node = EdGraph->NodeMap[RootNode]; 211 | EdNode_Node->NodePosX += Offset.X; 212 | EdNode_Node->NodePosY += Offset.Y; 213 | 214 | for (int32 i = 0; i < RootNode->ChildrenNodes.Num(); ++i) 215 | { 216 | UGenericGraphNode* Child = RootNode->ChildrenNodes[i]; 217 | 218 | if (Child->ParentNodes[0] == RootNode) 219 | { 220 | ShiftSubTree(RootNode->ChildrenNodes[i], Offset); 221 | } 222 | } 223 | } 224 | 225 | void UTreeLayoutStrategy::UpdateParentNodePosition(UGenericGraphNode* ParentNode) 226 | { 227 | UEdNode_GenericGraphNode* EdNode_ParentNode = EdGraph->NodeMap[ParentNode]; 228 | if (ParentNode->ChildrenNodes.Num() % 2 == 0) 229 | { 230 | UEdNode_GenericGraphNode* FirstChild = EdGraph->NodeMap[ParentNode->ChildrenNodes[0]]; 231 | UEdNode_GenericGraphNode* LastChild = EdGraph->NodeMap[ParentNode->ChildrenNodes.Last()]; 232 | float LeftBound = FirstChild->NodePosX; 233 | float RightBound = LastChild->NodePosX + GetNodeWidth(LastChild); 234 | EdNode_ParentNode->NodePosX = (LeftBound + RightBound) / 2 - GetNodeWidth(EdNode_ParentNode) / 2; 235 | } 236 | else 237 | { 238 | UEdNode_GenericGraphNode* MidChild = EdGraph->NodeMap[ParentNode->ChildrenNodes[ParentNode->ChildrenNodes.Num() / 2]]; 239 | EdNode_ParentNode->NodePosX = MidChild->NodePosX + GetNodeWidth(MidChild) / 2 - GetNodeWidth(EdNode_ParentNode) / 2; 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Private/GenericGraphAssetEditor/AssetEditorToolbar_GenericGraph.cpp: -------------------------------------------------------------------------------- 1 | #include "GenericGraphAssetEditor/AssetEditorToolbar_GenericGraph.h" 2 | #include "GenericGraphAssetEditor/AssetEditor_GenericGraph.h" 3 | #include "GenericGraphAssetEditor/EditorCommands_GenericGraph.h" 4 | #include "GenericGraphAssetEditor/GenericGraphEditorStyle.h" 5 | 6 | #define LOCTEXT_NAMESPACE "AssetEditorToolbar_GenericGraph" 7 | 8 | void FAssetEditorToolbar_GenericGraph::AddGenericGraphToolbar(TSharedPtr Extender) 9 | { 10 | check(GenericGraphEditor.IsValid()); 11 | TSharedPtr GenericGraphEditorPtr = GenericGraphEditor.Pin(); 12 | 13 | TSharedPtr ToolbarExtender = MakeShareable(new FExtender); 14 | ToolbarExtender->AddToolBarExtension("Asset", EExtensionHook::After, GenericGraphEditorPtr->GetToolkitCommands(), FToolBarExtensionDelegate::CreateSP( this, &FAssetEditorToolbar_GenericGraph::FillGenericGraphToolbar )); 15 | GenericGraphEditorPtr->AddToolbarExtender(ToolbarExtender); 16 | } 17 | 18 | void FAssetEditorToolbar_GenericGraph::FillGenericGraphToolbar(FToolBarBuilder& ToolbarBuilder) 19 | { 20 | check(GenericGraphEditor.IsValid()); 21 | TSharedPtr GenericGraphEditorPtr = GenericGraphEditor.Pin(); 22 | 23 | ToolbarBuilder.BeginSection("Generic Graph"); 24 | { 25 | ToolbarBuilder.AddToolBarButton(FEditorCommands_GenericGraph::Get().GraphSettings, 26 | NAME_None, 27 | LOCTEXT("GraphSettings_Label", "Graph Settings"), 28 | LOCTEXT("GraphSettings_ToolTip", "Show the Graph Settings"), 29 | FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.GameSettings")); 30 | } 31 | ToolbarBuilder.EndSection(); 32 | 33 | ToolbarBuilder.BeginSection("Util"); 34 | { 35 | ToolbarBuilder.AddToolBarButton(FEditorCommands_GenericGraph::Get().AutoArrange, 36 | NAME_None, 37 | LOCTEXT("AutoArrange_Label", "Auto Arrange"), 38 | LOCTEXT("AutoArrange_ToolTip", "Auto arrange nodes, not perfect, but still handy"), 39 | FSlateIcon(FGenericGraphEditorStyle::GetStyleSetName(), "GenericGraphEditor.AutoArrange")); 40 | } 41 | ToolbarBuilder.EndSection(); 42 | 43 | } 44 | 45 | 46 | #undef LOCTEXT_NAMESPACE 47 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Private/GenericGraphAssetEditor/AssetEditor_GenericGraph.cpp: -------------------------------------------------------------------------------- 1 | #include "GenericGraphAssetEditor/AssetEditor_GenericGraph.h" 2 | #include "GenericGraphEditorPCH.h" 3 | #include "GenericGraphAssetEditor/AssetEditorToolbar_GenericGraph.h" 4 | #include "GenericGraphAssetEditor/AssetGraphSchema_GenericGraph.h" 5 | #include "GenericGraphAssetEditor/EditorCommands_GenericGraph.h" 6 | #include "GenericGraphAssetEditor/EdGraph_GenericGraph.h" 7 | #include "AssetToolsModule.h" 8 | #include "HAL/PlatformApplicationMisc.h" 9 | #include "Framework/Commands/GenericCommands.h" 10 | #include "GraphEditorActions.h" 11 | #include "IDetailsView.h" 12 | #include "PropertyEditorModule.h" 13 | #include "Editor/UnrealEd/Public/Kismet2/BlueprintEditorUtils.h" 14 | #include "Kismet2/KismetEditorUtilities.h" 15 | #include "EdGraphUtilities.h" 16 | #include "GenericGraphAssetEditor/EdGraph_GenericGraph.h" 17 | #include "GenericGraphAssetEditor/EdNode_GenericGraphNode.h" 18 | #include "GenericGraphAssetEditor/EdNode_GenericGraphEdge.h" 19 | #include "AutoLayout/TreeLayoutStrategy.h" 20 | #include "AutoLayout/ForceDirectedLayoutStrategy.h" 21 | 22 | #define LOCTEXT_NAMESPACE "AssetEditor_GenericGraph" 23 | 24 | const FName GenericGraphEditorAppName = FName(TEXT("GenericGraphEditorApp")); 25 | 26 | struct FGenericGraphAssetEditorTabs 27 | { 28 | // Tab identifiers 29 | static const FName GenericGraphPropertyID; 30 | static const FName ViewportID; 31 | static const FName GenericGraphEditorSettingsID; 32 | }; 33 | 34 | ////////////////////////////////////////////////////////////////////////// 35 | 36 | const FName FGenericGraphAssetEditorTabs::GenericGraphPropertyID(TEXT("GenericGraphProperty")); 37 | const FName FGenericGraphAssetEditorTabs::ViewportID(TEXT("Viewport")); 38 | const FName FGenericGraphAssetEditorTabs::GenericGraphEditorSettingsID(TEXT("GenericGraphEditorSettings")); 39 | 40 | ////////////////////////////////////////////////////////////////////////// 41 | 42 | FAssetEditor_GenericGraph::FAssetEditor_GenericGraph() 43 | { 44 | EditingGraph = nullptr; 45 | 46 | GenricGraphEditorSettings = NewObject(UGenericGraphEditorSettings::StaticClass()); 47 | 48 | #if ENGINE_MAJOR_VERSION < 5 49 | OnPackageSavedDelegateHandle = UPackage::PackageSavedEvent.AddRaw(this, &FAssetEditor_GenericGraph::OnPackageSaved); 50 | #else // #if ENGINE_MAJOR_VERSION < 5 51 | OnPackageSavedDelegateHandle = UPackage::PackageSavedWithContextEvent.AddRaw(this, &FAssetEditor_GenericGraph::OnPackageSavedWithContext); 52 | #endif // #else // #if ENGINE_MAJOR_VERSION < 5 53 | } 54 | 55 | FAssetEditor_GenericGraph::~FAssetEditor_GenericGraph() 56 | { 57 | #if ENGINE_MAJOR_VERSION < 5 58 | UPackage::PackageSavedEvent.Remove(OnPackageSavedDelegateHandle); 59 | #else // #if ENGINE_MAJOR_VERSION < 5 60 | UPackage::PackageSavedWithContextEvent.Remove(OnPackageSavedDelegateHandle); 61 | #endif // #else // #if ENGINE_MAJOR_VERSION < 5 62 | } 63 | 64 | void FAssetEditor_GenericGraph::InitGenericGraphAssetEditor(const EToolkitMode::Type Mode, const TSharedPtr< IToolkitHost >& InitToolkitHost, UGenericGraph* Graph) 65 | { 66 | EditingGraph = Graph; 67 | CreateEdGraph(); 68 | 69 | FGenericCommands::Register(); 70 | FGraphEditorCommands::Register(); 71 | FEditorCommands_GenericGraph::Register(); 72 | 73 | if (!ToolbarBuilder.IsValid()) 74 | { 75 | ToolbarBuilder = MakeShareable(new FAssetEditorToolbar_GenericGraph(SharedThis(this))); 76 | } 77 | 78 | BindCommands(); 79 | 80 | CreateInternalWidgets(); 81 | 82 | TSharedPtr ToolbarExtender = MakeShareable(new FExtender); 83 | 84 | ToolbarBuilder->AddGenericGraphToolbar(ToolbarExtender); 85 | 86 | // Layout 87 | const TSharedRef StandaloneDefaultLayout = FTabManager::NewLayout("Standalone_GenericGraphEditor_Layout_v1") 88 | ->AddArea 89 | ( 90 | FTabManager::NewPrimaryArea()->SetOrientation(Orient_Vertical) 91 | #if ENGINE_MAJOR_VERSION < 5 92 | ->Split 93 | ( 94 | FTabManager::NewStack() 95 | ->SetSizeCoefficient(0.1f) 96 | ->AddTab(GetToolbarTabId(), ETabState::OpenedTab)->SetHideTabWell(true) 97 | ) 98 | #endif // #if ENGINE_MAJOR_VERSION < 5 99 | ->Split 100 | ( 101 | FTabManager::NewSplitter()->SetOrientation(Orient_Horizontal)->SetSizeCoefficient(0.9f) 102 | ->Split 103 | ( 104 | FTabManager::NewStack() 105 | ->SetSizeCoefficient(0.65f) 106 | ->AddTab(FGenericGraphAssetEditorTabs::ViewportID, ETabState::OpenedTab)->SetHideTabWell(true) 107 | ) 108 | ->Split 109 | ( 110 | FTabManager::NewSplitter()->SetOrientation(Orient_Vertical) 111 | ->Split 112 | ( 113 | FTabManager::NewStack() 114 | ->SetSizeCoefficient(0.7f) 115 | ->AddTab(FGenericGraphAssetEditorTabs::GenericGraphPropertyID, ETabState::OpenedTab)->SetHideTabWell(true) 116 | ) 117 | ->Split 118 | ( 119 | FTabManager::NewStack() 120 | ->SetSizeCoefficient(0.3f) 121 | ->AddTab(FGenericGraphAssetEditorTabs::GenericGraphEditorSettingsID, ETabState::OpenedTab) 122 | ) 123 | ) 124 | ) 125 | ); 126 | 127 | const bool bCreateDefaultStandaloneMenu = true; 128 | const bool bCreateDefaultToolbar = true; 129 | FAssetEditorToolkit::InitAssetEditor(Mode, InitToolkitHost, GenericGraphEditorAppName, StandaloneDefaultLayout, bCreateDefaultStandaloneMenu, bCreateDefaultToolbar, EditingGraph, false); 130 | 131 | RegenerateMenusAndToolbars(); 132 | } 133 | 134 | void FAssetEditor_GenericGraph::RegisterTabSpawners(const TSharedRef& InTabManager) 135 | { 136 | WorkspaceMenuCategory = InTabManager->AddLocalWorkspaceMenuCategory(LOCTEXT("WorkspaceMenu_GenericGraphEditor", "Generic Graph Editor")); 137 | auto WorkspaceMenuCategoryRef = WorkspaceMenuCategory.ToSharedRef(); 138 | 139 | FAssetEditorToolkit::RegisterTabSpawners(InTabManager); 140 | 141 | InTabManager->RegisterTabSpawner(FGenericGraphAssetEditorTabs::ViewportID, FOnSpawnTab::CreateSP(this, &FAssetEditor_GenericGraph::SpawnTab_Viewport)) 142 | .SetDisplayName(LOCTEXT("GraphCanvasTab", "Viewport")) 143 | .SetGroup(WorkspaceMenuCategoryRef) 144 | .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "GraphEditor.EventGraph_16x")); 145 | 146 | InTabManager->RegisterTabSpawner(FGenericGraphAssetEditorTabs::GenericGraphPropertyID, FOnSpawnTab::CreateSP(this, &FAssetEditor_GenericGraph::SpawnTab_Details)) 147 | .SetDisplayName(LOCTEXT("DetailsTab", "Property")) 148 | .SetGroup(WorkspaceMenuCategoryRef) 149 | .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.Tabs.Details")); 150 | 151 | InTabManager->RegisterTabSpawner(FGenericGraphAssetEditorTabs::GenericGraphEditorSettingsID, FOnSpawnTab::CreateSP(this, &FAssetEditor_GenericGraph::SpawnTab_EditorSettings)) 152 | .SetDisplayName(LOCTEXT("EditorSettingsTab", "Generic Graph Editor Setttings")) 153 | .SetGroup(WorkspaceMenuCategoryRef) 154 | .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.Tabs.Details")); 155 | } 156 | 157 | void FAssetEditor_GenericGraph::UnregisterTabSpawners(const TSharedRef& InTabManager) 158 | { 159 | FAssetEditorToolkit::UnregisterTabSpawners(InTabManager); 160 | 161 | InTabManager->UnregisterTabSpawner(FGenericGraphAssetEditorTabs::ViewportID); 162 | InTabManager->UnregisterTabSpawner(FGenericGraphAssetEditorTabs::GenericGraphPropertyID); 163 | InTabManager->UnregisterTabSpawner(FGenericGraphAssetEditorTabs::GenericGraphEditorSettingsID); 164 | } 165 | 166 | FName FAssetEditor_GenericGraph::GetToolkitFName() const 167 | { 168 | return FName("FGenericGraphEditor"); 169 | } 170 | 171 | FText FAssetEditor_GenericGraph::GetBaseToolkitName() const 172 | { 173 | return LOCTEXT("GenericGraphEditorAppLabel", "Generic Graph Editor"); 174 | } 175 | 176 | FText FAssetEditor_GenericGraph::GetToolkitName() const 177 | { 178 | const bool bDirtyState = EditingGraph->GetOutermost()->IsDirty(); 179 | 180 | FFormatNamedArguments Args; 181 | Args.Add(TEXT("GenericGraphName"), FText::FromString(EditingGraph->GetName())); 182 | Args.Add(TEXT("DirtyState"), bDirtyState ? FText::FromString(TEXT("*")) : FText::GetEmpty()); 183 | return FText::Format(LOCTEXT("GenericGraphEditorToolkitName", "{GenericGraphName}{DirtyState}"), Args); 184 | } 185 | 186 | FText FAssetEditor_GenericGraph::GetToolkitToolTipText() const 187 | { 188 | return FAssetEditorToolkit::GetToolTipTextForObject(EditingGraph); 189 | } 190 | 191 | FLinearColor FAssetEditor_GenericGraph::GetWorldCentricTabColorScale() const 192 | { 193 | return FLinearColor::White; 194 | } 195 | 196 | FString FAssetEditor_GenericGraph::GetWorldCentricTabPrefix() const 197 | { 198 | return TEXT("GenericGraphEditor"); 199 | } 200 | 201 | FString FAssetEditor_GenericGraph::GetDocumentationLink() const 202 | { 203 | return TEXT(""); 204 | } 205 | 206 | void FAssetEditor_GenericGraph::SaveAsset_Execute() 207 | { 208 | if (EditingGraph != nullptr) 209 | { 210 | RebuildGenericGraph(); 211 | } 212 | 213 | FAssetEditorToolkit::SaveAsset_Execute(); 214 | } 215 | 216 | void FAssetEditor_GenericGraph::AddReferencedObjects(FReferenceCollector& Collector) 217 | { 218 | Collector.AddReferencedObject(EditingGraph); 219 | Collector.AddReferencedObject(EditingGraph->EdGraph); 220 | } 221 | 222 | UGenericGraphEditorSettings* FAssetEditor_GenericGraph::GetSettings() const 223 | { 224 | return GenricGraphEditorSettings; 225 | } 226 | 227 | TSharedRef FAssetEditor_GenericGraph::SpawnTab_Viewport(const FSpawnTabArgs& Args) 228 | { 229 | check(Args.GetTabId() == FGenericGraphAssetEditorTabs::ViewportID); 230 | 231 | TSharedRef SpawnedTab = SNew(SDockTab) 232 | .Label(LOCTEXT("ViewportTab_Title", "Viewport")); 233 | 234 | if (ViewportWidget.IsValid()) 235 | { 236 | SpawnedTab->SetContent(ViewportWidget.ToSharedRef()); 237 | } 238 | 239 | return SpawnedTab; 240 | } 241 | 242 | TSharedRef FAssetEditor_GenericGraph::SpawnTab_Details(const FSpawnTabArgs& Args) 243 | { 244 | check(Args.GetTabId() == FGenericGraphAssetEditorTabs::GenericGraphPropertyID); 245 | 246 | return SNew(SDockTab) 247 | #if ENGINE_MAJOR_VERSION < 5 248 | .Icon(FAppStyle::GetBrush("LevelEditor.Tabs.Details")) 249 | #endif // #if ENGINE_MAJOR_VERSION < 5 250 | .Label(LOCTEXT("Details_Title", "Property")) 251 | [ 252 | PropertyWidget.ToSharedRef() 253 | ]; 254 | } 255 | 256 | TSharedRef FAssetEditor_GenericGraph::SpawnTab_EditorSettings(const FSpawnTabArgs& Args) 257 | { 258 | check(Args.GetTabId() == FGenericGraphAssetEditorTabs::GenericGraphEditorSettingsID); 259 | 260 | return SNew(SDockTab) 261 | #if ENGINE_MAJOR_VERSION < 5 262 | .Icon(FAppStyle::GetBrush("LevelEditor.Tabs.Details")) 263 | #endif // #if ENGINE_MAJOR_VERSION < 5 264 | .Label(LOCTEXT("EditorSettings_Title", "Generic Graph Editor Setttings")) 265 | [ 266 | EditorSettingsWidget.ToSharedRef() 267 | ]; 268 | } 269 | 270 | void FAssetEditor_GenericGraph::CreateInternalWidgets() 271 | { 272 | ViewportWidget = CreateViewportWidget(); 273 | 274 | FDetailsViewArgs Args; 275 | Args.bHideSelectionTip = true; 276 | Args.NotifyHook = this; 277 | 278 | FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor"); 279 | PropertyWidget = PropertyModule.CreateDetailView(Args); 280 | PropertyWidget->SetObject(EditingGraph); 281 | PropertyWidget->OnFinishedChangingProperties().AddSP(this, &FAssetEditor_GenericGraph::OnFinishedChangingProperties); 282 | 283 | EditorSettingsWidget = PropertyModule.CreateDetailView(Args); 284 | EditorSettingsWidget->SetObject(GenricGraphEditorSettings); 285 | } 286 | 287 | TSharedRef FAssetEditor_GenericGraph::CreateViewportWidget() 288 | { 289 | FGraphAppearanceInfo AppearanceInfo; 290 | AppearanceInfo.CornerText = LOCTEXT("AppearanceCornerText_GenericGraph", "Generic Graph"); 291 | 292 | CreateCommandList(); 293 | 294 | SGraphEditor::FGraphEditorEvents InEvents; 295 | InEvents.OnSelectionChanged = SGraphEditor::FOnSelectionChanged::CreateSP(this, &FAssetEditor_GenericGraph::OnSelectedNodesChanged); 296 | InEvents.OnNodeDoubleClicked = FSingleNodeEvent::CreateSP(this, &FAssetEditor_GenericGraph::OnNodeDoubleClicked); 297 | 298 | return SNew(SGraphEditor) 299 | .AdditionalCommands(GraphEditorCommands) 300 | .IsEditable(true) 301 | .Appearance(AppearanceInfo) 302 | .GraphToEdit(EditingGraph->EdGraph) 303 | .GraphEvents(InEvents) 304 | .AutoExpandActionMenu(true) 305 | .ShowGraphStateOverlay(false); 306 | } 307 | 308 | void FAssetEditor_GenericGraph::BindCommands() 309 | { 310 | ToolkitCommands->MapAction(FEditorCommands_GenericGraph::Get().GraphSettings, 311 | FExecuteAction::CreateSP(this, &FAssetEditor_GenericGraph::GraphSettings), 312 | FCanExecuteAction::CreateSP(this, &FAssetEditor_GenericGraph::CanGraphSettings) 313 | ); 314 | 315 | ToolkitCommands->MapAction(FEditorCommands_GenericGraph::Get().AutoArrange, 316 | FExecuteAction::CreateSP(this, &FAssetEditor_GenericGraph::AutoArrange), 317 | FCanExecuteAction::CreateSP(this, &FAssetEditor_GenericGraph::CanAutoArrange) 318 | ); 319 | } 320 | 321 | void FAssetEditor_GenericGraph::CreateEdGraph() 322 | { 323 | if (EditingGraph->EdGraph == nullptr) 324 | { 325 | EditingGraph->EdGraph = CastChecked(FBlueprintEditorUtils::CreateNewGraph(EditingGraph, NAME_None, UEdGraph_GenericGraph::StaticClass(), UAssetGraphSchema_GenericGraph::StaticClass())); 326 | EditingGraph->EdGraph->bAllowDeletion = false; 327 | 328 | // Give the schema a chance to fill out any required nodes (like the results node) 329 | const UEdGraphSchema* Schema = EditingGraph->EdGraph->GetSchema(); 330 | Schema->CreateDefaultNodesForGraph(*EditingGraph->EdGraph); 331 | } 332 | } 333 | 334 | void FAssetEditor_GenericGraph::CreateCommandList() 335 | { 336 | if (GraphEditorCommands.IsValid()) 337 | { 338 | return; 339 | } 340 | 341 | GraphEditorCommands = MakeShareable(new FUICommandList); 342 | 343 | // Can't use CreateSP here because derived editor are already implementing TSharedFromThis 344 | // however it should be safe, since commands are being used only within this editor 345 | // if it ever crashes, this function will have to go away and be reimplemented in each derived class 346 | 347 | GraphEditorCommands->MapAction(FEditorCommands_GenericGraph::Get().GraphSettings, 348 | FExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::GraphSettings), 349 | FCanExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::CanGraphSettings)); 350 | 351 | GraphEditorCommands->MapAction(FEditorCommands_GenericGraph::Get().AutoArrange, 352 | FExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::AutoArrange), 353 | FCanExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::CanAutoArrange)); 354 | 355 | GraphEditorCommands->MapAction(FGenericCommands::Get().SelectAll, 356 | FExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::SelectAllNodes), 357 | FCanExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::CanSelectAllNodes) 358 | ); 359 | 360 | GraphEditorCommands->MapAction(FGenericCommands::Get().Delete, 361 | FExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::DeleteSelectedNodes), 362 | FCanExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::CanDeleteNodes) 363 | ); 364 | 365 | GraphEditorCommands->MapAction(FGenericCommands::Get().Copy, 366 | FExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::CopySelectedNodes), 367 | FCanExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::CanCopyNodes) 368 | ); 369 | 370 | GraphEditorCommands->MapAction(FGenericCommands::Get().Cut, 371 | FExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::CutSelectedNodes), 372 | FCanExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::CanCutNodes) 373 | ); 374 | 375 | GraphEditorCommands->MapAction(FGenericCommands::Get().Paste, 376 | FExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::PasteNodes), 377 | FCanExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::CanPasteNodes) 378 | ); 379 | 380 | GraphEditorCommands->MapAction(FGenericCommands::Get().Duplicate, 381 | FExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::DuplicateNodes), 382 | FCanExecuteAction::CreateRaw(this, &FAssetEditor_GenericGraph::CanDuplicateNodes) 383 | ); 384 | 385 | GraphEditorCommands->MapAction(FGenericCommands::Get().Rename, 386 | FExecuteAction::CreateSP(this, &FAssetEditor_GenericGraph::OnRenameNode), 387 | FCanExecuteAction::CreateSP(this, &FAssetEditor_GenericGraph::CanRenameNodes) 388 | ); 389 | } 390 | 391 | TSharedPtr FAssetEditor_GenericGraph::GetCurrGraphEditor() const 392 | { 393 | return ViewportWidget; 394 | } 395 | 396 | FGraphPanelSelectionSet FAssetEditor_GenericGraph::GetSelectedNodes() const 397 | { 398 | FGraphPanelSelectionSet CurrentSelection; 399 | TSharedPtr FocusedGraphEd = GetCurrGraphEditor(); 400 | if (FocusedGraphEd.IsValid()) 401 | { 402 | CurrentSelection = FocusedGraphEd->GetSelectedNodes(); 403 | } 404 | 405 | return CurrentSelection; 406 | } 407 | 408 | void FAssetEditor_GenericGraph::RebuildGenericGraph() 409 | { 410 | if (EditingGraph == nullptr) 411 | { 412 | LOG_WARNING(TEXT("FGenericGraphAssetEditor::RebuildGenericGraph EditingGraph is nullptr")); 413 | return; 414 | } 415 | 416 | UEdGraph_GenericGraph* EdGraph = Cast(EditingGraph->EdGraph); 417 | check(EdGraph != nullptr); 418 | 419 | EdGraph->RebuildGenericGraph(); 420 | } 421 | 422 | void FAssetEditor_GenericGraph::SelectAllNodes() 423 | { 424 | TSharedPtr CurrentGraphEditor = GetCurrGraphEditor(); 425 | if (CurrentGraphEditor.IsValid()) 426 | { 427 | CurrentGraphEditor->SelectAllNodes(); 428 | } 429 | } 430 | 431 | bool FAssetEditor_GenericGraph::CanSelectAllNodes() 432 | { 433 | return true; 434 | } 435 | 436 | void FAssetEditor_GenericGraph::DeleteSelectedNodes() 437 | { 438 | TSharedPtr CurrentGraphEditor = GetCurrGraphEditor(); 439 | if (!CurrentGraphEditor.IsValid()) 440 | { 441 | return; 442 | } 443 | 444 | const FScopedTransaction Transaction(FGenericCommands::Get().Delete->GetDescription()); 445 | 446 | CurrentGraphEditor->GetCurrentGraph()->Modify(); 447 | 448 | const FGraphPanelSelectionSet SelectedNodes = CurrentGraphEditor->GetSelectedNodes(); 449 | CurrentGraphEditor->ClearSelectionSet(); 450 | 451 | for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt) 452 | { 453 | UEdGraphNode* EdNode = Cast(*NodeIt); 454 | if (EdNode == nullptr || !EdNode->CanUserDeleteNode()) 455 | continue;; 456 | 457 | if (UEdNode_GenericGraphNode* EdNode_Node = Cast(EdNode)) 458 | { 459 | EdNode_Node->Modify(); 460 | 461 | const UEdGraphSchema* Schema = EdNode_Node->GetSchema(); 462 | if (Schema != nullptr) 463 | { 464 | Schema->BreakNodeLinks(*EdNode_Node); 465 | } 466 | 467 | EdNode_Node->DestroyNode(); 468 | } 469 | else 470 | { 471 | EdNode->Modify(); 472 | EdNode->DestroyNode(); 473 | } 474 | } 475 | } 476 | 477 | bool FAssetEditor_GenericGraph::CanDeleteNodes() 478 | { 479 | // If any of the nodes can be deleted then we should allow deleting 480 | const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes(); 481 | for (FGraphPanelSelectionSet::TConstIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter) 482 | { 483 | UEdGraphNode* Node = Cast(*SelectedIter); 484 | if (Node != nullptr && Node->CanUserDeleteNode()) 485 | { 486 | return true; 487 | } 488 | } 489 | 490 | return false; 491 | } 492 | 493 | void FAssetEditor_GenericGraph::DeleteSelectedDuplicatableNodes() 494 | { 495 | TSharedPtr CurrentGraphEditor = GetCurrGraphEditor(); 496 | if (!CurrentGraphEditor.IsValid()) 497 | { 498 | return; 499 | } 500 | 501 | const FGraphPanelSelectionSet OldSelectedNodes = CurrentGraphEditor->GetSelectedNodes(); 502 | CurrentGraphEditor->ClearSelectionSet(); 503 | 504 | for (FGraphPanelSelectionSet::TConstIterator SelectedIter(OldSelectedNodes); SelectedIter; ++SelectedIter) 505 | { 506 | UEdGraphNode* Node = Cast(*SelectedIter); 507 | if (Node && Node->CanDuplicateNode()) 508 | { 509 | CurrentGraphEditor->SetNodeSelection(Node, true); 510 | } 511 | } 512 | 513 | // Delete the duplicatable nodes 514 | DeleteSelectedNodes(); 515 | 516 | CurrentGraphEditor->ClearSelectionSet(); 517 | 518 | for (FGraphPanelSelectionSet::TConstIterator SelectedIter(OldSelectedNodes); SelectedIter; ++SelectedIter) 519 | { 520 | if (UEdGraphNode* Node = Cast(*SelectedIter)) 521 | { 522 | CurrentGraphEditor->SetNodeSelection(Node, true); 523 | } 524 | } 525 | } 526 | 527 | void FAssetEditor_GenericGraph::CutSelectedNodes() 528 | { 529 | CopySelectedNodes(); 530 | DeleteSelectedDuplicatableNodes(); 531 | } 532 | 533 | bool FAssetEditor_GenericGraph::CanCutNodes() 534 | { 535 | return CanCopyNodes() && CanDeleteNodes(); 536 | } 537 | 538 | void FAssetEditor_GenericGraph::CopySelectedNodes() 539 | { 540 | // Export the selected nodes and place the text on the clipboard 541 | FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes(); 542 | 543 | FString ExportedText; 544 | 545 | for (FGraphPanelSelectionSet::TIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter) 546 | { 547 | UEdGraphNode* Node = Cast(*SelectedIter); 548 | if (Node == nullptr) 549 | { 550 | SelectedIter.RemoveCurrent(); 551 | continue; 552 | } 553 | 554 | if (UEdNode_GenericGraphEdge* EdNode_Edge = Cast(*SelectedIter)) 555 | { 556 | UEdNode_GenericGraphNode* StartNode = EdNode_Edge->GetStartNode(); 557 | UEdNode_GenericGraphNode* EndNode = EdNode_Edge->GetEndNode(); 558 | 559 | if (!SelectedNodes.Contains(StartNode) || !SelectedNodes.Contains(EndNode)) 560 | { 561 | SelectedIter.RemoveCurrent(); 562 | continue; 563 | } 564 | } 565 | 566 | Node->PrepareForCopying(); 567 | } 568 | 569 | FEdGraphUtilities::ExportNodesToText(SelectedNodes, ExportedText); 570 | FPlatformApplicationMisc::ClipboardCopy(*ExportedText); 571 | } 572 | 573 | bool FAssetEditor_GenericGraph::CanCopyNodes() 574 | { 575 | // If any of the nodes can be duplicated then we should allow copying 576 | const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes(); 577 | for (FGraphPanelSelectionSet::TConstIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter) 578 | { 579 | UEdGraphNode* Node = Cast(*SelectedIter); 580 | if (Node && Node->CanDuplicateNode()) 581 | { 582 | return true; 583 | } 584 | } 585 | 586 | return false; 587 | } 588 | 589 | void FAssetEditor_GenericGraph::PasteNodes() 590 | { 591 | TSharedPtr CurrentGraphEditor = GetCurrGraphEditor(); 592 | if (CurrentGraphEditor.IsValid()) 593 | { 594 | PasteNodesHere(CurrentGraphEditor->GetPasteLocation()); 595 | } 596 | } 597 | 598 | void FAssetEditor_GenericGraph::PasteNodesHere(const FVector2D& Location) 599 | { 600 | // Find the graph editor with focus 601 | TSharedPtr CurrentGraphEditor = GetCurrGraphEditor(); 602 | if (!CurrentGraphEditor.IsValid()) 603 | { 604 | return; 605 | } 606 | // Select the newly pasted stuff 607 | UEdGraph* EdGraph = CurrentGraphEditor->GetCurrentGraph(); 608 | 609 | { 610 | const FScopedTransaction Transaction(FGenericCommands::Get().Paste->GetDescription()); 611 | EdGraph->Modify(); 612 | 613 | // Clear the selection set (newly pasted stuff will be selected) 614 | CurrentGraphEditor->ClearSelectionSet(); 615 | 616 | // Grab the text to paste from the clipboard. 617 | FString TextToImport; 618 | FPlatformApplicationMisc::ClipboardPaste(TextToImport); 619 | 620 | // Import the nodes 621 | TSet PastedNodes; 622 | FEdGraphUtilities::ImportNodesFromText(EdGraph, TextToImport, PastedNodes); 623 | 624 | //Average position of nodes so we can move them while still maintaining relative distances to each other 625 | FVector2D AvgNodePosition(0.0f, 0.0f); 626 | 627 | for (TSet::TIterator It(PastedNodes); It; ++It) 628 | { 629 | UEdGraphNode* Node = *It; 630 | AvgNodePosition.X += Node->NodePosX; 631 | AvgNodePosition.Y += Node->NodePosY; 632 | } 633 | 634 | float InvNumNodes = 1.0f / float(PastedNodes.Num()); 635 | AvgNodePosition.X *= InvNumNodes; 636 | AvgNodePosition.Y *= InvNumNodes; 637 | 638 | for (TSet::TIterator It(PastedNodes); It; ++It) 639 | { 640 | UEdGraphNode* Node = *It; 641 | CurrentGraphEditor->SetNodeSelection(Node, true); 642 | 643 | Node->NodePosX = (Node->NodePosX - AvgNodePosition.X) + Location.X; 644 | Node->NodePosY = (Node->NodePosY - AvgNodePosition.Y) + Location.Y; 645 | 646 | Node->SnapToGrid(16); 647 | 648 | // Give new node a different Guid from the old one 649 | Node->CreateNewGuid(); 650 | } 651 | } 652 | 653 | // Update UI 654 | CurrentGraphEditor->NotifyGraphChanged(); 655 | 656 | UObject* GraphOwner = EdGraph->GetOuter(); 657 | if (GraphOwner) 658 | { 659 | GraphOwner->PostEditChange(); 660 | GraphOwner->MarkPackageDirty(); 661 | } 662 | } 663 | 664 | bool FAssetEditor_GenericGraph::CanPasteNodes() 665 | { 666 | TSharedPtr CurrentGraphEditor = GetCurrGraphEditor(); 667 | if (!CurrentGraphEditor.IsValid()) 668 | { 669 | return false; 670 | } 671 | 672 | FString ClipboardContent; 673 | FPlatformApplicationMisc::ClipboardPaste(ClipboardContent); 674 | 675 | return FEdGraphUtilities::CanImportNodesFromText(CurrentGraphEditor->GetCurrentGraph(), ClipboardContent); 676 | } 677 | 678 | void FAssetEditor_GenericGraph::DuplicateNodes() 679 | { 680 | CopySelectedNodes(); 681 | PasteNodes(); 682 | } 683 | 684 | bool FAssetEditor_GenericGraph::CanDuplicateNodes() 685 | { 686 | return CanCopyNodes(); 687 | } 688 | 689 | void FAssetEditor_GenericGraph::GraphSettings() 690 | { 691 | PropertyWidget->SetObject(EditingGraph); 692 | } 693 | 694 | bool FAssetEditor_GenericGraph::CanGraphSettings() const 695 | { 696 | return true; 697 | } 698 | 699 | void FAssetEditor_GenericGraph::AutoArrange() 700 | { 701 | UEdGraph_GenericGraph* EdGraph = Cast(EditingGraph->EdGraph); 702 | check(EdGraph != nullptr); 703 | 704 | const FScopedTransaction Transaction(LOCTEXT("GenericGraphEditorAutoArrange", "Generic Graph Editor: Auto Arrange")); 705 | 706 | EdGraph->Modify(); 707 | 708 | UAutoLayoutStrategy* LayoutStrategy = nullptr; 709 | switch (GenricGraphEditorSettings->AutoLayoutStrategy) 710 | { 711 | case EAutoLayoutStrategy::Tree: 712 | LayoutStrategy = NewObject(EdGraph, UTreeLayoutStrategy::StaticClass()); 713 | break; 714 | case EAutoLayoutStrategy::ForceDirected: 715 | LayoutStrategy = NewObject(EdGraph, UForceDirectedLayoutStrategy::StaticClass()); 716 | break; 717 | default: 718 | break; 719 | } 720 | 721 | if (LayoutStrategy != nullptr) 722 | { 723 | LayoutStrategy->Settings = GenricGraphEditorSettings; 724 | LayoutStrategy->Layout(EdGraph); 725 | LayoutStrategy->ConditionalBeginDestroy(); 726 | } 727 | else 728 | { 729 | LOG_ERROR(TEXT("FAssetEditor_GenericGraph::AutoArrange LayoutStrategy is null.")); 730 | } 731 | } 732 | 733 | bool FAssetEditor_GenericGraph::CanAutoArrange() const 734 | { 735 | return EditingGraph != nullptr && Cast(EditingGraph->EdGraph) != nullptr; 736 | } 737 | 738 | void FAssetEditor_GenericGraph::OnRenameNode() 739 | { 740 | TSharedPtr CurrentGraphEditor = GetCurrGraphEditor(); 741 | if (CurrentGraphEditor.IsValid()) 742 | { 743 | const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes(); 744 | for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt) 745 | { 746 | UEdGraphNode* SelectedNode = Cast(*NodeIt); 747 | if (SelectedNode != NULL && SelectedNode->bCanRenameNode) 748 | { 749 | CurrentGraphEditor->IsNodeTitleVisible(SelectedNode, true); 750 | break; 751 | } 752 | } 753 | } 754 | } 755 | 756 | bool FAssetEditor_GenericGraph::CanRenameNodes() const 757 | { 758 | UEdGraph_GenericGraph* EdGraph = Cast(EditingGraph->EdGraph); 759 | check(EdGraph != nullptr); 760 | 761 | UGenericGraph* Graph = EdGraph->GetGenericGraph(); 762 | check(Graph != nullptr) 763 | 764 | return Graph->bCanRenameNode && GetSelectedNodes().Num() == 1; 765 | } 766 | 767 | void FAssetEditor_GenericGraph::OnSelectedNodesChanged(const TSet& NewSelection) 768 | { 769 | TArray Selection; 770 | 771 | for (UObject* SelectionEntry : NewSelection) 772 | { 773 | Selection.Add(SelectionEntry); 774 | } 775 | 776 | if (Selection.Num() == 0) 777 | { 778 | PropertyWidget->SetObject(EditingGraph); 779 | 780 | } 781 | else 782 | { 783 | PropertyWidget->SetObjects(Selection); 784 | } 785 | } 786 | 787 | void FAssetEditor_GenericGraph::OnNodeDoubleClicked(UEdGraphNode* Node) 788 | { 789 | 790 | } 791 | 792 | void FAssetEditor_GenericGraph::OnFinishedChangingProperties(const FPropertyChangedEvent& PropertyChangedEvent) 793 | { 794 | if (EditingGraph == nullptr) 795 | return; 796 | 797 | EditingGraph->EdGraph->GetSchema()->ForceVisualizationCacheClear(); 798 | } 799 | 800 | #if ENGINE_MAJOR_VERSION < 5 801 | void FAssetEditor_GenericGraph::OnPackageSaved(const FString& PackageFileName, UObject* Outer) 802 | { 803 | RebuildGenericGraph(); 804 | } 805 | #else // #if ENGINE_MAJOR_VERSION < 5 806 | void FAssetEditor_GenericGraph::OnPackageSavedWithContext(const FString& PackageFileName, UPackage* Package, FObjectPostSaveContext ObjectSaveContext) 807 | { 808 | RebuildGenericGraph(); 809 | } 810 | #endif // #else // #if ENGINE_MAJOR_VERSION < 5 811 | 812 | void FAssetEditor_GenericGraph::RegisterToolbarTab(const TSharedRef& InTabManager) 813 | { 814 | FAssetEditorToolkit::RegisterTabSpawners(InTabManager); 815 | } 816 | 817 | 818 | #undef LOCTEXT_NAMESPACE 819 | 820 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Private/GenericGraphAssetEditor/AssetGraphSchema_GenericGraph.cpp: -------------------------------------------------------------------------------- 1 | #include "GenericGraphAssetEditor/AssetGraphSchema_GenericGraph.h" 2 | #include "ToolMenus.h" 3 | #include "GenericGraphEditorPCH.h" 4 | #include "GenericGraphAssetEditor/EdNode_GenericGraphNode.h" 5 | #include "GenericGraphAssetEditor/EdNode_GenericGraphEdge.h" 6 | #include "GenericGraphAssetEditor/ConnectionDrawingPolicy_GenericGraph.h" 7 | #include "GraphEditorActions.h" 8 | #include "Framework/Commands/GenericCommands.h" 9 | #include "AutoLayout/ForceDirectedLayoutStrategy.h" 10 | #include "AutoLayout/TreeLayoutStrategy.h" 11 | 12 | #define LOCTEXT_NAMESPACE "AssetSchema_GenericGraph" 13 | 14 | int32 UAssetGraphSchema_GenericGraph::CurrentCacheRefreshID = 0; 15 | 16 | 17 | class FNodeVisitorCycleChecker 18 | { 19 | public: 20 | /** Check whether a loop in the graph would be caused by linking the passed-in nodes */ 21 | bool CheckForLoop(UEdGraphNode* StartNode, UEdGraphNode* EndNode) 22 | { 23 | 24 | VisitedNodes.Add(StartNode); 25 | 26 | return TraverseNodes(EndNode); 27 | } 28 | 29 | private: 30 | bool TraverseNodes(UEdGraphNode* Node) 31 | { 32 | VisitedNodes.Add(Node); 33 | 34 | for (auto MyPin : Node->Pins) 35 | { 36 | if (MyPin->Direction == EGPD_Output) 37 | { 38 | for (auto OtherPin : MyPin->LinkedTo) 39 | { 40 | UEdGraphNode* OtherNode = OtherPin->GetOwningNode(); 41 | if (VisitedNodes.Contains(OtherNode)) 42 | { 43 | // Only an issue if this is a back-edge 44 | return false; 45 | } 46 | else if (!FinishedNodes.Contains(OtherNode)) 47 | { 48 | // Only should traverse if this node hasn't been traversed 49 | if (!TraverseNodes(OtherNode)) 50 | return false; 51 | } 52 | } 53 | } 54 | } 55 | 56 | VisitedNodes.Remove(Node); 57 | FinishedNodes.Add(Node); 58 | return true; 59 | }; 60 | 61 | 62 | TSet VisitedNodes; 63 | TSet FinishedNodes; 64 | }; 65 | 66 | UEdGraphNode* FAssetSchemaAction_GenericGraph_NewNode::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode /*= true*/) 67 | { 68 | UEdGraphNode* ResultNode = nullptr; 69 | 70 | if (NodeTemplate != nullptr) 71 | { 72 | const FScopedTransaction Transaction(LOCTEXT("GenericGraphEditorNewNode", "Generic Graph Editor: New Node")); 73 | ParentGraph->Modify(); 74 | if (FromPin != nullptr) 75 | FromPin->Modify(); 76 | 77 | NodeTemplate->Rename(nullptr, ParentGraph); 78 | ParentGraph->AddNode(NodeTemplate, true, bSelectNewNode); 79 | 80 | NodeTemplate->CreateNewGuid(); 81 | NodeTemplate->PostPlacedNewNode(); 82 | NodeTemplate->AllocateDefaultPins(); 83 | NodeTemplate->AutowireNewNode(FromPin); 84 | 85 | NodeTemplate->NodePosX = Location.X; 86 | NodeTemplate->NodePosY = Location.Y; 87 | 88 | NodeTemplate->GenericGraphNode->SetFlags(RF_Transactional); 89 | NodeTemplate->SetFlags(RF_Transactional); 90 | 91 | ResultNode = NodeTemplate; 92 | } 93 | 94 | return ResultNode; 95 | } 96 | 97 | void FAssetSchemaAction_GenericGraph_NewNode::AddReferencedObjects(FReferenceCollector& Collector) 98 | { 99 | FEdGraphSchemaAction::AddReferencedObjects(Collector); 100 | Collector.AddReferencedObject(NodeTemplate); 101 | } 102 | 103 | UEdGraphNode* FAssetSchemaAction_GenericGraph_NewEdge::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode /*= true*/) 104 | { 105 | UEdGraphNode* ResultNode = nullptr; 106 | 107 | if (NodeTemplate != nullptr) 108 | { 109 | const FScopedTransaction Transaction(LOCTEXT("GenericGraphEditorNewEdge", "Generic Graph Editor: New Edge")); 110 | ParentGraph->Modify(); 111 | if (FromPin != nullptr) 112 | FromPin->Modify(); 113 | 114 | NodeTemplate->Rename(nullptr, ParentGraph); 115 | ParentGraph->AddNode(NodeTemplate, true, bSelectNewNode); 116 | 117 | NodeTemplate->CreateNewGuid(); 118 | NodeTemplate->PostPlacedNewNode(); 119 | NodeTemplate->AllocateDefaultPins(); 120 | NodeTemplate->AutowireNewNode(FromPin); 121 | 122 | NodeTemplate->NodePosX = Location.X; 123 | NodeTemplate->NodePosY = Location.Y; 124 | 125 | NodeTemplate->GenericGraphEdge->SetFlags(RF_Transactional); 126 | NodeTemplate->SetFlags(RF_Transactional); 127 | 128 | ResultNode = NodeTemplate; 129 | } 130 | 131 | return ResultNode; 132 | } 133 | 134 | void FAssetSchemaAction_GenericGraph_NewEdge::AddReferencedObjects(FReferenceCollector& Collector) 135 | { 136 | FEdGraphSchemaAction::AddReferencedObjects(Collector); 137 | Collector.AddReferencedObject(NodeTemplate); 138 | } 139 | 140 | void UAssetGraphSchema_GenericGraph::GetBreakLinkToSubMenuActions(UToolMenu* Menu, UEdGraphPin* InGraphPin) 141 | { 142 | // Make sure we have a unique name for every entry in the list 143 | TMap< FString, uint32 > LinkTitleCount; 144 | 145 | FToolMenuSection& Section = Menu->FindOrAddSection("GenericGraphAssetGraphSchemaPinActions"); 146 | 147 | // Add all the links we could break from 148 | for (TArray::TConstIterator Links(InGraphPin->LinkedTo); Links; ++Links) 149 | { 150 | UEdGraphPin* Pin = *Links; 151 | FString TitleString = Pin->GetOwningNode()->GetNodeTitle(ENodeTitleType::ListView).ToString(); 152 | FText Title = FText::FromString(TitleString); 153 | if (Pin->PinName != TEXT("")) 154 | { 155 | TitleString = FString::Printf(TEXT("%s (%s)"), *TitleString, *Pin->PinName.ToString()); 156 | 157 | // Add name of connection if possible 158 | FFormatNamedArguments Args; 159 | Args.Add(TEXT("NodeTitle"), Title); 160 | Args.Add(TEXT("PinName"), Pin->GetDisplayName()); 161 | Title = FText::Format(LOCTEXT("BreakDescPin", "{NodeTitle} ({PinName})"), Args); 162 | } 163 | 164 | uint32& Count = LinkTitleCount.FindOrAdd(TitleString); 165 | 166 | FText Description; 167 | FFormatNamedArguments Args; 168 | Args.Add(TEXT("NodeTitle"), Title); 169 | Args.Add(TEXT("NumberOfNodes"), Count); 170 | 171 | if (Count == 0) 172 | { 173 | Description = FText::Format(LOCTEXT("BreakDesc", "Break link to {NodeTitle}"), Args); 174 | } 175 | else 176 | { 177 | Description = FText::Format(LOCTEXT("BreakDescMulti", "Break link to {NodeTitle} ({NumberOfNodes})"), Args); 178 | } 179 | ++Count; 180 | 181 | Section.AddMenuEntry(NAME_None, Description, Description, FSlateIcon(), FUIAction( 182 | FExecuteAction::CreateUObject(this, &UAssetGraphSchema_GenericGraph::BreakSinglePinLink, const_cast(InGraphPin), *Links))); 183 | } 184 | } 185 | 186 | EGraphType UAssetGraphSchema_GenericGraph::GetGraphType(const UEdGraph* TestEdGraph) const 187 | { 188 | return GT_StateMachine; 189 | } 190 | 191 | void UAssetGraphSchema_GenericGraph::GetGraphContextActions(FGraphContextMenuBuilder& ContextMenuBuilder) const 192 | { 193 | UGenericGraph* Graph = CastChecked(ContextMenuBuilder.CurrentGraph->GetOuter()); 194 | 195 | if (Graph->NodeType == nullptr) 196 | { 197 | return; 198 | } 199 | 200 | const bool bNoParent = (ContextMenuBuilder.FromPin == NULL); 201 | 202 | const FText AddToolTip = LOCTEXT("NewGenericGraphNodeTooltip", "Add node here"); 203 | 204 | TSet > Visited; 205 | 206 | FText Desc = Graph->NodeType.GetDefaultObject()->ContextMenuName; 207 | 208 | if (Desc.IsEmpty()) 209 | { 210 | FString Title = Graph->NodeType->GetName(); 211 | Title.RemoveFromEnd("_C"); 212 | Desc = FText::FromString(Title); 213 | } 214 | 215 | if (!Graph->NodeType->HasAnyClassFlags(CLASS_Abstract)) 216 | { 217 | TSharedPtr NewNodeAction(new FAssetSchemaAction_GenericGraph_NewNode(LOCTEXT("GenericGraphNodeAction", "Generic Graph Node"), Desc, AddToolTip, 0)); 218 | NewNodeAction->NodeTemplate = NewObject(ContextMenuBuilder.OwnerOfTemporaries); 219 | NewNodeAction->NodeTemplate->GenericGraphNode = NewObject(NewNodeAction->NodeTemplate, Graph->NodeType); 220 | NewNodeAction->NodeTemplate->GenericGraphNode->Graph = Graph; 221 | ContextMenuBuilder.AddAction(NewNodeAction); 222 | 223 | Visited.Add(Graph->NodeType); 224 | } 225 | 226 | for (TObjectIterator It; It; ++It) 227 | { 228 | if (It->IsChildOf(Graph->NodeType) && !It->HasAnyClassFlags(CLASS_Abstract) && !Visited.Contains(*It)) 229 | { 230 | TSubclassOf NodeType = *It; 231 | 232 | if (It->GetName().StartsWith("REINST") || It->GetName().StartsWith("SKEL")) 233 | continue; 234 | 235 | if (!Graph->GetClass()->IsChildOf(NodeType.GetDefaultObject()->CompatibleGraphType)) 236 | continue; 237 | 238 | Desc = NodeType.GetDefaultObject()->ContextMenuName; 239 | 240 | if (Desc.IsEmpty()) 241 | { 242 | FString Title = NodeType->GetName(); 243 | Title.RemoveFromEnd("_C"); 244 | Desc = FText::FromString(Title); 245 | } 246 | 247 | TSharedPtr Action(new FAssetSchemaAction_GenericGraph_NewNode(LOCTEXT("GenericGraphNodeAction", "Generic Graph Node"), Desc, AddToolTip, 0)); 248 | Action->NodeTemplate = NewObject(ContextMenuBuilder.OwnerOfTemporaries); 249 | Action->NodeTemplate->GenericGraphNode = NewObject(Action->NodeTemplate, NodeType); 250 | Action->NodeTemplate->GenericGraphNode->Graph = Graph; 251 | ContextMenuBuilder.AddAction(Action); 252 | 253 | Visited.Add(NodeType); 254 | } 255 | } 256 | } 257 | 258 | void UAssetGraphSchema_GenericGraph::GetContextMenuActions(UToolMenu* Menu, UGraphNodeContextMenuContext* Context) const 259 | { 260 | if (Context->Pin) 261 | { 262 | { 263 | FToolMenuSection& Section = Menu->AddSection("GenericGraphAssetGraphSchemaNodeActions", LOCTEXT("PinActionsMenuHeader", "Pin Actions")); 264 | // Only display the 'Break Links' option if there is a link to break! 265 | if (Context->Pin->LinkedTo.Num() > 0) 266 | { 267 | Section.AddMenuEntry(FGraphEditorCommands::Get().BreakPinLinks); 268 | 269 | // add sub menu for break link to 270 | if (Context->Pin->LinkedTo.Num() > 1) 271 | { 272 | Section.AddSubMenu( 273 | "BreakLinkTo", 274 | LOCTEXT("BreakLinkTo", "Break Link To..."), 275 | LOCTEXT("BreakSpecificLinks", "Break a specific link..."), 276 | FNewToolMenuDelegate::CreateUObject((UAssetGraphSchema_GenericGraph* const)this, &UAssetGraphSchema_GenericGraph::GetBreakLinkToSubMenuActions, const_cast(Context->Pin))); 277 | } 278 | else 279 | { 280 | ((UAssetGraphSchema_GenericGraph* const)this)->GetBreakLinkToSubMenuActions(Menu, const_cast(Context->Pin)); 281 | } 282 | } 283 | } 284 | } 285 | else if (Context->Node) 286 | { 287 | { 288 | FToolMenuSection& Section = Menu->AddSection("GenericGraphAssetGraphSchemaNodeActions", LOCTEXT("ClassActionsMenuHeader", "Node Actions")); 289 | Section.AddMenuEntry(FGenericCommands::Get().Delete); 290 | Section.AddMenuEntry(FGenericCommands::Get().Cut); 291 | Section.AddMenuEntry(FGenericCommands::Get().Copy); 292 | Section.AddMenuEntry(FGenericCommands::Get().Duplicate); 293 | 294 | Section.AddMenuEntry(FGraphEditorCommands::Get().BreakNodeLinks); 295 | } 296 | } 297 | 298 | Super::GetContextMenuActions(Menu, Context); 299 | } 300 | 301 | const FPinConnectionResponse UAssetGraphSchema_GenericGraph::CanCreateConnection(const UEdGraphPin* A, const UEdGraphPin* B) const 302 | { 303 | // Make sure the pins are not on the same node 304 | if (A->GetOwningNode() == B->GetOwningNode()) 305 | { 306 | return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("PinErrorSameNode", "Can't connect node to itself")); 307 | } 308 | 309 | const UEdGraphPin *Out = A; 310 | const UEdGraphPin *In = B; 311 | 312 | UEdNode_GenericGraphNode* EdNode_Out = Cast(Out->GetOwningNode()); 313 | UEdNode_GenericGraphNode* EdNode_In = Cast(In->GetOwningNode()); 314 | 315 | if (EdNode_Out == nullptr || EdNode_In == nullptr) 316 | { 317 | return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("PinError", "Not a valid UGenericGraphEdNode")); 318 | } 319 | 320 | //Determine if we can have cycles or not 321 | bool bAllowCycles = false; 322 | auto EdGraph = Cast(Out->GetOwningNode()->GetGraph()); 323 | if (EdGraph != nullptr) 324 | { 325 | bAllowCycles = EdGraph->GetGenericGraph()->bCanBeCyclical; 326 | } 327 | 328 | // check for cycles 329 | FNodeVisitorCycleChecker CycleChecker; 330 | if (!bAllowCycles && !CycleChecker.CheckForLoop(Out->GetOwningNode(), In->GetOwningNode())) 331 | { 332 | return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("PinErrorCycle", "Can't create a graph cycle")); 333 | } 334 | 335 | FText ErrorMessage; 336 | if (!EdNode_Out->GenericGraphNode->CanCreateConnectionTo(EdNode_In->GenericGraphNode, EdNode_Out->GetOutputPin()->LinkedTo.Num(), ErrorMessage)) 337 | { 338 | return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, ErrorMessage); 339 | } 340 | if (!EdNode_In->GenericGraphNode->CanCreateConnectionFrom(EdNode_Out->GenericGraphNode, EdNode_In->GetInputPin()->LinkedTo.Num(), ErrorMessage)) 341 | { 342 | return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, ErrorMessage); 343 | } 344 | 345 | 346 | if (EdNode_Out->GenericGraphNode->GetGraph()->bEdgeEnabled) 347 | { 348 | return FPinConnectionResponse(CONNECT_RESPONSE_MAKE_WITH_CONVERSION_NODE, LOCTEXT("PinConnect", "Connect nodes with edge")); 349 | } 350 | else 351 | { 352 | return FPinConnectionResponse(CONNECT_RESPONSE_MAKE, LOCTEXT("PinConnect", "Connect nodes")); 353 | } 354 | } 355 | 356 | bool UAssetGraphSchema_GenericGraph::TryCreateConnection(UEdGraphPin* A, UEdGraphPin* B) const 357 | { 358 | // We don't actually care about the pin, we want the node that is being dragged between 359 | UEdNode_GenericGraphNode* NodeA = Cast(A->GetOwningNode()); 360 | UEdNode_GenericGraphNode* NodeB = Cast(B->GetOwningNode()); 361 | 362 | // Check that this edge doesn't already exist 363 | for (UEdGraphPin *TestPin : NodeA->GetOutputPin()->LinkedTo) 364 | { 365 | UEdGraphNode* ChildNode = TestPin->GetOwningNode(); 366 | if (UEdNode_GenericGraphEdge* EdNode_Edge = Cast(ChildNode)) 367 | { 368 | ChildNode = EdNode_Edge->GetEndNode(); 369 | } 370 | 371 | if (ChildNode == NodeB) 372 | return false; 373 | } 374 | 375 | if (NodeA && NodeB) 376 | { 377 | // Always create connections from node A to B, don't allow adding in reverse 378 | Super::TryCreateConnection(NodeA->GetOutputPin(), NodeB->GetInputPin()); 379 | return true; 380 | } 381 | else 382 | { 383 | return false; 384 | } 385 | } 386 | 387 | bool UAssetGraphSchema_GenericGraph::CreateAutomaticConversionNodeAndConnections(UEdGraphPin* A, UEdGraphPin* B) const 388 | { 389 | UEdNode_GenericGraphNode* NodeA = Cast(A->GetOwningNode()); 390 | UEdNode_GenericGraphNode* NodeB = Cast(B->GetOwningNode()); 391 | 392 | // Are nodes and pins all valid? 393 | if (!NodeA || !NodeA->GetOutputPin() || !NodeB || !NodeB->GetInputPin()) 394 | return false; 395 | 396 | UGenericGraph* Graph = NodeA->GenericGraphNode->GetGraph(); 397 | 398 | FVector2D InitPos((NodeA->NodePosX + NodeB->NodePosX) / 2, (NodeA->NodePosY + NodeB->NodePosY) / 2); 399 | 400 | FAssetSchemaAction_GenericGraph_NewEdge Action; 401 | Action.NodeTemplate = NewObject(NodeA->GetGraph()); 402 | Action.NodeTemplate->SetEdge(NewObject(Action.NodeTemplate, Graph->EdgeType)); 403 | UEdNode_GenericGraphEdge* EdgeNode = Cast(Action.PerformAction(NodeA->GetGraph(), nullptr, InitPos, false)); 404 | 405 | // Always create connections from node A to B, don't allow adding in reverse 406 | EdgeNode->CreateConnections(NodeA, NodeB); 407 | 408 | return true; 409 | } 410 | 411 | class FConnectionDrawingPolicy* UAssetGraphSchema_GenericGraph::CreateConnectionDrawingPolicy(int32 InBackLayerID, int32 InFrontLayerID, float InZoomFactor, const FSlateRect& InClippingRect, class FSlateWindowElementList& InDrawElements, class UEdGraph* InGraphObj) const 412 | { 413 | return new FConnectionDrawingPolicy_GenericGraph(InBackLayerID, InFrontLayerID, InZoomFactor, InClippingRect, InDrawElements, InGraphObj); 414 | } 415 | 416 | FLinearColor UAssetGraphSchema_GenericGraph::GetPinTypeColor(const FEdGraphPinType& PinType) const 417 | { 418 | return FColor::White; 419 | } 420 | 421 | void UAssetGraphSchema_GenericGraph::BreakNodeLinks(UEdGraphNode& TargetNode) const 422 | { 423 | const FScopedTransaction Transaction(NSLOCTEXT("UnrealEd", "GraphEd_BreakNodeLinks", "Break Node Links")); 424 | 425 | Super::BreakNodeLinks(TargetNode); 426 | } 427 | 428 | void UAssetGraphSchema_GenericGraph::BreakPinLinks(UEdGraphPin& TargetPin, bool bSendsNodeNotifcation) const 429 | { 430 | const FScopedTransaction Transaction(NSLOCTEXT("UnrealEd", "GraphEd_BreakPinLinks", "Break Pin Links")); 431 | 432 | Super::BreakPinLinks(TargetPin, bSendsNodeNotifcation); 433 | } 434 | 435 | void UAssetGraphSchema_GenericGraph::BreakSinglePinLink(UEdGraphPin* SourcePin, UEdGraphPin* TargetPin) const 436 | { 437 | const FScopedTransaction Transaction(NSLOCTEXT("UnrealEd", "GraphEd_BreakSinglePinLink", "Break Pin Link")); 438 | 439 | Super::BreakSinglePinLink(SourcePin, TargetPin); 440 | } 441 | 442 | UEdGraphPin* UAssetGraphSchema_GenericGraph::DropPinOnNode(UEdGraphNode* InTargetNode, const FName& InSourcePinName, const FEdGraphPinType& InSourcePinType, EEdGraphPinDirection InSourcePinDirection) const 443 | { 444 | UEdNode_GenericGraphNode* EdNode = Cast(InTargetNode); 445 | switch (InSourcePinDirection) 446 | { 447 | case EGPD_Input: 448 | return EdNode->GetOutputPin(); 449 | case EGPD_Output: 450 | return EdNode->GetInputPin(); 451 | default: 452 | return nullptr; 453 | } 454 | } 455 | 456 | bool UAssetGraphSchema_GenericGraph::SupportsDropPinOnNode(UEdGraphNode* InTargetNode, const FEdGraphPinType& InSourcePinType, EEdGraphPinDirection InSourcePinDirection, FText& OutErrorMessage) const 457 | { 458 | return Cast(InTargetNode) != nullptr; 459 | } 460 | 461 | bool UAssetGraphSchema_GenericGraph::IsCacheVisualizationOutOfDate(int32 InVisualizationCacheID) const 462 | { 463 | return CurrentCacheRefreshID != InVisualizationCacheID; 464 | } 465 | 466 | int32 UAssetGraphSchema_GenericGraph::GetCurrentVisualizationCacheID() const 467 | { 468 | return CurrentCacheRefreshID; 469 | } 470 | 471 | void UAssetGraphSchema_GenericGraph::ForceVisualizationCacheClear() const 472 | { 473 | ++CurrentCacheRefreshID; 474 | } 475 | 476 | #undef LOCTEXT_NAMESPACE 477 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Private/GenericGraphAssetEditor/ConnectionDrawingPolicy_GenericGraph.cpp: -------------------------------------------------------------------------------- 1 | #include "GenericGraphAssetEditor/ConnectionDrawingPolicy_GenericGraph.h" 2 | #include "GenericGraphAssetEditor/EdNode_GenericGraphNode.h" 3 | #include "GenericGraphAssetEditor/EdNode_GenericGraphEdge.h" 4 | 5 | FConnectionDrawingPolicy_GenericGraph::FConnectionDrawingPolicy_GenericGraph(int32 InBackLayerID, int32 InFrontLayerID, float ZoomFactor, const FSlateRect& InClippingRect, FSlateWindowElementList& InDrawElements, UEdGraph* InGraphObj) 6 | : FConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements) 7 | , GraphObj(InGraphObj) 8 | { 9 | } 10 | 11 | void FConnectionDrawingPolicy_GenericGraph::DetermineWiringStyle(UEdGraphPin* OutputPin, UEdGraphPin* InputPin, /*inout*/ FConnectionParams& Params) 12 | { 13 | Params.AssociatedPin1 = OutputPin; 14 | Params.AssociatedPin2 = InputPin; 15 | Params.WireThickness = 1.5f; 16 | 17 | const bool bDeemphasizeUnhoveredPins = HoveredPins.Num() > 0; 18 | if (bDeemphasizeUnhoveredPins) 19 | { 20 | ApplyHoverDeemphasis(OutputPin, InputPin, /*inout*/ Params.WireThickness, /*inout*/ Params.WireColor); 21 | } 22 | } 23 | 24 | void FConnectionDrawingPolicy_GenericGraph::Draw(TMap, FArrangedWidget>& InPinGeometries, FArrangedChildren& ArrangedNodes) 25 | { 26 | // Build an acceleration structure to quickly find geometry for the nodes 27 | NodeWidgetMap.Empty(); 28 | for (int32 NodeIndex = 0; NodeIndex < ArrangedNodes.Num(); ++NodeIndex) 29 | { 30 | FArrangedWidget& CurWidget = ArrangedNodes[NodeIndex]; 31 | TSharedRef ChildNode = StaticCastSharedRef(CurWidget.Widget); 32 | NodeWidgetMap.Add(ChildNode->GetNodeObj(), NodeIndex); 33 | } 34 | 35 | // Now draw 36 | FConnectionDrawingPolicy::Draw(InPinGeometries, ArrangedNodes); 37 | } 38 | 39 | void FConnectionDrawingPolicy_GenericGraph::DrawPreviewConnector(const FGeometry& PinGeometry, const FVector2D& StartPoint, const FVector2D& EndPoint, UEdGraphPin* Pin) 40 | { 41 | FConnectionParams Params; 42 | DetermineWiringStyle(Pin, nullptr, /*inout*/ Params); 43 | 44 | if (Pin->Direction == EEdGraphPinDirection::EGPD_Output) 45 | { 46 | DrawSplineWithArrow(FGeometryHelper::FindClosestPointOnGeom(PinGeometry, EndPoint), EndPoint, Params); 47 | } 48 | else 49 | { 50 | DrawSplineWithArrow(FGeometryHelper::FindClosestPointOnGeom(PinGeometry, StartPoint), StartPoint, Params); 51 | } 52 | } 53 | 54 | void FConnectionDrawingPolicy_GenericGraph::DrawSplineWithArrow(const FVector2D& StartAnchorPoint, const FVector2D& EndAnchorPoint, const FConnectionParams& Params) 55 | { 56 | // bUserFlag1 indicates that we need to reverse the direction of connection (used by debugger) 57 | const FVector2D& P0 = Params.bUserFlag1 ? EndAnchorPoint : StartAnchorPoint; 58 | const FVector2D& P1 = Params.bUserFlag1 ? StartAnchorPoint : EndAnchorPoint; 59 | 60 | Internal_DrawLineWithArrow(P0, P1, Params); 61 | } 62 | 63 | void FConnectionDrawingPolicy_GenericGraph::Internal_DrawLineWithArrow(const FVector2D& StartAnchorPoint, const FVector2D& EndAnchorPoint, const FConnectionParams& Params) 64 | { 65 | //@TODO: Should this be scaled by zoom factor? 66 | const float LineSeparationAmount = 4.5f; 67 | 68 | const FVector2D DeltaPos = EndAnchorPoint - StartAnchorPoint; 69 | const FVector2D UnitDelta = DeltaPos.GetSafeNormal(); 70 | const FVector2D Normal = FVector2D(DeltaPos.Y, -DeltaPos.X).GetSafeNormal(); 71 | 72 | // Come up with the final start/end points 73 | const FVector2D DirectionBias = Normal * LineSeparationAmount; 74 | const FVector2D LengthBias = ArrowRadius.X * UnitDelta; 75 | const FVector2D StartPoint = StartAnchorPoint + DirectionBias + LengthBias; 76 | const FVector2D EndPoint = EndAnchorPoint + DirectionBias - LengthBias; 77 | 78 | // Draw a line/spline 79 | DrawConnection(WireLayerID, StartPoint, EndPoint, Params); 80 | 81 | // Draw the arrow 82 | const FVector2D ArrowDrawPos = EndPoint - ArrowRadius; 83 | const float AngleInRadians = FMath::Atan2(DeltaPos.Y, DeltaPos.X); 84 | 85 | FSlateDrawElement::MakeRotatedBox( 86 | DrawElementsList, 87 | ArrowLayerID, 88 | FPaintGeometry(ArrowDrawPos, ArrowImage->ImageSize * ZoomFactor, ZoomFactor), 89 | ArrowImage, 90 | ESlateDrawEffect::None, 91 | AngleInRadians, 92 | TOptional(), 93 | FSlateDrawElement::RelativeToElement, 94 | Params.WireColor 95 | ); 96 | } 97 | 98 | void FConnectionDrawingPolicy_GenericGraph::DrawSplineWithArrow(const FGeometry& StartGeom, const FGeometry& EndGeom, const FConnectionParams& Params) 99 | { 100 | // Get a reasonable seed point (halfway between the boxes) 101 | const FVector2D StartCenter = FGeometryHelper::CenterOf(StartGeom); 102 | const FVector2D EndCenter = FGeometryHelper::CenterOf(EndGeom); 103 | const FVector2D SeedPoint = (StartCenter + EndCenter) * 0.5f; 104 | 105 | // Find the (approximate) closest points between the two boxes 106 | const FVector2D StartAnchorPoint = FGeometryHelper::FindClosestPointOnGeom(StartGeom, SeedPoint); 107 | const FVector2D EndAnchorPoint = FGeometryHelper::FindClosestPointOnGeom(EndGeom, SeedPoint); 108 | 109 | DrawSplineWithArrow(StartAnchorPoint, EndAnchorPoint, Params); 110 | } 111 | 112 | FVector2D FConnectionDrawingPolicy_GenericGraph::ComputeSplineTangent(const FVector2D& Start, const FVector2D& End) const 113 | { 114 | const FVector2D Delta = End - Start; 115 | const FVector2D NormDelta = Delta.GetSafeNormal(); 116 | 117 | return NormDelta; 118 | } 119 | 120 | void FConnectionDrawingPolicy_GenericGraph::DetermineLinkGeometry(FArrangedChildren& ArrangedNodes, TSharedRef& OutputPinWidget, 121 | UEdGraphPin* OutputPin, UEdGraphPin* InputPin, FArrangedWidget*& StartWidgetGeometry, FArrangedWidget*& EndWidgetGeometry) 122 | { 123 | if (UEdNode_GenericGraphEdge* EdgeNode = Cast(InputPin->GetOwningNode())) 124 | { 125 | UEdNode_GenericGraphNode* Start = EdgeNode->GetStartNode(); 126 | UEdNode_GenericGraphNode* End = EdgeNode->GetEndNode(); 127 | if (Start != nullptr && End != nullptr) 128 | { 129 | int32* StartNodeIndex = NodeWidgetMap.Find(Start); 130 | int32* EndNodeIndex = NodeWidgetMap.Find(End); 131 | if (StartNodeIndex != nullptr && EndNodeIndex != nullptr) 132 | { 133 | StartWidgetGeometry = &(ArrangedNodes[*StartNodeIndex]); 134 | EndWidgetGeometry = &(ArrangedNodes[*EndNodeIndex]); 135 | } 136 | } 137 | } 138 | else 139 | { 140 | StartWidgetGeometry = PinGeometries->Find(OutputPinWidget); 141 | 142 | if (TSharedPtr* pTargetWidget = PinToPinWidgetMap.Find(InputPin)) 143 | { 144 | TSharedRef InputWidget = (*pTargetWidget).ToSharedRef(); 145 | EndWidgetGeometry = PinGeometries->Find(InputWidget); 146 | } 147 | } 148 | } 149 | 150 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Private/GenericGraphAssetEditor/EdGraph_GenericGraph.cpp: -------------------------------------------------------------------------------- 1 | #include "GenericGraphAssetEditor/EdGraph_GenericGraph.h" 2 | #include "GenericGraphEditorPCH.h" 3 | #include "GenericGraph.h" 4 | #include "GenericGraphAssetEditor/EdNode_GenericGraphNode.h" 5 | #include "GenericGraphAssetEditor/EdNode_GenericGraphEdge.h" 6 | 7 | UEdGraph_GenericGraph::UEdGraph_GenericGraph() 8 | { 9 | 10 | } 11 | 12 | UEdGraph_GenericGraph::~UEdGraph_GenericGraph() 13 | { 14 | 15 | } 16 | 17 | void UEdGraph_GenericGraph::RebuildGenericGraph() 18 | { 19 | LOG_INFO(TEXT("UGenericGraphEdGraph::RebuildGenericGraph has been called")); 20 | 21 | UGenericGraph* Graph = GetGenericGraph(); 22 | 23 | Clear(); 24 | 25 | for (int i = 0; i < Nodes.Num(); ++i) 26 | { 27 | if (UEdNode_GenericGraphNode* EdNode = Cast(Nodes[i])) 28 | { 29 | if (EdNode->GenericGraphNode == nullptr) 30 | continue; 31 | 32 | UGenericGraphNode* GenericGraphNode = EdNode->GenericGraphNode; 33 | 34 | NodeMap.Add(GenericGraphNode, EdNode); 35 | 36 | Graph->AllNodes.Add(GenericGraphNode); 37 | 38 | for (int PinIdx = 0; PinIdx < EdNode->Pins.Num(); ++PinIdx) 39 | { 40 | UEdGraphPin* Pin = EdNode->Pins[PinIdx]; 41 | 42 | if (Pin->Direction != EEdGraphPinDirection::EGPD_Output) 43 | continue; 44 | 45 | for (int LinkToIdx = 0; LinkToIdx < Pin->LinkedTo.Num(); ++LinkToIdx) 46 | { 47 | UGenericGraphNode* ChildNode = nullptr; 48 | if (UEdNode_GenericGraphNode* EdNode_Child = Cast(Pin->LinkedTo[LinkToIdx]->GetOwningNode())) 49 | { 50 | ChildNode = EdNode_Child->GenericGraphNode; 51 | } 52 | else if (UEdNode_GenericGraphEdge* EdNode_Edge = Cast(Pin->LinkedTo[LinkToIdx]->GetOwningNode())) 53 | { 54 | UEdNode_GenericGraphNode* Child = EdNode_Edge->GetEndNode();; 55 | if (Child != nullptr) 56 | { 57 | ChildNode = Child->GenericGraphNode; 58 | } 59 | } 60 | 61 | if (ChildNode != nullptr) 62 | { 63 | GenericGraphNode->ChildrenNodes.Add(ChildNode); 64 | 65 | ChildNode->ParentNodes.Add(GenericGraphNode); 66 | } 67 | else 68 | { 69 | LOG_ERROR(TEXT("UEdGraph_GenericGraph::RebuildGenericGraph can't find child node")); 70 | } 71 | } 72 | } 73 | } 74 | else if (UEdNode_GenericGraphEdge* EdgeNode = Cast(Nodes[i])) 75 | { 76 | UEdNode_GenericGraphNode* StartNode = EdgeNode->GetStartNode(); 77 | UEdNode_GenericGraphNode* EndNode = EdgeNode->GetEndNode(); 78 | UGenericGraphEdge* Edge = EdgeNode->GenericGraphEdge; 79 | 80 | if (StartNode == nullptr || EndNode == nullptr || Edge == nullptr) 81 | { 82 | LOG_ERROR(TEXT("UEdGraph_GenericGraph::RebuildGenericGraph add edge failed.")); 83 | continue; 84 | } 85 | 86 | EdgeMap.Add(Edge, EdgeNode); 87 | 88 | Edge->Graph = Graph; 89 | Edge->Rename(nullptr, Graph, REN_DontCreateRedirectors | REN_DoNotDirty); 90 | Edge->StartNode = StartNode->GenericGraphNode; 91 | Edge->EndNode = EndNode->GenericGraphNode; 92 | Edge->StartNode->Edges.Add(Edge->EndNode, Edge); 93 | } 94 | } 95 | 96 | for (int i = 0; i < Graph->AllNodes.Num(); ++i) 97 | { 98 | UGenericGraphNode* Node = Graph->AllNodes[i]; 99 | if (Node->ParentNodes.Num() == 0) 100 | { 101 | Graph->RootNodes.Add(Node); 102 | 103 | SortNodes(Node); 104 | } 105 | 106 | Node->Graph = Graph; 107 | Node->Rename(nullptr, Graph, REN_DontCreateRedirectors | REN_DoNotDirty); 108 | } 109 | 110 | Graph->RootNodes.Sort([&](const UGenericGraphNode& L, const UGenericGraphNode& R) 111 | { 112 | UEdNode_GenericGraphNode* EdNode_LNode = NodeMap[&L]; 113 | UEdNode_GenericGraphNode* EdNode_RNode = NodeMap[&R]; 114 | return EdNode_LNode->NodePosX < EdNode_RNode->NodePosX; 115 | }); 116 | } 117 | 118 | UGenericGraph* UEdGraph_GenericGraph::GetGenericGraph() const 119 | { 120 | return CastChecked(GetOuter()); 121 | } 122 | 123 | bool UEdGraph_GenericGraph::Modify(bool bAlwaysMarkDirty /*= true*/) 124 | { 125 | bool Rtn = Super::Modify(bAlwaysMarkDirty); 126 | 127 | GetGenericGraph()->Modify(); 128 | 129 | for (int32 i = 0; i < Nodes.Num(); ++i) 130 | { 131 | Nodes[i]->Modify(); 132 | } 133 | 134 | return Rtn; 135 | } 136 | 137 | void UEdGraph_GenericGraph::Clear() 138 | { 139 | UGenericGraph* Graph = GetGenericGraph(); 140 | 141 | Graph->ClearGraph(); 142 | NodeMap.Reset(); 143 | EdgeMap.Reset(); 144 | 145 | for (int i = 0; i < Nodes.Num(); ++i) 146 | { 147 | if (UEdNode_GenericGraphNode* EdNode = Cast(Nodes[i])) 148 | { 149 | UGenericGraphNode* GenericGraphNode = EdNode->GenericGraphNode; 150 | if (GenericGraphNode) 151 | { 152 | GenericGraphNode->ParentNodes.Reset(); 153 | GenericGraphNode->ChildrenNodes.Reset(); 154 | GenericGraphNode->Edges.Reset(); 155 | } 156 | } 157 | } 158 | } 159 | 160 | void UEdGraph_GenericGraph::SortNodes(UGenericGraphNode* RootNode) 161 | { 162 | int Level = 0; 163 | TArray CurrLevelNodes = { RootNode }; 164 | TArray NextLevelNodes; 165 | TSet Visited; 166 | 167 | while (CurrLevelNodes.Num() != 0) 168 | { 169 | int32 LevelWidth = 0; 170 | for (int i = 0; i < CurrLevelNodes.Num(); ++i) 171 | { 172 | UGenericGraphNode* Node = CurrLevelNodes[i]; 173 | Visited.Add(Node); 174 | 175 | auto Comp = [&](const UGenericGraphNode& L, const UGenericGraphNode& R) 176 | { 177 | UEdNode_GenericGraphNode* EdNode_LNode = NodeMap[&L]; 178 | UEdNode_GenericGraphNode* EdNode_RNode = NodeMap[&R]; 179 | return EdNode_LNode->NodePosX < EdNode_RNode->NodePosX; 180 | }; 181 | 182 | Node->ChildrenNodes.Sort(Comp); 183 | Node->ParentNodes.Sort(Comp); 184 | 185 | for (int j = 0; j < Node->ChildrenNodes.Num(); ++j) 186 | { 187 | UGenericGraphNode* ChildNode = Node->ChildrenNodes[j]; 188 | if(!Visited.Contains(ChildNode)) 189 | NextLevelNodes.Add(Node->ChildrenNodes[j]); 190 | } 191 | } 192 | 193 | CurrLevelNodes = NextLevelNodes; 194 | NextLevelNodes.Reset(); 195 | ++Level; 196 | } 197 | } 198 | 199 | void UEdGraph_GenericGraph::PostEditUndo() 200 | { 201 | Super::PostEditUndo(); 202 | 203 | NotifyGraphChanged(); 204 | } 205 | 206 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Private/GenericGraphAssetEditor/EdNode_GenericGraphEdge.cpp: -------------------------------------------------------------------------------- 1 | #include "GenericGraphAssetEditor/EdNode_GenericGraphEdge.h" 2 | #include "GenericGraphEdge.h" 3 | #include "GenericGraphAssetEditor/EdNode_GenericGraphNode.h" 4 | 5 | #define LOCTEXT_NAMESPACE "EdNode_GenericGraphEdge" 6 | 7 | UEdNode_GenericGraphEdge::UEdNode_GenericGraphEdge() 8 | { 9 | bCanRenameNode = true; 10 | } 11 | 12 | void UEdNode_GenericGraphEdge::SetEdge(UGenericGraphEdge* Edge) 13 | { 14 | GenericGraphEdge = Edge; 15 | } 16 | 17 | void UEdNode_GenericGraphEdge::AllocateDefaultPins() 18 | { 19 | UEdGraphPin* Inputs = CreatePin(EGPD_Input, TEXT("Edge"), FName(), TEXT("In")); 20 | Inputs->bHidden = true; 21 | UEdGraphPin* Outputs = CreatePin(EGPD_Output, TEXT("Edge"), FName(), TEXT("Out")); 22 | Outputs->bHidden = true; 23 | } 24 | 25 | FText UEdNode_GenericGraphEdge::GetNodeTitle(ENodeTitleType::Type TitleType) const 26 | { 27 | if (GenericGraphEdge) 28 | { 29 | return GenericGraphEdge->GetNodeTitle(); 30 | } 31 | return FText(); 32 | } 33 | 34 | void UEdNode_GenericGraphEdge::PinConnectionListChanged(UEdGraphPin* Pin) 35 | { 36 | if (Pin->LinkedTo.Num() == 0) 37 | { 38 | // Commit suicide; transitions must always have an input and output connection 39 | Modify(); 40 | 41 | // Our parent graph will have our graph in SubGraphs so needs to be modified to record that. 42 | if (UEdGraph* ParentGraph = GetGraph()) 43 | { 44 | ParentGraph->Modify(); 45 | } 46 | 47 | DestroyNode(); 48 | } 49 | } 50 | 51 | void UEdNode_GenericGraphEdge::PrepareForCopying() 52 | { 53 | GenericGraphEdge->Rename(nullptr, this, REN_DontCreateRedirectors | REN_DoNotDirty); 54 | } 55 | 56 | void UEdNode_GenericGraphEdge::CreateConnections(UEdNode_GenericGraphNode* Start, UEdNode_GenericGraphNode* End) 57 | { 58 | Pins[0]->Modify(); 59 | Pins[0]->LinkedTo.Empty(); 60 | 61 | Start->GetOutputPin()->Modify(); 62 | Pins[0]->MakeLinkTo(Start->GetOutputPin()); 63 | 64 | // This to next 65 | Pins[1]->Modify(); 66 | Pins[1]->LinkedTo.Empty(); 67 | 68 | End->GetInputPin()->Modify(); 69 | Pins[1]->MakeLinkTo(End->GetInputPin()); 70 | } 71 | 72 | UEdNode_GenericGraphNode* UEdNode_GenericGraphEdge::GetStartNode() 73 | { 74 | if (Pins[0]->LinkedTo.Num() > 0) 75 | { 76 | return Cast(Pins[0]->LinkedTo[0]->GetOwningNode()); 77 | } 78 | else 79 | { 80 | return nullptr; 81 | } 82 | } 83 | 84 | UEdNode_GenericGraphNode* UEdNode_GenericGraphEdge::GetEndNode() 85 | { 86 | if (Pins[1]->LinkedTo.Num() > 0) 87 | { 88 | return Cast(Pins[1]->LinkedTo[0]->GetOwningNode()); 89 | } 90 | else 91 | { 92 | return nullptr; 93 | } 94 | } 95 | 96 | #undef LOCTEXT_NAMESPACE 97 | 98 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Private/GenericGraphAssetEditor/EdNode_GenericGraphNode.cpp: -------------------------------------------------------------------------------- 1 | #include "GenericGraphAssetEditor/EdNode_GenericGraphNode.h" 2 | #include "GenericGraphAssetEditor/EdGraph_GenericGraph.h" 3 | #include "Kismet2/Kismet2NameValidators.h" 4 | #include "Kismet2/BlueprintEditorUtils.h" 5 | 6 | #define LOCTEXT_NAMESPACE "EdNode_GenericGraph" 7 | 8 | UEdNode_GenericGraphNode::UEdNode_GenericGraphNode() 9 | { 10 | bCanRenameNode = true; 11 | } 12 | 13 | UEdNode_GenericGraphNode::~UEdNode_GenericGraphNode() 14 | { 15 | 16 | } 17 | 18 | void UEdNode_GenericGraphNode::AllocateDefaultPins() 19 | { 20 | CreatePin(EGPD_Input, "MultipleNodes", FName(), TEXT("In")); 21 | CreatePin(EGPD_Output, "MultipleNodes", FName(), TEXT("Out")); 22 | } 23 | 24 | UEdGraph_GenericGraph* UEdNode_GenericGraphNode::GetGenericGraphEdGraph() 25 | { 26 | return Cast(GetGraph()); 27 | } 28 | 29 | FText UEdNode_GenericGraphNode::GetNodeTitle(ENodeTitleType::Type TitleType) const 30 | { 31 | if (GenericGraphNode == nullptr) 32 | { 33 | return Super::GetNodeTitle(TitleType); 34 | } 35 | else 36 | { 37 | return GenericGraphNode->GetNodeTitle(); 38 | } 39 | } 40 | 41 | void UEdNode_GenericGraphNode::PrepareForCopying() 42 | { 43 | GenericGraphNode->Rename(nullptr, this, REN_DontCreateRedirectors | REN_DoNotDirty); 44 | } 45 | 46 | void UEdNode_GenericGraphNode::AutowireNewNode(UEdGraphPin* FromPin) 47 | { 48 | Super::AutowireNewNode(FromPin); 49 | 50 | if (FromPin != nullptr) 51 | { 52 | if (GetSchema()->TryCreateConnection(FromPin, GetInputPin())) 53 | { 54 | FromPin->GetOwningNode()->NodeConnectionListChanged(); 55 | } 56 | } 57 | } 58 | 59 | void UEdNode_GenericGraphNode::SetGenericGraphNode(UGenericGraphNode* InNode) 60 | { 61 | GenericGraphNode = InNode; 62 | } 63 | 64 | FLinearColor UEdNode_GenericGraphNode::GetBackgroundColor() const 65 | { 66 | return GenericGraphNode == nullptr ? FLinearColor::Black : GenericGraphNode->GetBackgroundColor(); 67 | } 68 | 69 | UEdGraphPin* UEdNode_GenericGraphNode::GetInputPin() const 70 | { 71 | return Pins[0]; 72 | } 73 | 74 | UEdGraphPin* UEdNode_GenericGraphNode::GetOutputPin() const 75 | { 76 | return Pins[1]; 77 | } 78 | 79 | void UEdNode_GenericGraphNode::PostEditUndo() 80 | { 81 | UEdGraphNode::PostEditUndo(); 82 | } 83 | 84 | #undef LOCTEXT_NAMESPACE 85 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Private/GenericGraphAssetEditor/EditorCommands_GenericGraph.cpp: -------------------------------------------------------------------------------- 1 | #include "GenericGraphAssetEditor/EditorCommands_GenericGraph.h" 2 | 3 | #define LOCTEXT_NAMESPACE "EditorCommands_GenericGraph" 4 | 5 | void FEditorCommands_GenericGraph::RegisterCommands() 6 | { 7 | UI_COMMAND(GraphSettings, "Graph Settings", "Graph Settings", EUserInterfaceActionType::Button, FInputChord()); 8 | UI_COMMAND(AutoArrange, "Auto Arrange", "Auto Arrange", EUserInterfaceActionType::Button, FInputChord()); 9 | } 10 | 11 | #undef LOCTEXT_NAMESPACE 12 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Private/GenericGraphAssetEditor/GenericGraphDragConnection.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | 4 | #include "GenericGraphAssetEditor/GenericGraphDragConnection.h" 5 | #include "Widgets/SBoxPanel.h" 6 | #include "Framework/Application/SlateApplication.h" 7 | #include "Widgets/Images/SImage.h" 8 | #include "EdGraph/EdGraph.h" 9 | #include "SGraphPanel.h" 10 | #include "ScopedTransaction.h" 11 | #include "GenericGraphAssetEditor/EdNode_GenericGraphNode.h" 12 | 13 | TSharedRef FGenericGraphDragConnection::New(const TSharedRef& GraphPanel, const FDraggedPinTable& DraggedPins) 14 | { 15 | TSharedRef Operation = MakeShareable(new FGenericGraphDragConnection(GraphPanel, DraggedPins)); 16 | Operation->Construct(); 17 | 18 | return Operation; 19 | } 20 | 21 | void FGenericGraphDragConnection::OnDrop(bool bDropWasHandled, const FPointerEvent& MouseEvent) 22 | { 23 | GraphPanel->OnStopMakingConnection(); 24 | 25 | Super::OnDrop(bDropWasHandled, MouseEvent); 26 | } 27 | 28 | void FGenericGraphDragConnection::OnDragged(const class FDragDropEvent& DragDropEvent) 29 | { 30 | FVector2D TargetPosition = DragDropEvent.GetScreenSpacePosition(); 31 | 32 | // Reposition the info window wrt to the drag 33 | CursorDecoratorWindow->MoveWindowTo(DragDropEvent.GetScreenSpacePosition() + DecoratorAdjust); 34 | // Request the active panel to scroll if required 35 | GraphPanel->RequestDeferredPan(TargetPosition); 36 | } 37 | 38 | void FGenericGraphDragConnection::HoverTargetChanged() 39 | { 40 | TArray UniqueMessages; 41 | 42 | if (UEdGraphPin* TargetPinObj = GetHoveredPin()) 43 | { 44 | TArray ValidSourcePins; 45 | ValidateGraphPinList(/*out*/ ValidSourcePins); 46 | 47 | // Check the schema for connection responses 48 | for (UEdGraphPin* StartingPinObj : ValidSourcePins) 49 | { 50 | // The Graph object in which the pins reside. 51 | UEdGraph* GraphObj = StartingPinObj->GetOwningNode()->GetGraph(); 52 | 53 | // Determine what the schema thinks about the wiring action 54 | const FPinConnectionResponse Response = GraphObj->GetSchema()->CanCreateConnection(StartingPinObj, TargetPinObj); 55 | 56 | if (Response.Response == ECanCreateConnectionResponse::CONNECT_RESPONSE_DISALLOW) 57 | { 58 | TSharedPtr NodeWidget = TargetPinObj->GetOwningNode()->DEPRECATED_NodeWidget.Pin(); 59 | if (NodeWidget.IsValid()) 60 | { 61 | NodeWidget->NotifyDisallowedPinConnection(StartingPinObj, TargetPinObj); 62 | } 63 | } 64 | 65 | UniqueMessages.AddUnique(Response); 66 | } 67 | } 68 | else if (UEdNode_GenericGraphNode* TargetNodeObj = Cast(GetHoveredNode())) 69 | { 70 | TArray ValidSourcePins; 71 | ValidateGraphPinList(/*out*/ ValidSourcePins); 72 | 73 | // Check the schema for connection responses 74 | for (UEdGraphPin* StartingPinObj : ValidSourcePins) 75 | { 76 | FPinConnectionResponse Response; 77 | FText ResponseText; 78 | 79 | const UEdGraphSchema *Schema = StartingPinObj->GetSchema(); 80 | UEdGraphPin *TargetPin = TargetNodeObj->GetInputPin(); 81 | 82 | if (Schema && TargetPin) 83 | { 84 | Response = Schema->CanCreateConnection(StartingPinObj, TargetPin); 85 | if (Response.Response == ECanCreateConnectionResponse::CONNECT_RESPONSE_DISALLOW) 86 | { 87 | TSharedPtr NodeWidget = TargetPin->GetOwningNode()->DEPRECATED_NodeWidget.Pin(); 88 | if (NodeWidget.IsValid()) 89 | { 90 | NodeWidget->NotifyDisallowedPinConnection(StartingPinObj, TargetPinObj); 91 | } 92 | } 93 | } 94 | else 95 | { 96 | #define LOCTEXT_NAMESPACE "AssetSchema_GenericGraph" 97 | Response = FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("PinError", "Not a valid UGenericGraphEdNode")); 98 | #undef LOCTEXT_NAMESPACE 99 | } 100 | 101 | UniqueMessages.AddUnique(Response); 102 | } 103 | } 104 | else if (UEdGraph* CurrentHoveredGraph = GetHoveredGraph()) 105 | { 106 | TArray ValidSourcePins; 107 | ValidateGraphPinList(/*out*/ ValidSourcePins); 108 | 109 | for (UEdGraphPin* StartingPinObj : ValidSourcePins) 110 | { 111 | // Let the schema describe the connection we might make 112 | FPinConnectionResponse Response = CurrentHoveredGraph->GetSchema()->CanCreateNewNodes(StartingPinObj); 113 | if (!Response.Message.IsEmpty()) 114 | { 115 | UniqueMessages.AddUnique(Response); 116 | } 117 | } 118 | } 119 | 120 | // Let the user know the status of dropping now 121 | if (UniqueMessages.Num() == 0) 122 | { 123 | // Display the place a new node icon, we're not over a valid pin and have no message from the schema 124 | SetSimpleFeedbackMessage( 125 | FAppStyle::GetBrush(TEXT("Graph.ConnectorFeedback.NewNode")), 126 | FLinearColor::White, 127 | NSLOCTEXT("GraphEditor.Feedback", "PlaceNewNode", "Place a new node.")); 128 | } 129 | else 130 | { 131 | // Take the unique responses and create visual feedback for it 132 | TSharedRef FeedbackBox = SNew(SVerticalBox); 133 | for (auto ResponseIt = UniqueMessages.CreateConstIterator(); ResponseIt; ++ResponseIt) 134 | { 135 | // Determine the icon 136 | const FSlateBrush* StatusSymbol = NULL; 137 | 138 | switch (ResponseIt->Response) 139 | { 140 | case CONNECT_RESPONSE_MAKE: 141 | case CONNECT_RESPONSE_BREAK_OTHERS_A: 142 | case CONNECT_RESPONSE_BREAK_OTHERS_B: 143 | case CONNECT_RESPONSE_BREAK_OTHERS_AB: 144 | StatusSymbol = FAppStyle::GetBrush(TEXT("Graph.ConnectorFeedback.OK")); 145 | break; 146 | 147 | case CONNECT_RESPONSE_MAKE_WITH_CONVERSION_NODE: 148 | StatusSymbol = FAppStyle::GetBrush(TEXT("Graph.ConnectorFeedback.ViaCast")); 149 | break; 150 | 151 | case CONNECT_RESPONSE_DISALLOW: 152 | default: 153 | StatusSymbol = FAppStyle::GetBrush(TEXT("Graph.ConnectorFeedback.Error")); 154 | break; 155 | } 156 | 157 | // Add a new message row 158 | FeedbackBox->AddSlot() 159 | .AutoHeight() 160 | [ 161 | SNew(SHorizontalBox) 162 | + SHorizontalBox::Slot() 163 | .AutoWidth() 164 | .Padding(3.0f) 165 | .VAlign(VAlign_Center) 166 | [ 167 | SNew(SImage).Image(StatusSymbol) 168 | ] 169 | + SHorizontalBox::Slot() 170 | .AutoWidth() 171 | .VAlign(VAlign_Center) 172 | [ 173 | SNew(STextBlock).Text(ResponseIt->Message) 174 | ] 175 | ]; 176 | } 177 | 178 | SetFeedbackMessage(FeedbackBox); 179 | } 180 | } 181 | 182 | FGenericGraphDragConnection::FGenericGraphDragConnection(const TSharedRef& GraphPanelIn, const FDraggedPinTable& DraggedPinsIn) 183 | : GraphPanel(GraphPanelIn) 184 | , DraggingPins(DraggedPinsIn) 185 | , DecoratorAdjust(FSlateApplication::Get().GetCursorSize()) 186 | { 187 | if (DraggingPins.Num() > 0) 188 | { 189 | const UEdGraphPin* PinObj = FDraggedPinTable::TConstIterator(DraggedPinsIn)->GetPinObj(*GraphPanelIn); 190 | if (PinObj && PinObj->Direction == EGPD_Input) 191 | { 192 | DecoratorAdjust *= FVector2D(-1.0f, 1.0f); 193 | } 194 | } 195 | 196 | for (const FGraphPinHandle& DraggedPin : DraggedPinsIn) 197 | { 198 | GraphPanelIn->OnBeginMakingConnection(DraggedPin); 199 | } 200 | } 201 | 202 | FReply FGenericGraphDragConnection::DroppedOnPin(FVector2D ScreenPosition, FVector2D GraphPosition) 203 | { 204 | TArray ValidSourcePins; 205 | ValidateGraphPinList(/*out*/ ValidSourcePins); 206 | 207 | const FScopedTransaction Transaction(NSLOCTEXT("UnrealEd", "GraphEd_CreateConnection", "Create Pin Link")); 208 | 209 | UEdGraphPin* PinB = GetHoveredPin(); 210 | bool bError = false; 211 | TSet NodeList; 212 | 213 | for (UEdGraphPin* PinA : ValidSourcePins) 214 | { 215 | if ((PinA != NULL) && (PinB != NULL)) 216 | { 217 | UEdGraph* MyGraphObj = PinA->GetOwningNode()->GetGraph(); 218 | 219 | if (MyGraphObj->GetSchema()->TryCreateConnection(PinA, PinB)) 220 | { 221 | if (!PinA->IsPendingKill()) 222 | { 223 | NodeList.Add(PinA->GetOwningNode()); 224 | } 225 | if (!PinB->IsPendingKill()) 226 | { 227 | NodeList.Add(PinB->GetOwningNode()); 228 | } 229 | } 230 | } 231 | else 232 | { 233 | bError = true; 234 | } 235 | } 236 | 237 | // Send all nodes that received a new pin connection a notification 238 | for (auto It = NodeList.CreateConstIterator(); It; ++It) 239 | { 240 | UEdGraphNode* Node = (*It); 241 | Node->NodeConnectionListChanged(); 242 | } 243 | 244 | if (bError) 245 | { 246 | return FReply::Unhandled(); 247 | } 248 | 249 | return FReply::Handled(); 250 | } 251 | 252 | FReply FGenericGraphDragConnection::DroppedOnNode(FVector2D ScreenPosition, FVector2D GraphPosition) 253 | { 254 | bool bHandledPinDropOnNode = false; 255 | UEdGraphNode* NodeOver = GetHoveredNode(); 256 | 257 | if (NodeOver) 258 | { 259 | // Gather any source drag pins 260 | TArray ValidSourcePins; 261 | ValidateGraphPinList(/*out*/ ValidSourcePins); 262 | 263 | if (ValidSourcePins.Num()) 264 | { 265 | for (UEdGraphPin* SourcePin : ValidSourcePins) 266 | { 267 | // Check for pin drop support 268 | FText ResponseText; 269 | if (SourcePin->GetOwningNode() != NodeOver && SourcePin->GetSchema()->SupportsDropPinOnNode(NodeOver, SourcePin->PinType, SourcePin->Direction, ResponseText)) 270 | { 271 | bHandledPinDropOnNode = true; 272 | 273 | // Find which pin name to use and drop the pin on the node 274 | const FName PinName = SourcePin->PinFriendlyName.IsEmpty() ? SourcePin->PinName : *SourcePin->PinFriendlyName.ToString(); 275 | 276 | const FScopedTransaction Transaction((SourcePin->Direction == EGPD_Output) ? NSLOCTEXT("UnrealEd", "AddInParam", "Add In Parameter") : NSLOCTEXT("UnrealEd", "AddOutParam", "Add Out Parameter")); 277 | 278 | UEdGraphPin* EdGraphPin = NodeOver->GetSchema()->DropPinOnNode(GetHoveredNode(), PinName, SourcePin->PinType, SourcePin->Direction); 279 | 280 | // This can invalidate the source pin due to node reconstruction, abort in that case 281 | if (SourcePin->GetOwningNodeUnchecked() && EdGraphPin) 282 | { 283 | SourcePin->Modify(); 284 | EdGraphPin->Modify(); 285 | SourcePin->GetSchema()->TryCreateConnection(SourcePin, EdGraphPin); 286 | } 287 | } 288 | 289 | // If we have not handled the pin drop on node and there is an error message, do not let other actions occur. 290 | if (!bHandledPinDropOnNode && !ResponseText.IsEmpty()) 291 | { 292 | bHandledPinDropOnNode = true; 293 | } 294 | } 295 | } 296 | } 297 | return bHandledPinDropOnNode ? FReply::Handled() : FReply::Unhandled(); 298 | } 299 | 300 | FReply FGenericGraphDragConnection::DroppedOnPanel(const TSharedRef< SWidget >& Panel, FVector2D ScreenPosition, FVector2D GraphPosition, UEdGraph& Graph) 301 | { 302 | // Gather any source drag pins 303 | TArray PinObjects; 304 | ValidateGraphPinList(/*out*/ PinObjects); 305 | 306 | // Create a context menu 307 | TSharedPtr WidgetToFocus = GraphPanel->SummonContextMenu(ScreenPosition, GraphPosition, NULL, NULL, PinObjects); 308 | 309 | // Give the context menu focus 310 | return (WidgetToFocus.IsValid()) 311 | ? FReply::Handled().SetUserFocus(WidgetToFocus.ToSharedRef(), EFocusCause::SetDirectly) 312 | : FReply::Handled(); 313 | } 314 | 315 | 316 | void FGenericGraphDragConnection::ValidateGraphPinList(TArray& OutValidPins) 317 | { 318 | OutValidPins.Empty(DraggingPins.Num()); 319 | for (const FGraphPinHandle& PinHandle : DraggingPins) 320 | { 321 | if (UEdGraphPin* GraphPin = PinHandle.GetPinObj(*GraphPanel)) 322 | { 323 | OutValidPins.Add(GraphPin); 324 | } 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Private/GenericGraphAssetEditor/GenericGraphEditorStyle.cpp: -------------------------------------------------------------------------------- 1 | #include "GenericGraphAssetEditor/GenericGraphEditorStyle.h" 2 | 3 | #include "Interfaces/IPluginManager.h" 4 | #include "Styling/SlateStyleRegistry.h" 5 | #include "Styling/SlateTypes.h" 6 | #include "Misc/Paths.h" 7 | 8 | TSharedPtr FGenericGraphEditorStyle::StyleSet = nullptr; 9 | 10 | #define IMAGE_BRUSH( RelativePath, ... ) FSlateImageBrush( StyleSet->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ ) 11 | #define BOX_BRUSH( RelativePath, ... ) FSlateBoxBrush( StyleSet->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ ) 12 | #define BORDER_BRUSH( RelativePath, ... ) FSlateBorderBrush( StyleSet->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ ) 13 | #define TTF_FONT( RelativePath, ... ) FSlateFontInfo( StyleSet->RootToContentDir( RelativePath, TEXT(".ttf") ), __VA_ARGS__ ) 14 | #define OTF_FONT( RelativePath, ... ) FSlateFontInfo( StyleSet->RootToContentDir( RelativePath, TEXT(".otf") ), __VA_ARGS__ ) 15 | 16 | void FGenericGraphEditorStyle::Initialize() 17 | { 18 | const FVector2D Icon20x20(20.0f, 20.0f); 19 | const FVector2D Icon40x40(40.0f, 40.0f); 20 | const FVector2D Icon64x64(64.0f, 64.0f); 21 | 22 | if (StyleSet.IsValid()) 23 | { 24 | return; 25 | } 26 | 27 | StyleSet = MakeShareable(new FSlateStyleSet("GenericGraphEditorStyle")); 28 | 29 | StyleSet->SetContentRoot(IPluginManager::Get().FindPlugin("GenericGraph")->GetBaseDir() / TEXT("Resources")); 30 | 31 | StyleSet->Set("GenericGraphEditor.AutoArrange", new IMAGE_BRUSH("AutoArrangeIcon", Icon40x40)); 32 | StyleSet->Set("GenericGraphEditor.AutoArrange.Small", new IMAGE_BRUSH( "AutoArrangeIcon", Icon20x20 ) ); 33 | 34 | FSlateStyleRegistry::RegisterSlateStyle(*StyleSet.Get()); 35 | } 36 | 37 | void FGenericGraphEditorStyle::Shutdown() 38 | { 39 | if (StyleSet.IsValid()) 40 | { 41 | FSlateStyleRegistry::UnRegisterSlateStyle(*StyleSet.Get()); 42 | ensure(StyleSet.IsUnique()); 43 | StyleSet.Reset(); 44 | } 45 | } 46 | 47 | const FName& FGenericGraphEditorStyle::GetStyleSetName() 48 | { 49 | return StyleSet->GetStyleSetName(); 50 | } 51 | 52 | #undef IMAGE_BRUSH 53 | #undef BOX_BRUSH 54 | #undef BORDER_BRUSH 55 | #undef TTF_FONT 56 | #undef OTF_FONT 57 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Private/GenericGraphAssetEditor/SEdNode_GenericGraphEdge.cpp: -------------------------------------------------------------------------------- 1 | #include "GenericGraphAssetEditor/SEdNode_GenericGraphEdge.h" 2 | #include "Widgets/SBoxPanel.h" 3 | #include "Widgets/Images/SImage.h" 4 | #include "Widgets/Text/SInlineEditableTextBlock.h" 5 | #include "Widgets/SToolTip.h" 6 | #include "SGraphPanel.h" 7 | #include "EdGraphSchema_K2.h" 8 | #include "GenericGraphAssetEditor/EdNode_GenericGraphNode.h" 9 | #include "GenericGraphAssetEditor/EdNode_GenericGraphEdge.h" 10 | #include "GenericGraphAssetEditor/ConnectionDrawingPolicy_GenericGraph.h" 11 | #include "GenericGraphEdge.h" 12 | 13 | #define LOCTEXT_NAMESPACE "SGenericGraphEdge" 14 | 15 | void SEdNode_GenericGraphEdge::Construct(const FArguments& InArgs, UEdNode_GenericGraphEdge* InNode) 16 | { 17 | this->GraphNode = InNode; 18 | this->UpdateGraphNode(); 19 | } 20 | 21 | bool SEdNode_GenericGraphEdge::RequiresSecondPassLayout() const 22 | { 23 | return true; 24 | } 25 | 26 | void SEdNode_GenericGraphEdge::PerformSecondPassLayout(const TMap< UObject*, TSharedRef >& NodeToWidgetLookup) const 27 | { 28 | UEdNode_GenericGraphEdge* EdgeNode = CastChecked(GraphNode); 29 | 30 | FGeometry StartGeom; 31 | FGeometry EndGeom; 32 | 33 | UEdNode_GenericGraphNode* Start = EdgeNode->GetStartNode(); 34 | UEdNode_GenericGraphNode* End = EdgeNode->GetEndNode(); 35 | if (Start != nullptr && End != nullptr) 36 | { 37 | const TSharedRef* pFromWidget = NodeToWidgetLookup.Find(Start); 38 | const TSharedRef* pToWidget = NodeToWidgetLookup.Find(End); 39 | if (pFromWidget != nullptr && pToWidget != nullptr) 40 | { 41 | const TSharedRef& FromWidget = *pFromWidget; 42 | const TSharedRef& ToWidget = *pToWidget; 43 | 44 | StartGeom = FGeometry(FVector2D(Start->NodePosX, Start->NodePosY), FVector2D::ZeroVector, FromWidget->GetDesiredSize(), 1.0f); 45 | EndGeom = FGeometry(FVector2D(End->NodePosX, End->NodePosY), FVector2D::ZeroVector, ToWidget->GetDesiredSize(), 1.0f); 46 | } 47 | } 48 | 49 | PositionBetweenTwoNodesWithOffset(StartGeom, EndGeom, 0, 1); 50 | } 51 | 52 | void SEdNode_GenericGraphEdge::OnNameTextCommited(const FText& InText, ETextCommit::Type CommitInfo) 53 | { 54 | SGraphNode::OnNameTextCommited(InText, CommitInfo); 55 | 56 | UEdNode_GenericGraphEdge* MyNode = CastChecked(GraphNode); 57 | 58 | if (MyNode != nullptr && MyNode->GenericGraphEdge != nullptr) 59 | { 60 | const FScopedTransaction Transaction(LOCTEXT("GenericGraphEditorRenameEdge", "Generic Graph Editor: Rename Edge")); 61 | MyNode->Modify(); 62 | MyNode->GenericGraphEdge->SetNodeTitle(InText); 63 | UpdateGraphNode(); 64 | } 65 | } 66 | 67 | void SEdNode_GenericGraphEdge::UpdateGraphNode() 68 | { 69 | InputPins.Empty(); 70 | OutputPins.Empty(); 71 | 72 | RightNodeBox.Reset(); 73 | LeftNodeBox.Reset(); 74 | 75 | TSharedPtr NodeTitle = SNew(SNodeTitle, GraphNode); 76 | 77 | this->ContentScale.Bind( this, &SGraphNode::GetContentScale ); 78 | this->GetOrAddSlot( ENodeZone::Center ) 79 | .HAlign(HAlign_Center) 80 | .VAlign(VAlign_Center) 81 | [ 82 | SNew(SOverlay) 83 | + SOverlay::Slot() 84 | [ 85 | SNew(SImage) 86 | .Image(FAppStyle::GetBrush("Graph.TransitionNode.ColorSpill")) 87 | .ColorAndOpacity(this, &SEdNode_GenericGraphEdge::GetEdgeColor) 88 | ] 89 | + SOverlay::Slot() 90 | [ 91 | SNew(SImage) 92 | .Image(this, &SEdNode_GenericGraphEdge::GetEdgeImage) 93 | .Visibility(this, &SEdNode_GenericGraphEdge::GetEdgeImageVisibility) 94 | ] 95 | 96 | + SOverlay::Slot() 97 | .Padding(FMargin(4.0f, 4.0f, 4.0f, 4.0f)) 98 | [ 99 | SNew(SVerticalBox) 100 | + SVerticalBox::Slot() 101 | .HAlign(HAlign_Center) 102 | .AutoHeight() 103 | [ 104 | SAssignNew(InlineEditableText, SInlineEditableTextBlock) 105 | .ColorAndOpacity(FLinearColor::Black) 106 | .Visibility(this, &SEdNode_GenericGraphEdge::GetEdgeTitleVisbility) 107 | .Font(FCoreStyle::GetDefaultFontStyle("Regular", 12)) 108 | .Text(NodeTitle.Get(), &SNodeTitle::GetHeadTitle) 109 | .OnTextCommitted(this, &SEdNode_GenericGraphEdge::OnNameTextCommited) 110 | ] 111 | + SVerticalBox::Slot() 112 | .AutoHeight() 113 | [ 114 | NodeTitle.ToSharedRef() 115 | ] 116 | 117 | ] 118 | ]; 119 | } 120 | 121 | void SEdNode_GenericGraphEdge::PositionBetweenTwoNodesWithOffset(const FGeometry& StartGeom, const FGeometry& EndGeom, int32 NodeIndex, int32 MaxNodes) const 122 | { 123 | // Get a reasonable seed point (halfway between the boxes) 124 | const FVector2D StartCenter = FGeometryHelper::CenterOf(StartGeom); 125 | const FVector2D EndCenter = FGeometryHelper::CenterOf(EndGeom); 126 | const FVector2D SeedPoint = (StartCenter + EndCenter) * 0.5f; 127 | 128 | // Find the (approximate) closest points between the two boxes 129 | const FVector2D StartAnchorPoint = FGeometryHelper::FindClosestPointOnGeom(StartGeom, SeedPoint); 130 | const FVector2D EndAnchorPoint = FGeometryHelper::FindClosestPointOnGeom(EndGeom, SeedPoint); 131 | 132 | // Position ourselves halfway along the connecting line between the nodes, elevated away perpendicular to the direction of the line 133 | const float Height = 30.0f; 134 | 135 | const FVector2D DesiredNodeSize = GetDesiredSize(); 136 | 137 | FVector2D DeltaPos(EndAnchorPoint - StartAnchorPoint); 138 | 139 | if (DeltaPos.IsNearlyZero()) 140 | { 141 | DeltaPos = FVector2D(10.0f, 0.0f); 142 | } 143 | 144 | const FVector2D Normal = FVector2D(DeltaPos.Y, -DeltaPos.X).GetSafeNormal(); 145 | 146 | const FVector2D NewCenter = StartAnchorPoint + (0.5f * DeltaPos) + (Height * Normal); 147 | 148 | FVector2D DeltaNormal = DeltaPos.GetSafeNormal(); 149 | 150 | // Calculate node offset in the case of multiple transitions between the same two nodes 151 | // MultiNodeOffset: the offset where 0 is the centre of the transition, -1 is 1 152 | // towards the PrevStateNode and +1 is 1 towards the NextStateNode. 153 | 154 | const float MutliNodeSpace = 0.2f; // Space between multiple transition nodes (in units of ) 155 | const float MultiNodeStep = (1.f + MutliNodeSpace); //Step between node centres (Size of node + size of node spacer) 156 | 157 | const float MultiNodeStart = -((MaxNodes - 1) * MultiNodeStep) / 2.f; 158 | const float MultiNodeOffset = MultiNodeStart + (NodeIndex * MultiNodeStep); 159 | 160 | // Now we need to adjust the new center by the node size, zoom factor and multi node offset 161 | const FVector2D NewCorner = NewCenter - (0.5f * DesiredNodeSize) + (DeltaNormal * MultiNodeOffset * DesiredNodeSize.Size()); 162 | 163 | GraphNode->NodePosX = NewCorner.X; 164 | GraphNode->NodePosY = NewCorner.Y; 165 | } 166 | 167 | FSlateColor SEdNode_GenericGraphEdge::GetEdgeColor() const 168 | { 169 | UEdNode_GenericGraphEdge* EdgeNode = CastChecked(GraphNode); 170 | if (EdgeNode != nullptr && EdgeNode->GenericGraphEdge != nullptr) 171 | { 172 | return EdgeNode->GenericGraphEdge->GetEdgeColour(); 173 | } 174 | return FLinearColor(0.9f, 0.9f, 0.9f, 1.0f); 175 | } 176 | 177 | const FSlateBrush* SEdNode_GenericGraphEdge::GetEdgeImage() const 178 | { 179 | return FAppStyle::GetBrush("Graph.TransitionNode.Icon"); 180 | } 181 | 182 | EVisibility SEdNode_GenericGraphEdge::GetEdgeImageVisibility() const 183 | { 184 | UEdNode_GenericGraphEdge* EdgeNode = CastChecked(GraphNode); 185 | if (EdgeNode && EdgeNode->GenericGraphEdge && EdgeNode->GenericGraphEdge->bShouldDrawTitle) 186 | return EVisibility::Hidden; 187 | 188 | return EVisibility::Visible; 189 | } 190 | 191 | EVisibility SEdNode_GenericGraphEdge::GetEdgeTitleVisbility() const 192 | { 193 | UEdNode_GenericGraphEdge* EdgeNode = CastChecked(GraphNode); 194 | if (EdgeNode && EdgeNode->GenericGraphEdge && EdgeNode->GenericGraphEdge->bShouldDrawTitle) 195 | return EVisibility::Visible; 196 | 197 | return EVisibility::Collapsed; 198 | } 199 | 200 | #undef LOCTEXT_NAMESPACE 201 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Private/GenericGraphAssetEditor/SEdNode_GenericGraphNode.cpp: -------------------------------------------------------------------------------- 1 | #include "GenericGraphAssetEditor/SEdNode_GenericGraphNode.h" 2 | #include "GenericGraphEditorPCH.h" 3 | #include "GenericGraphAssetEditor/Colors_GenericGraph.h" 4 | #include "SLevelOfDetailBranchNode.h" 5 | #include "Widgets/Text/SInlineEditableTextBlock.h" 6 | #include "SCommentBubble.h" 7 | #include "SlateOptMacros.h" 8 | #include "SGraphPin.h" 9 | #include "GraphEditorSettings.h" 10 | #include "GenericGraphAssetEditor/EdNode_GenericGraphNode.h" 11 | #include "GenericGraphAssetEditor/GenericGraphDragConnection.h" 12 | 13 | #define LOCTEXT_NAMESPACE "EdNode_GenericGraph" 14 | 15 | ////////////////////////////////////////////////////////////////////////// 16 | class SGenericGraphPin : public SGraphPin 17 | { 18 | public: 19 | SLATE_BEGIN_ARGS(SGenericGraphPin) {} 20 | SLATE_END_ARGS() 21 | 22 | void Construct(const FArguments& InArgs, UEdGraphPin* InPin) 23 | { 24 | this->SetCursor(EMouseCursor::Default); 25 | 26 | bShowLabel = true; 27 | 28 | GraphPinObj = InPin; 29 | check(GraphPinObj != nullptr); 30 | 31 | const UEdGraphSchema* Schema = GraphPinObj->GetSchema(); 32 | check(Schema); 33 | 34 | SBorder::Construct(SBorder::FArguments() 35 | .BorderImage(this, &SGenericGraphPin::GetPinBorder) 36 | .BorderBackgroundColor(this, &SGenericGraphPin::GetPinColor) 37 | .OnMouseButtonDown(this, &SGenericGraphPin::OnPinMouseDown) 38 | .Cursor(this, &SGenericGraphPin::GetPinCursor) 39 | .Padding(FMargin(5.0f)) 40 | ); 41 | } 42 | 43 | protected: 44 | virtual FSlateColor GetPinColor() const override 45 | { 46 | return GenericGraphColors::Pin::Default; 47 | } 48 | 49 | virtual TSharedRef GetDefaultValueWidget() override 50 | { 51 | return SNew(STextBlock); 52 | } 53 | 54 | const FSlateBrush* GetPinBorder() const 55 | { 56 | return FAppStyle::GetBrush(TEXT("Graph.StateNode.Body")); 57 | } 58 | 59 | virtual TSharedRef SpawnPinDragEvent(const TSharedRef& InGraphPanel, const TArray< TSharedRef >& InStartingPins) override 60 | { 61 | FGenericGraphDragConnection::FDraggedPinTable PinHandles; 62 | PinHandles.Reserve(InStartingPins.Num()); 63 | // since the graph can be refreshed and pins can be reconstructed/replaced 64 | // behind the scenes, the DragDropOperation holds onto FGraphPinHandles 65 | // instead of direct widgets/graph-pins 66 | for (const TSharedRef& PinWidget : InStartingPins) 67 | { 68 | PinHandles.Add(PinWidget->GetPinObj()); 69 | } 70 | 71 | return FGenericGraphDragConnection::New(InGraphPanel, PinHandles); 72 | } 73 | 74 | }; 75 | 76 | 77 | ////////////////////////////////////////////////////////////////////////// 78 | void SEdNode_GenericGraphNode::Construct(const FArguments& InArgs, UEdNode_GenericGraphNode* InNode) 79 | { 80 | GraphNode = InNode; 81 | UpdateGraphNode(); 82 | InNode->SEdNode = this; 83 | } 84 | 85 | BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION 86 | void SEdNode_GenericGraphNode::UpdateGraphNode() 87 | { 88 | const FMargin NodePadding = FMargin(5); 89 | const FMargin NamePadding = FMargin(2); 90 | 91 | InputPins.Empty(); 92 | OutputPins.Empty(); 93 | 94 | // Reset variables that are going to be exposed, in case we are refreshing an already setup node. 95 | RightNodeBox.Reset(); 96 | LeftNodeBox.Reset(); 97 | 98 | const FSlateBrush *NodeTypeIcon = GetNameIcon(); 99 | 100 | FLinearColor TitleShadowColor(0.6f, 0.6f, 0.6f); 101 | TSharedPtr ErrorText; 102 | TSharedPtr NodeBody; 103 | TSharedPtr NodeTitle = SNew(SNodeTitle, GraphNode); 104 | 105 | this->ContentScale.Bind(this, &SGraphNode::GetContentScale); 106 | this->GetOrAddSlot(ENodeZone::Center) 107 | .HAlign(HAlign_Fill) 108 | .VAlign(VAlign_Center) 109 | [ 110 | SNew(SBorder) 111 | .BorderImage(FAppStyle::GetBrush("Graph.StateNode.Body")) 112 | .Padding(0.0f) 113 | .BorderBackgroundColor(this, &SEdNode_GenericGraphNode::GetBorderBackgroundColor) 114 | [ 115 | SNew(SOverlay) 116 | 117 | + SOverlay::Slot() 118 | .HAlign(HAlign_Fill) 119 | .VAlign(VAlign_Fill) 120 | [ 121 | SNew(SVerticalBox) 122 | 123 | // Input Pin Area 124 | + SVerticalBox::Slot() 125 | .FillHeight(1) 126 | [ 127 | SAssignNew(LeftNodeBox, SVerticalBox) 128 | ] 129 | 130 | // Output Pin Area 131 | + SVerticalBox::Slot() 132 | .FillHeight(1) 133 | [ 134 | SAssignNew(RightNodeBox, SVerticalBox) 135 | ] 136 | ] 137 | 138 | + SOverlay::Slot() 139 | .HAlign(HAlign_Center) 140 | .VAlign(VAlign_Center) 141 | .Padding(8.0f) 142 | [ 143 | SNew(SBorder) 144 | .BorderImage(FAppStyle::GetBrush("Graph.StateNode.ColorSpill")) 145 | .BorderBackgroundColor(TitleShadowColor) 146 | .HAlign(HAlign_Center) 147 | .VAlign(VAlign_Center) 148 | .Visibility(EVisibility::SelfHitTestInvisible) 149 | .Padding(6.0f) 150 | [ 151 | SAssignNew(NodeBody, SVerticalBox) 152 | 153 | // Title 154 | + SVerticalBox::Slot() 155 | .AutoHeight() 156 | [ 157 | SNew(SHorizontalBox) 158 | 159 | // Error message 160 | + SHorizontalBox::Slot() 161 | .AutoWidth() 162 | [ 163 | SAssignNew(ErrorText, SErrorText) 164 | .BackgroundColor(this, &SEdNode_GenericGraphNode::GetErrorColor) 165 | .ToolTipText(this, &SEdNode_GenericGraphNode::GetErrorMsgToolTip) 166 | ] 167 | 168 | // Icon 169 | +SHorizontalBox::Slot() 170 | .AutoWidth() 171 | .VAlign(VAlign_Center) 172 | [ 173 | SNew(SImage) 174 | .Image(NodeTypeIcon) 175 | ] 176 | 177 | // Node Title 178 | + SHorizontalBox::Slot() 179 | .Padding(FMargin(4.0f, 0.0f, 4.0f, 0.0f)) 180 | [ 181 | SNew(SVerticalBox) 182 | + SVerticalBox::Slot() 183 | .AutoHeight() 184 | [ 185 | SAssignNew(InlineEditableText, SInlineEditableTextBlock) 186 | .Style(FAppStyle::Get(), "Graph.StateNode.NodeTitleInlineEditableText") 187 | .Text(NodeTitle.Get(), &SNodeTitle::GetHeadTitle) 188 | .OnVerifyTextChanged(this, &SEdNode_GenericGraphNode::OnVerifyNameTextChanged) 189 | .OnTextCommitted(this, &SEdNode_GenericGraphNode::OnNameTextCommited) 190 | .IsReadOnly(this, &SEdNode_GenericGraphNode::IsNameReadOnly) 191 | .IsSelected(this, &SEdNode_GenericGraphNode::IsSelectedExclusively) 192 | ] 193 | + SVerticalBox::Slot() 194 | .AutoHeight() 195 | [ 196 | NodeTitle.ToSharedRef() 197 | ] 198 | ] 199 | ] 200 | ] 201 | ] 202 | ] 203 | ]; 204 | 205 | // Create comment bubble 206 | TSharedPtr CommentBubble; 207 | const FSlateColor CommentColor = GetDefault()->DefaultCommentNodeTitleColor; 208 | 209 | SAssignNew(CommentBubble, SCommentBubble) 210 | .GraphNode(GraphNode) 211 | .Text(this, &SGraphNode::GetNodeComment) 212 | .OnTextCommitted(this, &SGraphNode::OnCommentTextCommitted) 213 | .ColorAndOpacity(CommentColor) 214 | .AllowPinning(true) 215 | .EnableTitleBarBubble(true) 216 | .EnableBubbleCtrls(true) 217 | .GraphLOD(this, &SGraphNode::GetCurrentLOD) 218 | .IsGraphNodeHovered(this, &SGraphNode::IsHovered); 219 | 220 | GetOrAddSlot(ENodeZone::TopCenter) 221 | .SlotOffset(TAttribute(CommentBubble.Get(), &SCommentBubble::GetOffset)) 222 | .SlotSize(TAttribute(CommentBubble.Get(), &SCommentBubble::GetSize)) 223 | .AllowScaling(TAttribute(CommentBubble.Get(), &SCommentBubble::IsScalingAllowed)) 224 | .VAlign(VAlign_Top) 225 | [ 226 | CommentBubble.ToSharedRef() 227 | ]; 228 | 229 | ErrorReporting = ErrorText; 230 | ErrorReporting->SetError(ErrorMsg); 231 | CreatePinWidgets(); 232 | } 233 | 234 | void SEdNode_GenericGraphNode::CreatePinWidgets() 235 | { 236 | UEdNode_GenericGraphNode* StateNode = CastChecked(GraphNode); 237 | 238 | for (int32 PinIdx = 0; PinIdx < StateNode->Pins.Num(); PinIdx++) 239 | { 240 | UEdGraphPin* MyPin = StateNode->Pins[PinIdx]; 241 | if (!MyPin->bHidden) 242 | { 243 | TSharedPtr NewPin = SNew(SGenericGraphPin, MyPin); 244 | 245 | AddPin(NewPin.ToSharedRef()); 246 | } 247 | } 248 | } 249 | 250 | void SEdNode_GenericGraphNode::AddPin(const TSharedRef& PinToAdd) 251 | { 252 | PinToAdd->SetOwner(SharedThis(this)); 253 | 254 | const UEdGraphPin* PinObj = PinToAdd->GetPinObj(); 255 | const bool bAdvancedParameter = PinObj && PinObj->bAdvancedView; 256 | if (bAdvancedParameter) 257 | { 258 | PinToAdd->SetVisibility( TAttribute(PinToAdd, &SGraphPin::IsPinVisibleAsAdvanced) ); 259 | } 260 | 261 | TSharedPtr PinBox; 262 | if (PinToAdd->GetDirection() == EEdGraphPinDirection::EGPD_Input) 263 | { 264 | PinBox = LeftNodeBox; 265 | InputPins.Add(PinToAdd); 266 | } 267 | else // Direction == EEdGraphPinDirection::EGPD_Output 268 | { 269 | PinBox = RightNodeBox; 270 | OutputPins.Add(PinToAdd); 271 | } 272 | 273 | if (PinBox) 274 | { 275 | PinBox->AddSlot() 276 | .HAlign(HAlign_Fill) 277 | .VAlign(VAlign_Fill) 278 | .FillHeight(1.0f) 279 | //.Padding(6.0f, 0.0f) 280 | [ 281 | PinToAdd 282 | ]; 283 | } 284 | } 285 | 286 | bool SEdNode_GenericGraphNode::IsNameReadOnly() const 287 | { 288 | UEdNode_GenericGraphNode* EdNode_Node = Cast(GraphNode); 289 | check(EdNode_Node != nullptr); 290 | 291 | UGenericGraph* GenericGraph = EdNode_Node->GenericGraphNode->Graph; 292 | check(GenericGraph != nullptr); 293 | 294 | return (!GenericGraph->bCanRenameNode || !EdNode_Node->GenericGraphNode->IsNameEditable()) || SGraphNode::IsNameReadOnly(); 295 | } 296 | 297 | END_SLATE_FUNCTION_BUILD_OPTIMIZATION 298 | 299 | void SEdNode_GenericGraphNode::OnNameTextCommited(const FText& InText, ETextCommit::Type CommitInfo) 300 | { 301 | SGraphNode::OnNameTextCommited(InText, CommitInfo); 302 | 303 | UEdNode_GenericGraphNode* MyNode = CastChecked(GraphNode); 304 | 305 | if (MyNode != nullptr && MyNode->GenericGraphNode != nullptr) 306 | { 307 | const FScopedTransaction Transaction(LOCTEXT("GenericGraphEditorRenameNode", "Generic Graph Editor: Rename Node")); 308 | MyNode->Modify(); 309 | MyNode->GenericGraphNode->Modify(); 310 | MyNode->GenericGraphNode->SetNodeTitle(InText); 311 | UpdateGraphNode(); 312 | } 313 | } 314 | 315 | FSlateColor SEdNode_GenericGraphNode::GetBorderBackgroundColor() const 316 | { 317 | UEdNode_GenericGraphNode* MyNode = CastChecked(GraphNode); 318 | return MyNode ? MyNode->GetBackgroundColor() : GenericGraphColors::NodeBorder::HighlightAbortRange0; 319 | } 320 | 321 | FSlateColor SEdNode_GenericGraphNode::GetBackgroundColor() const 322 | { 323 | return GenericGraphColors::NodeBody::Default; 324 | } 325 | 326 | EVisibility SEdNode_GenericGraphNode::GetDragOverMarkerVisibility() const 327 | { 328 | return EVisibility::Visible; 329 | } 330 | 331 | const FSlateBrush* SEdNode_GenericGraphNode::GetNameIcon() const 332 | { 333 | return FAppStyle::GetBrush(TEXT("BTEditor.Graph.BTNode.Icon")); 334 | } 335 | 336 | #undef LOCTEXT_NAMESPACE 337 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Private/GenericGraphAssetEditor/Settings_GenericGraphEditor.cpp: -------------------------------------------------------------------------------- 1 | #include "GenericGraphAssetEditor/Settings_GenericGraphEditor.h" 2 | 3 | UGenericGraphEditorSettings::UGenericGraphEditorSettings() 4 | { 5 | AutoLayoutStrategy = EAutoLayoutStrategy::Tree; 6 | 7 | bFirstPassOnly = false; 8 | 9 | bRandomInit = false; 10 | 11 | OptimalDistance = 100.f; 12 | 13 | MaxIteration = 50; 14 | 15 | InitTemperature = 10.f; 16 | 17 | CoolDownRate = 10.f; 18 | } 19 | 20 | UGenericGraphEditorSettings::~UGenericGraphEditorSettings() 21 | { 22 | 23 | } 24 | 25 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Private/GenericGraphEditor.cpp: -------------------------------------------------------------------------------- 1 | #include "GenericGraphEditor.h" 2 | #include "GenericGraphNodeFactory.h" 3 | #include "AssetTypeActions_GenericGraph.h" 4 | #include "GenericGraphAssetEditor/GenericGraphEditorStyle.h" 5 | 6 | DEFINE_LOG_CATEGORY(GenericGraphEditor) 7 | 8 | #define LOCTEXT_NAMESPACE "Editor_GenericGraph" 9 | 10 | void FGenericGraphEditor::StartupModule() 11 | { 12 | FGenericGraphEditorStyle::Initialize(); 13 | 14 | GraphPanelNodeFactory_GenericGraph = MakeShareable(new FGraphPanelNodeFactory_GenericGraph()); 15 | FEdGraphUtilities::RegisterVisualNodeFactory(GraphPanelNodeFactory_GenericGraph); 16 | 17 | IAssetTools& AssetTools = FModuleManager::Get().LoadModuleChecked("AssetTools").Get(); 18 | 19 | GenericGraphAssetCategoryBit = AssetTools.RegisterAdvancedAssetCategory(FName(TEXT("GenericGraph")), LOCTEXT("GenericGraphAssetCategory", "GenericGraph")); 20 | 21 | RegisterAssetTypeAction(AssetTools, MakeShareable(new FAssetTypeActions_GenericGraph(GenericGraphAssetCategoryBit))); 22 | } 23 | 24 | 25 | void FGenericGraphEditor::ShutdownModule() 26 | { 27 | // Unregister all the asset types that we registered 28 | if (FModuleManager::Get().IsModuleLoaded("AssetTools")) 29 | { 30 | IAssetTools& AssetTools = FModuleManager::GetModuleChecked("AssetTools").Get(); 31 | for (int32 Index = 0; Index < CreatedAssetTypeActions.Num(); ++Index) 32 | { 33 | AssetTools.UnregisterAssetTypeActions(CreatedAssetTypeActions[Index].ToSharedRef()); 34 | } 35 | } 36 | 37 | if (GraphPanelNodeFactory_GenericGraph.IsValid()) 38 | { 39 | FEdGraphUtilities::UnregisterVisualNodeFactory(GraphPanelNodeFactory_GenericGraph); 40 | GraphPanelNodeFactory_GenericGraph.Reset(); 41 | } 42 | 43 | FGenericGraphEditorStyle::Shutdown(); 44 | } 45 | 46 | void FGenericGraphEditor::RegisterAssetTypeAction(IAssetTools& AssetTools, TSharedRef Action) 47 | { 48 | AssetTools.RegisterAssetTypeActions(Action); 49 | CreatedAssetTypeActions.Add(Action); 50 | } 51 | 52 | IMPLEMENT_MODULE(FGenericGraphEditor, GenericGraphEditor) 53 | 54 | #undef LOCTEXT_NAMESPACE 55 | 56 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Private/GenericGraphFactory.cpp: -------------------------------------------------------------------------------- 1 | #include "GenericGraphFactory.h" 2 | #include "GenericGraph.h" 3 | 4 | #include "ClassViewerModule.h" 5 | #include "ClassViewerFilter.h" 6 | #include "Kismet2/KismetEditorUtilities.h" 7 | #include "Kismet2/SClassPickerDialog.h" 8 | 9 | #define LOCTEXT_NAMESPACE "GenericGraphFactory" 10 | 11 | class FAssetClassParentFilter : public IClassViewerFilter 12 | { 13 | public: 14 | FAssetClassParentFilter() 15 | : DisallowedClassFlags(CLASS_None), bDisallowBlueprintBase(false) 16 | {} 17 | 18 | /** All children of these classes will be included unless filtered out by another setting. */ 19 | TSet< const UClass* > AllowedChildrenOfClasses; 20 | 21 | /** Disallowed class flags. */ 22 | EClassFlags DisallowedClassFlags; 23 | 24 | /** Disallow blueprint base classes. */ 25 | bool bDisallowBlueprintBase; 26 | 27 | virtual bool IsClassAllowed(const FClassViewerInitializationOptions& InInitOptions, const UClass* InClass, TSharedRef< FClassViewerFilterFuncs > InFilterFuncs) override 28 | { 29 | bool bAllowed= !InClass->HasAnyClassFlags(DisallowedClassFlags) 30 | && InFilterFuncs->IfInChildOfClassesSet(AllowedChildrenOfClasses, InClass) != EFilterReturn::Failed; 31 | 32 | if (bAllowed && bDisallowBlueprintBase) 33 | { 34 | if (FKismetEditorUtilities::CanCreateBlueprintOfClass(InClass)) 35 | { 36 | return false; 37 | } 38 | } 39 | 40 | return bAllowed; 41 | } 42 | 43 | virtual bool IsUnloadedClassAllowed(const FClassViewerInitializationOptions& InInitOptions, const TSharedRef< const IUnloadedBlueprintData > InUnloadedClassData, TSharedRef< FClassViewerFilterFuncs > InFilterFuncs) override 44 | { 45 | if (bDisallowBlueprintBase) 46 | { 47 | return false; 48 | } 49 | 50 | return !InUnloadedClassData->HasAnyClassFlags(DisallowedClassFlags) 51 | && InFilterFuncs->IfInChildOfClassesSet(AllowedChildrenOfClasses, InUnloadedClassData) != EFilterReturn::Failed; 52 | } 53 | }; 54 | 55 | 56 | UGenericGraphFactory::UGenericGraphFactory() 57 | { 58 | bCreateNew = true; 59 | bEditAfterNew = true; 60 | SupportedClass = UGenericGraph::StaticClass(); 61 | } 62 | 63 | UGenericGraphFactory::~UGenericGraphFactory() 64 | { 65 | 66 | } 67 | 68 | bool UGenericGraphFactory::ConfigureProperties() 69 | { 70 | // nullptr the GenericGraphClass so we can check for selection 71 | GenericGraphClass = nullptr; 72 | 73 | // Load the classviewer module to display a class picker 74 | FClassViewerModule& ClassViewerModule = FModuleManager::Get().LoadModuleChecked("ClassViewer"); 75 | 76 | // Fill in options 77 | FClassViewerInitializationOptions Options; 78 | Options.Mode = EClassViewerMode::ClassPicker; 79 | 80 | #if ENGINE_MAJOR_VERSION < 5 81 | TSharedPtr Filter = MakeShareable(new FAssetClassParentFilter); 82 | Options.ClassFilter = Filter; 83 | #else // #if ENGINE_MAJOR_VERSION < 5 84 | TSharedRef Filter = MakeShareable(new FAssetClassParentFilter); 85 | Options.ClassFilters.Add(Filter); 86 | #endif // #else // #if ENGINE_MAJOR_VERSION < 5 87 | 88 | Filter->DisallowedClassFlags = CLASS_Abstract | CLASS_Deprecated | CLASS_NewerVersionExists | CLASS_HideDropDown; 89 | Filter->AllowedChildrenOfClasses.Add(UGenericGraph::StaticClass()); 90 | 91 | const FText TitleText = LOCTEXT("CreateGenericGraphAssetOptions", "Pick Generic Graph Class"); 92 | UClass* ChosenClass = nullptr; 93 | const bool bPressedOk = SClassPickerDialog::PickClass(TitleText, Options, ChosenClass, UGenericGraph::StaticClass()); 94 | 95 | if ( bPressedOk ) 96 | { 97 | GenericGraphClass = ChosenClass; 98 | } 99 | 100 | return bPressedOk; 101 | } 102 | 103 | UObject* UGenericGraphFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) 104 | { 105 | if (GenericGraphClass != nullptr) 106 | { 107 | return NewObject(InParent, GenericGraphClass, Name, Flags | RF_Transactional); 108 | } 109 | else 110 | { 111 | check(Class->IsChildOf(UGenericGraph::StaticClass())); 112 | return NewObject(InParent, Class, Name, Flags | RF_Transactional); 113 | } 114 | 115 | } 116 | 117 | #undef LOCTEXT_NAMESPACE 118 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Private/GenericGraphNodeFactory.cpp: -------------------------------------------------------------------------------- 1 | #include "GenericGraphNodeFactory.h" 2 | #include 3 | #include "GenericGraphAssetEditor/SEdNode_GenericGraphEdge.h" 4 | #include "GenericGraphAssetEditor/EdNode_GenericGraphNode.h" 5 | #include "GenericGraphAssetEditor/SEdNode_GenericGraphNode.h" 6 | #include "GenericGraphAssetEditor/EdNode_GenericGraphEdge.h" 7 | 8 | TSharedPtr FGraphPanelNodeFactory_GenericGraph::CreateNode(UEdGraphNode* Node) const 9 | { 10 | if (UEdNode_GenericGraphNode* EdNode_GraphNode = Cast(Node)) 11 | { 12 | return SNew(SEdNode_GenericGraphNode, EdNode_GraphNode); 13 | } 14 | else if (UEdNode_GenericGraphEdge* EdNode_Edge = Cast(Node)) 15 | { 16 | return SNew(SEdNode_GenericGraphEdge, EdNode_Edge); 17 | } 18 | return nullptr; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Public/AssetTypeActions_GenericGraph.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "AssetTypeActions_Base.h" 5 | 6 | class GENERICGRAPHEDITOR_API FAssetTypeActions_GenericGraph : public FAssetTypeActions_Base 7 | { 8 | public: 9 | FAssetTypeActions_GenericGraph(EAssetTypeCategories::Type InAssetCategory); 10 | 11 | virtual FText GetName() const override; 12 | virtual FColor GetTypeColor() const override; 13 | virtual UClass* GetSupportedClass() const override; 14 | virtual void OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor = TSharedPtr()) override; 15 | virtual uint32 GetCategories() override; 16 | 17 | private: 18 | EAssetTypeCategories::Type MyAssetCategory; 19 | }; -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Public/AutoLayout/AutoLayoutStrategy.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "EdGraph/EdGraph.h" 5 | #include "GenericGraph.h" 6 | #include "GenericGraphAssetEditor/EdGraph_GenericGraph.h" 7 | #include "GenericGraphAssetEditor/EdNode_GenericGraphNode.h" 8 | #include "GenericGraphAssetEditor/EdNode_GenericGraphEdge.h" 9 | #include "GenericGraphAssetEditor/Settings_GenericGraphEditor.h" 10 | #include "AutoLayoutStrategy.generated.h" 11 | 12 | UCLASS(abstract) 13 | class GENERICGRAPHEDITOR_API UAutoLayoutStrategy : public UObject 14 | { 15 | GENERATED_BODY() 16 | public: 17 | UAutoLayoutStrategy(); 18 | virtual ~UAutoLayoutStrategy(); 19 | 20 | virtual void Layout(UEdGraph* G) {}; 21 | 22 | TObjectPtr Settings; 23 | 24 | protected: 25 | int32 GetNodeWidth(UEdNode_GenericGraphNode* EdNode); 26 | 27 | int32 GetNodeHeight(UEdNode_GenericGraphNode* EdNode); 28 | 29 | FBox2D GetNodeBound(UEdGraphNode* EdNode); 30 | 31 | FBox2D GetActualBounds(UGenericGraphNode* RootNode); 32 | 33 | virtual void RandomLayoutOneTree(UGenericGraphNode* RootNode, const FBox2D& Bound); 34 | 35 | protected: 36 | TObjectPtr Graph; 37 | TObjectPtr EdGraph; 38 | int32 MaxIteration; 39 | int32 OptimalDistance; 40 | }; 41 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Public/AutoLayout/ForceDirectedLayoutStrategy.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "AutoLayoutStrategy.h" 5 | #include "ForceDirectedLayoutStrategy.generated.h" 6 | 7 | UCLASS() 8 | class GENERICGRAPHEDITOR_API UForceDirectedLayoutStrategy : public UAutoLayoutStrategy 9 | { 10 | GENERATED_BODY() 11 | public: 12 | UForceDirectedLayoutStrategy(); 13 | virtual ~UForceDirectedLayoutStrategy(); 14 | 15 | virtual void Layout(UEdGraph* EdGraph) override; 16 | 17 | protected: 18 | virtual FBox2D LayoutOneTree(UGenericGraphNode* RootNode, const FBox2D& PreTreeBound); 19 | 20 | protected: 21 | bool bRandomInit; 22 | float InitTemperature; 23 | float CoolDownRate; 24 | }; 25 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Public/AutoLayout/TreeLayoutStrategy.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "AutoLayoutStrategy.h" 5 | #include "TreeLayoutStrategy.generated.h" 6 | 7 | UCLASS() 8 | class GENERICGRAPHEDITOR_API UTreeLayoutStrategy : public UAutoLayoutStrategy 9 | { 10 | GENERATED_BODY() 11 | public: 12 | UTreeLayoutStrategy(); 13 | virtual ~UTreeLayoutStrategy(); 14 | 15 | virtual void Layout(UEdGraph* EdGraph) override; 16 | 17 | protected: 18 | void InitPass(UGenericGraphNode* RootNode, const FVector2D& Anchor); 19 | bool ResolveConflictPass(UGenericGraphNode* Node); 20 | 21 | bool ResolveConflict(UGenericGraphNode* LRoot, UGenericGraphNode* RRoot); 22 | 23 | void GetLeftContour(UGenericGraphNode* RootNode, int32 Level, TArray& Contour); 24 | void GetRightContour(UGenericGraphNode* RootNode, int32 Level, TArray& Contour); 25 | 26 | void ShiftSubTree(UGenericGraphNode* RootNode, const FVector2D& Offset); 27 | 28 | void UpdateParentNodePosition(UGenericGraphNode* RootNode); 29 | }; 30 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Public/GenericGraphAssetEditor/AssetEditorToolbar_GenericGraph.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | 6 | class FAssetEditor_GenericGraph; 7 | class FExtender; 8 | class FToolBarBuilder; 9 | 10 | class GENERICGRAPHEDITOR_API FAssetEditorToolbar_GenericGraph : public TSharedFromThis 11 | { 12 | public: 13 | FAssetEditorToolbar_GenericGraph(TSharedPtr InGenericGraphEditor) 14 | : GenericGraphEditor(InGenericGraphEditor) {} 15 | 16 | void AddGenericGraphToolbar(TSharedPtr Extender); 17 | 18 | private: 19 | void FillGenericGraphToolbar(FToolBarBuilder& ToolbarBuilder); 20 | 21 | protected: 22 | /** Pointer back to the blueprint editor tool that owns us */ 23 | TWeakPtr GenericGraphEditor; 24 | }; 25 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Public/GenericGraphAssetEditor/AssetEditor_GenericGraph.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "Settings_GenericGraphEditor.h" 5 | #include "GenericGraph.h" 6 | 7 | #if ENGINE_MAJOR_VERSION == 5 8 | #include "UObject/ObjectSaveContext.h" 9 | #endif // #if ENGINE_MAJOR_VERSION == 5 10 | 11 | class FGGAssetEditorToolbar; 12 | 13 | class GENERICGRAPHEDITOR_API FAssetEditor_GenericGraph : public FAssetEditorToolkit, public FNotifyHook, public FGCObject 14 | { 15 | public: 16 | FAssetEditor_GenericGraph(); 17 | virtual ~FAssetEditor_GenericGraph(); 18 | 19 | void InitGenericGraphAssetEditor(const EToolkitMode::Type Mode, const TSharedPtr< IToolkitHost >& InitToolkitHost, UGenericGraph* Graph); 20 | 21 | // IToolkit interface 22 | virtual void RegisterTabSpawners(const TSharedRef& TabManager) override; 23 | virtual void UnregisterTabSpawners(const TSharedRef& TabManager) override; 24 | // End of IToolkit interface 25 | 26 | // FAssetEditorToolkit 27 | virtual FName GetToolkitFName() const override; 28 | virtual FText GetBaseToolkitName() const override; 29 | virtual FText GetToolkitName() const override; 30 | virtual FText GetToolkitToolTipText() const override; 31 | virtual FLinearColor GetWorldCentricTabColorScale() const override; 32 | virtual FString GetWorldCentricTabPrefix() const override; 33 | virtual FString GetDocumentationLink() const override; 34 | virtual void SaveAsset_Execute() override; 35 | // End of FAssetEditorToolkit 36 | 37 | //Toolbar 38 | void UpdateToolbar(); 39 | TSharedPtr GetToolbarBuilder() { return ToolbarBuilder; } 40 | void RegisterToolbarTab(const TSharedRef& TabManager); 41 | 42 | 43 | // FSerializableObject interface 44 | virtual void AddReferencedObjects(FReferenceCollector& Collector) override; 45 | // End of FSerializableObject interface 46 | 47 | #if ENGINE_MAJOR_VERSION == 5 48 | // FGCObject interface 49 | virtual FString GetReferencerName() const 50 | { 51 | return TEXT("FAssetEditor_LTGenericGraph"); 52 | } 53 | // ~FGCObject interface 54 | #endif // #if ENGINE_MAJOR_VERSION == 5 55 | 56 | UGenericGraphEditorSettings* GetSettings() const; 57 | 58 | protected: 59 | TSharedRef SpawnTab_Viewport(const FSpawnTabArgs& Args); 60 | TSharedRef SpawnTab_Details(const FSpawnTabArgs& Args); 61 | TSharedRef SpawnTab_EditorSettings(const FSpawnTabArgs& Args); 62 | 63 | void CreateInternalWidgets(); 64 | TSharedRef CreateViewportWidget(); 65 | 66 | 67 | void BindCommands(); 68 | 69 | void CreateEdGraph(); 70 | 71 | void CreateCommandList(); 72 | 73 | TSharedPtr GetCurrGraphEditor() const; 74 | 75 | FGraphPanelSelectionSet GetSelectedNodes() const; 76 | 77 | void RebuildGenericGraph(); 78 | 79 | // Delegates for graph editor commands 80 | void SelectAllNodes(); 81 | bool CanSelectAllNodes(); 82 | void DeleteSelectedNodes(); 83 | bool CanDeleteNodes(); 84 | void DeleteSelectedDuplicatableNodes(); 85 | void CutSelectedNodes(); 86 | bool CanCutNodes(); 87 | void CopySelectedNodes(); 88 | bool CanCopyNodes(); 89 | void PasteNodes(); 90 | void PasteNodesHere(const FVector2D& Location); 91 | bool CanPasteNodes(); 92 | void DuplicateNodes(); 93 | bool CanDuplicateNodes(); 94 | 95 | void GraphSettings(); 96 | bool CanGraphSettings() const; 97 | 98 | void AutoArrange(); 99 | bool CanAutoArrange() const; 100 | 101 | void OnRenameNode(); 102 | bool CanRenameNodes() const; 103 | 104 | ////////////////////////////////////////////////////////////////////////// 105 | // graph editor event 106 | void OnSelectedNodesChanged(const TSet& NewSelection); 107 | 108 | void OnNodeDoubleClicked(UEdGraphNode* Node); 109 | 110 | void OnFinishedChangingProperties(const FPropertyChangedEvent& PropertyChangedEvent); 111 | 112 | #if ENGINE_MAJOR_VERSION < 5 113 | void OnPackageSaved(const FString& PackageFileName, UObject* Outer); 114 | #else // #if ENGINE_MAJOR_VERSION < 5 115 | void OnPackageSavedWithContext(const FString& PackageFileName, UPackage* Package, FObjectPostSaveContext ObjectSaveContext); 116 | #endif // #else // #if ENGINE_MAJOR_VERSION < 5 117 | 118 | protected: 119 | TObjectPtr GenricGraphEditorSettings; 120 | 121 | TObjectPtr EditingGraph; 122 | 123 | //Toolbar 124 | TSharedPtr ToolbarBuilder; 125 | 126 | /** Handle to the registered OnPackageSave delegate */ 127 | FDelegateHandle OnPackageSavedDelegateHandle; 128 | 129 | TSharedPtr ViewportWidget; 130 | TSharedPtr PropertyWidget; 131 | TSharedPtr EditorSettingsWidget; 132 | 133 | /** The command list for this editor */ 134 | TSharedPtr GraphEditorCommands; 135 | }; 136 | 137 | 138 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Public/GenericGraphAssetEditor/AssetGraphSchema_GenericGraph.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "GenericGraph.h" 5 | #include "GenericGraphNode.h" 6 | #include "GenericGraphEdge.h" 7 | #include "AssetGraphSchema_GenericGraph.generated.h" 8 | 9 | class UEdNode_GenericGraphNode; 10 | class UEdNode_GenericGraphEdge; 11 | class UAutoLayoutStrategy; 12 | 13 | /** Action to add a node to the graph */ 14 | USTRUCT() 15 | struct GENERICGRAPHEDITOR_API FAssetSchemaAction_GenericGraph_NewNode : public FEdGraphSchemaAction 16 | { 17 | GENERATED_USTRUCT_BODY(); 18 | 19 | public: 20 | FAssetSchemaAction_GenericGraph_NewNode(): NodeTemplate(nullptr) {} 21 | 22 | FAssetSchemaAction_GenericGraph_NewNode(const FText& InNodeCategory, const FText& InMenuDesc, const FText& InToolTip, const int32 InGrouping) 23 | : FEdGraphSchemaAction(InNodeCategory, InMenuDesc, InToolTip, InGrouping), NodeTemplate(nullptr) {} 24 | 25 | virtual UEdGraphNode* PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode = true) override; 26 | virtual void AddReferencedObjects(FReferenceCollector& Collector) override; 27 | 28 | TObjectPtr NodeTemplate; 29 | }; 30 | 31 | USTRUCT() 32 | struct GENERICGRAPHEDITOR_API FAssetSchemaAction_GenericGraph_NewEdge : public FEdGraphSchemaAction 33 | { 34 | GENERATED_USTRUCT_BODY(); 35 | 36 | public: 37 | FAssetSchemaAction_GenericGraph_NewEdge(): NodeTemplate(nullptr){} 38 | 39 | FAssetSchemaAction_GenericGraph_NewEdge(const FText& InNodeCategory, const FText& InMenuDesc, const FText& InToolTip, const int32 InGrouping) 40 | : FEdGraphSchemaAction(InNodeCategory, InMenuDesc, InToolTip, InGrouping), NodeTemplate(nullptr) {} 41 | 42 | virtual UEdGraphNode* PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode = true) override; 43 | virtual void AddReferencedObjects(FReferenceCollector& Collector) override; 44 | 45 | TObjectPtr NodeTemplate; 46 | }; 47 | 48 | UCLASS(MinimalAPI) 49 | class UAssetGraphSchema_GenericGraph : public UEdGraphSchema 50 | { 51 | GENERATED_BODY() 52 | 53 | public: 54 | void GetBreakLinkToSubMenuActions(class UToolMenu* Menu, class UEdGraphPin* InGraphPin); 55 | 56 | virtual EGraphType GetGraphType(const UEdGraph* TestEdGraph) const override; 57 | 58 | virtual void GetGraphContextActions(FGraphContextMenuBuilder& ContextMenuBuilder) const override; 59 | 60 | virtual void GetContextMenuActions(class UToolMenu* Menu, class UGraphNodeContextMenuContext* Context) const override; 61 | 62 | virtual const FPinConnectionResponse CanCreateConnection(const UEdGraphPin* A, const UEdGraphPin* B) const override; 63 | 64 | virtual bool TryCreateConnection(UEdGraphPin* A, UEdGraphPin* B) const override; 65 | virtual bool CreateAutomaticConversionNodeAndConnections(UEdGraphPin* A, UEdGraphPin* B) const override; 66 | 67 | virtual class FConnectionDrawingPolicy* CreateConnectionDrawingPolicy(int32 InBackLayerID, int32 InFrontLayerID, float InZoomFactor, const FSlateRect& InClippingRect, class FSlateWindowElementList& InDrawElements, class UEdGraph* InGraphObj) const override; 68 | 69 | virtual FLinearColor GetPinTypeColor(const FEdGraphPinType& PinType) const override; 70 | 71 | virtual void BreakNodeLinks(UEdGraphNode& TargetNode) const override; 72 | 73 | virtual void BreakPinLinks(UEdGraphPin& TargetPin, bool bSendsNodeNotifcation) const override; 74 | 75 | virtual void BreakSinglePinLink(UEdGraphPin* SourcePin, UEdGraphPin* TargetPin) const override; 76 | 77 | virtual UEdGraphPin* DropPinOnNode(UEdGraphNode* InTargetNode, const FName& InSourcePinName, const FEdGraphPinType& InSourcePinType, EEdGraphPinDirection InSourcePinDirection) const override; 78 | 79 | virtual bool SupportsDropPinOnNode(UEdGraphNode* InTargetNode, const FEdGraphPinType& InSourcePinType, EEdGraphPinDirection InSourcePinDirection, FText& OutErrorMessage) const override; 80 | 81 | virtual bool IsCacheVisualizationOutOfDate(int32 InVisualizationCacheID) const override; 82 | 83 | virtual int32 GetCurrentVisualizationCacheID() const override; 84 | 85 | virtual void ForceVisualizationCacheClear() const override; 86 | 87 | private: 88 | static int32 CurrentCacheRefreshID; 89 | }; 90 | 91 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Public/GenericGraphAssetEditor/Colors_GenericGraph.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | 5 | namespace GenericGraphColors 6 | { 7 | namespace NodeBody 8 | { 9 | const FLinearColor Default(0.1f, 0.1f, 0.1f); 10 | const FLinearColor Root(0.5f, 0.5f, 0.5f, 0.1f); 11 | const FLinearColor Error(1.0f, 0.0f, 0.0f); 12 | } 13 | 14 | namespace NodeBorder 15 | { 16 | const FLinearColor Inactive(0.08f, 0.08f, 0.08f); 17 | const FLinearColor Root(0.2f, 0.2f, 0.2f, 0.2f); 18 | const FLinearColor Selected(1.00f, 0.08f, 0.08f); 19 | const FLinearColor ActiveDebugging(1.0f, 1.0f, 0.0f); 20 | const FLinearColor InactiveDebugging(0.4f, 0.4f, 0.0f); 21 | const FLinearColor HighlightAbortRange0(0.0f, 0.22f, 0.4f); 22 | const FLinearColor HighlightAbortRange1(0.0f, 0.4f, 0.22f); 23 | const FLinearColor Disconnected(0.f, 0.f, 0.f); 24 | const FLinearColor BrokenWithParent(1.f, 0.f, 1.f); 25 | const FLinearColor QuickFind(0.f, 0.8f, 0.f); 26 | } 27 | 28 | namespace Pin 29 | { 30 | const FLinearColor Diff(0.9f, 0.2f, 0.15f); 31 | const FLinearColor Hover(1.0f, 0.7f, 0.0f); 32 | const FLinearColor Default(0.02f, 0.02f, 0.02f); 33 | const FLinearColor SingleNode(0.02f, 0.02f, 0.02f); 34 | } 35 | 36 | namespace Connection 37 | { 38 | const FLinearColor Default(1.0f, 1.0f, 1.0f); 39 | } 40 | 41 | namespace Action 42 | { 43 | const FLinearColor DragMarker(1.0f, 1.0f, 0.2f); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Public/GenericGraphAssetEditor/ConnectionDrawingPolicy_GenericGraph.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "ConnectionDrawingPolicy.h" 5 | 6 | class GENERICGRAPHEDITOR_API FConnectionDrawingPolicy_GenericGraph : public FConnectionDrawingPolicy 7 | { 8 | protected: 9 | TObjectPtr GraphObj; 10 | TMap, int32> NodeWidgetMap; 11 | 12 | public: 13 | FConnectionDrawingPolicy_GenericGraph(int32 InBackLayerID, int32 InFrontLayerID, float ZoomFactor, const FSlateRect& InClippingRect, FSlateWindowElementList& InDrawElements, UEdGraph* InGraphObj); 14 | 15 | // FConnectionDrawingPolicy interface 16 | virtual void DetermineWiringStyle(UEdGraphPin* OutputPin, UEdGraphPin* InputPin, /*inout*/ FConnectionParams& Params) override; 17 | virtual void Draw(TMap, FArrangedWidget>& PinGeometries, FArrangedChildren& ArrangedNodes) override; 18 | virtual void DrawSplineWithArrow(const FGeometry& StartGeom, const FGeometry& EndGeom, const FConnectionParams& Params) override; 19 | virtual void DrawSplineWithArrow(const FVector2D& StartPoint, const FVector2D& EndPoint, const FConnectionParams& Params) override; 20 | virtual void DrawPreviewConnector(const FGeometry& PinGeometry, const FVector2D& StartPoint, const FVector2D& EndPoint, UEdGraphPin* Pin) override; 21 | virtual FVector2D ComputeSplineTangent(const FVector2D& Start, const FVector2D& End) const override; 22 | virtual void DetermineLinkGeometry(FArrangedChildren& ArrangedNodes, TSharedRef& OutputPinWidget, UEdGraphPin* OutputPin, UEdGraphPin* InputPin, FArrangedWidget*& StartWidgetGeometry, FArrangedWidget*& EndWidgetGeometry) override; 23 | // End of FConnectionDrawingPolicy interface 24 | 25 | protected: 26 | void Internal_DrawLineWithArrow(const FVector2D& StartAnchorPoint, const FVector2D& EndAnchorPoint, const FConnectionParams& Params); 27 | }; 28 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Public/GenericGraphAssetEditor/EdGraph_GenericGraph.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "EdGraph/EdGraph.h" 5 | #include "EdGraph_GenericGraph.generated.h" 6 | 7 | class UGenericGraph; 8 | class UGenericGraphNode; 9 | class UGenericGraphEdge; 10 | class UEdNode_GenericGraphNode; 11 | class UEdNode_GenericGraphEdge; 12 | 13 | UCLASS() 14 | class GENERICGRAPHEDITOR_API UEdGraph_GenericGraph : public UEdGraph 15 | { 16 | GENERATED_BODY() 17 | 18 | public: 19 | UEdGraph_GenericGraph(); 20 | virtual ~UEdGraph_GenericGraph(); 21 | 22 | virtual void RebuildGenericGraph(); 23 | 24 | UGenericGraph* GetGenericGraph() const; 25 | 26 | virtual bool Modify(bool bAlwaysMarkDirty = true) override; 27 | virtual void PostEditUndo() override; 28 | 29 | UPROPERTY(Transient) 30 | TMap NodeMap; 31 | 32 | UPROPERTY(Transient) 33 | TMap EdgeMap; 34 | 35 | protected: 36 | void Clear(); 37 | 38 | void SortNodes(UGenericGraphNode* RootNode); 39 | }; 40 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Public/GenericGraphAssetEditor/EdNode_GenericGraphEdge.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "EdGraph/EdGraphNode.h" 5 | #include "EdNode_GenericGraphEdge.generated.h" 6 | 7 | class UGenericGraphNode; 8 | class UGenericGraphEdge; 9 | class UEdNode_GenericGraphNode; 10 | 11 | UCLASS(MinimalAPI) 12 | class UEdNode_GenericGraphEdge : public UEdGraphNode 13 | { 14 | GENERATED_BODY() 15 | 16 | public: 17 | UEdNode_GenericGraphEdge(); 18 | 19 | UPROPERTY() 20 | TObjectPtr Graph; 21 | 22 | UPROPERTY(VisibleAnywhere, Instanced, Category = "GenericGraph") 23 | TObjectPtr GenericGraphEdge; 24 | 25 | void SetEdge(UGenericGraphEdge* Edge); 26 | 27 | virtual void AllocateDefaultPins() override; 28 | 29 | virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override; 30 | 31 | virtual void PinConnectionListChanged(UEdGraphPin* Pin) override; 32 | 33 | virtual void PrepareForCopying() override; 34 | 35 | virtual UEdGraphPin* GetInputPin() const { return Pins[0]; } 36 | virtual UEdGraphPin* GetOutputPin() const { return Pins[1]; } 37 | 38 | void CreateConnections(UEdNode_GenericGraphNode* Start, UEdNode_GenericGraphNode* End); 39 | 40 | UEdNode_GenericGraphNode* GetStartNode(); 41 | UEdNode_GenericGraphNode* GetEndNode(); 42 | }; 43 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Public/GenericGraphAssetEditor/EdNode_GenericGraphNode.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "EdGraph/EdGraphNode.h" 5 | #include "GenericGraphNode.h" 6 | #include "EdNode_GenericGraphNode.generated.h" 7 | 8 | class UEdNode_GenericGraphEdge; 9 | class UEdGraph_GenericGraph; 10 | class SEdNode_GenericGraphNode; 11 | 12 | UCLASS(MinimalAPI) 13 | class UEdNode_GenericGraphNode : public UEdGraphNode 14 | { 15 | GENERATED_BODY() 16 | 17 | public: 18 | UEdNode_GenericGraphNode(); 19 | virtual ~UEdNode_GenericGraphNode(); 20 | 21 | UPROPERTY(VisibleAnywhere, Instanced, Category = "GenericGraph") 22 | TObjectPtr GenericGraphNode; 23 | 24 | void SetGenericGraphNode(UGenericGraphNode* InNode); 25 | UEdGraph_GenericGraph* GetGenericGraphEdGraph(); 26 | 27 | SEdNode_GenericGraphNode* SEdNode; 28 | 29 | virtual void AllocateDefaultPins() override; 30 | virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override; 31 | virtual void PrepareForCopying() override; 32 | virtual void AutowireNewNode(UEdGraphPin* FromPin) override; 33 | 34 | virtual FLinearColor GetBackgroundColor() const; 35 | virtual UEdGraphPin* GetInputPin() const; 36 | virtual UEdGraphPin* GetOutputPin() const; 37 | 38 | #if WITH_EDITOR 39 | virtual void PostEditUndo() override; 40 | #endif 41 | 42 | }; 43 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Public/GenericGraphAssetEditor/EditorCommands_GenericGraph.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "EditorStyleSet.h" 5 | #include "Framework/Commands/Commands.h" 6 | 7 | class GENERICGRAPHEDITOR_API FEditorCommands_GenericGraph : public TCommands 8 | { 9 | public: 10 | /** Constructor */ 11 | FEditorCommands_GenericGraph() 12 | : TCommands("GenericGraphEditor", NSLOCTEXT("Contexts", "GenericGraphEditor", "Generic Graph Editor"), NAME_None, FAppStyle::GetAppStyleSetName()) 13 | { 14 | } 15 | 16 | TSharedPtr GraphSettings; 17 | TSharedPtr AutoArrange; 18 | 19 | virtual void RegisterCommands() override; 20 | }; 21 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Public/GenericGraphAssetEditor/GenericGraphDragConnection.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Input/DragAndDrop.h" 7 | #include "Input/Reply.h" 8 | #include "Widgets/SWidget.h" 9 | #include "SGraphPin.h" 10 | #include "GraphEditorDragDropAction.h" 11 | 12 | class SGraphPanel; 13 | class UEdGraph; 14 | 15 | class FGenericGraphDragConnection : public FGraphEditorDragDropAction 16 | { 17 | public: 18 | DRAG_DROP_OPERATOR_TYPE(FDragConnection, FGraphEditorDragDropAction) 19 | 20 | typedef TArray FDraggedPinTable; 21 | static TSharedRef New(const TSharedRef& InGraphPanel, const FDraggedPinTable& InStartingPins); 22 | 23 | // FDragDropOperation interface 24 | virtual void OnDrop(bool bDropWasHandled, const FPointerEvent& MouseEvent) override; 25 | // End of FDragDropOperation interface 26 | 27 | // FGraphEditorDragDropAction interface 28 | virtual void HoverTargetChanged() override; 29 | virtual FReply DroppedOnPin(FVector2D ScreenPosition, FVector2D GraphPosition) override; 30 | virtual FReply DroppedOnNode(FVector2D ScreenPosition, FVector2D GraphPosition) override; 31 | virtual FReply DroppedOnPanel(const TSharedRef< SWidget >& Panel, FVector2D ScreenPosition, FVector2D GraphPosition, UEdGraph& Graph) override; 32 | virtual void OnDragged(const class FDragDropEvent& DragDropEvent) override; 33 | // End of FGraphEditorDragDropAction interface 34 | 35 | /* 36 | * Function to check validity of graph pins in the StartPins list. This check helps to prevent processing graph pins which are outdated. 37 | */ 38 | virtual void ValidateGraphPinList(TArray& OutValidPins); 39 | 40 | protected: 41 | typedef FGraphEditorDragDropAction Super; 42 | 43 | // Constructor: Make sure to call Construct() after factorying one of these 44 | FGenericGraphDragConnection(const TSharedRef& GraphPanel, const FDraggedPinTable& DraggedPins); 45 | 46 | protected: 47 | TSharedPtr GraphPanel; 48 | FDraggedPinTable DraggingPins; 49 | 50 | /** Offset information for the decorator widget */ 51 | FVector2D DecoratorAdjust; 52 | }; 53 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Public/GenericGraphAssetEditor/GenericGraphEditorStyle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "Styling/SlateStyle.h" 5 | 6 | class GENERICGRAPHEDITOR_API FGenericGraphEditorStyle 7 | { 8 | public: 9 | static void Initialize(); 10 | static void Shutdown(); 11 | 12 | static const FName& GetStyleSetName(); 13 | 14 | private: 15 | static TSharedPtr StyleSet; 16 | }; 17 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Public/GenericGraphAssetEditor/SEdNode_GenericGraphEdge.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "Styling/SlateColor.h" 5 | #include "Widgets/DeclarativeSyntaxSupport.h" 6 | #include "Widgets/SWidget.h" 7 | #include "SNodePanel.h" 8 | #include "SGraphNode.h" 9 | 10 | class SToolTip; 11 | class UEdNode_GenericGraphEdge; 12 | 13 | class GENERICGRAPHEDITOR_API SEdNode_GenericGraphEdge : public SGraphNode 14 | { 15 | public: 16 | SLATE_BEGIN_ARGS(SEdNode_GenericGraphEdge){} 17 | SLATE_END_ARGS() 18 | 19 | void Construct(const FArguments& InArgs, UEdNode_GenericGraphEdge* InNode); 20 | 21 | virtual bool RequiresSecondPassLayout() const override; 22 | virtual void PerformSecondPassLayout(const TMap< UObject*, TSharedRef >& NodeToWidgetLookup) const override; 23 | 24 | virtual void UpdateGraphNode() override; 25 | 26 | // Calculate position for multiple nodes to be placed between a start and end point, by providing this nodes index and max expected nodes 27 | void PositionBetweenTwoNodesWithOffset(const FGeometry& StartGeom, const FGeometry& EndGeom, int32 NodeIndex, int32 MaxNodes) const; 28 | 29 | void OnNameTextCommited(const FText& InText, ETextCommit::Type CommitInfo); 30 | 31 | protected: 32 | FSlateColor GetEdgeColor() const; 33 | 34 | const FSlateBrush* GetEdgeImage() const; 35 | 36 | EVisibility GetEdgeImageVisibility() const; 37 | EVisibility GetEdgeTitleVisbility() const; 38 | private: 39 | TSharedPtr TextEntryWidget; 40 | }; 41 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Public/GenericGraphAssetEditor/SEdNode_GenericGraphNode.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "SGraphNode.h" 5 | 6 | class UEdNode_GenericGraphNode; 7 | 8 | class GENERICGRAPHEDITOR_API SEdNode_GenericGraphNode : public SGraphNode 9 | { 10 | public: 11 | SLATE_BEGIN_ARGS(SEdNode_GenericGraphNode) {} 12 | SLATE_END_ARGS() 13 | 14 | void Construct(const FArguments& InArgs, UEdNode_GenericGraphNode* InNode); 15 | 16 | virtual void UpdateGraphNode() override; 17 | virtual void CreatePinWidgets() override; 18 | virtual void AddPin(const TSharedRef& PinToAdd) override; 19 | virtual bool IsNameReadOnly() const override; 20 | 21 | void OnNameTextCommited(const FText& InText, ETextCommit::Type CommitInfo); 22 | 23 | virtual FSlateColor GetBorderBackgroundColor() const; 24 | virtual FSlateColor GetBackgroundColor() const; 25 | 26 | virtual EVisibility GetDragOverMarkerVisibility() const; 27 | 28 | virtual const FSlateBrush* GetNameIcon() const; 29 | 30 | protected: 31 | }; 32 | 33 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Public/GenericGraphAssetEditor/Settings_GenericGraphEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "Settings_GenericGraphEditor.generated.h" 5 | 6 | UENUM(BlueprintType) 7 | enum class EAutoLayoutStrategy : uint8 8 | { 9 | Tree, 10 | ForceDirected, 11 | }; 12 | 13 | UCLASS() 14 | class GENERICGRAPHEDITOR_API UGenericGraphEditorSettings : public UObject 15 | { 16 | GENERATED_BODY() 17 | 18 | public: 19 | UGenericGraphEditorSettings(); 20 | virtual ~UGenericGraphEditorSettings(); 21 | 22 | UPROPERTY(EditDefaultsOnly, Category = "AutoArrange") 23 | float OptimalDistance; 24 | 25 | UPROPERTY(EditDefaultsOnly, AdvancedDisplay, Category = "AutoArrange") 26 | EAutoLayoutStrategy AutoLayoutStrategy; 27 | 28 | UPROPERTY(EditDefaultsOnly, AdvancedDisplay, Category = "AutoArrange") 29 | int32 MaxIteration; 30 | 31 | UPROPERTY(EditDefaultsOnly, AdvancedDisplay, Category = "AutoArrange") 32 | bool bFirstPassOnly; 33 | 34 | UPROPERTY(EditDefaultsOnly, AdvancedDisplay, Category = "AutoArrange") 35 | bool bRandomInit; 36 | 37 | UPROPERTY(EditDefaultsOnly, AdvancedDisplay, Category = "AutoArrange") 38 | float InitTemperature; 39 | 40 | UPROPERTY(EditDefaultsOnly, AdvancedDisplay, Category = "AutoArrange") 41 | float CoolDownRate; 42 | }; 43 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Public/GenericGraphEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Modules/ModuleManager.h" 3 | #include "GenericGraphEditorModule.h" 4 | #include 5 | #include 6 | 7 | class FGenericGraphEditor : public IGenericGraphEditor 8 | { 9 | /** IModuleInterface implementation */ 10 | virtual void StartupModule() override; 11 | virtual void ShutdownModule() override; 12 | 13 | 14 | private: 15 | void RegisterAssetTypeAction(IAssetTools& AssetTools, TSharedRef Action); 16 | 17 | private: 18 | TArray< TSharedPtr > CreatedAssetTypeActions; 19 | 20 | EAssetTypeCategories::Type GenericGraphAssetCategoryBit; 21 | 22 | TSharedPtr GraphPanelNodeFactory_GenericGraph; 23 | }; -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Public/GenericGraphEditorModule.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Modules/ModuleManager.h" 4 | 5 | DECLARE_LOG_CATEGORY_EXTERN(GenericGraphEditor, Log, All); 6 | 7 | /** 8 | * The public interface to this module 9 | */ 10 | class IGenericGraphEditor : public IModuleInterface 11 | { 12 | 13 | public: 14 | 15 | /** 16 | * Singleton-like access to this module's interface. This is just for convenience! 17 | * Beware of calling this during the shutdown phase, though. Your module might have been unloaded already. 18 | * 19 | * @return Returns singleton instance, loading the module on demand if needed 20 | */ 21 | static IGenericGraphEditor& Get() 22 | { 23 | return FModuleManager::Get().LoadModuleChecked< IGenericGraphEditor >("GenericGraphEditor"); 24 | } 25 | 26 | /** 27 | * Checks to see if this module is loaded and ready. It is only valid to call Get() if IsAvailable() returns true. 28 | * 29 | * @return True if the module is loaded and ready to use 30 | */ 31 | static bool IsAvailable() 32 | { 33 | return FModuleManager::Get().IsModuleLoaded("GenericGraphEditor"); 34 | } 35 | }; 36 | 37 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Public/GenericGraphEditorPCH.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "GenericGraph.h" 4 | #include "GenericGraphNode.h" 5 | #include "GenericGraphEdge.h" 6 | 7 | // You should place include statements to your module's private header files here. You only need to 8 | // add includes for headers that are used in most of your module's source files though. 9 | #include "GenericGraphEditor.h" 10 | 11 | #define LOG_INFO(FMT, ...) UE_LOG(GenericGraphEditor, Display, (FMT), ##__VA_ARGS__) 12 | #define LOG_WARNING(FMT, ...) UE_LOG(GenericGraphEditor, Warning, (FMT), ##__VA_ARGS__) 13 | #define LOG_ERROR(FMT, ...) UE_LOG(GenericGraphEditor, Error, (FMT), ##__VA_ARGS__) 14 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Public/GenericGraphFactory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "Factories/Factory.h" 5 | #include "GenericGraph.h" 6 | #include "GenericGraphFactory.generated.h" 7 | 8 | UCLASS() 9 | class GENERICGRAPHEDITOR_API UGenericGraphFactory : public UFactory 10 | { 11 | GENERATED_BODY() 12 | 13 | public: 14 | UGenericGraphFactory(); 15 | virtual ~UGenericGraphFactory(); 16 | 17 | UPROPERTY(EditAnywhere, Category=DataAsset) 18 | TSubclassOf GenericGraphClass; 19 | 20 | virtual bool ConfigureProperties() override; 21 | virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; 22 | }; 23 | -------------------------------------------------------------------------------- /Source/GenericGraphEditor/Public/GenericGraphNodeFactory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | class FGraphPanelNodeFactory_GenericGraph : public FGraphPanelNodeFactory 6 | { 7 | virtual TSharedPtr CreateNode(UEdGraphNode* Node) const override; 8 | }; -------------------------------------------------------------------------------- /Source/GenericGraphRuntime/GenericGraphRuntime.Build.cs: -------------------------------------------------------------------------------- 1 | using UnrealBuildTool; 2 | 3 | public class GenericGraphRuntime : ModuleRules 4 | { 5 | public GenericGraphRuntime(ReadOnlyTargetRules Target) : base(Target) 6 | { 7 | PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; 8 | bLegacyPublicIncludePaths = false; 9 | ShadowVariableWarningLevel = WarningLevel.Error; 10 | 11 | PublicIncludePaths.AddRange( 12 | new string[] { 13 | // ... add public include paths required here ... 14 | } 15 | ); 16 | 17 | PrivateIncludePaths.AddRange( 18 | new string[] { 19 | "GenericGraphRuntime/Private", 20 | // ... add other private include paths required here ... 21 | } 22 | ); 23 | 24 | PublicDependencyModuleNames.AddRange( 25 | new string[] 26 | { 27 | "Core", 28 | "CoreUObject", 29 | "Engine", 30 | // ... add other public dependencies that you statically link with here ... 31 | } 32 | ); 33 | 34 | PrivateDependencyModuleNames.AddRange( 35 | new string[] 36 | { 37 | // ... add private dependencies that you statically link with here ... 38 | "Slate", 39 | "SlateCore", 40 | "GameplayTags" 41 | } 42 | ); 43 | 44 | DynamicallyLoadedModuleNames.AddRange( 45 | new string[] 46 | { 47 | // ... add any modules that your module loads dynamically here ... 48 | } 49 | ); 50 | } 51 | } -------------------------------------------------------------------------------- /Source/GenericGraphRuntime/Private/GenericGraph.cpp: -------------------------------------------------------------------------------- 1 | #include "GenericGraph.h" 2 | #include "GenericGraphRuntimePCH.h" 3 | #include "Engine/Engine.h" 4 | 5 | #define LOCTEXT_NAMESPACE "GenericGraph" 6 | 7 | UGenericGraph::UGenericGraph() 8 | { 9 | NodeType = UGenericGraphNode::StaticClass(); 10 | EdgeType = UGenericGraphEdge::StaticClass(); 11 | 12 | bEdgeEnabled = true; 13 | 14 | #if WITH_EDITORONLY_DATA 15 | EdGraph = nullptr; 16 | 17 | bCanRenameNode = true; 18 | #endif 19 | } 20 | 21 | UGenericGraph::~UGenericGraph() 22 | { 23 | 24 | } 25 | 26 | void UGenericGraph::Print(bool ToConsole /*= true*/, bool ToScreen /*= true*/) 27 | { 28 | int Level = 0; 29 | TArray CurrLevelNodes = RootNodes; 30 | TArray NextLevelNodes; 31 | 32 | while (CurrLevelNodes.Num() != 0) 33 | { 34 | for (int i = 0; i < CurrLevelNodes.Num(); ++i) 35 | { 36 | UGenericGraphNode* Node = CurrLevelNodes[i]; 37 | check(Node != nullptr); 38 | 39 | FString Message = FString::Printf(TEXT("%s, Level %d"), *Node->GetDescription().ToString(), Level); 40 | 41 | if (ToConsole) 42 | { 43 | LOG_INFO(TEXT("%s"), *Message); 44 | } 45 | 46 | if (ToScreen && GEngine != nullptr) 47 | { 48 | GEngine->AddOnScreenDebugMessage(-1, 15.f, FColor::Blue, Message); 49 | } 50 | 51 | for (int j = 0; j < Node->ChildrenNodes.Num(); ++j) 52 | { 53 | NextLevelNodes.Add(Node->ChildrenNodes[j]); 54 | } 55 | } 56 | 57 | CurrLevelNodes = NextLevelNodes; 58 | NextLevelNodes.Reset(); 59 | ++Level; 60 | } 61 | } 62 | 63 | int UGenericGraph::GetLevelNum() const 64 | { 65 | int Level = 0; 66 | TArray CurrLevelNodes = RootNodes; 67 | TArray NextLevelNodes; 68 | 69 | while (CurrLevelNodes.Num() != 0) 70 | { 71 | for (int i = 0; i < CurrLevelNodes.Num(); ++i) 72 | { 73 | UGenericGraphNode* Node = CurrLevelNodes[i]; 74 | check(Node != nullptr); 75 | 76 | for (int j = 0; j < Node->ChildrenNodes.Num(); ++j) 77 | { 78 | NextLevelNodes.Add(Node->ChildrenNodes[j]); 79 | } 80 | } 81 | 82 | CurrLevelNodes = NextLevelNodes; 83 | NextLevelNodes.Reset(); 84 | ++Level; 85 | } 86 | 87 | return Level; 88 | } 89 | 90 | void UGenericGraph::GetNodesByLevel(int Level, TArray& Nodes) 91 | { 92 | int CurrLEvel = 0; 93 | TArray NextLevelNodes; 94 | 95 | Nodes = RootNodes; 96 | 97 | while (Nodes.Num() != 0) 98 | { 99 | if (CurrLEvel == Level) 100 | break; 101 | 102 | for (int i = 0; i < Nodes.Num(); ++i) 103 | { 104 | UGenericGraphNode* Node = Nodes[i]; 105 | check(Node != nullptr); 106 | 107 | for (int j = 0; j < Node->ChildrenNodes.Num(); ++j) 108 | { 109 | NextLevelNodes.Add(Node->ChildrenNodes[j]); 110 | } 111 | } 112 | 113 | Nodes = NextLevelNodes; 114 | NextLevelNodes.Reset(); 115 | ++CurrLEvel; 116 | } 117 | } 118 | 119 | void UGenericGraph::ClearGraph() 120 | { 121 | for (int i = 0; i < AllNodes.Num(); ++i) 122 | { 123 | UGenericGraphNode* Node = AllNodes[i]; 124 | if (Node) 125 | { 126 | Node->ParentNodes.Empty(); 127 | Node->ChildrenNodes.Empty(); 128 | Node->Edges.Empty(); 129 | } 130 | } 131 | 132 | AllNodes.Empty(); 133 | RootNodes.Empty(); 134 | } 135 | 136 | #undef LOCTEXT_NAMESPACE 137 | -------------------------------------------------------------------------------- /Source/GenericGraphRuntime/Private/GenericGraphEdge.cpp: -------------------------------------------------------------------------------- 1 | #include "GenericGraphEdge.h" 2 | 3 | UGenericGraphEdge::UGenericGraphEdge() 4 | { 5 | 6 | } 7 | 8 | UGenericGraphEdge::~UGenericGraphEdge() 9 | { 10 | 11 | } 12 | 13 | UGenericGraph* UGenericGraphEdge::GetGraph() const 14 | { 15 | return Graph; 16 | } 17 | 18 | #if WITH_EDITOR 19 | void UGenericGraphEdge::SetNodeTitle(const FText& NewTitle) 20 | { 21 | NodeTitle = NewTitle; 22 | } 23 | #endif // #if WITH_EDITOR -------------------------------------------------------------------------------- /Source/GenericGraphRuntime/Private/GenericGraphNode.cpp: -------------------------------------------------------------------------------- 1 | #include "GenericGraphNode.h" 2 | #include "GenericGraph.h" 3 | 4 | #define LOCTEXT_NAMESPACE "GenericGraphNode" 5 | 6 | UGenericGraphNode::UGenericGraphNode() 7 | { 8 | #if WITH_EDITORONLY_DATA 9 | CompatibleGraphType = UGenericGraph::StaticClass(); 10 | 11 | BackgroundColor = FLinearColor::Black; 12 | #endif 13 | } 14 | 15 | UGenericGraphNode::~UGenericGraphNode() 16 | { 17 | } 18 | 19 | UGenericGraphEdge* UGenericGraphNode::GetEdge(UGenericGraphNode* ChildNode) 20 | { 21 | return Edges.Contains(ChildNode) ? Edges.FindChecked(ChildNode) : nullptr; 22 | } 23 | 24 | FText UGenericGraphNode::GetDescription_Implementation() const 25 | { 26 | return LOCTEXT("NodeDesc", "Generic Graph Node"); 27 | } 28 | 29 | #if WITH_EDITOR 30 | bool UGenericGraphNode::IsNameEditable() const 31 | { 32 | return true; 33 | } 34 | 35 | FLinearColor UGenericGraphNode::GetBackgroundColor() const 36 | { 37 | return BackgroundColor; 38 | } 39 | 40 | FText UGenericGraphNode::GetNodeTitle() const 41 | { 42 | return NodeTitle.IsEmpty() ? GetDescription() : NodeTitle; 43 | } 44 | 45 | void UGenericGraphNode::SetNodeTitle(const FText& NewTitle) 46 | { 47 | NodeTitle = NewTitle; 48 | } 49 | 50 | bool UGenericGraphNode::CanCreateConnection(UGenericGraphNode* Other, FText& ErrorMessage) 51 | { 52 | return true; 53 | } 54 | 55 | bool UGenericGraphNode::CanCreateConnectionTo(UGenericGraphNode* Other, int32 NumberOfChildrenNodes, FText& ErrorMessage) 56 | { 57 | if (ChildrenLimitType == ENodeLimit::Limited && NumberOfChildrenNodes >= ChildrenLimit) 58 | { 59 | ErrorMessage = FText::FromString("Children limit exceeded"); 60 | return false; 61 | } 62 | 63 | return CanCreateConnection(Other, ErrorMessage); 64 | } 65 | 66 | bool UGenericGraphNode::CanCreateConnectionFrom(UGenericGraphNode* Other, int32 NumberOfParentNodes, FText& ErrorMessage) 67 | { 68 | if (ParentLimitType == ENodeLimit::Limited && NumberOfParentNodes >= ParentLimit) 69 | { 70 | ErrorMessage = FText::FromString("Parent limit exceeded"); 71 | return false; 72 | } 73 | 74 | return true; 75 | } 76 | 77 | 78 | #endif 79 | 80 | bool UGenericGraphNode::IsLeafNode() const 81 | { 82 | return ChildrenNodes.Num() == 0; 83 | } 84 | 85 | UGenericGraph* UGenericGraphNode::GetGraph() const 86 | { 87 | return Graph; 88 | } 89 | 90 | #undef LOCTEXT_NAMESPACE 91 | -------------------------------------------------------------------------------- /Source/GenericGraphRuntime/Private/GenericGraphRuntime.cpp: -------------------------------------------------------------------------------- 1 | #include "GenericGraphRuntimePCH.h" 2 | 3 | DEFINE_LOG_CATEGORY(GenericGraphRuntime) 4 | 5 | class FGenericGraphRuntime : public IGenericGraphRuntime 6 | { 7 | /** IModuleInterface implementation */ 8 | virtual void StartupModule() override; 9 | virtual void ShutdownModule() override; 10 | }; 11 | 12 | IMPLEMENT_MODULE( FGenericGraphRuntime, GenericGraphRuntime ) 13 | 14 | 15 | 16 | void FGenericGraphRuntime::StartupModule() 17 | { 18 | // This code will execute after your module is loaded into memory (but after global variables are initialized, of course.) 19 | } 20 | 21 | 22 | void FGenericGraphRuntime::ShutdownModule() 23 | { 24 | // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, 25 | // we call this function before unloading the module. 26 | } 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Source/GenericGraphRuntime/Private/GenericGraphRuntimePCH.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // #include "CoreUObject.h" 4 | // #include "Engine.h" 5 | 6 | // You should place include statements to your module's private header files here. You only need to 7 | // add includes for headers that are used in most of your module's source files though. 8 | #include "IGenericGraphRuntime.h" 9 | 10 | #define LOG_INFO(FMT, ...) UE_LOG(GenericGraphRuntime, Display, (FMT), ##__VA_ARGS__) 11 | #define LOG_WARNING(FMT, ...) UE_LOG(GenericGraphRuntime, Warning, (FMT), ##__VA_ARGS__) 12 | #define LOG_ERROR(FMT, ...) UE_LOG(GenericGraphRuntime, Error, (FMT), ##__VA_ARGS__) 13 | -------------------------------------------------------------------------------- /Source/GenericGraphRuntime/Public/GenericGraph.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "GenericGraphNode.h" 5 | #include "GenericGraphEdge.h" 6 | #include "GameplayTagContainer.h" 7 | #include "GenericGraph.generated.h" 8 | 9 | UCLASS(Blueprintable) 10 | class GENERICGRAPHRUNTIME_API UGenericGraph : public UObject 11 | { 12 | GENERATED_BODY() 13 | 14 | public: 15 | UGenericGraph(); 16 | virtual ~UGenericGraph(); 17 | 18 | UPROPERTY(EditDefaultsOnly, Category = "GenericGraph") 19 | FString Name; 20 | 21 | UPROPERTY(EditDefaultsOnly, Category = "GenericGraph") 22 | TSubclassOf NodeType; 23 | 24 | UPROPERTY(EditDefaultsOnly, Category = "GenericGraph") 25 | TSubclassOf EdgeType; 26 | 27 | UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "GenericGraph") 28 | FGameplayTagContainer GraphTags; 29 | 30 | UPROPERTY(BlueprintReadOnly, Category = "GenericGraph") 31 | TArray RootNodes; 32 | 33 | UPROPERTY(BlueprintReadOnly, Category = "GenericGraph") 34 | TArray AllNodes; 35 | 36 | UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "GenericGraph") 37 | bool bEdgeEnabled; 38 | 39 | UFUNCTION(BlueprintCallable, Category = "GenericGraph") 40 | void Print(bool ToConsole = true, bool ToScreen = true); 41 | 42 | UFUNCTION(BlueprintCallable, Category = "GenericGraph") 43 | int GetLevelNum() const; 44 | 45 | UFUNCTION(BlueprintCallable, Category = "GenericGraph") 46 | void GetNodesByLevel(int Level, TArray& Nodes); 47 | 48 | void ClearGraph(); 49 | 50 | #if WITH_EDITORONLY_DATA 51 | UPROPERTY() 52 | TObjectPtr EdGraph; 53 | 54 | UPROPERTY(EditDefaultsOnly, Category = "GenericGraph_Editor") 55 | bool bCanRenameNode; 56 | 57 | UPROPERTY(EditDefaultsOnly, Category = "GenericGraph_Editor") 58 | bool bCanBeCyclical; 59 | #endif 60 | }; 61 | -------------------------------------------------------------------------------- /Source/GenericGraphRuntime/Public/GenericGraphEdge.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "GenericGraphNode.h" 5 | #include "GenericGraphEdge.generated.h" 6 | 7 | class UGenericGraph; 8 | 9 | UCLASS(Blueprintable) 10 | class GENERICGRAPHRUNTIME_API UGenericGraphEdge : public UObject 11 | { 12 | GENERATED_BODY() 13 | 14 | public: 15 | UGenericGraphEdge(); 16 | virtual ~UGenericGraphEdge(); 17 | 18 | UPROPERTY(VisibleAnywhere, Category = "GenericGraphNode") 19 | TObjectPtr Graph; 20 | 21 | UPROPERTY(BlueprintReadOnly, Category = "GenericGraphEdge") 22 | TObjectPtr StartNode; 23 | 24 | UPROPERTY(BlueprintReadOnly, Category = "GenericGraphEdge") 25 | TObjectPtr EndNode; 26 | 27 | UFUNCTION(BlueprintPure, Category = "GenericGraphEdge") 28 | UGenericGraph* GetGraph() const; 29 | 30 | #if WITH_EDITORONLY_DATA 31 | UPROPERTY(EditDefaultsOnly, Category = "GenericGraphNode_Editor") 32 | bool bShouldDrawTitle = false; 33 | 34 | UPROPERTY(EditDefaultsOnly, Category = "GenericGraphNode_Editor") 35 | FText NodeTitle; 36 | 37 | UPROPERTY(EditDefaultsOnly, Category = "GenericGraphEdge") 38 | FLinearColor EdgeColour = FLinearColor(0.9f, 0.9f, 0.9f, 1.0f); 39 | #endif 40 | 41 | #if WITH_EDITOR 42 | virtual FText GetNodeTitle() const { return NodeTitle; } 43 | FLinearColor GetEdgeColour() { return EdgeColour; } 44 | 45 | virtual void SetNodeTitle(const FText& NewTitle); 46 | #endif 47 | 48 | }; 49 | -------------------------------------------------------------------------------- /Source/GenericGraphRuntime/Public/GenericGraphNode.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "Templates/SubclassOf.h" 5 | #include "GenericGraphNode.generated.h" 6 | 7 | class UGenericGraph; 8 | class UGenericGraphEdge; 9 | 10 | UENUM(BlueprintType) 11 | enum class ENodeLimit : uint8 12 | { 13 | Unlimited, 14 | Limited 15 | }; 16 | 17 | 18 | UCLASS(Blueprintable) 19 | class GENERICGRAPHRUNTIME_API UGenericGraphNode : public UObject 20 | { 21 | GENERATED_BODY() 22 | 23 | public: 24 | UGenericGraphNode(); 25 | virtual ~UGenericGraphNode(); 26 | 27 | UPROPERTY(VisibleDefaultsOnly, Category = "GenericGraphNode") 28 | TObjectPtr Graph; 29 | 30 | UPROPERTY(BlueprintReadOnly, Category = "GenericGraphNode") 31 | TArray ParentNodes; 32 | 33 | UPROPERTY(BlueprintReadOnly, Category = "GenericGraphNode") 34 | TArray ChildrenNodes; 35 | 36 | UPROPERTY(BlueprintReadOnly, Category = "GenericGraphNode") 37 | TMap Edges; 38 | 39 | UFUNCTION(BlueprintCallable, Category = "GenericGraphNode") 40 | virtual UGenericGraphEdge* GetEdge(UGenericGraphNode* ChildNode); 41 | 42 | UFUNCTION(BlueprintCallable, Category = "GenericGraphNode") 43 | bool IsLeafNode() const; 44 | 45 | UFUNCTION(BlueprintCallable, Category = "GenericGraphNode") 46 | UGenericGraph* GetGraph() const; 47 | 48 | UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "GenericGraphNode") 49 | FText GetDescription() const; 50 | virtual FText GetDescription_Implementation() const; 51 | 52 | ////////////////////////////////////////////////////////////////////////// 53 | #if WITH_EDITORONLY_DATA 54 | UPROPERTY(EditDefaultsOnly, Category = "GenericGraphNode_Editor") 55 | FText NodeTitle; 56 | 57 | UPROPERTY(VisibleDefaultsOnly, Category = "GenericGraphNode_Editor") 58 | TSubclassOf CompatibleGraphType; 59 | 60 | UPROPERTY(EditDefaultsOnly, Category = "GenericGraphNode_Editor") 61 | FLinearColor BackgroundColor; 62 | 63 | UPROPERTY(EditDefaultsOnly, Category = "GenericGraphNode_Editor") 64 | FText ContextMenuName; 65 | 66 | UPROPERTY(EditDefaultsOnly, Category = "GenericGraphNode_Editor") 67 | ENodeLimit ParentLimitType; 68 | 69 | UPROPERTY(EditDefaultsOnly, Category = "GenericGraphNode_Editor" ,meta = (ClampMin = "0",EditCondition = "ParentLimitType == ENodeLimit::Limited", EditConditionHides)) 70 | int32 ParentLimit; 71 | 72 | UPROPERTY(EditDefaultsOnly, Category = "GenericGraphNode_Editor") 73 | ENodeLimit ChildrenLimitType; 74 | 75 | UPROPERTY(EditDefaultsOnly, Category = "GenericGraphNode_Editor" ,meta = (ClampMin = "0",EditCondition = "ChildrenLimitType == ENodeLimit::Limited", EditConditionHides)) 76 | int32 ChildrenLimit; 77 | 78 | #endif 79 | 80 | #if WITH_EDITOR 81 | virtual bool IsNameEditable() const; 82 | 83 | virtual FLinearColor GetBackgroundColor() const; 84 | 85 | virtual FText GetNodeTitle() const; 86 | 87 | virtual void SetNodeTitle(const FText& NewTitle); 88 | 89 | virtual bool CanCreateConnection(UGenericGraphNode* Other, FText& ErrorMessage); 90 | 91 | virtual bool CanCreateConnectionTo(UGenericGraphNode* Other, int32 NumberOfChildrenNodes, FText& ErrorMessage); 92 | virtual bool CanCreateConnectionFrom(UGenericGraphNode* Other, int32 NumberOfParentNodes, FText& ErrorMessage); 93 | #endif 94 | }; 95 | -------------------------------------------------------------------------------- /Source/GenericGraphRuntime/Public/IGenericGraphRuntime.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Modules/ModuleManager.h" 4 | 5 | DECLARE_LOG_CATEGORY_EXTERN(GenericGraphRuntime, Log, All); 6 | 7 | /** 8 | * The public interface to this module 9 | */ 10 | class IGenericGraphRuntime : public IModuleInterface 11 | { 12 | 13 | public: 14 | 15 | /** 16 | * Singleton-like access to this module's interface. This is just for convenience! 17 | * Beware of calling this during the shutdown phase, though. Your module might have been unloaded already. 18 | * 19 | * @return Returns singleton instance, loading the module on demand if needed 20 | */ 21 | static IGenericGraphRuntime& Get() 22 | { 23 | return FModuleManager::LoadModuleChecked< IGenericGraphRuntime >("GenericGraphRuntime"); 24 | } 25 | 26 | /** 27 | * Checks to see if this module is loaded and ready. It is only valid to call Get() if IsAvailable() returns true. 28 | * 29 | * @return True if the module is loaded and ready to use 30 | */ 31 | static bool IsAvailable() 32 | { 33 | return FModuleManager::Get().IsModuleLoaded( "GenericGraphRuntime" ); 34 | } 35 | }; 36 | 37 | -------------------------------------------------------------------------------- /docs/images/GenericGraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProjectBorealis/GenericGraph/e9a606dfd2ff4e9ca33ca44e6d835274c708dff4/docs/images/GenericGraph.png -------------------------------------------------------------------------------- /docs/images/ability-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProjectBorealis/GenericGraph/e9a606dfd2ff4e9ca33ca44e6d835274c708dff4/docs/images/ability-graph.png -------------------------------------------------------------------------------- /docs/images/dialogue/dialogue01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProjectBorealis/GenericGraph/e9a606dfd2ff4e9ca33ca44e6d835274c708dff4/docs/images/dialogue/dialogue01.png -------------------------------------------------------------------------------- /docs/images/dialogue/dialogue02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProjectBorealis/GenericGraph/e9a606dfd2ff4e9ca33ca44e6d835274c708dff4/docs/images/dialogue/dialogue02.png -------------------------------------------------------------------------------- /docs/images/dialogue/dialogue03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProjectBorealis/GenericGraph/e9a606dfd2ff4e9ca33ca44e6d835274c708dff4/docs/images/dialogue/dialogue03.png -------------------------------------------------------------------------------- /readme.rst: -------------------------------------------------------------------------------- 1 | GenericGraphPlugin 2 | ================== 3 | 4 | Generic graph data structure plugin for ue4 5 | 6 | .. image:: docs/images/GenericGraph.png 7 | 8 | Feature 9 | ------- 10 | 11 | * Custom asset type 12 | * UE4 BehaviorTree-like asset editor 13 | * Extendable graph node type 14 | * Extendable graph edge type 15 | * Extendable graph type(new asset type with generic graph editor, C++ only) 16 | 17 | Usage 18 | ----- 19 | 20 | * Ability system 21 | * Dialogue system 22 | * Quest system 23 | * Etc 24 | 25 | Install 26 | ------- 27 | 28 | #. Clone this project to ${YourProject}/Plugins/ 29 | #. Generate project file 30 | #. Compile 31 | 32 | Tutorial 33 | -------- 34 | 35 | `Dialogue System`_ (WIP) 36 | 37 | Example 38 | ------- 39 | 40 | Dialogue System and ability system: SRPGTemplate_ 41 | 42 | .. image:: docs/images/dialogue/dialogue01.png 43 | 44 | .. image:: docs/images/dialogue/dialogue02.png 45 | 46 | .. image:: docs/images/dialogue/dialogue03.png 47 | 48 | .. image:: docs/images/ability-graph.png 49 | 50 | .. _Dialogue System: https://jinyuliao.github.io/blog/html/2017/12/15/ue4_dialogue_system_part1.html 51 | .. _SRPGTemplate: https://github.com/jinyuliao/SRPGTemplate 52 | --------------------------------------------------------------------------------