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