From 0be7ca327dd39d5e22396a3f54e4220b3db4b3d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Fri, 2 Sep 2022 13:54:13 +0200 Subject: [PATCH 001/485] missing access specifier --- Source/FlowEditor/Public/Asset/FlowAssetEditor.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index cd3bca47e..7b4d280f0 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -23,6 +23,7 @@ struct Rect; class FFlowAssetEditor : public FAssetEditorToolkit, public FEditorUndoClient, public FGCObject, public FNotifyHook { +protected: /** The FlowAsset asset being inspected */ UFlowAsset* FlowAsset; From 17f92ab4907c57bb387931ca7c5e1aa83759fd74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Fri, 2 Sep 2022 13:59:33 +0200 Subject: [PATCH 002/485] exposed Initialize/Deinitialize Instance as virtual methods --- Source/Flow/Private/FlowAsset.cpp | 21 ++++++++++++++++----- Source/Flow/Public/FlowAsset.h | 5 +++-- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 7744705f8..62233e8b4 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -295,6 +295,18 @@ void UFlowAsset::InitializeInstance(const TWeakObjectPtr InOwner, UFlow } } +void UFlowAsset::DeinitializeInstance() +{ + if (TemplateAsset) + { + const int32 ActiveInstancesLeft = TemplateAsset->RemoveInstance(this); + if (ActiveInstancesLeft == 0 && GetFlowSubsystem()) + { + GetFlowSubsystem()->RemoveInstancedTemplate(TemplateAsset); + } + } +} + void UFlowAsset::PreloadNodes() { TArray GraphEntryNodes = {StartNode}; @@ -353,7 +365,7 @@ void UFlowAsset::StartFlow() StartNode->TriggerFirstOutput(true); } -void UFlowAsset::FinishFlow(const EFlowFinishPolicy InFinishPolicy) +void UFlowAsset::FinishFlow(const EFlowFinishPolicy InFinishPolicy, const bool bRemoveInstance /*= true*/) { FinishPolicy = InFinishPolicy; @@ -371,11 +383,10 @@ void UFlowAsset::FinishFlow(const EFlowFinishPolicy InFinishPolicy) } PreloadedNodes.Empty(); - // clear instance entries - const int32 ActiveInstancesLeft = TemplateAsset->RemoveInstance(this); - if (ActiveInstancesLeft == 0 && GetFlowSubsystem()) + // provides option to finish game-specific logic prior to removing asset instance + if (bRemoveInstance) { - GetFlowSubsystem()->RemoveInstancedTemplate(TemplateAsset); + DeinitializeInstance(); } } diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index f0e3c0363..b067d533a 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -207,7 +207,8 @@ class FLOW_API UFlowAsset : public UObject EFlowFinishPolicy FinishPolicy; public: - void InitializeInstance(const TWeakObjectPtr InOwner, UFlowAsset* InTemplateAsset); + virtual void InitializeInstance(const TWeakObjectPtr InOwner, UFlowAsset* InTemplateAsset); + virtual void DeinitializeInstance(); UFlowAsset* GetTemplateAsset() const { return TemplateAsset; } @@ -227,7 +228,7 @@ class FLOW_API UFlowAsset : public UObject virtual void PreStartFlow(); virtual void StartFlow(); - virtual void FinishFlow(const EFlowFinishPolicy InFinishPolicy); + virtual void FinishFlow(const EFlowFinishPolicy InFinishPolicy, const bool bRemoveInstance = true); // Get Flow Asset instance created by the given SubGraph node TWeakObjectPtr GetFlowInstance(UFlowNode_SubGraph* SubGraphNode) const; From 0cc20868a0b61c5bb50def231b30e50cf183783c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Fri, 2 Sep 2022 19:44:03 +0200 Subject: [PATCH 003/485] added export macro to every editor class that might be theoretically extended or called --- .../FlowEditor/Public/Asset/AssetTypeActions_FlowAsset.h | 2 +- Source/FlowEditor/Public/Asset/FlowAssetEditor.h | 2 +- Source/FlowEditor/Public/Asset/FlowAssetIndexer.h | 2 +- Source/FlowEditor/Public/Asset/FlowAssetToolbar.h | 8 ++++---- Source/FlowEditor/Public/Asset/FlowDebugger.h | 2 +- Source/FlowEditor/Public/FlowEditorCommands.h | 2 +- Source/FlowEditor/Public/FlowEditorStyle.h | 2 +- Source/FlowEditor/Public/Graph/FlowGraph.h | 2 +- .../Public/Graph/FlowGraphConnectionDrawingPolicy.h | 4 ++-- Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h | 2 +- .../Public/Graph/Nodes/FlowGraphNode_ExecutionSequence.h | 2 +- Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h | 4 ++-- .../Public/Graph/Widgets/SFlowGraphNode_Finish.h | 2 +- .../Public/Graph/Widgets/SFlowGraphNode_Start.h | 2 +- .../Public/Graph/Widgets/SFlowGraphNode_SubGraph.h | 2 +- Source/FlowEditor/Public/Graph/Widgets/SFlowPalette.h | 4 ++-- Source/FlowEditor/Public/LevelEditor/SLevelEditorFlow.h | 2 +- .../Public/Nodes/AssetTypeActions_FlowNodeBlueprint.h | 2 +- 18 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Source/FlowEditor/Public/Asset/AssetTypeActions_FlowAsset.h b/Source/FlowEditor/Public/Asset/AssetTypeActions_FlowAsset.h index b6f28e659..bee8c6b8f 100644 --- a/Source/FlowEditor/Public/Asset/AssetTypeActions_FlowAsset.h +++ b/Source/FlowEditor/Public/Asset/AssetTypeActions_FlowAsset.h @@ -5,7 +5,7 @@ #include "AssetTypeActions_Base.h" #include "Toolkits/IToolkitHost.h" -class FAssetTypeActions_FlowAsset : public FAssetTypeActions_Base +class FLOWEDITOR_API FAssetTypeActions_FlowAsset : public FAssetTypeActions_Base { public: virtual FText GetName() const override; diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index 7b4d280f0..63dd36cf7 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -21,7 +21,7 @@ struct FSlateBrush; struct FPropertyChangedEvent; struct Rect; -class FFlowAssetEditor : public FAssetEditorToolkit, public FEditorUndoClient, public FGCObject, public FNotifyHook +class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEditorUndoClient, public FGCObject, public FNotifyHook { protected: /** The FlowAsset asset being inspected */ diff --git a/Source/FlowEditor/Public/Asset/FlowAssetIndexer.h b/Source/FlowEditor/Public/Asset/FlowAssetIndexer.h index 859b6c177..afdb90c89 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetIndexer.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetIndexer.h @@ -12,7 +12,7 @@ class FSearchSerializer; * Documentation: https://github.com/MothCocoon/FlowGraph/wiki/Asset-Search * Uncomment entire class, if you made these changes to the engine: https://github.com/EpicGames/UnrealEngine/pull/9070 */ -/*class FFlowAssetIndexer : public IAssetIndexer +/*class FLOWEDITOR_API FFlowAssetIndexer : public IAssetIndexer { public: virtual FString GetName() const override { return TEXT("FlowAsset"); } diff --git a/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h b/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h index 1b0269ad7..1676dcbba 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h @@ -12,7 +12,7 @@ class FFlowAssetEditor; ////////////////////////////////////////////////////////////////////////// // Flow Asset Instance List -class SFlowAssetInstanceList final : public SCompoundWidget +class FLOWEDITOR_API SFlowAssetInstanceList final : public SCompoundWidget { public: SLATE_BEGIN_ARGS(SFlowAssetInstanceList) {} @@ -43,7 +43,7 @@ class SFlowAssetInstanceList final : public SCompoundWidget /** * The kind of breadcrumbs that Flow Debugger uses */ -struct FFlowBreadcrumb +struct FLOWEDITOR_API FFlowBreadcrumb { FString AssetPathName; FName InstanceName; @@ -59,7 +59,7 @@ struct FFlowBreadcrumb {} }; -class SFlowAssetBreadcrumb final : public SCompoundWidget +class FLOWEDITOR_API SFlowAssetBreadcrumb final : public SCompoundWidget { public: SLATE_BEGIN_ARGS(SFlowAssetInstanceList) {} @@ -78,7 +78,7 @@ class SFlowAssetBreadcrumb final : public SCompoundWidget ////////////////////////////////////////////////////////////////////////// // Flow Asset Toolbar -class FFlowAssetToolbar final : public TSharedFromThis +class FLOWEDITOR_API FFlowAssetToolbar final : public TSharedFromThis { public: explicit FFlowAssetToolbar(const TSharedPtr InAssetEditor, UToolMenu* ToolbarMenu); diff --git a/Source/FlowEditor/Public/Asset/FlowDebugger.h b/Source/FlowEditor/Public/Asset/FlowDebugger.h index 1487e20c9..75a5ef47b 100644 --- a/Source/FlowEditor/Public/Asset/FlowDebugger.h +++ b/Source/FlowEditor/Public/Asset/FlowDebugger.h @@ -8,7 +8,7 @@ ** Minimalistic form of breakpoint debugger ** See BehaviorTreeDebugger for a more complex example */ -class FFlowDebugger +class FLOWEDITOR_API FFlowDebugger { public: FFlowDebugger(); diff --git a/Source/FlowEditor/Public/FlowEditorCommands.h b/Source/FlowEditor/Public/FlowEditorCommands.h index 6f9defc3e..f3f9caf2c 100644 --- a/Source/FlowEditor/Public/FlowEditorCommands.h +++ b/Source/FlowEditor/Public/FlowEditorCommands.h @@ -7,7 +7,7 @@ #include "Framework/Commands/UICommandInfo.h" #include "Templates/SharedPointer.h" -class FFlowToolbarCommands final : public TCommands +class FLOWEDITOR_API FFlowToolbarCommands final : public TCommands { public: FFlowToolbarCommands(); diff --git a/Source/FlowEditor/Public/FlowEditorStyle.h b/Source/FlowEditor/Public/FlowEditorStyle.h index 0aa06b843..93b45ec05 100644 --- a/Source/FlowEditor/Public/FlowEditorStyle.h +++ b/Source/FlowEditor/Public/FlowEditorStyle.h @@ -4,7 +4,7 @@ #include "Styling/SlateStyle.h" -class FFlowEditorStyle +class FLOWEDITOR_API FFlowEditorStyle { public: static TSharedPtr Get() { return StyleSet; } diff --git a/Source/FlowEditor/Public/Graph/FlowGraph.h b/Source/FlowEditor/Public/Graph/FlowGraph.h index 9ca219976..134562e19 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraph.h +++ b/Source/FlowEditor/Public/Graph/FlowGraph.h @@ -10,7 +10,7 @@ class FLOWEDITOR_API FFlowGraphInterface final : public IFlowGraphInterface { public: - virtual ~FFlowGraphInterface() {} + virtual ~FFlowGraphInterface() override {} virtual void OnInputTriggered(UEdGraphNode* GraphNode, const int32 Index) const override; virtual void OnOutputTriggered(UEdGraphNode* GraphNode, const int32 Index) const override; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphConnectionDrawingPolicy.h b/Source/FlowEditor/Public/Graph/FlowGraphConnectionDrawingPolicy.h index 1cf9ea48c..0893d30e8 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphConnectionDrawingPolicy.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphConnectionDrawingPolicy.h @@ -14,7 +14,7 @@ enum class EFlowConnectionDrawType : uint8 struct FFlowGraphConnectionDrawingPolicyFactory : public FGraphPanelPinConnectionFactory { - virtual ~FFlowGraphConnectionDrawingPolicyFactory() + virtual ~FFlowGraphConnectionDrawingPolicyFactory() override { } @@ -25,7 +25,7 @@ class FSlateWindowElementList; class UEdGraph; // This class draws the connections between nodes -class FFlowGraphConnectionDrawingPolicy : public FConnectionDrawingPolicy +class FLOWEDITOR_API FFlowGraphConnectionDrawingPolicy : public FConnectionDrawingPolicy { float RecentWireDuration; diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h index 45a37adbb..4a7da0975 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h @@ -15,7 +15,7 @@ class UEdGraphSchema; class UFlowNode; USTRUCT() -struct FFlowBreakpoint +struct FLOWEDITOR_API FFlowBreakpoint { GENERATED_USTRUCT_BODY() diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_ExecutionSequence.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_ExecutionSequence.h index d7a4e1b9f..82105197f 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_ExecutionSequence.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_ExecutionSequence.h @@ -6,7 +6,7 @@ #include "FlowGraphNode_ExecutionSequence.generated.h" UCLASS() -class UFlowGraphNode_ExecutionSequence final : public UFlowGraphNode +class FLOWEDITOR_API UFlowGraphNode_ExecutionSequence : public UFlowGraphNode { GENERATED_UCLASS_BODY() diff --git a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h index a4fceb3ed..d54e948df 100644 --- a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h @@ -7,7 +7,7 @@ #include "Graph/Nodes/FlowGraphNode.h" -class SFlowGraphPinExec final : public SGraphPinExec +class FLOWEDITOR_API SFlowGraphPinExec : public SGraphPinExec { public: SFlowGraphPinExec(); @@ -18,7 +18,7 @@ class SFlowGraphPinExec final : public SGraphPinExec void Construct(const FArguments& InArgs, UEdGraphPin* InPin); }; -class SFlowGraphNode : public SGraphNode +class FLOWEDITOR_API SFlowGraphNode : public SGraphNode { public: SLATE_BEGIN_ARGS(SFlowGraphNode) {} diff --git a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode_Finish.h b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode_Finish.h index f68137550..d054d4a37 100644 --- a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode_Finish.h +++ b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode_Finish.h @@ -4,6 +4,6 @@ #include "Graph/Widgets/SFlowGraphNode.h" -class SFlowGraphNode_Finish : public SFlowGraphNode +class FLOWEDITOR_API SFlowGraphNode_Finish : public SFlowGraphNode { }; diff --git a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode_Start.h b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode_Start.h index 80c7946e5..52a959239 100644 --- a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode_Start.h +++ b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode_Start.h @@ -4,6 +4,6 @@ #include "Graph/Widgets/SFlowGraphNode.h" -class SFlowGraphNode_Start : public SFlowGraphNode +class FLOWEDITOR_API SFlowGraphNode_Start : public SFlowGraphNode { }; diff --git a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode_SubGraph.h b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode_SubGraph.h index a88348351..9a97dd3de 100644 --- a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode_SubGraph.h +++ b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode_SubGraph.h @@ -4,7 +4,7 @@ #include "Graph/Widgets/SFlowGraphNode.h" -class SFlowGraphNode_SubGraph : public SFlowGraphNode +class FLOWEDITOR_API SFlowGraphNode_SubGraph : public SFlowGraphNode { protected: // SGraphNode diff --git a/Source/FlowEditor/Public/Graph/Widgets/SFlowPalette.h b/Source/FlowEditor/Public/Graph/Widgets/SFlowPalette.h index d11873ef3..257bb200c 100644 --- a/Source/FlowEditor/Public/Graph/Widgets/SFlowPalette.h +++ b/Source/FlowEditor/Public/Graph/Widgets/SFlowPalette.h @@ -7,7 +7,7 @@ class FFlowAssetEditor; /** Widget displaying a single item */ -class SFlowPaletteItem : public SGraphPaletteItem +class FLOWEDITOR_API SFlowPaletteItem : public SGraphPaletteItem { public: SLATE_BEGIN_ARGS(SFlowPaletteItem) {} @@ -21,7 +21,7 @@ class SFlowPaletteItem : public SGraphPaletteItem }; /** Flow Palette */ -class SFlowPalette : public SGraphPalette +class FLOWEDITOR_API SFlowPalette : public SGraphPalette { public: SLATE_BEGIN_ARGS(SFlowPalette) {} diff --git a/Source/FlowEditor/Public/LevelEditor/SLevelEditorFlow.h b/Source/FlowEditor/Public/LevelEditor/SLevelEditorFlow.h index 5d4be19b0..d004bce48 100644 --- a/Source/FlowEditor/Public/LevelEditor/SLevelEditorFlow.h +++ b/Source/FlowEditor/Public/LevelEditor/SLevelEditorFlow.h @@ -8,7 +8,7 @@ struct FAssetData; -class SLevelEditorFlow : public SCompoundWidget +class FLOWEDITOR_API SLevelEditorFlow : public SCompoundWidget { public: SLATE_BEGIN_ARGS(SLevelEditorFlow) {} diff --git a/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeBlueprint.h b/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeBlueprint.h index 201124de5..cb7552fa3 100644 --- a/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeBlueprint.h +++ b/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeBlueprint.h @@ -4,7 +4,7 @@ #include "AssetTypeActions/AssetTypeActions_Blueprint.h" -class FAssetTypeActions_FlowNodeBlueprint final : public FAssetTypeActions_Blueprint +class FLOWEDITOR_API FAssetTypeActions_FlowNodeBlueprint final : public FAssetTypeActions_Blueprint { public: virtual FText GetName() const override; From 15a8b59dcc8615c5c7fa928a9548cb12afa791f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Fri, 2 Sep 2022 19:45:47 +0200 Subject: [PATCH 004/485] removed redundant include --- Source/Flow/Public/FlowAsset.h | 1 - Source/Flow/Public/FlowSave.h | 1 - Source/Flow/Public/Nodes/FlowNode.h | 1 - 3 files changed, 3 deletions(-) diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index b067d533a..93a8ef340 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -2,7 +2,6 @@ #pragma once -#include "CoreMinimal.h" #include "FlowSave.h" #include "FlowTypes.h" #include "FlowAsset.generated.h" diff --git a/Source/Flow/Public/FlowSave.h b/Source/Flow/Public/FlowSave.h index fc9e939ef..1c2a07735 100644 --- a/Source/Flow/Public/FlowSave.h +++ b/Source/Flow/Public/FlowSave.h @@ -2,7 +2,6 @@ #pragma once -#include "CoreMinimal.h" #include "GameFramework/SaveGame.h" #include "Serialization/BufferArchive.h" #include "Serialization/ObjectAndNameAsStringProxyArchive.h" diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index f0d20c63b..673ca0470 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -2,7 +2,6 @@ #pragma once -#include "CoreMinimal.h" #include "EdGraph/EdGraphNode.h" #include "Engine/StreamableManager.h" #include "GameplayTagContainer.h" From a2f9bcbef196dc855bfb063940cd4d37d25baa88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sat, 3 Sep 2022 15:42:05 +0200 Subject: [PATCH 005/485] added templated GetNode, as it was always tempting --- Source/Flow/Private/FlowAsset.cpp | 5 ----- Source/Flow/Public/FlowAsset.h | 15 ++++++++++++++- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 62233e8b4..80776e0f5 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -190,11 +190,6 @@ void UFlowAsset::HarvestNodeConnections() } #endif -UFlowNode* UFlowAsset::GetNode(const FGuid& Guid) const -{ - return Nodes.FindRef(Guid); -} - void UFlowAsset::AddInstance(UFlowAsset* Instance) { ActiveInstances.Add(Instance); diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 93a8ef340..6df8357ab 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -128,8 +128,21 @@ class FLOW_API UFlowAsset : public UObject void HarvestNodeConnections(); #endif - UFlowNode* GetNode(const FGuid& Guid) const; TMap GetNodes() const { return Nodes; } + UFlowNode* GetNode(const FGuid& Guid) const { return Nodes.FindRef(Guid); } + + template + T* GetNode(const FGuid& Guid) const + { + static_assert(TPointerIsConvertibleFromTo::Value, "'T' template parameter to GetNode must be derived from UFlowNode"); + + if (UFlowNode* Node = Nodes.FindRef(Guid)) + { + return Cast(Node); + } + + return nullptr; + } TArray GetCustomInputs() const { return CustomInputs; } TArray GetCustomOutputs() const { return CustomOutputs; } From e5286ebefcbd183f88b41eca554576f8100cbae5 Mon Sep 17 00:00:00 2001 From: Bargestt Date: Thu, 8 Sep 2022 22:48:30 +0700 Subject: [PATCH 006/485] Asset category merge (#117) --- .../FlowEditor/Private/FlowEditorModule.cpp | 24 ++++++++++++++++++- .../Public/Graph/FlowGraphSettings.h | 8 +++---- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/Source/FlowEditor/Private/FlowEditorModule.cpp b/Source/FlowEditor/Private/FlowEditorModule.cpp index 8d83d0340..33b922f4a 100644 --- a/Source/FlowEditor/Private/FlowEditorModule.cpp +++ b/Source/FlowEditor/Private/FlowEditorModule.cpp @@ -118,7 +118,29 @@ void FFlowEditorModule::ShutdownModule() void FFlowEditorModule::RegisterAssets() { IAssetTools& AssetTools = FModuleManager::LoadModuleChecked("AssetTools").Get(); - FlowAssetCategory = AssetTools.RegisterAdvancedAssetCategory(FName(TEXT("Flow")), UFlowGraphSettings::Get()->FlowAssetCategoryName); + + FText AssetCategoryText = UFlowGraphSettings::Get()->FlowAssetCategoryName; + + // Find matching BuildIn category + if (!AssetCategoryText.IsEmpty()) + { + TArray AllCategories; + AssetTools.GetAllAdvancedAssetCategories(AllCategories); + for (const FAdvancedAssetCategory& ExistingCategory : AllCategories) + { + if (ExistingCategory.CategoryName.EqualTo(AssetCategoryText)) + { + FlowAssetCategory = ExistingCategory.CategoryType; + break; + } + } + } + + if (FlowAssetCategory == EAssetTypeCategories::None) + { + FlowAssetCategory = AssetTools.RegisterAdvancedAssetCategory(FName(TEXT("Flow")), AssetCategoryText); + } + const TSharedRef FlowAssetActions = MakeShareable(new FAssetTypeActions_FlowAsset()); RegisteredAssetActions.Add(FlowAssetActions); diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h index b82092d3c..fb155608b 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h @@ -20,20 +20,20 @@ class UFlowGraphSettings final : public UDeveloperSettings /** Show Flow Asset in Flow category of "Create Asset" menu? * Requires restart after making a change. */ - UPROPERTY(EditAnywhere, config, Category = "Default UI") + UPROPERTY(EditAnywhere, config, Category = "Default UI", meta = (ConfigRestartRequired = true)) bool bExposeFlowAssetCreation; /** Show Flow Node blueprint in Flow category of "Create Asset" menu? * Requires restart after making a change. */ - UPROPERTY(EditAnywhere, config, Category = "Default UI") + UPROPERTY(EditAnywhere, config, Category = "Default UI", meta = (ConfigRestartRequired = true)) bool bExposeFlowNodeCreation; /** Show Flow Asset toolbar? * Requires restart after making a change. */ - UPROPERTY(EditAnywhere, config, Category = "Default UI") + UPROPERTY(EditAnywhere, config, Category = "Default UI", meta = (ConfigRestartRequired = true)) bool bShowAssetToolbarAboveLevelEditor; - UPROPERTY(EditAnywhere, config, Category = "Default UI") + UPROPERTY(EditAnywhere, config, Category = "Default UI", meta = (ConfigRestartRequired = true)) FText FlowAssetCategoryName; /** Flow Asset class allowed to be assigned via Level Editor toolbar*/ From 7e08575b789a5bde2cc58e6e89169f16f04b99d1 Mon Sep 17 00:00:00 2001 From: Bargestt Date: Thu, 8 Sep 2022 22:48:45 +0700 Subject: [PATCH 007/485] Class picker for new assets (#118) --- .../Private/Asset/FlowAssetFactory.cpp | 97 ++++++++++++++++++- .../Private/Graph/FlowGraphSettings.cpp | 1 + .../Public/Asset/FlowAssetFactory.h | 4 + .../Public/Graph/FlowGraphSettings.h | 4 + 4 files changed, 105 insertions(+), 1 deletion(-) diff --git a/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp b/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp index 13e6a31a6..1abe5bd9d 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp @@ -4,6 +4,56 @@ #include "FlowAsset.h" #include "Graph/FlowGraph.h" +#include +#include +#include +#include + + +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; + } +}; + UFlowAssetFactory::UFlowAssetFactory(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { @@ -14,9 +64,54 @@ UFlowAssetFactory::UFlowAssetFactory(const FObjectInitializer& ObjectInitializer bEditAfterNew = true; } +bool UFlowAssetFactory::ConfigureProperties() +{ + AssetClass = UFlowGraphSettings::Get()->DefaultFlowAssetClass;; + + if (AssetClass != nullptr) + { + // Class was selected in settings + return true; + } + + // Load the classviewer module to display a class picker + FClassViewerModule& ClassViewerModule = FModuleManager::LoadModuleChecked("ClassViewer"); + + // Fill in options + FClassViewerInitializationOptions Options; + Options.Mode = EClassViewerMode::ClassPicker; + + TSharedPtr Filter = MakeShareable(new FAssetClassParentFilter); + Options.ClassFilter = Filter; + + Filter->DisallowedClassFlags = CLASS_Abstract | CLASS_Deprecated | CLASS_NewerVersionExists | CLASS_HideDropDown; + Filter->AllowedChildrenOfClasses.Add(UFlowAsset::StaticClass()); + + const FText TitleText = NSLOCTEXT("FlowAssetFactory", "CreateFlowAssetOptions", "Pick Flow Asset Class"); + UClass* ChosenClass = nullptr; + const bool bPressedOk = SClassPickerDialog::PickClass(TitleText, Options, ChosenClass, UFlowAsset::StaticClass()); + + if (bPressedOk) + { + AssetClass = ChosenClass; + } + + return bPressedOk; +} + UObject* UFlowAssetFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) { - UFlowAsset* NewFlow = NewObject(InParent, Class, Name, Flags | RF_Transactional, Context); + UFlowAsset* NewFlow = nullptr; + if (AssetClass != nullptr) + { + NewFlow = NewObject(InParent, AssetClass, Name, Flags | RF_Transactional, Context); + } + else + { + // if we have no asset class, use the passed-in class instead + NewFlow = NewObject(InParent, Class, Name, Flags | RF_Transactional, Context); + } + UFlowGraph::CreateGraph(NewFlow); return NewFlow; } diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp index ea44964da..7c54658fb 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp @@ -12,6 +12,7 @@ UFlowGraphSettings::UFlowGraphSettings(const FObjectInitializer& ObjectInitializ , bExposeFlowNodeCreation(true) , bShowAssetToolbarAboveLevelEditor(true) , FlowAssetCategoryName(LOCTEXT("FlowAssetCategory", "Flow")) + , DefaultFlowAssetClass(UFlowAsset::StaticClass()) , WorldAssetClass(UFlowAsset::StaticClass()) , bShowDefaultPinNames(false) , ExecPinColorModifier(0.75f, 0.75f, 0.75f, 1.0f) diff --git a/Source/FlowEditor/Public/Asset/FlowAssetFactory.h b/Source/FlowEditor/Public/Asset/FlowAssetFactory.h index b8a3f93a3..1a085fa7c 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetFactory.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetFactory.h @@ -10,5 +10,9 @@ class UFlowAssetFactory : public UFactory { GENERATED_UCLASS_BODY() + UPROPERTY(EditAnywhere, Category = Asset) + TSubclassOf AssetClass; + + virtual bool ConfigureProperties() override; virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; }; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h index fb155608b..cbd5be68a 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h @@ -36,6 +36,10 @@ class UFlowGraphSettings final : public UDeveloperSettings UPROPERTY(EditAnywhere, config, Category = "Default UI", meta = (ConfigRestartRequired = true)) FText FlowAssetCategoryName; + /** Use this class to create new assets. Class picker will show up if None */ + UPROPERTY(EditAnywhere, config, Category = "Default UI") + TSubclassOf DefaultFlowAssetClass; + /** Flow Asset class allowed to be assigned via Level Editor toolbar*/ UPROPERTY(EditAnywhere, config, Category = "Default UI", meta = (EditCondition = "bShowAssetToolbarAboveLevelEditor")) TSubclassOf WorldAssetClass; From fc5a836a4f76c5b32ab4f67bc529651b7fd74c0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Thu, 8 Sep 2022 19:36:34 +0200 Subject: [PATCH 008/485] minor readability tweak to #117 --- .../Private/Asset/FlowAssetFactory.cpp | 41 ++++++++++--------- .../FlowEditor/Private/FlowEditorModule.cpp | 34 +++++++-------- 2 files changed, 40 insertions(+), 35 deletions(-) diff --git a/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp b/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp index 1abe5bd9d..47f0de8cd 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp @@ -3,22 +3,26 @@ #include "Asset/FlowAssetFactory.h" #include "FlowAsset.h" #include "Graph/FlowGraph.h" +#include "Graph/FlowGraphSettings.h" -#include -#include -#include -#include +#include "ClassViewerFilter.h" +#include "ClassViewerModule.h" +#include "Kismet2/KismetEditorUtilities.h" +#include "Kismet2/SClassPickerDialog.h" +#define LOCTEXT_NAMESPACE "FlowAssetFactory" class FAssetClassParentFilter : public IClassViewerFilter { public: FAssetClassParentFilter() - : DisallowedClassFlags(CLASS_None), bDisallowBlueprintBase(false) - {} + : DisallowedClassFlags(CLASS_None) + , bDisallowBlueprintBase(false) + { + } /** All children of these classes will be included unless filtered out by another setting. */ - TSet< const UClass* > AllowedChildrenOfClasses; + TSet AllowedChildrenOfClasses; /** Disallowed class flags. */ EClassFlags DisallowedClassFlags; @@ -26,10 +30,9 @@ class FAssetClassParentFilter : public IClassViewerFilter /** Disallow blueprint base classes. */ bool bDisallowBlueprintBase; - virtual bool IsClassAllowed(const FClassViewerInitializationOptions& InInitOptions, const UClass* InClass, TSharedRef< FClassViewerFilterFuncs > InFilterFuncs) override + virtual bool IsClassAllowed(const FClassViewerInitializationOptions& InInitOptions, const UClass* InClass, TSharedRef InFilterFuncs) override { - bool bAllowed = !InClass->HasAnyClassFlags(DisallowedClassFlags) - && InFilterFuncs->IfInChildOfClassesSet(AllowedChildrenOfClasses, InClass) != EFilterReturn::Failed; + const bool bAllowed = !InClass->HasAnyClassFlags(DisallowedClassFlags) && InFilterFuncs->IfInChildOfClassesSet(AllowedChildrenOfClasses, InClass) != EFilterReturn::Failed; if (bAllowed && bDisallowBlueprintBase) { @@ -42,15 +45,14 @@ class FAssetClassParentFilter : public IClassViewerFilter return bAllowed; } - virtual bool IsUnloadedClassAllowed(const FClassViewerInitializationOptions& InInitOptions, const TSharedRef< const IUnloadedBlueprintData > InUnloadedClassData, TSharedRef< FClassViewerFilterFuncs > InFilterFuncs) override + virtual bool IsUnloadedClassAllowed(const FClassViewerInitializationOptions& InInitOptions, const TSharedRef InUnloadedClassData, TSharedRef InFilterFuncs) override { if (bDisallowBlueprintBase) { return false; } - return !InUnloadedClassData->HasAnyClassFlags(DisallowedClassFlags) - && InFilterFuncs->IfInChildOfClassesSet(AllowedChildrenOfClasses, InUnloadedClassData) != EFilterReturn::Failed; + return !InUnloadedClassData->HasAnyClassFlags(DisallowedClassFlags) && InFilterFuncs->IfInChildOfClassesSet(AllowedChildrenOfClasses, InUnloadedClassData) != EFilterReturn::Failed; } }; @@ -67,27 +69,26 @@ UFlowAssetFactory::UFlowAssetFactory(const FObjectInitializer& ObjectInitializer bool UFlowAssetFactory::ConfigureProperties() { AssetClass = UFlowGraphSettings::Get()->DefaultFlowAssetClass;; - if (AssetClass != nullptr) - { + { // Class was selected in settings return true; } - // Load the classviewer module to display a class picker - FClassViewerModule& ClassViewerModule = FModuleManager::LoadModuleChecked("ClassViewer"); + // Load the Class Viewer module to display a class picker + FModuleManager::LoadModuleChecked("ClassViewer"); // Fill in options FClassViewerInitializationOptions Options; Options.Mode = EClassViewerMode::ClassPicker; - TSharedPtr Filter = MakeShareable(new FAssetClassParentFilter); + const TSharedPtr Filter = MakeShareable(new FAssetClassParentFilter); Options.ClassFilter = Filter; Filter->DisallowedClassFlags = CLASS_Abstract | CLASS_Deprecated | CLASS_NewerVersionExists | CLASS_HideDropDown; Filter->AllowedChildrenOfClasses.Add(UFlowAsset::StaticClass()); - const FText TitleText = NSLOCTEXT("FlowAssetFactory", "CreateFlowAssetOptions", "Pick Flow Asset Class"); + const FText TitleText = LOCTEXT("CreateFlowAssetOptions", "Pick Flow Asset Class"); UClass* ChosenClass = nullptr; const bool bPressedOk = SClassPickerDialog::PickClass(TitleText, Options, ChosenClass, UFlowAsset::StaticClass()); @@ -115,3 +116,5 @@ UObject* UFlowAssetFactory::FactoryCreateNew(UClass* Class, UObject* InParent, F UFlowGraph::CreateGraph(NewFlow); return NewFlow; } + +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/FlowEditor/Private/FlowEditorModule.cpp b/Source/FlowEditor/Private/FlowEditorModule.cpp index 33b922f4a..faf8797ff 100644 --- a/Source/FlowEditor/Private/FlowEditorModule.cpp +++ b/Source/FlowEditor/Private/FlowEditorModule.cpp @@ -111,7 +111,7 @@ void FFlowEditorModule::ShutdownModule() } } } - + FModuleManager::Get().OnModulesChanged().Remove(ModulesChangedHandle); } @@ -119,29 +119,31 @@ void FFlowEditorModule::RegisterAssets() { IAssetTools& AssetTools = FModuleManager::LoadModuleChecked("AssetTools").Get(); - FText AssetCategoryText = UFlowGraphSettings::Get()->FlowAssetCategoryName; + // try to merge asset category with a built-in one + { + FText AssetCategoryText = UFlowGraphSettings::Get()->FlowAssetCategoryName; - // Find matching BuildIn category - if (!AssetCategoryText.IsEmpty()) - { - TArray AllCategories; - AssetTools.GetAllAdvancedAssetCategories(AllCategories); - for (const FAdvancedAssetCategory& ExistingCategory : AllCategories) + // Find matching built-in category + if (!AssetCategoryText.IsEmpty()) { - if (ExistingCategory.CategoryName.EqualTo(AssetCategoryText)) + TArray AllCategories; + AssetTools.GetAllAdvancedAssetCategories(AllCategories); + for (const FAdvancedAssetCategory& ExistingCategory : AllCategories) { - FlowAssetCategory = ExistingCategory.CategoryType; - break; + if (ExistingCategory.CategoryName.EqualTo(AssetCategoryText)) + { + FlowAssetCategory = ExistingCategory.CategoryType; + break; + } } } - } - if (FlowAssetCategory == EAssetTypeCategories::None) - { - FlowAssetCategory = AssetTools.RegisterAdvancedAssetCategory(FName(TEXT("Flow")), AssetCategoryText); + if (FlowAssetCategory == EAssetTypeCategories::None) + { + FlowAssetCategory = AssetTools.RegisterAdvancedAssetCategory(FName(TEXT("Flow")), AssetCategoryText); + } } - const TSharedRef FlowAssetActions = MakeShareable(new FAssetTypeActions_FlowAsset()); RegisteredAssetActions.Add(FlowAssetActions); AssetTools.RegisterAssetTypeActions(FlowAssetActions); From 73f3dedc7be0bf8951e7f87ea0613155df09199b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Wed, 21 Sep 2022 15:59:07 +0200 Subject: [PATCH 009/485] compilation fixes --- Source/Flow/Public/FlowSave.h | 1 + Source/FlowEditor/Public/Asset/FlowAssetFactory.h | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Flow/Public/FlowSave.h b/Source/Flow/Public/FlowSave.h index 1c2a07735..fc9e939ef 100644 --- a/Source/Flow/Public/FlowSave.h +++ b/Source/Flow/Public/FlowSave.h @@ -2,6 +2,7 @@ #pragma once +#include "CoreMinimal.h" #include "GameFramework/SaveGame.h" #include "Serialization/BufferArchive.h" #include "Serialization/ObjectAndNameAsStringProxyArchive.h" diff --git a/Source/FlowEditor/Public/Asset/FlowAssetFactory.h b/Source/FlowEditor/Public/Asset/FlowAssetFactory.h index 1a085fa7c..e1c3f5274 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetFactory.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetFactory.h @@ -5,8 +5,8 @@ #include "Factories/Factory.h" #include "FlowAssetFactory.generated.h" -UCLASS(HideCategories = Object, MinimalAPI) -class UFlowAssetFactory : public UFactory +UCLASS(HideCategories = Object) +class FLOWEDITOR_API UFlowAssetFactory : public UFactory { GENERATED_UCLASS_BODY() From d2a216a1fbe7327826b12920fd2504badd026d71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Wed, 21 Sep 2022 18:01:33 +0200 Subject: [PATCH 010/485] 5.0 deprecation warning fixed --- Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp b/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp index 47f0de8cd..03f29d11d 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp @@ -83,11 +83,11 @@ bool UFlowAssetFactory::ConfigureProperties() Options.Mode = EClassViewerMode::ClassPicker; const TSharedPtr Filter = MakeShareable(new FAssetClassParentFilter); - Options.ClassFilter = Filter; - Filter->DisallowedClassFlags = CLASS_Abstract | CLASS_Deprecated | CLASS_NewerVersionExists | CLASS_HideDropDown; Filter->AllowedChildrenOfClasses.Add(UFlowAsset::StaticClass()); + Options.ClassFilters = {Filter.ToSharedRef()}; + const FText TitleText = LOCTEXT("CreateFlowAssetOptions", "Pick Flow Asset Class"); UClass* ChosenClass = nullptr; const bool bPressedOk = SClassPickerDialog::PickClass(TitleText, Options, ChosenClass, UFlowAsset::StaticClass()); From cafa8c80aa4d8c1c64ba6b8db46c708df85157bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Wed, 21 Sep 2022 18:04:34 +0200 Subject: [PATCH 011/485] added AllowedAssetClasses & DeniedAssetClasses to Flow Node class Flow Node class now can defined in which Flow Asset class can be placed --- Source/Flow/Public/Nodes/FlowNode.h | 3 + .../Private/Graph/FlowGraphSchema.cpp | 100 +++++++++++++----- .../FlowEditor/Public/Graph/FlowGraphSchema.h | 3 +- 3 files changed, 77 insertions(+), 29 deletions(-) diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 673ca0470..b3b969e24 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -42,6 +42,9 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte #if WITH_EDITORONLY_DATA protected: + TArray> AllowedAssetClasses; + TArray> DeniedAssetClasses; + UPROPERTY() FString Category; diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index 43aebb508..cbe7b267b 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -18,7 +18,6 @@ #include "Developer/ToolMenus/Public/ToolMenus.h" #include "EdGraph/EdGraph.h" #include "ScopedTransaction.h" -#include "UObject/UObjectIterator.h" #define LOCTEXT_NAMESPACE "FlowGraphSchema" @@ -245,17 +244,72 @@ UClass* UFlowGraphSchema::GetAssignedGraphNodeClass(const UClass* FlowNodeClass) return UFlowGraphNode::StaticClass(); } -bool UFlowGraphSchema::IsClassContained(const TArray> Classes, const UClass* Class) +void UFlowGraphSchema::ApplyNodeFilter(const UFlowAsset* AssetClassDefaults, const UClass* FlowNodeClass, TArray& FilteredNodes) { - for (const UClass* CurrentClass : Classes) + if (FlowNodeClass == nullptr) { - if (Class->IsChildOf(CurrentClass)) + return; + } + + UFlowNode* NodeDefaults = FlowNodeClass->GetDefaultObject(); + + // UFlowNode class limits which UFlowAsset class can use it + { + for (const UClass* DeniedAssetClass : NodeDefaults->DeniedAssetClasses) + { + if (DeniedAssetClass && AssetClassDefaults->GetClass()->IsChildOf(DeniedAssetClass)) + { + return; + } + } + + if (NodeDefaults->AllowedAssetClasses.Num() > 0) + { + bool bAllowedInAsset = false; + for (const UClass* AllowedAssetClass : NodeDefaults->AllowedAssetClasses) + { + if (AllowedAssetClass && AssetClassDefaults->GetClass()->IsChildOf(AllowedAssetClass)) + { + bAllowedInAsset = true; + break; + } + } + if (!bAllowedInAsset) + { + return; + } + } + } + + // UFlowAsset class can limit which UFlowNode classes can be used + { + for (const UClass* DeniedNodeClass : AssetClassDefaults->DeniedNodeClasses) + { + if (DeniedNodeClass && FlowNodeClass->IsChildOf(DeniedNodeClass)) + { + return; + } + } + + if (AssetClassDefaults->AllowedNodeClasses.Num() > 0) { - return true; + bool bAllowedInAsset = false; + for (const UClass* AllowedNodeClass : AssetClassDefaults->AllowedNodeClasses) + { + if (AllowedNodeClass && FlowNodeClass->IsChildOf(AllowedNodeClass)) + { + bAllowedInAsset = true; + break; + } + } + if (!bAllowedInAsset) + { + return; + } } } - return false; + FilteredNodes.Emplace(NodeDefaults); } void UFlowGraphSchema::GetFlowNodeActions(FGraphActionMenuBuilder& ActionMenuBuilder, const UFlowAsset* AssetClassDefaults, const FString& CategoryName) @@ -265,38 +319,28 @@ void UFlowGraphSchema::GetFlowNodeActions(FGraphActionMenuBuilder& ActionMenuBui GatherFlowNodes(); } - TArray FlowNodes; - FlowNodes.Reserve(NativeFlowNodes.Num() + BlueprintFlowNodes.Num()); - - for (const UClass* FlowNodeClass : NativeFlowNodes) + // Flow Asset type might limit which nodes are placeable + TArray FilteredNodes; { - // Flow Asset type might limit which nodes are placeable - if (IsClassContained(AssetClassDefaults->DeniedNodeClasses, FlowNodeClass)) - { - continue; - } + FilteredNodes.Reserve(NativeFlowNodes.Num() + BlueprintFlowNodes.Num()); - if (IsClassContained(AssetClassDefaults->AllowedNodeClasses, FlowNodeClass)) + for (const UClass* FlowNodeClass : NativeFlowNodes) { - FlowNodes.Emplace(FlowNodeClass->GetDefaultObject()); + ApplyNodeFilter(AssetClassDefaults, FlowNodeClass, FilteredNodes); } - } - for (const TPair& AssetData : BlueprintFlowNodes) - { - if (const UBlueprint* Blueprint = GetPlaceableNodeBlueprint(AssetData.Value)) + + for (const TPair& AssetData : BlueprintFlowNodes) { - for (const UClass* AllowedClass : AssetClassDefaults->AllowedNodeClasses) + if (const UBlueprint* Blueprint = GetPlaceableNodeBlueprint(AssetData.Value)) { - if (Blueprint->GeneratedClass->IsChildOf(AllowedClass)) - { - FlowNodes.Emplace(Blueprint->GeneratedClass->GetDefaultObject()); - } + ApplyNodeFilter(AssetClassDefaults, Blueprint->GeneratedClass, FilteredNodes); } } + + FilteredNodes.Shrink(); } - FlowNodes.Shrink(); - for (const UFlowNode* FlowNode : FlowNodes) + for (const UFlowNode* FlowNode : FilteredNodes) { if ((CategoryName.IsEmpty() || CategoryName.Equals(FlowNode->GetNodeCategory())) && !UFlowGraphSettings::Get()->NodesHiddenFromPalette.Contains(FlowNode->GetClass())) { diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h index ed4110b81..592309bc1 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h @@ -5,6 +5,7 @@ #include "EdGraph/EdGraphSchema.h" #include "FlowGraphSchema.generated.h" +class UFlowNode; class UFlowAsset; DECLARE_MULTICAST_DELEGATE(FFlowGraphSchemaRefresh); @@ -43,7 +44,7 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema static UClass* GetAssignedGraphNodeClass(const UClass* FlowNodeClass); private: - static bool IsClassContained(const TArray> Classes, const UClass* Class); + static void ApplyNodeFilter(const UFlowAsset* AssetClassDefaults, const UClass* FlowNodeClass, TArray& FilteredNodes); static void GetFlowNodeActions(FGraphActionMenuBuilder& ActionMenuBuilder, const UFlowAsset* AssetClassDefaults, const FString& CategoryName); static void GetCommentAction(FGraphActionMenuBuilder& ActionMenuBuilder, const UEdGraph* CurrentGraph = nullptr); From da66083e5a29f216dd728e2977a7e0154db0c4b8 Mon Sep 17 00:00:00 2001 From: Jani Hartikainen Date: Sun, 25 Sep 2022 13:25:08 +0300 Subject: [PATCH 012/485] Improve drawing for reroute nodes that go backwards (#121) --- .../FlowGraphConnectionDrawingPolicy.cpp | 109 ++++++++++++++++++ .../Graph/FlowGraphConnectionDrawingPolicy.h | 7 ++ 2 files changed, 116 insertions(+) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp b/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp index 4aa0c4eb0..4d462c1d7 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp @@ -11,6 +11,7 @@ #include "Graph/Nodes/FlowGraphNode.h" #include "FlowAsset.h" +#include "Graph/Nodes/FlowGraphNode_Reroute.h" #include "Nodes/FlowNode.h" #include "Misc/App.h" @@ -126,6 +127,28 @@ void FFlowGraphConnectionDrawingPolicy::DetermineWiringStyle(UEdGraphPin* Output check(GraphObj); const UEdGraphSchema* Schema = GraphObj->GetSchema(); + { + //If reroute node path goes backwards, we need to flip the direction to make it look nice + //(all of the logic for this is basically same as in FKismetConnectionDrawingPolicy) + UEdGraphNode* OutputNode = OutputPin->GetOwningNode(); + UEdGraphNode* InputNode = (InputPin != nullptr) ? InputPin->GetOwningNode() : nullptr; + if (auto* OutputRerouteNode = Cast(OutputNode)) + { + if (ShouldChangeTangentForReroute(OutputRerouteNode)) + { + Params.StartDirection = EGPD_Input; + } + } + + if (auto* InputRerouteNode = Cast(InputNode)) + { + if (ShouldChangeTangentForReroute(InputRerouteNode)) + { + Params.EndDirection = EGPD_Output; + } + } + } + if (OutputPin->bOrphanedPin || (InputPin && InputPin->bOrphanedPin)) { Params.WireColor = FLinearColor::Red; @@ -258,3 +281,89 @@ FVector2D FFlowGraphConnectionDrawingPolicy::GetControlPoint(const FVector2D& So return FVector2D(Target.X, Source.Y - SlopeHeight); } + +bool FFlowGraphConnectionDrawingPolicy::ShouldChangeTangentForReroute(UFlowGraphNode_Reroute* Reroute) +{ + if (bool* pResult = RerouteToReversedDirectionMap.Find(Reroute)) + { + return *pResult; + } + else + { + bool bPinReversed = false; + + FVector2D AverageLeftPin; + FVector2D AverageRightPin; + FVector2D CenterPin; + bool bCenterValid = Reroute->OutputPins.Num() == 0 ? false : FindPinCenter(Reroute->OutputPins[0], /*out*/ CenterPin); + bool bLeftValid = GetAverageConnectedPosition(Reroute, EGPD_Input, /*out*/ AverageLeftPin); + bool bRightValid = GetAverageConnectedPosition(Reroute, EGPD_Output, /*out*/ AverageRightPin); + + if (bLeftValid && bRightValid) + { + bPinReversed = AverageRightPin.X < AverageLeftPin.X; + } + else if (bCenterValid) + { + if (bLeftValid) + { + bPinReversed = CenterPin.X < AverageLeftPin.X; + } + else if (bRightValid) + { + bPinReversed = AverageRightPin.X < CenterPin.X; + } + } + + RerouteToReversedDirectionMap.Add(Reroute, bPinReversed); + + return bPinReversed; + } +} + +bool FFlowGraphConnectionDrawingPolicy::FindPinCenter(UEdGraphPin* Pin, FVector2D& OutCenter) const +{ + if (const TSharedPtr* pPinWidget = PinToPinWidgetMap.Find(Pin)) + { + if (FArrangedWidget* pPinEntry = PinGeometries->Find((*pPinWidget).ToSharedRef())) + { + OutCenter = FGeometryHelper::CenterOf(pPinEntry->Geometry); + return true; + } + } + + return false; +} + +bool FFlowGraphConnectionDrawingPolicy::GetAverageConnectedPosition(UFlowGraphNode_Reroute* Reroute, EEdGraphPinDirection Direction, FVector2D& OutPos) const +{ + FVector2D Result = FVector2D::ZeroVector; + int32 ResultCount = 0; + + if(Reroute->InputPins.Num() == 0 || Reroute->OutputPins.Num() == 0) + { + return false; + } + + UEdGraphPin* Pin = (Direction == EGPD_Input) ? Reroute->InputPins[0] : Reroute->OutputPins[0]; + for (UEdGraphPin* LinkedPin : Pin->LinkedTo) + { + FVector2D CenterPoint; + if (FindPinCenter(LinkedPin, /*out*/ CenterPoint)) + { + Result += CenterPoint; + ResultCount++; + } + } + + if (ResultCount > 0) + { + OutPos = Result * (1.0f / ResultCount); + return true; + } + else + { + return false; + } +} + diff --git a/Source/FlowEditor/Public/Graph/FlowGraphConnectionDrawingPolicy.h b/Source/FlowEditor/Public/Graph/FlowGraphConnectionDrawingPolicy.h index 0893d30e8..c940adfc4 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphConnectionDrawingPolicy.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphConnectionDrawingPolicy.h @@ -45,6 +45,9 @@ class FLOWEDITOR_API FFlowGraphConnectionDrawingPolicy : public FConnectionDrawi TMap RecordedPaths; TMap SelectedPaths; + //Used to help reversing pins on nodes that go backwards + TMap RerouteToReversedDirectionMap; + public: FFlowGraphConnectionDrawingPolicy(int32 InBackLayerID, int32 InFrontLayerID, float ZoomFactor, const FSlateRect& InClippingRect, FSlateWindowElementList& InDrawElements, UEdGraph* InGraphObj); @@ -60,4 +63,8 @@ class FLOWEDITOR_API FFlowGraphConnectionDrawingPolicy : public FConnectionDrawi void DrawCircuitSpline(const int32& LayerId, const FVector2D& Start, const FVector2D& End, const FConnectionParams& Params) const; void DrawCircuitConnection(const int32& LayerId, const FVector2D& Start, const FVector2D& StartDirection, const FVector2D& End, const FVector2D& EndDirection, const FConnectionParams& Params) const; static FVector2D GetControlPoint(const FVector2D& Source, const FVector2D& Target); + + bool ShouldChangeTangentForReroute(class UFlowGraphNode_Reroute* Reroute); + bool FindPinCenter(UEdGraphPin* Pin, FVector2D& OutCenter) const; + bool GetAverageConnectedPosition(class UFlowGraphNode_Reroute* Reroute, EEdGraphPinDirection Direction, FVector2D& OutPos) const; }; From 3ba3e991327ef1560ad97923a865fa78f1e9e877 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Thu, 6 Oct 2022 20:09:30 +0200 Subject: [PATCH 013/485] =?UTF-8?q?Revert=20"Revert=20"Rename=20uses=20of?= =?UTF-8?q?=20FEditorStyle=20to=20FAppStyle=20as=20it's=20been=20deprecate?= =?UTF-8?q?d=20in=205.1=20=E2=80=A6=20(#114)""?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit fd88965f7262ea940d2a9a376db72bc00ca73e5e. --- Source/Flow/Public/FlowAsset.h | 1 + Source/Flow/Public/Nodes/FlowNode.h | 1 + .../FlowEditor/Private/Asset/FlowAssetEditor.cpp | 6 +++--- .../FlowEditor/Private/Asset/FlowAssetToolbar.cpp | 6 +++--- Source/FlowEditor/Private/FlowEditorCommands.cpp | 8 ++++---- .../FlowEditor/Private/Graph/FlowGraphSchema.cpp | 12 ++++++------ .../Private/Graph/Widgets/SFlowGraphNode.cpp | 12 +++++++----- .../Graph/Widgets/SFlowGraphNode_SubGraph.cpp | 1 + .../Private/Graph/Widgets/SFlowPalette.cpp | 4 ++-- .../FlowEditor/Private/MovieScene/FlowSection.cpp | 8 ++++---- .../Private/MovieScene/FlowTrackEditor.cpp | 6 +++--- .../Private/Nodes/FlowNodeBlueprintFactory.cpp | 14 +++++++------- Source/FlowEditor/Public/Asset/FlowAssetToolbar.h | 1 + Source/FlowEditor/Public/FlowEditorModule.h | 3 +++ 14 files changed, 46 insertions(+), 37 deletions(-) diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 6df8357ab..4d085a9b0 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -4,6 +4,7 @@ #include "FlowSave.h" #include "FlowTypes.h" +#include "Templates/SubclassOf.h" #include "FlowAsset.generated.h" class UFlowNode; diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index b3b969e24..d164a74e5 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -6,6 +6,7 @@ #include "Engine/StreamableManager.h" #include "GameplayTagContainer.h" #include "VisualLogger/VisualLoggerDebugSnapshotInterface.h" +#include "Templates/SubclassOf.h" #include "FlowTypes.h" #include "Nodes/FlowPin.h" diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index 7b4e90d91..b5cd4094c 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -111,17 +111,17 @@ void FFlowAssetEditor::RegisterTabSpawners(const TSharedRef& InTabManager->RegisterTabSpawner(GraphTab, FOnSpawnTab::CreateSP(this, &FFlowAssetEditor::SpawnTab_GraphCanvas)) .SetDisplayName(LOCTEXT("GraphTab", "Viewport")) .SetGroup(WorkspaceMenuCategoryRef) - .SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "GraphEditor.EventGraph_16x")); + .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "GraphEditor.EventGraph_16x")); InTabManager->RegisterTabSpawner(DetailsTab, FOnSpawnTab::CreateSP(this, &FFlowAssetEditor::SpawnTab_Details)) .SetDisplayName(LOCTEXT("DetailsTab", "Details")) .SetGroup(WorkspaceMenuCategoryRef) - .SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Details")); + .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.Tabs.Details")); InTabManager->RegisterTabSpawner(PaletteTab, FOnSpawnTab::CreateSP(this, &FFlowAssetEditor::SpawnTab_Palette)) .SetDisplayName(LOCTEXT("PaletteTab", "Palette")) .SetGroup(WorkspaceMenuCategoryRef) - .SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "Kismet.Tabs.Palette")); + .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.Tabs.Palette")); } void FFlowAssetEditor::UnregisterTabSpawners(const TSharedRef& InTabManager) diff --git a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp index 99259929d..3287b063a 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp @@ -118,10 +118,10 @@ void SFlowAssetBreadcrumb::Construct(const FArguments& InArgs, const TWeakObject SAssignNew(BreadcrumbTrail, SBreadcrumbTrail) .OnCrumbClicked(this, &SFlowAssetBreadcrumb::OnCrumbClicked) .Visibility_Static(&FFlowAssetEditor::GetDebuggerVisibility) - .ButtonStyle(FEditorStyle::Get(), "FlatButton") - .DelimiterImage(FEditorStyle::GetBrush("Sequencer.BreadcrumbIcon")) + .ButtonStyle(FAppStyle::Get(), "FlatButton") + .DelimiterImage(FAppStyle::GetBrush("Sequencer.BreadcrumbIcon")) .PersistentBreadcrumbs(true) - .TextStyle(FEditorStyle::Get(), "Sequencer.BreadcrumbText"); + .TextStyle(FAppStyle::Get(), "Sequencer.BreadcrumbText"); ChildSlot [ diff --git a/Source/FlowEditor/Private/FlowEditorCommands.cpp b/Source/FlowEditor/Private/FlowEditorCommands.cpp index 863c9c75a..eac9e011e 100644 --- a/Source/FlowEditor/Private/FlowEditorCommands.cpp +++ b/Source/FlowEditor/Private/FlowEditorCommands.cpp @@ -25,7 +25,7 @@ void FFlowToolbarCommands::RegisterCommands() } FFlowGraphCommands::FFlowGraphCommands() - : TCommands("FlowGraph", LOCTEXT("FlowGraph", "Flow Graph"), NAME_None, FEditorStyle::GetStyleSetName()) + : TCommands("FlowGraph", LOCTEXT("FlowGraph", "Flow Graph"), NAME_None, FAppStyle::GetAppStyleSetName()) { } @@ -50,7 +50,7 @@ void FFlowGraphCommands::RegisterCommands() } FFlowSpawnNodeCommands::FFlowSpawnNodeCommands() - : TCommands(TEXT("FFlowSpawnNodeCommands"), LOCTEXT("FlowGraph_SpawnNodes", "Flow Graph - Spawn Nodes"), NAME_None, FEditorStyle::GetStyleSetName()) + : TCommands(TEXT("FFlowSpawnNodeCommands"), LOCTEXT("FlowGraph_SpawnNodes", "Flow Graph - Spawn Nodes"), NAME_None, FAppStyle::GetAppStyleSetName()) { } @@ -68,7 +68,7 @@ void FFlowSpawnNodeCommands::RegisterCommands() FString ClassName; if (FParse::Value(*NodeSpawns[x], TEXT("Class="), ClassName)) { - UClass* FoundClass = FindObject(ANY_PACKAGE, *ClassName, true); + UClass* FoundClass = FindObject(nullptr, *ClassName, true); if (FoundClass && FoundClass->IsChildOf(UFlowNode::StaticClass())) { NodeClass = FoundClass; @@ -116,7 +116,7 @@ void FFlowSpawnNodeCommands::RegisterCommands() const FText CommandLabelText = FText::FromString(NodeClass->GetName()); const FText Description = FText::Format(LOCTEXT("NodeSpawnDescription", "Hold down the bound keys and left click in the graph panel to spawn a {0} node."), CommandLabelText); - FUICommandInfo::MakeCommandInfo(this->AsShared(), CommandInfo, FName(*NodeSpawns[x]), CommandLabelText, Description, FSlateIcon(FEditorStyle::GetStyleSetName(), *FString::Printf(TEXT("%s.%s"), *this->GetContextName().ToString(), *NodeSpawns[x])), EUserInterfaceActionType::Button, Chord); + FUICommandInfo::MakeCommandInfo(this->AsShared(), CommandInfo, FName(*NodeSpawns[x]), CommandLabelText, Description, FSlateIcon(FAppStyle::GetAppStyleSetName(), *FString::Printf(TEXT("%s.%s"), *this->GetContextName().ToString(), *NodeSpawns[x])), EUserInterfaceActionType::Button, Chord); NodeCommands.Add(NodeClass, CommandInfo); } diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index cbe7b267b..4e758b225 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -436,8 +436,8 @@ void UFlowGraphSchema::GatherFlowNodes() const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(AssetRegistryConstants::ModuleName); FARFilter Filter; - Filter.ClassNames.Add(UBlueprint::StaticClass()->GetFName()); - Filter.ClassNames.Add(UBlueprintGeneratedClass::StaticClass()->GetFName()); + Filter.ClassPaths.Add(UBlueprint::StaticClass()->GetClassPathName()); + Filter.ClassPaths.Add(UBlueprintGeneratedClass::StaticClass()->GetClassPathName()); Filter.bRecursiveClasses = true; TArray FoundAssets; @@ -470,9 +470,9 @@ void UFlowGraphSchema::AddAsset(const FAssetData& AssetData, const bool bBatch) return; } - TArray AncestorClassNames; - AssetRegistryModule.Get().GetAncestorClassNames(AssetData.AssetClass, AncestorClassNames); - if (!AncestorClassNames.Contains(UBlueprintCore::StaticClass()->GetFName())) + TArray AncestorClassNames; + AssetRegistryModule.Get().GetAncestorClassNames(AssetData.AssetClassPath, AncestorClassNames); + if (!AncestorClassNames.Contains(UBlueprintCore::StaticClass()->GetClassPathName())) { return; } @@ -483,7 +483,7 @@ void UFlowGraphSchema::AddAsset(const FAssetData& AssetData, const bool bBatch) { UObject* Outer = nullptr; ResolveName(Outer, NativeParentClassPath, false, false); - const UClass* NativeParentClass = FindObject(ANY_PACKAGE, *NativeParentClassPath); + const UClass* NativeParentClass = FindObject(nullptr, *NativeParentClassPath); // accept only Flow Node blueprints if (NativeParentClass && NativeParentClass->IsChildOf(UFlowNode::StaticClass())) diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp index f906f6781..ad3bc9442 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp @@ -24,9 +24,11 @@ #include "Styling/SlateColor.h" #include "TutorialMetaData.h" #include "Widgets/Images/SImage.h" +#include "Widgets/Input/SButton.h" #include "Widgets/Layout/SBorder.h" #include "Widgets/SBoxPanel.h" #include "Widgets/SOverlay.h" +#include "Widgets/SToolTip.h" #define LOCTEXT_NAMESPACE "SFlowGraphNode" @@ -360,7 +362,7 @@ void SFlowGraphNode::UpdateErrorInfo() if (FlowNode->GetClass()->HasAnyClassFlags(CLASS_Deprecated) || FlowNode->bNodeDeprecated) { ErrorMsg = FlowNode->ReplacedBy ? FString::Printf(TEXT(" REPLACED BY: %s "), *FlowNode->ReplacedBy->GetName()) : FString(TEXT(" DEPRECATED! ")); - ErrorColor = FEditorStyle::GetColor("ErrorReporting.WarningBackgroundColor"); + ErrorColor = FAppStyle::GetColor("ErrorReporting.WarningBackgroundColor"); return; } } @@ -371,7 +373,7 @@ void SFlowGraphNode::UpdateErrorInfo() TSharedRef SFlowGraphNode::CreateNodeContentArea() { return SNew(SBorder) - .BorderImage(FEditorStyle::GetBrush("NoBorder")) + .BorderImage(FAppStyle::GetBrush("NoBorder")) .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) [ @@ -440,7 +442,7 @@ void SFlowGraphNode::CreateInputSideAddButton(TSharedPtr OutputBox . Padding( 0,0,7,0 ) [ SNew(SImage) - .Image(FEditorStyle::GetBrush(TEXT("Icons.PlusCircle"))) + .Image(FAppStyle::GetBrush(TEXT("Icons.PlusCircle"))) ] +SHorizontalBox::Slot() .AutoWidth() @@ -475,7 +477,7 @@ void SFlowGraphNode::CreateOutputSideAddButton(TSharedPtr OutputBo .Padding(7,0,0,0) [ SNew(SImage) - .Image(FEditorStyle::GetBrush(TEXT("Icons.PlusCircle"))) + .Image(FAppStyle::GetBrush(TEXT("Icons.PlusCircle"))) ]; AddPinButton(OutputBox, AddPinWidget.ToSharedRef(), EGPD_Output); @@ -498,7 +500,7 @@ void SFlowGraphNode::AddPinButton(TSharedPtr OutputBox, const TSha const TSharedRef AddPinButton = SNew(SButton) .ContentPadding(0.0f) - .ButtonStyle(FEditorStyle::Get(), "NoBorder") + .ButtonStyle(FAppStyle::Get(), "NoBorder") .OnClicked(this, &SFlowGraphNode::OnAddFlowPin, Direction) .IsEnabled(this, &SFlowGraphNode::IsNodeEditable) .ToolTipText(PinTooltipText) diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode_SubGraph.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode_SubGraph.cpp index 7e626f880..006c98576 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode_SubGraph.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode_SubGraph.cpp @@ -8,6 +8,7 @@ #include "IDocumentation.h" #include "SGraphPreviewer.h" +#include "Widgets/Layout/SBox.h" #include "Widgets/SToolTip.h" #define LOCTEXT_NAMESPACE "SFlowGraphNode_SubGraph" diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowPalette.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowPalette.cpp index b73374648..009104dc1 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowPalette.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowPalette.cpp @@ -47,7 +47,7 @@ void SFlowPaletteItem::Construct(const FArguments& InArgs, FCreateWidgetForActio } // Find icons - const FSlateBrush* IconBrush = FEditorStyle::GetBrush(TEXT("NoBrush")); + const FSlateBrush* IconBrush = FAppStyle::GetBrush(TEXT("NoBrush")); const FSlateColor IconColor = FSlateColor::UseForeground(); const FText IconToolTip = GraphAction->GetTooltipDescription(); constexpr bool bIsReadOnly = false; @@ -110,7 +110,7 @@ void SFlowPalette::Construct(const FArguments& InArgs, TWeakPtr FontMeasureService = FSlateApplication::Get().GetRenderer()->GetFontMeasureService(); @@ -66,7 +66,7 @@ void FFlowSectionBase::PaintEventName(FSequencerSectionPainter& Painter, int32 L Painter.DrawElements, LayerId + 1, Painter.SectionGeometry.ToPaintGeometry(BoxOffset, BoxSize), - FEditorStyle::GetBrush("WhiteBrush"), + FAppStyle::GetBrush("WhiteBrush"), ESlateDrawEffect::None, FLinearColor::Black.CopyWithNewOpacity(0.5f) ); @@ -81,7 +81,7 @@ void FFlowSectionBase::PaintEventName(FSequencerSectionPainter& Painter, int32 L WarningString, FontAwesomeFont, Painter.bParentEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect, - FEditorStyle::GetWidgetStyle("Log.Warning").ColorAndOpacity.GetSpecifiedColor() + FAppStyle::GetWidgetStyle("Log.Warning").ColorAndOpacity.GetSpecifiedColor() ); } diff --git a/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp b/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp index 51dd0d55d..583243c09 100644 --- a/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp +++ b/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp @@ -76,7 +76,7 @@ void FFlowTrackEditor::BuildAddTrackMenu(FMenuBuilder& MenuBuilder) LOCTEXT("AddTooltip", "Adds a new flow track that can trigger events in the Flow graph."), FNewMenuDelegate::CreateRaw(this, &FFlowTrackEditor::AddFlowSubMenu), false, - FSlateIcon(FEditorStyle::GetStyleSetName(), "Sequencer.Tracks.Event") + FSlateIcon(FAppStyle::GetAppStyleSetName(), "Sequencer.Tracks.Event") ); } } @@ -138,13 +138,13 @@ bool FFlowTrackEditor::SupportsType(TSubclassOf Type) const bool FFlowTrackEditor::SupportsSequence(UMovieSceneSequence* InSequence) const { - static UClass* LevelSequenceClass = FindObject(ANY_PACKAGE, TEXT("LevelSequence"), true); + static UClass* LevelSequenceClass = FindObject(nullptr, TEXT("LevelSequence"), true); return InSequence && LevelSequenceClass && InSequence->GetClass()->IsChildOf(LevelSequenceClass); } const FSlateBrush* FFlowTrackEditor::GetIconBrush() const { - return FEditorStyle::GetBrush("Sequencer.Tracks.Event"); + return FAppStyle::GetBrush("Sequencer.Tracks.Event"); } void FFlowTrackEditor::HandleAddFlowTrackMenuEntryExecute(UClass* SectionType) diff --git a/Source/FlowEditor/Private/Nodes/FlowNodeBlueprintFactory.cpp b/Source/FlowEditor/Private/Nodes/FlowNodeBlueprintFactory.cpp index 06209c356..86d11dcf7 100644 --- a/Source/FlowEditor/Private/Nodes/FlowNodeBlueprintFactory.cpp +++ b/Source/FlowEditor/Private/Nodes/FlowNodeBlueprintFactory.cpp @@ -45,7 +45,7 @@ class SFlowNodeBlueprintCreateDialog final : public SCompoundWidget [ SNew(SBorder) .Visibility(EVisibility::Visible) - .BorderImage(FEditorStyle::GetBrush("Menu.Background")) + .BorderImage(FAppStyle::GetBrush("Menu.Background")) [ SNew(SBox) .Visibility(EVisibility::Visible) @@ -56,7 +56,7 @@ class SFlowNodeBlueprintCreateDialog final : public SCompoundWidget .FillHeight(1) [ SNew(SBorder) - .BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder")) + .BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder")) .Content() [ SAssignNew(ParentClassContainer, SVerticalBox) @@ -69,14 +69,14 @@ class SFlowNodeBlueprintCreateDialog final : public SCompoundWidget .Padding(8) [ SNew(SUniformGridPanel) - .SlotPadding(FEditorStyle::GetMargin("StandardDialog.SlotPadding")) - .MinDesiredSlotWidth(FEditorStyle::GetFloat("StandardDialog.MinDesiredSlotWidth")) - .MinDesiredSlotHeight(FEditorStyle::GetFloat("StandardDialog.MinDesiredSlotHeight")) + .SlotPadding(FAppStyle::GetMargin("StandardDialog.SlotPadding")) + .MinDesiredSlotWidth(FAppStyle::GetFloat("StandardDialog.MinDesiredSlotWidth")) + .MinDesiredSlotHeight(FAppStyle::GetFloat("StandardDialog.MinDesiredSlotHeight")) + SUniformGridPanel::Slot(0, 0) [ SNew(SButton) .HAlign(HAlign_Center) - .ContentPadding(FEditorStyle::GetMargin("StandardDialog.ContentPadding")) + .ContentPadding(FAppStyle::GetMargin("StandardDialog.ContentPadding")) .OnClicked(this, &SFlowNodeBlueprintCreateDialog::OkClicked) .Text(LOCTEXT("CreateFlowNodeBlueprintOk", "OK")) ] @@ -84,7 +84,7 @@ class SFlowNodeBlueprintCreateDialog final : public SCompoundWidget [ SNew(SButton) .HAlign(HAlign_Center) - .ContentPadding(FEditorStyle::GetMargin("StandardDialog.ContentPadding")) + .ContentPadding(FAppStyle::GetMargin("StandardDialog.ContentPadding")) .OnClicked(this, &SFlowNodeBlueprintCreateDialog::CancelClicked) .Text(LOCTEXT("CreateFlowNodeBlueprintCancel", "Cancel")) ] diff --git a/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h b/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h index 1676dcbba..7068f1505 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h @@ -8,6 +8,7 @@ #include "FlowAsset.h" class FFlowAssetEditor; +class UToolMenu; ////////////////////////////////////////////////////////////////////////// // Flow Asset Instance List diff --git a/Source/FlowEditor/Public/FlowEditorModule.h b/Source/FlowEditor/Public/FlowEditorModule.h index 0672e916d..506491fc7 100644 --- a/Source/FlowEditor/Public/FlowEditorModule.h +++ b/Source/FlowEditor/Public/FlowEditorModule.h @@ -3,8 +3,11 @@ #pragma once #include "AssetTypeCategories.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" #include "IAssetTypeActions.h" #include "Modules/ModuleInterface.h" +#include "PropertyEditorDelegates.h" +#include "Toolkits/IToolkit.h" class FSlateStyleSet; struct FGraphPanelPinConnectionFactory; From 0d0ac7c5690bd522216623c017747db161e01f95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Thu, 6 Oct 2022 20:45:44 +0200 Subject: [PATCH 014/485] --- UE 5.1 --- --- Flow.uplugin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.uplugin b/Flow.uplugin index fdba8368f..3ee28713c 100644 --- a/Flow.uplugin +++ b/Flow.uplugin @@ -8,7 +8,7 @@ "DocsURL" : "https://github.com/MothCocoon/FlowGraph/wiki", "MarketplaceURL" : "", "SupportURL": "https://discord.gg/zMtMQ2vUUa", - "EngineAssociation": "5.0", + "EngineAssociation": "5.1", "EnabledByDefault" : true, "CanContainContent" : false, "IsBetaVersion" : false, From 9bfd011e77e9423e0f10f95e83344354a44c1222 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Thu, 6 Oct 2022 21:05:37 +0200 Subject: [PATCH 015/485] Asset Search enabled by default --- Source/FlowEditor/Private/Asset/FlowAssetIndexer.cpp | 4 ++-- Source/FlowEditor/Private/FlowEditorModule.cpp | 6 +----- Source/FlowEditor/Public/Asset/FlowAssetIndexer.h | 5 ++--- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/Source/FlowEditor/Private/Asset/FlowAssetIndexer.cpp b/Source/FlowEditor/Private/Asset/FlowAssetIndexer.cpp index 55e1e5bdc..ee14123f5 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetIndexer.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetIndexer.cpp @@ -16,7 +16,7 @@ #define LOCTEXT_NAMESPACE "FFlowAssetIndexer" -/*enum class EFlowAssetIndexerVersion +enum class EFlowAssetIndexerVersion { Empty, Initial, @@ -133,6 +133,6 @@ void FFlowAssetIndexer::IndexGraph(const UFlowAsset* InFlowAsset, FSearchSeriali } } } -}*/ +} #undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/FlowEditorModule.cpp b/Source/FlowEditor/Private/FlowEditorModule.cpp index faf8797ff..7e7f7ba25 100644 --- a/Source/FlowEditor/Private/FlowEditorModule.cpp +++ b/Source/FlowEditor/Private/FlowEditorModule.cpp @@ -196,11 +196,7 @@ void FFlowEditorModule::ModulesChangesCallback(FName ModuleName, EModuleChangeRe void FFlowEditorModule::RegisterAssetIndexers() const { - /** - * Documentation: https://github.com/MothCocoon/FlowGraph/wiki/Asset-Search - * Uncomment line below, if you made these changes to the engine: https://github.com/EpicGames/UnrealEngine/pull/9070 - */ - //IAssetSearchModule::Get().RegisterAssetIndexer(UFlowAsset::StaticClass(), MakeUnique()); + IAssetSearchModule::Get().RegisterAssetIndexer(UFlowAsset::StaticClass(), MakeUnique()); } void FFlowEditorModule::CreateFlowToolbar(FToolBarBuilder& ToolbarBuilder) const diff --git a/Source/FlowEditor/Public/Asset/FlowAssetIndexer.h b/Source/FlowEditor/Public/Asset/FlowAssetIndexer.h index afdb90c89..acac56d80 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetIndexer.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetIndexer.h @@ -10,9 +10,8 @@ class FSearchSerializer; /** * Documentation: https://github.com/MothCocoon/FlowGraph/wiki/Asset-Search - * Uncomment entire class, if you made these changes to the engine: https://github.com/EpicGames/UnrealEngine/pull/9070 */ -/*class FLOWEDITOR_API FFlowAssetIndexer : public IAssetIndexer +class FLOWEDITOR_API FFlowAssetIndexer : public IAssetIndexer { public: virtual FString GetName() const override { return TEXT("FlowAsset"); } @@ -22,4 +21,4 @@ class FSearchSerializer; private: // Variant of FBlueprintIndexer::IndexGraphs void IndexGraph(const UFlowAsset* InFlowAsset, FSearchSerializer& Serializer) const; -};*/ +}; From 4279c8915c283b8fe0988e94d3e5e1d5085c5a97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Thu, 6 Oct 2022 21:21:19 +0200 Subject: [PATCH 016/485] API warnings fixed --- .../FlowEditor/Private/Graph/FlowGraphSchema.cpp | 2 +- .../Private/LevelEditor/SLevelEditorFlow.cpp | 15 +++++---------- .../Public/LevelEditor/SLevelEditorFlow.h | 6 ++---- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index 4e758b225..e1693572f 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -14,7 +14,7 @@ #include "Nodes/Route/FlowNode_Start.h" #include "Nodes/Route/FlowNode_Reroute.h" -#include "AssetRegistryModule.h" +#include "AssetRegistry/AssetRegistryModule.h" #include "Developer/ToolMenus/Public/ToolMenus.h" #include "EdGraph/EdGraph.h" #include "ScopedTransaction.h" diff --git a/Source/FlowEditor/Private/LevelEditor/SLevelEditorFlow.cpp b/Source/FlowEditor/Private/LevelEditor/SLevelEditorFlow.cpp index 11c18509e..b63971225 100644 --- a/Source/FlowEditor/Private/LevelEditor/SLevelEditorFlow.cpp +++ b/Source/FlowEditor/Private/LevelEditor/SLevelEditorFlow.cpp @@ -25,13 +25,13 @@ void SLevelEditorFlow::OnMapOpened(const FString& Filename, bool bAsTemplate) void SLevelEditorFlow::CreateFlowWidget() { - if (UFlowComponent* FlowComponent = FindFlowComponent(); FlowComponent && FlowComponent->RootFlow) + if (const UFlowComponent* FlowComponent = FindFlowComponent(); FlowComponent && FlowComponent->RootFlow) { - FlowPath = FName(*FlowComponent->RootFlow->GetPathName()); + FlowAssetPath = FlowComponent->RootFlow->GetPathName(); } else { - FlowPath = FName(); + FlowAssetPath = FString(); } ChildSlot @@ -44,14 +44,14 @@ void SLevelEditorFlow::CreateFlowWidget() .AllowedClass(UFlowGraphSettings::Get()->WorldAssetClass) .DisplayThumbnail(false) .OnObjectChanged(this, &SLevelEditorFlow::OnFlowChanged) - .ObjectPath(this, &SLevelEditorFlow::GetFlowPath) + .ObjectPath(FlowAssetPath) ] ]; } void SLevelEditorFlow::OnFlowChanged(const FAssetData& NewAsset) { - FlowPath = NewAsset.ObjectPath; + FlowAssetPath = NewAsset.GetSoftObjectPath().ToString(); if (UFlowComponent* FlowComponent = FindFlowComponent()) { @@ -69,11 +69,6 @@ void SLevelEditorFlow::OnFlowChanged(const FAssetData& NewAsset) } } -FString SLevelEditorFlow::GetFlowPath() const -{ - return FlowPath.IsValid() ? FlowPath.ToString() : FString(); -} - UFlowComponent* SLevelEditorFlow::FindFlowComponent() const { if (const UWorld* World = GEditor->GetEditorWorldContext().World()) diff --git a/Source/FlowEditor/Public/LevelEditor/SLevelEditorFlow.h b/Source/FlowEditor/Public/LevelEditor/SLevelEditorFlow.h index d004bce48..fbeed17ab 100644 --- a/Source/FlowEditor/Public/LevelEditor/SLevelEditorFlow.h +++ b/Source/FlowEditor/Public/LevelEditor/SLevelEditorFlow.h @@ -16,14 +16,12 @@ class FLOWEDITOR_API SLevelEditorFlow : public SCompoundWidget void Construct(const FArguments& InArgs); -private: +protected: void OnMapOpened(const FString& Filename, bool bAsTemplate); void CreateFlowWidget(); void OnFlowChanged(const FAssetData& NewAsset); - FString GetFlowPath() const; - class UFlowComponent* FindFlowComponent() const; - FName FlowPath; + FString FlowAssetPath; }; From 40f4dc172f7ba5578f4965a20b1ef4410a1621a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 9 Oct 2022 16:19:21 +0200 Subject: [PATCH 017/485] redundant include removed --- Source/FlowEditor/Private/FlowEditorCommands.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/FlowEditor/Private/FlowEditorCommands.cpp b/Source/FlowEditor/Private/FlowEditorCommands.cpp index eac9e011e..ee41942ad 100644 --- a/Source/FlowEditor/Private/FlowEditorCommands.cpp +++ b/Source/FlowEditor/Private/FlowEditorCommands.cpp @@ -3,7 +3,6 @@ #include "FlowEditorCommands.h" #include "FlowEditorStyle.h" -#include "Graph/FlowGraphSchema.h" #include "Graph/FlowGraphSchema_Actions.h" #include "Nodes/FlowNode.h" From 38319b78f923396a5cf5703bcf32f88c517e20e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 9 Oct 2022 16:19:48 +0200 Subject: [PATCH 018/485] moved icon from 5.1 to plugin resources --- Resources/Icons/Refresh.png | Bin 0 -> 5742 bytes Source/FlowEditor/Private/FlowEditorStyle.cpp | 7 +++---- 2 files changed, 3 insertions(+), 4 deletions(-) create mode 100644 Resources/Icons/Refresh.png diff --git a/Resources/Icons/Refresh.png b/Resources/Icons/Refresh.png new file mode 100644 index 0000000000000000000000000000000000000000..4bf3091f3ec3eaae54b613fa17358b1b4f8a0e0f GIT binary patch literal 5742 zcmV-!7LnKLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000Y_Nkl>oyMo zQZz`L1O;56D2f7cfg){Cw{99g6ire$X=A``8pp1ST9)ldc4$Si7E82DEtVx~BPkB| z8Im(|mvc@ZW=K&MRoFluf*=RDz`ek|=l;&O{Ll9tVT|Dyc}RZ2$FBf?=>hHe4-Jc! z01$-%&F#zCw)1zfq$XXLrmilHj(no1DAMWL?yjz`UD<5ru2{@n>UmzBW82dAi&HA{ z`=5R3tsnI4+w;Qmwr0}FINh5z6DdV;riksh1VMn-29QKi1W1ImP)bwCPx8u3&ti<> z-05RC0^Vw9rBTY%w6?W>adCU|mm0E}jh<%rIM+eU&ki?RS z`c$g6J&ZI_WU6roS{oe4O*J+&?XItH+}+W+@~@|-r;p_elZQ{e*ZaIN`sW~S223O~ z7-I+=pPjou|5xkRuKco!3|fGc1}ViHnAV2;REhENNy_C?!Ex-!cH*g_;i1!Ed3wUw z32fV59*cRIFw`hzFb0ePZ7Q&{&#q%Tj+@vzIMnZ)JbCsNr4+X9ERe;`T?BrK+tzJ) z^fM1V_UB=sD={VwQVU#Hpp@a{xr@AW{KUog&-VTJ>Xq|*E!6u!f-y2L7R#dg@7E zNGUL~;?k>wqwIbC&FJ*IJzqr!&v>p!NQ7nCl@$w7(XyE$3X51GC9>JZ4N)`;c-^JO z7_S_3>^wN;4CbXC|;Mi$!g#X>46}vjtP>?ABB&-5EyLt!F|*@2L+M8SOt%7`yyrJeej8 zX0YA(oE(LrPco4NqYy%(R7AWc(H`@>I;ARZ9UL0h$wWeWo`cqvm{dy8(b2iNxo!Ez z{P@suX;l{OdNm8XINQ*&86+`C@9h9j^32NQ!ADX!f{GL^-$Y_!oBt;MOnW8W=Ij-Pft zFS0BP75U@_FZK)$4h^@pwlvLpNo%N0rj|L5*BFK6+f zDV4u^`TWI}mgXi^jYEOJcHCsFrZ!tHnmIrr7T8ImFw|PlT2Nvz#A?HJ0cly- zj?0xZeH`ES;teYegEFJJOH7UpQkQMPc4JKCN14tK1A^%X*&w0io|NSY$*|z-aC= zqq)oTBuG-p4nz)p0 zShQAYjT8b)3M?atC6a4G-|q^_r4I)$oMoi{(kH5ljnP~_e~dL7?{BP4WtS>#h_uES zgAjrs48p-HXQs==Df%zI%ljwaM5*v*3yut5J>1@L+k;wb3<9Gd9)Oj! z?CA@=M;2nJsa^>n4tVqCRzMK=r4P%+sk^cbEp=fKqLjj5kkTRug8aAQ2z&e{S9~jr6r}nXcSO0qcDzS$;vw)?>n&n-zJ6!Cjkp+1loXBpsosKEzsu( zAOiI2JMQ_DL?THL2F&*3(sH?S>FkA`SH2g8LCFEaTQ*IF$&nLdBLk-q0&O(fXi9}L zu|VQDiOywf?*2Bg1b7JeEU*RW0MbAVaH?|}Kv&@maA?iO`*(NWwd>O=s#s5JjnW!z z;KbW|u7*MB0pPcQ2Y{|wHhE(vmZD4Nd%s)JCymjDnR0+#itvnNm^FWsNcv}Aw!!ngi) zDnI-h&@Y6Xk(PyJIk;Y&nq)o6R09o*I%wt)IcRsbGy(v*D`6yMD7YwddL+{bo zPEHp_azH&ZvLjU}>Qtn_85#*F`wFGB4*i+)MymlKW$2en8OMz+tEq|AH)K3KM=&u_A_$|o-2nuS8{Hsyid#5YT%|z<44xr|!~noLvup?T4FpJofp(FV1VI3=Q>hGMNO9<4`PX z%D$meqCw2Fh!o%g-Es?Zu~mSPe5-b?ZUk#4Q2EM@k7H zME!3(^7v1--TQ@mq9{PCXbut}QR9$Gdc-^%DJ9w%!bmYSw(arO z>(9RP>I>igcVl!^l_skigKq#s9~-cmsoOT){pb_-Jn#p*Qgsbh;1??;xe$Orsp~sJ z<%4R^EN`r~U9W~xaf&zh{L9Y{?tAv-stnoHe+&aZ2hPoJj6V+G>@KmTp}F(X?f3oO zXI5?4(dl|Ms3<^dRfYTLsssp-(#E!3lnOb2=J2_mz2EQYzx3X@s%Bb15jYPV1+Ff1 zc<^xmsS5B4U~|jj)w{a4es){O^6vJ!`WD-9V=QF&C<>U)j|TmhPF?Fg{6BjyoH_J7 zFg8!bT_9ftE8psL4&7%Gfh z`|w(B;Dd|BnaOKa2`K=Vfs+ea>;DUI{;+@)kgZ;I)j|Ov07_NSEdZ0%`|?ePFP{YP gS0}8$jDY_;0Aw{4pa(fKg8%>k07*qoM6N<$g6|Lo761SM literal 0 HcmV?d00001 diff --git a/Source/FlowEditor/Private/FlowEditorStyle.cpp b/Source/FlowEditor/Private/FlowEditorStyle.cpp index ab4046e05..e5954617e 100644 --- a/Source/FlowEditor/Private/FlowEditorStyle.cpp +++ b/Source/FlowEditor/Private/FlowEditorStyle.cpp @@ -30,9 +30,6 @@ void FFlowEditorStyle::Initialize() // engine assets StyleSet->SetContentRoot(FPaths::EngineContentDir() / TEXT("Editor/Slate/")); - StyleSet->Set("FlowToolbar.RefreshAsset", new IMAGE_BRUSH("Automation/RefreshTests", Icon40)); - StyleSet->Set("FlowToolbar.RefreshAsset.Small", new IMAGE_BRUSH("Automation/RefreshTests", Icon20)); - StyleSet->Set("FlowToolbar.GoToMasterInstance", new IMAGE_BRUSH("Icons/icon_DebugStepOut_40x", Icon40)); StyleSet->Set("FlowToolbar.GoToMasterInstance.Small", new IMAGE_BRUSH("Icons/icon_DebugStepOut_40x", Icon20)); @@ -41,7 +38,7 @@ void FFlowEditorStyle::Initialize() StyleSet->Set("FlowGraph.BreakpointHit", new IMAGE_BRUSH("Old/Kismet2/IP_Breakpoint", Icon40)); StyleSet->Set("FlowGraph.PinBreakpointHit", new IMAGE_BRUSH("Old/Kismet2/IP_Breakpoint", Icon30)); - StyleSet->Set( "GraphEditor.Sequence_16x", new IMAGE_BRUSH("Icons/icon_Blueprint_Sequence_16x", Icon16)); + StyleSet->Set("GraphEditor.Sequence_16x", new IMAGE_BRUSH("Icons/icon_Blueprint_Sequence_16x", Icon16)); // Flow assets StyleSet->SetContentRoot(IPluginManager::Get().FindPlugin(TEXT("Flow"))->GetBaseDir() / TEXT("Resources")); @@ -49,6 +46,8 @@ void FFlowEditorStyle::Initialize() StyleSet->Set("ClassIcon.FlowAsset", new IMAGE_BRUSH(TEXT("Icons/FlowAsset_16x"), Icon16)); StyleSet->Set("ClassThumbnail.FlowAsset", new IMAGE_BRUSH(TEXT("Icons/FlowAsset_64x"), Icon64)); + StyleSet->Set("FlowToolbar.RefreshAsset", new IMAGE_BRUSH("Icons/Refresh", Icon40)); + StyleSet->Set("Flow.Node.Title", new BOX_BRUSH("Icons/FlowNode_Title", FMargin(8.0f/64.0f, 0, 0, 0))); StyleSet->Set("Flow.Node.Body", new BOX_BRUSH("Icons/FlowNode_Body", FMargin(16.f/64.f))); StyleSet->Set("Flow.Node.ActiveShadow", new BOX_BRUSH("Icons/FlowNode_Shadow_Active", FMargin(18.0f/64.0f))); From a4e9ca2670cd951ad8f7767c48892636c4076df5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 9 Oct 2022 21:06:07 +0200 Subject: [PATCH 019/485] restored accidentally removed refresh function --- Source/FlowEditor/Private/LevelEditor/SLevelEditorFlow.cpp | 7 ++++++- Source/FlowEditor/Public/LevelEditor/SLevelEditorFlow.h | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Source/FlowEditor/Private/LevelEditor/SLevelEditorFlow.cpp b/Source/FlowEditor/Private/LevelEditor/SLevelEditorFlow.cpp index b63971225..8eb7ff7db 100644 --- a/Source/FlowEditor/Private/LevelEditor/SLevelEditorFlow.cpp +++ b/Source/FlowEditor/Private/LevelEditor/SLevelEditorFlow.cpp @@ -44,11 +44,16 @@ void SLevelEditorFlow::CreateFlowWidget() .AllowedClass(UFlowGraphSettings::Get()->WorldAssetClass) .DisplayThumbnail(false) .OnObjectChanged(this, &SLevelEditorFlow::OnFlowChanged) - .ObjectPath(FlowAssetPath) + .ObjectPath(this, &SLevelEditorFlow::GetFlowAssetPath) // needs function to automatically refresh view upon data change ] ]; } +FString SLevelEditorFlow::GetFlowAssetPath() const +{ + return FlowAssetPath; +} + void SLevelEditorFlow::OnFlowChanged(const FAssetData& NewAsset) { FlowAssetPath = NewAsset.GetSoftObjectPath().ToString(); diff --git a/Source/FlowEditor/Public/LevelEditor/SLevelEditorFlow.h b/Source/FlowEditor/Public/LevelEditor/SLevelEditorFlow.h index fbeed17ab..ba7c93de1 100644 --- a/Source/FlowEditor/Public/LevelEditor/SLevelEditorFlow.h +++ b/Source/FlowEditor/Public/LevelEditor/SLevelEditorFlow.h @@ -20,7 +20,9 @@ class FLOWEDITOR_API SLevelEditorFlow : public SCompoundWidget void OnMapOpened(const FString& Filename, bool bAsTemplate); void CreateFlowWidget(); + FString GetFlowAssetPath() const; void OnFlowChanged(const FAssetData& NewAsset); + class UFlowComponent* FindFlowComponent() const; FString FlowAssetPath; From d8f541e939a7354cfbf5e093868c41f466538c07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 9 Oct 2022 21:14:09 +0200 Subject: [PATCH 020/485] redundant include removed --- Source/Flow/Public/FlowSave.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Flow/Public/FlowSave.h b/Source/Flow/Public/FlowSave.h index fc9e939ef..1c2a07735 100644 --- a/Source/Flow/Public/FlowSave.h +++ b/Source/Flow/Public/FlowSave.h @@ -2,7 +2,6 @@ #pragma once -#include "CoreMinimal.h" #include "GameFramework/SaveGame.h" #include "Serialization/BufferArchive.h" #include "Serialization/ObjectAndNameAsStringProxyArchive.h" From c790e076e42d1f091af5980f61acab1f4b65e9c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Fri, 14 Oct 2022 21:40:55 +0200 Subject: [PATCH 021/485] #105 implemented visual Diff: properties, graph it requires engine modification to work https://github.com/MothCocoon/FlowGraph/wiki/Visual-Diff --- Source/FlowEditor/FlowEditor.Build.cs | 2 + .../Asset/AssetTypeActions_FlowAsset.cpp | 24 +- .../Private/Asset/FlowAssetToolbar.cpp | 120 ++- .../Private/Asset/FlowDiffControl.cpp | 193 ++++ .../Private/Asset/SAssetRevisionMenu.cpp | 236 +++++ Source/FlowEditor/Private/Asset/SFlowDiff.cpp | 862 ++++++++++++++++++ .../Public/Asset/AssetTypeActions_FlowAsset.h | 10 + .../Public/Asset/FlowAssetToolbar.h | 4 +- .../FlowEditor/Public/Asset/FlowDiffControl.h | 73 ++ .../Public/Asset/SAssetRevisionMenu.h | 53 ++ Source/FlowEditor/Public/Asset/SFlowDiff.h | 224 +++++ Source/FlowEditor/Public/FlowEditorDefines.h | 9 + 12 files changed, 1801 insertions(+), 9 deletions(-) create mode 100644 Source/FlowEditor/Private/Asset/FlowDiffControl.cpp create mode 100644 Source/FlowEditor/Private/Asset/SAssetRevisionMenu.cpp create mode 100644 Source/FlowEditor/Private/Asset/SFlowDiff.cpp create mode 100644 Source/FlowEditor/Public/Asset/FlowDiffControl.h create mode 100644 Source/FlowEditor/Public/Asset/SAssetRevisionMenu.h create mode 100644 Source/FlowEditor/Public/Asset/SFlowDiff.h create mode 100644 Source/FlowEditor/Public/FlowEditorDefines.h diff --git a/Source/FlowEditor/FlowEditor.Build.cs b/Source/FlowEditor/FlowEditor.Build.cs index 9623bb4ab..3f1c9ae2f 100644 --- a/Source/FlowEditor/FlowEditor.Build.cs +++ b/Source/FlowEditor/FlowEditor.Build.cs @@ -32,6 +32,7 @@ public FlowEditor(ReadOnlyTargetRules Target) : base(Target) "InputCore", "Json", "JsonUtilities", + "Kismet", "KismetWidgets", "LevelEditor", "MovieScene", @@ -43,6 +44,7 @@ public FlowEditor(ReadOnlyTargetRules Target) : base(Target) "Sequencer", "Slate", "SlateCore", + "SourceControl", "ToolMenus", "UnrealEd" }); diff --git a/Source/FlowEditor/Private/Asset/AssetTypeActions_FlowAsset.cpp b/Source/FlowEditor/Private/Asset/AssetTypeActions_FlowAsset.cpp index 576cfabef..a9edee225 100644 --- a/Source/FlowEditor/Private/Asset/AssetTypeActions_FlowAsset.cpp +++ b/Source/FlowEditor/Private/Asset/AssetTypeActions_FlowAsset.cpp @@ -1,6 +1,7 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "Asset/AssetTypeActions_FlowAsset.h" +#include "Asset/SFlowDiff.h" #include "FlowEditorModule.h" #include "Graph/FlowGraphSettings.h" @@ -33,10 +34,31 @@ void FAssetTypeActions_FlowAsset::OpenAssetEditor(const TArray& InObje { if (UFlowAsset* FlowAsset = Cast(*ObjIt)) { - FFlowEditorModule* FlowModule = &FModuleManager::LoadModuleChecked("FlowEditor"); + const FFlowEditorModule* FlowModule = &FModuleManager::LoadModuleChecked("FlowEditor"); FlowModule->CreateFlowAssetEditor(Mode, EditWithinLevelEditor, FlowAsset); } } } +/** + * Documentation: https://github.com/MothCocoon/FlowGraph/wiki/Visual-Diff + * Set macro value to 1, if you made these changes to the engine: https://github.com/EpicGames/UnrealEngine/pull/9659 + */ +#if ENABLE_FLOW_DIFF +void FAssetTypeActions_FlowAsset::PerformAssetDiff(UObject* OldAsset, UObject* NewAsset, const FRevisionInfo& OldRevision, const FRevisionInfo& NewRevision) const +{ + const UFlowAsset* OldFlow = CastChecked(OldAsset); + const UFlowAsset* NewFlow = CastChecked(NewAsset); + + // sometimes we're comparing different revisions of one single asset (other + // times we're comparing two completely separate assets altogether) + const bool bIsSingleAsset = (OldFlow->GetName() == NewFlow->GetName()); + + static const FText BasicWindowTitle = LOCTEXT("FlowAssetDiff", "FlowAsset Diff"); + const FText WindowTitle = !bIsSingleAsset ? BasicWindowTitle : FText::Format(LOCTEXT("FlowAsset Diff", "{0} - FlowAsset Diff"), FText::FromString(NewFlow->GetName())); + + SFlowDiff::CreateDiffWindow(WindowTitle, OldFlow, NewFlow, OldRevision, NewRevision); +} +#endif + #undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp index 3287b063a..6f7070369 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp @@ -1,8 +1,11 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "Asset/FlowAssetToolbar.h" + #include "Asset/FlowAssetEditor.h" +#include "Asset/SAssetRevisionMenu.h" #include "FlowEditorCommands.h" +#include "FlowEditorDefines.h" #include "FlowAsset.h" @@ -16,6 +19,12 @@ #include "Widgets/SBoxPanel.h" #include "Widgets/Text/STextBlock.h" +#include "AssetToolsModule.h" +#include "IAssetTypeActions.h" +#include "ISourceControlModule.h" +#include "ISourceControlProvider.h" +#include "SourceControlHelpers.h" + #define LOCTEXT_NAMESPACE "FlowDebuggerToolbar" ////////////////////////////////////////////////////////////////////////// @@ -38,10 +47,10 @@ void SFlowAssetInstanceList::Construct(const FArguments& InArgs, const TWeakObje .OnGenerateWidget(this, &SFlowAssetInstanceList::OnGenerateWidget) .OnSelectionChanged(this, &SFlowAssetInstanceList::OnSelectionChanged) .Visibility_Static(&FFlowAssetEditor::GetDebuggerVisibility) - [ - SNew(STextBlock) - .Text(this, &SFlowAssetInstanceList::GetSelectedInstanceName) - ]; + [ + SNew(STextBlock) + .Text(this, &SFlowAssetInstanceList::GetSelectedInstanceName) + ]; ChildSlot [ @@ -186,19 +195,116 @@ void FFlowAssetToolbar::BuildAssetToolbar(UToolMenu* ToolbarMenu) const { FToolMenuSection& Section = ToolbarMenu->AddSection("Editing"); Section.InsertPosition = FToolMenuInsert("Asset", EToolMenuInsertType::After); - + Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().RefreshAsset)); + + /** + * Documentation: https://github.com/MothCocoon/FlowGraph/wiki/Visual-Diff + * Set macro value to 1, if you made these changes to the engine: https://github.com/EpicGames/UnrealEngine/pull/9659 + */ +#if ENABLE_FLOW_DIFF + FToolMenuSection& DiffSection = ToolbarMenu->AddSection("SourceControl"); + DiffSection.InsertPosition = FToolMenuInsert("Asset", EToolMenuInsertType::After); + DiffSection.AddDynamicEntry("SourceControlCommands", FNewToolMenuSectionDelegate::CreateLambda([this](FToolMenuSection& InSection) + { + InSection.InsertPosition = FToolMenuInsert(); + FToolMenuEntry DiffEntry = FToolMenuEntry::InitComboButton( + "Diff", + FUIAction(), + FOnGetContent::CreateRaw(this, &FFlowAssetToolbar::MakeDiffMenu), + LOCTEXT("Diff", "Diff"), + LOCTEXT("FlowAssetEditorDiffToolTip", "Diff against previous revisions"), + FSlateIcon(FAppStyle::Get().GetStyleSetName(), "BlueprintDiff.ToolbarIcon") + ); + DiffEntry.StyleNameOverride = "CalloutToolbar"; + InSection.AddEntry(DiffEntry); + })); +#endif +} + +/** Delegate called to diff a specific revision with the current */ +// Copy from FBlueprintEditorToolbar::OnDiffRevisionPicked +static void OnDiffRevisionPicked(FRevisionInfo const& RevisionInfo, const FString& Filename, TWeakObjectPtr CurrentAsset) +{ + ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); + + // Get the SCC state + const FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(Filename, EStateCacheUsage::Use); + if (SourceControlState.IsValid()) + { + for (int32 HistoryIndex = 0; HistoryIndex < SourceControlState->GetHistorySize(); HistoryIndex++) + { + TSharedPtr Revision = SourceControlState->GetHistoryItem(HistoryIndex); + check(Revision.IsValid()); + if (Revision->GetRevision() == RevisionInfo.Revision) + { + // Get the revision of this package from source control + FString PreviousTempPkgName; + if (Revision->Get(PreviousTempPkgName)) + { + // Try and load that package + UPackage* PreviousTempPkg = LoadPackage(nullptr, *PreviousTempPkgName, LOAD_ForDiff | LOAD_DisableCompileOnLoad); + if (PreviousTempPkg) + { + const FString PreviousAssetName = FPaths::GetBaseFilename(Filename, true); + UObject* PreviousAsset = FindObject(PreviousTempPkg, *PreviousAssetName); + if (PreviousAsset) + { + const FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked(TEXT("AssetTools")); + const FRevisionInfo OldRevision = {Revision->GetRevision(), Revision->GetCheckInIdentifier(), Revision->GetDate()}; + const FRevisionInfo CurrentRevision = {TEXT(""), Revision->GetCheckInIdentifier(), Revision->GetDate()}; + AssetToolsModule.Get().DiffAssets(PreviousAsset, CurrentAsset.Get(), OldRevision, CurrentRevision); + } + } + else + { + FMessageDialog::Open(EAppMsgType::Ok, NSLOCTEXT("SourceControl.HistoryWindow", "UnableToLoadAssets", "Unable to load assets to diff. Content may no longer be supported?")); + } + } + break; + } + } + } +} + +// Variant of FBlueprintEditorToolbar::MakeDiffMenu +TSharedRef FFlowAssetToolbar::MakeDiffMenu() const +{ + if (ISourceControlModule::Get().IsEnabled() && ISourceControlModule::Get().GetProvider().IsAvailable()) + { + UFlowAsset* FlowAsset = FlowAssetEditor.Pin()->GetFlowAsset(); + if (FlowAsset) + { + FString Filename = SourceControlHelpers::PackageFilename(FlowAsset->GetPathName()); + TWeakObjectPtr AssetPtr = FlowAsset; + + // Add our async SCC task widget + return SNew(SAssetRevisionMenu, Filename) + .OnRevisionSelected_Static(&OnDiffRevisionPicked, AssetPtr); + } + else + { + // if asset is null then this means that multiple assets are selected + FMenuBuilder MenuBuilder(true, nullptr); + MenuBuilder.AddMenuEntry(LOCTEXT("NoRevisionsForMultipleFlowAssets", "Multiple Flow Assets selected"), FText(), FSlateIcon(), FUIAction()); + return MenuBuilder.MakeWidget(); + } + } + + FMenuBuilder MenuBuilder(true, nullptr); + MenuBuilder.AddMenuEntry(LOCTEXT("SourceControlDisabled", "Source control is disabled"), FText(), FSlateIcon(), FUIAction()); + return MenuBuilder.MakeWidget(); } void FFlowAssetToolbar::BuildDebuggerToolbar(UToolMenu* ToolbarMenu) { FToolMenuSection& Section = ToolbarMenu->AddSection("Debugging"); Section.InsertPosition = FToolMenuInsert("Asset", EToolMenuInsertType::After); - + FPlayWorldCommands::BuildToolbar(Section); TWeakObjectPtr TemplateAsset = FlowAssetEditor.Pin()->GetFlowAsset(); - + AssetInstanceList = SNew(SFlowAssetInstanceList, TemplateAsset); Section.AddEntry(FToolMenuEntry::InitWidget("AssetInstances", AssetInstanceList.ToSharedRef(), FText(), true)); diff --git a/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp b/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp new file mode 100644 index 000000000..cd68d6502 --- /dev/null +++ b/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp @@ -0,0 +1,193 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Asset/FlowDiffControl.h" + +/** + * Documentation: https://github.com/MothCocoon/FlowGraph/wiki/Visual-Diff + * Set macro value to 1, if you made these changes to the engine: https://github.com/EpicGames/UnrealEngine/pull/9659 + */ + +#if ENABLE_FLOW_DIFF +#include "Asset/SFlowDiff.h" + +#include "FlowAsset.h" + +#include "GraphDiffControl.h" +#include "SBlueprintDiff.h" + +#define LOCTEXT_NAMESPACE "SFlowDiffControl" + +///////////////////////////////////////////////////////////////////////////// +/// FFlowAssetDiffControl + +FFlowAssetDiffControl::FFlowAssetDiffControl(const UFlowAsset* InOldFlowAsset, const UFlowAsset* InNewFlowAsset, FOnDiffEntryFocused InSelectionCallback) + : TDetailsDiffControl(InOldFlowAsset, InNewFlowAsset, InSelectionCallback) +{ +} + +// TDetailsDiffControl::GenerateTreeEntries + "NoDifferences" entry + category label +void FFlowAssetDiffControl::GenerateTreeEntries(TArray>& OutTreeEntries, TArray>& OutRealDifferences) +{ + TDetailsDiffControl::GenerateTreeEntries(OutTreeEntries, OutRealDifferences); + + const bool bHasDifferences = Children.Num() != 0; + if (!bHasDifferences) + { + // make one child informing the user that there are no differences: + Children.Push(FBlueprintDifferenceTreeEntry::NoDifferencesEntry()); + } + + static const FText AssetPropertiesLabel = LOCTEXT("AssetPropertiesLabel", "Properties"); + static const FText AssetPropertiesTooltip = LOCTEXT("AssetPropertiesTooltip", "The list of changes made to Flow Asset properties."); + OutTreeEntries.Push(FBlueprintDifferenceTreeEntry::CreateCategoryEntry( + AssetPropertiesLabel, + AssetPropertiesTooltip, + SelectionCallback, + Children, + bHasDifferences + )); +} + +///////////////////////////////////////////////////////////////////////////// +/// FFlowGraphToDiff + +FFlowGraphToDiff::FFlowGraphToDiff(SFlowDiff* InDiffWidget, UEdGraph* InGraphOld, UEdGraph* InGraphNew, const FRevisionInfo& InRevisionOld, const FRevisionInfo& InRevisionNew) + : FoundDiffs(MakeShared>()) + , DiffWidget(InDiffWidget) + , GraphOld(InGraphOld) + , GraphNew(InGraphNew) + , RevisionOld(InRevisionOld) + , RevisionNew(InRevisionNew) +{ + check(InGraphOld || InGraphNew); + + if (InGraphNew) + { + OnGraphChangedDelegateHandle = InGraphNew->AddOnGraphChangedHandler(FOnGraphChanged::FDelegate::CreateRaw(this, &FFlowGraphToDiff::OnGraphChanged)); + } + + BuildDiffSourceArray(); +} + +FFlowGraphToDiff::~FFlowGraphToDiff() +{ + if (GraphNew) + { + GraphNew->RemoveOnGraphChangedHandler(OnGraphChangedDelegateHandle); + } +} + +void FFlowGraphToDiff::GenerateTreeEntries(TArray>& OutTreeEntries, TArray>& OutRealDifferences) +{ + if (!DiffListSource.IsEmpty()) + { + RealDifferencesStartIndex = OutRealDifferences.Num(); + } + + TArray> Children; + for (const TSharedPtr& Difference : DiffListSource) + { + TSharedPtr ChildEntry = MakeShared( + FOnDiffEntryFocused::CreateRaw(DiffWidget, &SFlowDiff::OnDiffListSelectionChanged, Difference), + FGenerateDiffEntryWidget::CreateSP(Difference.ToSharedRef(), &FDiffResultItem::GenerateWidget)); + Children.Push(ChildEntry); + OutRealDifferences.Push(ChildEntry); + } + + if (Children.Num() == 0) + { + // make one child informing the user that there are no differences: + Children.Push(FBlueprintDifferenceTreeEntry::NoDifferencesEntry()); + } + + const TSharedPtr Entry = MakeShared( + FOnDiffEntryFocused::CreateRaw(DiffWidget, &SFlowDiff::OnGraphSelectionChanged, TSharedPtr(AsShared()), ESelectInfo::Direct), + FGenerateDiffEntryWidget::CreateSP(AsShared(), &FFlowGraphToDiff::GenerateCategoryWidget), + Children); + OutTreeEntries.Push(Entry); +} + +FText FFlowGraphToDiff::GetToolTip() const +{ + if (GraphOld && GraphNew) + { + if (DiffListSource.Num() > 0) + { + return LOCTEXT("ContainsDifferences", "Revisions are different"); + } + else + { + return LOCTEXT("GraphsIdentical", "Revisions appear to be identical"); + } + } + else + { + const UEdGraph* GoodGraph = GraphOld ? GraphOld : GraphNew; + check(GoodGraph); + const FRevisionInfo& Revision = GraphNew ? RevisionOld : RevisionNew; + FText RevisionText = LOCTEXT("CurrentRevision", "Current Revision"); + + if (!Revision.Revision.IsEmpty()) + { + RevisionText = FText::Format(LOCTEXT("Revision Number", "Revision {0}"), FText::FromString(Revision.Revision)); + } + + return FText::Format(LOCTEXT("MissingGraph", "Graph '{0}' missing from {1}"), FText::FromString(GoodGraph->GetName()), RevisionText); + } +} + +TSharedRef FFlowGraphToDiff::GenerateCategoryWidget() const +{ + const UEdGraph* Graph = GraphOld ? GraphOld : GraphNew; + check(Graph); + + FLinearColor Color = (GraphOld && GraphNew) ? DiffViewUtils::Identical() : FLinearColor(0.3f, 0.3f, 1.f); + + const bool bHasDiffs = DiffListSource.Num() > 0; + + if (bHasDiffs) + { + Color = DiffViewUtils::Differs(); + } + + return SNew(SHorizontalBox) + + SHorizontalBox::Slot() + [ + SNew(STextBlock) + .ColorAndOpacity(Color) + .Text(FText::FromString(TEXT("Graph"))) + .ToolTipText(GetToolTip()) + ] + + DiffViewUtils::Box(GraphOld != nullptr, Color) + + DiffViewUtils::Box(GraphNew != nullptr, Color); +} + +void FFlowGraphToDiff::BuildDiffSourceArray() +{ + FoundDiffs->Empty(); + FGraphDiffControl::DiffGraphs(GraphOld, GraphNew, *FoundDiffs); + + struct SortDiff + { + bool operator ()(const FDiffSingleResult& A, const FDiffSingleResult& B) const + { + return A.Diff < B.Diff; + } + }; + + Sort(FoundDiffs->GetData(), FoundDiffs->Num(), SortDiff()); + + DiffListSource.Empty(); + for (const FDiffSingleResult& Diff : *FoundDiffs) + { + DiffListSource.Add(MakeShared(Diff)); + } +} + +void FFlowGraphToDiff::OnGraphChanged(const FEdGraphEditAction& Action) const +{ + DiffWidget->OnGraphChanged(this); +} + +#undef LOCTEXT_NAMESPACE +#endif diff --git a/Source/FlowEditor/Private/Asset/SAssetRevisionMenu.cpp b/Source/FlowEditor/Private/Asset/SAssetRevisionMenu.cpp new file mode 100644 index 000000000..e6b17761f --- /dev/null +++ b/Source/FlowEditor/Private/Asset/SAssetRevisionMenu.cpp @@ -0,0 +1,236 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Asset/SAssetRevisionMenu.h" + +#include "IAssetTypeActions.h" +#include "ISourceControlModule.h" +#include "ISourceControlRevision.h" +#include "SourceControlOperations.h" +#include "Widgets/Images/SThrobber.h" + +#define LOCTEXT_NAMESPACE "SFlowRevisionMenu" + +/** */ +namespace ESourceControlQueryState +{ + enum Type + { + NotQueried, + QueryInProgress, + Queried, + }; +} + +//------------------------------------------------------------------------------ +SAssetRevisionMenu::~SAssetRevisionMenu() +{ + // cancel any operation if this widget is destroyed while in progress + if (SourceControlQueryState == ESourceControlQueryState::QueryInProgress) + { + ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); + if (SourceControlQueryOp.IsValid() && SourceControlProvider.CanCancelOperation(SourceControlQueryOp.ToSharedRef())) + { + SourceControlProvider.CancelOperation(SourceControlQueryOp.ToSharedRef()); + } + } +} + +//------------------------------------------------------------------------------ +void SAssetRevisionMenu::Construct(const FArguments& InArgs, const FString& InFilename) +{ + bIncludeLocalRevision = InArgs._bIncludeLocalRevision; + OnRevisionSelected = InArgs._OnRevisionSelected; + + SourceControlQueryState = ESourceControlQueryState::NotQueried; + + ChildSlot + [ + SAssignNew(MenuBox, SVerticalBox) + + SVerticalBox::Slot() + [ + SNew(SBorder) + .Visibility(this, &SAssetRevisionMenu::GetInProgressVisibility) + .BorderImage(FAppStyle::GetBrush("Menu.Background")) + .Content() + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Center) + [ + SNew(SThrobber) + ] + + SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Center) + .Padding(2.0f, 0.0f, 4.0f, 0.0f) + [ + SNew(STextBlock) + .Text(LOCTEXT("DiffMenuOperationInProgress", "Updating history...")) + ] + + SHorizontalBox::Slot() + .FillWidth(1.0f) + .HAlign(HAlign_Right) + .VAlign(VAlign_Center) + [ + SNew(SButton) + .Visibility(this, &SAssetRevisionMenu::GetCancelButtonVisibility) + .OnClicked(this, &SAssetRevisionMenu::OnCancelButtonClicked) + .VAlign(VAlign_Center) + .HAlign(HAlign_Center) + .Content() + [ + SNew(STextBlock) + .Text(LOCTEXT("DiffMenuCancelButton", "Cancel")) + ] + ] + ] + ] + ]; + + Filename = InFilename; + if (!Filename.IsEmpty()) + { + // make sure the history info is up to date + SourceControlQueryOp = ISourceControlOperation::Create(); + SourceControlQueryOp->SetUpdateHistory(true); + ISourceControlModule::Get().GetProvider().Execute(SourceControlQueryOp.ToSharedRef(), Filename, EConcurrency::Asynchronous, FSourceControlOperationComplete::CreateSP(this, &SAssetRevisionMenu::OnSourceControlQueryComplete)); + + SourceControlQueryState = ESourceControlQueryState::QueryInProgress; + } +} + +//------------------------------------------------------------------------------ +EVisibility SAssetRevisionMenu::GetInProgressVisibility() const +{ + return (SourceControlQueryState == ESourceControlQueryState::QueryInProgress) ? EVisibility::Visible : EVisibility::Collapsed; +} + +//------------------------------------------------------------------------------ +EVisibility SAssetRevisionMenu::GetCancelButtonVisibility() const +{ + const ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); + return SourceControlQueryOp.IsValid() && SourceControlProvider.CanCancelOperation(SourceControlQueryOp.ToSharedRef()) ? EVisibility::Visible : EVisibility::Collapsed; +} + +//------------------------------------------------------------------------------ +FReply SAssetRevisionMenu::OnCancelButtonClicked() const +{ + if (SourceControlQueryOp.IsValid()) + { + ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); + SourceControlProvider.CancelOperation(SourceControlQueryOp.ToSharedRef()); + } + + return FReply::Handled(); +} + +//------------------------------------------------------------------------------ +void SAssetRevisionMenu::OnSourceControlQueryComplete(const FSourceControlOperationRef& InOperation, ECommandResult::Type InResult) +{ + check(SourceControlQueryOp == InOperation); + + + // Add pop-out menu for each revision + FMenuBuilder MenuBuilder(/*bInShouldCloseWindowAfterMenuSelection =*/true, /*InCommandList =*/nullptr); + + MenuBuilder.BeginSection("AddDiffRevision", LOCTEXT("Revisions", "Revisions")); + if (bIncludeLocalRevision) + { + FText const ToolTipText = LOCTEXT("LocalRevisionToolTip", "The current copy you have saved to disk (locally)"); + + FOnRevisionSelected OnRevisionSelectedDelegate = OnRevisionSelected; + auto OnMenuItemSelected = [OnRevisionSelectedDelegate, this]() + { + OnRevisionSelectedDelegate.ExecuteIfBound(FRevisionInfo::InvalidRevision(), Filename); + }; + + MenuBuilder.AddMenuEntry(LOCTEXT("LocalRevision", "Local"), ToolTipText, FSlateIcon(), FUIAction(FExecuteAction::CreateLambda(OnMenuItemSelected))); + } + + if (InResult == ECommandResult::Succeeded) + { + // get the cached state + ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); + FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(Filename, EStateCacheUsage::Use); + + if (SourceControlState.IsValid() && SourceControlState->GetHistorySize() > 0) + { + // Figure out the highest revision # (so we can label it "Depot") + int32 LatestRevision = 0; + for (int32 HistoryIndex = 0; HistoryIndex < SourceControlState->GetHistorySize(); HistoryIndex++) + { + TSharedPtr Revision = SourceControlState->GetHistoryItem(HistoryIndex); + if (Revision.IsValid() && Revision->GetRevisionNumber() > LatestRevision) + { + LatestRevision = Revision->GetRevisionNumber(); + } + } + + for (int32 HistoryIndex = 0; HistoryIndex < SourceControlState->GetHistorySize(); HistoryIndex++) + { + TSharedPtr Revision = SourceControlState->GetHistoryItem(HistoryIndex); + if (Revision.IsValid()) + { + FInternationalization& I18N = FInternationalization::Get(); + + FText Label = FText::Format(LOCTEXT("RevisionNumber", "Revision {0}"), FText::AsNumber(Revision->GetRevisionNumber(), nullptr, I18N.GetInvariantCulture())); + + FFormatNamedArguments Args; + Args.Add(TEXT("CheckInNumber"), FText::AsNumber(Revision->GetCheckInIdentifier(), nullptr, I18N.GetInvariantCulture())); + Args.Add(TEXT("Revision"), FText::FromString(Revision->GetRevision())); + Args.Add(TEXT("UserName"), FText::FromString(Revision->GetUserName())); + Args.Add(TEXT("DateTime"), FText::AsDate(Revision->GetDate())); + Args.Add(TEXT("ChanglistDescription"), FText::FromString(Revision->GetDescription())); + FText ToolTipText; + if (ISourceControlModule::Get().GetProvider().UsesChangelists()) + { + ToolTipText = FText::Format(LOCTEXT("ChangelistToolTip", "CL #{CheckInNumber} {UserName} \n{DateTime} \n{ChanglistDescription}"), Args); + } + else + { + ToolTipText = FText::Format(LOCTEXT("RevisionToolTip", "{Revision} {UserName} \n{DateTime} \n{ChanglistDescription}"), Args); + } + + if (LatestRevision == Revision->GetRevisionNumber()) + { + Label = LOCTEXT("Depo", "Depot"); + } + + FRevisionInfo RevisionInfo = { + Revision->GetRevision(), + Revision->GetCheckInIdentifier(), + Revision->GetDate() + }; + FOnRevisionSelected OnRevisionSelectedDelegate = OnRevisionSelected; + auto OnMenuItemSelected = [RevisionInfo, OnRevisionSelectedDelegate, this]() + { + OnRevisionSelectedDelegate.ExecuteIfBound(RevisionInfo, Filename); + }; + MenuBuilder.AddMenuEntry(TAttribute(Label), ToolTipText, FSlateIcon(), FUIAction(FExecuteAction::CreateLambda(OnMenuItemSelected))); + } + } + } + else if (!bIncludeLocalRevision) + { + // Show 'empty' item in toolbar + MenuBuilder.AddMenuEntry(LOCTEXT("NoRevisonHistory", "No revisions found"), FText(), FSlateIcon(), FUIAction()); + } + } + else if (!bIncludeLocalRevision) + { + // Show 'empty' item in toolbar + MenuBuilder.AddMenuEntry(LOCTEXT("NoRevisonHistory", "No revisions found"), FText(), FSlateIcon(), FUIAction()); + } + + MenuBuilder.EndSection(); + MenuBox->AddSlot() + [ + MenuBuilder.MakeWidget(nullptr, 500) + ]; + + SourceControlQueryOp.Reset(); + SourceControlQueryState = ESourceControlQueryState::Queried; +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/Asset/SFlowDiff.cpp b/Source/FlowEditor/Private/Asset/SFlowDiff.cpp new file mode 100644 index 000000000..ec58621f1 --- /dev/null +++ b/Source/FlowEditor/Private/Asset/SFlowDiff.cpp @@ -0,0 +1,862 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +/** + * Documentation: https://github.com/MothCocoon/FlowGraph/wiki/Visual-Diff + * Set macro value to 1, if you made these changes to the engine: https://github.com/EpicGames/UnrealEngine/pull/9659 + */ + +#include "Asset/SFlowDiff.h" + +#if ENABLE_FLOW_DIFF +#include "Asset/FlowDiffControl.h" + +#include "FlowAsset.h" + +#include "EdGraphUtilities.h" +#include "Framework/Commands/GenericCommands.h" +#include "GraphDiffControl.h" +#include "HAL/PlatformApplicationMisc.h" +#include "Internationalization/Text.h" +#include "SBlueprintDiff.h" +#include "SlateOptMacros.h" + +#define LOCTEXT_NAMESPACE "SFlowDiff" + +static const FName DetailsMode = FName(TEXT("DetailsMode")); +static const FName GraphMode = FName(TEXT("GraphMode")); + +FFlowDiffPanel::FFlowDiffPanel() + : FlowAsset(nullptr) + , bShowAssetName(false) +{ +} + +static int32 GetCurrentIndex(SListView> const& ListView, const TArray>& ListViewSource) +{ + const TArray>& Selected = ListView.GetSelectedItems(); + if (Selected.Num() == 1) + { + for (const TSharedPtr& Diff : ListViewSource) + { + if (Diff == Selected[0]) + { + return 0; + } + } + } + return -1; +} + +void FlowDiffUtils::SelectNextRow(SListView>& ListView, const TArray>& ListViewSource) +{ + const int32 CurrentIndex = GetCurrentIndex(ListView, ListViewSource); + if (CurrentIndex == ListViewSource.Num() - 1) + { + return; + } + + ListView.SetSelection(ListViewSource[CurrentIndex + 1]); +} + +void FlowDiffUtils::SelectPrevRow(SListView>& ListView, const TArray>& ListViewSource) +{ + const int32 CurrentIndex = GetCurrentIndex(ListView, ListViewSource); + if (CurrentIndex == 0) + { + return; + } + + ListView.SetSelection(ListViewSource[CurrentIndex - 1]); +} + +bool FlowDiffUtils::HasNextDifference(const SListView>& ListView, const TArray>& ListViewSource) +{ + const int32 CurrentIndex = GetCurrentIndex(ListView, ListViewSource); + return ListViewSource.IsValidIndex(CurrentIndex + 1); +} + +bool FlowDiffUtils::HasPrevDifference(const SListView>& ListView, const TArray>& ListViewSource) +{ + const int32 CurrentIndex = GetCurrentIndex(ListView, ListViewSource); + return ListViewSource.IsValidIndex(CurrentIndex - 1); +} + +BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION + +void SFlowDiff::Construct(const FArguments& InArgs) +{ + check(InArgs._OldFlow && InArgs._NewFlow); + PanelOld.FlowAsset = InArgs._OldFlow; + PanelNew.FlowAsset = InArgs._NewFlow; + PanelOld.RevisionInfo = InArgs._OldRevision; + PanelNew.RevisionInfo = InArgs._NewRevision; + + // sometimes we want to clearly identify the assets being diffed (when it's + // not the same asset in each panel) + PanelOld.bShowAssetName = InArgs._ShowAssetNames; + PanelNew.bShowAssetName = InArgs._ShowAssetNames; + + bLockViews = true; + + if (InArgs._ParentWindow.IsValid()) + { + WeakParentWindow = InArgs._ParentWindow; + + AssetEditorCloseDelegate = GEditor->GetEditorSubsystem()->OnAssetEditorRequestClose().AddSP(this, &SFlowDiff::OnCloseAssetEditor); + } + + FToolBarBuilder NavToolBarBuilder(TSharedPtr(), FMultiBoxCustomization::None); + NavToolBarBuilder.AddToolBarButton( + FUIAction( + FExecuteAction::CreateSP(this, &SFlowDiff::PrevDiff), + FCanExecuteAction::CreateSP(this, &SFlowDiff::HasPrevDiff) + ) + , NAME_None + , LOCTEXT("PrevDiffLabel", "Prev") + , LOCTEXT("PrevDiffTooltip", "Go to previous difference") + , FSlateIcon(FAppStyle::GetAppStyleSetName(), "BlueprintDif.PrevDiff") + ); + NavToolBarBuilder.AddToolBarButton( + FUIAction( + FExecuteAction::CreateSP(this, &SFlowDiff::NextDiff), + FCanExecuteAction::CreateSP(this, &SFlowDiff::HasNextDiff) + ) + , NAME_None + , LOCTEXT("NextDiffLabel", "Next") + , LOCTEXT("NextDiffTooltip", "Go to next difference") + , FSlateIcon(FAppStyle::GetAppStyleSetName(), "BlueprintDif.NextDiff") + ); + + FToolBarBuilder GraphToolbarBuilder(TSharedPtr(), FMultiBoxCustomization::None); + GraphToolbarBuilder.AddToolBarButton( + FUIAction(FExecuteAction::CreateSP(this, &SFlowDiff::OnToggleLockView)) + , NAME_None + , LOCTEXT("LockGraphsLabel", "Lock/Unlock") + , LOCTEXT("LockGraphsTooltip", "Force all graph views to change together, or allow independent scrolling/zooming") + , TAttribute(this, &SFlowDiff::GetLockViewImage) + ); + GraphToolbarBuilder.AddToolBarButton( + FUIAction(FExecuteAction::CreateSP(this, &SFlowDiff::OnToggleSplitViewMode)) + , NAME_None + , LOCTEXT("SplitGraphsModeLabel", "Vertical/Horizontal") + , LOCTEXT("SplitGraphsModeLabelTooltip", "Toggles the split view of graphs between vertical and horizontal") + , TAttribute(this, &SFlowDiff::GetSplitViewModeImage) + ); + + DifferencesTreeView = DiffTreeView::CreateTreeView(&PrimaryDifferencesList); + + GenerateDifferencesList(); + + const auto TextBlock = [](FText Text) -> TSharedRef + { + return SNew(SBox) + .Padding(FMargin(4.0f, 10.0f)) + .VAlign(VAlign_Center) + .HAlign(HAlign_Left) + [ + SNew(STextBlock) + .Visibility(EVisibility::HitTestInvisible) + .TextStyle(FAppStyle::Get(), "DetailsView.CategoryTextStyle") + .Text(Text) + ]; + }; + + TopRevisionInfoWidget = + SNew(SSplitter) + .Visibility(EVisibility::HitTestInvisible) + + SSplitter::Slot() + .Value(.2f) + [ + SNew(SBox) + ] + + SSplitter::Slot() + .Value(.8f) + [ + SNew(SSplitter) + .PhysicalSplitterHandleSize(10.0f) + + SSplitter::Slot() + .Value(.5f) + [ + TextBlock(DiffViewUtils::GetPanelLabel(PanelOld.FlowAsset, PanelOld.RevisionInfo, FText())) + ] + + SSplitter::Slot() + .Value(.5f) + [ + TextBlock(DiffViewUtils::GetPanelLabel(PanelNew.FlowAsset, PanelNew.RevisionInfo, FText())) + ] + ]; + + GraphToolBarWidget = + SNew(SSplitter) + .Visibility(EVisibility::HitTestInvisible) + + SSplitter::Slot() + .Value(.2f) + [ + SNew(SBox) + ] + + SSplitter::Slot() + .Value(.8f) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + [ + GraphToolbarBuilder.MakeWidget() + ] + ]; + + this->ChildSlot + [ + SNew(SBorder) + .BorderImage(FAppStyle::GetBrush("Docking.Tab", ".ContentAreaBrush")) + [ + SNew(SOverlay) + + SOverlay::Slot() + .VAlign(VAlign_Top) + [ + TopRevisionInfoWidget.ToSharedRef() + ] + + SOverlay::Slot() + .VAlign(VAlign_Top) + .Padding(0.0f, 6.0f, 0.0f, 4.0f) + [ + GraphToolBarWidget.ToSharedRef() + ] + + SOverlay::Slot() + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .AutoHeight() + .Padding(0.0f, 2.0f, 0.0f, 2.0f) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .Padding(4.f) + .AutoWidth() + [ + NavToolBarBuilder.MakeWidget() + ] + + SHorizontalBox::Slot() + [ + SNew(SSpacer) + ] + ] + + SVerticalBox::Slot() + [ + SNew(SSplitter) + + SSplitter::Slot() + .Value(.2f) + [ + SNew(SBorder) + .BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder")) + [ + DifferencesTreeView.ToSharedRef() + ] + ] + + SSplitter::Slot() + .Value(.8f) + [ + SAssignNew(ModeContents, SBox) + ] + ] + ] + ] + ]; + + SetCurrentMode(DetailsMode); +} + +END_SLATE_FUNCTION_BUILD_OPTIMIZATION + +SFlowDiff::~SFlowDiff() +{ + if (AssetEditorCloseDelegate.IsValid()) + { + GEditor->GetEditorSubsystem()->OnAssetEditorRequestClose().Remove(AssetEditorCloseDelegate); + } +} + +void SFlowDiff::OnCloseAssetEditor(UObject* Asset, const EAssetEditorCloseReason CloseReason) +{ + if (PanelOld.FlowAsset == Asset || PanelNew.FlowAsset == Asset || CloseReason == EAssetEditorCloseReason::CloseAllAssetEditors) + { + // Tell our window to close and set our selves to collapsed to try and stop it from ticking + SetVisibility(EVisibility::Collapsed); + + if (AssetEditorCloseDelegate.IsValid()) + { + GEditor->GetEditorSubsystem()->OnAssetEditorRequestClose().Remove(AssetEditorCloseDelegate); + } + + if (WeakParentWindow.IsValid()) + { + WeakParentWindow.Pin()->RequestDestroyWindow(); + } + } +} + +void SFlowDiff::OnGraphSelectionChanged(const TSharedPtr Item, ESelectInfo::Type SelectionType) +{ + if (!Item.IsValid()) + { + return; + } + + FocusOnGraphRevisions(Item.Get()); +} + +void SFlowDiff::OnGraphChanged(const FFlowGraphToDiff* Diff) +{ + if (PanelNew.GraphEditor.IsValid() && PanelNew.GraphEditor.Pin()->GetCurrentGraph() == Diff->GetGraphNew()) + { + FocusOnGraphRevisions(Diff); + } +} + +TSharedRef SFlowDiff::DefaultEmptyPanel() +{ + return SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .HAlign(HAlign_Center) + .VAlign(VAlign_Center) + [ + SNew(STextBlock) + .Text(LOCTEXT("BlueprintDifGraphsToolTip", "Select Graph to Diff")) + ]; +} + +TSharedPtr SFlowDiff::CreateDiffWindow(const FText WindowTitle, const UFlowAsset* OldFlow, const UFlowAsset* NewFlow, const FRevisionInfo& OldRevision, const FRevisionInfo& NewRevision) +{ + // sometimes we're comparing different revisions of one single asset (other + // times we're comparing two completely separate assets altogether) + const bool bIsSingleAsset = (NewFlow->GetName() == OldFlow->GetName()); + + TSharedPtr Window = SNew(SWindow) + .Title(WindowTitle) + .ClientSize(FVector2D(1000, 800)); + + Window->SetContent(SNew(SFlowDiff) + .OldFlow(OldFlow) + .NewFlow(NewFlow) + .OldRevision(OldRevision) + .NewRevision(NewRevision) + .ShowAssetNames(!bIsSingleAsset) + .ParentWindow(Window)); + + // Make this window a child of the modal window if we've been spawned while one is active. + const TSharedPtr ActiveModal = FSlateApplication::Get().GetActiveModalWindow(); + if (ActiveModal.IsValid()) + { + FSlateApplication::Get().AddWindowAsNativeChild(Window.ToSharedRef(), ActiveModal.ToSharedRef()); + } + else + { + FSlateApplication::Get().AddWindow(Window.ToSharedRef()); + } + + return Window; +} + +void SFlowDiff::NextDiff() const +{ + DiffTreeView::HighlightNextDifference(DifferencesTreeView.ToSharedRef(), RealDifferences, PrimaryDifferencesList); +} + +void SFlowDiff::PrevDiff() const +{ + DiffTreeView::HighlightPrevDifference(DifferencesTreeView.ToSharedRef(), RealDifferences, PrimaryDifferencesList); +} + +bool SFlowDiff::HasNextDiff() const +{ + return DiffTreeView::HasNextDifference(DifferencesTreeView.ToSharedRef(), RealDifferences); +} + +bool SFlowDiff::HasPrevDiff() const +{ + return DiffTreeView::HasPrevDifference(DifferencesTreeView.ToSharedRef(), RealDifferences); +} + +FFlowGraphToDiff* SFlowDiff::FindGraphToDiffEntry(const FString& GraphPath) const +{ + const FString SearchGraphPath = GraphToDiff->GetGraphOld() ? FGraphDiffControl::GetGraphPath(GraphToDiff->GetGraphOld()) : FGraphDiffControl::GetGraphPath(GraphToDiff->GetGraphNew()); + if (SearchGraphPath.Equals(GraphPath, ESearchCase::CaseSensitive)) + { + return GraphToDiff.Get(); + } + + return nullptr; +} + +void SFlowDiff::FocusOnGraphRevisions(const FFlowGraphToDiff* Diff) +{ + UEdGraph* Graph = Diff->GetGraphOld() ? Diff->GetGraphOld() : Diff->GetGraphNew(); + + const FString GraphPath = FGraphDiffControl::GetGraphPath(Graph); + HandleGraphChanged(GraphPath); + + ResetGraphEditors(); +} + +void SFlowDiff::OnDiffListSelectionChanged(TSharedPtr TheDiff) +{ + check(!TheDiff->Result.OwningObjectPath.IsEmpty()); + FocusOnGraphRevisions(FindGraphToDiffEntry(TheDiff->Result.OwningObjectPath)); + const FDiffSingleResult Result = TheDiff->Result; + + const auto SafeClearSelection = [](TWeakPtr GraphEditor) + { + const TSharedPtr GraphEditorPtr = GraphEditor.Pin(); + if (GraphEditorPtr.IsValid()) + { + GraphEditorPtr->ClearSelectionSet(); + } + }; + + SafeClearSelection(PanelNew.GraphEditor); + SafeClearSelection(PanelOld.GraphEditor); + + if (Result.Pin1) + { + GetDiffPanelForNode(*Result.Pin1->GetOwningNode()).FocusDiff(*Result.Pin1); + if (Result.Pin2) + { + GetDiffPanelForNode(*Result.Pin2->GetOwningNode()).FocusDiff(*Result.Pin2); + } + } + else if (Result.Node1) + { + GetDiffPanelForNode(*Result.Node1).FocusDiff(*Result.Node1); + if (Result.Node2) + { + GetDiffPanelForNode(*Result.Node2).FocusDiff(*Result.Node2); + } + } +} + +void SFlowDiff::OnToggleLockView() +{ + bLockViews = !bLockViews; + ResetGraphEditors(); +} + +void SFlowDiff::OnToggleSplitViewMode() +{ + bVerticalSplitGraphMode = !bVerticalSplitGraphMode; + + if (SSplitter* DiffGraphSplitterPtr = DiffGraphSplitter.Get()) + { + DiffGraphSplitterPtr->SetOrientation(bVerticalSplitGraphMode ? Orient_Horizontal : Orient_Vertical); + } +} + +FSlateIcon SFlowDiff::GetLockViewImage() const +{ + return FSlateIcon(FAppStyle::GetAppStyleSetName(), bLockViews ? "Icons.Lock" : "Icons.Unlock"); +} + +FSlateIcon SFlowDiff::GetSplitViewModeImage() const +{ + return FSlateIcon(FAppStyle::GetAppStyleSetName(), bVerticalSplitGraphMode ? "BlueprintDif.VerticalDiff.Small" : "BlueprintDif.HorizontalDiff.Small"); +} + +void SFlowDiff::ResetGraphEditors() const +{ + if (PanelOld.GraphEditor.IsValid() && PanelNew.GraphEditor.IsValid()) + { + if (bLockViews) + { + PanelOld.GraphEditor.Pin()->LockToGraphEditor(PanelNew.GraphEditor); + PanelNew.GraphEditor.Pin()->LockToGraphEditor(PanelOld.GraphEditor); + } + else + { + PanelOld.GraphEditor.Pin()->UnlockFromGraphEditor(PanelNew.GraphEditor); + PanelNew.GraphEditor.Pin()->UnlockFromGraphEditor(PanelOld.GraphEditor); + } + } +} + +void FFlowDiffPanel::GeneratePanel(UEdGraph* NewGraph, UEdGraph* OldGraph) +{ + const TSharedPtr> Diff = MakeShared>(); + FGraphDiffControl::DiffGraphs(OldGraph, NewGraph, *Diff); + GeneratePanel(NewGraph, Diff, {}); +} + +void FFlowDiffPanel::GeneratePanel(UEdGraph* Graph, TSharedPtr> DiffResults, TAttribute FocusedDiffResult) +{ + if (GraphEditor.IsValid() && GraphEditor.Pin()->GetCurrentGraph() == Graph) + { + return; + } + + TSharedPtr Widget = SNew(SBorder) + .HAlign(HAlign_Center) + .VAlign(VAlign_Center) + [ + SNew(STextBlock).Text(LOCTEXT("FlowDiffPanelNoGraphTip", "Graph does not exist in this revision")) + ]; + + if (Graph) + { + SGraphEditor::FGraphEditorEvents InEvents; + { + const auto SelectionChangedHandler = [](const FGraphPanelSelectionSet& SelectionSet, TSharedPtr Container) + { + Container->SetObjects(SelectionSet.Array()); + }; + + const auto ContextMenuHandler = [](UEdGraph* CurrentGraph, const UEdGraphNode* InGraphNode, const UEdGraphPin* InGraphPin, FMenuBuilder* MenuBuilder, bool bIsDebugging) + { + MenuBuilder->AddMenuEntry(FGenericCommands::Get().Copy); + return FActionMenuContent(MenuBuilder->MakeWidget()); + }; + + InEvents.OnSelectionChanged = SGraphEditor::FOnSelectionChanged::CreateStatic(SelectionChangedHandler, DetailsView); + InEvents.OnCreateNodeOrPinMenu = SGraphEditor::FOnCreateNodeOrPinMenu::CreateStatic(ContextMenuHandler); + } + + if (!GraphEditorCommands.IsValid()) + { + GraphEditorCommands = MakeShared(); + + GraphEditorCommands->MapAction(FGenericCommands::Get().Copy, + FExecuteAction::CreateRaw(this, &FFlowDiffPanel::CopySelectedNodes), + FCanExecuteAction::CreateRaw(this, &FFlowDiffPanel::CanCopyNodes) + ); + } + + const TSharedRef Editor = SNew(SGraphEditor) + .AdditionalCommands(GraphEditorCommands) + .GraphToEdit(Graph) + .GraphToDiff(nullptr) + .DiffResults(DiffResults) + .FocusedDiffResult(FocusedDiffResult) + .IsEditable(false) + .GraphEvents(InEvents); + + GraphEditor = Editor; + Widget = Editor; + } + + GraphEditorBox->SetContent(Widget.ToSharedRef()); +} + +FGraphPanelSelectionSet FFlowDiffPanel::GetSelectedNodes() const +{ + FGraphPanelSelectionSet CurrentSelection; + const TSharedPtr FocusedGraphEd = GraphEditor.Pin(); + if (FocusedGraphEd.IsValid()) + { + CurrentSelection = FocusedGraphEd->GetSelectedNodes(); + } + return CurrentSelection; +} + +void FFlowDiffPanel::CopySelectedNodes() const +{ + // Export the selected nodes and place the text on the clipboard + const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes(); + + FString ExportedText; + FEdGraphUtilities::ExportNodesToText(SelectedNodes, /*out*/ ExportedText); + FPlatformApplicationMisc::ClipboardCopy(*ExportedText); +} + +bool FFlowDiffPanel::CanCopyNodes() const +{ + // If any of the nodes can be duplicated then we should allow copying + const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes(); + for (FGraphPanelSelectionSet::TConstIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter) + { + const UEdGraphNode* Node = Cast(*SelectedIter); + if ((Node != nullptr) && Node->CanDuplicateNode()) + { + return true; + } + } + return false; +} + +void FFlowDiffPanel::FocusDiff(const UEdGraphPin& Pin) const +{ + GraphEditor.Pin()->JumpToPin(&Pin); +} + +void FFlowDiffPanel::FocusDiff(const UEdGraphNode& Node) const +{ + if (GraphEditor.IsValid()) + { + GraphEditor.Pin()->JumpToNode(&Node, false); + } +} + +FFlowDiffPanel& SFlowDiff::GetDiffPanelForNode(const UEdGraphNode& Node) +{ + const TSharedPtr OldGraphEditorPtr = PanelOld.GraphEditor.Pin(); + if (OldGraphEditorPtr.IsValid() && Node.GetGraph() == OldGraphEditorPtr->GetCurrentGraph()) + { + return PanelOld; + } + + const TSharedPtr NewGraphEditorPtr = PanelNew.GraphEditor.Pin(); + if (NewGraphEditorPtr.IsValid() && Node.GetGraph() == NewGraphEditorPtr->GetCurrentGraph()) + { + return PanelNew; + } + + ensureMsgf(false, TEXT("Looking for node %s but it cannot be found in provided panels"), *Node.GetName()); + static FFlowDiffPanel Default; + return Default; +} + +void SFlowDiff::HandleGraphChanged(const FString& GraphPath) +{ + SetCurrentMode(GraphMode); + + UEdGraph* GraphOld = nullptr; + UEdGraph* GraphNew = nullptr; + TSharedPtr> DiffResults; + int32 RealDifferencesStartIndex = INDEX_NONE; + { + UEdGraph* NewGraph = GraphToDiff->GetGraphNew(); + UEdGraph* OldGraph = GraphToDiff->GetGraphOld(); + const FString OtherGraphPath = NewGraph ? FGraphDiffControl::GetGraphPath(NewGraph) : FGraphDiffControl::GetGraphPath(OldGraph); + if (GraphPath.Equals(OtherGraphPath)) + { + GraphNew = NewGraph; + GraphOld = OldGraph; + DiffResults = GraphToDiff->FoundDiffs; + RealDifferencesStartIndex = GraphToDiff->RealDifferencesStartIndex; + } + } + + const TAttribute FocusedDiffResult = TAttribute::CreateLambda( + [this, RealDifferencesStartIndex]() + { + int32 FocusedDiffResult = INDEX_NONE; + if (RealDifferencesStartIndex != INDEX_NONE) + { + FocusedDiffResult = DiffTreeView::CurrentDifference(DifferencesTreeView.ToSharedRef(), RealDifferences) - RealDifferencesStartIndex; + } + + // find selected index in all the graphs, and subtract the index of the first entry in this graph + return FocusedDiffResult; + }); + + // only regenerate PanelOld if the old graph has changed + if (!PanelOld.GraphEditor.IsValid() || GraphOld != PanelOld.GraphEditor.Pin()->GetCurrentGraph()) + { + PanelOld.GeneratePanel(GraphOld, DiffResults, FocusedDiffResult); + } + + // only regenerate PanelNew if the old graph has changed + if (!PanelNew.GraphEditor.IsValid() || GraphNew != PanelNew.GraphEditor.Pin()->GetCurrentGraph()) + { + PanelNew.GeneratePanel(GraphNew, DiffResults, FocusedDiffResult); + } +} + +void SFlowDiff::GenerateDifferencesList() +{ + PrimaryDifferencesList.Empty(); + RealDifferences.Empty(); + ModePanels.Empty(); + + const auto CreateInspector = [](const UObject* Object) + { + FPropertyEditorModule& EditModule = FModuleManager::Get().GetModuleChecked("PropertyEditor"); + + FNotifyHook* NotifyHook = nullptr; + + FDetailsViewArgs DetailsViewArgs; + DetailsViewArgs.NameAreaSettings = FDetailsViewArgs::HideNameArea; + DetailsViewArgs.bHideSelectionTip = true; + DetailsViewArgs.NotifyHook = NotifyHook; + DetailsViewArgs.ViewIdentifier = FName("ObjectInspector"); + TSharedRef DetailsView = EditModule.CreateDetailView(DetailsViewArgs); + DetailsView->SetObject(const_cast(Object)); + + return DetailsView; + }; + + // TODO: construct DetailsView of PanelOld and PanelNew + PanelOld.DetailsView = CreateInspector(PanelOld.FlowAsset); + PanelNew.DetailsView = CreateInspector(PanelOld.FlowAsset); + + // Now that we have done the diffs, create the panel widgets + ModePanels.Add(DetailsMode, GenerateDetailsPanel()); + ModePanels.Add(GraphMode, GenerateGraphPanel()); + + DifferencesTreeView->RebuildList(); +} + +SFlowDiff::FDiffControl SFlowDiff::GenerateDetailsPanel() +{ + const TSharedPtr NewDiffControl = MakeShared(PanelOld.FlowAsset, PanelNew.FlowAsset, FOnDiffEntryFocused::CreateRaw(this, &SFlowDiff::SetCurrentMode, DetailsMode)); + NewDiffControl->GenerateTreeEntries(PrimaryDifferencesList, RealDifferences); + + SFlowDiff::FDiffControl Ret; + Ret.DiffControl = NewDiffControl; + Ret.Widget = SNew(SSplitter) + .PhysicalSplitterHandleSize(10.0f) + + SSplitter::Slot() + .Value(0.5f) + [ + NewDiffControl->OldDetailsWidget() + ] + + SSplitter::Slot() + .Value(0.5f) + [ + NewDiffControl->NewDetailsWidget() + ]; + + return Ret; +} + +SFlowDiff::FDiffControl SFlowDiff::GenerateGraphPanel() +{ + // We only have a single permanent graph in Flow Asset + GraphToDiff = MakeShared(this, PanelOld.FlowAsset->GetGraph(), PanelNew.FlowAsset->GetGraph(), PanelOld.RevisionInfo, PanelNew.RevisionInfo); + GraphToDiff->GenerateTreeEntries(PrimaryDifferencesList, RealDifferences); + + FDiffControl Ret; + + Ret.Widget = SNew(SVerticalBox) + + SVerticalBox::Slot() + .FillHeight(1.f) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .FillWidth(1.f) + [ + //diff window + SNew(SSplitter) + .Orientation(Orient_Vertical) + + SSplitter::Slot() + .Value(.8f) + [ + SAssignNew(DiffGraphSplitter, SSplitter) + .PhysicalSplitterHandleSize(10.0f) + .Orientation(bVerticalSplitGraphMode ? Orient_Horizontal : Orient_Vertical) + + SSplitter::Slot() // Old revision graph slot + [ + GenerateGraphWidgetForPanel(PanelOld) + ] + + SSplitter::Slot() // New revision graph slot + [ + GenerateGraphWidgetForPanel(PanelNew) + ] + ] + + SSplitter::Slot() + .Value(.2f) + [ + SNew(SSplitter) + .PhysicalSplitterHandleSize(10.0f) + + SSplitter::Slot() + [ + PanelOld.DetailsView.ToSharedRef() + ] + + SSplitter::Slot() + [ + PanelNew.DetailsView.ToSharedRef() + ] + ] + ] + ]; + + return Ret; +} + +TSharedRef SFlowDiff::GenerateGraphWidgetForPanel(FFlowDiffPanel& OutDiffPanel) const +{ + return SNew(SOverlay) + + SOverlay::Slot() // Graph slot + [ + SAssignNew(OutDiffPanel.GraphEditorBox, SBox) + .HAlign(HAlign_Fill) + [ + DefaultEmptyPanel() + ] + ] + + SOverlay::Slot() // Revision info slot + .VAlign(VAlign_Bottom) + .HAlign(HAlign_Right) + .Padding(FMargin(20.0f, 10.0f)) + [ + GenerateRevisionInfoWidgetForPanel(OutDiffPanel.OverlayGraphRevisionInfo, DiffViewUtils::GetPanelLabel(OutDiffPanel.FlowAsset, OutDiffPanel.RevisionInfo, FText())) + ]; +} + +TSharedRef SFlowDiff::GenerateRevisionInfoWidgetForPanel(TSharedPtr& OutGeneratedWidget, const FText& InRevisionText) const +{ + return SAssignNew(OutGeneratedWidget, SBox) + .Padding(FMargin(4.0f, 10.0f)) + .VAlign(VAlign_Center) + .HAlign(HAlign_Left) + [ + SNew(STextBlock) + .TextStyle(FAppStyle::Get(), "DetailsView.CategoryTextStyle") + .Text(InRevisionText) + .ShadowColorAndOpacity(FColor::Black) + .ShadowOffset(FVector2D(1.4, 1.4)) + ]; +} + +void SFlowDiff::SetCurrentMode(FName NewMode) +{ + if (CurrentMode == NewMode) + { + return; + } + + CurrentMode = NewMode; + + const FDiffControl* FoundControl = ModePanels.Find(NewMode); + + if (FoundControl) + { + // Reset inspector view + PanelOld.DetailsView->SetObjects(TArray()); + PanelNew.DetailsView->SetObjects(TArray()); + + ModeContents->SetContent(FoundControl->Widget.ToSharedRef()); + } + else + { + ensureMsgf(false, TEXT("Diff panel does not support mode %s"), *NewMode.ToString()); + } + + OnModeChanged(NewMode); +} + +void SFlowDiff::UpdateTopSectionVisibility(const FName& InNewViewMode) const +{ + SSplitter* GraphToolBarPtr = GraphToolBarWidget.Get(); + SSplitter* TopRevisionInfoWidgetPtr = TopRevisionInfoWidget.Get(); + + if (!GraphToolBarPtr || !TopRevisionInfoWidgetPtr) + { + return; + } + + if (InNewViewMode == GraphMode) + { + GraphToolBarPtr->SetVisibility(EVisibility::Visible); + TopRevisionInfoWidgetPtr->SetVisibility(EVisibility::Collapsed); + } + else + { + GraphToolBarPtr->SetVisibility(EVisibility::Collapsed); + TopRevisionInfoWidgetPtr->SetVisibility(EVisibility::HitTestInvisible); + } +} + +void SFlowDiff::OnModeChanged(const FName& InNewViewMode) const +{ + UpdateTopSectionVisibility(InNewViewMode); +} + +#undef LOCTEXT_NAMESPACE +#endif diff --git a/Source/FlowEditor/Public/Asset/AssetTypeActions_FlowAsset.h b/Source/FlowEditor/Public/Asset/AssetTypeActions_FlowAsset.h index bee8c6b8f..dfab704c8 100644 --- a/Source/FlowEditor/Public/Asset/AssetTypeActions_FlowAsset.h +++ b/Source/FlowEditor/Public/Asset/AssetTypeActions_FlowAsset.h @@ -5,6 +5,8 @@ #include "AssetTypeActions_Base.h" #include "Toolkits/IToolkitHost.h" +#include "FlowEditorDefines.h" + class FLOWEDITOR_API FAssetTypeActions_FlowAsset : public FAssetTypeActions_Base { public: @@ -14,4 +16,12 @@ class FLOWEDITOR_API FAssetTypeActions_FlowAsset : public FAssetTypeActions_Base virtual UClass* GetSupportedClass() const override; virtual void OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor = TSharedPtr()) override; + + /** + * Documentation: https://github.com/MothCocoon/FlowGraph/wiki/Visual-Diff + * Set macro value to 1, if you made these changes to the engine: https://github.com/EpicGames/UnrealEngine/pull/9659 + */ +#if ENABLE_FLOW_DIFF + virtual void PerformAssetDiff(UObject* OldAsset, UObject* NewAsset, const FRevisionInfo& OldRevision, const FRevisionInfo& NewRevision) const override; +#endif }; diff --git a/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h b/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h index 7068f1505..dbc72ca8f 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h @@ -86,9 +86,11 @@ class FLOWEDITOR_API FFlowAssetToolbar final : public TSharedFromThis MakeDiffMenu() const; + void BuildDebuggerToolbar(UToolMenu* ToolbarMenu); -public: +public: TSharedPtr GetAssetInstanceList() const { return AssetInstanceList; } private: diff --git a/Source/FlowEditor/Public/Asset/FlowDiffControl.h b/Source/FlowEditor/Public/Asset/FlowDiffControl.h new file mode 100644 index 000000000..efb6aaf11 --- /dev/null +++ b/Source/FlowEditor/Public/Asset/FlowDiffControl.h @@ -0,0 +1,73 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +/** + * Documentation: https://github.com/MothCocoon/FlowGraph/wiki/Visual-Diff + * Set macro value to 1, if you made these changes to the engine: https://github.com/EpicGames/UnrealEngine/pull/9659 + */ + +#include "FlowEditorDefines.h" +#if ENABLE_FLOW_DIFF +#include "DetailsDiff.h" +#include "DiffResults.h" +#include "IAssetTypeActions.h" +#include "Kismet/Private/DiffControl.h" + +struct FDiffResultItem; +class UEdGraph; +struct FEdGraphEditAction; + +class UFlowAsset; +class SFlowDiff; + +///////////////////////////////////////////////////////////////////////////// +/// FFlowAssetDiffControl +class FLOWEDITOR_API FFlowAssetDiffControl : public TDetailsDiffControl +{ +public: + FFlowAssetDiffControl(const UFlowAsset* InOldFlowAsset, const UFlowAsset* InNewFlowAsset, FOnDiffEntryFocused InSelectionCallback); + + virtual void GenerateTreeEntries(TArray>& OutTreeEntries, TArray>& OutRealDifferences) override; +}; + +///////////////////////////////////////////////////////////////////////////// +/// FFlowGraphToDiff: engine's FGraphToDiff customized to Flow Graph +struct FLOWEDITOR_API FFlowGraphToDiff : public TSharedFromThis, IDiffControl +{ + FFlowGraphToDiff(class SFlowDiff* DiffWidget, UEdGraph* GraphOld, UEdGraph* GraphNew, const FRevisionInfo& RevisionOld, const FRevisionInfo& RevisionNew); + virtual ~FFlowGraphToDiff() override; + + /** Add widgets to the differences tree */ + virtual void GenerateTreeEntries(TArray>& OutTreeEntries, TArray>& OutRealDifferences) override; + + UEdGraph* GetGraphOld() const { return GraphOld; }; + UEdGraph* GetGraphNew() const { return GraphNew; }; + + /** Source for list view */ + TArray> DiffListSource; + TSharedPtr> FoundDiffs; + + /** Index of the first item in RealDifferences that was generated by this graph */ + int32 RealDifferencesStartIndex = INDEX_NONE; + +private: + FText GetToolTip() const; + TSharedRef GenerateCategoryWidget() const; + + /** Called when the Newer Graph is modified*/ + void OnGraphChanged(const FEdGraphEditAction& Action) const; + + void BuildDiffSourceArray(); + + class SFlowDiff* DiffWidget; + UEdGraph* GraphOld; + UEdGraph* GraphNew; + + /** Description of Old and new graph */ + FRevisionInfo RevisionOld; + FRevisionInfo RevisionNew; + + FDelegateHandle OnGraphChangedDelegateHandle; +}; +#endif \ No newline at end of file diff --git a/Source/FlowEditor/Public/Asset/SAssetRevisionMenu.h b/Source/FlowEditor/Public/Asset/SAssetRevisionMenu.h new file mode 100644 index 000000000..4bd540150 --- /dev/null +++ b/Source/FlowEditor/Public/Asset/SAssetRevisionMenu.h @@ -0,0 +1,53 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "ISourceControlProvider.h" +#include "Widgets/SCompoundWidget.h" + +class FUpdateStatus; +struct FRevisionInfo; + +// Forced to make a variant of SBlueprintRevisionMenu, only to replace to UBlueprint* parameter +class FLOWEDITOR_API SAssetRevisionMenu : public SCompoundWidget +{ + DECLARE_DELEGATE_TwoParams(FOnRevisionSelected, FRevisionInfo const& RevisionInfo, const FString& InFilename) + +public: + SLATE_BEGIN_ARGS(SAssetRevisionMenu) + : _bIncludeLocalRevision(false) + { + } + + SLATE_ARGUMENT(bool, bIncludeLocalRevision) + SLATE_EVENT(FOnRevisionSelected, OnRevisionSelected) + SLATE_END_ARGS() + + virtual ~SAssetRevisionMenu() override; + + void Construct(const FArguments& InArgs, const FString& InFilename); + +private: + /** Delegate used to determine the visibility 'in progress' widgets */ + EVisibility GetInProgressVisibility() const; + /** Delegate used to determine the visibility of the cancel button */ + EVisibility GetCancelButtonVisibility() const; + + /** Delegate used to cancel a source control operation in progress */ + FReply OnCancelButtonClicked() const; + /** Callback for when the source control operation is complete */ + void OnSourceControlQueryComplete(const FSourceControlOperationRef& InOperation, ECommandResult::Type InResult); + + /** */ + bool bIncludeLocalRevision = false; + /** */ + FOnRevisionSelected OnRevisionSelected; + /** The name of the file we want revision info for */ + FString Filename; + /** The box we are using to display our menu */ + TSharedPtr MenuBox; + /** The source control operation in progress */ + TSharedPtr SourceControlQueryOp; + /** The state of the SCC query */ + uint32 SourceControlQueryState = 0; +}; diff --git a/Source/FlowEditor/Public/Asset/SFlowDiff.h b/Source/FlowEditor/Public/Asset/SFlowDiff.h new file mode 100644 index 000000000..c054204ba --- /dev/null +++ b/Source/FlowEditor/Public/Asset/SFlowDiff.h @@ -0,0 +1,224 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +/** + * Documentation: https://github.com/MothCocoon/FlowGraph/wiki/Visual-Diff + * Set macro value to 1, if you made these changes to the engine: https://github.com/EpicGames/UnrealEngine/pull/9659 + */ + +#include "FlowEditorDefines.h" +#if ENABLE_FLOW_DIFF +#include "SDetailsDiff.h" + +struct FFlowGraphToDiff; +class UFlowAsset; + +enum class EAssetEditorCloseReason : uint8; + +namespace FlowDiffUtils +{ + FLOWEDITOR_API void SelectNextRow(SListView>& ListView, const TArray>& ListViewSource); + FLOWEDITOR_API void SelectPrevRow(SListView>& ListView, const TArray>& ListViewSource); + FLOWEDITOR_API bool HasNextDifference(const SListView>& ListView, const TArray>& ListViewSource); + FLOWEDITOR_API bool HasPrevDifference(const SListView>& ListView, const TArray>& ListViewSource); +} + +/** Panel used to display the asset */ +struct FLOWEDITOR_API FFlowDiffPanel +{ + FFlowDiffPanel(); + + /** Generate a panel for NewGraph diffed against OldGraph */ + void GeneratePanel(UEdGraph* NewGraph, UEdGraph* OldGraph); + + /** Generate a panel that displays the Graph and reflects the items in the DiffResults */ + void GeneratePanel(UEdGraph* Graph, TSharedPtr> DiffResults, TAttribute FocusedDiffResult); + + /** Called when user hits keyboard shortcut to copy nodes */ + void CopySelectedNodes() const; + + /** Gets whatever nodes are selected in the Graph Editor */ + FGraphPanelSelectionSet GetSelectedNodes() const; + + /** Can user copy any of the selected nodes? */ + bool CanCopyNodes() const; + + /** Functions used to focus/find a particular change in a diff result */ + void FocusDiff(const UEdGraphPin& Pin) const; + void FocusDiff(const UEdGraphNode& Node) const; + + /** The Flow Asset that owns the graph we are showing */ + const UFlowAsset* FlowAsset; + + /** The box around the graph editor, used to change the content when new graphs are set */ + TSharedPtr GraphEditorBox; + + /** The details view associated with the graph editor */ + TSharedPtr DetailsView; + + /** The graph editor which does the work of displaying the graph */ + TWeakPtr GraphEditor; + + /** Revision information for this asset */ + FRevisionInfo RevisionInfo; + + /** True if we should show a name identifying which asset this panel is displaying */ + bool bShowAssetName; + + /** The widget that contains the revision info in graph mode */ + TSharedPtr OverlayGraphRevisionInfo; +private: + /** Command list for this diff panel */ + TSharedPtr GraphEditorCommands; +}; + +/* Visual Diff between two Flow Assets */ +class FLOWEDITOR_API SFlowDiff : public SCompoundWidget +{ +public: + DECLARE_DELEGATE_TwoParams(FOpenInDefaults, const class UFlowAsset*, const class UFlowAsset*); + + SLATE_BEGIN_ARGS(SFlowDiff) + { + } + + SLATE_ARGUMENT(const class UFlowAsset*, OldFlow) + SLATE_ARGUMENT(const class UFlowAsset*, NewFlow) + SLATE_ARGUMENT(struct FRevisionInfo, OldRevision) + SLATE_ARGUMENT(struct FRevisionInfo, NewRevision) + SLATE_ARGUMENT(bool, ShowAssetNames) + SLATE_ARGUMENT(TSharedPtr, ParentWindow) + SLATE_END_ARGS() + + void Construct(const FArguments& InArgs); + virtual ~SFlowDiff() override; + + /** Called when a new Graph is clicked on by user */ + void OnGraphChanged(const FFlowGraphToDiff* Diff); + + /** Called when user clicks on a new graph list item */ + void OnGraphSelectionChanged(const TSharedPtr Item, ESelectInfo::Type SelectionType); + + /** Called when user clicks on an entry in the listview of differences */ + void OnDiffListSelectionChanged(TSharedPtr TheDiff); + + /** Helper function for generating an empty widget */ + static TSharedRef DefaultEmptyPanel(); + + /** Helper function to create a window that holds a diff widget */ + static TSharedPtr CreateDiffWindow(const FText WindowTitle, const UFlowAsset* OldFlow, const UFlowAsset* NewFlow, const struct FRevisionInfo& OldRevision, const struct FRevisionInfo& NewRevision); + +protected: + /** Called when user clicks button to go to next difference */ + void NextDiff() const; + + /** Called when user clicks button to go to prev difference */ + void PrevDiff() const; + + /** Called to determine whether we have a list of differences to cycle through */ + bool HasNextDiff() const; + bool HasPrevDiff() const; + + /** Find the FGraphToDiff that displays the graph with GraphPath relative path */ + FFlowGraphToDiff* FindGraphToDiffEntry(const FString& GraphPath) const; + + /** Bring these revisions of graph into focus on main display*/ + void FocusOnGraphRevisions(const FFlowGraphToDiff* Diff); + + /** User toggles the option to lock the views between the two assets */ + void OnToggleLockView(); + + /** User toggles the option to change the split view mode between vertical and horizontal */ + void OnToggleSplitViewMode(); + + /** Reset the graph editor, called when user switches graphs to display*/ + void ResetGraphEditors() const; + + /** Get the image to show for the toggle lock option*/ + FSlateIcon GetLockViewImage() const; + + /** Get the image to show for the toggle split view mode option*/ + FSlateIcon GetSplitViewModeImage() const; + + /** List of graphs to diff, are added to panel last */ + TSharedPtr GraphToDiff; + + /** Get Graph editor associated with this Graph */ + FFlowDiffPanel& GetDiffPanelForNode(const UEdGraphNode& Node); + + /** Event handler that updates the graph view when user selects a new graph */ + void HandleGraphChanged(const FString& GraphPath); + + /** Function used to generate the list of differences and the widgets needed to calculate that list */ + void GenerateDifferencesList(); + + /** Called when editor may need to be closed */ + void OnCloseAssetEditor(UObject* Asset, const EAssetEditorCloseReason CloseReason); + + struct FDiffControl + { + FDiffControl() + : Widget() + , DiffControl(nullptr) + { + } + + TSharedPtr Widget; + TSharedPtr DiffControl; + }; + + FDiffControl GenerateDetailsPanel(); + FDiffControl GenerateGraphPanel(); + + TSharedRef GenerateGraphWidgetForPanel(FFlowDiffPanel& OutDiffPanel) const; + TSharedRef GenerateRevisionInfoWidgetForPanel(TSharedPtr& OutGeneratedWidget, const FText& InRevisionText) const; + + /** Accessor and event handler for toggling between diff view modes (defaults, components, graph view, interface, macro): */ + void SetCurrentMode(FName NewMode); + FName GetCurrentMode() const { return CurrentMode; } + void OnModeChanged(const FName& InNewViewMode) const; + + void UpdateTopSectionVisibility(const FName& InNewViewMode) const; + + FName CurrentMode; + + /*The two panels used to show the old & new revision*/ + FFlowDiffPanel PanelOld, PanelNew; + + /** If the two views should be locked */ + bool bLockViews; + + /** If the view on Graph Mode should be divided vertically */ + bool bVerticalSplitGraphMode = true; + + /** Contents widget that we swap when mode changes (defaults, components, etc) */ + TSharedPtr ModeContents; + + TSharedPtr TopRevisionInfoWidget; + TSharedPtr DiffGraphSplitter; + TSharedPtr GraphToolBarWidget; + + friend struct FListItemGraphToDiff; + + /** We can't use the global tab manager because we need to instance the diff control, so we have our own tab manager: */ + TSharedPtr TabManager; + + /** Tree of differences collected across all panels: */ + TArray> PrimaryDifferencesList; + + /** List of all differences, cached so that we can iterate only the differences and not labels, etc: */ + TArray> RealDifferences; + + /** Tree view that displays the differences, cached for the buttons that iterate the differences: */ + TSharedPtr>> DifferencesTreeView; + + /** Stored references to widgets used to display various parts of a asset, from the mode name */ + TMap ModePanels; + + /** A pointer to the window holding this */ + TWeakPtr WeakParentWindow; + + FDelegateHandle AssetEditorCloseDelegate; +}; +#endif diff --git a/Source/FlowEditor/Public/FlowEditorDefines.h b/Source/FlowEditor/Public/FlowEditorDefines.h new file mode 100644 index 000000000..736d9ce32 --- /dev/null +++ b/Source/FlowEditor/Public/FlowEditorDefines.h @@ -0,0 +1,9 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +/** + * Documentation: https://github.com/MothCocoon/FlowGraph/wiki/Visual-Diff + * Set macro value to 1, if you made these changes to the engine: https://github.com/EpicGames/UnrealEngine/pull/9659 + */ +#define ENABLE_FLOW_DIFF 0 From 83420ded85b9ecdcb4d24dc4d5acf780dae83bfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Fri, 14 Oct 2022 21:41:12 +0200 Subject: [PATCH 022/485] seems to be a redundant include --- Source/FlowEditor/Public/FlowEditorModule.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/FlowEditor/Public/FlowEditorModule.h b/Source/FlowEditor/Public/FlowEditorModule.h index 506491fc7..f1487622d 100644 --- a/Source/FlowEditor/Public/FlowEditorModule.h +++ b/Source/FlowEditor/Public/FlowEditorModule.h @@ -3,7 +3,6 @@ #pragma once #include "AssetTypeCategories.h" -#include "Framework/MultiBox/MultiBoxBuilder.h" #include "IAssetTypeActions.h" #include "Modules/ModuleInterface.h" #include "PropertyEditorDelegates.h" From db8c1594b9ddd65e4ca210ac1189610d8572b5de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 16 Oct 2022 21:09:03 +0200 Subject: [PATCH 023/485] fixed Event Graph not created with new blueprint in 5.1 --- Source/Flow/Public/Nodes/FlowNodeBlueprint.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/Source/Flow/Public/Nodes/FlowNodeBlueprint.h b/Source/Flow/Public/Nodes/FlowNodeBlueprint.h index 32f82c4ba..d939925b6 100644 --- a/Source/Flow/Public/Nodes/FlowNodeBlueprint.h +++ b/Source/Flow/Public/Nodes/FlowNodeBlueprint.h @@ -9,7 +9,6 @@ /** * A specialized blueprint class required for customizing Asset Type Actions */ - UCLASS(BlueprintType) class FLOW_API UFlowNodeBlueprint : public UBlueprint { @@ -20,8 +19,6 @@ class FLOW_API UFlowNodeBlueprint : public UBlueprint virtual bool SupportedByDefaultBlueprintFactory() const override { return false; } virtual bool SupportsDelegates() const override { return false; } - virtual bool SupportsEventGraphs() const override { return false; } - virtual bool SupportsAnimLayers() const override { return false; } // -- #endif }; From 8a5fdb0a5525471bf6cbfbabf836c764306d40ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 16 Oct 2022 21:18:02 +0200 Subject: [PATCH 024/485] fixed ANY_PACKAGE deprecation, listing blueprint nodes --- Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index e1693572f..cdd6232f8 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -483,7 +483,7 @@ void UFlowGraphSchema::AddAsset(const FAssetData& AssetData, const bool bBatch) { UObject* Outer = nullptr; ResolveName(Outer, NativeParentClassPath, false, false); - const UClass* NativeParentClass = FindObject(nullptr, *NativeParentClassPath); + const UClass* NativeParentClass = FindObject(Outer, *NativeParentClassPath); // accept only Flow Node blueprints if (NativeParentClass && NativeParentClass->IsChildOf(UFlowNode::StaticClass())) From 41305740c97891cf962c5a4b0105857ae158007a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Tue, 18 Oct 2022 19:33:26 +0200 Subject: [PATCH 025/485] renamed "master instance" to "parent instance" + button hidden if can't be used --- Source/Flow/Private/FlowAsset.cpp | 2 +- Source/Flow/Public/FlowAsset.h | 2 +- .../FlowEditor/Private/Asset/FlowAssetEditor.cpp | 14 +++++++------- .../FlowEditor/Private/Asset/FlowAssetToolbar.cpp | 8 ++++---- Source/FlowEditor/Private/FlowEditorCommands.cpp | 2 +- Source/FlowEditor/Private/FlowEditorStyle.cpp | 4 ++-- Source/FlowEditor/Public/Asset/FlowAssetEditor.h | 4 ++-- Source/FlowEditor/Public/FlowEditorCommands.h | 2 +- 8 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 80776e0f5..87f193826 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -471,7 +471,7 @@ UFlowNode_SubGraph* UFlowAsset::GetNodeOwningThisAssetInstance() const return NodeOwningThisAssetInstance.Get(); } -UFlowAsset* UFlowAsset::GetMasterInstance() const +UFlowAsset* UFlowAsset::GetParentInstance() const { return NodeOwningThisAssetInstance.IsValid() ? NodeOwningThisAssetInstance.Get()->GetFlowAsset() : nullptr; } diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 4d085a9b0..a6058b710 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -260,7 +260,7 @@ class FLOW_API UFlowAsset : public UObject FName GetDisplayName() const; UFlowNode_SubGraph* GetNodeOwningThisAssetInstance() const; - UFlowAsset* GetMasterInstance() const; + UFlowAsset* GetParentInstance() const; // Are there any active nodes? UFUNCTION(BlueprintPure, Category = "Flow") diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index b5cd4094c..8d666d7de 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -250,11 +250,11 @@ void FFlowAssetEditor::BindToolbarCommands() ToolkitCommands->Append(FPlayWorldCommands::GlobalPlayWorldActions.ToSharedRef()); // Debugging - ToolkitCommands->MapAction(ToolbarCommands.GoToMasterInstance, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::GoToMasterInstance), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanGoToMasterInstance), + ToolkitCommands->MapAction(ToolbarCommands.GoToParentInstance, + FExecuteAction::CreateSP(this, &FFlowAssetEditor::GoToParentInstance), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanGoToParentInstance), FIsActionChecked(), - FIsActionButtonVisible::CreateStatic(&FFlowAssetEditor::IsPIE)); + FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanGoToParentInstance)); } void FFlowAssetEditor::RefreshAsset() @@ -268,15 +268,15 @@ void FFlowAssetEditor::RefreshAsset() } } -void FFlowAssetEditor::GoToMasterInstance() +void FFlowAssetEditor::GoToParentInstance() { - const UFlowAsset* AssetThatInstancedThisAsset = FlowAsset->GetInspectedInstance()->GetMasterInstance(); + const UFlowAsset* AssetThatInstancedThisAsset = FlowAsset->GetInspectedInstance()->GetParentInstance(); GEditor->GetEditorSubsystem()->OpenEditorForAsset(AssetThatInstancedThisAsset->GetTemplateAsset()); AssetThatInstancedThisAsset->GetTemplateAsset()->SetInspectedInstance(AssetThatInstancedThisAsset->GetDisplayName()); } -bool FFlowAssetEditor::CanGoToMasterInstance() +bool FFlowAssetEditor::CanGoToParentInstance() { return FlowAsset->GetInspectedInstance() && FlowAsset->GetInspectedInstance()->GetNodeOwningThisAssetInstance() != nullptr; } diff --git a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp index 6f7070369..0f967081d 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp @@ -152,10 +152,10 @@ void SFlowAssetBreadcrumb::Construct(const FArguments& InArgs, const TWeakObject TArray InstancesFromRoot = {InspectedInstance}; const UFlowAsset* CheckedInstance = InspectedInstance; - while (UFlowAsset* MasterInstance = CheckedInstance->GetMasterInstance()) + while (UFlowAsset* ParentInstance = CheckedInstance->GetParentInstance()) { - InstancesFromRoot.Insert(MasterInstance, 0); - CheckedInstance = MasterInstance; + InstancesFromRoot.Insert(ParentInstance, 0); + CheckedInstance = ParentInstance; } for (UFlowAsset* Instance : InstancesFromRoot) @@ -308,7 +308,7 @@ void FFlowAssetToolbar::BuildDebuggerToolbar(UToolMenu* ToolbarMenu) AssetInstanceList = SNew(SFlowAssetInstanceList, TemplateAsset); Section.AddEntry(FToolMenuEntry::InitWidget("AssetInstances", AssetInstanceList.ToSharedRef(), FText(), true)); - Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().GoToMasterInstance)); + Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().GoToParentInstance)); Breadcrumb = SNew(SFlowAssetBreadcrumb, TemplateAsset); Section.AddEntry(FToolMenuEntry::InitWidget("AssetBreadcrumb", Breadcrumb.ToSharedRef(), FText(), true)); diff --git a/Source/FlowEditor/Private/FlowEditorCommands.cpp b/Source/FlowEditor/Private/FlowEditorCommands.cpp index ee41942ad..86e175178 100644 --- a/Source/FlowEditor/Private/FlowEditorCommands.cpp +++ b/Source/FlowEditor/Private/FlowEditorCommands.cpp @@ -20,7 +20,7 @@ FFlowToolbarCommands::FFlowToolbarCommands() void FFlowToolbarCommands::RegisterCommands() { UI_COMMAND(RefreshAsset, "Refresh Asset", "Refresh asset and all nodes", EUserInterfaceActionType::Button, FInputChord()); - UI_COMMAND(GoToMasterInstance, "Go To Master", "Open editor for the Flow Asset that created this Flow instance", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(GoToParentInstance, "Go To Parent", "Open editor for the Flow Asset that created this Flow instance", EUserInterfaceActionType::Button, FInputChord()); } FFlowGraphCommands::FFlowGraphCommands() diff --git a/Source/FlowEditor/Private/FlowEditorStyle.cpp b/Source/FlowEditor/Private/FlowEditorStyle.cpp index e5954617e..6faa3b813 100644 --- a/Source/FlowEditor/Private/FlowEditorStyle.cpp +++ b/Source/FlowEditor/Private/FlowEditorStyle.cpp @@ -30,8 +30,8 @@ void FFlowEditorStyle::Initialize() // engine assets StyleSet->SetContentRoot(FPaths::EngineContentDir() / TEXT("Editor/Slate/")); - StyleSet->Set("FlowToolbar.GoToMasterInstance", new IMAGE_BRUSH("Icons/icon_DebugStepOut_40x", Icon40)); - StyleSet->Set("FlowToolbar.GoToMasterInstance.Small", new IMAGE_BRUSH("Icons/icon_DebugStepOut_40x", Icon20)); + StyleSet->Set("FlowToolbar.GoToParentInstance", new IMAGE_BRUSH("Icons/icon_DebugStepOut_40x", Icon40)); + StyleSet->Set("FlowToolbar.GoToParentInstance.Small", new IMAGE_BRUSH("Icons/icon_DebugStepOut_40x", Icon20)); StyleSet->Set("FlowGraph.BreakpointEnabled", new IMAGE_BRUSH("Old/Kismet2/Breakpoint_Valid", FVector2D(24.0f, 24.0f))); StyleSet->Set("FlowGraph.BreakpointDisabled", new IMAGE_BRUSH("Old/Kismet2/Breakpoint_Disabled", FVector2D(24.0f, 24.0f))); diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index 63dd36cf7..96264559c 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -93,8 +93,8 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit virtual void BindToolbarCommands(); virtual void RefreshAsset(); - virtual void GoToMasterInstance(); - virtual bool CanGoToMasterInstance(); + virtual void GoToParentInstance(); + virtual bool CanGoToParentInstance(); virtual void CreateWidgets(); diff --git a/Source/FlowEditor/Public/FlowEditorCommands.h b/Source/FlowEditor/Public/FlowEditorCommands.h index f3f9caf2c..bd6fc6480 100644 --- a/Source/FlowEditor/Public/FlowEditorCommands.h +++ b/Source/FlowEditor/Public/FlowEditorCommands.h @@ -13,7 +13,7 @@ class FLOWEDITOR_API FFlowToolbarCommands final : public TCommands RefreshAsset; - TSharedPtr GoToMasterInstance; + TSharedPtr GoToParentInstance; virtual void RegisterCommands() override; }; From ac908d46d9af9ea4d6dbd5e7e83809006f8ba525 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Tue, 18 Oct 2022 19:37:54 +0200 Subject: [PATCH 026/485] removed small icon style, as Small variants are removed in UE5 --- Source/FlowEditor/Private/FlowEditorStyle.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Source/FlowEditor/Private/FlowEditorStyle.cpp b/Source/FlowEditor/Private/FlowEditorStyle.cpp index 6faa3b813..875cfc585 100644 --- a/Source/FlowEditor/Private/FlowEditorStyle.cpp +++ b/Source/FlowEditor/Private/FlowEditorStyle.cpp @@ -22,7 +22,6 @@ void FFlowEditorStyle::Initialize() StyleSet = MakeShareable(new FSlateStyleSet(TEXT("FlowEditorStyle"))); const FVector2D Icon16(16.0f, 16.0f); - const FVector2D Icon20(20.0f, 20.0f); const FVector2D Icon30(30.0f, 30.0f); const FVector2D Icon40(40.0f, 40.0f); const FVector2D Icon64(64.0f, 64.0f); @@ -31,7 +30,6 @@ void FFlowEditorStyle::Initialize() StyleSet->SetContentRoot(FPaths::EngineContentDir() / TEXT("Editor/Slate/")); StyleSet->Set("FlowToolbar.GoToParentInstance", new IMAGE_BRUSH("Icons/icon_DebugStepOut_40x", Icon40)); - StyleSet->Set("FlowToolbar.GoToParentInstance.Small", new IMAGE_BRUSH("Icons/icon_DebugStepOut_40x", Icon20)); StyleSet->Set("FlowGraph.BreakpointEnabled", new IMAGE_BRUSH("Old/Kismet2/Breakpoint_Valid", FVector2D(24.0f, 24.0f))); StyleSet->Set("FlowGraph.BreakpointDisabled", new IMAGE_BRUSH("Old/Kismet2/Breakpoint_Disabled", FVector2D(24.0f, 24.0f))); From 4fb4adafee50863874879f01671b4dfe2e7ada92 Mon Sep 17 00:00:00 2001 From: Cchnn <12191713+Cchnn@users.noreply.github.com> Date: Tue, 18 Oct 2022 13:39:19 -0400 Subject: [PATCH 027/485] Custom output finish (#119) * custom output finish * Rename bFinish Co-authored-by: Chun --- Source/Flow/Private/FlowAsset.cpp | 4 ++-- Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp | 5 +++-- Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp | 2 +- Source/Flow/Public/FlowAsset.h | 2 +- Source/Flow/Public/Nodes/Route/FlowNode_CustomOutput.h | 4 ++++ 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 87f193826..d2bd80fdc 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -406,9 +406,9 @@ void UFlowAsset::TriggerCustomEvent(UFlowNode_SubGraph* Node, const FName& Event } } -void UFlowAsset::TriggerCustomOutput(const FName& EventName) const +void UFlowAsset::TriggerCustomOutput(const FName& EventName, const bool bFinish) const { - NodeOwningThisAssetInstance->TriggerOutput(EventName); + NodeOwningThisAssetInstance->TriggerOutput(EventName, bFinish); } void UFlowAsset::TriggerInput(const FGuid& NodeGuid, const FName& PinName) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp index fe32ed2ae..097ffe2a1 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp @@ -7,6 +7,7 @@ UFlowNode_CustomOutput::UFlowNode_CustomOutput(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) + , bFinishGraph(false) { #if WITH_EDITOR Category = TEXT("Route"); @@ -20,13 +21,13 @@ void UFlowNode_CustomOutput::ExecuteInput(const FName& PinName) { if (!EventName.IsNone() && GetFlowAsset()->GetCustomOutputs().Contains(EventName) && GetFlowAsset()->GetNodeOwningThisAssetInstance()) { - GetFlowAsset()->TriggerCustomOutput(EventName); + GetFlowAsset()->TriggerCustomOutput(EventName, bFinishGraph); } } #if WITH_EDITOR FString UFlowNode_CustomOutput::GetNodeDescription() const { - return EventName.ToString(); + return EventName.ToString() + LINE_TERMINATOR + (bFinishGraph ? TEXT("True") : TEXT("False")); } #endif diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp index dede5195c..262d50696 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp @@ -18,7 +18,7 @@ UFlowNode_SubGraph::UFlowNode_SubGraph(const FObjectInitializer& ObjectInitializ #endif InputPins = {StartPin}; - OutputPins = {FinishPin}; + OutputPins = {}; } bool UFlowNode_SubGraph::CanBeAssetInstanced() const diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index a6058b710..63d454096 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -248,7 +248,7 @@ class FLOW_API UFlowAsset : public UObject private: void TriggerCustomEvent(UFlowNode_SubGraph* Node, const FName& EventName) const; - void TriggerCustomOutput(const FName& EventName) const; + void TriggerCustomOutput(const FName& EventName, const bool bFinish) const; void TriggerInput(const FGuid& NodeGuid, const FName& PinName); diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_CustomOutput.h b/Source/Flow/Public/Nodes/Route/FlowNode_CustomOutput.h index 9e430b267..01fac8ca1 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_CustomOutput.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_CustomOutput.h @@ -16,6 +16,10 @@ class FLOW_API UFlowNode_CustomOutput final : public UFlowNode UPROPERTY() FName EventName; + + // this allows to finish execution of this Flow Asset + UPROPERTY(EditAnywhere, Category = "Output") + bool bFinishGraph; protected: virtual void ExecuteInput(const FName& PinName) override; From dbf881ef694a1c9896d0e227b1b95050d84cd958 Mon Sep 17 00:00:00 2001 From: Cchnn <12191713+Cchnn@users.noreply.github.com> Date: Tue, 18 Oct 2022 13:39:32 -0400 Subject: [PATCH 028/485] On node input triggered/output triggered (#120) * Activate-function * OnNodeInputTriggered and OnNodeOutputTriggered Co-authored-by: Chun --- Source/Flow/Private/FlowSubsystem.cpp | 8 ++++++++ Source/Flow/Private/Nodes/FlowNode.cpp | 15 +++++++++++++++ Source/Flow/Public/FlowSubsystem.h | 4 ++++ Source/Flow/Public/Nodes/FlowNode.h | 5 +++++ 4 files changed, 32 insertions(+) diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index bcbdde0e9..0e14d3d0b 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -48,6 +48,14 @@ void UFlowSubsystem::Deinitialize() AbortActiveFlows(); } +void UFlowSubsystem::OnNodeInputTriggered(const UFlowNode* node, const bool bWasActive) +{ +} + +void UFlowSubsystem::OnNodeOutputTriggered(const UFlowNode* node, const bool bFinish) +{ +} + void UFlowSubsystem::AbortActiveFlows() { if (InstancedTemplates.Num() > 0) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 438b2a9d5..a7851b082 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -338,8 +338,16 @@ void UFlowNode::TriggerInput(const FName& PinName, const bool bForcedActivation { if (InputPins.Contains(PinName)) { + EFlowNodeState PreviousActivationState = ActivationState; + if (PreviousActivationState != EFlowNodeState::Active) + { + Activate(); + } + ActivationState = EFlowNodeState::Active; + GetFlowSubsystem()->OnNodeInputTriggered(this, PreviousActivationState == EFlowNodeState::Active); + #if !UE_BUILD_SHIPPING // record for debugging TArray& Records = InputRecords.FindOrAdd(PinName); @@ -379,6 +387,8 @@ void UFlowNode::TriggerFirstOutput(const bool bFinish) void UFlowNode::TriggerOutput(const FName& PinName, const bool bFinish /*= false*/, const bool bForcedActivation /*= false*/) { + GetFlowSubsystem()->OnNodeOutputTriggered(this, bFinish); + // clean up node, if needed if (bFinish) { @@ -458,6 +468,11 @@ void UFlowNode::Cleanup() K2_Cleanup(); } +void UFlowNode::Activate() +{ + K2_Activate(); +} + void UFlowNode::ForceFinishNode() { K2_ForceFinishNode(); diff --git a/Source/Flow/Public/FlowSubsystem.h b/Source/Flow/Public/FlowSubsystem.h index 3315609dd..d2706f28d 100644 --- a/Source/Flow/Public/FlowSubsystem.h +++ b/Source/Flow/Public/FlowSubsystem.h @@ -12,6 +12,7 @@ class UFlowAsset; class UFlowNode_SubGraph; +class UFlowNode; DECLARE_DYNAMIC_MULTICAST_DELEGATE(FSimpleFlowEvent); DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSimpleFlowComponentEvent, UFlowComponent*, Component); @@ -60,6 +61,9 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem virtual void Initialize(FSubsystemCollectionBase& Collection) override; virtual void Deinitialize() override; + virtual void OnNodeInputTriggered(const UFlowNode* node, const bool bWasActive); + virtual void OnNodeOutputTriggered(const UFlowNode* node, const bool bFinish); + virtual void AbortActiveFlows(); /* Start the root Flow, graph that will eventually instantiate next Flow Graphs through the SubGraph node */ diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index d164a74e5..4b04e1f60 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -284,6 +284,11 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Cleanup")) void K2_Cleanup(); + virtual void Activate(); + + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Activate")) + void K2_Activate(); + public: // Define what happens when node is terminated from the outside virtual void ForceFinishNode(); From 47eb1dbc016ca0e3c92267dc5ed8cae69a18f443 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Tue, 18 Oct 2022 20:01:26 +0200 Subject: [PATCH 029/485] PR #120 cleanup: removal of subsystem part, renamed event to Activate --- Source/Flow/Private/FlowSubsystem.cpp | 8 -------- Source/Flow/Private/Nodes/FlowNode.cpp | 18 +++++++----------- Source/Flow/Public/FlowSubsystem.h | 4 ---- Source/Flow/Public/Nodes/FlowNode.h | 10 +++++----- 4 files changed, 12 insertions(+), 28 deletions(-) diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index 0e14d3d0b..bcbdde0e9 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -48,14 +48,6 @@ void UFlowSubsystem::Deinitialize() AbortActiveFlows(); } -void UFlowSubsystem::OnNodeInputTriggered(const UFlowNode* node, const bool bWasActive) -{ -} - -void UFlowSubsystem::OnNodeOutputTriggered(const UFlowNode* node, const bool bFinish) -{ -} - void UFlowSubsystem::AbortActiveFlows() { if (InstancedTemplates.Num() > 0) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index a7851b082..d04834017 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -334,20 +334,23 @@ void UFlowNode::FlushContent() K2_FlushContent(); } +void UFlowNode::OnActivate() +{ + K2_OnActivate(); +} + void UFlowNode::TriggerInput(const FName& PinName, const bool bForcedActivation /*= false*/) { if (InputPins.Contains(PinName)) { - EFlowNodeState PreviousActivationState = ActivationState; + const EFlowNodeState PreviousActivationState = ActivationState; if (PreviousActivationState != EFlowNodeState::Active) { - Activate(); + OnActivate(); } ActivationState = EFlowNodeState::Active; - GetFlowSubsystem()->OnNodeInputTriggered(this, PreviousActivationState == EFlowNodeState::Active); - #if !UE_BUILD_SHIPPING // record for debugging TArray& Records = InputRecords.FindOrAdd(PinName); @@ -387,8 +390,6 @@ void UFlowNode::TriggerFirstOutput(const bool bFinish) void UFlowNode::TriggerOutput(const FName& PinName, const bool bFinish /*= false*/, const bool bForcedActivation /*= false*/) { - GetFlowSubsystem()->OnNodeOutputTriggered(this, bFinish); - // clean up node, if needed if (bFinish) { @@ -468,11 +469,6 @@ void UFlowNode::Cleanup() K2_Cleanup(); } -void UFlowNode::Activate() -{ - K2_Activate(); -} - void UFlowNode::ForceFinishNode() { K2_ForceFinishNode(); diff --git a/Source/Flow/Public/FlowSubsystem.h b/Source/Flow/Public/FlowSubsystem.h index d2706f28d..3315609dd 100644 --- a/Source/Flow/Public/FlowSubsystem.h +++ b/Source/Flow/Public/FlowSubsystem.h @@ -12,7 +12,6 @@ class UFlowAsset; class UFlowNode_SubGraph; -class UFlowNode; DECLARE_DYNAMIC_MULTICAST_DELEGATE(FSimpleFlowEvent); DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSimpleFlowComponentEvent, UFlowComponent*, Component); @@ -61,9 +60,6 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem virtual void Initialize(FSubsystemCollectionBase& Collection) override; virtual void Deinitialize() override; - virtual void OnNodeInputTriggered(const UFlowNode* node, const bool bWasActive); - virtual void OnNodeOutputTriggered(const UFlowNode* node, const bool bFinish); - virtual void AbortActiveFlows(); /* Start the root Flow, graph that will eventually instantiate next Flow Graphs through the SubGraph node */ diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 4b04e1f60..b7eaa2edd 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -247,6 +247,11 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "FlushContent")) void K2_FlushContent(); + virtual void OnActivate(); + + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "On Activate")) + void K2_OnActivate(); + // Trigger execution of input pin void TriggerInput(const FName& PinName, const bool bForcedActivation = false); @@ -284,11 +289,6 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Cleanup")) void K2_Cleanup(); - virtual void Activate(); - - UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Activate")) - void K2_Activate(); - public: // Define what happens when node is terminated from the outside virtual void ForceFinishNode(); From b59491116b6cc81455318663befeb8a9dd1533ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Tue, 18 Oct 2022 20:31:38 +0200 Subject: [PATCH 030/485] Revert "Custom output finish (#119)" This reverts commit 4fb4adafee50863874879f01671b4dfe2e7ada92. --- Source/Flow/Private/FlowAsset.cpp | 4 ++-- Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp | 5 ++--- Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp | 2 +- Source/Flow/Public/FlowAsset.h | 2 +- Source/Flow/Public/Nodes/Route/FlowNode_CustomOutput.h | 4 ---- 5 files changed, 6 insertions(+), 11 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index d2bd80fdc..87f193826 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -406,9 +406,9 @@ void UFlowAsset::TriggerCustomEvent(UFlowNode_SubGraph* Node, const FName& Event } } -void UFlowAsset::TriggerCustomOutput(const FName& EventName, const bool bFinish) const +void UFlowAsset::TriggerCustomOutput(const FName& EventName) const { - NodeOwningThisAssetInstance->TriggerOutput(EventName, bFinish); + NodeOwningThisAssetInstance->TriggerOutput(EventName); } void UFlowAsset::TriggerInput(const FGuid& NodeGuid, const FName& PinName) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp index 097ffe2a1..fe32ed2ae 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp @@ -7,7 +7,6 @@ UFlowNode_CustomOutput::UFlowNode_CustomOutput(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) - , bFinishGraph(false) { #if WITH_EDITOR Category = TEXT("Route"); @@ -21,13 +20,13 @@ void UFlowNode_CustomOutput::ExecuteInput(const FName& PinName) { if (!EventName.IsNone() && GetFlowAsset()->GetCustomOutputs().Contains(EventName) && GetFlowAsset()->GetNodeOwningThisAssetInstance()) { - GetFlowAsset()->TriggerCustomOutput(EventName, bFinishGraph); + GetFlowAsset()->TriggerCustomOutput(EventName); } } #if WITH_EDITOR FString UFlowNode_CustomOutput::GetNodeDescription() const { - return EventName.ToString() + LINE_TERMINATOR + (bFinishGraph ? TEXT("True") : TEXT("False")); + return EventName.ToString(); } #endif diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp index 262d50696..dede5195c 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp @@ -18,7 +18,7 @@ UFlowNode_SubGraph::UFlowNode_SubGraph(const FObjectInitializer& ObjectInitializ #endif InputPins = {StartPin}; - OutputPins = {}; + OutputPins = {FinishPin}; } bool UFlowNode_SubGraph::CanBeAssetInstanced() const diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 63d454096..a6058b710 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -248,7 +248,7 @@ class FLOW_API UFlowAsset : public UObject private: void TriggerCustomEvent(UFlowNode_SubGraph* Node, const FName& EventName) const; - void TriggerCustomOutput(const FName& EventName, const bool bFinish) const; + void TriggerCustomOutput(const FName& EventName) const; void TriggerInput(const FGuid& NodeGuid, const FName& PinName); diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_CustomOutput.h b/Source/Flow/Public/Nodes/Route/FlowNode_CustomOutput.h index 01fac8ca1..9e430b267 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_CustomOutput.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_CustomOutput.h @@ -16,10 +16,6 @@ class FLOW_API UFlowNode_CustomOutput final : public UFlowNode UPROPERTY() FName EventName; - - // this allows to finish execution of this Flow Asset - UPROPERTY(EditAnywhere, Category = "Output") - bool bFinishGraph; protected: virtual void ExecuteInput(const FName& PinName) override; From 9353b9293201ae30b6cb983cdcc28ddce9984015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Tue, 18 Oct 2022 20:41:41 +0200 Subject: [PATCH 031/485] added Can Finish Graph method, this logic isn't hardcoded to Finish node anymore --- Source/Flow/Private/FlowAsset.cpp | 3 +-- Source/Flow/Public/Nodes/FlowNode.h | 3 +++ Source/Flow/Public/Nodes/Route/FlowNode_Finish.h | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 87f193826..054474096 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -7,7 +7,6 @@ #include "Nodes/FlowNode.h" #include "Nodes/Route/FlowNode_CustomInput.h" #include "Nodes/Route/FlowNode_Start.h" -#include "Nodes/Route/FlowNode_Finish.h" #include "Nodes/Route/FlowNode_SubGraph.h" #include "Engine/World.h" @@ -432,7 +431,7 @@ void UFlowAsset::FinishNode(UFlowNode* Node) ActiveNodes.Remove(Node); // if graph reached Finish and this asset instance was created by SubGraph node - if (Node->GetClass()->IsChildOf(UFlowNode_Finish::StaticClass())) + if (Node->CanFinishGraph()) { if (NodeOwningThisAssetInstance.IsValid()) { diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index b7eaa2edd..757ef7bc0 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -111,6 +111,9 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UFUNCTION(BlueprintPure, Category = "FlowNode") UFlowAsset* GetFlowAsset() const; +protected: + virtual bool CanFinishGraph() const { return false; } + ////////////////////////////////////////////////////////////////////////// // All created pins (default, class-specific and added by user) diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_Finish.h b/Source/Flow/Public/Nodes/Route/FlowNode_Finish.h index d5d4c59af..a9f9299b6 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_Finish.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_Finish.h @@ -15,5 +15,6 @@ class FLOW_API UFlowNode_Finish : public UFlowNode GENERATED_UCLASS_BODY() protected: + virtual bool CanFinishGraph() const override { return true; } virtual void ExecuteInput(const FName& PinName) override; }; From 5766c4015746787d6b2a4206e0b7ee1ff5835860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Tue, 18 Oct 2022 20:49:03 +0200 Subject: [PATCH 032/485] added spaces to blueprint event Display Names --- Source/Flow/Public/Nodes/FlowNode.h | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 757ef7bc0..3fc7c70d9 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -97,7 +97,7 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte protected: // Short summary of node's content - displayed over node as NodeInfoPopup - UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "GetNodeDescription")) + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Get Node Description")) FString K2_GetNodeDescription() const; // Inherits Guid after graph node @@ -164,10 +164,10 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte #endif protected: - UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "CanUserAddInput")) + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Can User Add Input")) bool K2_CanUserAddInput() const; - UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "CanUserAddOutput")) + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Can User Add Output")) bool K2_CanUserAddOutput() const; ////////////////////////////////////////////////////////////////////////// @@ -233,7 +233,7 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte // Event called just after creating the node instance, while initializing the Flow Asset instance // This happens before executing graph, only called during gameplay - UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "InitInstance")) + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Init Instance")) void K2_InitializeInstance(); public: @@ -244,10 +244,10 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte virtual void PreloadContent(); virtual void FlushContent(); - UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "PreloadContent")) + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Preload Content")) void K2_PreloadContent(); - UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "FlushContent")) + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Flush Content")) void K2_FlushContent(); virtual void OnActivate(); @@ -262,7 +262,7 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte virtual void ExecuteInput(const FName& PinName); // Event reacting on triggering Input pin - UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "ExecuteInput")) + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Execute Input")) void K2_ExecuteInput(const FName& PinName); // Simply trigger the first Output Pin, convenient to use if node has only one output @@ -298,7 +298,7 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte protected: // Define what happens when node is terminated from the outside - UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "ForceFinishNode")) + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Force Finish Node")) void K2_ForceFinishNode(); private: @@ -322,19 +322,19 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte protected: // Information displayed while node is working - displayed over node as NodeInfoPopup - UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "GetStatusString")) + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Get Status String")) FString K2_GetStatusString() const; - UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "GetStatusBackgroundColor")) + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Get Status Background Color")) bool K2_GetStatusBackgroundColor(FLinearColor& OutColor) const; - UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "GetAssetPath")) + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Get Asset Path")) FString K2_GetAssetPath(); - UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "GetAssetToEdit")) + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Get Asset To Edit")) UObject* K2_GetAssetToEdit(); - UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "GetActorToFocus")) + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Get Actor To Focus")) AActor* K2_GetActorToFocus(); template From 01fc36b5ab87630c4d52929610ed0ed0041119d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Tue, 18 Oct 2022 21:17:11 +0200 Subject: [PATCH 033/485] static analysis fixes --- Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp | 3 +-- Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp index 386baa6ac..e19a6b199 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp @@ -11,7 +11,6 @@ #include "FlowAsset.h" #include "Nodes/FlowNode.h" -#include "Developer/ToolMenus/Public/ToolMenus.h" #include "EdGraph/EdGraph.h" #include "EdGraphNode_Comment.h" #include "Editor.h" @@ -39,7 +38,7 @@ UEdGraphNode* FFlowGraphSchemaAction_NewNode::PerformAction(class UEdGraph* Pare return nullptr; } -UFlowGraphNode* FFlowGraphSchemaAction_NewNode::CreateNode(UEdGraph* ParentGraph, UEdGraphPin* FromPin, UClass* NodeClass, const FVector2D Location, const bool bSelectNewNode /*= true*/) +UFlowGraphNode* FFlowGraphSchemaAction_NewNode::CreateNode(UEdGraph* ParentGraph, UEdGraphPin* FromPin, const UClass* NodeClass, const FVector2D Location, const bool bSelectNewNode /*= true*/) { check(NodeClass); diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h index d882bd41d..520f6176a 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h @@ -47,7 +47,7 @@ struct FLOWEDITOR_API FFlowGraphSchemaAction_NewNode : public FEdGraphSchemaActi virtual UEdGraphNode* PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode = true) override; // -- - static UFlowGraphNode* CreateNode(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, UClass* NodeClass, const FVector2D Location, const bool bSelectNewNode = true); + static UFlowGraphNode* CreateNode(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const UClass* NodeClass, const FVector2D Location, const bool bSelectNewNode = true); }; /** Action to paste clipboard contents into the graph */ From b05da99b489f04f6029ae05556e8a30e260d0b03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Tue, 18 Oct 2022 21:40:45 +0200 Subject: [PATCH 034/485] ANY_PACKAGE fix: shortcut commands --- Source/FlowEditor/Private/FlowEditorCommands.cpp | 6 +++--- Source/FlowEditor/Public/FlowEditorCommands.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/FlowEditor/Private/FlowEditorCommands.cpp b/Source/FlowEditor/Private/FlowEditorCommands.cpp index 86e175178..28b6114d2 100644 --- a/Source/FlowEditor/Private/FlowEditorCommands.cpp +++ b/Source/FlowEditor/Private/FlowEditorCommands.cpp @@ -67,7 +67,7 @@ void FFlowSpawnNodeCommands::RegisterCommands() FString ClassName; if (FParse::Value(*NodeSpawns[x], TEXT("Class="), ClassName)) { - UClass* FoundClass = FindObject(nullptr, *ClassName, true); + UClass* FoundClass = FindFirstObject(*ClassName, EFindFirstObjectOptions::ExactClass, ELogVerbosity::Warning, TEXT("looking for SpawnNodes")); if (FoundClass && FoundClass->IsChildOf(UFlowNode::StaticClass())) { NodeClass = FoundClass; @@ -121,7 +121,7 @@ void FFlowSpawnNodeCommands::RegisterCommands() } } -TSharedPtr FFlowSpawnNodeCommands::GetChordByClass(UClass* NodeClass) const +TSharedPtr FFlowSpawnNodeCommands::GetChordByClass(const UClass* NodeClass) const { if (NodeCommands.Contains(NodeClass) && NodeCommands[NodeClass]->GetFirstValidChord()->IsValidChord()) { @@ -131,7 +131,7 @@ TSharedPtr FFlowSpawnNodeCommands::GetChordByClass(UClass* No return nullptr; } -TSharedPtr FFlowSpawnNodeCommands::GetActionByChord(FInputChord& InChord) const +TSharedPtr FFlowSpawnNodeCommands::GetActionByChord(const FInputChord& InChord) const { if (InChord.IsValidChord()) { diff --git a/Source/FlowEditor/Public/FlowEditorCommands.h b/Source/FlowEditor/Public/FlowEditorCommands.h index bd6fc6480..09bb6f485 100644 --- a/Source/FlowEditor/Public/FlowEditorCommands.h +++ b/Source/FlowEditor/Public/FlowEditorCommands.h @@ -57,8 +57,8 @@ class FFlowSpawnNodeCommands : public TCommands virtual void RegisterCommands() override; - TSharedPtr GetChordByClass(UClass* NodeClass) const; - TSharedPtr GetActionByChord(FInputChord& InChord) const; + TSharedPtr GetChordByClass(const UClass* NodeClass) const; + TSharedPtr GetActionByChord(const FInputChord& InChord) const; private: TSharedPtr GetActionByClass(UClass* NodeClass) const; From 06d8996ee813df21e64995595d43d33f646b79be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Tue, 18 Oct 2022 21:46:18 +0200 Subject: [PATCH 035/485] ANY_PACKAGE fix: removed redundant search for static class --- Source/FlowEditor/FlowEditor.Build.cs | 1 + Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp | 9 +++------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Source/FlowEditor/FlowEditor.Build.cs b/Source/FlowEditor/FlowEditor.Build.cs index 3f1c9ae2f..90b593114 100644 --- a/Source/FlowEditor/FlowEditor.Build.cs +++ b/Source/FlowEditor/FlowEditor.Build.cs @@ -35,6 +35,7 @@ public FlowEditor(ReadOnlyTargetRules Target) : base(Target) "Kismet", "KismetWidgets", "LevelEditor", + "LevelSequence", "MovieScene", "MovieSceneTracks", "MovieSceneTools", diff --git a/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp b/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp index 583243c09..0d7c4d282 100644 --- a/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp +++ b/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp @@ -8,13 +8,11 @@ #include "MovieScene/MovieSceneFlowTriggerSection.h" #include "Framework/MultiBox/MultiBoxBuilder.h" -#include "UObject/Package.h" #include "ISequencerSection.h" -#include "DetailLayoutBuilder.h" -#include "DetailCategoryBuilder.h" +#include "LevelSequence.h" +#include "MovieSceneSequenceEditor.h" #include "Sections/MovieSceneEventSection.h" #include "SequencerUtilities.h" -#include "MovieSceneSequenceEditor.h" #define LOCTEXT_NAMESPACE "FFlowTrackEditor" @@ -138,8 +136,7 @@ bool FFlowTrackEditor::SupportsType(TSubclassOf Type) const bool FFlowTrackEditor::SupportsSequence(UMovieSceneSequence* InSequence) const { - static UClass* LevelSequenceClass = FindObject(nullptr, TEXT("LevelSequence"), true); - return InSequence && LevelSequenceClass && InSequence->GetClass()->IsChildOf(LevelSequenceClass); + return InSequence && InSequence->GetClass()->IsChildOf(ULevelSequence::StaticClass()); } const FSlateBrush* FFlowTrackEditor::GetIconBrush() const From 6eed74b7624b5b2df0e278ebf3bc6dc18bb55cbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Wed, 19 Oct 2022 00:25:34 +0200 Subject: [PATCH 036/485] #113 Refactored numbered pins support, so it would fully safe to add regular pins --- Source/Flow/Private/Nodes/FlowNode.cpp | 50 +++++++++++++++++-- Source/Flow/Public/Nodes/FlowNode.h | 7 ++- .../Private/Graph/Nodes/FlowGraphNode.cpp | 40 +++++++++++---- .../Public/Graph/Nodes/FlowGraphNode.h | 2 +- 4 files changed, 81 insertions(+), 18 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index d04834017..7230d2771 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -182,6 +182,32 @@ void UFlowNode::SetNumberedOutputPins(const uint8 FirstNumber /*= 0*/, const uin } } +uint8 UFlowNode::CountNumberedInputs() const +{ + uint8 Result = 0; + for (const FFlowPin& Pin : InputPins) + { + if (Pin.PinName.ToString().IsNumeric()) + { + Result++; + } + } + return Result; +} + +uint8 UFlowNode::CountNumberedOutputs() const +{ + uint8 Result = 0; + for (const FFlowPin& Pin : OutputPins) + { + if (Pin.PinName.ToString().IsNumeric()) + { + Result++; + } + } + return Result; +} + TArray UFlowNode::GetInputNames() const { TArray Result; @@ -219,16 +245,32 @@ bool UFlowNode::CanUserAddOutput() const return K2_CanUserAddOutput(); } -void UFlowNode::RemoveUserInput() +void UFlowNode::RemoveUserInput(const FName& PinName) { Modify(); - InputPins.RemoveAt(InputPins.Num() - 1); + + for (int32 i = 0; i < InputPins.Num(); i++) + { + if (InputPins[i].PinName == PinName) + { + InputPins.RemoveAt(i); + break; + } + } } -void UFlowNode::RemoveUserOutput() +void UFlowNode::RemoveUserOutput(const FName& PinName) { Modify(); - OutputPins.RemoveAt(OutputPins.Num() - 1); + + for (int32 i = 0; i < OutputPins.Num(); i++) + { + if (OutputPins[i].PinName == PinName) + { + OutputPins.RemoveAt(i); + break; + } + } } #endif diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 3fc7c70d9..c1e22beac 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -137,6 +137,9 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte void SetNumberedInputPins(const uint8 FirstNumber = 0, const uint8 LastNumber = 1); void SetNumberedOutputPins(const uint8 FirstNumber = 0, const uint8 LastNumber = 1); + uint8 CountNumberedInputs() const; + uint8 CountNumberedOutputs() const; + TArray GetInputPins() const { return InputPins; } TArray GetOutputPins() const { return OutputPins; } @@ -159,8 +162,8 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte virtual bool CanUserAddInput() const; virtual bool CanUserAddOutput() const; - void RemoveUserInput(); - void RemoveUserOutput(); + void RemoveUserInput(const FName& PinName); + void RemoveUserOutput(const FName& PinName); #endif protected: diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index b1b5b14d6..28f302105 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -808,38 +808,56 @@ bool UFlowGraphNode::CanUserAddOutput() const bool UFlowGraphNode::CanUserRemoveInput(const UEdGraphPin* Pin) const { - return FlowNode && FlowNode->InputPins.Num() > FlowNode->GetClass()->GetDefaultObject()->InputPins.Num(); + return FlowNode && !FlowNode->GetClass()->GetDefaultObject()->InputPins.Contains(Pin->PinName); } bool UFlowGraphNode::CanUserRemoveOutput(const UEdGraphPin* Pin) const { - return FlowNode && FlowNode->OutputPins.Num() > FlowNode->GetClass()->GetDefaultObject()->OutputPins.Num(); + return FlowNode && !FlowNode->GetClass()->GetDefaultObject()->OutputPins.Contains(Pin->PinName); } void UFlowGraphNode::AddUserInput() { - AddInstancePin(EGPD_Input, *FString::FromInt(InputPins.Num())); + AddInstancePin(EGPD_Input, FlowNode->CountNumberedInputs()); } void UFlowGraphNode::AddUserOutput() { - AddInstancePin(EGPD_Output, *FString::FromInt(OutputPins.Num())); + AddInstancePin(EGPD_Output, FlowNode->CountNumberedOutputs()); } -void UFlowGraphNode::AddInstancePin(const EEdGraphPinDirection Direction, const FName& PinName) +void UFlowGraphNode::AddInstancePin(const EEdGraphPinDirection Direction, const uint8 NumberedPinsAmount) { const FScopedTransaction Transaction(LOCTEXT("AddInstancePin", "Add Instance Pin")); Modify(); + const FFlowPin PinName = FFlowPin(FString::FromInt(NumberedPinsAmount)); + if (Direction == EGPD_Input) { - FlowNode->InputPins.Emplace(PinName); - CreateInputPin(FlowNode->InputPins.Last()); + if (FlowNode->InputPins.IsValidIndex(NumberedPinsAmount)) + { + FlowNode->InputPins.Insert(PinName, NumberedPinsAmount); + } + else + { + FlowNode->InputPins.Add(PinName); + } + + CreateInputPin(PinName, NumberedPinsAmount); } else { - FlowNode->OutputPins.Emplace(PinName); - CreateOutputPin(FlowNode->OutputPins.Last()); + if (FlowNode->OutputPins.IsValidIndex(NumberedPinsAmount)) + { + FlowNode->OutputPins.Insert(PinName, NumberedPinsAmount); + } + else + { + FlowNode->InputPins.Add(PinName); + } + + CreateOutputPin(PinName, NumberedPinsAmount); } GetGraph()->NotifyGraphChanged(); @@ -857,7 +875,7 @@ void UFlowGraphNode::RemoveInstancePin(UEdGraphPin* Pin) if (InputPins.Contains(Pin)) { InputPins.Remove(Pin); - FlowNode->RemoveUserInput(); + FlowNode->RemoveUserInput(Pin->PinName); Pin->MarkAsGarbage(); Pins.Remove(Pin); @@ -868,7 +886,7 @@ void UFlowGraphNode::RemoveInstancePin(UEdGraphPin* Pin) if (OutputPins.Contains(Pin)) { OutputPins.Remove(Pin); - FlowNode->RemoveUserOutput(); + FlowNode->RemoveUserOutput(Pin->PinName); Pin->MarkAsGarbage(); Pins.Remove(Pin); diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h index 4a7da0975..a47e80e68 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h @@ -191,7 +191,7 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode void AddUserOutput(); // Add pin only on this instance of node, under default pins - void AddInstancePin(const EEdGraphPinDirection Direction, const FName& PinName); + void AddInstancePin(const EEdGraphPinDirection Direction, const uint8 NumberedPinsAmount); // Call node and graph updates manually, if using bBatchRemoval void RemoveInstancePin(UEdGraphPin* Pin); From 3ca79292ac61d8e7907d8a0167fd5ac0f71b6cce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Wed, 19 Oct 2022 00:30:14 +0200 Subject: [PATCH 037/485] Reworked version of #109 - possible to disable infinite execution of the OR node Now OR node executes output only once, by default. Added logic to enable/disable the `Logical OR` node. This can be a breaking change if you somewhere relied on an infinitely working OR node. You can fix this by changing the `Execution Limit` value to 0. --- .../Nodes/Operators/FlowNode_LogicalOR.cpp | 46 ++++++++++++++++++- .../Nodes/Operators/FlowNode_LogicalOR.h | 17 +++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp b/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp index f814ee709..402758b77 100644 --- a/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp +++ b/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp @@ -4,6 +4,9 @@ UFlowNode_LogicalOR::UFlowNode_LogicalOR(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) + , bEnabled(true) + , ExecutionLimit(1) + , ExecutionCount(0) { #if WITH_EDITOR Category = TEXT("Operators"); @@ -11,9 +14,50 @@ UFlowNode_LogicalOR::UFlowNode_LogicalOR(const FObjectInitializer& ObjectInitial #endif SetNumberedInputPins(0, 1); + InputPins.Add(FFlowPin(TEXT("Enable"), TEXT("Enabling resets Execution Count"))); + InputPins.Add(FFlowPin(TEXT("Disable"), TEXT("Disabling resets Execution Count"))); } void UFlowNode_LogicalOR::ExecuteInput(const FName& PinName) { - TriggerFirstOutput(true); + if (PinName == TEXT("Enable")) + { + if (!bEnabled) + { + ResetCounter(); + bEnabled = true; + } + return; + } + + if (PinName == TEXT("Disable")) + { + if (bEnabled) + { + bEnabled = false; + Finish(); + } + return; + } + + if (bEnabled && PinName.ToString().IsNumeric()) + { + ExecutionCount++; + if (ExecutionLimit > 0 && ExecutionCount == ExecutionLimit) + { + bEnabled = false; + } + + TriggerFirstOutput(true); + } +} + +void UFlowNode_LogicalOR::Cleanup() +{ + ResetCounter(); +} + +void UFlowNode_LogicalOR::ResetCounter() +{ + ExecutionCount = 0; } diff --git a/Source/Flow/Public/Nodes/Operators/FlowNode_LogicalOR.h b/Source/Flow/Public/Nodes/Operators/FlowNode_LogicalOR.h index 75828dab9..3265478a3 100644 --- a/Source/Flow/Public/Nodes/Operators/FlowNode_LogicalOR.h +++ b/Source/Flow/Public/Nodes/Operators/FlowNode_LogicalOR.h @@ -14,10 +14,27 @@ class FLOW_API UFlowNode_LogicalOR final : public UFlowNode { GENERATED_UCLASS_BODY() +protected: + UPROPERTY(EditAnywhere, Category = "Lifetime", SaveGame) + bool bEnabled; + + // This node will become Blocked (not executed any more), if Execution Limit > 0 and Execution Count reaches this limit + // Set this to zero, if you'd like fire output indefinitely + UPROPERTY(EditAnywhere, Category = "Lifetime", meta = (ClampMin = 0)) + int32 ExecutionLimit; + + // This node will become Blocked (not executed any more), if Execution Limit > 0 and Execution Count reaches this limit + UPROPERTY(VisibleAnywhere, Category = "Lifetime", SaveGame) + int32 ExecutionCount; + #if WITH_EDITOR +public: virtual bool CanUserAddInput() const override { return true; } #endif protected: virtual void ExecuteInput(const FName& PinName) override; + virtual void Cleanup() override; + + void ResetCounter(); }; From 3ed05f2833886fdf50b19cabebc90eaf4d1cee17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Wed, 19 Oct 2022 19:06:41 +0200 Subject: [PATCH 038/485] simple way to check if Asset Data represents Flow Node blueprint --- .../Private/Graph/FlowGraphSchema.cpp | 27 ++++--------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index cdd6232f8..0f358c2d9 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -11,11 +11,11 @@ #include "FlowAsset.h" #include "Nodes/FlowNode.h" +#include "Nodes/FlowNodeBlueprint.h" #include "Nodes/Route/FlowNode_Start.h" #include "Nodes/Route/FlowNode_Reroute.h" #include "AssetRegistry/AssetRegistryModule.h" -#include "Developer/ToolMenus/Public/ToolMenus.h" #include "EdGraph/EdGraph.h" #include "ScopedTransaction.h" @@ -470,30 +470,13 @@ void UFlowGraphSchema::AddAsset(const FAssetData& AssetData, const bool bBatch) return; } - TArray AncestorClassNames; - AssetRegistryModule.Get().GetAncestorClassNames(AssetData.AssetClassPath, AncestorClassNames); - if (!AncestorClassNames.Contains(UBlueprintCore::StaticClass()->GetClassPathName())) + if (AssetData.GetClass()->IsChildOf(UFlowNodeBlueprint::StaticClass())) { - return; - } - - FString NativeParentClassPath; - AssetData.GetTagValue(FBlueprintTags::NativeParentClassPath, NativeParentClassPath); - if (!NativeParentClassPath.IsEmpty()) - { - UObject* Outer = nullptr; - ResolveName(Outer, NativeParentClassPath, false, false); - const UClass* NativeParentClass = FindObject(Outer, *NativeParentClassPath); + BlueprintFlowNodes.Emplace(AssetData.PackageName, AssetData); - // accept only Flow Node blueprints - if (NativeParentClass && NativeParentClass->IsChildOf(UFlowNode::StaticClass())) + if (!bBatch) { - BlueprintFlowNodes.Emplace(AssetData.PackageName, AssetData); - - if (!bBatch) - { - OnNodeListChanged.Broadcast(); - } + OnNodeListChanged.Broadcast(); } } } From 47528837d2f4052df705e5c269d2c9bbe4da21e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Thu, 20 Oct 2022 00:36:40 +0200 Subject: [PATCH 039/485] fixed adding user output pins --- Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 28f302105..91eb93198 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -854,10 +854,10 @@ void UFlowGraphNode::AddInstancePin(const EEdGraphPinDirection Direction, const } else { - FlowNode->InputPins.Add(PinName); + FlowNode->OutputPins.Add(PinName); } - CreateOutputPin(PinName, NumberedPinsAmount); + CreateOutputPin(PinName, FlowNode->InputPins.Num() + NumberedPinsAmount); } GetGraph()->NotifyGraphChanged(); From 75fe6928e07e7b950cd8f7dc51498226004bd988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 23 Oct 2022 22:02:33 +0200 Subject: [PATCH 040/485] Play Level Sequence improvements #102, #105: Transform Origin and playback replication --- .../LevelSequence/FlowLevelSequenceActor.cpp | 10 +-- .../LevelSequence/FlowLevelSequencePlayer.cpp | 79 +++++++++++-------- .../World/FlowNode_PlayLevelSequence.cpp | 5 +- .../LevelSequence/FlowLevelSequenceActor.h | 13 ++- .../LevelSequence/FlowLevelSequencePlayer.h | 22 ++++-- .../Nodes/World/FlowNode_PlayLevelSequence.h | 15 ++-- .../FlowNode_PlayLevelSequenceDetails.cpp | 2 + 7 files changed, 83 insertions(+), 63 deletions(-) diff --git a/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp b/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp index bcb0cdedb..ae97484e4 100644 --- a/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp +++ b/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp @@ -5,8 +5,8 @@ #include "Net/UnrealNetwork.h" AFlowLevelSequenceActor::AFlowLevelSequenceActor(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer - .SetDefaultSubobjectClass("AnimationPlayer")) + : Super(ObjectInitializer.SetDefaultSubobjectClass("AnimationPlayer")) + , ReplicatedLevelSequenceAsset(nullptr) { } @@ -29,9 +29,7 @@ void AFlowLevelSequenceActor::SetReplicatedLevelSequenceAsset(ULevelSequence* As void AFlowLevelSequenceActor::OnRep_ReplicatedLevelSequenceAsset() { LevelSequenceAsset = ReplicatedLevelSequenceAsset; -} + ReplicatedLevelSequenceAsset = nullptr; -void AFlowLevelSequenceActor::RPC_InitializePlayer_Implementation() -{ InitializePlayer(); -}; +} diff --git a/Source/Flow/Private/LevelSequence/FlowLevelSequencePlayer.cpp b/Source/Flow/Private/LevelSequence/FlowLevelSequencePlayer.cpp index 011760d90..29f51f385 100644 --- a/Source/Flow/Private/LevelSequence/FlowLevelSequencePlayer.cpp +++ b/Source/Flow/Private/LevelSequence/FlowLevelSequencePlayer.cpp @@ -12,7 +12,16 @@ UFlowLevelSequencePlayer::UFlowLevelSequencePlayer(const FObjectInitializer& Obj { } -UFlowLevelSequencePlayer* UFlowLevelSequencePlayer::CreateFlowLevelSequencePlayer(UObject* WorldContextObject, ULevelSequence* LevelSequence, FMovieSceneSequencePlaybackSettings Settings, FLevelSequenceCameraSettings CameraSettings, AActor* TransformOriginActor, bool bReplicates, ALevelSequenceActor*& OutActor) +UFlowLevelSequencePlayer* UFlowLevelSequencePlayer::CreateFlowLevelSequencePlayer( + const UObject* WorldContextObject, + ULevelSequence* LevelSequence, + FMovieSceneSequencePlaybackSettings Settings, + FLevelSequenceCameraSettings CameraSettings, + AActor* TransformOriginActor, + const bool bReplicates, + const bool bAlwaysRelevant, + ALevelSequenceActor*& OutActor +) { if (LevelSequence == nullptr) { @@ -25,53 +34,53 @@ UFlowLevelSequencePlayer* UFlowLevelSequencePlayer::CreateFlowLevelSequencePlaye return nullptr; } - FActorSpawnParameters SpawnParams; - SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; - SpawnParams.ObjectFlags |= RF_Transient; - SpawnParams.bAllowDuringConstructionScript = true; - - // Defer construction for autoplay so that BeginPlay() is called - SpawnParams.bDeferConstruction = true; - - AFlowLevelSequenceActor* Actor = World->SpawnActor(SpawnParams); + // Sequence Actor might be spawned exactly where playback happens + FTransform SpawnTransform = FTransform::Identity; + { + // apply Transform Origin + // https://docs.unrealengine.com/5.0/en-US/creating-level-sequences-with-dynamic-transforms-in-unreal-engine/ + if (IsValid(TransformOriginActor)) + { + // moving Level Sequence Actor might allow proper distance-based actor replication in networked games + SpawnTransform = TransformOriginActor->GetTransform(); + SpawnTransform = FTransform(SpawnTransform.GetRotation(), SpawnTransform.GetLocation(), FVector::OneVector); + } + } + // Create Sequence Actor + // We use deferred spawn, so we can set all actor properties prior to its initialization. + // This also helpful in case of multiplayer, since all actor settings are replicated with the spawned actor. No need to call replication just after spawn. + AFlowLevelSequenceActor* Actor = World->SpawnActorDeferred(AFlowLevelSequenceActor::StaticClass(), SpawnTransform, nullptr, nullptr, ESpawnActorCollisionHandlingMethod::AlwaysSpawn); Actor->PlaybackSettings = Settings; Actor->CameraSettings = CameraSettings; + + // apply Transform Origin to spawned actor + if (IsValid(TransformOriginActor)) + { + if (UDefaultLevelSequenceInstanceData* InstanceData = Cast(Actor->DefaultInstanceData)) + { + Actor->bOverrideInstanceData = true; + InstanceData->TransformOriginActor = TransformOriginActor; + } + } + + // support networking if (bReplicates) { + Actor->bReplicatePlayback = true; + Actor->bAlwaysRelevant = bAlwaysRelevant; Actor->SetReplicatedLevelSequenceAsset(LevelSequence); - Actor->SetReplicatePlayback(true); - Actor->bAlwaysRelevant = true; - Actor->RPC_InitializePlayer(); } else { Actor->LevelSequenceAsset = LevelSequence; - Actor->InitializePlayer(); } - OutActor = Actor; - - { - FTransform DefaultTransform; - - // apply Transform Origin - // https://docs.unrealengine.com/5.0/en-US/creating-level-sequences-with-dynamic-transforms-in-unreal-engine/ - if (IsValid(TransformOriginActor)) - { - if (UDefaultLevelSequenceInstanceData* InstanceData = Cast(Actor->DefaultInstanceData)) - { - Actor->bOverrideInstanceData = true; - InstanceData->TransformOriginActor = TransformOriginActor; - // moving Level Sequence Actor might allow proper distance-based actor replication in networked games - const FTransform OriginTransform = TransformOriginActor->GetTransform(); - DefaultTransform = FTransform(OriginTransform.GetRotation(), OriginTransform.GetLocation(), FVector::OneVector); - } - } + // finish deferred spawn + Actor->FinishSpawning(SpawnTransform); + OutActor = Actor; - Actor->FinishSpawning(DefaultTransform); - } - + // Sequence Player is created by Level Sequence Actor return Cast(Actor->SequencePlayer); } diff --git a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp index 9a7063abe..b0dfa8a70 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp @@ -19,8 +19,9 @@ FFlowNodeLevelSequenceEvent UFlowNode_PlayLevelSequence::OnPlaybackCompleted; UFlowNode_PlayLevelSequence::UFlowNode_PlayLevelSequence(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , bPlayReverse(false) - , bReplicates(false) , bUseGraphOwnerAsTransformOrigin(false) + , bReplicates(false) + , bAlwaysRelevant(false) , bApplyOwnerTimeDilation(true) , LoadedSequence(nullptr) , SequencePlayer(nullptr) @@ -157,7 +158,7 @@ void UFlowNode_PlayLevelSequence::CreatePlayer() AActor* TransformOriginActor = bUseGraphOwnerAsTransformOrigin ? OwningActor : nullptr; // Finally create the player - SequencePlayer = UFlowLevelSequencePlayer::CreateFlowLevelSequencePlayer(this, LoadedSequence, PlaybackSettings, CameraSettings, TransformOriginActor, bReplicates, SequenceActor); + SequencePlayer = UFlowLevelSequencePlayer::CreateFlowLevelSequencePlayer(this, LoadedSequence, PlaybackSettings, CameraSettings, TransformOriginActor, bReplicates, bAlwaysRelevant, SequenceActor); if (SequencePlayer) { diff --git a/Source/Flow/Public/LevelSequence/FlowLevelSequenceActor.h b/Source/Flow/Public/LevelSequence/FlowLevelSequenceActor.h index c84cc538e..8242cae05 100644 --- a/Source/Flow/Public/LevelSequence/FlowLevelSequenceActor.h +++ b/Source/Flow/Public/LevelSequence/FlowLevelSequenceActor.h @@ -13,19 +13,16 @@ class FLOW_API AFlowLevelSequenceActor : public ALevelSequenceActor { GENERATED_UCLASS_BODY() -protected: +protected: + UPROPERTY(ReplicatedUsing = OnRep_ReplicatedLevelSequenceAsset) + TObjectPtr ReplicatedLevelSequenceAsset; + virtual void GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const override; - + public: void SetReplicatedLevelSequenceAsset(ULevelSequence* Asset); - UFUNCTION(NetMulticast, Reliable) - void RPC_InitializePlayer(); - protected: - UPROPERTY(ReplicatedUsing = OnRep_ReplicatedLevelSequenceAsset) - TObjectPtr ReplicatedLevelSequenceAsset; - UFUNCTION() void OnRep_ReplicatedLevelSequenceAsset(); }; diff --git a/Source/Flow/Public/LevelSequence/FlowLevelSequencePlayer.h b/Source/Flow/Public/LevelSequence/FlowLevelSequencePlayer.h index 8818a9786..3e98e11c4 100644 --- a/Source/Flow/Public/LevelSequence/FlowLevelSequencePlayer.h +++ b/Source/Flow/Public/LevelSequence/FlowLevelSequencePlayer.h @@ -13,20 +13,28 @@ class UFlowNode; UCLASS() class FLOW_API UFlowLevelSequencePlayer : public ULevelSequencePlayer { - GENERATED_UCLASS_BODY() + GENERATED_UCLASS_BODY() private: - // most likely this is a UFlowNode_PlayLevelSequence or its child - UPROPERTY() - UFlowNode* FlowEventReceiver; + // most likely this is a UFlowNode_PlayLevelSequence or its child + UPROPERTY() + UFlowNode* FlowEventReceiver; public: - // variant of ULevelSequencePlayer::CreateLevelSequencePlayer - static UFlowLevelSequencePlayer* CreateFlowLevelSequencePlayer(UObject* WorldContextObject, ULevelSequence* LevelSequence, FMovieSceneSequencePlaybackSettings Settings, FLevelSequenceCameraSettings CameraSettings, AActor* TransformOriginActor, bool bReplicates, ALevelSequenceActor*& OutActor); + // variant of ULevelSequencePlayer::CreateLevelSequencePlayer + static UFlowLevelSequencePlayer* CreateFlowLevelSequencePlayer( + const UObject* WorldContextObject, + ULevelSequence* LevelSequence, + FMovieSceneSequencePlaybackSettings Settings, + FLevelSequenceCameraSettings CameraSettings, + AActor* TransformOriginActor, + const bool bReplicates, + const bool bAlwaysRelevant, + ALevelSequenceActor*& OutActor); void SetFlowEventReceiver(UFlowNode* FlowNode) { FlowEventReceiver = FlowNode; } // IMovieScenePlayer virtual TArray GetEventContexts() const override; - // -- + // -- }; diff --git a/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h b/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h index ac526fe1c..b46cad5df 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h @@ -38,9 +38,6 @@ class FLOW_API UFlowNode_PlayLevelSequence : public UFlowNode UPROPERTY(EditAnywhere, Category = "Sequence") bool bPlayReverse; - UPROPERTY(EditAnywhere, Category = "Sequence") - bool bReplicates; - UPROPERTY(EditAnywhere, Category = "Sequence") FLevelSequenceCameraSettings CameraSettings; @@ -49,8 +46,16 @@ class FLOW_API UFlowNode_PlayLevelSequence : public UFlowNode // https://docs.unrealengine.com/5.0/en-US/creating-level-sequences-with-dynamic-transforms-in-unreal-engine/ UPROPERTY(EditAnywhere, Category = "Sequence") bool bUseGraphOwnerAsTransformOrigin; - - // if True, Play Rate will by multiplied by Custom Time Dilation + + // If true, playback of this level sequence on the server will be synchronized across other clients + UPROPERTY(EditAnywhere, Category = "Sequence") + bool bReplicates; + + // Always relevant for network (overrides bOnlyRelevantToOwner) + UPROPERTY(EditAnywhere, Category = "Sequence") + bool bAlwaysRelevant; + + // If True, Play Rate will by multiplied by Custom Time Dilation // Enabling this option will use Custom Time Dilation from actor that created Root Flow instance, i.e. World Settings or Player Controller UPROPERTY(EditAnywhere, Category = "Sequence") bool bApplyOwnerTimeDilation; diff --git a/Source/FlowEditor/Private/Nodes/Customizations/FlowNode_PlayLevelSequenceDetails.cpp b/Source/FlowEditor/Private/Nodes/Customizations/FlowNode_PlayLevelSequenceDetails.cpp index f57695a0c..3f1374fed 100644 --- a/Source/FlowEditor/Private/Nodes/Customizations/FlowNode_PlayLevelSequenceDetails.cpp +++ b/Source/FlowEditor/Private/Nodes/Customizations/FlowNode_PlayLevelSequenceDetails.cpp @@ -14,5 +14,7 @@ void FFlowNode_PlayLevelSequenceDetails::CustomizeDetails(IDetailLayoutBuilder& SequenceCategory.AddProperty(GET_MEMBER_NAME_CHECKED(UFlowNode_PlayLevelSequence, bPlayReverse)); SequenceCategory.AddProperty(GET_MEMBER_NAME_CHECKED(UFlowNode_PlayLevelSequence, CameraSettings)); SequenceCategory.AddProperty(GET_MEMBER_NAME_CHECKED(UFlowNode_PlayLevelSequence, bUseGraphOwnerAsTransformOrigin)); + SequenceCategory.AddProperty(GET_MEMBER_NAME_CHECKED(UFlowNode_PlayLevelSequence, bReplicates)); + SequenceCategory.AddProperty(GET_MEMBER_NAME_CHECKED(UFlowNode_PlayLevelSequence, bAlwaysRelevant)); SequenceCategory.AddProperty(GET_MEMBER_NAME_CHECKED(UFlowNode_PlayLevelSequence, bApplyOwnerTimeDilation)); } From fd00c777a95c8bf80370cd66e30121c509d4f9a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 30 Oct 2022 14:31:51 +0100 Subject: [PATCH 041/485] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d6f18b4d0..97056f896 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ The aim of publishing it as open-source project is to let people tell great stor ## In-depth video presentation This 24-minute presentation breaks down the concept of the Flow Graph. It goes through everything written in this ReadMe but in greater detail. -[![Introducing Flow Graph for Unreal Engine](https://img.youtube.com/vi/Rj76JP1f-I4/0.jpg)](https://www.youtube.com/watch?v=BAqhccgKx_k) +[![Introducing Flow Graph for Unreal Engine](https://img.youtube.com/vi/BAqhccgKx_k/0.jpg)](https://www.youtube.com/watch?v=BAqhccgKx_k) ## Acknowledgements I got an opportunity to work on something like the Flow Graph at Reikon Games. They shared my enthusiasm for providing the plugin as open source and as such allowed me to publish this work and keep expanding it as a personal project. Kudos, guys! From 4102c3a391ccfb931506b4f33791e79712117d12 Mon Sep 17 00:00:00 2001 From: sturcotte06 Date: Tue, 8 Nov 2022 11:15:00 -0500 Subject: [PATCH 042/485] Fix instance names to be truly unique (#123) Co-authored-by: Simon Turcotte-Langevin --- Source/Flow/Private/FlowSubsystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index bcbdde0e9..122b46f70 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -208,7 +208,7 @@ UFlowAsset* UFlowSubsystem::CreateFlowInstance(const TWeakObjectPtr Own // it won't be empty, if we're restoring Flow Asset instance from the SaveGame if (NewInstanceName.IsEmpty()) { - NewInstanceName = FPaths::GetBaseFilename(FlowAsset.Get()->GetPathName()) + TEXT("_") + FString::FromInt(FlowAsset.Get()->GetInstancesNum()); + NewInstanceName = MakeUniqueObjectName(this, UFlowAsset::StaticClass(), *FPaths::GetBaseFilename(FlowAsset.Get()->GetPathName())).ToString(); } UFlowAsset* NewInstance = NewObject(this, FlowAsset->GetClass(), *NewInstanceName, RF_Transient, FlowAsset.Get(), false, nullptr); From d1370d1dd09bd7b0bb5b6ddfe885667d82acff6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Tue, 8 Nov 2022 23:08:58 +0100 Subject: [PATCH 043/485] cosmetic cleanup --- .../Private/Nodes/Route/FlowNode_Timer.cpp | 30 +++++++++---------- .../World/FlowNode_PlayLevelSequence.cpp | 1 - 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp index d55703707..6583fd6c7 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp @@ -7,11 +7,11 @@ UFlowNode_Timer::UFlowNode_Timer(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) - , CompletionTime(1.0f) - , StepTime(0.0f) - , SumOfSteps(0.0f) - , RemainingCompletionTime(0.0f) - , RemainingStepTime(0.0f) + , CompletionTime(1.0f) + , StepTime(0.0f) + , SumOfSteps(0.0f) + , RemainingCompletionTime(0.0f) + , RemainingStepTime(0.0f) { #if WITH_EDITOR Category = TEXT("Route"); @@ -138,17 +138,18 @@ void UFlowNode_Timer::OnSave_Implementation() void UFlowNode_Timer::OnLoad_Implementation() { - if (RemainingStepTime > 0.0f) + if (RemainingStepTime > 0.0f || RemainingCompletionTime > 0.0f) { - GetWorld()->GetTimerManager().SetTimer(StepTimerHandle, this, &UFlowNode_Timer::OnStep, StepTime, true, - RemainingStepTime); - } + if (RemainingStepTime > 0.0f) + { + GetWorld()->GetTimerManager().SetTimer(StepTimerHandle, this, &UFlowNode_Timer::OnStep, StepTime, true, RemainingStepTime); + } - GetWorld()->GetTimerManager().SetTimer(CompletionTimerHandle, this, &UFlowNode_Timer::OnCompletion, - RemainingCompletionTime, false); + GetWorld()->GetTimerManager().SetTimer(CompletionTimerHandle, this, &UFlowNode_Timer::OnCompletion, RemainingCompletionTime, false); - RemainingStepTime = 0.0f; - RemainingCompletionTime = 0.0f; + RemainingStepTime = 0.0f; + RemainingCompletionTime = 0.0f; + } } #if WITH_EDITOR @@ -177,8 +178,7 @@ FString UFlowNode_Timer::GetStatusString() const if (CompletionTimerHandle.IsValid() && GetWorld()) { - return TEXT("Progress: ") + GetProgressAsString( - GetWorld()->GetTimerManager().GetTimerElapsed(CompletionTimerHandle)); + return TEXT("Progress: ") + GetProgressAsString(GetWorld()->GetTimerManager().GetTimerElapsed(CompletionTimerHandle)); } return FString(); diff --git a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp index b0dfa8a70..428951044 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp @@ -221,7 +221,6 @@ void UFlowNode_PlayLevelSequence::OnLoad_Implementation() if (ElapsedTime != 0.0f) { LoadedSequence = LoadAsset(Sequence); - if (GetFlowSubsystem()->GetWorld() && LoadedSequence) { CreatePlayer(); From bfe386d2e52e4a6d936271009d9117facbc507e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Tue, 8 Nov 2022 23:10:43 +0100 Subject: [PATCH 044/485] last rogue new line fixed --- Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp index 6583fd6c7..3e14f88cb 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp @@ -159,8 +159,7 @@ FString UFlowNode_Timer::GetNodeDescription() const { if (StepTime > 0.0f) { - return FString::SanitizeFloat(CompletionTime, 2).Append(TEXT(", step by ")).Append( - FString::SanitizeFloat(StepTime, 2)); + return FString::SanitizeFloat(CompletionTime, 2).Append(TEXT(", step by ")).Append(FString::SanitizeFloat(StepTime, 2)); } return FString::SanitizeFloat(CompletionTime, 2); From d8b8e938d369f6657b244e8400438474856d48a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Fri, 11 Nov 2022 12:48:34 +0100 Subject: [PATCH 045/485] implemented Signal Modes: Enabled (default), Disabled, Pass Through https://github.com/MothCocoon/FlowGraph/wiki/Signal-Modes --- Source/Flow/Private/Nodes/FlowNode.cpp | 71 +++++++++++--- Source/Flow/Private/Nodes/FlowPin.cpp | 7 +- .../Nodes/Route/FlowNode_CustomInput.cpp | 1 + .../Nodes/Route/FlowNode_CustomOutput.cpp | 1 + .../Route/FlowNode_ExecutionMultiGate.cpp | 1 + .../Route/FlowNode_ExecutionSequence.cpp | 1 + .../Private/Nodes/Route/FlowNode_Finish.cpp | 1 + .../Private/Nodes/Route/FlowNode_Reroute.cpp | 2 + .../Private/Nodes/Route/FlowNode_Start.cpp | 1 + Source/Flow/Public/FlowTypes.h | 8 ++ Source/Flow/Public/Nodes/FlowNode.h | 20 +++- Source/Flow/Public/Nodes/FlowPin.h | 15 ++- .../Private/Asset/FlowAssetEditor.cpp | 46 ++++++++- .../FlowEditor/Private/FlowEditorCommands.cpp | 3 + .../FlowGraphConnectionDrawingPolicy.cpp | 49 +++++----- .../Private/Graph/Nodes/FlowGraphNode.cpp | 59 ++++++++++-- .../Private/Graph/Widgets/SFlowGraphNode.cpp | 95 ++++++++++++++++++- .../FlowEditor/Public/Asset/FlowAssetEditor.h | 5 + Source/FlowEditor/Public/FlowEditorCommands.h | 3 + .../Public/Graph/Nodes/FlowGraphNode.h | 12 ++- .../Public/Graph/Widgets/SFlowGraphNode.h | 9 ++ 21 files changed, 354 insertions(+), 56 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 7230d2771..549bb11a4 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -31,6 +31,8 @@ UFlowNode::UFlowNode(const FObjectInitializer& ObjectInitializer) , bCanDuplicate(true) , bNodeDeprecated(false) #endif + , AllowedSignalModes({EFlowSignalMode::Enabled, EFlowSignalMode::Disabled, EFlowSignalMode::PassThrough}) + , SignalMode(EFlowSignalMode::Enabled) , bPreloaded(false) , ActivationState(EFlowNodeState::NeverActivated) { @@ -381,22 +383,30 @@ void UFlowNode::OnActivate() K2_OnActivate(); } -void UFlowNode::TriggerInput(const FName& PinName, const bool bForcedActivation /*= false*/) +void UFlowNode::TriggerInput(const FName& PinName, const EFlowPinActivationType ActivationType /*= Default*/) { + if (SignalMode == EFlowSignalMode::Disabled) + { + // entirely ignore any Input activation + } + if (InputPins.Contains(PinName)) { - const EFlowNodeState PreviousActivationState = ActivationState; - if (PreviousActivationState != EFlowNodeState::Active) + if (SignalMode == EFlowSignalMode::Enabled) { - OnActivate(); - } + const EFlowNodeState PreviousActivationState = ActivationState; + if (PreviousActivationState != EFlowNodeState::Active) + { + OnActivate(); + } - ActivationState = EFlowNodeState::Active; + ActivationState = EFlowNodeState::Active; + } #if !UE_BUILD_SHIPPING // record for debugging TArray& Records = InputRecords.FindOrAdd(PinName); - Records.Add(FPinRecord(FApp::GetCurrentTime(), bForcedActivation)); + Records.Add(FPinRecord(FApp::GetCurrentTime(), ActivationType)); #endif // UE_BUILD_SHIPPING #if WITH_EDITOR @@ -414,7 +424,14 @@ void UFlowNode::TriggerInput(const FName& PinName, const bool bForcedActivation return; } - ExecuteInput(PinName); + if (SignalMode == EFlowSignalMode::Enabled) + { + ExecuteInput(PinName); + } + else if (SignalMode == EFlowSignalMode::PassThrough) + { + OnPassThrough(); + } } void UFlowNode::ExecuteInput(const FName& PinName) @@ -430,7 +447,7 @@ void UFlowNode::TriggerFirstOutput(const bool bFinish) } } -void UFlowNode::TriggerOutput(const FName& PinName, const bool bFinish /*= false*/, const bool bForcedActivation /*= false*/) +void UFlowNode::TriggerOutput(const FName& PinName, const bool bFinish /*= false*/, const EFlowPinActivationType ActivationType /*= Default*/) { // clean up node, if needed if (bFinish) @@ -443,7 +460,7 @@ void UFlowNode::TriggerOutput(const FName& PinName, const bool bFinish /*= false { // record for debugging, even if nothing is connected to this pin TArray& Records = OutputRecords.FindOrAdd(PinName); - Records.Add(FPinRecord(FApp::GetCurrentTime(), bForcedActivation)); + Records.Add(FPinRecord(FApp::GetCurrentTime(), ActivationType)); #if WITH_EDITOR if (GetWorld()->WorldType == EWorldType::PIE && UFlowAsset::GetFlowGraphInterface().IsValid()) @@ -466,9 +483,9 @@ void UFlowNode::TriggerOutput(const FName& PinName, const bool bFinish /*= false } } -void UFlowNode::TriggerOutputPin(const FFlowOutputPinHandle Pin, const bool bFinish, const bool bForcedActivation) +void UFlowNode::TriggerOutputPin(const FFlowOutputPinHandle Pin, const bool bFinish, const EFlowPinActivationType ActivationType /*= Default*/) { - TriggerOutput(Pin.PinName, bFinish, bForcedActivation); + TriggerOutput(Pin.PinName, bFinish, ActivationType); } void UFlowNode::TriggerOutput(const FString& PinName, const bool bFinish) @@ -691,7 +708,20 @@ void UFlowNode::LoadInstance(const FFlowNodeSaveData& NodeRecord) FlowAsset->OnActivationStateLoaded(this); } - OnLoad(); + switch (SignalMode) + { + case EFlowSignalMode::Enabled: + OnLoad(); + break; + case EFlowSignalMode::Disabled: + // designer doesn't want to execute this node's logic at all, so we kill it + Finish(); + break; + case EFlowSignalMode::PassThrough: + OnPassThrough(); + break; + default: ; + } } void UFlowNode::OnSave_Implementation() @@ -701,3 +731,18 @@ void UFlowNode::OnSave_Implementation() void UFlowNode::OnLoad_Implementation() { } + +void UFlowNode::OnPassThrough_Implementation() +{ + // trigger all connected outputs + // pin connections aren't serialized to the SaveGame, so users can safely change connections post game release + for (const FFlowPin& OutputPin : OutputPins) + { + if (Connections.Contains(OutputPin.PinName)) + { + TriggerOutput(OutputPin.PinName, false, EFlowPinActivationType::PassThrough); + } + } + + Finish(); +} diff --git a/Source/Flow/Private/Nodes/FlowPin.cpp b/Source/Flow/Private/Nodes/FlowPin.cpp index 3ae2a42d5..d072bd140 100644 --- a/Source/Flow/Private/Nodes/FlowPin.cpp +++ b/Source/Flow/Private/Nodes/FlowPin.cpp @@ -7,17 +7,18 @@ FString FPinRecord::NoActivations = TEXT("No activations"); FString FPinRecord::PinActivations = TEXT("Pin activations"); FString FPinRecord::ForcedActivation = TEXT(" (forced activation)"); +FString FPinRecord::PassThroughActivation = TEXT(" (pass-through activation)"); FPinRecord::FPinRecord() : Time(0.0f) , HumanReadableTime(FString()) - , bForcedActivation(false) + , ActivationType(EFlowPinActivationType::Default) { } -FPinRecord::FPinRecord(const double InTime, const bool bInForcedActivation) +FPinRecord::FPinRecord(const double InTime, const EFlowPinActivationType InActivationType) : Time(InTime) - , bForcedActivation(bInForcedActivation) + , ActivationType(InActivationType) { const FDateTime SystemTime(FDateTime::Now()); HumanReadableTime = DoubleDigit(SystemTime.GetHour()) + TEXT(".") diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp index a4f2f6aca..bfa30140d 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp @@ -11,6 +11,7 @@ UFlowNode_CustomInput::UFlowNode_CustomInput(const FObjectInitializer& ObjectIni #endif InputPins.Empty(); + AllowedSignalModes = {EFlowSignalMode::Enabled, EFlowSignalMode::Disabled}; } void UFlowNode_CustomInput::ExecuteInput(const FName& PinName) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp index fe32ed2ae..48acfe972 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp @@ -14,6 +14,7 @@ UFlowNode_CustomOutput::UFlowNode_CustomOutput(const FObjectInitializer& ObjectI #endif OutputPins.Empty(); + AllowedSignalModes = {EFlowSignalMode::Enabled, EFlowSignalMode::Disabled}; } void UFlowNode_CustomOutput::ExecuteInput(const FName& PinName) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp index c8ca5016b..455989751 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp @@ -17,6 +17,7 @@ UFlowNode_ExecutionMultiGate::UFlowNode_ExecutionMultiGate(const FObjectInitiali InputPins.Add(FFlowPin(TEXT("Reset"), ResetPinTooltip)); SetNumberedOutputPins(0, 1); + AllowedSignalModes = {EFlowSignalMode::Enabled, EFlowSignalMode::Disabled}; } void UFlowNode_ExecutionMultiGate::ExecuteInput(const FName& PinName) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp index 93afd9dd9..e7d8d1631 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp @@ -11,6 +11,7 @@ UFlowNode_ExecutionSequence::UFlowNode_ExecutionSequence(const FObjectInitialize #endif SetNumberedOutputPins(0, 1); + AllowedSignalModes = {EFlowSignalMode::Enabled, EFlowSignalMode::Disabled}; } void UFlowNode_ExecutionSequence::ExecuteInput(const FName& PinName) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp index ba6a5e635..7001d8617 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp @@ -11,6 +11,7 @@ UFlowNode_Finish::UFlowNode_Finish(const FObjectInitializer& ObjectInitializer) #endif OutputPins = {}; + AllowedSignalModes = {EFlowSignalMode::Enabled, EFlowSignalMode::Disabled}; } void UFlowNode_Finish::ExecuteInput(const FName& PinName) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Reroute.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Reroute.cpp index 3f002b6bd..cf9d012d3 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Reroute.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Reroute.cpp @@ -8,6 +8,8 @@ UFlowNode_Reroute::UFlowNode_Reroute(const FObjectInitializer& ObjectInitializer #if WITH_EDITOR Category = TEXT("Route"); #endif + + AllowedSignalModes = {EFlowSignalMode::Enabled, EFlowSignalMode::Disabled}; } void UFlowNode_Reroute::ExecuteInput(const FName& PinName) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp index 746c01adf..01fcae6fb 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp @@ -12,6 +12,7 @@ UFlowNode_Start::UFlowNode_Start(const FObjectInitializer& ObjectInitializer) #endif InputPins = {}; + AllowedSignalModes = {EFlowSignalMode::Enabled, EFlowSignalMode::Disabled}; } void UFlowNode_Start::ExecuteInput(const FName& PinName) diff --git a/Source/Flow/Public/FlowTypes.h b/Source/Flow/Public/FlowTypes.h index 11dec7977..334a6d254 100644 --- a/Source/Flow/Public/FlowTypes.h +++ b/Source/Flow/Public/FlowTypes.h @@ -39,6 +39,14 @@ enum class EFlowFinishPolicy : uint8 Abort }; +UENUM(BlueprintType) +enum class EFlowSignalMode : uint8 +{ + Enabled UMETA(ToolTip = "Node executes its logic."), + Disabled UMETA(ToolTip = "No logic executed, any Input Pin activation is ignored"), + PassThrough UMETA(ToolTip = "No logic executed, but signal pass through from Input Pin to assigned Output Pin") +}; + UENUM(BlueprintType) enum class EFlowNetMode : uint8 { diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index c1e22beac..702068bb1 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -114,6 +114,15 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte protected: virtual bool CanFinishGraph() const { return false; } +protected: + UPROPERTY(EditDefaultsOnly, Category = "FlowNode") + TArray AllowedSignalModes; + + // If enabled, signal will pass through node without calling ExecuteInput() + // Designed to handle patching + UPROPERTY() + EFlowSignalMode SignalMode; + ////////////////////////////////////////////////////////////////////////// // All created pins (default, class-specific and added by user) @@ -259,7 +268,7 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte void K2_OnActivate(); // Trigger execution of input pin - void TriggerInput(const FName& PinName, const bool bForcedActivation = false); + void TriggerInput(const FName& PinName, const EFlowPinActivationType ActivationType = EFlowPinActivationType::Default); // Method reacting on triggering Input pin virtual void ExecuteInput(const FName& PinName); @@ -273,14 +282,14 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte void TriggerFirstOutput(const bool bFinish); UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (HidePin = "bForcedActivation")) - void TriggerOutput(const FName& PinName, const bool bFinish = false, const bool bForcedActivation = false); + void TriggerOutput(const FName& PinName, const bool bFinish = false, const EFlowPinActivationType ActivationType = EFlowPinActivationType::Default); void TriggerOutput(const FString& PinName, const bool bFinish = false); void TriggerOutput(const FText& PinName, const bool bFinish = false); void TriggerOutput(const TCHAR* PinName, const bool bFinish = false); - UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (HidePin = "bForcedActivation")) - void TriggerOutputPin(const FFlowOutputPinHandle Pin, const bool bFinish = false, const bool bForcedActivation = false); + UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (HidePin = "ActivationType")) + void TriggerOutputPin(const FFlowOutputPinHandle Pin, const bool bFinish = false, const EFlowPinActivationType ActivationType = EFlowPinActivationType::Default); // Finish execution of node, it will call Cleanup UFUNCTION(BlueprintCallable, Category = "FlowNode") @@ -386,6 +395,9 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UFUNCTION(BlueprintNativeEvent, Category = "FlowNode") void OnLoad(); + UFUNCTION(BlueprintNativeEvent, Category = "FlowNode") + void OnPassThrough(); + private: UPROPERTY() TArray InputNames_DEPRECATED; diff --git a/Source/Flow/Public/Nodes/FlowPin.h b/Source/Flow/Public/Nodes/FlowPin.h index 42186bfc9..78ef72c41 100644 --- a/Source/Flow/Public/Nodes/FlowPin.h +++ b/Source/Flow/Public/Nodes/FlowPin.h @@ -20,6 +20,8 @@ struct FLOW_API FFlowPin UPROPERTY(EditDefaultsOnly, Category = "FlowPin") FString PinToolTip; + static inline FName AnyPinName = TEXT("AnyPinName"); + FFlowPin() : PinName(NAME_None) { @@ -180,20 +182,29 @@ struct FLOW_API FConnectedPin } }; +UENUM(BlueprintType) +enum class EFlowPinActivationType : uint8 +{ + Default, + Forced, + PassThrough +}; + // Every time pin is activated, we record it and display this data while user hovers mouse over pin #if !UE_BUILD_SHIPPING struct FLOW_API FPinRecord { double Time; FString HumanReadableTime; - bool bForcedActivation; + EFlowPinActivationType ActivationType; static FString NoActivations; static FString PinActivations; static FString ForcedActivation; + static FString PassThroughActivation; FPinRecord(); - FPinRecord(const double InTime, const bool bInForcedActivation); + FPinRecord(const double InTime, const EFlowPinActivationType InActivationType); private: FORCEINLINE static FString DoubleDigit(const int32 Number); diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index 8d666d7de..3debd5ab7 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -476,13 +476,34 @@ void FFlowAssetEditor::BindGraphCommands() ); // Execution Override commands + ToolkitCommands->MapAction(FlowGraphCommands.EnableNode, + FExecuteAction::CreateSP(this, &FFlowAssetEditor::SetSignalMode, EFlowSignalMode::Enabled), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanSetSignalMode, EFlowSignalMode::Enabled), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanSetSignalMode, EFlowSignalMode::Enabled) + ); + + ToolkitCommands->MapAction(FlowGraphCommands.DisableNode, + FExecuteAction::CreateSP(this, &FFlowAssetEditor::SetSignalMode, EFlowSignalMode::Disabled), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanSetSignalMode, EFlowSignalMode::Disabled), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanSetSignalMode, EFlowSignalMode::Disabled) + ); + + ToolkitCommands->MapAction(FlowGraphCommands.SetPassThrough, + FExecuteAction::CreateSP(this, &FFlowAssetEditor::SetSignalMode, EFlowSignalMode::PassThrough), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanSetSignalMode, EFlowSignalMode::PassThrough), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanSetSignalMode, EFlowSignalMode::PassThrough) + ); + ToolkitCommands->MapAction(FlowGraphCommands.ForcePinActivation, FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnForcePinActivation), FCanExecuteAction::CreateStatic(&FFlowAssetEditor::IsPIE), FIsActionChecked(), FIsActionButtonVisible::CreateStatic(&FFlowAssetEditor::IsPIE) ); - + // Jump commands ToolkitCommands->MapAction(FlowGraphCommands.FocusViewport, FExecuteAction::CreateSP(this, &FFlowAssetEditor::FocusViewport), @@ -1238,6 +1259,29 @@ bool FFlowAssetEditor::CanTogglePinBreakpoint() const return FocusedGraphEditor->GetGraphPinForMenu() != nullptr; } +void FFlowAssetEditor::SetSignalMode(const EFlowSignalMode Mode) const +{ + for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) + { + SelectedNode->SetSignalMode(Mode); + } +} + +bool FFlowAssetEditor::CanSetSignalMode(const EFlowSignalMode Mode) const +{ + if (IsPIE()) + { + return false; + } + + for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) + { + return SelectedNode->CanSetSignalMode(Mode); + } + + return false; +} + void FFlowAssetEditor::OnForcePinActivation() const { if (UEdGraphPin* Pin = FocusedGraphEditor->GetGraphPinForMenu()) diff --git a/Source/FlowEditor/Private/FlowEditorCommands.cpp b/Source/FlowEditor/Private/FlowEditorCommands.cpp index 28b6114d2..9e092496d 100644 --- a/Source/FlowEditor/Private/FlowEditorCommands.cpp +++ b/Source/FlowEditor/Private/FlowEditorCommands.cpp @@ -42,6 +42,9 @@ void FFlowGraphCommands::RegisterCommands() UI_COMMAND(DisablePinBreakpoint, "Disable Pin Breakpoint", "Disables a breakpoint on the pin", EUserInterfaceActionType::Button, FInputChord()); UI_COMMAND(TogglePinBreakpoint, "Toggle Pin Breakpoint", "Toggles a breakpoint on the pin", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(EnableNode, "Enable Node", "Enable node execution", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(DisableNode, "Disable Node", "Any Input Pin activation would be ignored", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(SetPassThrough, "Set Pass Through", "Signal will pass through node without executing its logic", EUserInterfaceActionType::Button, FInputChord()); UI_COMMAND(ForcePinActivation, "Force Pin Activation", "Forces execution of the pin in a graph, used to bypass blockers", EUserInterfaceActionType::Button, FInputChord()); UI_COMMAND(FocusViewport, "Focus Viewport", "Focus viewport on actor assigned to the node", EUserInterfaceActionType::Button, FInputChord()); diff --git a/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp b/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp index 4d462c1d7..78397fd7f 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp @@ -127,32 +127,15 @@ void FFlowGraphConnectionDrawingPolicy::DetermineWiringStyle(UEdGraphPin* Output check(GraphObj); const UEdGraphSchema* Schema = GraphObj->GetSchema(); - { - //If reroute node path goes backwards, we need to flip the direction to make it look nice - //(all of the logic for this is basically same as in FKismetConnectionDrawingPolicy) - UEdGraphNode* OutputNode = OutputPin->GetOwningNode(); - UEdGraphNode* InputNode = (InputPin != nullptr) ? InputPin->GetOwningNode() : nullptr; - if (auto* OutputRerouteNode = Cast(OutputNode)) - { - if (ShouldChangeTangentForReroute(OutputRerouteNode)) - { - Params.StartDirection = EGPD_Input; - } - } - - if (auto* InputRerouteNode = Cast(InputNode)) - { - if (ShouldChangeTangentForReroute(InputRerouteNode)) - { - Params.EndDirection = EGPD_Output; - } - } - } - if (OutputPin->bOrphanedPin || (InputPin && InputPin->bOrphanedPin)) { Params.WireColor = FLinearColor::Red; } + else if (Cast(OutputPin->GetOwningNode())->GetSignalMode() == EFlowSignalMode::Disabled) + { + Params.WireColor *= 0.5f; + Params.WireThickness = 0.5f; + } else { Params.WireColor = Schema->GetPinTypeColor(OutputPin->PinType); @@ -191,6 +174,28 @@ void FFlowGraphConnectionDrawingPolicy::DetermineWiringStyle(UEdGraphPin* Output Params.WireThickness = InactiveWireThickness; } } + + // If reroute node path goes backwards, we need to flip the direction to make it look nice + // (all of the logic for this is basically same as in FKismetConnectionDrawingPolicy) + { + UEdGraphNode* OutputNode = OutputPin->GetOwningNode(); + UEdGraphNode* InputNode = (InputPin != nullptr) ? InputPin->GetOwningNode() : nullptr; + if (auto* OutputRerouteNode = Cast(OutputNode)) + { + if (ShouldChangeTangentForReroute(OutputRerouteNode)) + { + Params.StartDirection = EGPD_Input; + } + } + + if (auto* InputRerouteNode = Cast(InputNode)) + { + if (ShouldChangeTangentForReroute(InputRerouteNode)) + { + Params.EndDirection = EGPD_Output; + } + } + } } void FFlowGraphConnectionDrawingPolicy::Draw(TMap, FArrangedWidget>& InPinGeometries, FArrangedChildren& ArrangedNodes) diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 91eb93198..4eb48e408 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -524,6 +524,22 @@ void UFlowGraphNode::GetNodeContextMenuActions(class UToolMenu* Menu, class UGra Section.AddMenuEntry(GraphCommands.ToggleBreakpoint); } + { + FToolMenuSection& Section = Menu->AddSection("FlowGraphNodeExecutionOverride", LOCTEXT("NodeExecutionOverrideMenuHeader", "Execution Override")); + if (CanSetSignalMode(EFlowSignalMode::Enabled)) + { + Section.AddMenuEntry(FlowGraphCommands.EnableNode); + } + if (CanSetSignalMode(EFlowSignalMode::Disabled)) + { + Section.AddMenuEntry(FlowGraphCommands.DisableNode); + } + if (CanSetSignalMode(EFlowSignalMode::PassThrough)) + { + Section.AddMenuEntry(FlowGraphCommands.SetPassThrough); + } + } + { FToolMenuSection& Section = Menu->AddSection("FlowGraphNodeJumps", LOCTEXT("NodeJumpsMenuHeader", "Jumps")); if (CanFocusViewport()) @@ -749,7 +765,7 @@ void UFlowGraphNode::CreateInputPin(const FFlowPin& FlowPin, const int32 Index / NewPin->bAllowFriendlyName = true; NewPin->PinFriendlyName = FlowPin.PinFriendlyName; } - + NewPin->PinToolTip = FlowPin.PinToolTip; InputPins.Emplace(NewPin); @@ -832,7 +848,7 @@ void UFlowGraphNode::AddInstancePin(const EEdGraphPinDirection Direction, const Modify(); const FFlowPin PinName = FFlowPin(FString::FromInt(NumberedPinsAmount)); - + if (Direction == EGPD_Input) { if (FlowNode->InputPins.IsValidIndex(NumberedPinsAmount)) @@ -843,7 +859,7 @@ void UFlowGraphNode::AddInstancePin(const EEdGraphPinDirection Direction, const { FlowNode->InputPins.Add(PinName); } - + CreateInputPin(PinName, NumberedPinsAmount); } else @@ -856,7 +872,7 @@ void UFlowGraphNode::AddInstancePin(const EEdGraphPinDirection Direction, const { FlowNode->OutputPins.Add(PinName); } - + CreateOutputPin(PinName, FlowNode->InputPins.Num() + NumberedPinsAmount); } @@ -950,9 +966,17 @@ void UFlowGraphNode::GetPinHoverText(const UEdGraphPin& Pin, FString& HoverTextO HoverTextOut.Append(LINE_TERMINATOR); HoverTextOut.Appendf(TEXT("%d) %s"), i + 1, *PinRecords[i].HumanReadableTime); - if (PinRecords[i].bForcedActivation) + switch (PinRecords[i].ActivationType) { - HoverTextOut.Append(FPinRecord::ForcedActivation); + case EFlowPinActivationType::Default: + break; + case EFlowPinActivationType::Forced: + HoverTextOut.Append(FPinRecord::ForcedActivation); + break; + case EFlowPinActivationType::PassThrough: + HoverTextOut.Append(FPinRecord::PassThroughActivation); + break; + default: ; } } } @@ -1035,14 +1059,33 @@ void UFlowGraphNode::ForcePinActivation(const FEdGraphPinReference PinReference) switch (FoundPin->Direction) { case EGPD_Input: - InspectedNodeInstance->TriggerInput(FoundPin->PinName, true); + InspectedNodeInstance->TriggerInput(FoundPin->PinName, EFlowPinActivationType::Forced); break; case EGPD_Output: - InspectedNodeInstance->TriggerOutput(FoundPin->PinName, false, true); + InspectedNodeInstance->TriggerOutput(FoundPin->PinName, false, EFlowPinActivationType::Forced); break; default: ; } } } +void UFlowGraphNode::SetSignalMode(const EFlowSignalMode Mode) +{ + if (FlowNode) + { + FlowNode->SignalMode = Mode; + OnSignalModeChanged.ExecuteIfBound(); + } +} + +EFlowSignalMode UFlowGraphNode::GetSignalMode() const +{ + return FlowNode ? FlowNode->SignalMode : EFlowSignalMode::Disabled; +} + +bool UFlowGraphNode::CanSetSignalMode(const EFlowSignalMode Mode) const +{ + return FlowNode ? (FlowNode->AllowedSignalModes.Contains(Mode) && FlowNode->SignalMode != Mode) : false; +} + #undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp index ad3bc9442..20f84b970 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp @@ -29,6 +29,7 @@ #include "Widgets/SBoxPanel.h" #include "Widgets/SOverlay.h" #include "Widgets/SToolTip.h" +#include "Widgets/Text/SInlineEditableTextBlock.h" #define LOCTEXT_NAMESPACE "SFlowGraphNode" @@ -46,7 +47,9 @@ void SFlowGraphPinExec::Construct(const FArguments& InArgs, UEdGraphPin* InPin) void SFlowGraphNode::Construct(const FArguments& InArgs, UFlowGraphNode* InNode) { GraphNode = InNode; + FlowGraphNode = InNode; + FlowGraphNode->OnSignalModeChanged.BindRaw(this, &SFlowGraphNode::UpdateGraphNode); SetCursor(EMouseCursor::CardinalCross); UpdateGraphNode(); @@ -215,7 +218,7 @@ void SFlowGraphNode::UpdateGraphNode() [ SNew(SImage) .Image(IconBrush) - .ColorAndOpacity(this, &SGraphNode::GetNodeTitleIconColor) + .ColorAndOpacity(this, &SFlowGraphNode::GetNodeTitleIconColor) ] + SHorizontalBox::Slot() [ @@ -310,7 +313,7 @@ void SFlowGraphNode::UpdateGraphNode() [ SNew(SImage) .Image(GetNodeBodyBrush()) - .ColorAndOpacity(this, &SGraphNode::GetNodeBodyColor) + .ColorAndOpacity(this, &SFlowGraphNode::GetNodeBodyColor) ] + SOverlay::Slot() [ @@ -370,6 +373,20 @@ void SFlowGraphNode::UpdateErrorInfo() SGraphNode::UpdateErrorInfo(); } +TSharedRef SFlowGraphNode::CreateTitleWidget(TSharedPtr NodeTitle) +{ + SAssignNew(InlineEditableText, SInlineEditableTextBlock) + .Style(FAppStyle::Get(), "Graph.Node.NodeTitleInlineEditableText") + .Text(NodeTitle.Get(), &SNodeTitle::GetHeadTitle) + .OnVerifyTextChanged(this, &SFlowGraphNode::OnVerifyNameTextChanged) + .OnTextCommitted(this, &SFlowGraphNode::OnNameTextCommited) + .IsReadOnly(this, &SFlowGraphNode::IsNameReadOnly) + .IsSelected(this, &SFlowGraphNode::IsSelectedExclusively); + InlineEditableText->SetColorAndOpacity(TAttribute::Create(TAttribute::FGetter::CreateSP(this, &SFlowGraphNode::GetNodeTitleTextColor))); + + return InlineEditableText.ToSharedRef(); +} + TSharedRef SFlowGraphNode::CreateNodeContentArea() { return SNew(SBorder) @@ -398,6 +415,80 @@ const FSlateBrush* SFlowGraphNode::GetNodeBodyBrush() const return FFlowEditorStyle::GetBrush("Flow.Node.Body"); } +FSlateColor SFlowGraphNode::GetNodeTitleColor() const +{ + FLinearColor ReturnTitleColor = GraphNode->IsDeprecated() ? FLinearColor::Red : GetNodeObj()->GetNodeTitleColor(); + + if (FlowGraphNode->GetSignalMode() == EFlowSignalMode::Enabled) + { + ReturnTitleColor.A = FadeCurve.GetLerp(); + } + else + { + ReturnTitleColor *= FLinearColor(0.5f, 0.5f, 0.5f, 0.4f); + } + + return ReturnTitleColor; +} + +FSlateColor SFlowGraphNode::GetNodeBodyColor() const +{ + FLinearColor ReturnBodyColor = GraphNode->GetNodeBodyTintColor(); + if (FlowGraphNode->GetSignalMode() != EFlowSignalMode::Enabled) + { + ReturnBodyColor *= FLinearColor(1.0f, 1.0f, 1.0f, 0.5f); + } + return ReturnBodyColor; +} + +FSlateColor SFlowGraphNode::GetNodeTitleIconColor() const +{ + FLinearColor ReturnIconColor = IconColor; + if (FlowGraphNode->GetSignalMode() != EFlowSignalMode::Enabled) + { + ReturnIconColor *= FLinearColor(1.0f, 1.0f, 1.0f, 0.3f); + } + return ReturnIconColor; +} + +FLinearColor SFlowGraphNode::GetNodeTitleTextColor() const +{ + FLinearColor ReturnTextColor = FLinearColor::White; + if (FlowGraphNode->GetSignalMode() != EFlowSignalMode::Enabled) + { + ReturnTextColor *= FLinearColor(1.0f, 1.0f, 1.0f, 0.3f); + } + return ReturnTextColor; +} + +TSharedPtr SFlowGraphNode::GetEnabledStateWidget() const +{ + if (FlowGraphNode->GetSignalMode() != EFlowSignalMode::Enabled && !GraphNode->IsAutomaticallyPlacedGhostNode()) + { + const bool bPassThrough = FlowGraphNode->GetSignalMode() == EFlowSignalMode::PassThrough; + const FText StatusMessage = bPassThrough ? LOCTEXT("PassThrough", "Pass Through") : LOCTEXT("DisabledNode", "Disabled"); + const FText StatusMessageTooltip = bPassThrough ? + LOCTEXT("PassThroughTooltip", "This node won't execute internal logic, but it will trigger all connected outputs") : + LOCTEXT("DisabledNodeTooltip", "This node is disabled and will not be executed"); + + return SNew(SBorder) + .BorderImage(FAppStyle::GetBrush(bPassThrough ? "Graph.Node.DevelopmentBanner" : "Graph.Node.DisabledBanner")) + .HAlign(HAlign_Fill) + .VAlign(VAlign_Fill) + [ + SNew(STextBlock) + .Text(StatusMessage) + .ToolTipText(StatusMessageTooltip) + .Justification(ETextJustify::Center) + .ColorAndOpacity(FLinearColor::White) + .ShadowOffset(FVector2D::UnitVector) + .Visibility(EVisibility::Visible) + ]; + } + + return TSharedPtr(); +} + void SFlowGraphNode::CreateStandardPinWidget(UEdGraphPin* Pin) { const TSharedPtr NewPin = SNew(SFlowGraphPinExec, Pin); diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index 96264559c..a72f0f59e 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -9,6 +9,8 @@ #include "Toolkits/IToolkitHost.h" #include "UObject/GCObject.h" +#include "FlowTypes.h" + class SFlowPalette; class UFlowAsset; class UFlowGraphNode; @@ -206,6 +208,9 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit bool CanToggleBreakpoint() const; bool CanTogglePinBreakpoint() const; + void SetSignalMode(const EFlowSignalMode Mode) const; + bool CanSetSignalMode(const EFlowSignalMode Mode) const; + void OnForcePinActivation() const; void FocusViewport() const; diff --git a/Source/FlowEditor/Public/FlowEditorCommands.h b/Source/FlowEditor/Public/FlowEditorCommands.h index 09bb6f485..64b0f4a7d 100644 --- a/Source/FlowEditor/Public/FlowEditorCommands.h +++ b/Source/FlowEditor/Public/FlowEditorCommands.h @@ -40,6 +40,9 @@ class FFlowGraphCommands final : public TCommands TSharedPtr TogglePinBreakpoint; /** Execution Override */ + TSharedPtr EnableNode; + TSharedPtr DisableNode; + TSharedPtr SetPassThrough; TSharedPtr ForcePinActivation; /** Jumps */ diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h index a47e80e68..7cbc1092f 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h @@ -45,6 +45,8 @@ struct FLOWEDITOR_API FFlowBreakpoint void ToggleBreakpoint(); }; +DECLARE_DELEGATE(FFlowGraphNodeEvent); + /** * Graph representation of the Flow Node */ @@ -221,6 +223,14 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode // Execution Override public: + FFlowGraphNodeEvent OnSignalModeChanged; + // Pin activation forced by user during PIE - void ForcePinActivation(const FEdGraphPinReference PinReference) const; + virtual void ForcePinActivation(const FEdGraphPinReference PinReference) const; + + // Pass-through forced by designer, set per node instance + virtual void SetSignalMode(const EFlowSignalMode Mode); + + virtual EFlowSignalMode GetSignalMode() const; + virtual bool CanSetSignalMode(const EFlowSignalMode Mode) const; }; diff --git a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h index d54e948df..3d50e872e 100644 --- a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h @@ -38,9 +38,18 @@ class FLOWEDITOR_API SFlowGraphNode : public SGraphNode // SGraphNode virtual void UpdateGraphNode() override; virtual void UpdateErrorInfo() override; + + virtual TSharedRef CreateTitleWidget(TSharedPtr NodeTitle) override; virtual TSharedRef CreateNodeContentArea() override; virtual const FSlateBrush* GetNodeBodyBrush() const override; + // purposely overriden non-virtual methods, avoiding engine modification + FSlateColor GetNodeTitleColor() const; + FSlateColor GetNodeBodyColor() const; + FSlateColor GetNodeTitleIconColor() const; + FLinearColor GetNodeTitleTextColor() const; + TSharedPtr GetEnabledStateWidget() const; + virtual void CreateStandardPinWidget(UEdGraphPin* Pin) override; virtual TSharedPtr GetComplexTooltip() override; From f29d9ab40b8bee544ca073355d233a504f9d4b25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Fri, 11 Nov 2022 13:14:52 +0100 Subject: [PATCH 046/485] documentation updates --- Source/Flow/Private/Nodes/FlowNode.cpp | 1 + Source/Flow/Public/FlowTypes.h | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 549bb11a4..995ec1c0c 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -744,5 +744,6 @@ void UFlowNode::OnPassThrough_Implementation() } } + // deactivate node, so it doesn't get saved to a new SaveGame Finish(); } diff --git a/Source/Flow/Public/FlowTypes.h b/Source/Flow/Public/FlowTypes.h index 334a6d254..b45dedba7 100644 --- a/Source/Flow/Public/FlowTypes.h +++ b/Source/Flow/Public/FlowTypes.h @@ -43,8 +43,8 @@ UENUM(BlueprintType) enum class EFlowSignalMode : uint8 { Enabled UMETA(ToolTip = "Node executes its logic."), - Disabled UMETA(ToolTip = "No logic executed, any Input Pin activation is ignored"), - PassThrough UMETA(ToolTip = "No logic executed, but signal pass through from Input Pin to assigned Output Pin") + Disabled UMETA(ToolTip = "No logic executed, any Input Pin activation is ignored. Node instantly enters a deactivated state."), + PassThrough UMETA(ToolTip = "Internal node logic not executed. All connected outputs are triggered, node finishes its work.") }; UENUM(BlueprintType) From 8b212d0b39b86e9964f4ec80e5adc8ea1592dc93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Fri, 11 Nov 2022 13:52:57 +0100 Subject: [PATCH 047/485] documentation updates --- Source/Flow/Public/FlowTypes.h | 2 +- Source/FlowEditor/Private/FlowEditorCommands.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Flow/Public/FlowTypes.h b/Source/Flow/Public/FlowTypes.h index b45dedba7..235cc366a 100644 --- a/Source/Flow/Public/FlowTypes.h +++ b/Source/Flow/Public/FlowTypes.h @@ -42,7 +42,7 @@ enum class EFlowFinishPolicy : uint8 UENUM(BlueprintType) enum class EFlowSignalMode : uint8 { - Enabled UMETA(ToolTip = "Node executes its logic."), + Enabled UMETA(ToolTip = "Default state, node is fully executed."), Disabled UMETA(ToolTip = "No logic executed, any Input Pin activation is ignored. Node instantly enters a deactivated state."), PassThrough UMETA(ToolTip = "Internal node logic not executed. All connected outputs are triggered, node finishes its work.") }; diff --git a/Source/FlowEditor/Private/FlowEditorCommands.cpp b/Source/FlowEditor/Private/FlowEditorCommands.cpp index 9e092496d..32478708c 100644 --- a/Source/FlowEditor/Private/FlowEditorCommands.cpp +++ b/Source/FlowEditor/Private/FlowEditorCommands.cpp @@ -42,9 +42,9 @@ void FFlowGraphCommands::RegisterCommands() UI_COMMAND(DisablePinBreakpoint, "Disable Pin Breakpoint", "Disables a breakpoint on the pin", EUserInterfaceActionType::Button, FInputChord()); UI_COMMAND(TogglePinBreakpoint, "Toggle Pin Breakpoint", "Toggles a breakpoint on the pin", EUserInterfaceActionType::Button, FInputChord()); - UI_COMMAND(EnableNode, "Enable Node", "Enable node execution", EUserInterfaceActionType::Button, FInputChord()); - UI_COMMAND(DisableNode, "Disable Node", "Any Input Pin activation would be ignored", EUserInterfaceActionType::Button, FInputChord()); - UI_COMMAND(SetPassThrough, "Set Pass Through", "Signal will pass through node without executing its logic", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(EnableNode, "Enable Node", "Default state, node is fully executed.", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(DisableNode, "Disable Node", "No logic executed, any Input Pin activation is ignored. Node instantly enters a deactivated state.", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(SetPassThrough, "Set Pass Through", "Internal node logic not executed. All connected outputs are triggered, node finishes its work.", EUserInterfaceActionType::Button, FInputChord()); UI_COMMAND(ForcePinActivation, "Force Pin Activation", "Forces execution of the pin in a graph, used to bypass blockers", EUserInterfaceActionType::Button, FInputChord()); UI_COMMAND(FocusViewport, "Focus Viewport", "Focus viewport on actor assigned to the node", EUserInterfaceActionType::Button, FInputChord()); From 9dcb391111443ed5535b0231a84a63d812b969b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Tue, 15 Nov 2022 19:02:57 +0100 Subject: [PATCH 048/485] Flow Asset diff enabled by default --- .../Private/Asset/AssetTypeActions_FlowAsset.cpp | 6 ------ Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp | 8 +------- Source/FlowEditor/Private/Asset/FlowDiffControl.cpp | 8 -------- Source/FlowEditor/Private/Asset/SFlowDiff.cpp | 8 -------- .../FlowEditor/Public/Asset/AssetTypeActions_FlowAsset.h | 8 -------- Source/FlowEditor/Public/Asset/FlowDiffControl.h | 8 -------- Source/FlowEditor/Public/Asset/SFlowDiff.h | 8 -------- Source/FlowEditor/Public/FlowEditorDefines.h | 9 --------- 8 files changed, 1 insertion(+), 62 deletions(-) delete mode 100644 Source/FlowEditor/Public/FlowEditorDefines.h diff --git a/Source/FlowEditor/Private/Asset/AssetTypeActions_FlowAsset.cpp b/Source/FlowEditor/Private/Asset/AssetTypeActions_FlowAsset.cpp index a9edee225..23d0d0d77 100644 --- a/Source/FlowEditor/Private/Asset/AssetTypeActions_FlowAsset.cpp +++ b/Source/FlowEditor/Private/Asset/AssetTypeActions_FlowAsset.cpp @@ -40,11 +40,6 @@ void FAssetTypeActions_FlowAsset::OpenAssetEditor(const TArray& InObje } } -/** - * Documentation: https://github.com/MothCocoon/FlowGraph/wiki/Visual-Diff - * Set macro value to 1, if you made these changes to the engine: https://github.com/EpicGames/UnrealEngine/pull/9659 - */ -#if ENABLE_FLOW_DIFF void FAssetTypeActions_FlowAsset::PerformAssetDiff(UObject* OldAsset, UObject* NewAsset, const FRevisionInfo& OldRevision, const FRevisionInfo& NewRevision) const { const UFlowAsset* OldFlow = CastChecked(OldAsset); @@ -59,6 +54,5 @@ void FAssetTypeActions_FlowAsset::PerformAssetDiff(UObject* OldAsset, UObject* N SFlowDiff::CreateDiffWindow(WindowTitle, OldFlow, NewFlow, OldRevision, NewRevision); } -#endif #undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp index 0f967081d..8dc092482 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp @@ -5,7 +5,6 @@ #include "Asset/FlowAssetEditor.h" #include "Asset/SAssetRevisionMenu.h" #include "FlowEditorCommands.h" -#include "FlowEditorDefines.h" #include "FlowAsset.h" @@ -198,11 +197,7 @@ void FFlowAssetToolbar::BuildAssetToolbar(UToolMenu* ToolbarMenu) const Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().RefreshAsset)); - /** - * Documentation: https://github.com/MothCocoon/FlowGraph/wiki/Visual-Diff - * Set macro value to 1, if you made these changes to the engine: https://github.com/EpicGames/UnrealEngine/pull/9659 - */ -#if ENABLE_FLOW_DIFF + // visual diff: menu to choose asset revision compared with the current one FToolMenuSection& DiffSection = ToolbarMenu->AddSection("SourceControl"); DiffSection.InsertPosition = FToolMenuInsert("Asset", EToolMenuInsertType::After); DiffSection.AddDynamicEntry("SourceControlCommands", FNewToolMenuSectionDelegate::CreateLambda([this](FToolMenuSection& InSection) @@ -219,7 +214,6 @@ void FFlowAssetToolbar::BuildAssetToolbar(UToolMenu* ToolbarMenu) const DiffEntry.StyleNameOverride = "CalloutToolbar"; InSection.AddEntry(DiffEntry); })); -#endif } /** Delegate called to diff a specific revision with the current */ diff --git a/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp b/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp index cd68d6502..752de02a4 100644 --- a/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp +++ b/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp @@ -1,13 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "Asset/FlowDiffControl.h" - -/** - * Documentation: https://github.com/MothCocoon/FlowGraph/wiki/Visual-Diff - * Set macro value to 1, if you made these changes to the engine: https://github.com/EpicGames/UnrealEngine/pull/9659 - */ - -#if ENABLE_FLOW_DIFF #include "Asset/SFlowDiff.h" #include "FlowAsset.h" @@ -190,4 +183,3 @@ void FFlowGraphToDiff::OnGraphChanged(const FEdGraphEditAction& Action) const } #undef LOCTEXT_NAMESPACE -#endif diff --git a/Source/FlowEditor/Private/Asset/SFlowDiff.cpp b/Source/FlowEditor/Private/Asset/SFlowDiff.cpp index ec58621f1..3dddec73d 100644 --- a/Source/FlowEditor/Private/Asset/SFlowDiff.cpp +++ b/Source/FlowEditor/Private/Asset/SFlowDiff.cpp @@ -1,13 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -/** - * Documentation: https://github.com/MothCocoon/FlowGraph/wiki/Visual-Diff - * Set macro value to 1, if you made these changes to the engine: https://github.com/EpicGames/UnrealEngine/pull/9659 - */ - #include "Asset/SFlowDiff.h" - -#if ENABLE_FLOW_DIFF #include "Asset/FlowDiffControl.h" #include "FlowAsset.h" @@ -859,4 +852,3 @@ void SFlowDiff::OnModeChanged(const FName& InNewViewMode) const } #undef LOCTEXT_NAMESPACE -#endif diff --git a/Source/FlowEditor/Public/Asset/AssetTypeActions_FlowAsset.h b/Source/FlowEditor/Public/Asset/AssetTypeActions_FlowAsset.h index dfab704c8..8cbd2f330 100644 --- a/Source/FlowEditor/Public/Asset/AssetTypeActions_FlowAsset.h +++ b/Source/FlowEditor/Public/Asset/AssetTypeActions_FlowAsset.h @@ -5,8 +5,6 @@ #include "AssetTypeActions_Base.h" #include "Toolkits/IToolkitHost.h" -#include "FlowEditorDefines.h" - class FLOWEDITOR_API FAssetTypeActions_FlowAsset : public FAssetTypeActions_Base { public: @@ -17,11 +15,5 @@ class FLOWEDITOR_API FAssetTypeActions_FlowAsset : public FAssetTypeActions_Base virtual UClass* GetSupportedClass() const override; virtual void OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor = TSharedPtr()) override; - /** - * Documentation: https://github.com/MothCocoon/FlowGraph/wiki/Visual-Diff - * Set macro value to 1, if you made these changes to the engine: https://github.com/EpicGames/UnrealEngine/pull/9659 - */ -#if ENABLE_FLOW_DIFF virtual void PerformAssetDiff(UObject* OldAsset, UObject* NewAsset, const FRevisionInfo& OldRevision, const FRevisionInfo& NewRevision) const override; -#endif }; diff --git a/Source/FlowEditor/Public/Asset/FlowDiffControl.h b/Source/FlowEditor/Public/Asset/FlowDiffControl.h index efb6aaf11..0d0bba5d9 100644 --- a/Source/FlowEditor/Public/Asset/FlowDiffControl.h +++ b/Source/FlowEditor/Public/Asset/FlowDiffControl.h @@ -2,13 +2,6 @@ #pragma once -/** - * Documentation: https://github.com/MothCocoon/FlowGraph/wiki/Visual-Diff - * Set macro value to 1, if you made these changes to the engine: https://github.com/EpicGames/UnrealEngine/pull/9659 - */ - -#include "FlowEditorDefines.h" -#if ENABLE_FLOW_DIFF #include "DetailsDiff.h" #include "DiffResults.h" #include "IAssetTypeActions.h" @@ -70,4 +63,3 @@ struct FLOWEDITOR_API FFlowGraphToDiff : public TSharedFromThis Date: Tue, 15 Nov 2022 18:17:55 +0100 Subject: [PATCH 049/485] Merge pull request #124 from ArseniyZvezda/refresh-nodes-5.0 Refresh Node - Fix mapping existing nodes to their new classes --- .../Private/Asset/FlowAssetEditor.cpp | 95 ++++++++++++++++++- 1 file changed, 91 insertions(+), 4 deletions(-) diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index 3debd5ab7..f450c1be1 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -33,6 +33,7 @@ #include "ScopedTransaction.h" #include "SNodePanel.h" #include "ToolMenus.h" +#include "Nodes/Route/FlowNode_Start.h" #include "Widgets/Docking/SDockTab.h" #define LOCTEXT_NAMESPACE "FlowAssetEditor" @@ -259,13 +260,99 @@ void FFlowAssetEditor::BindToolbarCommands() void FFlowAssetEditor::RefreshAsset() { - TArray FlowGraphNodes; - FlowAsset->GetGraph()->GetNodesOfClass(FlowGraphNodes); + UEdGraph* Graph = FlowAsset->GetGraph(); + + TMap AssetNodes = FlowAsset->GetNodes(); + + TArray GraphNodes; + Graph->GetNodesOfClass(GraphNodes); + + for (UFlowGraphNode* GraphNode : GraphNodes) + { + UFlowNode* FlowNode = GraphNode->GetFlowNode(); + if (FlowNode) + { + if (!FlowNode->GetGraphNode()) + { + FlowNode->FixNode(GraphNode); + GraphNode->ReconstructNode(); + UE_LOG(LogTemp, Display, TEXT("Node %s fix - Missing GraphNode %s"), *GetNameSafe(FlowNode), *GetNameSafe(GraphNode)); + } + } + } - for (UFlowGraphNode* GraphNode : FlowGraphNodes) + TArray GraphNodesToRemove; + for (auto&& Node : AssetNodes) + { + if (IsValid(Node.Value)) + { + UEdGraphNode* CurrentGraphNode = Node.Value->GetGraphNode(); + + const UClass* CorrectClass = UFlowGraphSchema::GetAssignedGraphNodeClass(Node.Value->GetClass()); + if ((!CurrentGraphNode || CurrentGraphNode->GetClass() != CorrectClass) && + Node.Value->GetClass() != UFlowNode_Start::StaticClass()) + { + UE_LOG(LogTemp, Display, TEXT("Fixing node %s ..."), *GetNameSafe(Node.Value)); + // Create a new node with the correct graph type. + UFlowGraphNode* NewGraphNode = NewObject(Graph, CorrectClass); + + NewGraphNode->NodeGuid = Node.Value->GetGuid(); + if (CurrentGraphNode) + { + NewGraphNode->NodePosX = CurrentGraphNode->NodePosX; + NewGraphNode->NodePosY = CurrentGraphNode->NodePosY; + } + else + { + NewGraphNode->NodePosX = 0; + NewGraphNode->NodePosY = 0; + } + + Graph->AddNode(NewGraphNode, false, false); + + NewGraphNode->SetFlowNode(Node.Value); + + Node.Value->FixNode(NewGraphNode); + + NewGraphNode->PostPlacedNewNode(); + NewGraphNode->AllocateDefaultPins(); + + if (CurrentGraphNode) + { + // Move links from the old node to the new node. + for (UEdGraphPin* OldNodePin : CurrentGraphNode->Pins) + { + if (UEdGraphPin** NewNodePin = NewGraphNode->Pins.FindByPredicate( + [OldNodePin](const UEdGraphPin* GraphPin) + { + return GraphPin->PinName == OldNodePin->PinName; + })) + { + TArray Connections = OldNodePin->LinkedTo; + OldNodePin->BreakAllPinLinks(true); + for (UEdGraphPin* const Connection : Connections) + { + (*(NewNodePin))->MakeLinkTo(Connection); + } + } + } + + // Remove the old node. + GraphNodesToRemove.Add(CurrentGraphNode); + } + + } + } + } + + for (UEdGraphNode* RemovedGraphNode : GraphNodesToRemove) { - GraphNode->RefreshContextPins(true); + UE_LOG(LogTemp, Display, TEXT("Deleting node %s"), *GetNameSafe(RemovedGraphNode)); + const FGuid NodeGuid = RemovedGraphNode->NodeGuid; + FBlueprintEditorUtils::RemoveNode(nullptr, RemovedGraphNode, true); + // GetFlowAsset()->UnregisterNode(NodeGuid); } + Graph->NotifyGraphChanged(); } void FFlowAssetEditor::GoToParentInstance() From ebf7810635fb043633e19a434165883a5a7fbdf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Tue, 15 Nov 2022 22:27:20 +0100 Subject: [PATCH 050/485] reverting last pull request --- .../Private/Asset/FlowAssetEditor.cpp | 95 +------------------ 1 file changed, 4 insertions(+), 91 deletions(-) diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index f450c1be1..3debd5ab7 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -33,7 +33,6 @@ #include "ScopedTransaction.h" #include "SNodePanel.h" #include "ToolMenus.h" -#include "Nodes/Route/FlowNode_Start.h" #include "Widgets/Docking/SDockTab.h" #define LOCTEXT_NAMESPACE "FlowAssetEditor" @@ -260,99 +259,13 @@ void FFlowAssetEditor::BindToolbarCommands() void FFlowAssetEditor::RefreshAsset() { - UEdGraph* Graph = FlowAsset->GetGraph(); - - TMap AssetNodes = FlowAsset->GetNodes(); - - TArray GraphNodes; - Graph->GetNodesOfClass(GraphNodes); - - for (UFlowGraphNode* GraphNode : GraphNodes) - { - UFlowNode* FlowNode = GraphNode->GetFlowNode(); - if (FlowNode) - { - if (!FlowNode->GetGraphNode()) - { - FlowNode->FixNode(GraphNode); - GraphNode->ReconstructNode(); - UE_LOG(LogTemp, Display, TEXT("Node %s fix - Missing GraphNode %s"), *GetNameSafe(FlowNode), *GetNameSafe(GraphNode)); - } - } - } + TArray FlowGraphNodes; + FlowAsset->GetGraph()->GetNodesOfClass(FlowGraphNodes); - TArray GraphNodesToRemove; - for (auto&& Node : AssetNodes) - { - if (IsValid(Node.Value)) - { - UEdGraphNode* CurrentGraphNode = Node.Value->GetGraphNode(); - - const UClass* CorrectClass = UFlowGraphSchema::GetAssignedGraphNodeClass(Node.Value->GetClass()); - if ((!CurrentGraphNode || CurrentGraphNode->GetClass() != CorrectClass) && - Node.Value->GetClass() != UFlowNode_Start::StaticClass()) - { - UE_LOG(LogTemp, Display, TEXT("Fixing node %s ..."), *GetNameSafe(Node.Value)); - // Create a new node with the correct graph type. - UFlowGraphNode* NewGraphNode = NewObject(Graph, CorrectClass); - - NewGraphNode->NodeGuid = Node.Value->GetGuid(); - if (CurrentGraphNode) - { - NewGraphNode->NodePosX = CurrentGraphNode->NodePosX; - NewGraphNode->NodePosY = CurrentGraphNode->NodePosY; - } - else - { - NewGraphNode->NodePosX = 0; - NewGraphNode->NodePosY = 0; - } - - Graph->AddNode(NewGraphNode, false, false); - - NewGraphNode->SetFlowNode(Node.Value); - - Node.Value->FixNode(NewGraphNode); - - NewGraphNode->PostPlacedNewNode(); - NewGraphNode->AllocateDefaultPins(); - - if (CurrentGraphNode) - { - // Move links from the old node to the new node. - for (UEdGraphPin* OldNodePin : CurrentGraphNode->Pins) - { - if (UEdGraphPin** NewNodePin = NewGraphNode->Pins.FindByPredicate( - [OldNodePin](const UEdGraphPin* GraphPin) - { - return GraphPin->PinName == OldNodePin->PinName; - })) - { - TArray Connections = OldNodePin->LinkedTo; - OldNodePin->BreakAllPinLinks(true); - for (UEdGraphPin* const Connection : Connections) - { - (*(NewNodePin))->MakeLinkTo(Connection); - } - } - } - - // Remove the old node. - GraphNodesToRemove.Add(CurrentGraphNode); - } - - } - } - } - - for (UEdGraphNode* RemovedGraphNode : GraphNodesToRemove) + for (UFlowGraphNode* GraphNode : FlowGraphNodes) { - UE_LOG(LogTemp, Display, TEXT("Deleting node %s"), *GetNameSafe(RemovedGraphNode)); - const FGuid NodeGuid = RemovedGraphNode->NodeGuid; - FBlueprintEditorUtils::RemoveNode(nullptr, RemovedGraphNode, true); - // GetFlowAsset()->UnregisterNode(NodeGuid); + GraphNode->RefreshContextPins(true); } - Graph->NotifyGraphChanged(); } void FFlowAssetEditor::GoToParentInstance() From c51bdf0e663aa0786e3e88a6458558b25a289f48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Tue, 15 Nov 2022 22:28:38 +0100 Subject: [PATCH 051/485] cleanup --- Source/Flow/Private/Nodes/FlowNode.cpp | 6 +++--- Source/Flow/Public/Nodes/FlowNode.h | 2 +- Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 995ec1c0c..650a2ab17 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -65,12 +65,12 @@ void UFlowNode::PostLoad() FixNode(nullptr); } -void UFlowNode::FixNode(UEdGraphNode* NewGraph) +void UFlowNode::FixNode(UEdGraphNode* NewGraphNode) { // Fix any node pointers that may be out of date - if (NewGraph) + if (NewGraphNode) { - GraphNode = NewGraph; + GraphNode = NewGraphNode; } // v1.1 upgraded pins to be defined as structs diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 702068bb1..8777b6466 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -74,7 +74,7 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte // -- // Opportunity to update node's data before UFlowGraphNode would call ReconstructNode() - virtual void FixNode(UEdGraphNode* NewGraph); + virtual void FixNode(UEdGraphNode* NewGraphNode); #endif UEdGraphNode* GetGraphNode() const { return GraphNode; } diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 4eb48e408..3c5d81619 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -1085,7 +1085,7 @@ EFlowSignalMode UFlowGraphNode::GetSignalMode() const bool UFlowGraphNode::CanSetSignalMode(const EFlowSignalMode Mode) const { - return FlowNode ? (FlowNode->AllowedSignalModes.Contains(Mode) && FlowNode->SignalMode != Mode) : false; + return FlowNode ? (FlowNode->AllowedSignalModes.Contains(Mode) && FlowNode->SignalMode != Mode) : false; } #undef LOCTEXT_NAMESPACE From 45ff7b4be8b67883a11384b9b18ace7b22c0b0a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Wed, 16 Nov 2022 01:01:51 +0100 Subject: [PATCH 052/485] removed properties deprecated in version 1.1 --- Source/Flow/Private/FlowWorldSettings.cpp | 10 ---------- Source/Flow/Private/Nodes/FlowNode.cpp | 16 ---------------- .../Nodes/World/FlowNode_ComponentObserver.cpp | 10 ---------- .../Private/Nodes/World/FlowNode_NotifyActor.cpp | 16 ---------------- .../Nodes/World/FlowNode_OnNotifyFromActor.cpp | 10 ---------- Source/Flow/Public/FlowWorldSettings.h | 4 ---- Source/Flow/Public/Nodes/FlowNode.h | 7 ------- .../Nodes/World/FlowNode_ComponentObserver.h | 6 ------ .../Public/Nodes/World/FlowNode_NotifyActor.h | 10 ---------- .../Nodes/World/FlowNode_OnNotifyFromActor.h | 6 ------ 10 files changed, 95 deletions(-) diff --git a/Source/Flow/Private/FlowWorldSettings.cpp b/Source/Flow/Private/FlowWorldSettings.cpp index 82526f4b7..447fafdbf 100644 --- a/Source/Flow/Private/FlowWorldSettings.cpp +++ b/Source/Flow/Private/FlowWorldSettings.cpp @@ -14,16 +14,6 @@ AFlowWorldSettings::AFlowWorldSettings(const FObjectInitializer& ObjectInitializ FlowComponent->bAllowMultipleInstances = false; } -void AFlowWorldSettings::PostLoad() -{ - Super::PostLoad(); - - if (FlowAsset_DEPRECATED) - { - FlowComponent->RootFlow = FlowAsset_DEPRECATED; - } -} - void AFlowWorldSettings::PostInitializeComponents() { Super::PostInitializeComponents(); diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 650a2ab17..3bde1928f 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -72,22 +72,6 @@ void UFlowNode::FixNode(UEdGraphNode* NewGraphNode) { GraphNode = NewGraphNode; } - - // v1.1 upgraded pins to be defined as structs - if (InputNames_DEPRECATED.Num() > InputPins.Num()) - { - for (int32 i = InputPins.Num(); i < InputNames_DEPRECATED.Num(); i++) - { - InputPins.Emplace(InputNames_DEPRECATED[i]); - } - } - if (OutputNames_DEPRECATED.Num() > OutputPins.Num()) - { - for (int32 i = OutputPins.Num(); i < OutputNames_DEPRECATED.Num(); i++) - { - OutputPins.Emplace(OutputNames_DEPRECATED[i]); - } - } } void UFlowNode::SetGraphNode(UEdGraphNode* NewGraph) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp b/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp index 0ebde212d..bbe89feb1 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp @@ -18,16 +18,6 @@ UFlowNode_ComponentObserver::UFlowNode_ComponentObserver(const FObjectInitialize OutputPins = {FFlowPin(TEXT("Success")), FFlowPin(TEXT("Completed")), FFlowPin(TEXT("Stopped"))}; } -void UFlowNode_ComponentObserver::PostLoad() -{ - Super::PostLoad(); - - if (IdentityTag_DEPRECATED.IsValid()) - { - IdentityTags = FGameplayTagContainer(IdentityTag_DEPRECATED); - } -} - void UFlowNode_ComponentObserver::ExecuteInput(const FName& PinName) { if (IdentityTags.IsValid()) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp b/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp index f9f9c4819..d67df77e5 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp @@ -15,22 +15,6 @@ UFlowNode_NotifyActor::UFlowNode_NotifyActor(const FObjectInitializer& ObjectIni #endif } -void UFlowNode_NotifyActor::PostLoad() -{ - Super::PostLoad(); - - if (IdentityTag_DEPRECATED.IsValid()) - { - IdentityTags = FGameplayTagContainer(IdentityTag_DEPRECATED); - } - - if (NotifyTag_DEPRECATED.IsValid()) - { - NotifyTags = FGameplayTagContainer(NotifyTag_DEPRECATED); - NotifyTag_DEPRECATED = FGameplayTag(); - } -} - void UFlowNode_NotifyActor::ExecuteInput(const FName& PinName) { if (const UFlowSubsystem* FlowSubsystem = GetWorld()->GetGameInstance()->GetSubsystem()) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp b/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp index 4e64bbec1..ae59ecf3a 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp @@ -13,16 +13,6 @@ UFlowNode_OnNotifyFromActor::UFlowNode_OnNotifyFromActor(const FObjectInitialize #endif } -void UFlowNode_OnNotifyFromActor::PostLoad() -{ - Super::PostLoad(); - - if (NotifyTag_DEPRECATED.IsValid()) - { - NotifyTags = FGameplayTagContainer(NotifyTag_DEPRECATED); - } -} - void UFlowNode_OnNotifyFromActor::ExecuteInput(const FName& PinName) { Super::ExecuteInput(PinName); diff --git a/Source/Flow/Public/FlowWorldSettings.h b/Source/Flow/Public/FlowWorldSettings.h index b6fb3dffd..ba1b1dacc 100644 --- a/Source/Flow/Public/FlowWorldSettings.h +++ b/Source/Flow/Public/FlowWorldSettings.h @@ -22,12 +22,8 @@ class FLOW_API AFlowWorldSettings : public AWorldSettings public: UFlowComponent* GetFlowComponent() const { return FlowComponent; } - virtual void PostLoad() override; virtual void PostInitializeComponents() override; private: bool IsValidInstance() const; - - UPROPERTY() - class UFlowAsset* FlowAsset_DEPRECATED; }; diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 8777b6466..b82dde32a 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -397,11 +397,4 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UFUNCTION(BlueprintNativeEvent, Category = "FlowNode") void OnPassThrough(); - -private: - UPROPERTY() - TArray InputNames_DEPRECATED; - - UPROPERTY() - TArray OutputNames_DEPRECATED; }; diff --git a/Source/Flow/Public/Nodes/World/FlowNode_ComponentObserver.h b/Source/Flow/Public/Nodes/World/FlowNode_ComponentObserver.h index 32a7254a8..724768836 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_ComponentObserver.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_ComponentObserver.h @@ -41,8 +41,6 @@ class FLOW_API UFlowNode_ComponentObserver : public UFlowNode TMap, TWeakObjectPtr> RegisteredActors; protected: - virtual void PostLoad() override; - virtual void ExecuteInput(const FName& PinName) override; virtual void OnLoad_Implementation() override; @@ -74,8 +72,4 @@ class FLOW_API UFlowNode_ComponentObserver : public UFlowNode virtual FString GetNodeDescription() const override; virtual FString GetStatusString() const override; #endif - -private: - UPROPERTY() - FGameplayTag IdentityTag_DEPRECATED; }; diff --git a/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h b/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h index f637cd27e..228cc47d1 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h @@ -25,20 +25,10 @@ class FLOW_API UFlowNode_NotifyActor : public UFlowNode UPROPERTY(EditAnywhere, Category = "Notify") EFlowNetMode NetMode; - virtual void PostLoad() override; - virtual void ExecuteInput(const FName& PinName) override; #if WITH_EDITOR public: virtual FString GetNodeDescription() const override; #endif - -private: - UPROPERTY() - FGameplayTag IdentityTag_DEPRECATED; - - UPROPERTY() - FGameplayTag NotifyTag_DEPRECATED; - }; diff --git a/Source/Flow/Public/Nodes/World/FlowNode_OnNotifyFromActor.h b/Source/Flow/Public/Nodes/World/FlowNode_OnNotifyFromActor.h index 3cab19a33..9c5f77a7d 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_OnNotifyFromActor.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_OnNotifyFromActor.h @@ -22,8 +22,6 @@ class FLOW_API UFlowNode_OnNotifyFromActor : public UFlowNode_ComponentObserver UPROPERTY(EditAnywhere, Category = "Notify") bool bRetroactive; - virtual void PostLoad() override; - virtual void ExecuteInput(const FName& PinName) override; virtual void ObserveActor(TWeakObjectPtr Actor, TWeakObjectPtr Component) override; @@ -35,8 +33,4 @@ class FLOW_API UFlowNode_OnNotifyFromActor : public UFlowNode_ComponentObserver public: virtual FString GetNodeDescription() const override; #endif - -private: - UPROPERTY() - FGameplayTag NotifyTag_DEPRECATED; }; From 17493daf45b9b891230ae1e39f3a8d0ef02ec1d1 Mon Sep 17 00:00:00 2001 From: ryanjon2040 Date: Fri, 18 Nov 2022 18:57:00 +0530 Subject: [PATCH 053/485] Mark asset as dirty after changing signal mode --- Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index 3debd5ab7..6483801cd 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -1265,6 +1265,8 @@ void FFlowAssetEditor::SetSignalMode(const EFlowSignalMode Mode) const { SelectedNode->SetSignalMode(Mode); } + + FlowAsset->Modify(); } bool FFlowAssetEditor::CanSetSignalMode(const EFlowSignalMode Mode) const From 8909902fe19a4fb5110706dd6f8779e811deff8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 20 Nov 2022 16:05:12 +0100 Subject: [PATCH 054/485] Revert "removed properties deprecated in version 1.1" This reverts commit 45ff7b4be8b67883a11384b9b18ace7b22c0b0a4. --- Source/Flow/Private/FlowWorldSettings.cpp | 10 ++++++++++ Source/Flow/Private/Nodes/FlowNode.cpp | 16 ++++++++++++++++ .../Nodes/World/FlowNode_ComponentObserver.cpp | 10 ++++++++++ .../Private/Nodes/World/FlowNode_NotifyActor.cpp | 16 ++++++++++++++++ .../Nodes/World/FlowNode_OnNotifyFromActor.cpp | 10 ++++++++++ Source/Flow/Public/FlowWorldSettings.h | 4 ++++ Source/Flow/Public/Nodes/FlowNode.h | 7 +++++++ .../Nodes/World/FlowNode_ComponentObserver.h | 6 ++++++ .../Public/Nodes/World/FlowNode_NotifyActor.h | 10 ++++++++++ .../Nodes/World/FlowNode_OnNotifyFromActor.h | 6 ++++++ 10 files changed, 95 insertions(+) diff --git a/Source/Flow/Private/FlowWorldSettings.cpp b/Source/Flow/Private/FlowWorldSettings.cpp index 447fafdbf..82526f4b7 100644 --- a/Source/Flow/Private/FlowWorldSettings.cpp +++ b/Source/Flow/Private/FlowWorldSettings.cpp @@ -14,6 +14,16 @@ AFlowWorldSettings::AFlowWorldSettings(const FObjectInitializer& ObjectInitializ FlowComponent->bAllowMultipleInstances = false; } +void AFlowWorldSettings::PostLoad() +{ + Super::PostLoad(); + + if (FlowAsset_DEPRECATED) + { + FlowComponent->RootFlow = FlowAsset_DEPRECATED; + } +} + void AFlowWorldSettings::PostInitializeComponents() { Super::PostInitializeComponents(); diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 3bde1928f..650a2ab17 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -72,6 +72,22 @@ void UFlowNode::FixNode(UEdGraphNode* NewGraphNode) { GraphNode = NewGraphNode; } + + // v1.1 upgraded pins to be defined as structs + if (InputNames_DEPRECATED.Num() > InputPins.Num()) + { + for (int32 i = InputPins.Num(); i < InputNames_DEPRECATED.Num(); i++) + { + InputPins.Emplace(InputNames_DEPRECATED[i]); + } + } + if (OutputNames_DEPRECATED.Num() > OutputPins.Num()) + { + for (int32 i = OutputPins.Num(); i < OutputNames_DEPRECATED.Num(); i++) + { + OutputPins.Emplace(OutputNames_DEPRECATED[i]); + } + } } void UFlowNode::SetGraphNode(UEdGraphNode* NewGraph) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp b/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp index bbe89feb1..0ebde212d 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp @@ -18,6 +18,16 @@ UFlowNode_ComponentObserver::UFlowNode_ComponentObserver(const FObjectInitialize OutputPins = {FFlowPin(TEXT("Success")), FFlowPin(TEXT("Completed")), FFlowPin(TEXT("Stopped"))}; } +void UFlowNode_ComponentObserver::PostLoad() +{ + Super::PostLoad(); + + if (IdentityTag_DEPRECATED.IsValid()) + { + IdentityTags = FGameplayTagContainer(IdentityTag_DEPRECATED); + } +} + void UFlowNode_ComponentObserver::ExecuteInput(const FName& PinName) { if (IdentityTags.IsValid()) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp b/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp index d67df77e5..f9f9c4819 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp @@ -15,6 +15,22 @@ UFlowNode_NotifyActor::UFlowNode_NotifyActor(const FObjectInitializer& ObjectIni #endif } +void UFlowNode_NotifyActor::PostLoad() +{ + Super::PostLoad(); + + if (IdentityTag_DEPRECATED.IsValid()) + { + IdentityTags = FGameplayTagContainer(IdentityTag_DEPRECATED); + } + + if (NotifyTag_DEPRECATED.IsValid()) + { + NotifyTags = FGameplayTagContainer(NotifyTag_DEPRECATED); + NotifyTag_DEPRECATED = FGameplayTag(); + } +} + void UFlowNode_NotifyActor::ExecuteInput(const FName& PinName) { if (const UFlowSubsystem* FlowSubsystem = GetWorld()->GetGameInstance()->GetSubsystem()) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp b/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp index ae59ecf3a..4e64bbec1 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp @@ -13,6 +13,16 @@ UFlowNode_OnNotifyFromActor::UFlowNode_OnNotifyFromActor(const FObjectInitialize #endif } +void UFlowNode_OnNotifyFromActor::PostLoad() +{ + Super::PostLoad(); + + if (NotifyTag_DEPRECATED.IsValid()) + { + NotifyTags = FGameplayTagContainer(NotifyTag_DEPRECATED); + } +} + void UFlowNode_OnNotifyFromActor::ExecuteInput(const FName& PinName) { Super::ExecuteInput(PinName); diff --git a/Source/Flow/Public/FlowWorldSettings.h b/Source/Flow/Public/FlowWorldSettings.h index ba1b1dacc..b6fb3dffd 100644 --- a/Source/Flow/Public/FlowWorldSettings.h +++ b/Source/Flow/Public/FlowWorldSettings.h @@ -22,8 +22,12 @@ class FLOW_API AFlowWorldSettings : public AWorldSettings public: UFlowComponent* GetFlowComponent() const { return FlowComponent; } + virtual void PostLoad() override; virtual void PostInitializeComponents() override; private: bool IsValidInstance() const; + + UPROPERTY() + class UFlowAsset* FlowAsset_DEPRECATED; }; diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index b82dde32a..8777b6466 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -397,4 +397,11 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UFUNCTION(BlueprintNativeEvent, Category = "FlowNode") void OnPassThrough(); + +private: + UPROPERTY() + TArray InputNames_DEPRECATED; + + UPROPERTY() + TArray OutputNames_DEPRECATED; }; diff --git a/Source/Flow/Public/Nodes/World/FlowNode_ComponentObserver.h b/Source/Flow/Public/Nodes/World/FlowNode_ComponentObserver.h index 724768836..32a7254a8 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_ComponentObserver.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_ComponentObserver.h @@ -41,6 +41,8 @@ class FLOW_API UFlowNode_ComponentObserver : public UFlowNode TMap, TWeakObjectPtr> RegisteredActors; protected: + virtual void PostLoad() override; + virtual void ExecuteInput(const FName& PinName) override; virtual void OnLoad_Implementation() override; @@ -72,4 +74,8 @@ class FLOW_API UFlowNode_ComponentObserver : public UFlowNode virtual FString GetNodeDescription() const override; virtual FString GetStatusString() const override; #endif + +private: + UPROPERTY() + FGameplayTag IdentityTag_DEPRECATED; }; diff --git a/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h b/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h index 228cc47d1..f637cd27e 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h @@ -25,10 +25,20 @@ class FLOW_API UFlowNode_NotifyActor : public UFlowNode UPROPERTY(EditAnywhere, Category = "Notify") EFlowNetMode NetMode; + virtual void PostLoad() override; + virtual void ExecuteInput(const FName& PinName) override; #if WITH_EDITOR public: virtual FString GetNodeDescription() const override; #endif + +private: + UPROPERTY() + FGameplayTag IdentityTag_DEPRECATED; + + UPROPERTY() + FGameplayTag NotifyTag_DEPRECATED; + }; diff --git a/Source/Flow/Public/Nodes/World/FlowNode_OnNotifyFromActor.h b/Source/Flow/Public/Nodes/World/FlowNode_OnNotifyFromActor.h index 9c5f77a7d..3cab19a33 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_OnNotifyFromActor.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_OnNotifyFromActor.h @@ -22,6 +22,8 @@ class FLOW_API UFlowNode_OnNotifyFromActor : public UFlowNode_ComponentObserver UPROPERTY(EditAnywhere, Category = "Notify") bool bRetroactive; + virtual void PostLoad() override; + virtual void ExecuteInput(const FName& PinName) override; virtual void ObserveActor(TWeakObjectPtr Actor, TWeakObjectPtr Component) override; @@ -33,4 +35,8 @@ class FLOW_API UFlowNode_OnNotifyFromActor : public UFlowNode_ComponentObserver public: virtual FString GetNodeDescription() const override; #endif + +private: + UPROPERTY() + FGameplayTag NotifyTag_DEPRECATED; }; From 322dab0db94645da2a82a325a6415d3cc77386f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 20 Nov 2022 22:40:50 +0100 Subject: [PATCH 055/485] using method recommend by deprecation warning --- Source/FlowEditor/Private/LevelEditor/SLevelEditorFlow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/FlowEditor/Private/LevelEditor/SLevelEditorFlow.cpp b/Source/FlowEditor/Private/LevelEditor/SLevelEditorFlow.cpp index 8eb7ff7db..54062ffac 100644 --- a/Source/FlowEditor/Private/LevelEditor/SLevelEditorFlow.cpp +++ b/Source/FlowEditor/Private/LevelEditor/SLevelEditorFlow.cpp @@ -56,7 +56,7 @@ FString SLevelEditorFlow::GetFlowAssetPath() const void SLevelEditorFlow::OnFlowChanged(const FAssetData& NewAsset) { - FlowAssetPath = NewAsset.GetSoftObjectPath().ToString(); + FlowAssetPath = NewAsset.GetObjectPathString(); if (UFlowComponent* FlowComponent = FindFlowComponent()) { From 9f39885104d3f8777c42c2c7ecf53da873e8558e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Mon, 21 Nov 2022 21:30:33 +0100 Subject: [PATCH 056/485] Revert "Revert "removed properties deprecated in version 1.1"" This reverts commit 8909902fe19a4fb5110706dd6f8779e811deff8d. --- Source/Flow/Private/FlowWorldSettings.cpp | 10 ---------- Source/Flow/Private/Nodes/FlowNode.cpp | 16 ---------------- .../Nodes/World/FlowNode_ComponentObserver.cpp | 10 ---------- .../Private/Nodes/World/FlowNode_NotifyActor.cpp | 16 ---------------- .../Nodes/World/FlowNode_OnNotifyFromActor.cpp | 10 ---------- Source/Flow/Public/FlowWorldSettings.h | 4 ---- Source/Flow/Public/Nodes/FlowNode.h | 7 ------- .../Nodes/World/FlowNode_ComponentObserver.h | 6 ------ .../Public/Nodes/World/FlowNode_NotifyActor.h | 10 ---------- .../Nodes/World/FlowNode_OnNotifyFromActor.h | 6 ------ 10 files changed, 95 deletions(-) diff --git a/Source/Flow/Private/FlowWorldSettings.cpp b/Source/Flow/Private/FlowWorldSettings.cpp index 82526f4b7..447fafdbf 100644 --- a/Source/Flow/Private/FlowWorldSettings.cpp +++ b/Source/Flow/Private/FlowWorldSettings.cpp @@ -14,16 +14,6 @@ AFlowWorldSettings::AFlowWorldSettings(const FObjectInitializer& ObjectInitializ FlowComponent->bAllowMultipleInstances = false; } -void AFlowWorldSettings::PostLoad() -{ - Super::PostLoad(); - - if (FlowAsset_DEPRECATED) - { - FlowComponent->RootFlow = FlowAsset_DEPRECATED; - } -} - void AFlowWorldSettings::PostInitializeComponents() { Super::PostInitializeComponents(); diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 650a2ab17..3bde1928f 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -72,22 +72,6 @@ void UFlowNode::FixNode(UEdGraphNode* NewGraphNode) { GraphNode = NewGraphNode; } - - // v1.1 upgraded pins to be defined as structs - if (InputNames_DEPRECATED.Num() > InputPins.Num()) - { - for (int32 i = InputPins.Num(); i < InputNames_DEPRECATED.Num(); i++) - { - InputPins.Emplace(InputNames_DEPRECATED[i]); - } - } - if (OutputNames_DEPRECATED.Num() > OutputPins.Num()) - { - for (int32 i = OutputPins.Num(); i < OutputNames_DEPRECATED.Num(); i++) - { - OutputPins.Emplace(OutputNames_DEPRECATED[i]); - } - } } void UFlowNode::SetGraphNode(UEdGraphNode* NewGraph) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp b/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp index 0ebde212d..bbe89feb1 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp @@ -18,16 +18,6 @@ UFlowNode_ComponentObserver::UFlowNode_ComponentObserver(const FObjectInitialize OutputPins = {FFlowPin(TEXT("Success")), FFlowPin(TEXT("Completed")), FFlowPin(TEXT("Stopped"))}; } -void UFlowNode_ComponentObserver::PostLoad() -{ - Super::PostLoad(); - - if (IdentityTag_DEPRECATED.IsValid()) - { - IdentityTags = FGameplayTagContainer(IdentityTag_DEPRECATED); - } -} - void UFlowNode_ComponentObserver::ExecuteInput(const FName& PinName) { if (IdentityTags.IsValid()) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp b/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp index f9f9c4819..d67df77e5 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp @@ -15,22 +15,6 @@ UFlowNode_NotifyActor::UFlowNode_NotifyActor(const FObjectInitializer& ObjectIni #endif } -void UFlowNode_NotifyActor::PostLoad() -{ - Super::PostLoad(); - - if (IdentityTag_DEPRECATED.IsValid()) - { - IdentityTags = FGameplayTagContainer(IdentityTag_DEPRECATED); - } - - if (NotifyTag_DEPRECATED.IsValid()) - { - NotifyTags = FGameplayTagContainer(NotifyTag_DEPRECATED); - NotifyTag_DEPRECATED = FGameplayTag(); - } -} - void UFlowNode_NotifyActor::ExecuteInput(const FName& PinName) { if (const UFlowSubsystem* FlowSubsystem = GetWorld()->GetGameInstance()->GetSubsystem()) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp b/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp index 4e64bbec1..ae59ecf3a 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp @@ -13,16 +13,6 @@ UFlowNode_OnNotifyFromActor::UFlowNode_OnNotifyFromActor(const FObjectInitialize #endif } -void UFlowNode_OnNotifyFromActor::PostLoad() -{ - Super::PostLoad(); - - if (NotifyTag_DEPRECATED.IsValid()) - { - NotifyTags = FGameplayTagContainer(NotifyTag_DEPRECATED); - } -} - void UFlowNode_OnNotifyFromActor::ExecuteInput(const FName& PinName) { Super::ExecuteInput(PinName); diff --git a/Source/Flow/Public/FlowWorldSettings.h b/Source/Flow/Public/FlowWorldSettings.h index b6fb3dffd..ba1b1dacc 100644 --- a/Source/Flow/Public/FlowWorldSettings.h +++ b/Source/Flow/Public/FlowWorldSettings.h @@ -22,12 +22,8 @@ class FLOW_API AFlowWorldSettings : public AWorldSettings public: UFlowComponent* GetFlowComponent() const { return FlowComponent; } - virtual void PostLoad() override; virtual void PostInitializeComponents() override; private: bool IsValidInstance() const; - - UPROPERTY() - class UFlowAsset* FlowAsset_DEPRECATED; }; diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 8777b6466..b82dde32a 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -397,11 +397,4 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UFUNCTION(BlueprintNativeEvent, Category = "FlowNode") void OnPassThrough(); - -private: - UPROPERTY() - TArray InputNames_DEPRECATED; - - UPROPERTY() - TArray OutputNames_DEPRECATED; }; diff --git a/Source/Flow/Public/Nodes/World/FlowNode_ComponentObserver.h b/Source/Flow/Public/Nodes/World/FlowNode_ComponentObserver.h index 32a7254a8..724768836 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_ComponentObserver.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_ComponentObserver.h @@ -41,8 +41,6 @@ class FLOW_API UFlowNode_ComponentObserver : public UFlowNode TMap, TWeakObjectPtr> RegisteredActors; protected: - virtual void PostLoad() override; - virtual void ExecuteInput(const FName& PinName) override; virtual void OnLoad_Implementation() override; @@ -74,8 +72,4 @@ class FLOW_API UFlowNode_ComponentObserver : public UFlowNode virtual FString GetNodeDescription() const override; virtual FString GetStatusString() const override; #endif - -private: - UPROPERTY() - FGameplayTag IdentityTag_DEPRECATED; }; diff --git a/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h b/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h index f637cd27e..228cc47d1 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h @@ -25,20 +25,10 @@ class FLOW_API UFlowNode_NotifyActor : public UFlowNode UPROPERTY(EditAnywhere, Category = "Notify") EFlowNetMode NetMode; - virtual void PostLoad() override; - virtual void ExecuteInput(const FName& PinName) override; #if WITH_EDITOR public: virtual FString GetNodeDescription() const override; #endif - -private: - UPROPERTY() - FGameplayTag IdentityTag_DEPRECATED; - - UPROPERTY() - FGameplayTag NotifyTag_DEPRECATED; - }; diff --git a/Source/Flow/Public/Nodes/World/FlowNode_OnNotifyFromActor.h b/Source/Flow/Public/Nodes/World/FlowNode_OnNotifyFromActor.h index 3cab19a33..9c5f77a7d 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_OnNotifyFromActor.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_OnNotifyFromActor.h @@ -22,8 +22,6 @@ class FLOW_API UFlowNode_OnNotifyFromActor : public UFlowNode_ComponentObserver UPROPERTY(EditAnywhere, Category = "Notify") bool bRetroactive; - virtual void PostLoad() override; - virtual void ExecuteInput(const FName& PinName) override; virtual void ObserveActor(TWeakObjectPtr Actor, TWeakObjectPtr Component) override; @@ -35,8 +33,4 @@ class FLOW_API UFlowNode_OnNotifyFromActor : public UFlowNode_ComponentObserver public: virtual FString GetNodeDescription() const override; #endif - -private: - UPROPERTY() - FGameplayTag NotifyTag_DEPRECATED; }; From fc0ce61d2f064c5904a725bba3ad07aa2d790f1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Mon, 21 Nov 2022 22:38:57 +0100 Subject: [PATCH 057/485] bump to 1.4 --- Flow.uplugin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.uplugin b/Flow.uplugin index 3ee28713c..eaa712ff9 100644 --- a/Flow.uplugin +++ b/Flow.uplugin @@ -1,6 +1,6 @@ { "FileVersion" : 3, - "Version" : 1.3, + "Version" : 1.4, "FriendlyName" : "Flow", "Description" : "Design-agnostic node editor for scripting game’s flow.", "Category" : "Gameplay", From 29d3e550cef30c11a18381e1cb9554001f9794b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 27 Nov 2022 19:56:25 +0100 Subject: [PATCH 058/485] #124 reworked pull request - recreating UFlowGraphNode with a new class on graph load --- Source/FlowEditor/Private/Graph/FlowGraph.cpp | 38 ++++++- .../Private/Graph/FlowGraphSchema.cpp | 67 +++++++------ .../Private/Graph/FlowGraphSchema_Actions.cpp | 99 ++++++++++++++++--- Source/FlowEditor/Public/Graph/FlowGraph.h | 1 + .../FlowEditor/Public/Graph/FlowGraphSchema.h | 7 +- .../Public/Graph/FlowGraphSchema_Actions.h | 1 + 6 files changed, 164 insertions(+), 49 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraph.cpp b/Source/FlowEditor/Private/Graph/FlowGraph.cpp index b776077e9..57fc743d0 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraph.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraph.cpp @@ -2,8 +2,11 @@ #include "Graph/FlowGraph.h" #include "Graph/FlowGraphSchema.h" +#include "Graph/FlowGraphSchema_Actions.h" #include "Graph/Nodes/FlowGraphNode.h" +#include "Nodes/FlowNode.h" + #include "Kismet2/BlueprintEditorUtils.h" void FFlowGraphInterface::OnInputTriggered(UEdGraphNode* GraphNode, const int32 Index) const @@ -27,7 +30,7 @@ UFlowGraph::UFlowGraph(const FObjectInitializer& ObjectInitializer) UEdGraph* UFlowGraph::CreateGraph(UFlowAsset* InFlowAsset) { - UFlowGraph* NewGraph = CastChecked(FBlueprintEditorUtils::CreateNewGraph(InFlowAsset, NAME_None, StaticClass(), UFlowGraphSchema::StaticClass())); + UEdGraph* NewGraph = CastChecked(FBlueprintEditorUtils::CreateNewGraph(InFlowAsset, NAME_None, StaticClass(), UFlowGraphSchema::StaticClass())); NewGraph->bAllowDeletion = false; InFlowAsset->FlowGraph = NewGraph; @@ -36,6 +39,37 @@ UEdGraph* UFlowGraph::CreateGraph(UFlowAsset* InFlowAsset) return NewGraph; } +void UFlowGraph::PostLoad() +{ + Super::PostLoad(); + + // gather AssignedGraphNodeClasses before we'd checking nodes below + const UFlowGraphSchema* FlowGraphSchema = CastChecked(GetSchema()); + FlowGraphSchema->GatherNativeNodes(); + + // Check if all Graph Nodes have expected, up-to-date type + bool bAnyUpdate = false; + for (const TPair& Node : GetFlowAsset()->GetNodes()) + { + if (UFlowNode* FlowNode = Node.Value) + { + const UClass* ExpectGraphNodeClass = UFlowGraphSchema::GetAssignedGraphNodeClass(FlowNode->GetClass()); + if (FlowNode->GetGraphNode() && FlowNode->GetGraphNode()->GetClass() != ExpectGraphNodeClass) + { + // Create a new Flow Graph Node of proper type + FFlowGraphSchemaAction_NewNode::RecreateNode(this, FlowNode->GetGraphNode(), FlowNode); + bAnyUpdate = true; + } + } + } + + if (bAnyUpdate) + { + GetFlowAsset()->HarvestNodeConnections(); + GetOutermost()->SetDirtyFlag(true); // force dirty while loading asset + } +} + void UFlowGraph::NotifyGraphChanged() { GetFlowAsset()->HarvestNodeConnections(); @@ -46,5 +80,5 @@ void UFlowGraph::NotifyGraphChanged() UFlowAsset* UFlowGraph::GetFlowAsset() const { - return CastChecked(GetOuter()); + return GetTypedOuter(); } diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index 0f358c2d9..d1af2ee76 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -37,7 +37,7 @@ UFlowGraphSchema::UFlowGraphSchema(const FObjectInitializer& ObjectInitializer) void UFlowGraphSchema::SubscribeToAssetChanges() { const FAssetRegistryModule& AssetRegistry = FModuleManager::LoadModuleChecked(AssetRegistryConstants::ModuleName); - AssetRegistry.Get().OnFilesLoaded().AddStatic(&UFlowGraphSchema::GatherFlowNodes); + AssetRegistry.Get().OnFilesLoaded().AddStatic(&UFlowGraphSchema::GatherNodes); AssetRegistry.Get().OnAssetAdded().AddStatic(&UFlowGraphSchema::OnAssetAdded); AssetRegistry.Get().OnAssetRemoved().AddStatic(&UFlowGraphSchema::OnAssetRemoved); @@ -198,7 +198,7 @@ TArray> UFlowGraphSchema::GetFlowNodeCategories() { if (NativeFlowNodes.Num() == 0) { - GatherFlowNodes(); + GatherNodes(); } TSet UnsortedCategories; @@ -316,7 +316,7 @@ void UFlowGraphSchema::GetFlowNodeActions(FGraphActionMenuBuilder& ActionMenuBui { if (NativeFlowNodes.Num() == 0) { - GatherFlowNodes(); + GatherNodes(); } // Flow Asset type might limit which nodes are placeable @@ -390,47 +390,59 @@ void UFlowGraphSchema::OnBlueprintCompiled() { if (bBlueprintCompilationPending) { - GatherFlowNodes(); + GatherNodes(); } bBlueprintCompilationPending = false; } -void UFlowGraphSchema::GatherFlowNodes() +void UFlowGraphSchema::OnHotReload(EReloadCompleteReason ReloadCompleteReason) { - // prevent asset crunching during PIE - if (GEditor && GEditor->PlayWorld) + GatherNodes(); +} + +void UFlowGraphSchema::GatherNativeNodes() +{ + // collect C++ nodes once per editor session + if (NativeFlowNodes.Num() > 0) { return; } - // collect C++ nodes once per editor session - if (NativeFlowNodes.Num() == 0) + TArray FlowNodes; + GetDerivedClasses(UFlowNode::StaticClass(), FlowNodes); + for (UClass* Class : FlowNodes) { - TArray FlowNodes; - GetDerivedClasses(UFlowNode::StaticClass(), FlowNodes); - for (UClass* Class : FlowNodes) + if (Class->ClassGeneratedBy == nullptr && IsFlowNodePlaceable(Class)) { - if (Class->ClassGeneratedBy == nullptr && IsFlowNodePlaceable(Class)) - { - NativeFlowNodes.Emplace(Class); - } + NativeFlowNodes.Emplace(Class); } + } - TArray GraphNodes; - GetDerivedClasses(UFlowGraphNode::StaticClass(), GraphNodes); - for (UClass* Class : GraphNodes) + TArray GraphNodes; + GetDerivedClasses(UFlowGraphNode::StaticClass(), GraphNodes); + for (UClass* Class : GraphNodes) + { + const UFlowGraphNode* DefaultObject = Class->GetDefaultObject(); + for (UClass* AssignedClass : DefaultObject->AssignedNodeClasses) { - const UFlowGraphNode* DefaultObject = Class->GetDefaultObject(); - for (UClass* AssignedClass : DefaultObject->AssignedNodeClasses) + if (AssignedClass->IsChildOf(UFlowNode::StaticClass())) { - if (AssignedClass->IsChildOf(UFlowNode::StaticClass())) - { - AssignedGraphNodeClasses.Emplace(AssignedClass, Class); - } + AssignedGraphNodeClasses.Emplace(AssignedClass, Class); } } } +} + +void UFlowGraphSchema::GatherNodes() +{ + // prevent asset crunching during PIE + if (GEditor && GEditor->PlayWorld) + { + return; + } + + GatherNativeNodes(); // retrieve all blueprint nodes const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(AssetRegistryConstants::ModuleName); @@ -450,11 +462,6 @@ void UFlowGraphSchema::GatherFlowNodes() OnNodeListChanged.Broadcast(); } -void UFlowGraphSchema::OnHotReload(EReloadCompleteReason ReloadCompleteReason) -{ - GatherFlowNodes(); -} - void UFlowGraphSchema::OnAssetAdded(const FAssetData& AssetData) { AddAsset(AssetData, false); diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp index e19a6b199..f6fbc8e59 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp @@ -14,7 +14,6 @@ #include "EdGraph/EdGraph.h" #include "EdGraphNode_Comment.h" #include "Editor.h" -#include "Layout/SlateRect.h" #include "ScopedTransaction.h" #define LOCTEXT_NAMESPACE "FlowGraphSchema_Actions" @@ -53,37 +52,107 @@ UFlowGraphNode* FFlowGraphSchemaAction_NewNode::CreateNode(UEdGraph* ParentGraph UFlowAsset* FlowAsset = CastChecked(ParentGraph)->GetFlowAsset(); FlowAsset->Modify(); + // create new Flow Graph node const UClass* GraphNodeClass = UFlowGraphSchema::GetAssignedGraphNodeClass(NodeClass); UFlowGraphNode* NewGraphNode = NewObject(ParentGraph, GraphNodeClass, NAME_None, RF_Transactional); - NewGraphNode->CreateNewGuid(); - NewGraphNode->NodePosX = Location.X; - NewGraphNode->NodePosY = Location.Y; + // register to the graph + NewGraphNode->CreateNewGuid(); ParentGraph->AddNode(NewGraphNode, false, bSelectNewNode); - UFlowNode* NewNode = FlowAsset->CreateNode(NodeClass, NewGraphNode); - NewGraphNode->SetFlowNode(NewNode); + // link editor and runtime nodes together + UFlowNode* FlowNode = FlowAsset->CreateNode(NodeClass, NewGraphNode); + NewGraphNode->SetFlowNode(FlowNode); - NewGraphNode->PostPlacedNewNode(); + // create pins and connections NewGraphNode->AllocateDefaultPins(); - NewGraphNode->AutowireNewNode(FromPin); - + + // set position + NewGraphNode->NodePosX = Location.X; + NewGraphNode->NodePosY = Location.Y; + + // call notifies + NewGraphNode->PostPlacedNewNode(); ParentGraph->NotifyGraphChanged(); - const TSharedPtr FlowEditor = FFlowGraphUtils::GetFlowAssetEditor(ParentGraph); - if (FlowEditor.IsValid()) + // select in editor UI + if (bSelectNewNode) { - FlowEditor->SelectSingleNode(NewGraphNode); + const TSharedPtr FlowEditor = FFlowGraphUtils::GetFlowAssetEditor(ParentGraph); + if (FlowEditor.IsValid()) + { + FlowEditor->SelectSingleNode(NewGraphNode); + } } FlowAsset->PostEditChange(); - FlowAsset->MarkPackageDirty(); return NewGraphNode; } -UEdGraphNode* FFlowGraphSchemaAction_Paste::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode/* = true*/) +UFlowGraphNode* FFlowGraphSchemaAction_NewNode::RecreateNode(UEdGraph* ParentGraph, UEdGraphNode* OldInstance, UFlowNode* FlowNode) +{ + check(FlowNode); + + ParentGraph->Modify(); + + UFlowAsset* FlowAsset = CastChecked(ParentGraph)->GetFlowAsset(); + FlowAsset->Modify(); + + // create new Flow Graph node + const UClass* GraphNodeClass = UFlowGraphSchema::GetAssignedGraphNodeClass(FlowNode->GetClass()); + UFlowGraphNode* NewGraphNode = NewObject(ParentGraph, GraphNodeClass, NAME_None, RF_Transactional); + + // register to the graph + NewGraphNode->NodeGuid = FlowNode->GetGuid(); + ParentGraph->AddNode(NewGraphNode, false, false); + + // link editor and runtime nodes together + FlowNode->SetGraphNode(NewGraphNode); + NewGraphNode->SetFlowNode(FlowNode); + + // move links from the old node + NewGraphNode->AllocateDefaultPins(); + if (OldInstance) + { + for (UEdGraphPin* OldPin : OldInstance->Pins) + { + if (OldPin->LinkedTo.Num() == 0) + { + continue; + } + + for (UEdGraphPin* NewPin : NewGraphNode->Pins) + { + if (NewPin->Direction == OldPin->Direction && NewPin->PinName == OldPin->PinName) + { + TArray Connections = OldPin->LinkedTo; + for (UEdGraphPin* ConnectedPin : Connections) + { + ConnectedPin->BreakLinkTo(OldPin); + ConnectedPin->MakeLinkTo(NewPin); + } + } + } + } + } + + // keep old position + NewGraphNode->NodePosX = OldInstance ? OldInstance->NodePosX : 0; + NewGraphNode->NodePosY = OldInstance ? OldInstance->NodePosY : 0; + + // remove leftover + if (OldInstance) + { + OldInstance->DestroyNode(); + } + + NewGraphNode->PostPlacedNewNode(); + return NewGraphNode; +} + +UEdGraphNode* FFlowGraphSchemaAction_Paste::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, const bool bSelectNewNode/* = true*/) { // prevent adding new nodes while playing if (GEditor->PlayWorld == nullptr) @@ -97,7 +166,7 @@ UEdGraphNode* FFlowGraphSchemaAction_Paste::PerformAction(class UEdGraph* Parent ///////////////////////////////////////////////////// // Comment Node -UEdGraphNode* FFlowGraphSchemaAction_NewComment::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode/* = true*/) +UEdGraphNode* FFlowGraphSchemaAction_NewComment::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, const bool bSelectNewNode/* = true*/) { // prevent adding new nodes while playing if (GEditor->PlayWorld != nullptr) diff --git a/Source/FlowEditor/Public/Graph/FlowGraph.h b/Source/FlowEditor/Public/Graph/FlowGraph.h index 134562e19..1e92342ad 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraph.h +++ b/Source/FlowEditor/Public/Graph/FlowGraph.h @@ -24,6 +24,7 @@ class FLOWEDITOR_API UFlowGraph : public UEdGraph static UEdGraph* CreateGraph(UFlowAsset* InFlowAsset); // UEdGraph + virtual void PostLoad() override; virtual void NotifyGraphChanged() override; // -- diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h index 592309bc1..842c27788 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h @@ -15,6 +15,8 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema { GENERATED_UCLASS_BODY() + friend class UFlowGraph; + private: static TArray NativeFlowNodes; static TMap BlueprintFlowNodes; @@ -52,10 +54,11 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema static void OnBlueprintPreCompile(UBlueprint* Blueprint); static void OnBlueprintCompiled(); - - static void GatherFlowNodes(); static void OnHotReload(EReloadCompleteReason ReloadCompleteReason); + static void GatherNativeNodes(); + static void GatherNodes(); + static void OnAssetAdded(const FAssetData& AssetData); static void AddAsset(const FAssetData& AssetData, const bool bBatch); static void OnAssetRemoved(const FAssetData& AssetData); diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h index 520f6176a..528934593 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h @@ -48,6 +48,7 @@ struct FLOWEDITOR_API FFlowGraphSchemaAction_NewNode : public FEdGraphSchemaActi // -- static UFlowGraphNode* CreateNode(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const UClass* NodeClass, const FVector2D Location, const bool bSelectNewNode = true); + static UFlowGraphNode* RecreateNode(UEdGraph* ParentGraph, UEdGraphNode* OldInstance, UFlowNode* FlowNode); }; /** Action to paste clipboard contents into the graph */ From c5047179d9572b21275dbb57121d6f277cee5ad7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 27 Nov 2022 19:58:35 +0100 Subject: [PATCH 059/485] replaced redundant call to blueprint editor utils --- Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index 6483801cd..af30b8bd6 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -5,7 +5,6 @@ #include "Asset/FlowAssetToolbar.h" #include "Asset/FlowDebugger.h" #include "FlowEditorCommands.h" -#include "Graph/FlowGraph.h" #include "Graph/FlowGraphEditorSettings.h" #include "Graph/FlowGraphSchema.h" #include "Graph/FlowGraphSchema_Actions.h" @@ -19,13 +18,11 @@ #include "EdGraphUtilities.h" #include "EdGraph/EdGraphNode.h" #include "Editor.h" -#include "EditorStyleSet.h" #include "Framework/Commands/GenericCommands.h" #include "GraphEditor.h" #include "GraphEditorActions.h" #include "HAL/PlatformApplicationMisc.h" #include "IDetailsView.h" -#include "Kismet2/BlueprintEditorUtils.h" #include "Kismet2/DebuggerCommands.h" #include "LevelEditor.h" #include "Modules/ModuleManager.h" @@ -678,7 +675,6 @@ void FFlowAssetEditor::DeleteSelectedNodes() for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt) { UEdGraphNode* Node = CastChecked(*NodeIt); - if (Node->CanUserDeleteNode()) { if (const UFlowGraphNode* FlowGraphNode = Cast(Node)) @@ -686,13 +682,17 @@ void FFlowAssetEditor::DeleteSelectedNodes() if (FlowGraphNode->GetFlowNode()) { const FGuid NodeGuid = FlowGraphNode->GetFlowNode()->GetGuid(); - FBlueprintEditorUtils::RemoveNode(nullptr, Node, true); + + FocusedGraphEditor->GetCurrentGraph()->GetSchema()->BreakNodeLinks(*Node); + Node->DestroyNode(); + FlowAsset->UnregisterNode(NodeGuid); continue; } } - FBlueprintEditorUtils::RemoveNode(nullptr, Node, true); + FocusedGraphEditor->GetCurrentGraph()->GetSchema()->BreakNodeLinks(*Node); + Node->DestroyNode(); } } } From 1540f14b4f6198730ee6b5adb002ec42ea22a859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 27 Nov 2022 20:34:10 +0100 Subject: [PATCH 060/485] replaced naive counter check with bespoke flag --- Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp | 10 ++++++---- Source/FlowEditor/Public/Graph/FlowGraphSchema.h | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index d1af2ee76..3ee4e8e17 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -21,6 +21,7 @@ #define LOCTEXT_NAMESPACE "FlowGraphSchema" +bool UFlowGraphSchema::bInitialGatherPerformed = false; TArray UFlowGraphSchema::NativeFlowNodes; TMap UFlowGraphSchema::BlueprintFlowNodes; TMap UFlowGraphSchema::AssignedGraphNodeClasses; @@ -196,7 +197,7 @@ void UFlowGraphSchema::OnPinConnectionDoubleCicked(UEdGraphPin* PinA, UEdGraphPi TArray> UFlowGraphSchema::GetFlowNodeCategories() { - if (NativeFlowNodes.Num() == 0) + if (!bInitialGatherPerformed) { GatherNodes(); } @@ -314,7 +315,7 @@ void UFlowGraphSchema::ApplyNodeFilter(const UFlowAsset* AssetClassDefaults, con void UFlowGraphSchema::GetFlowNodeActions(FGraphActionMenuBuilder& ActionMenuBuilder, const UFlowAsset* AssetClassDefaults, const FString& CategoryName) { - if (NativeFlowNodes.Num() == 0) + if (!bInitialGatherPerformed) { GatherNodes(); } @@ -442,17 +443,18 @@ void UFlowGraphSchema::GatherNodes() return; } + bInitialGatherPerformed = true; + GatherNativeNodes(); // retrieve all blueprint nodes - const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(AssetRegistryConstants::ModuleName); - FARFilter Filter; Filter.ClassPaths.Add(UBlueprint::StaticClass()->GetClassPathName()); Filter.ClassPaths.Add(UBlueprintGeneratedClass::StaticClass()->GetClassPathName()); Filter.bRecursiveClasses = true; TArray FoundAssets; + const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(AssetRegistryConstants::ModuleName); AssetRegistryModule.Get().GetAssets(Filter, FoundAssets); for (const FAssetData& AssetData : FoundAssets) { diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h index 842c27788..6c97a6c64 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h @@ -18,6 +18,7 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema friend class UFlowGraph; private: + static bool bInitialGatherPerformed; static TArray NativeFlowNodes; static TMap BlueprintFlowNodes; static TMap AssignedGraphNodeClasses; From 9adebb8f85c6d66957781b6918dd3a7bc32706f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 27 Nov 2022 20:34:22 +0100 Subject: [PATCH 061/485] redundant include removed --- Source/FlowEditor/Private/Graph/Widgets/SFlowPalette.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowPalette.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowPalette.cpp index 009104dc1..d2b27637a 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowPalette.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowPalette.cpp @@ -9,7 +9,6 @@ #include "FlowAsset.h" #include "Nodes/FlowNode.h" -#include "EditorStyleSet.h" #include "Fonts/SlateFontInfo.h" #include "Styling/CoreStyle.h" #include "Styling/SlateBrush.h" From 9b77da7f52dd44c328cc9951988acf4b4492bfb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 27 Nov 2022 20:49:19 +0100 Subject: [PATCH 062/485] narrowed down Flow Node blueprint search filter --- Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index 3ee4e8e17..a0419810a 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -449,8 +449,7 @@ void UFlowGraphSchema::GatherNodes() // retrieve all blueprint nodes FARFilter Filter; - Filter.ClassPaths.Add(UBlueprint::StaticClass()->GetClassPathName()); - Filter.ClassPaths.Add(UBlueprintGeneratedClass::StaticClass()->GetClassPathName()); + Filter.ClassPaths.Add(UFlowNodeBlueprint::StaticClass()->GetClassPathName()); Filter.bRecursiveClasses = true; TArray FoundAssets; From 32ff5b56d7476106b0c7c595d5d25b26b686f104 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 27 Nov 2022 22:05:28 +0100 Subject: [PATCH 063/485] rollback to recreating Graph Nodes manually as PostLoad has some issues --- .../Private/Asset/FlowAssetEditor.cpp | 8 ++- Source/FlowEditor/Private/Graph/FlowGraph.cpp | 41 +++++++------- .../Private/Graph/FlowGraphSchema_Actions.cpp | 53 ++++++++++--------- Source/FlowEditor/Public/Graph/FlowGraph.h | 2 +- 4 files changed, 52 insertions(+), 52 deletions(-) diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index af30b8bd6..3a489fe81 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -5,6 +5,7 @@ #include "Asset/FlowAssetToolbar.h" #include "Asset/FlowDebugger.h" #include "FlowEditorCommands.h" +#include "Graph/FlowGraph.h" #include "Graph/FlowGraphEditorSettings.h" #include "Graph/FlowGraphSchema.h" #include "Graph/FlowGraphSchema_Actions.h" @@ -256,12 +257,9 @@ void FFlowAssetEditor::BindToolbarCommands() void FFlowAssetEditor::RefreshAsset() { - TArray FlowGraphNodes; - FlowAsset->GetGraph()->GetNodesOfClass(FlowGraphNodes); - - for (UFlowGraphNode* GraphNode : FlowGraphNodes) + if (UFlowGraph* FlowGraph = Cast(FlowAsset->GetGraph())) { - GraphNode->RefreshContextPins(true); + FlowGraph->RefreshGraph(); } } diff --git a/Source/FlowEditor/Private/Graph/FlowGraph.cpp b/Source/FlowEditor/Private/Graph/FlowGraph.cpp index 57fc743d0..096d48bf6 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraph.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraph.cpp @@ -39,34 +39,33 @@ UEdGraph* UFlowGraph::CreateGraph(UFlowAsset* InFlowAsset) return NewGraph; } -void UFlowGraph::PostLoad() +void UFlowGraph::RefreshGraph() { - Super::PostLoad(); - - // gather AssignedGraphNodeClasses before we'd checking nodes below - const UFlowGraphSchema* FlowGraphSchema = CastChecked(GetSchema()); - FlowGraphSchema->GatherNativeNodes(); - - // Check if all Graph Nodes have expected, up-to-date type - bool bAnyUpdate = false; - for (const TPair& Node : GetFlowAsset()->GetNodes()) + // don't run fixup in commandlets or PIE + if (!IsRunningCommandlet() && GEditor && !GEditor->PlayWorld) { - if (UFlowNode* FlowNode = Node.Value) + // check if all Graph Nodes have expected, up-to-date type + CastChecked(GetSchema())->GatherNativeNodes(); + for (const TPair& Node : GetFlowAsset()->GetNodes()) { - const UClass* ExpectGraphNodeClass = UFlowGraphSchema::GetAssignedGraphNodeClass(FlowNode->GetClass()); - if (FlowNode->GetGraphNode() && FlowNode->GetGraphNode()->GetClass() != ExpectGraphNodeClass) + if (UFlowNode* FlowNode = Node.Value) { - // Create a new Flow Graph Node of proper type - FFlowGraphSchemaAction_NewNode::RecreateNode(this, FlowNode->GetGraphNode(), FlowNode); - bAnyUpdate = true; + const UClass* ExpectGraphNodeClass = UFlowGraphSchema::GetAssignedGraphNodeClass(FlowNode->GetClass()); + if (FlowNode->GetGraphNode() && FlowNode->GetGraphNode()->GetClass() != ExpectGraphNodeClass) + { + // Create a new Flow Graph Node of proper type + FFlowGraphSchemaAction_NewNode::RecreateNode(this, FlowNode->GetGraphNode(), FlowNode); + } } } - } - if (bAnyUpdate) - { - GetFlowAsset()->HarvestNodeConnections(); - GetOutermost()->SetDirtyFlag(true); // force dirty while loading asset + // update context pins + TArray FlowGraphNodes; + GetNodesOfClass(FlowGraphNodes); + for (UFlowGraphNode* GraphNode : FlowGraphNodes) + { + GraphNode->RefreshContextPins(true); + } } } diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp index f6fbc8e59..06dfad3b7 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp @@ -76,7 +76,9 @@ UFlowGraphNode* FFlowGraphSchemaAction_NewNode::CreateNode(UEdGraph* ParentGraph NewGraphNode->PostPlacedNewNode(); ParentGraph->NotifyGraphChanged(); - // select in editor UI + FlowAsset->PostEditChange(); + + // select in editor UI if (bSelectNewNode) { const TSharedPtr FlowEditor = FFlowGraphUtils::GetFlowAssetEditor(ParentGraph); @@ -86,8 +88,6 @@ UFlowGraphNode* FFlowGraphSchemaAction_NewNode::CreateNode(UEdGraph* ParentGraph } } - FlowAsset->PostEditChange(); - return NewGraphNode; } @@ -103,7 +103,7 @@ UFlowGraphNode* FFlowGraphSchemaAction_NewNode::RecreateNode(UEdGraph* ParentGra // create new Flow Graph node const UClass* GraphNodeClass = UFlowGraphSchema::GetAssignedGraphNodeClass(FlowNode->GetClass()); UFlowGraphNode* NewGraphNode = NewObject(ParentGraph, GraphNodeClass, NAME_None, RF_Transactional); - + // register to the graph NewGraphNode->NodeGuid = FlowNode->GetGuid(); ParentGraph->AddNode(NewGraphNode, false, false); @@ -116,26 +116,26 @@ UFlowGraphNode* FFlowGraphSchemaAction_NewNode::RecreateNode(UEdGraph* ParentGra NewGraphNode->AllocateDefaultPins(); if (OldInstance) { - for (UEdGraphPin* OldPin : OldInstance->Pins) - { - if (OldPin->LinkedTo.Num() == 0) - { - continue; - } - - for (UEdGraphPin* NewPin : NewGraphNode->Pins) - { - if (NewPin->Direction == OldPin->Direction && NewPin->PinName == OldPin->PinName) - { - TArray Connections = OldPin->LinkedTo; - for (UEdGraphPin* ConnectedPin : Connections) - { - ConnectedPin->BreakLinkTo(OldPin); - ConnectedPin->MakeLinkTo(NewPin); - } - } - } - } + for (UEdGraphPin* OldPin : OldInstance->Pins) + { + if (OldPin->LinkedTo.Num() == 0) + { + continue; + } + + for (UEdGraphPin* NewPin : NewGraphNode->Pins) + { + if (NewPin->Direction == OldPin->Direction && NewPin->PinName == OldPin->PinName) + { + TArray Connections = OldPin->LinkedTo; + for (UEdGraphPin* ConnectedPin : Connections) + { + ConnectedPin->BreakLinkTo(OldPin); + ConnectedPin->MakeLinkTo(NewPin); + } + } + } + } } // keep old position @@ -147,8 +147,11 @@ UFlowGraphNode* FFlowGraphSchemaAction_NewNode::RecreateNode(UEdGraph* ParentGra { OldInstance->DestroyNode(); } - + + // call notifies NewGraphNode->PostPlacedNewNode(); + ParentGraph->NotifyGraphChanged(); + return NewGraphNode; } diff --git a/Source/FlowEditor/Public/Graph/FlowGraph.h b/Source/FlowEditor/Public/Graph/FlowGraph.h index 1e92342ad..9759dc676 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraph.h +++ b/Source/FlowEditor/Public/Graph/FlowGraph.h @@ -22,9 +22,9 @@ class FLOWEDITOR_API UFlowGraph : public UEdGraph GENERATED_UCLASS_BODY() static UEdGraph* CreateGraph(UFlowAsset* InFlowAsset); + void RefreshGraph(); // UEdGraph - virtual void PostLoad() override; virtual void NotifyGraphChanged() override; // -- From 815bf52a777c25bbacb499abd8b694bf96f952a8 Mon Sep 17 00:00:00 2001 From: michalmocarskiintermarum Date: Wed, 30 Nov 2022 20:15:11 +0100 Subject: [PATCH 064/485] Plumb PlaybackSettings in a way which is not preferred in UE 5.1 In UE 5.1 PlaybackSettings should be plumb through explicitly. --- .../Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp | 6 ++++++ .../Flow/Private/LevelSequence/FlowLevelSequencePlayer.cpp | 2 +- Source/Flow/Public/LevelSequence/FlowLevelSequenceActor.h | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp b/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp index ae97484e4..7af3e47e2 100644 --- a/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp +++ b/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp @@ -17,6 +17,12 @@ void AFlowLevelSequenceActor::GetLifetimeReplicatedProps(TArraySetPlaybackSettings(PlaybackSettings); +} + void AFlowLevelSequenceActor::SetReplicatedLevelSequenceAsset(ULevelSequence* Asset) { if (HasAuthority()) diff --git a/Source/Flow/Private/LevelSequence/FlowLevelSequencePlayer.cpp b/Source/Flow/Private/LevelSequence/FlowLevelSequencePlayer.cpp index 29f51f385..bc2223ad8 100644 --- a/Source/Flow/Private/LevelSequence/FlowLevelSequencePlayer.cpp +++ b/Source/Flow/Private/LevelSequence/FlowLevelSequencePlayer.cpp @@ -51,7 +51,7 @@ UFlowLevelSequencePlayer* UFlowLevelSequencePlayer::CreateFlowLevelSequencePlaye // We use deferred spawn, so we can set all actor properties prior to its initialization. // This also helpful in case of multiplayer, since all actor settings are replicated with the spawned actor. No need to call replication just after spawn. AFlowLevelSequenceActor* Actor = World->SpawnActorDeferred(AFlowLevelSequenceActor::StaticClass(), SpawnTransform, nullptr, nullptr, ESpawnActorCollisionHandlingMethod::AlwaysSpawn); - Actor->PlaybackSettings = Settings; + Actor->SetPlaybackSettings(Settings); Actor->CameraSettings = CameraSettings; // apply Transform Origin to spawned actor diff --git a/Source/Flow/Public/LevelSequence/FlowLevelSequenceActor.h b/Source/Flow/Public/LevelSequence/FlowLevelSequenceActor.h index 8242cae05..9a74c4081 100644 --- a/Source/Flow/Public/LevelSequence/FlowLevelSequenceActor.h +++ b/Source/Flow/Public/LevelSequence/FlowLevelSequenceActor.h @@ -20,6 +20,7 @@ class FLOW_API AFlowLevelSequenceActor : public ALevelSequenceActor virtual void GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const override; public: + void SetPlaybackSettings(FMovieSceneSequencePlaybackSettings NewPlaybackSettings); void SetReplicatedLevelSequenceAsset(ULevelSequence* Asset); protected: From 6e85d7ff5609fabeee1901d6f5207ce02ef24415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Wed, 30 Nov 2022 21:27:15 +0100 Subject: [PATCH 065/485] compile out body of LogError in Shipping --- Source/Flow/Private/Nodes/FlowNode.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 3bde1928f..13c4fcf6f 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -645,6 +645,7 @@ FString UFlowNode::GetProgressAsString(float Value) void UFlowNode::LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType) const { +#if !UE_BUILD_SHIPPING const FString TemplatePath = GetFlowAsset()->TemplateAsset->GetPathName(); Message += TEXT(" --- node ") + GetName() + TEXT(", asset ") + FPaths::GetPath(TemplatePath) / FPaths::GetBaseFilename(TemplatePath); @@ -669,6 +670,7 @@ void UFlowNode::LogError(FString Message, const EFlowOnScreenMessageType OnScree } UE_LOG(LogFlow, Error, TEXT("%s"), *Message); +#endif } void UFlowNode::SaveInstance(FFlowNodeSaveData& NodeRecord) From 679a01da58885d4034f430a39ef275cd4cbae510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Thu, 1 Dec 2022 18:53:47 +0100 Subject: [PATCH 066/485] added LogMessage + added messages on executing Disabled and PassThrough nodes --- Source/Flow/Private/Nodes/FlowNode.cpp | 32 ++++++++++++++++++++------ Source/Flow/Public/Nodes/FlowNode.h | 5 +++- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 13c4fcf6f..d2aefbaa6 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -408,13 +408,19 @@ void UFlowNode::TriggerInput(const FName& PinName, const EFlowPinActivationType return; } - if (SignalMode == EFlowSignalMode::Enabled) - { - ExecuteInput(PinName); - } - else if (SignalMode == EFlowSignalMode::PassThrough) + switch (SignalMode) { - OnPassThrough(); + case EFlowSignalMode::Enabled: + ExecuteInput(PinName); + break; + case EFlowSignalMode::Disabled: + LogMessage(FString::Printf(TEXT("Node disabled while triggering input %s"), *PinName.ToString())); + break; + case EFlowSignalMode::PassThrough: + LogMessage(FString::Printf(TEXT("Signal pass-through on triggering input %s"), *PinName.ToString())); + OnPassThrough(); + break; + default: ; } } @@ -670,7 +676,17 @@ void UFlowNode::LogError(FString Message, const EFlowOnScreenMessageType OnScree } UE_LOG(LogFlow, Error, TEXT("%s"), *Message); -#endif +#endif +} + +void UFlowNode::LogMessage(FString Message) const +{ +#if !UE_BUILD_SHIPPING + const FString TemplatePath = GetFlowAsset()->TemplateAsset->GetPathName(); + Message += TEXT(" --- node ") + GetName() + TEXT(", asset ") + FPaths::GetPath(TemplatePath) / FPaths::GetBaseFilename(TemplatePath); + + UE_LOG(LogFlow, Log, TEXT("%s"), *Message); +#endif } void UFlowNode::SaveInstance(FFlowNodeSaveData& NodeRecord) @@ -701,9 +717,11 @@ void UFlowNode::LoadInstance(const FFlowNodeSaveData& NodeRecord) break; case EFlowSignalMode::Disabled: // designer doesn't want to execute this node's logic at all, so we kill it + LogMessage(TEXT("Signal disabled while loading Flow Node from SaveGame")); Finish(); break; case EFlowSignalMode::PassThrough: + LogMessage(TEXT("Signal pass-through on loading Flow Node from SaveGame")); OnPassThrough(); break; default: ; diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index b82dde32a..d905cb0aa 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -379,9 +379,12 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UFUNCTION(BlueprintPure, Category = "FlowNode") static FString GetProgressAsString(float Value); - UFUNCTION(BlueprintCallable, Category = "FlowNode") + UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) void LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType = EFlowOnScreenMessageType::Permanent) const; + UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) + void LogMessage(FString Message) const; + UFUNCTION(BlueprintCallable, Category = "FlowNode") void SaveInstance(FFlowNodeSaveData& NodeRecord); From 182cd20542e572bc49d222e1f2a7a99cacf129eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 4 Dec 2022 16:15:15 +0100 Subject: [PATCH 067/485] Jump from Asset Search result to the Flow Node, if found property belongs to the node requires integrating engine change: https://github.com/EpicGames/UnrealEngine/pull/9882 --- Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp | 10 ++++++++++ Source/FlowEditor/Public/Asset/FlowAssetEditor.h | 7 +++++++ Source/FlowEditor/Public/FlowEditorDefines.h | 9 +++++++++ 3 files changed, 26 insertions(+) create mode 100644 Source/FlowEditor/Public/FlowEditorDefines.h diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index 3a489fe81..b2c82f50c 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -651,6 +651,16 @@ void FFlowAssetEditor::SelectSingleNode(UEdGraphNode* Node) const FocusedGraphEditor->SetNodeSelection(Node, true); } +#if ENABLE_JUMP_TO_INNER_OBJECT +void FFlowAssetEditor::JumpToInnerObject(UObject* InnerObject) +{ + if (const UFlowNode* FlowNode = Cast(InnerObject)) + { + FocusedGraphEditor->JumpToNode(FlowNode->GetGraphNode(), false); + } +} +#endif + void FFlowAssetEditor::SelectAllNodes() const { FocusedGraphEditor->SelectAllNodes(); diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index a72f0f59e..2e811e975 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -9,6 +9,7 @@ #include "Toolkits/IToolkitHost.h" #include "UObject/GCObject.h" +#include "FlowEditorDefines.h" #include "FlowTypes.h" class SFlowPalette; @@ -138,6 +139,12 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit public: virtual void SelectSingleNode(UEdGraphNode* Node) const; +#if ENABLE_JUMP_TO_INNER_OBJECT + // FAssetEditorToolkit + virtual void JumpToInnerObject(UObject* InnerObject) override; + // -- +#endif + protected: virtual void SelectAllNodes() const; virtual bool CanSelectAllNodes() const; diff --git a/Source/FlowEditor/Public/FlowEditorDefines.h b/Source/FlowEditor/Public/FlowEditorDefines.h new file mode 100644 index 000000000..17aee3eb2 --- /dev/null +++ b/Source/FlowEditor/Public/FlowEditorDefines.h @@ -0,0 +1,9 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +/** + * Documentation: https://github.com/MothCocoon/FlowGraph/wiki/Asset-Search + * Set macro value to 1, if you made these changes to the engine: https://github.com/EpicGames/UnrealEngine/pull/9882 + */ +#define ENABLE_JUMP_TO_INNER_OBJECT 1 From e45521b2a8c4f3c9c16f6fad69990c5068cf4a74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 4 Dec 2022 16:19:04 +0100 Subject: [PATCH 068/485] ENABLE_JUMP_TO_INNER_OBJECT needs to be disabled in vanilla engine --- Source/FlowEditor/Public/FlowEditorDefines.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/FlowEditor/Public/FlowEditorDefines.h b/Source/FlowEditor/Public/FlowEditorDefines.h index 17aee3eb2..fb1b09357 100644 --- a/Source/FlowEditor/Public/FlowEditorDefines.h +++ b/Source/FlowEditor/Public/FlowEditorDefines.h @@ -6,4 +6,4 @@ * Documentation: https://github.com/MothCocoon/FlowGraph/wiki/Asset-Search * Set macro value to 1, if you made these changes to the engine: https://github.com/EpicGames/UnrealEngine/pull/9882 */ -#define ENABLE_JUMP_TO_INNER_OBJECT 1 +#define ENABLE_JUMP_TO_INNER_OBJECT 0 From 423f2931b1f65b59ad5a22f2d5857fcadbbdd72a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 4 Dec 2022 23:12:14 +0100 Subject: [PATCH 069/485] don't bind to delegates if node got deactivated during first loop on actors --- .../Nodes/World/FlowNode_ComponentObserver.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp b/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp index bbe89feb1..3a875af3d 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp @@ -68,13 +68,13 @@ void UFlowNode_ComponentObserver::StartObserving() } } - // clear old bindings before binding again, which might happen while loading a SaveGame - StopObserving(); - - FlowSubsystem->OnComponentRegistered.AddDynamic(this, &UFlowNode_ComponentObserver::OnComponentRegistered); - FlowSubsystem->OnComponentTagAdded.AddDynamic(this, &UFlowNode_ComponentObserver::OnComponentTagAdded); - FlowSubsystem->OnComponentTagRemoved.AddDynamic(this, &UFlowNode_ComponentObserver::OnComponentTagRemoved); - FlowSubsystem->OnComponentUnregistered.AddDynamic(this, &UFlowNode_ComponentObserver::OnComponentUnregistered); + if (GetActivationState() == EFlowNodeState::Active) + { + FlowSubsystem->OnComponentRegistered.AddUniqueDynamic(this, &UFlowNode_ComponentObserver::OnComponentRegistered); + FlowSubsystem->OnComponentTagAdded.AddUniqueDynamic(this, &UFlowNode_ComponentObserver::OnComponentTagAdded); + FlowSubsystem->OnComponentTagRemoved.AddUniqueDynamic(this, &UFlowNode_ComponentObserver::OnComponentTagRemoved); + FlowSubsystem->OnComponentUnregistered.AddUniqueDynamic(this, &UFlowNode_ComponentObserver::OnComponentUnregistered); + } } } From 9cd318d6672dc3ea6dbd663db155e238f0674ea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Tue, 6 Dec 2022 21:42:59 +0100 Subject: [PATCH 070/485] fix by @lfowles Component Observer may continue triggering outputs if the last component triggered a finish during UFlowNode_ComponentObserver::StartObserving --- .../World/FlowNode_ComponentObserver.cpp | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp b/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp index 3a875af3d..896c9530d 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp @@ -56,25 +56,20 @@ void UFlowNode_ComponentObserver::StartObserving() // collect already registered components for (const TWeakObjectPtr& FoundComponent : FlowSubsystem->GetComponents(IdentityTags, ContainerMatchType, bExactMatch)) { - if (GetActivationState() == EFlowNodeState::Active) + ObserveActor(FoundComponent->GetOwner(), FoundComponent); + + // node might finish work immediately as the effect of ObserveActor() + // we should terminate iteration in this case + if (GetActivationState() != EFlowNodeState::Active) { - ObserveActor(FoundComponent->GetOwner(), FoundComponent); - } - else - { - // node might finish work as the effect of triggering event on the found actor - // we should terminate iteration in this case return; } } - - if (GetActivationState() == EFlowNodeState::Active) - { - FlowSubsystem->OnComponentRegistered.AddUniqueDynamic(this, &UFlowNode_ComponentObserver::OnComponentRegistered); - FlowSubsystem->OnComponentTagAdded.AddUniqueDynamic(this, &UFlowNode_ComponentObserver::OnComponentTagAdded); - FlowSubsystem->OnComponentTagRemoved.AddUniqueDynamic(this, &UFlowNode_ComponentObserver::OnComponentTagRemoved); - FlowSubsystem->OnComponentUnregistered.AddUniqueDynamic(this, &UFlowNode_ComponentObserver::OnComponentUnregistered); - } + + FlowSubsystem->OnComponentRegistered.AddUniqueDynamic(this, &UFlowNode_ComponentObserver::OnComponentRegistered); + FlowSubsystem->OnComponentTagAdded.AddUniqueDynamic(this, &UFlowNode_ComponentObserver::OnComponentTagAdded); + FlowSubsystem->OnComponentTagRemoved.AddUniqueDynamic(this, &UFlowNode_ComponentObserver::OnComponentTagRemoved); + FlowSubsystem->OnComponentUnregistered.AddUniqueDynamic(this, &UFlowNode_ComponentObserver::OnComponentUnregistered); } } From db7638b2177052625db877cb78bc69337e075283 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Thu, 8 Dec 2022 21:52:45 +0100 Subject: [PATCH 071/485] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 97056f896..2f4856bdf 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ The aim of publishing it as open-source project is to let people tell great stor * It's easy to include plugin in your own project, follow this short [Getting Started](https://github.com/MothCocoon/FlowGraph/wiki/Getting-Started) guide. ## In-depth video presentation -This 24-minute presentation breaks down the concept of the Flow Graph. It goes through everything written in this ReadMe but in greater detail. +This 24-minute presentation breaks down the concept of the Flow Graph. Trust me, you want to understand concept properly before diving into implementation. [![Introducing Flow Graph for Unreal Engine](https://img.youtube.com/vi/BAqhccgKx_k/0.jpg)](https://www.youtube.com/watch?v=BAqhccgKx_k) From be767104df3bf1094b7c3bc4c1bf0de33657555a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Wed, 21 Dec 2022 14:33:03 +0100 Subject: [PATCH 072/485] removed obsolete EditorStyleSet.h includes --- Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp | 1 - Source/FlowEditor/Private/FlowEditorCommands.cpp | 2 -- Source/FlowEditor/Private/Nodes/FlowNodeBlueprintFactory.cpp | 1 - 3 files changed, 4 deletions(-) diff --git a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp index 8dc092482..f6ad02c32 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp @@ -8,7 +8,6 @@ #include "FlowAsset.h" -#include "EditorStyleSet.h" #include "Kismet2/DebuggerCommands.h" #include "Misc/Attribute.h" #include "Subsystems/AssetEditorSubsystem.h" diff --git a/Source/FlowEditor/Private/FlowEditorCommands.cpp b/Source/FlowEditor/Private/FlowEditorCommands.cpp index 32478708c..75643e609 100644 --- a/Source/FlowEditor/Private/FlowEditorCommands.cpp +++ b/Source/FlowEditor/Private/FlowEditorCommands.cpp @@ -1,13 +1,11 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "FlowEditorCommands.h" - #include "FlowEditorStyle.h" #include "Graph/FlowGraphSchema_Actions.h" #include "Nodes/FlowNode.h" -#include "EditorStyleSet.h" #include "Misc/ConfigCacheIni.h" #define LOCTEXT_NAMESPACE "FlowGraphCommands" diff --git a/Source/FlowEditor/Private/Nodes/FlowNodeBlueprintFactory.cpp b/Source/FlowEditor/Private/Nodes/FlowNodeBlueprintFactory.cpp index 86d11dcf7..cf7f422ae 100644 --- a/Source/FlowEditor/Private/Nodes/FlowNodeBlueprintFactory.cpp +++ b/Source/FlowEditor/Private/Nodes/FlowNodeBlueprintFactory.cpp @@ -9,7 +9,6 @@ #include "ClassViewerFilter.h" #include "ClassViewerModule.h" #include "Editor.h" -#include "EditorStyleSet.h" #include "Kismet2/KismetEditorUtilities.h" #include "Misc/MessageDialog.h" #include "Modules/ModuleManager.h" From a19f090518c8d72c58eb73de23a07faa0d3dbcb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 25 Dec 2022 19:34:08 +0100 Subject: [PATCH 073/485] promoted FlowComponentRegistry to protected --- Source/Flow/Public/FlowSubsystem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Flow/Public/FlowSubsystem.h b/Source/Flow/Public/FlowSubsystem.h index 3315609dd..c98d8c4a0 100644 --- a/Source/Flow/Public/FlowSubsystem.h +++ b/Source/Flow/Public/FlowSubsystem.h @@ -126,7 +126,7 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem ////////////////////////////////////////////////////////////////////////// // Component Registry -private: +protected: /* All the Flow Components currently existing in the world */ TMultiMap> FlowComponentRegistry; From c732798bdb3713c6972c095c609e7968cf1e14c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 25 Dec 2022 19:34:17 +0100 Subject: [PATCH 074/485] added commentary --- Source/Flow/Private/FlowSubsystem.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index 122b46f70..397394184 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -325,6 +325,9 @@ void UFlowSubsystem::OnGameSaved(UFlowSaveGame* SaveGame) void UFlowSubsystem::OnGameLoaded(UFlowSaveGame* SaveGame) { LoadedSaveGame = SaveGame; + + // here's opportunity to apply loaded data to custom systems + // it's recommended to do this by overriding method in the subclass } void UFlowSubsystem::LoadRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const FString& SavedAssetInstanceName) From f79a5c2f1708769df2bb7c5ff3438708686c67b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Fri, 30 Dec 2022 17:13:49 +0100 Subject: [PATCH 075/485] Asset Search: added option to run search on single asset, with Search Browser opened as asset editor tab Requires engine modification: https://github.com/EpicGames/UnrealEngine/pull/9943 docs: https://github.com/MothCocoon/FlowGraph/wiki/Asset-Search --- Resources/Icons/Refresh.png | Bin 5742 -> 0 bytes .../Private/Asset/FlowAssetEditor.cpp | 79 +++++++++++++++--- .../Private/Asset/FlowAssetToolbar.cpp | 10 ++- .../FlowEditor/Private/FlowEditorCommands.cpp | 3 +- Source/FlowEditor/Private/FlowEditorStyle.cpp | 6 +- .../FlowEditor/Public/Asset/FlowAssetEditor.h | 13 ++- Source/FlowEditor/Public/FlowEditorCommands.h | 1 + Source/FlowEditor/Public/FlowEditorDefines.h | 6 ++ 8 files changed, 99 insertions(+), 19 deletions(-) delete mode 100644 Resources/Icons/Refresh.png diff --git a/Resources/Icons/Refresh.png b/Resources/Icons/Refresh.png deleted file mode 100644 index 4bf3091f3ec3eaae54b613fa17358b1b4f8a0e0f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5742 zcmV-!7LnKLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000Y_Nkl>oyMo zQZz`L1O;56D2f7cfg){Cw{99g6ire$X=A``8pp1ST9)ldc4$Si7E82DEtVx~BPkB| z8Im(|mvc@ZW=K&MRoFluf*=RDz`ek|=l;&O{Ll9tVT|Dyc}RZ2$FBf?=>hHe4-Jc! z01$-%&F#zCw)1zfq$XXLrmilHj(no1DAMWL?yjz`UD<5ru2{@n>UmzBW82dAi&HA{ z`=5R3tsnI4+w;Qmwr0}FINh5z6DdV;riksh1VMn-29QKi1W1ImP)bwCPx8u3&ti<> z-05RC0^Vw9rBTY%w6?W>adCU|mm0E}jh<%rIM+eU&ki?RS z`c$g6J&ZI_WU6roS{oe4O*J+&?XItH+}+W+@~@|-r;p_elZQ{e*ZaIN`sW~S223O~ z7-I+=pPjou|5xkRuKco!3|fGc1}ViHnAV2;REhENNy_C?!Ex-!cH*g_;i1!Ed3wUw z32fV59*cRIFw`hzFb0ePZ7Q&{&#q%Tj+@vzIMnZ)JbCsNr4+X9ERe;`T?BrK+tzJ) z^fM1V_UB=sD={VwQVU#Hpp@a{xr@AW{KUog&-VTJ>Xq|*E!6u!f-y2L7R#dg@7E zNGUL~;?k>wqwIbC&FJ*IJzqr!&v>p!NQ7nCl@$w7(XyE$3X51GC9>JZ4N)`;c-^JO z7_S_3>^wN;4CbXC|;Mi$!g#X>46}vjtP>?ABB&-5EyLt!F|*@2L+M8SOt%7`yyrJeej8 zX0YA(oE(LrPco4NqYy%(R7AWc(H`@>I;ARZ9UL0h$wWeWo`cqvm{dy8(b2iNxo!Ez z{P@suX;l{OdNm8XINQ*&86+`C@9h9j^32NQ!ADX!f{GL^-$Y_!oBt;MOnW8W=Ij-Pft zFS0BP75U@_FZK)$4h^@pwlvLpNo%N0rj|L5*BFK6+f zDV4u^`TWI}mgXi^jYEOJcHCsFrZ!tHnmIrr7T8ImFw|PlT2Nvz#A?HJ0cly- zj?0xZeH`ES;teYegEFJJOH7UpQkQMPc4JKCN14tK1A^%X*&w0io|NSY$*|z-aC= zqq)oTBuG-p4nz)p0 zShQAYjT8b)3M?atC6a4G-|q^_r4I)$oMoi{(kH5ljnP~_e~dL7?{BP4WtS>#h_uES zgAjrs48p-HXQs==Df%zI%ljwaM5*v*3yut5J>1@L+k;wb3<9Gd9)Oj! z?CA@=M;2nJsa^>n4tVqCRzMK=r4P%+sk^cbEp=fKqLjj5kkTRug8aAQ2z&e{S9~jr6r}nXcSO0qcDzS$;vw)?>n&n-zJ6!Cjkp+1loXBpsosKEzsu( zAOiI2JMQ_DL?THL2F&*3(sH?S>FkA`SH2g8LCFEaTQ*IF$&nLdBLk-q0&O(fXi9}L zu|VQDiOywf?*2Bg1b7JeEU*RW0MbAVaH?|}Kv&@maA?iO`*(NWwd>O=s#s5JjnW!z z;KbW|u7*MB0pPcQ2Y{|wHhE(vmZD4Nd%s)JCymjDnR0+#itvnNm^FWsNcv}Aw!!ngi) zDnI-h&@Y6Xk(PyJIk;Y&nq)o6R09o*I%wt)IcRsbGy(v*D`6yMD7YwddL+{bo zPEHp_azH&ZvLjU}>Qtn_85#*F`wFGB4*i+)MymlKW$2en8OMz+tEq|AH)K3KM=&u_A_$|o-2nuS8{Hsyid#5YT%|z<44xr|!~noLvup?T4FpJofp(FV1VI3=Q>hGMNO9<4`PX z%D$meqCw2Fh!o%g-Es?Zu~mSPe5-b?ZUk#4Q2EM@k7H zME!3(^7v1--TQ@mq9{PCXbut}QR9$Gdc-^%DJ9w%!bmYSw(arO z>(9RP>I>igcVl!^l_skigKq#s9~-cmsoOT){pb_-Jn#p*Qgsbh;1??;xe$Orsp~sJ z<%4R^EN`r~U9W~xaf&zh{L9Y{?tAv-stnoHe+&aZ2hPoJj6V+G>@KmTp}F(X?f3oO zXI5?4(dl|Ms3<^dRfYTLsssp-(#E!3lnOb2=J2_mz2EQYzx3X@s%Bb15jYPV1+Ff1 zc<^xmsS5B4U~|jj)w{a4es){O^6vJ!`WD-9V=QF&C<>U)j|TmhPF?Fg{6BjyoH_J7 zFg8!bT_9ftE8psL4&7%Gfh z`|w(B;Dd|BnaOKa2`K=Vfs+ea>;DUI{;+@)kgZ;I)j|Ov07_NSEdZ0%`|?ePFP{YP gS0}8$jDY_;0Aw{4pa(fKg8%>k07*qoM6N<$g6|Lo761SM diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index b2c82f50c..36e1b0621 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -30,6 +30,7 @@ #include "PropertyEditorModule.h" #include "ScopedTransaction.h" #include "SNodePanel.h" +#include "Source/Private/Widgets/SSearchBrowser.h" #include "ToolMenus.h" #include "Widgets/Docking/SDockTab.h" @@ -38,6 +39,7 @@ const FName FFlowAssetEditor::DetailsTab(TEXT("Details")); const FName FFlowAssetEditor::GraphTab(TEXT("Graph")); const FName FFlowAssetEditor::PaletteTab(TEXT("Palette")); +const FName FFlowAssetEditor::SearchTab(TEXT("Search")); FFlowAssetEditor::FFlowAssetEditor() : FlowAsset(nullptr) @@ -106,29 +108,39 @@ void FFlowAssetEditor::RegisterTabSpawners(const TSharedRef& FAssetEditorToolkit::RegisterTabSpawners(InTabManager); - InTabManager->RegisterTabSpawner(GraphTab, FOnSpawnTab::CreateSP(this, &FFlowAssetEditor::SpawnTab_GraphCanvas)) - .SetDisplayName(LOCTEXT("GraphTab", "Viewport")) - .SetGroup(WorkspaceMenuCategoryRef) - .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "GraphEditor.EventGraph_16x")); - InTabManager->RegisterTabSpawner(DetailsTab, FOnSpawnTab::CreateSP(this, &FFlowAssetEditor::SpawnTab_Details)) .SetDisplayName(LOCTEXT("DetailsTab", "Details")) .SetGroup(WorkspaceMenuCategoryRef) .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.Tabs.Details")); + InTabManager->RegisterTabSpawner(GraphTab, FOnSpawnTab::CreateSP(this, &FFlowAssetEditor::SpawnTab_Graph)) + .SetDisplayName(LOCTEXT("GraphTab", "Viewport")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "GraphEditor.EventGraph_16x")); + InTabManager->RegisterTabSpawner(PaletteTab, FOnSpawnTab::CreateSP(this, &FFlowAssetEditor::SpawnTab_Palette)) .SetDisplayName(LOCTEXT("PaletteTab", "Palette")) .SetGroup(WorkspaceMenuCategoryRef) .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.Tabs.Palette")); + +#if ENABLE_SEARCH_IN_ASSET_EDITOR + InTabManager->RegisterTabSpawner(SearchTab, FOnSpawnTab::CreateSP(this, &FFlowAssetEditor::SpawnTab_Search)) + .SetDisplayName(LOCTEXT("SearchTab", "Search")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.Tabs.FindResults")); +#endif } void FFlowAssetEditor::UnregisterTabSpawners(const TSharedRef& InTabManager) { FAssetEditorToolkit::UnregisterTabSpawners(InTabManager); - InTabManager->UnregisterTabSpawner(GraphTab); InTabManager->UnregisterTabSpawner(DetailsTab); + InTabManager->UnregisterTabSpawner(GraphTab); InTabManager->UnregisterTabSpawner(PaletteTab); +#if ENABLE_SEARCH_IN_ASSET_EDITOR + InTabManager->UnregisterTabSpawner(SearchTab); +#endif } TSharedRef FFlowAssetEditor::SpawnTab_Details(const FSpawnTabArgs& Args) const @@ -142,7 +154,24 @@ TSharedRef FFlowAssetEditor::SpawnTab_Details(const FSpawnTabArgs& Arg ]; } -TSharedRef FFlowAssetEditor::SpawnTab_GraphCanvas(const FSpawnTabArgs& Args) const +#if ENABLE_SEARCH_IN_ASSET_EDITOR +TSharedRef FFlowAssetEditor::SpawnTab_Search(const FSpawnTabArgs& Args) const +{ + check(Args.GetTabId() == SearchTab); + + return SNew(SDockTab) + .Label(LOCTEXT("FlowSearchTitle", "Search")) + [ + SNew(SBox) + .AddMetaData(FTagMetaData(TEXT("FlowSearch"))) + [ + SearchBrowser.ToSharedRef() + ] + ]; +} +#endif + +TSharedRef FFlowAssetEditor::SpawnTab_Graph(const FSpawnTabArgs& Args) const { check(Args.GetTabId() == GraphTab); @@ -185,7 +214,7 @@ void FFlowAssetEditor::InitFlowAssetEditor(const EToolkitMode::Type Mode, const BindGraphCommands(); CreateWidgets(); - const TSharedRef StandaloneDefaultLayout = FTabManager::NewLayout("FlowAssetEditor_Layout_v3") + const TSharedRef StandaloneDefaultLayout = FTabManager::NewLayout("FlowAssetEditor_Layout_v4") ->AddArea ( FTabManager::NewPrimaryArea()->SetOrientation(Orient_Horizontal) @@ -197,9 +226,22 @@ void FFlowAssetEditor::InitFlowAssetEditor(const EToolkitMode::Type Mode, const ) ->Split ( - FTabManager::NewStack() + FTabManager::NewSplitter() ->SetSizeCoefficient(0.65f) - ->AddTab(GraphTab, ETabState::OpenedTab)->SetHideTabWell(true) + ->SetOrientation(Orient_Vertical) + ->Split + ( + FTabManager::NewStack() + ->SetSizeCoefficient(0.8f) + ->SetHideTabWell(true) + ->AddTab(GraphTab, ETabState::OpenedTab) + ) + ->Split + ( + FTabManager::NewStack() + ->SetSizeCoefficient(0.2f) + ->AddTab(SearchTab, ETabState::ClosedTab) + ) ) ->Split ( @@ -244,6 +286,12 @@ void FFlowAssetEditor::BindToolbarCommands() FExecuteAction::CreateSP(this, &FFlowAssetEditor::RefreshAsset), FCanExecuteAction::CreateStatic(&FFlowAssetEditor::CanEdit)); +#if ENABLE_SEARCH_IN_ASSET_EDITOR + ToolkitCommands->MapAction(ToolbarCommands.SearchInAsset, + FExecuteAction::CreateSP(this, &FFlowAssetEditor::SearchInAsset), + FCanExecuteAction()); +#endif + // Engine's Play commands ToolkitCommands->Append(FPlayWorldCommands::GlobalPlayWorldActions.ToSharedRef()); @@ -263,6 +311,14 @@ void FFlowAssetEditor::RefreshAsset() } } +#if ENABLE_SEARCH_IN_ASSET_EDITOR +void FFlowAssetEditor::SearchInAsset() +{ + TabManager->TryInvokeTab(SearchTab); + SearchBrowser->FocusForUse(); +} +#endif + void FFlowAssetEditor::GoToParentInstance() { const UFlowAsset* AssetThatInstancedThisAsset = FlowAsset->GetInspectedInstance()->GetParentInstance(); @@ -291,6 +347,9 @@ void FFlowAssetEditor::CreateWidgets() DetailsView->SetIsPropertyEditingEnabledDelegate(FIsPropertyEditingEnabled::CreateStatic(&FFlowAssetEditor::CanEdit)); DetailsView->SetObject(FlowAsset); +#if ENABLE_SEARCH_IN_ASSET_EDITOR + SearchBrowser = SNew(SSearchBrowser, GetFlowAsset()); +#endif Palette = SNew(SFlowPalette, SharedThis(this)); } diff --git a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp index f6ad02c32..f95db4098 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp @@ -194,12 +194,14 @@ void FFlowAssetToolbar::BuildAssetToolbar(UToolMenu* ToolbarMenu) const FToolMenuSection& Section = ToolbarMenu->AddSection("Editing"); Section.InsertPosition = FToolMenuInsert("Asset", EToolMenuInsertType::After); + // add buttons Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().RefreshAsset)); +#if ENABLE_SEARCH_IN_ASSET_EDITOR + Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().SearchInAsset)); +#endif - // visual diff: menu to choose asset revision compared with the current one - FToolMenuSection& DiffSection = ToolbarMenu->AddSection("SourceControl"); - DiffSection.InsertPosition = FToolMenuInsert("Asset", EToolMenuInsertType::After); - DiffSection.AddDynamicEntry("SourceControlCommands", FNewToolMenuSectionDelegate::CreateLambda([this](FToolMenuSection& InSection) + // Visual Diff: menu to choose asset revision compared with the current one + Section.AddDynamicEntry("SourceControlCommands", FNewToolMenuSectionDelegate::CreateLambda([this](FToolMenuSection& InSection) { InSection.InsertPosition = FToolMenuInsert(); FToolMenuEntry DiffEntry = FToolMenuEntry::InitComboButton( diff --git a/Source/FlowEditor/Private/FlowEditorCommands.cpp b/Source/FlowEditor/Private/FlowEditorCommands.cpp index 75643e609..530e40783 100644 --- a/Source/FlowEditor/Private/FlowEditorCommands.cpp +++ b/Source/FlowEditor/Private/FlowEditorCommands.cpp @@ -17,7 +17,8 @@ FFlowToolbarCommands::FFlowToolbarCommands() void FFlowToolbarCommands::RegisterCommands() { - UI_COMMAND(RefreshAsset, "Refresh Asset", "Refresh asset and all nodes", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(RefreshAsset, "Refresh", "Refresh asset and all nodes", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(SearchInAsset, "Search", "Search in the current Flow Graph", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control | EModifierKey::Shift, EKeys::F) ); UI_COMMAND(GoToParentInstance, "Go To Parent", "Open editor for the Flow Asset that created this Flow instance", EUserInterfaceActionType::Button, FInputChord()); } diff --git a/Source/FlowEditor/Private/FlowEditorStyle.cpp b/Source/FlowEditor/Private/FlowEditorStyle.cpp index 875cfc585..98bd0cd70 100644 --- a/Source/FlowEditor/Private/FlowEditorStyle.cpp +++ b/Source/FlowEditor/Private/FlowEditorStyle.cpp @@ -8,6 +8,7 @@ #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 IMAGE_BRUSH_SVG( RelativePath, ... ) FSlateVectorImageBrush(StyleSet->RootToContentDir(RelativePath, TEXT(".svg")), __VA_ARGS__) TSharedPtr FFlowEditorStyle::StyleSet = nullptr; @@ -22,6 +23,7 @@ void FFlowEditorStyle::Initialize() StyleSet = MakeShareable(new FSlateStyleSet(TEXT("FlowEditorStyle"))); const FVector2D Icon16(16.0f, 16.0f); + const FVector2D Icon20(20.0f, 20.0f); const FVector2D Icon30(30.0f, 30.0f); const FVector2D Icon40(40.0f, 40.0f); const FVector2D Icon64(64.0f, 64.0f); @@ -29,6 +31,8 @@ void FFlowEditorStyle::Initialize() // engine assets StyleSet->SetContentRoot(FPaths::EngineContentDir() / TEXT("Editor/Slate/")); + StyleSet->Set("FlowToolbar.RefreshAsset", new IMAGE_BRUSH_SVG( "Starship/Common/Apply", Icon20)); + StyleSet->Set("FlowToolbar.SearchInAsset", new IMAGE_BRUSH_SVG( "Starship/Common/Search", Icon20)); StyleSet->Set("FlowToolbar.GoToParentInstance", new IMAGE_BRUSH("Icons/icon_DebugStepOut_40x", Icon40)); StyleSet->Set("FlowGraph.BreakpointEnabled", new IMAGE_BRUSH("Old/Kismet2/Breakpoint_Valid", FVector2D(24.0f, 24.0f))); @@ -44,8 +48,6 @@ void FFlowEditorStyle::Initialize() StyleSet->Set("ClassIcon.FlowAsset", new IMAGE_BRUSH(TEXT("Icons/FlowAsset_16x"), Icon16)); StyleSet->Set("ClassThumbnail.FlowAsset", new IMAGE_BRUSH(TEXT("Icons/FlowAsset_64x"), Icon64)); - StyleSet->Set("FlowToolbar.RefreshAsset", new IMAGE_BRUSH("Icons/Refresh", Icon40)); - StyleSet->Set("Flow.Node.Title", new BOX_BRUSH("Icons/FlowNode_Title", FMargin(8.0f/64.0f, 0, 0, 0))); StyleSet->Set("Flow.Node.Body", new BOX_BRUSH("Icons/FlowNode_Body", FMargin(16.f/64.f))); StyleSet->Set("Flow.Node.ActiveShadow", new BOX_BRUSH("Icons/FlowNode_Shadow_Active", FMargin(18.0f/64.0f))); diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index 2e811e975..063419f87 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -36,12 +36,16 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit TSharedPtr FocusedGraphEditor; TSharedPtr DetailsView; TSharedPtr Palette; +#if ENABLE_SEARCH_IN_ASSET_EDITOR + TSharedPtr SearchBrowser; +#endif public: /** The tab ids for all the tabs used */ static const FName DetailsTab; static const FName GraphTab; static const FName PaletteTab; + static const FName SearchTab; private: /** The current UI selection state of this editor */ @@ -84,8 +88,9 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit private: TSharedRef SpawnTab_Details(const FSpawnTabArgs& Args) const; - TSharedRef SpawnTab_GraphCanvas(const FSpawnTabArgs& Args) const; + TSharedRef SpawnTab_Graph(const FSpawnTabArgs& Args) const; TSharedRef SpawnTab_Palette(const FSpawnTabArgs& Args) const; + TSharedRef SpawnTab_Search(const FSpawnTabArgs& Args) const; public: /** Edits the specified FlowAsset object */ @@ -96,6 +101,10 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit virtual void BindToolbarCommands(); virtual void RefreshAsset(); +#if ENABLE_SEARCH_IN_ASSET_EDITOR + virtual void SearchInAsset(); +#endif + virtual void GoToParentInstance(); virtual bool CanGoToParentInstance(); @@ -143,7 +152,7 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit // FAssetEditorToolkit virtual void JumpToInnerObject(UObject* InnerObject) override; // -- -#endif +#endif protected: virtual void SelectAllNodes() const; diff --git a/Source/FlowEditor/Public/FlowEditorCommands.h b/Source/FlowEditor/Public/FlowEditorCommands.h index 64b0f4a7d..1a2446c8f 100644 --- a/Source/FlowEditor/Public/FlowEditorCommands.h +++ b/Source/FlowEditor/Public/FlowEditorCommands.h @@ -13,6 +13,7 @@ class FLOWEDITOR_API FFlowToolbarCommands final : public TCommands RefreshAsset; + TSharedPtr SearchInAsset; TSharedPtr GoToParentInstance; virtual void RegisterCommands() override; diff --git a/Source/FlowEditor/Public/FlowEditorDefines.h b/Source/FlowEditor/Public/FlowEditorDefines.h index fb1b09357..62cdd64fd 100644 --- a/Source/FlowEditor/Public/FlowEditorDefines.h +++ b/Source/FlowEditor/Public/FlowEditorDefines.h @@ -7,3 +7,9 @@ * Set macro value to 1, if you made these changes to the engine: https://github.com/EpicGames/UnrealEngine/pull/9882 */ #define ENABLE_JUMP_TO_INNER_OBJECT 0 + +/** + * Documentation: https://github.com/MothCocoon/FlowGraph/wiki/Asset-Search + * Set macro value to 1, if you made these changes to the engine: https://github.com/EpicGames/UnrealEngine/pull/9943 + */ +#define ENABLE_SEARCH_IN_ASSET_EDITOR 0 From 393b4a09f6cd6d59747765b9a200b4218e9a044f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Fri, 30 Dec 2022 17:15:33 +0100 Subject: [PATCH 076/485] removed dead code --- Source/FlowEditor/Private/Asset/FlowAssetIndexer.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Source/FlowEditor/Private/Asset/FlowAssetIndexer.cpp b/Source/FlowEditor/Private/Asset/FlowAssetIndexer.cpp index ee14123f5..5bf0bf94f 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetIndexer.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetIndexer.cpp @@ -9,7 +9,6 @@ #include "EdGraph/EdGraphPin.h" #include "EdGraphNode_Comment.h" -#include "Engine/SimpleConstructionScript.h" #include "Internationalization/Text.h" #include "SearchSerializer.h" #include "Utility/IndexerUtilities.h" @@ -38,15 +37,6 @@ void FFlowAssetIndexer::IndexAsset(const UObject* InAssetObject, FSearchSerializ { Serializer.BeginIndexingObject(FlowAsset, TEXT("$self")); - // for (const FName& CustomInput : FlowAsset->GetCustomInputs()) - // { - // Serializer.IndexProperty(CustomInput.ToString(), CustomInput); - // } - // for (const FName& CustomOutput : FlowAsset->GetCustomOutputs()) - // { - // Serializer.IndexProperty(CustomOutput.ToString(), CustomOutput); - // } - FIndexerUtilities::IterateIndexableProperties(FlowAsset, [&Serializer](const FProperty* Property, const FString& Value) { Serializer.IndexProperty(Property, Value); From b14fdb219a202276e448ca163df26470ee85ac34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sat, 31 Dec 2022 12:46:21 +0100 Subject: [PATCH 077/485] ifdef include to silence static analysis --- Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index 36e1b0621..e0679497c 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -30,10 +30,13 @@ #include "PropertyEditorModule.h" #include "ScopedTransaction.h" #include "SNodePanel.h" -#include "Source/Private/Widgets/SSearchBrowser.h" #include "ToolMenus.h" #include "Widgets/Docking/SDockTab.h" +#if ENABLE_SEARCH_IN_ASSET_EDITOR +#include "Source/Private/Widgets/SSearchBrowser.h" +#endif + #define LOCTEXT_NAMESPACE "FlowAssetEditor" const FName FFlowAssetEditor::DetailsTab(TEXT("Details")); From 28ff146f1d5b1beb7cde131b4b6cfab29977deec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sat, 31 Dec 2022 12:50:00 +0100 Subject: [PATCH 078/485] cosmetic cleanup --- Source/FlowEditor/Public/Asset/FlowAssetEditor.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index 063419f87..7de2d5d6c 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -59,10 +59,12 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit // FGCObject virtual void AddReferencedObjects(FReferenceCollector& Collector) override; + virtual FString GetReferencerName() const override { return TEXT("FFlowAssetEditor"); } + // -- // FEditorUndoClient @@ -90,7 +92,9 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit TSharedRef SpawnTab_Details(const FSpawnTabArgs& Args) const; TSharedRef SpawnTab_Graph(const FSpawnTabArgs& Args) const; TSharedRef SpawnTab_Palette(const FSpawnTabArgs& Args) const; +#if ENABLE_SEARCH_IN_ASSET_EDITOR TSharedRef SpawnTab_Search(const FSpawnTabArgs& Args) const; +#endif public: /** Edits the specified FlowAsset object */ @@ -103,8 +107,8 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit virtual void RefreshAsset(); #if ENABLE_SEARCH_IN_ASSET_EDITOR virtual void SearchInAsset(); -#endif - +#endif + virtual void GoToParentInstance(); virtual bool CanGoToParentInstance(); @@ -228,7 +232,7 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit bool CanSetSignalMode(const EFlowSignalMode Mode) const; void OnForcePinActivation() const; - + void FocusViewport() const; bool CanFocusViewport() const; From f396bd0dcd8d384772ddad475c12a8e44d803e75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 1 Jan 2023 19:32:53 +0100 Subject: [PATCH 079/485] added Message Log support! Node validation uses new UFlowNode::ValidateNode method which is available in C++ only --- Source/Flow/Flow.Build.cs | 8 +- Source/Flow/Private/FlowAsset.cpp | 23 ++-- Source/Flow/Private/FlowMessageLog.cpp | 84 +++++++++++++ Source/Flow/Private/Nodes/FlowNode.cpp | 24 +++- Source/Flow/Public/FlowAsset.h | 7 +- Source/Flow/Public/FlowMessageLog.h | 94 ++++++++++++++ Source/Flow/Public/Nodes/FlowNode.h | 11 +- Source/FlowEditor/FlowEditor.Build.cs | 1 + .../Private/Asset/FlowAssetEditor.cpp | 116 +++++++++++++++--- .../Private/Asset/FlowMessageLogListing.cpp | 70 +++++++++++ Source/FlowEditor/Private/Graph/FlowGraph.cpp | 13 +- .../Private/Graph/FlowGraphSchema_Actions.cpp | 4 +- .../Private/Graph/Nodes/FlowGraphNode.cpp | 13 +- .../Private/Graph/Widgets/SFlowGraphNode.cpp | 34 ++++- .../Graph/Widgets/SFlowGraphNode_SubGraph.cpp | 1 - .../FlowEditor/Public/Asset/FlowAssetEditor.h | 9 +- .../Public/Asset/FlowMessageLogListing.h | 29 +++++ Source/FlowEditor/Public/Graph/FlowGraph.h | 2 + .../Public/Graph/Nodes/FlowGraphNode.h | 7 +- 19 files changed, 505 insertions(+), 45 deletions(-) create mode 100644 Source/Flow/Private/FlowMessageLog.cpp create mode 100644 Source/Flow/Public/FlowMessageLog.h create mode 100644 Source/FlowEditor/Private/Asset/FlowMessageLogListing.cpp create mode 100644 Source/FlowEditor/Public/Asset/FlowMessageLogListing.h diff --git a/Source/Flow/Flow.Build.cs b/Source/Flow/Flow.Build.cs index b69475b87..62f81ef48 100644 --- a/Source/Flow/Flow.Build.cs +++ b/Source/Flow/Flow.Build.cs @@ -24,11 +24,15 @@ public Flow(ReadOnlyTargetRules Target) : base(Target) "MovieSceneTracks", "Slate", "SlateCore" - }); + }); if (Target.Type == TargetType.Editor) { - PublicDependencyModuleNames.Add("UnrealEd"); + PublicDependencyModuleNames.AddRange(new[] + { + "MessageLog", + "UnrealEd" + }); } } } diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 054474096..45c330ac6 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -1,6 +1,8 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "FlowAsset.h" + +#include "FlowMessageLog.h" #include "FlowSettings.h" #include "FlowSubsystem.h" @@ -62,23 +64,28 @@ void UFlowAsset::PostDuplicate(bool bDuplicateForPIE) } } -EDataValidationResult UFlowAsset::IsDataValid(TArray& ValidationErrors) +EDataValidationResult UFlowAsset::ValidateAsset(FFlowMessageLog& MessageLog) { + // first attempt to refresh graph, fix common issues automatically + if (GetFlowGraphInterface().IsValid()) + { + GetFlowGraphInterface()->RefreshGraph(this); + } + + // validate nodes for (const TPair& Node : Nodes) { - if (Node.Value == nullptr || Node.Value->IsDataValid(ValidationErrors) == EDataValidationResult::Invalid) + if (Node.Value) { - // refresh data if Node is missing, i.e. its class has been deleted - if (Node.Value == nullptr) + Node.Value->Log.Messages.Empty(); + if (Node.Value->ValidateNode() == EDataValidationResult::Invalid) { - HarvestNodeConnections(); + MessageLog.Messages.Append(Node.Value->Log.Messages); } - - return EDataValidationResult::Invalid; } } - return EDataValidationResult::Valid; + return MessageLog.Messages.Num() > 0 ? EDataValidationResult::Invalid : EDataValidationResult::Valid; } TSharedPtr UFlowAsset::FlowGraphInterface = nullptr; diff --git a/Source/Flow/Private/FlowMessageLog.cpp b/Source/Flow/Private/FlowMessageLog.cpp new file mode 100644 index 000000000..ec5afe928 --- /dev/null +++ b/Source/Flow/Private/FlowMessageLog.cpp @@ -0,0 +1,84 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "FlowMessageLog.h" +#include "Nodes/FlowNode.h" +#include "FlowAsset.h" + +#define LOCTEXT_NAMESPACE "FlowMessageLog" + +const FName FFlowMessageLog::LogName(TEXT("FlowGraph")); + +FFlowGraphToken::FFlowGraphToken(const UFlowAsset* InFlowAsset) +{ + CachedText = FText::FromString(InFlowAsset->GetClass()->GetPathName()); +} + +FFlowGraphToken::FFlowGraphToken(const UFlowNode* InFlowNode) + : GraphNode(InFlowNode->GetGraphNode()) +{ + CachedText = InFlowNode->GetNodeTitle(); +} + +FFlowGraphToken::FFlowGraphToken(UEdGraphNode* InGraphNode, const UEdGraphPin* InPin) + : GraphNode(InGraphNode) + , GraphPin(InPin) +{ + if (InPin) + { + CachedText = InPin->GetDisplayName(); + if (CachedText.IsEmpty()) + { + CachedText = LOCTEXT("UnnamedPin", ""); + } + } + else + { + CachedText = GraphNode->GetNodeTitle(ENodeTitleType::ListView); + } +} + +TSharedPtr FFlowGraphToken::Create(const UFlowAsset* InFlowAsset, FTokenizedMessage& Message) +{ + if (InFlowAsset) + { + Message.AddToken(MakeShareable(new FFlowGraphToken(InFlowAsset))); + return Message.GetMessageTokens().Last(); + } + + return nullptr; +} + +TSharedPtr FFlowGraphToken::Create(const UFlowNode* InFlowNode, FTokenizedMessage& Message) +{ + if (InFlowNode) + { + Message.AddToken(MakeShareable(new FFlowGraphToken(InFlowNode))); + return Message.GetMessageTokens().Last(); + } + + return nullptr; +} + +TSharedPtr FFlowGraphToken::Create(UEdGraphNode* InGraphNode, FTokenizedMessage& Message) +{ + if (InGraphNode) + { + Message.AddToken(MakeShareable(new FFlowGraphToken(InGraphNode, nullptr))); + return Message.GetMessageTokens().Last(); + } + + return nullptr; +} + +TSharedPtr FFlowGraphToken::Create(const UEdGraphPin* InPin, FTokenizedMessage& Message) +{ + if (InPin && InPin->GetOwningNode()) + { + Message.AddToken(MakeShareable(new FFlowGraphToken(InPin->GetOwningNode(), InPin))); + return Message.GetMessageTokens().Last(); + } + + return nullptr; +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index d2aefbaa6..db83ace65 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -3,6 +3,7 @@ #include "Nodes/FlowNode.h" #include "FlowAsset.h" +#include "FlowMessageLog.h" #include "FlowModule.h" #include "FlowSubsystem.h" #include "FlowTypes.h" @@ -649,12 +650,13 @@ FString UFlowNode::GetProgressAsString(float Value) return TempString; } -void UFlowNode::LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType) const +void UFlowNode::LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType) { #if !UE_BUILD_SHIPPING const FString TemplatePath = GetFlowAsset()->TemplateAsset->GetPathName(); Message += TEXT(" --- node ") + GetName() + TEXT(", asset ") + FPaths::GetPath(TemplatePath) / FPaths::GetBaseFilename(TemplatePath); + // OnScreen Message if (OnScreenMessageType == EFlowOnScreenMessageType::Permanent) { if (GetWorld()) @@ -675,16 +677,34 @@ void UFlowNode::LogError(FString Message, const EFlowOnScreenMessageType OnScree GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, Message); } + // Message Log - not yet functional + { + Log.Error(*Message, GetGraphNode()); + + FMessageLog MessageLog("FlowGraph"); + MessageLog.AddMessages(Log.Messages); + } + + // Output Log UE_LOG(LogFlow, Error, TEXT("%s"), *Message); #endif } -void UFlowNode::LogMessage(FString Message) const +void UFlowNode::LogMessage(FString Message) { #if !UE_BUILD_SHIPPING const FString TemplatePath = GetFlowAsset()->TemplateAsset->GetPathName(); Message += TEXT(" --- node ") + GetName() + TEXT(", asset ") + FPaths::GetPath(TemplatePath) / FPaths::GetBaseFilename(TemplatePath); + // Message Log - not yet functional + { + Log.Note(*Message, GetGraphNode()); + + FMessageLog MessageLog("FlowGraph"); + MessageLog.AddMessages(Log.Messages); + } + + // Output Log UE_LOG(LogFlow, Log, TEXT("%s"), *Message); #endif } diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index a6058b710..efb57a00e 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -2,9 +2,9 @@ #pragma once +#include "FlowMessageLog.h" #include "FlowSave.h" #include "FlowTypes.h" -#include "Templates/SubclassOf.h" #include "FlowAsset.generated.h" class UFlowNode; @@ -26,6 +26,8 @@ class FLOW_API IFlowGraphInterface IFlowGraphInterface() {} virtual ~IFlowGraphInterface() {} + virtual void RefreshGraph(UFlowAsset* FlowAsset) {} + virtual void OnInputTriggered(UEdGraphNode* GraphNode, const int32 Index) const {} virtual void OnOutputTriggered(UEdGraphNode* GraphNode, const int32 Index) const {} }; @@ -67,8 +69,9 @@ class FLOW_API UFlowAsset : public UObject static void AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector); virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; virtual void PostDuplicate(bool bDuplicateForPIE) override; - virtual EDataValidationResult IsDataValid(TArray& ValidationErrors) override; // -- + + virtual EDataValidationResult ValidateAsset(FFlowMessageLog& MessageLog); #endif // IFlowGraphInterface diff --git a/Source/Flow/Public/FlowMessageLog.h b/Source/Flow/Public/FlowMessageLog.h new file mode 100644 index 000000000..3ac6cc84c --- /dev/null +++ b/Source/Flow/Public/FlowMessageLog.h @@ -0,0 +1,94 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "EdGraph/EdGraphNode.h" +#include "Logging/TokenizedMessage.h" +#include "Misc/UObjectToken.h" + +class UFlowAsset; +class UFlowNode; + +/** + * Message Log token that links to an element in Flow Graph + */ +class FLOW_API FFlowGraphToken : public IMessageToken +{ +private: + const TWeakObjectPtr GraphNode; + const FEdGraphPinReference GraphPin; + + explicit FFlowGraphToken(const UFlowAsset* InFlowAsset); + explicit FFlowGraphToken(const UFlowNode* InFlowNode); + explicit FFlowGraphToken(UEdGraphNode* InGraphNode, const UEdGraphPin* InPin); + +public: + /** Factory method, tokens can only be constructed as shared refs */ + static TSharedPtr Create(const UFlowAsset* InFlowAsset, FTokenizedMessage& Message); + static TSharedPtr Create(const UFlowNode* InFlowNode, FTokenizedMessage& Message); + static TSharedPtr Create(UEdGraphNode* InGraphNode, FTokenizedMessage& Message); + static TSharedPtr Create(const UEdGraphPin* InPin, FTokenizedMessage& Message); + + const UEdGraphNode* GetGraphNode() const { return GraphNode.Get(); } + const UEdGraphPin* GetPin() const { return GraphPin.Get(); } + + // IMessageToken + virtual EMessageToken::Type GetType() const override + { + return EMessageToken::EdGraph; + } +}; + +/** + * List of Message Log lines + */ +class FLOW_API FFlowMessageLog +{ +public: + static const FName LogName; + TArray> Messages; + +public: + FFlowMessageLog() + { + } + + template + void Error(const TCHAR* Format, T* Object) + { + TSharedRef Message = FTokenizedMessage::Create(EMessageSeverity::Error); + AddMessage(NAME_None, Format, Message, Object); + } + + template + void Warning(const TCHAR* Format, T* Object) + { + TSharedRef Message = FTokenizedMessage::Create(EMessageSeverity::Warning); + AddMessage(NAME_None, Format, Message, Object); + } + + template + void Note(const TCHAR* Format, T* Object) + { + TSharedRef Message = FTokenizedMessage::Create(EMessageSeverity::Info); + AddMessage(NAME_None, Format, Message, Object); + } + +protected: + template + void AddMessage(FName MessageID, const TCHAR* Format, TSharedRef& Message, T* Object) + { + Message->SetIdentifier(MessageID); + + if (Object) + { + if (const TSharedPtr Token = FFlowGraphToken::Create(Object, Message.Get())) + { + Message->SetMessageLink(FUObjectToken::Create(Object)); + } + } + + Message.Get().AddToken(FTextToken::Create(FText::FromString(Format))); + Messages.Add(Message); + } +}; diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index d905cb0aa..7c811ead5 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -8,6 +8,7 @@ #include "VisualLogger/VisualLoggerDebugSnapshotInterface.h" #include "Templates/SubclassOf.h" +#include "FlowMessageLog.h" #include "FlowTypes.h" #include "Nodes/FlowPin.h" #include "FlowNode.generated.h" @@ -75,6 +76,8 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte // Opportunity to update node's data before UFlowGraphNode would call ReconstructNode() virtual void FixNode(UEdGraphNode* NewGraphNode); + + virtual EDataValidationResult ValidateNode() { return EDataValidationResult::NotValidated; } #endif UEdGraphNode* GetGraphNode() const { return GraphNode; } @@ -122,6 +125,10 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte // Designed to handle patching UPROPERTY() EFlowSignalMode SignalMode; + +#if !UE_BUILD_SHIPPING + FFlowMessageLog Log; +#endif ////////////////////////////////////////////////////////////////////////// // All created pins (default, class-specific and added by user) @@ -380,10 +387,10 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte static FString GetProgressAsString(float Value); UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) - void LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType = EFlowOnScreenMessageType::Permanent) const; + void LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType = EFlowOnScreenMessageType::Permanent); UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) - void LogMessage(FString Message) const; + void LogMessage(FString Message); UFUNCTION(BlueprintCallable, Category = "FlowNode") void SaveInstance(FFlowNodeSaveData& NodeRecord); diff --git a/Source/FlowEditor/FlowEditor.Build.cs b/Source/FlowEditor/FlowEditor.Build.cs index 90b593114..3de6ce699 100644 --- a/Source/FlowEditor/FlowEditor.Build.cs +++ b/Source/FlowEditor/FlowEditor.Build.cs @@ -36,6 +36,7 @@ public FlowEditor(ReadOnlyTargetRules Target) : base(Target) "KismetWidgets", "LevelEditor", "LevelSequence", + "MessageLog", "MovieScene", "MovieSceneTracks", "MovieSceneTools", diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index e0679497c..b2b1fe840 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -2,10 +2,13 @@ #include "Asset/FlowAssetEditor.h" +#include "FlowEditorCommands.h" +#include "FlowEditorModule.h" +#include "FlowMessageLog.h" + #include "Asset/FlowAssetToolbar.h" #include "Asset/FlowDebugger.h" -#include "FlowEditorCommands.h" -#include "Graph/FlowGraph.h" +#include "Asset/FlowMessageLogListing.h" #include "Graph/FlowGraphEditorSettings.h" #include "Graph/FlowGraphSchema.h" #include "Graph/FlowGraphSchema_Actions.h" @@ -24,8 +27,11 @@ #include "GraphEditorActions.h" #include "HAL/PlatformApplicationMisc.h" #include "IDetailsView.h" +#include "IMessageLogListing.h" #include "Kismet2/DebuggerCommands.h" #include "LevelEditor.h" +#include "MessageLogModule.h" +#include "Misc/UObjectToken.h" #include "Modules/ModuleManager.h" #include "PropertyEditorModule.h" #include "ScopedTransaction.h" @@ -41,6 +47,7 @@ const FName FFlowAssetEditor::DetailsTab(TEXT("Details")); const FName FFlowAssetEditor::GraphTab(TEXT("Graph")); +const FName FFlowAssetEditor::MessagesTab(TEXT("Messages")); const FName FFlowAssetEditor::PaletteTab(TEXT("Palette")); const FName FFlowAssetEditor::SearchTab(TEXT("Search")); @@ -120,6 +127,11 @@ void FFlowAssetEditor::RegisterTabSpawners(const TSharedRef& .SetDisplayName(LOCTEXT("GraphTab", "Viewport")) .SetGroup(WorkspaceMenuCategoryRef) .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "GraphEditor.EventGraph_16x")); + + InTabManager->RegisterTabSpawner(MessagesTab, FOnSpawnTab::CreateSP(this, &FFlowAssetEditor::SpawnTab_MessageLog)) + .SetDisplayName(LOCTEXT("MessagesTab", "Messages")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.Tabs.CompilerResults")); InTabManager->RegisterTabSpawner(PaletteTab, FOnSpawnTab::CreateSP(this, &FFlowAssetEditor::SpawnTab_Palette)) .SetDisplayName(LOCTEXT("PaletteTab", "Palette")) @@ -140,6 +152,7 @@ void FFlowAssetEditor::UnregisterTabSpawners(const TSharedRef InTabManager->UnregisterTabSpawner(DetailsTab); InTabManager->UnregisterTabSpawner(GraphTab); + InTabManager->UnregisterTabSpawner(MessagesTab); InTabManager->UnregisterTabSpawner(PaletteTab); #if ENABLE_SEARCH_IN_ASSET_EDITOR InTabManager->UnregisterTabSpawner(SearchTab); @@ -189,6 +202,17 @@ TSharedRef FFlowAssetEditor::SpawnTab_Graph(const FSpawnTabArgs& Args) return SpawnedTab; } +TSharedRef FFlowAssetEditor::SpawnTab_MessageLog(const FSpawnTabArgs& Args) const +{ + check(Args.GetTabId() == MessagesTab); + + return SNew(SDockTab) + .Label(LOCTEXT("FlowMessagesTitle", "Messages")) + [ + MessageLog.ToSharedRef() + ]; +} + TSharedRef FFlowAssetEditor::SpawnTab_Palette(const FSpawnTabArgs& Args) const { check(Args.GetTabId() == PaletteTab); @@ -217,7 +241,7 @@ void FFlowAssetEditor::InitFlowAssetEditor(const EToolkitMode::Type Mode, const BindGraphCommands(); CreateWidgets(); - const TSharedRef StandaloneDefaultLayout = FTabManager::NewLayout("FlowAssetEditor_Layout_v4") + const TSharedRef StandaloneDefaultLayout = FTabManager::NewLayout("FlowAssetEditor_Layout_v5") ->AddArea ( FTabManager::NewPrimaryArea()->SetOrientation(Orient_Horizontal) @@ -240,6 +264,12 @@ void FFlowAssetEditor::InitFlowAssetEditor(const EToolkitMode::Type Mode, const ->AddTab(GraphTab, ETabState::OpenedTab) ) ->Split + ( + FTabManager::NewStack() + ->SetSizeCoefficient(0.2f) + ->AddTab(MessagesTab, ETabState::ClosedTab) + ) + ->Split ( FTabManager::NewStack() ->SetSizeCoefficient(0.2f) @@ -308,10 +338,18 @@ void FFlowAssetEditor::BindToolbarCommands() void FFlowAssetEditor::RefreshAsset() { - if (UFlowGraph* FlowGraph = Cast(FlowAsset->GetGraph())) + // Refresh and validate asset, including graph + FFlowMessageLog LogResults; + FlowAsset->ValidateAsset(LogResults); + + // push messages to its window + MessageLogListing->ClearMessages(); + if (LogResults.Messages.Num() > 0) { - FlowGraph->RefreshGraph(); + TabManager->TryInvokeTab(MessagesTab); + MessageLogListing->AddMessages(LogResults.Messages); } + MessageLogListing->OnDataChanged().Broadcast(); } #if ENABLE_SEARCH_IN_ASSET_EDITOR @@ -339,21 +377,32 @@ void FFlowAssetEditor::CreateWidgets() { FocusedGraphEditor = CreateGraphWidget(); - FDetailsViewArgs Args; - Args.bHideSelectionTip = true; - Args.bShowPropertyMatrixButton = false; - Args.DefaultsOnlyVisibility = EEditDefaultsOnlyNodeVisibility::Hide; - Args.NotifyHook = this; + { + FDetailsViewArgs Args; + Args.bHideSelectionTip = true; + Args.bShowPropertyMatrixButton = false; + Args.DefaultsOnlyVisibility = EEditDefaultsOnlyNodeVisibility::Hide; + Args.NotifyHook = this; + + FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor"); + DetailsView = PropertyModule.CreateDetailView(Args); + DetailsView->SetIsPropertyEditingEnabledDelegate(FIsPropertyEditingEnabled::CreateStatic(&FFlowAssetEditor::CanEdit)); + DetailsView->SetObject(FlowAsset); + } - FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor"); - DetailsView = PropertyModule.CreateDetailView(Args); - DetailsView->SetIsPropertyEditingEnabledDelegate(FIsPropertyEditingEnabled::CreateStatic(&FFlowAssetEditor::CanEdit)); - DetailsView->SetObject(FlowAsset); + Palette = SNew(SFlowPalette, SharedThis(this)); #if ENABLE_SEARCH_IN_ASSET_EDITOR SearchBrowser = SNew(SSearchBrowser, GetFlowAsset()); #endif - Palette = SNew(SFlowPalette, SharedThis(this)); + + { + MessageLogListing = FFlowMessageLogListing::GetLogListing(FlowAsset); + MessageLogListing->OnMessageTokenClicked().AddSP(this, &FFlowAssetEditor::OnLogTokenClicked); + + FMessageLogModule& MessageLogModule = FModuleManager::LoadModuleChecked("MessageLog"); + MessageLog = MessageLogModule.CreateLogListingWidget(MessageLogListing.ToSharedRef()); + } } TSharedRef FFlowAssetEditor::CreateGraphWidget() @@ -718,11 +767,46 @@ void FFlowAssetEditor::JumpToInnerObject(UObject* InnerObject) { if (const UFlowNode* FlowNode = Cast(InnerObject)) { - FocusedGraphEditor->JumpToNode(FlowNode->GetGraphNode(), false); + FocusedGraphEditor->JumpToNode(FlowNode->GetGraphNode(), true); } } #endif +void FFlowAssetEditor::OnLogTokenClicked(const TSharedRef& Token) const +{ + if (Token->GetType() == EMessageToken::Object) + { + const TSharedRef ObjectToken = StaticCastSharedRef(Token); + if (const UObject* Object = ObjectToken->GetObject().Get()) + { + if (Object->IsAsset()) + { + GEditor->GetEditorSubsystem()->OpenEditorForAsset(const_cast(Object)); + } + else + { + UE_LOG(LogFlowEditor, Warning, TEXT("Unknown type of hyperlinked object (%s), cannot focus it"), *GetNameSafe(Object)); + } + } + } + else if (Token->GetType() == EMessageToken::EdGraph && FocusedGraphEditor.IsValid()) + { + const TSharedRef EdGraphToken = StaticCastSharedRef(Token); + + if (const UEdGraphPin* GraphPin = EdGraphToken->GetPin()) + { + if (!GraphPin->IsPendingKill()) + { + FocusedGraphEditor->JumpToPin(GraphPin); + } + } + else if (const UEdGraphNode* GraphNode = EdGraphToken->GetGraphNode()) + { + FocusedGraphEditor->JumpToNode(GraphNode, true); + } + } +} + void FFlowAssetEditor::SelectAllNodes() const { FocusedGraphEditor->SelectAllNodes(); diff --git a/Source/FlowEditor/Private/Asset/FlowMessageLogListing.cpp b/Source/FlowEditor/Private/Asset/FlowMessageLogListing.cpp new file mode 100644 index 000000000..516630d1d --- /dev/null +++ b/Source/FlowEditor/Private/Asset/FlowMessageLogListing.cpp @@ -0,0 +1,70 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Asset/FlowMessageLogListing.h" + +#include "MessageLogModule.h" + +#define LOCTEXT_NAMESPACE "FlowMessageLogListing" + +FFlowMessageLogListing::FFlowMessageLogListing(const UFlowAsset* InFlowAsset) + : Log(RegisterLogListing(InFlowAsset)) +{ +} + +FFlowMessageLogListing::~FFlowMessageLogListing() +{ + // Unregister the log so it will be ref-counted to zero if it has no messages + if(Log->NumMessages(EMessageSeverity::Info) == 0) + { + FMessageLogModule& MessageLogModule = FModuleManager::LoadModuleChecked("MessageLog"); + MessageLogModule.UnregisterLogListing(Log->GetName()); + } +} + +TSharedRef FFlowMessageLogListing::RegisterLogListing(const UFlowAsset* InFlowAsset) +{ + FMessageLogModule& MessageLogModule = FModuleManager::LoadModuleChecked("MessageLog"); + + const FName LogName = GetListingName(InFlowAsset); + + // Register the log (this will return an existing log if it has been used before) + FMessageLogInitializationOptions LogInitOptions; + LogInitOptions.bShowInLogWindow = false; + MessageLogModule.RegisterLogListing(LogName, LOCTEXT("FlowGraphLogLabel", "FlowGraph"), LogInitOptions); + return MessageLogModule.GetLogListing(LogName); +} + +TSharedRef FFlowMessageLogListing::GetLogListing(const UFlowAsset* InFlowAsset) +{ + FMessageLogModule& MessageLogModule = FModuleManager::LoadModuleChecked("MessageLog"); + + const FName LogName = GetListingName(InFlowAsset); + + // Reuse any existing log, or create a new one (that is not held onto bey the message log system) + if(MessageLogModule.IsRegisteredLogListing(LogName)) + { + return MessageLogModule.GetLogListing(LogName); + } + else + { + FMessageLogInitializationOptions LogInitOptions; + LogInitOptions.bShowInLogWindow = false; + return MessageLogModule.CreateLogListing(LogName, LogInitOptions); + } +} + +FName FFlowMessageLogListing::GetListingName(const UFlowAsset* InFlowAsset) +{ + FName LogListingName; + if (InFlowAsset) + { + LogListingName = *FString::Printf(TEXT("%s_%s_FlowMessageLog"), *InFlowAsset->AssetGuid.ToString(), *InFlowAsset->GetName()); + } + else + { + LogListingName = "FlowGraph"; + } + return LogListingName; +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/Graph/FlowGraph.cpp b/Source/FlowEditor/Private/Graph/FlowGraph.cpp index 096d48bf6..93626cedb 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraph.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraph.cpp @@ -9,6 +9,11 @@ #include "Kismet2/BlueprintEditorUtils.h" +void FFlowGraphInterface::RefreshGraph(UFlowAsset* FlowAsset) +{ + CastChecked(FlowAsset->GetGraph())->RefreshGraph(); +} + void FFlowGraphInterface::OnInputTriggered(UEdGraphNode* GraphNode, const int32 Index) const { CastChecked(GraphNode)->OnInputTriggered(Index); @@ -41,8 +46,8 @@ UEdGraph* UFlowGraph::CreateGraph(UFlowAsset* InFlowAsset) void UFlowGraph::RefreshGraph() { - // don't run fixup in commandlets or PIE - if (!IsRunningCommandlet() && GEditor && !GEditor->PlayWorld) + // don't run fixup in PIE + if (GEditor && !GEditor->PlayWorld) { // check if all Graph Nodes have expected, up-to-date type CastChecked(GetSchema())->GatherNativeNodes(); @@ -59,12 +64,12 @@ void UFlowGraph::RefreshGraph() } } - // update context pins + // refresh nodes TArray FlowGraphNodes; GetNodesOfClass(FlowGraphNodes); for (UFlowGraphNode* GraphNode : FlowGraphNodes) { - GraphNode->RefreshContextPins(true); + GraphNode->OnGraphRefresh(); } } } diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp index 06dfad3b7..6baba1027 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp @@ -62,7 +62,7 @@ UFlowGraphNode* FFlowGraphSchemaAction_NewNode::CreateNode(UEdGraph* ParentGraph // link editor and runtime nodes together UFlowNode* FlowNode = FlowAsset->CreateNode(NodeClass, NewGraphNode); - NewGraphNode->SetFlowNode(FlowNode); + NewGraphNode->SetNodeTemplate(FlowNode); // create pins and connections NewGraphNode->AllocateDefaultPins(); @@ -110,7 +110,7 @@ UFlowGraphNode* FFlowGraphSchemaAction_NewNode::RecreateNode(UEdGraph* ParentGra // link editor and runtime nodes together FlowNode->SetGraphNode(NewGraphNode); - NewGraphNode->SetFlowNode(FlowNode); + NewGraphNode->SetNodeTemplate(FlowNode); // move links from the old node NewGraphNode->AllocateDefaultPins(); diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 3c5d81619..d72ae9260 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -25,7 +25,6 @@ #include "SourceCodeNavigation.h" #include "Textures/SlateIcon.h" #include "ToolMenuSection.h" -#include "UnrealEd.h" #define LOCTEXT_NAMESPACE "FlowGraphNode" @@ -107,11 +106,16 @@ UFlowGraphNode::UFlowGraphNode(const FObjectInitializer& ObjectInitializer) OrphanedPinSaveMode = ESaveOrphanPinMode::SaveAll; } -void UFlowGraphNode::SetFlowNode(UFlowNode* InFlowNode) +void UFlowGraphNode::SetNodeTemplate(UFlowNode* InFlowNode) { FlowNode = InFlowNode; } +const UFlowNode* UFlowGraphNode::GetNodeTemplate() const +{ + return FlowNode; +} + UFlowNode* UFlowGraphNode::GetFlowNode() const { if (FlowNode) @@ -239,6 +243,11 @@ void UFlowGraphNode::OnExternalChange() GetGraph()->NotifyGraphChanged(); } +void UFlowGraphNode::OnGraphRefresh() +{ + RefreshContextPins(true); +} + bool UFlowGraphNode::CanCreateUnderSpecifiedSchema(const UEdGraphSchema* Schema) const { return Schema->IsA(UFlowGraphSchema::StaticClass()); diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp index 20f84b970..04ed6420a 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp @@ -2,10 +2,8 @@ #include "Graph/Widgets/SFlowGraphNode.h" #include "FlowEditorStyle.h" -#include "Graph/FlowGraphEditorSettings.h" #include "Graph/FlowGraphSettings.h" -#include "FlowAsset.h" #include "Nodes/FlowNode.h" #include "EdGraph/EdGraphPin.h" @@ -362,6 +360,38 @@ void SFlowGraphNode::UpdateErrorInfo() { if (const UFlowNode* FlowNode = FlowGraphNode->GetFlowNode()) { + if (FlowNode->Log.Messages.Num() > 0) + { + EMessageSeverity::Type MaxSeverity = EMessageSeverity::Info; + for (const TSharedRef& Message : FlowNode->Log.Messages) + { + if (Message->GetSeverity() < MaxSeverity) + { + MaxSeverity = Message->GetSeverity(); + } + } + + switch(MaxSeverity) + { + case EMessageSeverity::Error: + ErrorMsg = FString(TEXT("ERROR!")); + ErrorColor = FAppStyle::GetColor("ErrorReporting.BackgroundColor"); + break; + case EMessageSeverity::PerformanceWarning: + case EMessageSeverity::Warning: + ErrorMsg = FString(TEXT("WARNING!")); + ErrorColor = FAppStyle::GetColor("ErrorReporting.WarningBackgroundColor"); + break; + case EMessageSeverity::Info: + ErrorMsg = FString(TEXT("NOTE")); + ErrorColor = FAppStyle::GetColor("InfoReporting.BackgroundColor"); + break; + default: ; + } + + return; + } + if (FlowNode->GetClass()->HasAnyClassFlags(CLASS_Deprecated) || FlowNode->bNodeDeprecated) { ErrorMsg = FlowNode->ReplacedBy ? FString::Printf(TEXT(" REPLACED BY: %s "), *FlowNode->ReplacedBy->GetName()) : FString(TEXT(" DEPRECATED! ")); diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode_SubGraph.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode_SubGraph.cpp index 006c98576..6ec6a11c3 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode_SubGraph.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode_SubGraph.cpp @@ -6,7 +6,6 @@ #include "FlowAsset.h" #include "Nodes/Route/FlowNode_SubGraph.h" -#include "IDocumentation.h" #include "SGraphPreviewer.h" #include "Widgets/Layout/SBox.h" #include "Widgets/SToolTip.h" diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index 7de2d5d6c..04e056a9a 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -40,10 +40,15 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit TSharedPtr SearchBrowser; #endif + /** Message log, with the log listing that it reflects */ + TSharedPtr MessageLog; + TSharedPtr MessageLogListing; + public: /** The tab ids for all the tabs used */ static const FName DetailsTab; static const FName GraphTab; + static const FName MessagesTab; static const FName PaletteTab; static const FName SearchTab; @@ -64,7 +69,6 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit { return TEXT("FFlowAssetEditor"); } - // -- // FEditorUndoClient @@ -91,6 +95,7 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit private: TSharedRef SpawnTab_Details(const FSpawnTabArgs& Args) const; TSharedRef SpawnTab_Graph(const FSpawnTabArgs& Args) const; + TSharedRef SpawnTab_MessageLog(const FSpawnTabArgs& Args) const; TSharedRef SpawnTab_Palette(const FSpawnTabArgs& Args) const; #if ENABLE_SEARCH_IN_ASSET_EDITOR TSharedRef SpawnTab_Search(const FSpawnTabArgs& Args) const; @@ -159,6 +164,8 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit #endif protected: + void OnLogTokenClicked(const TSharedRef& Token) const; + virtual void SelectAllNodes() const; virtual bool CanSelectAllNodes() const; diff --git a/Source/FlowEditor/Public/Asset/FlowMessageLogListing.h b/Source/FlowEditor/Public/Asset/FlowMessageLogListing.h new file mode 100644 index 000000000..b9935e8b1 --- /dev/null +++ b/Source/FlowEditor/Public/Asset/FlowMessageLogListing.h @@ -0,0 +1,29 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "IMessageLogListing.h" + +#include "FlowAsset.h" + +/** + * Scope wrapper for the message log. Ensures we don't leak logs that we dont need (i.e. those that have no messages) + * Replicated after FScopedBlueprintMessageLog + */ +class FLOWEDITOR_API FFlowMessageLogListing +{ +public: + FFlowMessageLogListing(const UFlowAsset* InFlowAsset); + ~FFlowMessageLogListing(); + +public: + TSharedRef Log; + FName LogName; + +private: + static TSharedRef RegisterLogListing(const UFlowAsset* InFlowAsset); + static FName GetListingName(const UFlowAsset* InFlowAsset); + +public: + static TSharedRef GetLogListing(const UFlowAsset* InFlowAsset); +}; diff --git a/Source/FlowEditor/Public/Graph/FlowGraph.h b/Source/FlowEditor/Public/Graph/FlowGraph.h index 9759dc676..d46cea58f 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraph.h +++ b/Source/FlowEditor/Public/Graph/FlowGraph.h @@ -12,6 +12,8 @@ class FLOWEDITOR_API FFlowGraphInterface final : public IFlowGraphInterface public: virtual ~FFlowGraphInterface() override {} + virtual void RefreshGraph(UFlowAsset* FlowAsset) override; + virtual void OnInputTriggered(UEdGraphNode* GraphNode, const int32 Index) const override; virtual void OnOutputTriggered(UEdGraphNode* GraphNode, const int32 Index) const override; }; diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h index 7cbc1092f..35733f77d 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h @@ -72,7 +72,9 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode UPROPERTY() TArray> AssignedNodeClasses; - void SetFlowNode(UFlowNode* InFlowNode); + void SetNodeTemplate(UFlowNode* InFlowNode); + const UFlowNode* GetNodeTemplate() const; + UFlowNode* GetFlowNode() const; // UObject @@ -96,6 +98,9 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode void OnExternalChange(); +public: + virtual void OnGraphRefresh(); + ////////////////////////////////////////////////////////////////////////// // Graph node From 981a4c3780f4fcaebcd8bb3871ea6971bf147c9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 1 Jan 2023 19:33:34 +0100 Subject: [PATCH 080/485] node validation added --- .../Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp | 12 ++++++++++++ Source/Flow/Public/Nodes/Route/FlowNode_SubGraph.h | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp index dede5195c..ab6dfa5bb 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp @@ -3,6 +3,7 @@ #include "Nodes/Route/FlowNode_SubGraph.h" #include "FlowAsset.h" +#include "FlowMessageLog.h" #include "FlowSubsystem.h" FFlowPin UFlowNode_SubGraph::StartPin(TEXT("Start")); @@ -105,6 +106,17 @@ UObject* UFlowNode_SubGraph::GetAssetToEdit() return Asset.IsNull() ? nullptr : LoadAsset(Asset); } +EDataValidationResult UFlowNode_SubGraph::ValidateNode() +{ + if (Asset.IsNull()) + { + Log.Error(TEXT("Flow Asset not assigned or invalid!"), this); + return EDataValidationResult::Invalid; + } + + return EDataValidationResult::Valid; +} + TArray UFlowNode_SubGraph::GetContextInputs() { TArray EventNames; diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_SubGraph.h b/Source/Flow/Public/Nodes/Route/FlowNode_SubGraph.h index 522540f25..d6356e1a0 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_SubGraph.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_SubGraph.h @@ -52,7 +52,8 @@ class FLOW_API UFlowNode_SubGraph : public UFlowNode #if WITH_EDITOR virtual FString GetNodeDescription() const override; virtual UObject* GetAssetToEdit() override; - + virtual EDataValidationResult ValidateNode() override; + virtual bool SupportsContextPins() const override { return true; } virtual TArray GetContextInputs() override; From bf0934e43752ecf9b57f4721f4442000c22df3ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 1 Jan 2023 19:37:47 +0100 Subject: [PATCH 081/485] renamed reference to Graph Editor --- .../Private/Asset/FlowAssetEditor.cpp | 92 +++++++++---------- .../FlowEditor/Public/Asset/FlowAssetEditor.h | 2 +- 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index b2b1fe840..d145585d0 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -79,7 +79,7 @@ void FFlowAssetEditor::PostRedo(bool bSuccess) void FFlowAssetEditor::HandleUndoTransaction() { SetUISelectionState(NAME_None); - FocusedGraphEditor->NotifyGraphChanged(); + GraphEditor->NotifyGraphChanged(); FSlateApplication::Get().DismissAllMenus(); } @@ -87,7 +87,7 @@ void FFlowAssetEditor::NotifyPostChange(const FPropertyChangedEvent& PropertyCha { if (PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive) { - FocusedGraphEditor->NotifyGraphChanged(); + GraphEditor->NotifyGraphChanged(); } } @@ -194,9 +194,9 @@ TSharedRef FFlowAssetEditor::SpawnTab_Graph(const FSpawnTabArgs& Args) TSharedRef SpawnedTab = SNew(SDockTab) .Label(LOCTEXT("FlowGraphTitle", "Graph")); - if (FocusedGraphEditor.IsValid()) + if (GraphEditor.IsValid()) { - SpawnedTab->SetContent(FocusedGraphEditor.ToSharedRef()); + SpawnedTab->SetContent(GraphEditor.ToSharedRef()); } return SpawnedTab; @@ -375,7 +375,7 @@ bool FFlowAssetEditor::CanGoToParentInstance() void FFlowAssetEditor::CreateWidgets() { - FocusedGraphEditor = CreateGraphWidget(); + GraphEditor = CreateGraphWidget(); { FDetailsViewArgs Args; @@ -661,7 +661,7 @@ void FFlowAssetEditor::ClearSelectionStateFor(const FName SelectionOwner) { if (SelectionOwner == GraphTab) { - FocusedGraphEditor->ClearSelectionSet(); + GraphEditor->ClearSelectionSet(); } else if (SelectionOwner == PaletteTab) { @@ -675,12 +675,12 @@ void FFlowAssetEditor::ClearSelectionStateFor(const FName SelectionOwner) void FFlowAssetEditor::OnCreateComment() const { FFlowGraphSchemaAction_NewComment CommentAction; - CommentAction.PerformAction(FlowAsset->GetGraph(), nullptr, FocusedGraphEditor->GetPasteLocation()); + CommentAction.PerformAction(FlowAsset->GetGraph(), nullptr, GraphEditor->GetPasteLocation()); } void FFlowAssetEditor::OnStraightenConnections() const { - FocusedGraphEditor->OnStraightenConnections(); + GraphEditor->OnStraightenConnections(); } bool FFlowAssetEditor::CanEdit() @@ -702,7 +702,7 @@ TSet FFlowAssetEditor::GetSelectedFlowNodes() const { TSet Result; - const FGraphPanelSelectionSet SelectedNodes = FocusedGraphEditor->GetSelectedNodes(); + const FGraphPanelSelectionSet SelectedNodes = GraphEditor->GetSelectedNodes(); for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt) { if (UFlowGraphNode* SelectedNode = Cast(*NodeIt)) @@ -716,12 +716,12 @@ TSet FFlowAssetEditor::GetSelectedFlowNodes() const int32 FFlowAssetEditor::GetNumberOfSelectedNodes() const { - return FocusedGraphEditor->GetSelectedNodes().Num(); + return GraphEditor->GetSelectedNodes().Num(); } bool FFlowAssetEditor::GetBoundsForSelectedNodes(class FSlateRect& Rect, float Padding) const { - return FocusedGraphEditor->GetBoundsForSelectedNodes(Rect, Padding); + return GraphEditor->GetBoundsForSelectedNodes(Rect, Padding); } void FFlowAssetEditor::OnSelectedNodesChanged(const TSet& Nodes) @@ -758,8 +758,8 @@ void FFlowAssetEditor::OnSelectedNodesChanged(const TSet& Nodes) void FFlowAssetEditor::SelectSingleNode(UEdGraphNode* Node) const { - FocusedGraphEditor->ClearSelectionSet(); - FocusedGraphEditor->SetNodeSelection(Node, true); + GraphEditor->ClearSelectionSet(); + GraphEditor->SetNodeSelection(Node, true); } #if ENABLE_JUMP_TO_INNER_OBJECT @@ -789,7 +789,7 @@ void FFlowAssetEditor::OnLogTokenClicked(const TSharedRef& Token) } } } - else if (Token->GetType() == EMessageToken::EdGraph && FocusedGraphEditor.IsValid()) + else if (Token->GetType() == EMessageToken::EdGraph && GraphEditor.IsValid()) { const TSharedRef EdGraphToken = StaticCastSharedRef(Token); @@ -797,19 +797,19 @@ void FFlowAssetEditor::OnLogTokenClicked(const TSharedRef& Token) { if (!GraphPin->IsPendingKill()) { - FocusedGraphEditor->JumpToPin(GraphPin); + GraphEditor->JumpToPin(GraphPin); } } else if (const UEdGraphNode* GraphNode = EdGraphToken->GetGraphNode()) { - FocusedGraphEditor->JumpToNode(GraphNode, true); + GraphEditor->JumpToNode(GraphNode, true); } } } void FFlowAssetEditor::SelectAllNodes() const { - FocusedGraphEditor->SelectAllNodes(); + GraphEditor->SelectAllNodes(); } bool FFlowAssetEditor::CanSelectAllNodes() const @@ -820,10 +820,10 @@ bool FFlowAssetEditor::CanSelectAllNodes() const void FFlowAssetEditor::DeleteSelectedNodes() { const FScopedTransaction Transaction(LOCTEXT("DeleteSelectedNode", "Delete Selected Node")); - FocusedGraphEditor->GetCurrentGraph()->Modify(); + GraphEditor->GetCurrentGraph()->Modify(); FlowAsset->Modify(); - const FGraphPanelSelectionSet SelectedNodes = FocusedGraphEditor->GetSelectedNodes(); + const FGraphPanelSelectionSet SelectedNodes = GraphEditor->GetSelectedNodes(); SetUISelectionState(NAME_None); for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt) @@ -837,7 +837,7 @@ void FFlowAssetEditor::DeleteSelectedNodes() { const FGuid NodeGuid = FlowGraphNode->GetFlowNode()->GetGuid(); - FocusedGraphEditor->GetCurrentGraph()->GetSchema()->BreakNodeLinks(*Node); + GraphEditor->GetCurrentGraph()->GetSchema()->BreakNodeLinks(*Node); Node->DestroyNode(); FlowAsset->UnregisterNode(NodeGuid); @@ -845,7 +845,7 @@ void FFlowAssetEditor::DeleteSelectedNodes() } } - FocusedGraphEditor->GetCurrentGraph()->GetSchema()->BreakNodeLinks(*Node); + GraphEditor->GetCurrentGraph()->GetSchema()->BreakNodeLinks(*Node); Node->DestroyNode(); } } @@ -854,11 +854,11 @@ void FFlowAssetEditor::DeleteSelectedNodes() void FFlowAssetEditor::DeleteSelectedDuplicableNodes() { // Cache off the old selection - const FGraphPanelSelectionSet OldSelectedNodes = FocusedGraphEditor->GetSelectedNodes(); + const FGraphPanelSelectionSet OldSelectedNodes = GraphEditor->GetSelectedNodes(); // Clear the selection and only select the nodes that can be duplicated FGraphPanelSelectionSet RemainingNodes; - FocusedGraphEditor->ClearSelectionSet(); + GraphEditor->ClearSelectionSet(); for (FGraphPanelSelectionSet::TConstIterator SelectedIt(OldSelectedNodes); SelectedIt; ++SelectedIt) { @@ -866,7 +866,7 @@ void FFlowAssetEditor::DeleteSelectedDuplicableNodes() { if (Node->CanDuplicateNode()) { - FocusedGraphEditor->SetNodeSelection(Node, true); + GraphEditor->SetNodeSelection(Node, true); } else { @@ -882,7 +882,7 @@ void FFlowAssetEditor::DeleteSelectedDuplicableNodes() { if (UEdGraphNode* Node = Cast(*SelectedIt)) { - FocusedGraphEditor->SetNodeSelection(Node, true); + GraphEditor->SetNodeSelection(Node, true); } } } @@ -891,7 +891,7 @@ bool FFlowAssetEditor::CanDeleteNodes() const { if (CanEdit()) { - const FGraphPanelSelectionSet SelectedNodes = FocusedGraphEditor->GetSelectedNodes(); + const FGraphPanelSelectionSet SelectedNodes = GraphEditor->GetSelectedNodes(); for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt) { if (const UEdGraphNode* Node = Cast(*NodeIt)) @@ -924,7 +924,7 @@ bool FFlowAssetEditor::CanCutNodes() const void FFlowAssetEditor::CopySelectedNodes() const { - const FGraphPanelSelectionSet SelectedNodes = FocusedGraphEditor->GetSelectedNodes(); + const FGraphPanelSelectionSet SelectedNodes = GraphEditor->GetSelectedNodes(); for (FGraphPanelSelectionSet::TConstIterator SelectedIt(SelectedNodes); SelectedIt; ++SelectedIt) { if (UFlowGraphNode* Node = Cast(*SelectedIt)) @@ -951,7 +951,7 @@ bool FFlowAssetEditor::CanCopyNodes() const { if (CanEdit()) { - const FGraphPanelSelectionSet SelectedNodes = FocusedGraphEditor->GetSelectedNodes(); + const FGraphPanelSelectionSet SelectedNodes = GraphEditor->GetSelectedNodes(); for (FGraphPanelSelectionSet::TConstIterator SelectedIt(SelectedNodes); SelectedIt; ++SelectedIt) { const UEdGraphNode* Node = Cast(*SelectedIt); @@ -967,7 +967,7 @@ bool FFlowAssetEditor::CanCopyNodes() const void FFlowAssetEditor::PasteNodes() { - PasteNodesHere(FocusedGraphEditor->GetPasteLocation()); + PasteNodesHere(GraphEditor->GetPasteLocation()); } void FFlowAssetEditor::PasteNodesHere(const FVector2D& Location) @@ -980,7 +980,7 @@ void FFlowAssetEditor::PasteNodesHere(const FVector2D& Location) FlowAsset->Modify(); // Clear the selection set (newly pasted stuff will be selected) - FocusedGraphEditor->ClearSelectionSet(); + GraphEditor->ClearSelectionSet(); // Grab the text to paste from the clipboard. FString TextToImport; @@ -1020,7 +1020,7 @@ void FFlowAssetEditor::PasteNodesHere(const FVector2D& Location) } // Select the newly pasted stuff - FocusedGraphEditor->SetNodeSelection(Node, true); + GraphEditor->SetNodeSelection(Node, true); Node->NodePosX = (Node->NodePosX - AvgNodePosition.X) + Location.X; Node->NodePosY = (Node->NodePosY - AvgNodePosition.Y) + Location.Y; @@ -1032,7 +1032,7 @@ void FFlowAssetEditor::PasteNodesHere(const FVector2D& Location) FlowAsset->HarvestNodeConnections(); // Update UI - FocusedGraphEditor->NotifyGraphChanged(); + GraphEditor->NotifyGraphChanged(); FlowAsset->PostEditChange(); FlowAsset->MarkPackageDirty(); @@ -1174,7 +1174,7 @@ bool FFlowAssetEditor::CanAddOutput() const void FFlowAssetEditor::RemovePin() const { - if (UEdGraphPin* SelectedPin = FocusedGraphEditor->GetGraphPinForMenu()) + if (UEdGraphPin* SelectedPin = GraphEditor->GetGraphPinForMenu()) { if (UFlowGraphNode* SelectedNode = Cast(SelectedPin->GetOwningNode())) { @@ -1187,7 +1187,7 @@ bool FFlowAssetEditor::CanRemovePin() const { if (CanEdit() && GetSelectedFlowNodes().Num() == 1) { - if (const UEdGraphPin* Pin = FocusedGraphEditor->GetGraphPinForMenu()) + if (const UEdGraphPin* Pin = GraphEditor->GetGraphPinForMenu()) { if (const UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) { @@ -1216,7 +1216,7 @@ void FFlowAssetEditor::OnAddBreakpoint() const void FFlowAssetEditor::OnAddPinBreakpoint() const { - if (UEdGraphPin* Pin = FocusedGraphEditor->GetGraphPinForMenu()) + if (UEdGraphPin* Pin = GraphEditor->GetGraphPinForMenu()) { if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) { @@ -1238,7 +1238,7 @@ bool FFlowAssetEditor::CanAddBreakpoint() const bool FFlowAssetEditor::CanAddPinBreakpoint() const { - if (UEdGraphPin* Pin = FocusedGraphEditor->GetGraphPinForMenu()) + if (UEdGraphPin* Pin = GraphEditor->GetGraphPinForMenu()) { if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) { @@ -1259,7 +1259,7 @@ void FFlowAssetEditor::OnRemoveBreakpoint() const void FFlowAssetEditor::OnRemovePinBreakpoint() const { - if (UEdGraphPin* Pin = FocusedGraphEditor->GetGraphPinForMenu()) + if (UEdGraphPin* Pin = GraphEditor->GetGraphPinForMenu()) { if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) { @@ -1280,7 +1280,7 @@ bool FFlowAssetEditor::CanRemoveBreakpoint() const bool FFlowAssetEditor::CanRemovePinBreakpoint() const { - if (UEdGraphPin* Pin = FocusedGraphEditor->GetGraphPinForMenu()) + if (UEdGraphPin* Pin = GraphEditor->GetGraphPinForMenu()) { if (const UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) { @@ -1301,7 +1301,7 @@ void FFlowAssetEditor::OnEnableBreakpoint() const void FFlowAssetEditor::OnEnablePinBreakpoint() const { - if (UEdGraphPin* Pin = FocusedGraphEditor->GetGraphPinForMenu()) + if (UEdGraphPin* Pin = GraphEditor->GetGraphPinForMenu()) { if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) { @@ -1312,7 +1312,7 @@ void FFlowAssetEditor::OnEnablePinBreakpoint() const bool FFlowAssetEditor::CanEnableBreakpoint() const { - if (UEdGraphPin* Pin = FocusedGraphEditor->GetGraphPinForMenu()) + if (UEdGraphPin* Pin = GraphEditor->GetGraphPinForMenu()) { if (const UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) { @@ -1330,7 +1330,7 @@ bool FFlowAssetEditor::CanEnableBreakpoint() const bool FFlowAssetEditor::CanEnablePinBreakpoint() const { - if (UEdGraphPin* Pin = FocusedGraphEditor->GetGraphPinForMenu()) + if (UEdGraphPin* Pin = GraphEditor->GetGraphPinForMenu()) { if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) { @@ -1351,7 +1351,7 @@ void FFlowAssetEditor::OnDisableBreakpoint() const void FFlowAssetEditor::OnDisablePinBreakpoint() const { - if (UEdGraphPin* Pin = FocusedGraphEditor->GetGraphPinForMenu()) + if (UEdGraphPin* Pin = GraphEditor->GetGraphPinForMenu()) { if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) { @@ -1372,7 +1372,7 @@ bool FFlowAssetEditor::CanDisableBreakpoint() const bool FFlowAssetEditor::CanDisablePinBreakpoint() const { - if (UEdGraphPin* Pin = FocusedGraphEditor->GetGraphPinForMenu()) + if (UEdGraphPin* Pin = GraphEditor->GetGraphPinForMenu()) { if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) { @@ -1393,7 +1393,7 @@ void FFlowAssetEditor::OnToggleBreakpoint() const void FFlowAssetEditor::OnTogglePinBreakpoint() const { - if (UEdGraphPin* Pin = FocusedGraphEditor->GetGraphPinForMenu()) + if (UEdGraphPin* Pin = GraphEditor->GetGraphPinForMenu()) { if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) { @@ -1410,7 +1410,7 @@ bool FFlowAssetEditor::CanToggleBreakpoint() const bool FFlowAssetEditor::CanTogglePinBreakpoint() const { - return FocusedGraphEditor->GetGraphPinForMenu() != nullptr; + return GraphEditor->GetGraphPinForMenu() != nullptr; } void FFlowAssetEditor::SetSignalMode(const EFlowSignalMode Mode) const @@ -1440,7 +1440,7 @@ bool FFlowAssetEditor::CanSetSignalMode(const EFlowSignalMode Mode) const void FFlowAssetEditor::OnForcePinActivation() const { - if (UEdGraphPin* Pin = FocusedGraphEditor->GetGraphPinForMenu()) + if (UEdGraphPin* Pin = GraphEditor->GetGraphPinForMenu()) { if (const UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) { diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index 04e056a9a..c08b218e1 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -33,7 +33,7 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit TSharedPtr AssetToolbar; TSharedPtr FlowDebugger; - TSharedPtr FocusedGraphEditor; + TSharedPtr GraphEditor; TSharedPtr DetailsView; TSharedPtr Palette; #if ENABLE_SEARCH_IN_ASSET_EDITOR From f15ab4134b44d372a3afed0aa77c91b1098d7136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 1 Jan 2023 19:48:40 +0100 Subject: [PATCH 082/485] CIS fix --- Source/Flow/Public/FlowMessageLog.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Flow/Public/FlowMessageLog.h b/Source/Flow/Public/FlowMessageLog.h index 3ac6cc84c..75fd57009 100644 --- a/Source/Flow/Public/FlowMessageLog.h +++ b/Source/Flow/Public/FlowMessageLog.h @@ -3,6 +3,7 @@ #pragma once #include "EdGraph/EdGraphNode.h" +#include "EdGraph/EdGraphPin.h" #include "Logging/TokenizedMessage.h" #include "Misc/UObjectToken.h" From 7f6b7fbbab745a0f92de191ee82b02a1c85db62e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 1 Jan 2023 20:22:51 +0100 Subject: [PATCH 083/485] non-editor build fix --- Source/Flow/Private/FlowMessageLog.cpp | 3 +++ Source/Flow/Public/FlowMessageLog.h | 4 ++++ Source/Flow/Public/Nodes/FlowNode.h | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Source/Flow/Private/FlowMessageLog.cpp b/Source/Flow/Private/FlowMessageLog.cpp index ec5afe928..f4e1ef621 100644 --- a/Source/Flow/Private/FlowMessageLog.cpp +++ b/Source/Flow/Private/FlowMessageLog.cpp @@ -4,6 +4,8 @@ #include "Nodes/FlowNode.h" #include "FlowAsset.h" +#if WITH_EDITOR + #define LOCTEXT_NAMESPACE "FlowMessageLog" const FName FFlowMessageLog::LogName(TEXT("FlowGraph")); @@ -82,3 +84,4 @@ TSharedPtr FFlowGraphToken::Create(const UEdGraphPin* InPin, FTok } #undef LOCTEXT_NAMESPACE +#endif // WITH_EDITOR diff --git a/Source/Flow/Public/FlowMessageLog.h b/Source/Flow/Public/FlowMessageLog.h index 75fd57009..ee6308636 100644 --- a/Source/Flow/Public/FlowMessageLog.h +++ b/Source/Flow/Public/FlowMessageLog.h @@ -10,6 +10,8 @@ class UFlowAsset; class UFlowNode; +#if WITH_EDITOR + /** * Message Log token that links to an element in Flow Graph */ @@ -93,3 +95,5 @@ class FLOW_API FFlowMessageLog Messages.Add(Message); } }; + +#endif // WITH_EDITOR diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 7c811ead5..bfe1781b8 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -126,7 +126,7 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UPROPERTY() EFlowSignalMode SignalMode; -#if !UE_BUILD_SHIPPING +#if WITH_EDITOR FFlowMessageLog Log; #endif From 32039139f8025d688ad2fe8d268807e9f723ccd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 1 Jan 2023 20:28:49 +0100 Subject: [PATCH 084/485] added node validation --- .../Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp | 11 +++++++++++ .../Private/Nodes/Route/FlowNode_CustomOutput.cpp | 11 +++++++++++ .../Nodes/World/FlowNode_ComponentObserver.cpp | 11 +++++++++++ .../Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp | 11 +++++++++++ .../Nodes/World/FlowNode_PlayLevelSequence.cpp | 11 +++++++++++ Source/Flow/Public/Nodes/Route/FlowNode_CustomInput.h | 1 + .../Flow/Public/Nodes/Route/FlowNode_CustomOutput.h | 1 + .../Public/Nodes/World/FlowNode_ComponentObserver.h | 2 ++ Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h | 1 + .../Public/Nodes/World/FlowNode_PlayLevelSequence.h | 2 ++ 10 files changed, 62 insertions(+) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp index bfa30140d..ae3946235 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp @@ -24,4 +24,15 @@ FString UFlowNode_CustomInput::GetNodeDescription() const { return EventName.ToString(); } + +EDataValidationResult UFlowNode_CustomInput::ValidateNode() +{ + if (EventName.IsNone()) + { + Log.Error(TEXT("Event Name is empty!"), this); + return EDataValidationResult::Invalid; + } + + return EDataValidationResult::Valid; +} #endif diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp index 48acfe972..df59bc1d3 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp @@ -30,4 +30,15 @@ FString UFlowNode_CustomOutput::GetNodeDescription() const { return EventName.ToString(); } + +EDataValidationResult UFlowNode_CustomOutput::ValidateNode() +{ + if (EventName.IsNone()) + { + Log.Error(TEXT("Event Name is empty!"), this); + return EDataValidationResult::Invalid; + } + + return EDataValidationResult::Valid; +} #endif diff --git a/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp b/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp index 896c9530d..0ad362e00 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp @@ -151,6 +151,17 @@ FString UFlowNode_ComponentObserver::GetNodeDescription() const return GetIdentityTagsDescription(IdentityTags); } +EDataValidationResult UFlowNode_ComponentObserver::ValidateNode() +{ + if (IdentityTags.IsEmpty()) + { + Log.Error(*UFlowNode::MissingIdentityTag, this); + return EDataValidationResult::Invalid; + } + + return EDataValidationResult::Valid; +} + FString UFlowNode_ComponentObserver::GetStatusString() const { if (ActivationState == EFlowNodeState::Active && RegisteredActors.Num() == 0) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp b/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp index d67df77e5..12f303690 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp @@ -33,4 +33,15 @@ FString UFlowNode_NotifyActor::GetNodeDescription() const { return GetIdentityTagsDescription(IdentityTags) + LINE_TERMINATOR + GetNotifyTagsDescription(NotifyTags); } + +EDataValidationResult UFlowNode_NotifyActor::ValidateNode() +{ + if (IdentityTags.IsEmpty()) + { + Log.Error(*UFlowNode::MissingIdentityTag, this); + return EDataValidationResult::Invalid; + } + + return EDataValidationResult::Valid; +} #endif diff --git a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp index 428951044..727d7e0f4 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp @@ -314,6 +314,17 @@ FString UFlowNode_PlayLevelSequence::GetNodeDescription() const return Sequence.IsNull() ? TEXT("[No sequence]") : Sequence.GetAssetName(); } +EDataValidationResult UFlowNode_PlayLevelSequence::ValidateNode() +{ + if (Sequence.IsNull()) + { + Log.Error(TEXT("Level Sequence asset not assigned or invalid!"), this); + return EDataValidationResult::Invalid; + } + + return EDataValidationResult::Valid; +} + FString UFlowNode_PlayLevelSequence::GetStatusString() const { return GetPlaybackProgress(); diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_CustomInput.h b/Source/Flow/Public/Nodes/Route/FlowNode_CustomInput.h index be86208af..e1919866e 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_CustomInput.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_CustomInput.h @@ -22,5 +22,6 @@ class FLOW_API UFlowNode_CustomInput : public UFlowNode #if WITH_EDITOR public: virtual FString GetNodeDescription() const override; + virtual EDataValidationResult ValidateNode() override; #endif }; diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_CustomOutput.h b/Source/Flow/Public/Nodes/Route/FlowNode_CustomOutput.h index 9e430b267..c2624cb2f 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_CustomOutput.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_CustomOutput.h @@ -22,5 +22,6 @@ class FLOW_API UFlowNode_CustomOutput final : public UFlowNode #if WITH_EDITOR virtual FString GetNodeDescription() const override; + virtual EDataValidationResult ValidateNode() override; #endif }; diff --git a/Source/Flow/Public/Nodes/World/FlowNode_ComponentObserver.h b/Source/Flow/Public/Nodes/World/FlowNode_ComponentObserver.h index 724768836..ecc5bbf35 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_ComponentObserver.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_ComponentObserver.h @@ -70,6 +70,8 @@ class FLOW_API UFlowNode_ComponentObserver : public UFlowNode #if WITH_EDITOR public: virtual FString GetNodeDescription() const override; + virtual EDataValidationResult ValidateNode() override; + virtual FString GetStatusString() const override; #endif }; diff --git a/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h b/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h index 228cc47d1..74219765b 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h @@ -30,5 +30,6 @@ class FLOW_API UFlowNode_NotifyActor : public UFlowNode #if WITH_EDITOR public: virtual FString GetNodeDescription() const override; + virtual EDataValidationResult ValidateNode() override; #endif }; diff --git a/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h b/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h index b46cad5df..eb24d35ac 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h @@ -120,6 +120,8 @@ class FLOW_API UFlowNode_PlayLevelSequence : public UFlowNode #if WITH_EDITOR virtual FString GetNodeDescription() const override; + virtual EDataValidationResult ValidateNode() override; + virtual FString GetStatusString() const override; virtual UObject* GetAssetToEdit() override; #endif From fc674189e963be94c296b715c34cf707a29b1c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 1 Jan 2023 20:42:24 +0100 Subject: [PATCH 085/485] more WITH_EDITOR --- Source/Flow/Private/Nodes/FlowNode.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index db83ace65..32f7e46ef 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -677,6 +677,7 @@ void UFlowNode::LogError(FString Message, const EFlowOnScreenMessageType OnScree GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, Message); } +#if WITH_EDITOR // Message Log - not yet functional { Log.Error(*Message, GetGraphNode()); @@ -684,6 +685,7 @@ void UFlowNode::LogError(FString Message, const EFlowOnScreenMessageType OnScree FMessageLog MessageLog("FlowGraph"); MessageLog.AddMessages(Log.Messages); } +#endif // Output Log UE_LOG(LogFlow, Error, TEXT("%s"), *Message); @@ -696,6 +698,7 @@ void UFlowNode::LogMessage(FString Message) const FString TemplatePath = GetFlowAsset()->TemplateAsset->GetPathName(); Message += TEXT(" --- node ") + GetName() + TEXT(", asset ") + FPaths::GetPath(TemplatePath) / FPaths::GetBaseFilename(TemplatePath); +#if WITH_EDITOR // Message Log - not yet functional { Log.Note(*Message, GetGraphNode()); @@ -703,7 +706,8 @@ void UFlowNode::LogMessage(FString Message) FMessageLog MessageLog("FlowGraph"); MessageLog.AddMessages(Log.Messages); } - +#endif + // Output Log UE_LOG(LogFlow, Log, TEXT("%s"), *Message); #endif From f5a7041bde55f0a013b5ac269392c1dd0c7d02af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Mon, 2 Jan 2023 14:35:46 +0100 Subject: [PATCH 086/485] compilation fix --- Source/FlowEditor/FlowEditor.Build.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/FlowEditor/FlowEditor.Build.cs b/Source/FlowEditor/FlowEditor.Build.cs index 3de6ce699..07c98553a 100644 --- a/Source/FlowEditor/FlowEditor.Build.cs +++ b/Source/FlowEditor/FlowEditor.Build.cs @@ -10,7 +10,8 @@ public FlowEditor(ReadOnlyTargetRules Target) : base(Target) PublicDependencyModuleNames.AddRange(new[] { - "Flow" + "Flow", + "MessageLog" }); PrivateDependencyModuleNames.AddRange(new[] @@ -36,7 +37,6 @@ public FlowEditor(ReadOnlyTargetRules Target) : base(Target) "KismetWidgets", "LevelEditor", "LevelSequence", - "MessageLog", "MovieScene", "MovieSceneTracks", "MovieSceneTools", From dd5f9ff423c12edb07d5e628e8823e6505fca384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Mon, 2 Jan 2023 14:39:20 +0100 Subject: [PATCH 087/485] compilation fix --- Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index d145585d0..1963478b1 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -767,7 +767,7 @@ void FFlowAssetEditor::JumpToInnerObject(UObject* InnerObject) { if (const UFlowNode* FlowNode = Cast(InnerObject)) { - FocusedGraphEditor->JumpToNode(FlowNode->GetGraphNode(), true); + GraphEditor->JumpToNode(FlowNode->GetGraphNode(), true); } } #endif From c0000735ee8628b303b8690c395e4f9cdaf3a316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Fri, 6 Jan 2023 15:14:44 +0100 Subject: [PATCH 088/485] moved SLevelEditorFlow to the Utils folder --- Source/FlowEditor/Private/FlowEditorModule.cpp | 2 +- .../Private/{LevelEditor => Utils}/SLevelEditorFlow.cpp | 2 +- .../FlowEditor/Public/{LevelEditor => Utils}/SLevelEditorFlow.h | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename Source/FlowEditor/Private/{LevelEditor => Utils}/SLevelEditorFlow.cpp (98%) rename Source/FlowEditor/Public/{LevelEditor => Utils}/SLevelEditorFlow.h (100%) diff --git a/Source/FlowEditor/Private/FlowEditorModule.cpp b/Source/FlowEditor/Private/FlowEditorModule.cpp index 7e7f7ba25..7c66344c0 100644 --- a/Source/FlowEditor/Private/FlowEditorModule.cpp +++ b/Source/FlowEditor/Private/FlowEditorModule.cpp @@ -9,7 +9,7 @@ #include "Asset/FlowAssetIndexer.h" #include "Graph/FlowGraphConnectionDrawingPolicy.h" #include "Graph/FlowGraphSettings.h" -#include "LevelEditor/SLevelEditorFlow.h" +#include "Utils/SLevelEditorFlow.h" #include "MovieScene/FlowTrackEditor.h" #include "Nodes/AssetTypeActions_FlowNodeBlueprint.h" #include "Nodes/Customizations/FlowNode_Details.h" diff --git a/Source/FlowEditor/Private/LevelEditor/SLevelEditorFlow.cpp b/Source/FlowEditor/Private/Utils/SLevelEditorFlow.cpp similarity index 98% rename from Source/FlowEditor/Private/LevelEditor/SLevelEditorFlow.cpp rename to Source/FlowEditor/Private/Utils/SLevelEditorFlow.cpp index 54062ffac..25c01b159 100644 --- a/Source/FlowEditor/Private/LevelEditor/SLevelEditorFlow.cpp +++ b/Source/FlowEditor/Private/Utils/SLevelEditorFlow.cpp @@ -1,6 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "LevelEditor/SLevelEditorFlow.h" +#include "Utils/SLevelEditorFlow.h" #include "FlowAsset.h" #include "FlowComponent.h" #include "FlowWorldSettings.h" diff --git a/Source/FlowEditor/Public/LevelEditor/SLevelEditorFlow.h b/Source/FlowEditor/Public/Utils/SLevelEditorFlow.h similarity index 100% rename from Source/FlowEditor/Public/LevelEditor/SLevelEditorFlow.h rename to Source/FlowEditor/Public/Utils/SLevelEditorFlow.h From dff120f3fcadbe78cfeef0c0df9e0292421d96f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Fri, 6 Jan 2023 15:31:12 +0100 Subject: [PATCH 089/485] Exposed all remaining closed out of the module, allowing to extend any class --- .../Private/MovieScene/FlowSection.cpp | 13 ++++++------- .../Private/MovieScene/FlowTrackEditor.cpp | 16 ++++++++-------- .../FlowEditor/Public/Asset/FlowAssetToolbar.h | 6 +++--- Source/FlowEditor/Public/FlowEditorCommands.h | 6 +++--- Source/FlowEditor/Public/Graph/FlowGraph.h | 2 +- .../Graph/FlowGraphConnectionDrawingPolicy.h | 2 +- .../Public/Graph/FlowGraphEditorSettings.h | 2 +- .../FlowEditor/Public/Graph/FlowGraphSettings.h | 2 +- .../{Private => Public}/MovieScene/FlowSection.h | 8 ++++---- .../MovieScene/FlowTrackEditor.h | 4 ++-- .../Nodes/AssetTypeActions_FlowNodeBlueprint.h | 2 +- .../Public/Nodes/FlowNodeBlueprintFactory.h | 4 ++-- 12 files changed, 33 insertions(+), 34 deletions(-) rename Source/FlowEditor/{Private => Public}/MovieScene/FlowSection.h (83%) rename Source/FlowEditor/{Private => Public}/MovieScene/FlowTrackEditor.h (92%) diff --git a/Source/FlowEditor/Private/MovieScene/FlowSection.cpp b/Source/FlowEditor/Private/MovieScene/FlowSection.cpp index e00da9c6a..72d77a793 100644 --- a/Source/FlowEditor/Private/MovieScene/FlowSection.cpp +++ b/Source/FlowEditor/Private/MovieScene/FlowSection.cpp @@ -1,13 +1,12 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "FlowSection.h" +#include "MovieScene/FlowSection.h" #include "MovieScene/MovieSceneFlowRepeaterSection.h" #include "MovieScene/MovieSceneFlowTriggerSection.h" #include "CommonMovieSceneTools.h" #include "Fonts/FontMeasure.h" #include "Framework/Application/SlateApplication.h" -#include "MovieSceneEventUtils.h" #include "MovieSceneTrack.h" #include "Rendering/DrawElements.h" #include "Sections/MovieSceneEventSection.h" @@ -18,19 +17,19 @@ bool FFlowSectionBase::IsSectionSelected() const { - TSharedPtr SequencerPtr = Sequencer.Pin(); + const TSharedPtr SequencerPtr = Sequencer.Pin(); TArray SelectedTracks; SequencerPtr->GetSelectedTracks(SelectedTracks); - UMovieSceneSection* Section = WeakSection.Get(); + const UMovieSceneSection* Section = WeakSection.Get(); UMovieSceneTrack* Track = Section ? CastChecked(Section->GetOuter()) : nullptr; return Track && SelectedTracks.Contains(Track); } void FFlowSectionBase::PaintEventName(FSequencerSectionPainter& Painter, int32 LayerId, const FString& InEventString, float PixelPos, bool bIsEventValid) const { - static const float BoxOffsetPx = 10.f; + static constexpr float BoxOffsetPx = 10.f; static const TCHAR* WarningString = TEXT("\xf071"); const FSlateFontInfo FontAwesomeFont = FAppStyle::Get().GetFontStyle("FontAwesome.10"); @@ -99,7 +98,7 @@ void FFlowSectionBase::PaintEventName(FSequencerSectionPainter& Painter, int32 L int32 FFlowSection::OnPaintSection(FSequencerSectionPainter& Painter) const { const int32 LayerId = Painter.PaintSectionBackground(); - UMovieSceneEventSection* EventSection = Cast(WeakSection.Get()); + const UMovieSceneEventSection* EventSection = Cast(WeakSection.Get()); if (!EventSection || !IsSectionSelected()) { return LayerId; @@ -160,7 +159,7 @@ int32 FFlowRepeaterSection::OnPaintSection(FSequencerSectionPainter& Painter) co { const int32 LayerId = Painter.PaintSectionBackground(); - UMovieSceneFlowRepeaterSection* EventRepeaterSection = Cast(WeakSection.Get()); + const UMovieSceneFlowRepeaterSection* EventRepeaterSection = Cast(WeakSection.Get()); if (!EventRepeaterSection) { return LayerId; diff --git a/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp b/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp index 0d7c4d282..28a687ffd 100644 --- a/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp +++ b/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp @@ -1,7 +1,7 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "FlowTrackEditor.h" -#include "FlowSection.h" +#include "MovieScene/FlowTrackEditor.h" +#include "MovieScene/FlowSection.h" #include "MovieScene/MovieSceneFlowRepeaterSection.h" #include "MovieScene/MovieSceneFlowTrack.h" @@ -65,7 +65,7 @@ void FFlowTrackEditor::AddFlowSubMenu(FMenuBuilder& MenuBuilder) void FFlowTrackEditor::BuildAddTrackMenu(FMenuBuilder& MenuBuilder) { UMovieSceneSequence* RootMovieSceneSequence = GetSequencer()->GetRootMovieSceneSequence(); - FMovieSceneSequenceEditor* SequenceEditor = FMovieSceneSequenceEditor::Find(RootMovieSceneSequence); + const FMovieSceneSequenceEditor* SequenceEditor = FMovieSceneSequenceEditor::Find(RootMovieSceneSequence); if (SequenceEditor && SequenceEditor->SupportsEvents(RootMovieSceneSequence)) { @@ -144,7 +144,7 @@ const FSlateBrush* FFlowTrackEditor::GetIconBrush() const return FAppStyle::GetBrush("Sequencer.Tracks.Event"); } -void FFlowTrackEditor::HandleAddFlowTrackMenuEntryExecute(UClass* SectionType) +void FFlowTrackEditor::HandleAddFlowTrackMenuEntryExecute(UClass* SectionType) const { UMovieScene* FocusedMovieScene = GetFocusedMovieScene(); @@ -179,12 +179,12 @@ void FFlowTrackEditor::HandleAddFlowTrackMenuEntryExecute(UClass* SectionType) } } -void FFlowTrackEditor::CreateNewSection(UMovieSceneTrack* Track, int32 RowIndex, UClass* SectionType, bool bSelect) const +void FFlowTrackEditor::CreateNewSection(UMovieSceneTrack* Track, const int32 RowIndex, UClass* SectionType, const bool bSelect) const { - TSharedPtr SequencerPtr = GetSequencer(); + const TSharedPtr SequencerPtr = GetSequencer(); if (SequencerPtr.IsValid()) { - UMovieScene* FocusedMovieScene = GetFocusedMovieScene(); + const UMovieScene* FocusedMovieScene = GetFocusedMovieScene(); const FQualifiedFrameTime CurrentTime = SequencerPtr->GetLocalTime(); FScopedTransaction Transaction(LOCTEXT("CreateNewFlowSectionTransactionText", "Add Flow Section")); @@ -218,7 +218,7 @@ void FFlowTrackEditor::CreateNewSection(UMovieSceneTrack* Track, int32 RowIndex, } else { - const float DefaultLengthInSeconds = 5.f; + constexpr float DefaultLengthInSeconds = 5.f; NewSectionRange = TRange(CurrentTime.Time.FrameNumber, CurrentTime.Time.FrameNumber + (DefaultLengthInSeconds * SequencerPtr->GetFocusedTickResolution()).FloorToFrame()); } diff --git a/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h b/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h index dbc72ca8f..7a9c954ee 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h @@ -13,7 +13,7 @@ class UToolMenu; ////////////////////////////////////////////////////////////////////////// // Flow Asset Instance List -class FLOWEDITOR_API SFlowAssetInstanceList final : public SCompoundWidget +class FLOWEDITOR_API SFlowAssetInstanceList : public SCompoundWidget { public: SLATE_BEGIN_ARGS(SFlowAssetInstanceList) {} @@ -60,7 +60,7 @@ struct FLOWEDITOR_API FFlowBreadcrumb {} }; -class FLOWEDITOR_API SFlowAssetBreadcrumb final : public SCompoundWidget +class FLOWEDITOR_API SFlowAssetBreadcrumb : public SCompoundWidget { public: SLATE_BEGIN_ARGS(SFlowAssetInstanceList) {} @@ -79,7 +79,7 @@ class FLOWEDITOR_API SFlowAssetBreadcrumb final : public SCompoundWidget ////////////////////////////////////////////////////////////////////////// // Flow Asset Toolbar -class FLOWEDITOR_API FFlowAssetToolbar final : public TSharedFromThis +class FLOWEDITOR_API FFlowAssetToolbar : public TSharedFromThis { public: explicit FFlowAssetToolbar(const TSharedPtr InAssetEditor, UToolMenu* ToolbarMenu); diff --git a/Source/FlowEditor/Public/FlowEditorCommands.h b/Source/FlowEditor/Public/FlowEditorCommands.h index 1a2446c8f..3ceff0032 100644 --- a/Source/FlowEditor/Public/FlowEditorCommands.h +++ b/Source/FlowEditor/Public/FlowEditorCommands.h @@ -7,7 +7,7 @@ #include "Framework/Commands/UICommandInfo.h" #include "Templates/SharedPointer.h" -class FLOWEDITOR_API FFlowToolbarCommands final : public TCommands +class FLOWEDITOR_API FFlowToolbarCommands : public TCommands { public: FFlowToolbarCommands(); @@ -20,7 +20,7 @@ class FLOWEDITOR_API FFlowToolbarCommands final : public TCommands +class FLOWEDITOR_API FFlowGraphCommands : public TCommands { public: FFlowGraphCommands(); @@ -54,7 +54,7 @@ class FFlowGraphCommands final : public TCommands }; /** Handles spawning nodes by keyboard shortcut */ -class FFlowSpawnNodeCommands : public TCommands +class FLOWEDITOR_API FFlowSpawnNodeCommands : public TCommands { public: FFlowSpawnNodeCommands(); diff --git a/Source/FlowEditor/Public/Graph/FlowGraph.h b/Source/FlowEditor/Public/Graph/FlowGraph.h index d46cea58f..de76d7949 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraph.h +++ b/Source/FlowEditor/Public/Graph/FlowGraph.h @@ -7,7 +7,7 @@ #include "FlowAsset.h" #include "FlowGraph.generated.h" -class FLOWEDITOR_API FFlowGraphInterface final : public IFlowGraphInterface +class FLOWEDITOR_API FFlowGraphInterface : public IFlowGraphInterface { public: virtual ~FFlowGraphInterface() override {} diff --git a/Source/FlowEditor/Public/Graph/FlowGraphConnectionDrawingPolicy.h b/Source/FlowEditor/Public/Graph/FlowGraphConnectionDrawingPolicy.h index c940adfc4..f62db79ab 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphConnectionDrawingPolicy.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphConnectionDrawingPolicy.h @@ -12,7 +12,7 @@ enum class EFlowConnectionDrawType : uint8 Circuit }; -struct FFlowGraphConnectionDrawingPolicyFactory : public FGraphPanelPinConnectionFactory +struct FLOWEDITOR_API FFlowGraphConnectionDrawingPolicyFactory : public FGraphPanelPinConnectionFactory { virtual ~FFlowGraphConnectionDrawingPolicyFactory() override { diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h index 11ceb0619..6f3cb06b1 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h @@ -16,7 +16,7 @@ enum class EFlowNodeDoubleClickTarget : uint8 * */ UCLASS(Config = EditorPerProjectUserSettings, meta = (DisplayName = "Flow Graph")) -class UFlowGraphEditorSettings final : public UDeveloperSettings +class FLOWEDITOR_API UFlowGraphEditorSettings : public UDeveloperSettings { GENERATED_UCLASS_BODY() diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h index cbd5be68a..8f5b3fa49 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h @@ -12,7 +12,7 @@ * */ UCLASS(Config = Editor, defaultconfig, meta = (DisplayName = "Flow Graph")) -class UFlowGraphSettings final : public UDeveloperSettings +class FLOWEDITOR_API UFlowGraphSettings : public UDeveloperSettings { GENERATED_UCLASS_BODY() diff --git a/Source/FlowEditor/Private/MovieScene/FlowSection.h b/Source/FlowEditor/Public/MovieScene/FlowSection.h similarity index 83% rename from Source/FlowEditor/Private/MovieScene/FlowSection.h rename to Source/FlowEditor/Public/MovieScene/FlowSection.h index e1202d61e..b630ca77c 100644 --- a/Source/FlowEditor/Private/MovieScene/FlowSection.h +++ b/Source/FlowEditor/Public/MovieScene/FlowSection.h @@ -7,7 +7,7 @@ class FSequencerSectionPainter; -class FFlowSectionBase : public FSequencerSection +class FLOWEDITOR_API FFlowSectionBase : public FSequencerSection { public: FFlowSectionBase(UMovieSceneSection& InSectionObject, TWeakPtr InSequencer) @@ -26,7 +26,7 @@ class FFlowSectionBase : public FSequencerSection /** * An implementation of flow sections. */ -class FFlowSection final : public FFlowSectionBase +class FLOWEDITOR_API FFlowSection : public FFlowSectionBase { public: FFlowSection(UMovieSceneSection& InSectionObject, TWeakPtr InSequencer) @@ -37,7 +37,7 @@ class FFlowSection final : public FFlowSectionBase virtual int32 OnPaintSection(FSequencerSectionPainter& Painter) const override; }; -class FFlowTriggerSection : public FFlowSectionBase +class FLOWEDITOR_API FFlowTriggerSection : public FFlowSectionBase { public: FFlowTriggerSection(UMovieSceneSection& InSectionObject, TWeakPtr InSequencer) @@ -48,7 +48,7 @@ class FFlowTriggerSection : public FFlowSectionBase virtual int32 OnPaintSection(FSequencerSectionPainter& Painter) const override; }; -class FFlowRepeaterSection : public FFlowSectionBase +class FLOWEDITOR_API FFlowRepeaterSection : public FFlowSectionBase { public: FFlowRepeaterSection(UMovieSceneSection& InSectionObject, TWeakPtr InSequencer) diff --git a/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.h b/Source/FlowEditor/Public/MovieScene/FlowTrackEditor.h similarity index 92% rename from Source/FlowEditor/Private/MovieScene/FlowTrackEditor.h rename to Source/FlowEditor/Public/MovieScene/FlowTrackEditor.h index 6e7fa7ed0..c6e32d5a0 100644 --- a/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.h +++ b/Source/FlowEditor/Public/MovieScene/FlowTrackEditor.h @@ -14,7 +14,7 @@ class FMenuBuilder; /** * A property track editor for named events. */ -class FFlowTrackEditor final : public FMovieSceneTrackEditor +class FLOWEDITOR_API FFlowTrackEditor : public FMovieSceneTrackEditor { public: /** @@ -47,7 +47,7 @@ class FFlowTrackEditor final : public FMovieSceneTrackEditor void AddFlowSubMenu(FMenuBuilder& MenuBuilder); /** Callback for executing the "Add Event Track" menu entry. */ - void HandleAddFlowTrackMenuEntryExecute(UClass* SectionType); + void HandleAddFlowTrackMenuEntryExecute(UClass* SectionType) const; void CreateNewSection(UMovieSceneTrack* Track, int32 RowIndex, UClass* SectionType, bool bSelect) const; }; diff --git a/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeBlueprint.h b/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeBlueprint.h index cb7552fa3..e8243cf42 100644 --- a/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeBlueprint.h +++ b/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeBlueprint.h @@ -4,7 +4,7 @@ #include "AssetTypeActions/AssetTypeActions_Blueprint.h" -class FLOWEDITOR_API FAssetTypeActions_FlowNodeBlueprint final : public FAssetTypeActions_Blueprint +class FLOWEDITOR_API FAssetTypeActions_FlowNodeBlueprint : public FAssetTypeActions_Blueprint { public: virtual FText GetName() const override; diff --git a/Source/FlowEditor/Public/Nodes/FlowNodeBlueprintFactory.h b/Source/FlowEditor/Public/Nodes/FlowNodeBlueprintFactory.h index cec3d3a25..d3b8a129e 100644 --- a/Source/FlowEditor/Public/Nodes/FlowNodeBlueprintFactory.h +++ b/Source/FlowEditor/Public/Nodes/FlowNodeBlueprintFactory.h @@ -5,8 +5,8 @@ #include "Factories/Factory.h" #include "FlowNodeBlueprintFactory.generated.h" -UCLASS(hidecategories=Object, MinimalAPI) -class UFlowNodeBlueprintFactory : public UFactory +UCLASS(hidecategories=Object) +class FLOWEDITOR_API UFlowNodeBlueprintFactory : public UFactory { GENERATED_UCLASS_BODY() From a38fe88f0615fa282b53dd4b9fffc0aa3751e9a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Fri, 6 Jan 2023 15:45:23 +0100 Subject: [PATCH 090/485] Moved all Details Customizations classes to a single folder --- .../FlowAssetDetails.cpp | 2 +- .../FlowNode_ComponentObserverDetails.cpp | 2 +- .../FlowNode_CustomInputDetails.cpp | 3 +-- .../FlowNode_CustomOutputDetails.cpp | 3 +-- .../FlowNode_Details.cpp | 2 +- .../FlowNode_PlayLevelSequenceDetails.cpp | 2 +- .../FlowEditor/Private/FlowEditorModule.cpp | 19 ++++++++++--------- .../DetailCustomizations}/FlowAssetDetails.h | 0 .../FlowNode_ComponentObserverDetails.h | 0 .../FlowNode_CustomInputDetails.h | 0 .../FlowNode_CustomOutputDetails.h | 0 .../DetailCustomizations}/FlowNode_Details.h | 0 .../FlowNode_PlayLevelSequenceDetails.h | 0 Source/FlowEditor/Public/FlowEditorModule.h | 2 +- 14 files changed, 17 insertions(+), 18 deletions(-) rename Source/FlowEditor/Private/{Asset => DetailCustomizations}/FlowAssetDetails.cpp (98%) rename Source/FlowEditor/Private/{Nodes/Customizations => DetailCustomizations}/FlowNode_ComponentObserverDetails.cpp (89%) rename Source/FlowEditor/Private/{Nodes/Customizations => DetailCustomizations}/FlowNode_CustomInputDetails.cpp (97%) rename Source/FlowEditor/Private/{Nodes/Customizations => DetailCustomizations}/FlowNode_CustomOutputDetails.cpp (97%) rename Source/FlowEditor/Private/{Nodes/Customizations => DetailCustomizations}/FlowNode_Details.cpp (87%) rename Source/FlowEditor/Private/{Nodes/Customizations => DetailCustomizations}/FlowNode_PlayLevelSequenceDetails.cpp (94%) rename Source/FlowEditor/{Private/Asset => Public/DetailCustomizations}/FlowAssetDetails.h (100%) rename Source/FlowEditor/{Private/Nodes/Customizations => Public/DetailCustomizations}/FlowNode_ComponentObserverDetails.h (100%) rename Source/FlowEditor/{Private/Nodes/Customizations => Public/DetailCustomizations}/FlowNode_CustomInputDetails.h (100%) rename Source/FlowEditor/{Private/Nodes/Customizations => Public/DetailCustomizations}/FlowNode_CustomOutputDetails.h (100%) rename Source/FlowEditor/{Private/Nodes/Customizations => Public/DetailCustomizations}/FlowNode_Details.h (100%) rename Source/FlowEditor/{Private/Nodes/Customizations => Public/DetailCustomizations}/FlowNode_PlayLevelSequenceDetails.h (100%) diff --git a/Source/FlowEditor/Private/Asset/FlowAssetDetails.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowAssetDetails.cpp similarity index 98% rename from Source/FlowEditor/Private/Asset/FlowAssetDetails.cpp rename to Source/FlowEditor/Private/DetailCustomizations/FlowAssetDetails.cpp index ef202ebaa..41e6b8861 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetDetails.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowAssetDetails.cpp @@ -1,6 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "FlowAssetDetails.h" +#include "DetailCustomizations/FlowAssetDetails.h" #include "FlowAsset.h" #include "Nodes/Route/FlowNode_SubGraph.h" diff --git a/Source/FlowEditor/Private/Nodes/Customizations/FlowNode_ComponentObserverDetails.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_ComponentObserverDetails.cpp similarity index 89% rename from Source/FlowEditor/Private/Nodes/Customizations/FlowNode_ComponentObserverDetails.cpp rename to Source/FlowEditor/Private/DetailCustomizations/FlowNode_ComponentObserverDetails.cpp index dec2f9222..297fb3afa 100644 --- a/Source/FlowEditor/Private/Nodes/Customizations/FlowNode_ComponentObserverDetails.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_ComponentObserverDetails.cpp @@ -1,6 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "FlowNode_ComponentObserverDetails.h" +#include "DetailCustomizations/FlowNode_ComponentObserverDetails.h" #include "Nodes/World/FlowNode_ComponentObserver.h" #include "DetailCategoryBuilder.h" diff --git a/Source/FlowEditor/Private/Nodes/Customizations/FlowNode_CustomInputDetails.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomInputDetails.cpp similarity index 97% rename from Source/FlowEditor/Private/Nodes/Customizations/FlowNode_CustomInputDetails.cpp rename to Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomInputDetails.cpp index 4892202b3..ca37aa4fb 100644 --- a/Source/FlowEditor/Private/Nodes/Customizations/FlowNode_CustomInputDetails.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomInputDetails.cpp @@ -1,13 +1,12 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "FlowNode_CustomInputDetails.h" +#include "DetailCustomizations/FlowNode_CustomInputDetails.h" #include "FlowAsset.h" #include "Nodes/Route/FlowNode_CustomInput.h" #include "DetailCategoryBuilder.h" #include "DetailWidgetRow.h" #include "PropertyEditing.h" -#include "UnrealEd.h" #include "Widgets/Input/SComboBox.h" #include "Widgets/Text/STextBlock.h" diff --git a/Source/FlowEditor/Private/Nodes/Customizations/FlowNode_CustomOutputDetails.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomOutputDetails.cpp similarity index 97% rename from Source/FlowEditor/Private/Nodes/Customizations/FlowNode_CustomOutputDetails.cpp rename to Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomOutputDetails.cpp index bc7923bb2..d2ab37aee 100644 --- a/Source/FlowEditor/Private/Nodes/Customizations/FlowNode_CustomOutputDetails.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomOutputDetails.cpp @@ -1,13 +1,12 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "FlowNode_CustomOutputDetails.h" +#include "DetailCustomizations/FlowNode_CustomOutputDetails.h" #include "FlowAsset.h" #include "Nodes/Route/FlowNode_CustomOutput.h" #include "DetailCategoryBuilder.h" #include "DetailWidgetRow.h" #include "PropertyEditing.h" -#include "UnrealEd.h" #include "Widgets/Input/SComboBox.h" #include "Widgets/Text/STextBlock.h" diff --git a/Source/FlowEditor/Private/Nodes/Customizations/FlowNode_Details.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_Details.cpp similarity index 87% rename from Source/FlowEditor/Private/Nodes/Customizations/FlowNode_Details.cpp rename to Source/FlowEditor/Private/DetailCustomizations/FlowNode_Details.cpp index ae9ddd072..98074032c 100644 --- a/Source/FlowEditor/Private/Nodes/Customizations/FlowNode_Details.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_Details.cpp @@ -1,6 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "FlowNode_Details.h" +#include "DetailCustomizations/FlowNode_Details.h" #include "PropertyEditing.h" void FFlowNode_Details::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) diff --git a/Source/FlowEditor/Private/Nodes/Customizations/FlowNode_PlayLevelSequenceDetails.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_PlayLevelSequenceDetails.cpp similarity index 94% rename from Source/FlowEditor/Private/Nodes/Customizations/FlowNode_PlayLevelSequenceDetails.cpp rename to Source/FlowEditor/Private/DetailCustomizations/FlowNode_PlayLevelSequenceDetails.cpp index 3f1374fed..76d5b1f4b 100644 --- a/Source/FlowEditor/Private/Nodes/Customizations/FlowNode_PlayLevelSequenceDetails.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_PlayLevelSequenceDetails.cpp @@ -1,6 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "FlowNode_PlayLevelSequenceDetails.h" +#include "DetailCustomizations/FlowNode_PlayLevelSequenceDetails.h" #include "Nodes/World/FlowNode_PlayLevelSequence.h" #include "DetailCategoryBuilder.h" diff --git a/Source/FlowEditor/Private/FlowEditorModule.cpp b/Source/FlowEditor/Private/FlowEditorModule.cpp index 7c66344c0..4c91cd4f9 100644 --- a/Source/FlowEditor/Private/FlowEditorModule.cpp +++ b/Source/FlowEditor/Private/FlowEditorModule.cpp @@ -4,7 +4,6 @@ #include "FlowEditorStyle.h" #include "Asset/AssetTypeActions_FlowAsset.h" -#include "Asset/FlowAssetDetails.h" #include "Asset/FlowAssetEditor.h" #include "Asset/FlowAssetIndexer.h" #include "Graph/FlowGraphConnectionDrawingPolicy.h" @@ -12,14 +11,16 @@ #include "Utils/SLevelEditorFlow.h" #include "MovieScene/FlowTrackEditor.h" #include "Nodes/AssetTypeActions_FlowNodeBlueprint.h" -#include "Nodes/Customizations/FlowNode_Details.h" -#include "Nodes/Customizations/FlowNode_ComponentObserverDetails.h" -#include "Nodes/Customizations/FlowNode_CustomInputDetails.h" -#include "Nodes/Customizations/FlowNode_CustomOutputDetails.h" -#include "Nodes/Customizations/FlowNode_PlayLevelSequenceDetails.h" #include "Pins/SFlowInputPinHandle.h" #include "Pins/SFlowOutputPinHandle.h" +#include "DetailCustomizations/FlowAssetDetails.h" +#include "DetailCustomizations/FlowNode_Details.h" +#include "DetailCustomizations/FlowNode_ComponentObserverDetails.h" +#include "DetailCustomizations/FlowNode_CustomInputDetails.h" +#include "DetailCustomizations/FlowNode_CustomOutputDetails.h" +#include "DetailCustomizations/FlowNode_PlayLevelSequenceDetails.h" + #include "FlowAsset.h" #include "Nodes/Route/FlowNode_CustomInput.h" #include "Nodes/Route/FlowNode_CustomOutput.h" @@ -30,7 +31,7 @@ #include "EdGraphUtilities.h" #include "IAssetSearchModule.h" #include "Framework/MultiBox/MultiBoxBuilder.h" -#include "ISequencerChannelInterface.h" +#include "ISequencerChannelInterface.h" // ignore Rider's false "unused include" warning #include "ISequencerModule.h" #include "LevelEditor.h" #include "Modules/ModuleManager.h" @@ -121,7 +122,7 @@ void FFlowEditorModule::RegisterAssets() // try to merge asset category with a built-in one { - FText AssetCategoryText = UFlowGraphSettings::Get()->FlowAssetCategoryName; + const FText AssetCategoryText = UFlowGraphSettings::Get()->FlowAssetCategoryName; // Find matching built-in category if (!AssetCategoryText.IsEmpty()) @@ -186,7 +187,7 @@ void FFlowEditorModule::RegisterCustomClassLayout(const TSubclassOf Cla } } -void FFlowEditorModule::ModulesChangesCallback(FName ModuleName, EModuleChangeReason ReasonForChange) +void FFlowEditorModule::ModulesChangesCallback(const FName ModuleName, const EModuleChangeReason ReasonForChange) const { if (ReasonForChange == EModuleChangeReason::ModuleLoaded && ModuleName == AssetSearchModuleName) { diff --git a/Source/FlowEditor/Private/Asset/FlowAssetDetails.h b/Source/FlowEditor/Public/DetailCustomizations/FlowAssetDetails.h similarity index 100% rename from Source/FlowEditor/Private/Asset/FlowAssetDetails.h rename to Source/FlowEditor/Public/DetailCustomizations/FlowAssetDetails.h diff --git a/Source/FlowEditor/Private/Nodes/Customizations/FlowNode_ComponentObserverDetails.h b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_ComponentObserverDetails.h similarity index 100% rename from Source/FlowEditor/Private/Nodes/Customizations/FlowNode_ComponentObserverDetails.h rename to Source/FlowEditor/Public/DetailCustomizations/FlowNode_ComponentObserverDetails.h diff --git a/Source/FlowEditor/Private/Nodes/Customizations/FlowNode_CustomInputDetails.h b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomInputDetails.h similarity index 100% rename from Source/FlowEditor/Private/Nodes/Customizations/FlowNode_CustomInputDetails.h rename to Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomInputDetails.h diff --git a/Source/FlowEditor/Private/Nodes/Customizations/FlowNode_CustomOutputDetails.h b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomOutputDetails.h similarity index 100% rename from Source/FlowEditor/Private/Nodes/Customizations/FlowNode_CustomOutputDetails.h rename to Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomOutputDetails.h diff --git a/Source/FlowEditor/Private/Nodes/Customizations/FlowNode_Details.h b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_Details.h similarity index 100% rename from Source/FlowEditor/Private/Nodes/Customizations/FlowNode_Details.h rename to Source/FlowEditor/Public/DetailCustomizations/FlowNode_Details.h diff --git a/Source/FlowEditor/Private/Nodes/Customizations/FlowNode_PlayLevelSequenceDetails.h b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_PlayLevelSequenceDetails.h similarity index 100% rename from Source/FlowEditor/Private/Nodes/Customizations/FlowNode_PlayLevelSequenceDetails.h rename to Source/FlowEditor/Public/DetailCustomizations/FlowNode_PlayLevelSequenceDetails.h diff --git a/Source/FlowEditor/Public/FlowEditorModule.h b/Source/FlowEditor/Public/FlowEditorModule.h index f1487622d..129f2c679 100644 --- a/Source/FlowEditor/Public/FlowEditorModule.h +++ b/Source/FlowEditor/Public/FlowEditorModule.h @@ -41,7 +41,7 @@ class FLOWEDITOR_API FFlowEditorModule : public IModuleInterface FDelegateHandle ModulesChangedHandle; private: - void ModulesChangesCallback(FName ModuleName, EModuleChangeReason ReasonForChange); + void ModulesChangesCallback(FName ModuleName, EModuleChangeReason ReasonForChange) const; void RegisterAssetIndexers() const; void CreateFlowToolbar(FToolBarBuilder& ToolbarBuilder) const; From 7f0a7855c8fba92c991be8fbc9569b9ac6bc1ec8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Fri, 6 Jan 2023 15:54:43 +0100 Subject: [PATCH 091/485] added LogWarning method, renamed LogMessage to LogNote --- Source/Flow/Private/Nodes/FlowNode.cpp | 44 ++++++++++++++++++++------ Source/Flow/Public/Nodes/FlowNode.h | 11 ++++++- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 32f7e46ef..7c6272285 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -415,10 +415,10 @@ void UFlowNode::TriggerInput(const FName& PinName, const EFlowPinActivationType ExecuteInput(PinName); break; case EFlowSignalMode::Disabled: - LogMessage(FString::Printf(TEXT("Node disabled while triggering input %s"), *PinName.ToString())); + LogNote(FString::Printf(TEXT("Node disabled while triggering input %s"), *PinName.ToString())); break; case EFlowSignalMode::PassThrough: - LogMessage(FString::Printf(TEXT("Signal pass-through on triggering input %s"), *PinName.ToString())); + LogNote(FString::Printf(TEXT("Signal pass-through on triggering input %s"), *PinName.ToString())); OnPassThrough(); break; default: ; @@ -653,8 +653,7 @@ FString UFlowNode::GetProgressAsString(float Value) void UFlowNode::LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType) { #if !UE_BUILD_SHIPPING - const FString TemplatePath = GetFlowAsset()->TemplateAsset->GetPathName(); - Message += TEXT(" --- node ") + GetName() + TEXT(", asset ") + FPaths::GetPath(TemplatePath) / FPaths::GetBaseFilename(TemplatePath); + BuildMessage(Message); // OnScreen Message if (OnScreenMessageType == EFlowOnScreenMessageType::Permanent) @@ -692,11 +691,30 @@ void UFlowNode::LogError(FString Message, const EFlowOnScreenMessageType OnScree #endif } -void UFlowNode::LogMessage(FString Message) +void UFlowNode::LogWarning(FString Message) { #if !UE_BUILD_SHIPPING - const FString TemplatePath = GetFlowAsset()->TemplateAsset->GetPathName(); - Message += TEXT(" --- node ") + GetName() + TEXT(", asset ") + FPaths::GetPath(TemplatePath) / FPaths::GetBaseFilename(TemplatePath); + BuildMessage(Message); + +#if WITH_EDITOR + // Message Log - not yet functional + { + Log.Warning(*Message, GetGraphNode()); + + FMessageLog MessageLog("FlowGraph"); + MessageLog.AddMessages(Log.Messages); + } +#endif + + // Output Log + UE_LOG(LogFlow, Warning, TEXT("%s"), *Message); +#endif +} + +void UFlowNode::LogNote(FString Message) +{ +#if !UE_BUILD_SHIPPING + BuildMessage(Message); #if WITH_EDITOR // Message Log - not yet functional @@ -713,6 +731,14 @@ void UFlowNode::LogMessage(FString Message) #endif } +#if !UE_BUILD_SHIPPING +void UFlowNode::BuildMessage(FString& Message) const +{ + const FString TemplatePath = GetFlowAsset()->TemplateAsset->GetPathName(); + Message.Append(TEXT(" --- node ")).Append(GetName()).Append(TEXT(", asset ")).Append(FPaths::GetPath(TemplatePath) / FPaths::GetBaseFilename(TemplatePath)); +} +#endif + void UFlowNode::SaveInstance(FFlowNodeSaveData& NodeRecord) { NodeRecord.NodeGuid = NodeGuid; @@ -741,11 +767,11 @@ void UFlowNode::LoadInstance(const FFlowNodeSaveData& NodeRecord) break; case EFlowSignalMode::Disabled: // designer doesn't want to execute this node's logic at all, so we kill it - LogMessage(TEXT("Signal disabled while loading Flow Node from SaveGame")); + LogNote(TEXT("Signal disabled while loading Flow Node from SaveGame")); Finish(); break; case EFlowSignalMode::PassThrough: - LogMessage(TEXT("Signal pass-through on loading Flow Node from SaveGame")); + LogNote(TEXT("Signal pass-through on loading Flow Node from SaveGame")); OnPassThrough(); break; default: ; diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index bfe1781b8..eadf41ddf 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -390,8 +390,17 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte void LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType = EFlowOnScreenMessageType::Permanent); UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) - void LogMessage(FString Message); + void LogWarning(FString Message); + UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) + void LogNote(FString Message); + +#if !UE_BUILD_SHIPPING +private: + void BuildMessage(FString& Message) const; +#endif + +public: UFUNCTION(BlueprintCallable, Category = "FlowNode") void SaveInstance(FFlowNodeSaveData& NodeRecord); From 13126d03b72fc06abe85afde7159b2bdf7fd19d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Fri, 6 Jan 2023 16:11:44 +0100 Subject: [PATCH 092/485] addressed static analysis warning and fixes - Editor module --- Source/FlowEditor/FlowEditor.Build.cs | 2 +- .../Private/Asset/FlowAssetFactory.cpp | 4 ++-- .../Private/Asset/FlowAssetToolbar.cpp | 13 ++++++------- .../Graph/FlowGraphConnectionDrawingPolicy.cpp | 18 +++++++++--------- .../Private/Graph/FlowGraphUtils.cpp | 2 +- .../Private/Nodes/FlowNodeBlueprintFactory.cpp | 2 +- .../FlowEditor/Public/Asset/FlowAssetToolbar.h | 1 - .../FlowEditor/Public/Asset/FlowDiffControl.h | 1 - .../Graph/FlowGraphConnectionDrawingPolicy.h | 2 +- .../Public/Graph/Widgets/SFlowGraphNode.h | 2 +- 10 files changed, 22 insertions(+), 25 deletions(-) diff --git a/Source/FlowEditor/FlowEditor.Build.cs b/Source/FlowEditor/FlowEditor.Build.cs index 07c98553a..4da586c31 100644 --- a/Source/FlowEditor/FlowEditor.Build.cs +++ b/Source/FlowEditor/FlowEditor.Build.cs @@ -4,7 +4,7 @@ public class FlowEditor : ModuleRules { - public FlowEditor(ReadOnlyTargetRules Target) : base(Target) + public FlowEditor(ReadOnlyTargetRules target) : base(target) { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; diff --git a/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp b/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp index 03f29d11d..f9b25fc65 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp @@ -30,7 +30,7 @@ class FAssetClassParentFilter : public IClassViewerFilter /** Disallow blueprint base classes. */ bool bDisallowBlueprintBase; - virtual bool IsClassAllowed(const FClassViewerInitializationOptions& InInitOptions, const UClass* InClass, TSharedRef InFilterFuncs) override + virtual bool IsClassAllowed(const FClassViewerInitializationOptions& InInitOptions, const UClass* InClass, const TSharedRef InFilterFuncs) override { const bool bAllowed = !InClass->HasAnyClassFlags(DisallowedClassFlags) && InFilterFuncs->IfInChildOfClassesSet(AllowedChildrenOfClasses, InClass) != EFilterReturn::Failed; @@ -102,7 +102,7 @@ bool UFlowAssetFactory::ConfigureProperties() UObject* UFlowAssetFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) { - UFlowAsset* NewFlow = nullptr; + UFlowAsset* NewFlow; if (AssetClass != nullptr) { NewFlow = NewObject(InParent, AssetClass, Name, Flags | RF_Transactional, Context); diff --git a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp index f95db4098..4c7f26422 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp @@ -158,8 +158,12 @@ void SFlowAssetBreadcrumb::Construct(const FArguments& InArgs, const TWeakObject for (UFlowAsset* Instance : InstancesFromRoot) { - TAttribute CrumbNameAttribute = MakeAttributeSP(this, &SFlowAssetBreadcrumb::GetBreadcrumbText, Instance); - BreadcrumbTrail->PushCrumb(CrumbNameAttribute, FFlowBreadcrumb(Instance)); + TAttribute CrumbText = MakeAttributeLambda([Instance]() + { + return Instance ? FText::FromName(Instance->GetDisplayName()) : FText(); + }); + + BreadcrumbTrail->PushCrumb(CrumbText, FFlowBreadcrumb(Instance)); } } } @@ -174,11 +178,6 @@ void SFlowAssetBreadcrumb::OnCrumbClicked(const FFlowBreadcrumb& Item) const } } -FText SFlowAssetBreadcrumb::GetBreadcrumbText(const TWeakObjectPtr FlowInstance) const -{ - return FlowInstance.IsValid() ? FText::FromName(FlowInstance->GetDisplayName()) : FText(); -} - ////////////////////////////////////////////////////////////////////////// // Flow Asset Toolbar diff --git a/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp b/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp index 78397fd7f..78ee5c179 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp @@ -289,7 +289,7 @@ FVector2D FFlowGraphConnectionDrawingPolicy::GetControlPoint(const FVector2D& So bool FFlowGraphConnectionDrawingPolicy::ShouldChangeTangentForReroute(UFlowGraphNode_Reroute* Reroute) { - if (bool* pResult = RerouteToReversedDirectionMap.Find(Reroute)) + if (const bool* pResult = RerouteToReversedDirectionMap.Find(Reroute)) { return *pResult; } @@ -300,9 +300,9 @@ bool FFlowGraphConnectionDrawingPolicy::ShouldChangeTangentForReroute(UFlowGraph FVector2D AverageLeftPin; FVector2D AverageRightPin; FVector2D CenterPin; - bool bCenterValid = Reroute->OutputPins.Num() == 0 ? false : FindPinCenter(Reroute->OutputPins[0], /*out*/ CenterPin); - bool bLeftValid = GetAverageConnectedPosition(Reroute, EGPD_Input, /*out*/ AverageLeftPin); - bool bRightValid = GetAverageConnectedPosition(Reroute, EGPD_Output, /*out*/ AverageRightPin); + const bool bCenterValid = Reroute->OutputPins.Num() == 0 ? false : FindPinCenter(Reroute->OutputPins[0], /*out*/ CenterPin); + const bool bLeftValid = GetAverageConnectedPosition(Reroute, EGPD_Input, /*out*/ AverageLeftPin); + const bool bRightValid = GetAverageConnectedPosition(Reroute, EGPD_Output, /*out*/ AverageRightPin); if (bLeftValid && bRightValid) { @@ -326,13 +326,13 @@ bool FFlowGraphConnectionDrawingPolicy::ShouldChangeTangentForReroute(UFlowGraph } } -bool FFlowGraphConnectionDrawingPolicy::FindPinCenter(UEdGraphPin* Pin, FVector2D& OutCenter) const +bool FFlowGraphConnectionDrawingPolicy::FindPinCenter(const UEdGraphPin* Pin, FVector2D& OutCenter) const { - if (const TSharedPtr* pPinWidget = PinToPinWidgetMap.Find(Pin)) + if (const TSharedPtr* PinWidget = PinToPinWidgetMap.Find(Pin)) { - if (FArrangedWidget* pPinEntry = PinGeometries->Find((*pPinWidget).ToSharedRef())) + if (const FArrangedWidget* PinEntry = PinGeometries->Find((*PinWidget).ToSharedRef())) { - OutCenter = FGeometryHelper::CenterOf(pPinEntry->Geometry); + OutCenter = FGeometryHelper::CenterOf(PinEntry->Geometry); return true; } } @@ -351,7 +351,7 @@ bool FFlowGraphConnectionDrawingPolicy::GetAverageConnectedPosition(UFlowGraphNo } UEdGraphPin* Pin = (Direction == EGPD_Input) ? Reroute->InputPins[0] : Reroute->OutputPins[0]; - for (UEdGraphPin* LinkedPin : Pin->LinkedTo) + for (const UEdGraphPin* LinkedPin : Pin->LinkedTo) { FVector2D CenterPoint; if (FindPinCenter(LinkedPin, /*out*/ CenterPoint)) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp b/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp index 81cecdd66..b00e6a898 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp @@ -13,7 +13,7 @@ TSharedPtr FFlowGraphUtils::GetFlowAssetEditor(const UObject* check(ObjectToFocusOn); TSharedPtr FlowAssetEditor; - if (UFlowAsset* FlowAsset = Cast(ObjectToFocusOn)->GetFlowAsset()) + if (const UFlowAsset* FlowAsset = Cast(ObjectToFocusOn)->GetFlowAsset()) { const TSharedPtr FoundAssetEditor = FToolkitManager::Get().FindEditorForAsset(FlowAsset); if (FoundAssetEditor.IsValid()) diff --git a/Source/FlowEditor/Private/Nodes/FlowNodeBlueprintFactory.cpp b/Source/FlowEditor/Private/Nodes/FlowNodeBlueprintFactory.cpp index cf7f422ae..751c8e6ca 100644 --- a/Source/FlowEditor/Private/Nodes/FlowNodeBlueprintFactory.cpp +++ b/Source/FlowEditor/Private/Nodes/FlowNodeBlueprintFactory.cpp @@ -261,7 +261,7 @@ UObject* UFlowNodeBlueprintFactory::FactoryCreateNew(UClass* Class, UObject* InP if (NewBP && NewBP->UbergraphPages.Num() > 0) { - UBlueprintEditorSettings* Settings = GetMutableDefault(); + const UBlueprintEditorSettings* Settings = GetMutableDefault(); if(Settings && Settings->bSpawnDefaultBlueprintNodes) { int32 NodePositionY = 0; diff --git a/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h b/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h index 7a9c954ee..0739cb875 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h @@ -70,7 +70,6 @@ class FLOWEDITOR_API SFlowAssetBreadcrumb : public SCompoundWidget private: void OnCrumbClicked(const FFlowBreadcrumb& Item) const; - FText GetBreadcrumbText(const TWeakObjectPtr FlowInstance) const; TWeakObjectPtr TemplateAsset; TSharedPtr> BreadcrumbTrail; diff --git a/Source/FlowEditor/Public/Asset/FlowDiffControl.h b/Source/FlowEditor/Public/Asset/FlowDiffControl.h index 0d0bba5d9..410aaa083 100644 --- a/Source/FlowEditor/Public/Asset/FlowDiffControl.h +++ b/Source/FlowEditor/Public/Asset/FlowDiffControl.h @@ -2,7 +2,6 @@ #pragma once -#include "DetailsDiff.h" #include "DiffResults.h" #include "IAssetTypeActions.h" #include "Kismet/Private/DiffControl.h" diff --git a/Source/FlowEditor/Public/Graph/FlowGraphConnectionDrawingPolicy.h b/Source/FlowEditor/Public/Graph/FlowGraphConnectionDrawingPolicy.h index f62db79ab..410a78ae7 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphConnectionDrawingPolicy.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphConnectionDrawingPolicy.h @@ -65,6 +65,6 @@ class FLOWEDITOR_API FFlowGraphConnectionDrawingPolicy : public FConnectionDrawi static FVector2D GetControlPoint(const FVector2D& Source, const FVector2D& Target); bool ShouldChangeTangentForReroute(class UFlowGraphNode_Reroute* Reroute); - bool FindPinCenter(UEdGraphPin* Pin, FVector2D& OutCenter) const; + bool FindPinCenter(const UEdGraphPin* Pin, FVector2D& OutCenter) const; bool GetAverageConnectedPosition(class UFlowGraphNode_Reroute* Reroute, EEdGraphPinDirection Direction, FVector2D& OutPos) const; }; diff --git a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h index 3d50e872e..cc33a3e78 100644 --- a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h @@ -43,7 +43,7 @@ class FLOWEDITOR_API SFlowGraphNode : public SGraphNode virtual TSharedRef CreateNodeContentArea() override; virtual const FSlateBrush* GetNodeBodyBrush() const override; - // purposely overriden non-virtual methods, avoiding engine modification + // purposely overriden non-virtual methods, added PR #9791 to made these methods virtual: https://github.com/EpicGames/UnrealEngine/pull/9791 FSlateColor GetNodeTitleColor() const; FSlateColor GetNodeBodyColor() const; FSlateColor GetNodeTitleIconColor() const; From 5312b6d5b338924f8a84d7a29ce2af0accc2f078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Fri, 6 Jan 2023 16:12:46 +0100 Subject: [PATCH 093/485] autoformat --- Source/Flow/Flow.Build.cs | 38 +++++------ Source/FlowEditor/FlowEditor.Build.cs | 92 +++++++++++++-------------- 2 files changed, 65 insertions(+), 65 deletions(-) diff --git a/Source/Flow/Flow.Build.cs b/Source/Flow/Flow.Build.cs index 62f81ef48..5896761f9 100644 --- a/Source/Flow/Flow.Build.cs +++ b/Source/Flow/Flow.Build.cs @@ -4,35 +4,35 @@ public class Flow : ModuleRules { - public Flow(ReadOnlyTargetRules Target) : base(Target) + public Flow(ReadOnlyTargetRules target) : base(target) { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - PublicDependencyModuleNames.AddRange(new[] + PublicDependencyModuleNames.AddRange(new[] { "LevelSequence" }); - - PrivateDependencyModuleNames.AddRange(new[] + + PrivateDependencyModuleNames.AddRange(new[] { - "Core", + "Core", "CoreUObject", - "DeveloperSettings", + "DeveloperSettings", "Engine", - "GameplayTags", + "GameplayTags", "MovieScene", "MovieSceneTracks", - "Slate", - "SlateCore" + "Slate", + "SlateCore" }); - if (Target.Type == TargetType.Editor) - { - PublicDependencyModuleNames.AddRange(new[] - { - "MessageLog", - "UnrealEd" - }); - } - } -} + if (target.Type == TargetType.Editor) + { + PublicDependencyModuleNames.AddRange(new[] + { + "MessageLog", + "UnrealEd" + }); + } + } +} \ No newline at end of file diff --git a/Source/FlowEditor/FlowEditor.Build.cs b/Source/FlowEditor/FlowEditor.Build.cs index 4da586c31..4fdf98210 100644 --- a/Source/FlowEditor/FlowEditor.Build.cs +++ b/Source/FlowEditor/FlowEditor.Build.cs @@ -4,51 +4,51 @@ public class FlowEditor : ModuleRules { - public FlowEditor(ReadOnlyTargetRules target) : base(target) - { - PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + public FlowEditor(ReadOnlyTargetRules target) : base(target) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - PublicDependencyModuleNames.AddRange(new[] - { - "Flow", - "MessageLog" - }); + PublicDependencyModuleNames.AddRange(new[] + { + "Flow", + "MessageLog" + }); - PrivateDependencyModuleNames.AddRange(new[] - { - "ApplicationCore", - "AssetSearch", - "AssetTools", - "BlueprintGraph", - "ClassViewer", - "ContentBrowser", - "Core", - "CoreUObject", - "DetailCustomizations", - "DeveloperSettings", - "EditorFramework", - "EditorStyle", - "Engine", - "GraphEditor", - "InputCore", - "Json", - "JsonUtilities", - "Kismet", - "KismetWidgets", - "LevelEditor", - "LevelSequence", - "MovieScene", - "MovieSceneTracks", - "MovieSceneTools", - "Projects", - "PropertyEditor", - "RenderCore", - "Sequencer", - "Slate", - "SlateCore", - "SourceControl", - "ToolMenus", - "UnrealEd" - }); - } -} + PrivateDependencyModuleNames.AddRange(new[] + { + "ApplicationCore", + "AssetSearch", + "AssetTools", + "BlueprintGraph", + "ClassViewer", + "ContentBrowser", + "Core", + "CoreUObject", + "DetailCustomizations", + "DeveloperSettings", + "EditorFramework", + "EditorStyle", + "Engine", + "GraphEditor", + "InputCore", + "Json", + "JsonUtilities", + "Kismet", + "KismetWidgets", + "LevelEditor", + "LevelSequence", + "MovieScene", + "MovieSceneTracks", + "MovieSceneTools", + "Projects", + "PropertyEditor", + "RenderCore", + "Sequencer", + "Slate", + "SlateCore", + "SourceControl", + "ToolMenus", + "UnrealEd" + }); + } +} \ No newline at end of file From 07811d2186d50e02462c03fbe19bd470ad91f193 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Fri, 6 Jan 2023 16:19:21 +0100 Subject: [PATCH 094/485] addressed static analysis warning and fixes - Runtime module --- Source/Flow/Private/FlowSubsystem.cpp | 1 - Source/Flow/Private/FlowWorldSettings.cpp | 1 - Source/Flow/Private/MovieScene/MovieSceneFlowTriggerSection.cpp | 2 +- Source/Flow/Public/FlowSave.h | 1 - Source/Flow/Public/FlowTypes.h | 2 -- 5 files changed, 1 insertion(+), 6 deletions(-) diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index 397394184..405673b25 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -11,7 +11,6 @@ #include "Engine/GameInstance.h" #include "Engine/World.h" -#include "Misc/FileHelper.h" #include "Misc/Paths.h" #include "UObject/UObjectHash.h" diff --git a/Source/Flow/Private/FlowWorldSettings.cpp b/Source/Flow/Private/FlowWorldSettings.cpp index 447fafdbf..b0f03d6ec 100644 --- a/Source/Flow/Private/FlowWorldSettings.cpp +++ b/Source/Flow/Private/FlowWorldSettings.cpp @@ -2,7 +2,6 @@ #include "FlowWorldSettings.h" #include "FlowComponent.h" -#include "FlowSubsystem.h" AFlowWorldSettings::AFlowWorldSettings(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) diff --git a/Source/Flow/Private/MovieScene/MovieSceneFlowTriggerSection.cpp b/Source/Flow/Private/MovieScene/MovieSceneFlowTriggerSection.cpp index d1cc40a7e..bc86dd921 100644 --- a/Source/Flow/Private/MovieScene/MovieSceneFlowTriggerSection.cpp +++ b/Source/Flow/Private/MovieScene/MovieSceneFlowTriggerSection.cpp @@ -8,7 +8,7 @@ UMovieSceneFlowTriggerSection::UMovieSceneFlowTriggerSection(const FObjectInitia : Super(ObjInit) { bSupportsInfiniteRange = true; - SetRange(TRange::All()); + UMovieSceneSection::SetRange(TRange::All()); #if WITH_EDITOR ChannelProxy = MakeShared(StringChannel, FMovieSceneChannelMetaData(), TMovieSceneExternalValue::Make()); diff --git a/Source/Flow/Public/FlowSave.h b/Source/Flow/Public/FlowSave.h index 1c2a07735..f79711f9c 100644 --- a/Source/Flow/Public/FlowSave.h +++ b/Source/Flow/Public/FlowSave.h @@ -3,7 +3,6 @@ #pragma once #include "GameFramework/SaveGame.h" -#include "Serialization/BufferArchive.h" #include "Serialization/ObjectAndNameAsStringProxyArchive.h" #include "FlowSave.generated.h" diff --git a/Source/Flow/Public/FlowTypes.h b/Source/Flow/Public/FlowTypes.h index 235cc366a..a1be09b7b 100644 --- a/Source/Flow/Public/FlowTypes.h +++ b/Source/Flow/Public/FlowTypes.h @@ -3,8 +3,6 @@ #pragma once #include "GameplayTagContainer.h" - -#include "FlowSave.h" #include "FlowTypes.generated.h" #if WITH_EDITORONLY_DATA From 04d09e6a433aa79d94402c1575c96e2d6f79768f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 8 Jan 2023 18:48:36 +0100 Subject: [PATCH 095/485] importing blueprint graph into Flow Graph - basic functionality - Creating Flow Graph asset in the same folder as selected blueprint. - Simple graph iteration attempting to recreate Flow Graph with nodes matching UFunctions. - Transferring blueprint input pin values to Flow Node properties, names need to match. --- Flow.uplugin | 4 + Source/Flow/Public/FlowSettings.h | 6 +- Source/Flow/Public/Nodes/FlowPin.h | 19 +- Source/FlowEditor/FlowEditor.Build.cs | 2 + .../Private/Asset/FlowAssetFactory.cpp | 17 +- .../Private/Asset/FlowImportUtils.cpp | 243 ++++++++++++++++++ .../Private/Graph/FlowGraphSchema_Actions.cpp | 42 ++- .../FlowEditor/Public/Asset/FlowImportUtils.h | 22 ++ .../FlowEditor/Public/Graph/FlowGraphSchema.h | 4 +- .../Public/Graph/FlowGraphSchema_Actions.h | 1 + 10 files changed, 346 insertions(+), 14 deletions(-) create mode 100644 Source/FlowEditor/Private/Asset/FlowImportUtils.cpp create mode 100644 Source/FlowEditor/Public/Asset/FlowImportUtils.h diff --git a/Flow.uplugin b/Flow.uplugin index eaa712ff9..4879ba3f1 100644 --- a/Flow.uplugin +++ b/Flow.uplugin @@ -30,6 +30,10 @@ { "Name": "AssetSearch", "Enabled": true + }, + { + "Name": "EditorScriptingUtilities", + "Enabled": true } ] } \ No newline at end of file diff --git a/Source/Flow/Public/FlowSettings.h b/Source/Flow/Public/FlowSettings.h index 475c11c09..c4c21dcc8 100644 --- a/Source/Flow/Public/FlowSettings.h +++ b/Source/Flow/Public/FlowSettings.h @@ -12,7 +12,7 @@ class UFlowNode; * */ UCLASS(Config = Game, defaultconfig, meta = (DisplayName = "Flow")) -class UFlowSettings final : public UDeveloperSettings +class FLOW_API UFlowSettings : public UDeveloperSettings { GENERATED_UCLASS_BODY() @@ -29,4 +29,8 @@ class UFlowSettings final : public UDeveloperSettings UPROPERTY(Config, EditAnywhere, Category = "SaveSystem") bool bWarnAboutMissingIdentityTags; + + // How many nodes of given class should be preloaded with the Flow Asset instance? + UPROPERTY(Config, EditAnywhere, Category = "Importer") + TMap> BlueprintFunctionsToFlowNodes; }; diff --git a/Source/Flow/Public/Nodes/FlowPin.h b/Source/Flow/Public/Nodes/FlowPin.h index 78ef72c41..55be81cfb 100644 --- a/Source/Flow/Public/Nodes/FlowPin.h +++ b/Source/Flow/Public/Nodes/FlowPin.h @@ -21,7 +21,7 @@ struct FLOW_API FFlowPin FString PinToolTip; static inline FName AnyPinName = TEXT("AnyPinName"); - + FFlowPin() : PinName(NAME_None) { @@ -182,6 +182,23 @@ struct FLOW_API FConnectedPin } }; +USTRUCT() +struct FLOW_API FGraphNodeImport +{ + GENERATED_USTRUCT_BODY() + + UPROPERTY() + UEdGraphNode* SourceGraphNode; + + TArray Inputs; + TArray Outputs; + + FGraphNodeImport() + : SourceGraphNode(nullptr) + { + } +}; + UENUM(BlueprintType) enum class EFlowPinActivationType : uint8 { diff --git a/Source/FlowEditor/FlowEditor.Build.cs b/Source/FlowEditor/FlowEditor.Build.cs index 4fdf98210..6a7901f69 100644 --- a/Source/FlowEditor/FlowEditor.Build.cs +++ b/Source/FlowEditor/FlowEditor.Build.cs @@ -26,6 +26,7 @@ public FlowEditor(ReadOnlyTargetRules target) : base(target) "CoreUObject", "DetailCustomizations", "DeveloperSettings", + "EditorScriptingUtilities", "EditorFramework", "EditorStyle", "Engine", @@ -42,6 +43,7 @@ public FlowEditor(ReadOnlyTargetRules target) : base(target) "MovieSceneTools", "Projects", "PropertyEditor", + "PropertyPath", "RenderCore", "Sequencer", "Slate", diff --git a/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp b/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp index f9b25fc65..65ab06e68 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp @@ -68,10 +68,9 @@ UFlowAssetFactory::UFlowAssetFactory(const FObjectInitializer& ObjectInitializer bool UFlowAssetFactory::ConfigureProperties() { - AssetClass = UFlowGraphSettings::Get()->DefaultFlowAssetClass;; - if (AssetClass != nullptr) + AssetClass = UFlowGraphSettings::Get()->DefaultFlowAssetClass; + if (AssetClass) // Class was set in settings { - // Class was selected in settings return true; } @@ -102,19 +101,19 @@ bool UFlowAssetFactory::ConfigureProperties() UObject* UFlowAssetFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) { - UFlowAsset* NewFlow; - if (AssetClass != nullptr) + UFlowAsset* NewFlowAsset; + if (AssetClass) { - NewFlow = NewObject(InParent, AssetClass, Name, Flags | RF_Transactional, Context); + NewFlowAsset = NewObject(InParent, AssetClass, Name, Flags | RF_Transactional, Context); } else { // if we have no asset class, use the passed-in class instead - NewFlow = NewObject(InParent, Class, Name, Flags | RF_Transactional, Context); + NewFlowAsset = NewObject(InParent, Class, Name, Flags | RF_Transactional, Context); } - UFlowGraph::CreateGraph(NewFlow); - return NewFlow; + UFlowGraph::CreateGraph(NewFlowAsset); + return NewFlowAsset; } #undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp new file mode 100644 index 000000000..3807d186d --- /dev/null +++ b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp @@ -0,0 +1,243 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Asset/FlowImportUtils.h" +#include "Asset/FlowAssetFactory.h" +#include "Graph/FlowGraphSchema_Actions.h" + +#include "FlowAsset.h" +#include "FlowSettings.h" +#include "Nodes/FlowPin.h" +#include "Nodes/Route/FlowNode_Start.h" + +#include "AssetRegistry/AssetRegistryModule.h" +#include "AssetToolsModule.h" +#include "EditorAssetLibrary.h" +#include "K2Node_CallFunction.h" +#include "K2Node_Event.h" +#include "Misc/ScopedSlowTask.h" +#include "PropertyPathHelpers.h" + +#define LOCTEXT_NAMESPACE "FlowImportUtils" + +UFlowAsset* UFlowImportUtils::ImportBlueprintGraph(UObject* BlueprintAsset, TSubclassOf FlowAssetClass, FString FlowAssetName, const FName StartEventName) +{ + if (BlueprintAsset == nullptr || FlowAssetClass == nullptr || FlowAssetName.IsEmpty() || StartEventName.IsNone()) + { + return nullptr; + } + + UBlueprint* Blueprint = Cast(BlueprintAsset); + UFlowAsset* FlowAsset = nullptr; + + // we assume that users want to have a converted asset in the same folder as the legacy blueprint + const FString PackageFolder = FPaths::GetPath(Blueprint->GetOuter()->GetPathName()); + + if (!FPackageName::DoesPackageExist(PackageFolder / FlowAssetName, nullptr)) // create a new asset + { + IAssetTools& AssetTools = FModuleManager::GetModuleChecked("AssetTools").Get(); + UFactory* Factory = Cast(UFlowAssetFactory::StaticClass()->GetDefaultObject()); + + if (UObject* NewAsset = AssetTools.CreateAsset(FlowAssetName, PackageFolder, FlowAssetClass, Factory)) + { + UEditorAssetLibrary::SaveLoadedAsset(NewAsset->GetPackage()); + FlowAsset = Cast(NewAsset); + } + } + else // load existing asset + { + const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(AssetRegistryConstants::ModuleName); + + const FString PackageName = PackageFolder / (FlowAssetName + TEXT(".") + FlowAssetName); + const FAssetData& FoundAssetData = AssetRegistryModule.GetRegistry().GetAssetByObjectPath(*PackageName); + + FlowAsset = Cast(FoundAssetData.GetAsset()); + } + + // import graph + if (FlowAsset) + { + ImportBlueprintGraph(Blueprint, FlowAsset, StartEventName); + } + + return FlowAsset; +} + +void UFlowImportUtils::ImportBlueprintGraph(UBlueprint* Blueprint, const UFlowAsset* FlowAsset, const FName StartEventName) +{ + ensureAlways(Blueprint && FlowAsset); + + UEdGraph* BlueprintGraph = Blueprint->UbergraphPages.IsValidIndex(0) ? Blueprint->UbergraphPages[0] : nullptr; + if (BlueprintGraph == nullptr) + { + return; + } + + FScopedSlowTask ExecuteAssetTask(BlueprintGraph->Nodes.Num(), FText::Format(LOCTEXT("FFlowGraphUtils::ImportBlueprintGraph", "Reading {0}"), FText::FromString(Blueprint->GetFriendlyName()))); + ExecuteAssetTask.MakeDialog(); + + TMap ImportedNodes; + UEdGraphNode* StartNode = nullptr; + + for (UEdGraphNode* ThisNode : BlueprintGraph->Nodes) + { + ExecuteAssetTask.EnterProgressFrame(1, FText::Format(LOCTEXT("FFlowGraphUtils::ImportBlueprintGraph", "Processing blueprint node: {0}"), ThisNode->GetNodeTitle(ENodeTitleType::ListView))); + + const UK2Node* K2Node = Cast(ThisNode); + if (K2Node == nullptr || K2Node->IsNodePure()) + { + continue; + } + + // create map of all non-pure blueprint nodes with theirs pin connections + for (const UEdGraphPin* ThisPin : ThisNode->Pins) + { + if (ThisPin->Direction == EGPD_Output && ThisPin->LinkedTo.Num() > 0) + { + if (const UEdGraphPin* LinkedPin = ThisPin->LinkedTo[0]) + { + UEdGraphNode* LinkedNode = LinkedPin->GetOwningNode(); + + // we assume all imported nodes except the entry point node represent functions + // only the first node from the left in the blueprint uber-graph has to be the Event node (UK2Node_Event) + const UK2Node_CallFunction* LinkedFunctionNode = Cast(LinkedNode); + if (LinkedFunctionNode && !LinkedFunctionNode->IsNodePure()) + { + FGraphNodeImport& ThisNodeImport = ImportedNodes.FindOrAdd(ThisNode->NodeGuid); + ThisNodeImport.Outputs.Add(FConnectedPin(LinkedNode->NodeGuid, LinkedPin->PinName)); + + FGraphNodeImport& LinkedNodeImport = ImportedNodes.FindOrAdd(LinkedNode->NodeGuid); + LinkedNodeImport.SourceGraphNode = LinkedNode; + LinkedNodeImport.Inputs.Add(FConnectedPin(ThisNode->NodeGuid, ThisPin->PinName)); + } + } + } + } + + // we need to know the default entry point of blueprint graph + const UK2Node_Event* EventNode = Cast(ThisNode); + if (EventNode && (EventNode->EventReference.GetMemberName() == StartEventName || EventNode->CustomFunctionName == StartEventName)) + { + StartNode = ThisNode; + } + } + + // can't start import if provided graph doesn't have required start node + // todo: do we really needs this? + if (StartNode == nullptr) + { + return; + } + + // clear existing graph + UEdGraph* FlowGraph = FlowAsset->GetGraph(); + FlowGraph->Nodes.Empty(); + + // recreated UFlowNode_Start, assign it a blueprint node FGuid + UFlowGraphNode* NewGraphNode = FFlowGraphSchemaAction_NewNode::CreateNode(FlowGraph, nullptr, UFlowNode_Start::StaticClass(), FVector2D::ZeroVector); + FlowGraph->GetSchema()->SetNodeMetaData(NewGraphNode, FNodeMetadata::DefaultGraphNode); + NewGraphNode->NodeGuid = StartNode->NodeGuid; + NewGraphNode->GetFlowNode()->SetGuid(StartNode->NodeGuid); + + // execute graph import + ImportBlueprintFunction_Recursive(FlowGraph->Nodes[0], ImportedNodes); +} + +void UFlowImportUtils::ImportBlueprintFunction_Recursive(UEdGraphNode* PrecedingGraphNode, const TMap SourceNodes) +{ + UEdGraph* Graph = PrecedingGraphNode->GetGraph(); + const FGraphNodeImport& PrecedingNode = SourceNodes.FindRef(PrecedingGraphNode->NodeGuid); + + for (const FConnectedPin& Output : PrecedingNode.Outputs) + { + // todo: support multiple output pins + //UFlowNode* LinkedNodeDefaults = MatchingFlowNodeClass.GetDefaultObject(); + //const FName PinName = LinkedNodeDefaults->OutputPins[0].PinName; + UEdGraphPin* OutputPin = Cast(PrecedingGraphNode)->OutputPins[0]; + + const FGuid& LinkedNodeGuid = Output.NodeGuid; + const FGraphNodeImport& LinkedNodeImport = SourceNodes.FindRef(LinkedNodeGuid); + + // we only accept here blueprint nodes representing functions + const UK2Node_CallFunction* FunctionNode = Cast(LinkedNodeImport.SourceGraphNode); + if (FunctionNode == nullptr) + { + continue; + } + + // find FlowNode class matching provided UFunction name + const TSubclassOf MatchingFlowNodeClass = UFlowSettings::Get()->BlueprintFunctionsToFlowNodes.FindRef(FunctionNode->GetFunctionName()); + if (MatchingFlowNodeClass == nullptr) + { + continue; + } + + // check if already imported connected node + // todo: optimize? + UFlowGraphNode* LinkedGraphNode = nullptr; + for (const TObjectPtr ExistingGraphNode : Graph->Nodes) + { + if (ExistingGraphNode->NodeGuid == LinkedNodeGuid) + { + LinkedGraphNode = Cast(ExistingGraphNode); + break; + } + } + + if (LinkedGraphNode == nullptr) + { + // create a new Flow Graph node + const FVector2d Location = FVector2D(LinkedNodeImport.SourceGraphNode->NodePosX, LinkedNodeImport.SourceGraphNode->NodePosY); + LinkedGraphNode = FFlowGraphSchemaAction_NewNode::ImportNode(PrecedingGraphNode->GetGraph(), OutputPin, MatchingFlowNodeClass, LinkedNodeGuid, Location); + } + else + { + UEdGraphPin* LinkedPin = nullptr; + for (UEdGraphPin* InputPin : LinkedGraphNode->InputPins) + { + if (InputPin->PinName == Output.PinName) + { + LinkedPin = InputPin; + break; + } + } + + // just link the pin to existing node + Graph->GetSchema()->TryCreateConnection(OutputPin, LinkedPin); + } + + if (LinkedGraphNode) + { + // transfer properties from UFunction input parameters to Flow Node properties + { + TMap InputPins; + for (UEdGraphPin* Pin : FunctionNode->Pins) + { + if (Pin->Direction == EGPD_Input && !Pin->bHidden && !Pin->bOrphanedPin) + { + InputPins.Add(Pin->PinName, Pin); + } + } + + for (TFieldIterator PropIt(LinkedGraphNode->GetFlowNode()->GetClass(), EFieldIteratorFlags::IncludeSuper); PropIt; ++PropIt) + { + const FProperty* Param = *PropIt; + const bool bIsEditable = !Param->HasAnyPropertyFlags(CPF_Edit | CPF_Deprecated); + if (bIsEditable) + { + if (const UEdGraphPin* InputPin = InputPins.FindRef(*Param->GetAuthoredName())) + { + FString const PinValue = InputPin->GetDefaultAsString(); + uint8* Offset = Param->ContainerPtrToValuePtr(LinkedGraphNode->GetFlowNode()); + Param->ImportText(*PinValue, Offset, PPF_Copy, nullptr, GLog); + } + } + } + } + + // iterate next nodes + ImportBlueprintFunction_Recursive(LinkedGraphNode, SourceNodes); + } + } +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp index 6baba1027..4d8464b26 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp @@ -77,7 +77,7 @@ UFlowGraphNode* FFlowGraphSchemaAction_NewNode::CreateNode(UEdGraph* ParentGraph ParentGraph->NotifyGraphChanged(); FlowAsset->PostEditChange(); - + // select in editor UI if (bSelectNewNode) { @@ -155,6 +155,46 @@ UFlowGraphNode* FFlowGraphSchemaAction_NewNode::RecreateNode(UEdGraph* ParentGra return NewGraphNode; } +UFlowGraphNode* FFlowGraphSchemaAction_NewNode::ImportNode(UEdGraph* ParentGraph, UEdGraphPin* FromPin, const UClass* NodeClass, const FGuid& NodeGuid, const FVector2D Location) +{ + check(NodeClass); + + ParentGraph->Modify(); + if (FromPin) + { + FromPin->Modify(); + } + + UFlowAsset* FlowAsset = CastChecked(ParentGraph)->GetFlowAsset(); + FlowAsset->Modify(); + + // create new Flow Graph node + const UClass* GraphNodeClass = UFlowGraphSchema::GetAssignedGraphNodeClass(NodeClass); + UFlowGraphNode* NewGraphNode = NewObject(ParentGraph, GraphNodeClass, NAME_None, RF_Transactional); + + // register to the graph + NewGraphNode->NodeGuid = NodeGuid; + ParentGraph->AddNode(NewGraphNode, false, false); + + // link editor and runtime nodes together + UFlowNode* FlowNode = FlowAsset->CreateNode(NodeClass, NewGraphNode); + NewGraphNode->SetNodeTemplate(FlowNode); + + // create pins and connections + NewGraphNode->AllocateDefaultPins(); + NewGraphNode->AutowireNewNode(FromPin); + + // set position + NewGraphNode->NodePosX = Location.X; + NewGraphNode->NodePosY = Location.Y; + + // call notifies + NewGraphNode->PostPlacedNewNode(); + ParentGraph->NotifyGraphChanged(); + + return NewGraphNode; +} + UEdGraphNode* FFlowGraphSchemaAction_Paste::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, const bool bSelectNewNode/* = true*/) { // prevent adding new nodes while playing diff --git a/Source/FlowEditor/Public/Asset/FlowImportUtils.h b/Source/FlowEditor/Public/Asset/FlowImportUtils.h new file mode 100644 index 000000000..a040802d2 --- /dev/null +++ b/Source/FlowEditor/Public/Asset/FlowImportUtils.h @@ -0,0 +1,22 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "FlowAsset.h" +#include "FlowImportUtils.generated.h" + +/** + * + */ +UCLASS(meta = (ScriptName = "FlowImportUtils")) +class FLOWEDITOR_API UFlowImportUtils final : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + UFUNCTION(BlueprintCallable, Category = "FlowImportUtils") + static UFlowAsset* ImportBlueprintGraph(UObject* BlueprintAsset, TSubclassOf FlowAssetClass, FString FlowAssetName, const FName StartEventName = TEXT("BeginPlay")); + + static void ImportBlueprintGraph(UBlueprint* Blueprint, const UFlowAsset* FlowAsset, const FName StartEventName = TEXT("BeginPlay")); + static void ImportBlueprintFunction_Recursive(UEdGraphNode* PrecedingGraphNode, const TMap SourceNodes); +}; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h index 6c97a6c64..3a5b4ef58 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h @@ -5,8 +5,8 @@ #include "EdGraph/EdGraphSchema.h" #include "FlowGraphSchema.generated.h" -class UFlowNode; class UFlowAsset; +class UFlowNode; DECLARE_MULTICAST_DELEGATE(FFlowGraphSchemaRefresh); @@ -16,7 +16,7 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema GENERATED_UCLASS_BODY() friend class UFlowGraph; - + private: static bool bInitialGatherPerformed; static TArray NativeFlowNodes; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h index 528934593..3bb8d230d 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h @@ -49,6 +49,7 @@ struct FLOWEDITOR_API FFlowGraphSchemaAction_NewNode : public FEdGraphSchemaActi static UFlowGraphNode* CreateNode(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const UClass* NodeClass, const FVector2D Location, const bool bSelectNewNode = true); static UFlowGraphNode* RecreateNode(UEdGraph* ParentGraph, UEdGraphNode* OldInstance, UFlowNode* FlowNode); + static UFlowGraphNode* ImportNode(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const UClass* NodeClass, const FGuid& NodeGuid, const FVector2D Location); }; /** Action to paste clipboard contents into the graph */ From 323dca082124659bae99ecca3546be1e0d7d62ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 8 Jan 2023 20:43:11 +0100 Subject: [PATCH 096/485] UE 5.1 fixes --- Source/FlowEditor/Private/Asset/FlowImportUtils.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp index 3807d186d..1397b2aa2 100644 --- a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp +++ b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp @@ -15,7 +15,6 @@ #include "K2Node_CallFunction.h" #include "K2Node_Event.h" #include "Misc/ScopedSlowTask.h" -#include "PropertyPathHelpers.h" #define LOCTEXT_NAMESPACE "FlowImportUtils" @@ -48,7 +47,7 @@ UFlowAsset* UFlowImportUtils::ImportBlueprintGraph(UObject* BlueprintAsset, TSub const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(AssetRegistryConstants::ModuleName); const FString PackageName = PackageFolder / (FlowAssetName + TEXT(".") + FlowAssetName); - const FAssetData& FoundAssetData = AssetRegistryModule.GetRegistry().GetAssetByObjectPath(*PackageName); + const FAssetData& FoundAssetData = AssetRegistryModule.GetRegistry().GetAssetByObjectPath(FSoftObjectPath(PackageName)); FlowAsset = Cast(FoundAssetData.GetAsset()); } @@ -218,17 +217,17 @@ void UFlowImportUtils::ImportBlueprintFunction_Recursive(UEdGraphNode* Preceding } } - for (TFieldIterator PropIt(LinkedGraphNode->GetFlowNode()->GetClass(), EFieldIteratorFlags::IncludeSuper); PropIt; ++PropIt) + for (TFieldIterator PropIt(LinkedGraphNode->GetFlowNode()->GetClass(), EFieldIteratorFlags::IncludeSuper); PropIt && (PropIt->PropertyFlags & CPF_Edit); ++PropIt) { const FProperty* Param = *PropIt; - const bool bIsEditable = !Param->HasAnyPropertyFlags(CPF_Edit | CPF_Deprecated); + const bool bIsEditable = !Param->HasAnyPropertyFlags(CPF_Deprecated); if (bIsEditable) { if (const UEdGraphPin* InputPin = InputPins.FindRef(*Param->GetAuthoredName())) { FString const PinValue = InputPin->GetDefaultAsString(); uint8* Offset = Param->ContainerPtrToValuePtr(LinkedGraphNode->GetFlowNode()); - Param->ImportText(*PinValue, Offset, PPF_Copy, nullptr, GLog); + Param->ImportText_Direct(*PinValue, Offset, LinkedGraphNode->GetFlowNode(), PPF_Copy, GLog); } } } From f07b94e5d599df0b513e3fb717ec58017701a91a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Mon, 9 Jan 2023 00:56:15 +0100 Subject: [PATCH 097/485] import utils improvements - importing Comment nodes - importing BaseAsyncTask nodes (i.e. gameplay tasks) - added UFlowNode::PostImport() - supporting nodes with multiple outputs --- Source/Flow/Private/Nodes/FlowNode.cpp | 12 +- Source/Flow/Public/Nodes/FlowNode.h | 3 + Source/Flow/Public/Nodes/FlowPin.h | 17 --- .../Private/Asset/FlowImportUtils.cpp | 135 ++++++++++++------ .../FlowEditor/Public/Asset/FlowImportUtils.h | 16 +++ 5 files changed, 116 insertions(+), 67 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 7c6272285..dbd275761 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -133,19 +133,19 @@ UFlowAsset* UFlowNode::GetFlowAsset() const return GetOuter() ? Cast(GetOuter()) : nullptr; } -void UFlowNode::AddInputPins(TArray PinNames) +void UFlowNode::AddInputPins(TArray Pins) { - for (const FName& PinName : PinNames) + for (const FFlowPin& Pin : Pins) { - InputPins.Emplace(PinName); + InputPins.Emplace(Pin); } } -void UFlowNode::AddOutputPins(TArray PinNames) +void UFlowNode::AddOutputPins(TArray Pins) { - for (const FName& PinName : PinNames) + for (const FFlowPin& Pin : Pins) { - OutputPins.Emplace(PinName); + OutputPins.Emplace(Pin); } } diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index eadf41ddf..2d135054d 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -78,6 +78,9 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte virtual void FixNode(UEdGraphNode* NewGraphNode); virtual EDataValidationResult ValidateNode() { return EDataValidationResult::NotValidated; } + + // used when import graph from another asset + virtual void PostImport() {} #endif UEdGraphNode* GetGraphNode() const { return GraphNode; } diff --git a/Source/Flow/Public/Nodes/FlowPin.h b/Source/Flow/Public/Nodes/FlowPin.h index 55be81cfb..1f5c2d82b 100644 --- a/Source/Flow/Public/Nodes/FlowPin.h +++ b/Source/Flow/Public/Nodes/FlowPin.h @@ -182,23 +182,6 @@ struct FLOW_API FConnectedPin } }; -USTRUCT() -struct FLOW_API FGraphNodeImport -{ - GENERATED_USTRUCT_BODY() - - UPROPERTY() - UEdGraphNode* SourceGraphNode; - - TArray Inputs; - TArray Outputs; - - FGraphNodeImport() - : SourceGraphNode(nullptr) - { - } -}; - UENUM(BlueprintType) enum class EFlowPinActivationType : uint8 { diff --git a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp index 1397b2aa2..3c6a2bffd 100644 --- a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp +++ b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp @@ -1,8 +1,10 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "Asset/FlowImportUtils.h" + #include "Asset/FlowAssetFactory.h" #include "Graph/FlowGraphSchema_Actions.h" +#include "Graph/FlowGraph.h" #include "FlowAsset.h" #include "FlowSettings.h" @@ -11,7 +13,9 @@ #include "AssetRegistry/AssetRegistryModule.h" #include "AssetToolsModule.h" +#include "EdGraphNode_Comment.h" #include "EditorAssetLibrary.h" +#include "K2Node_BaseAsyncTask.h" #include "K2Node_CallFunction.h" #include "K2Node_Event.h" #include "Misc/ScopedSlowTask.h" @@ -38,7 +42,6 @@ UFlowAsset* UFlowImportUtils::ImportBlueprintGraph(UObject* BlueprintAsset, TSub if (UObject* NewAsset = AssetTools.CreateAsset(FlowAssetName, PackageFolder, FlowAssetClass, Factory)) { - UEditorAssetLibrary::SaveLoadedAsset(NewAsset->GetPackage()); FlowAsset = Cast(NewAsset); } } @@ -56,6 +59,9 @@ UFlowAsset* UFlowImportUtils::ImportBlueprintGraph(UObject* BlueprintAsset, TSub if (FlowAsset) { ImportBlueprintGraph(Blueprint, FlowAsset, StartEventName); + + Cast(FlowAsset->GetGraph())->RefreshGraph(); + UEditorAssetLibrary::SaveLoadedAsset(FlowAsset->GetPackage()); } return FlowAsset; @@ -81,47 +87,63 @@ void UFlowImportUtils::ImportBlueprintGraph(UBlueprint* Blueprint, const UFlowAs { ExecuteAssetTask.EnterProgressFrame(1, FText::Format(LOCTEXT("FFlowGraphUtils::ImportBlueprintGraph", "Processing blueprint node: {0}"), ThisNode->GetNodeTitle(ENodeTitleType::ListView))); - const UK2Node* K2Node = Cast(ThisNode); - if (K2Node == nullptr || K2Node->IsNodePure()) + if (UEdGraphNode_Comment* CommentNode = Cast(ThisNode)) { - continue; - } + // special case: recreate Comment node + FFlowGraphSchemaAction_NewComment CommentAction; + UEdGraphNode* NewNode = CommentAction.PerformAction(FlowAsset->GetGraph(), nullptr, FVector2D(CommentNode->NodePosX, CommentNode->NodePosY), false); + if (UEdGraphNode_Comment* CommentCopy = Cast(NewNode)) + { + CommentCopy->NodeComment = CommentNode->NodeComment; - // create map of all non-pure blueprint nodes with theirs pin connections - for (const UEdGraphPin* ThisPin : ThisNode->Pins) + CommentCopy->CommentColor = CommentNode->CommentColor; + CommentCopy->FontSize = CommentNode->FontSize; + CommentCopy->bCommentBubbleVisible_InDetailsPanel = CommentNode->bCommentBubbleVisible_InDetailsPanel; + CommentCopy->bColorCommentBubble = CommentNode->bColorCommentBubble; + CommentCopy->MoveMode = CommentNode->MoveMode; + } + } + else // non-pure K2Nodes { - if (ThisPin->Direction == EGPD_Output && ThisPin->LinkedTo.Num() > 0) + const UK2Node* K2Node = Cast(ThisNode); + if (K2Node && !K2Node->IsNodePure()) { - if (const UEdGraphPin* LinkedPin = ThisPin->LinkedTo[0]) + // create map of all non-pure blueprint nodes with theirs pin connections + for (const UEdGraphPin* ThisPin : ThisNode->Pins) { - UEdGraphNode* LinkedNode = LinkedPin->GetOwningNode(); - - // we assume all imported nodes except the entry point node represent functions - // only the first node from the left in the blueprint uber-graph has to be the Event node (UK2Node_Event) - const UK2Node_CallFunction* LinkedFunctionNode = Cast(LinkedNode); - if (LinkedFunctionNode && !LinkedFunctionNode->IsNodePure()) + if (ThisPin->Direction == EGPD_Output && ThisPin->LinkedTo.Num() > 0) { - FGraphNodeImport& ThisNodeImport = ImportedNodes.FindOrAdd(ThisNode->NodeGuid); - ThisNodeImport.Outputs.Add(FConnectedPin(LinkedNode->NodeGuid, LinkedPin->PinName)); - - FGraphNodeImport& LinkedNodeImport = ImportedNodes.FindOrAdd(LinkedNode->NodeGuid); - LinkedNodeImport.SourceGraphNode = LinkedNode; - LinkedNodeImport.Inputs.Add(FConnectedPin(ThisNode->NodeGuid, ThisPin->PinName)); + if (const UEdGraphPin* LinkedPin = ThisPin->LinkedTo[0]) + { + UEdGraphNode* LinkedNode = LinkedPin->GetOwningNode(); + + // we assume all imported nodes except the entry point node represent functions + // only the first node from the left in the blueprint uber-graph has to be the Event node (UK2Node_Event) + const UK2Node_CallFunction* LinkedFunctionNode = Cast(LinkedNode); + const UK2Node_BaseAsyncTask* LinkedAsyncTaskNode = Cast(LinkedNode); + if ((LinkedFunctionNode && !LinkedFunctionNode->IsNodePure()) || LinkedAsyncTaskNode) + { + FGraphNodeImport& ThisNodeImport = ImportedNodes.FindOrAdd(ThisNode->NodeGuid); + ThisNodeImport.Connections.Add(ThisPin->PinName, FConnectedPin(LinkedNode->NodeGuid, LinkedPin->PinName)); + + FGraphNodeImport& LinkedNodeImport = ImportedNodes.FindOrAdd(LinkedNode->NodeGuid); + LinkedNodeImport.SourceGraphNode = LinkedNode; + } + } } } - } - } - // we need to know the default entry point of blueprint graph - const UK2Node_Event* EventNode = Cast(ThisNode); - if (EventNode && (EventNode->EventReference.GetMemberName() == StartEventName || EventNode->CustomFunctionName == StartEventName)) - { - StartNode = ThisNode; + // we need to know the default entry point of blueprint graph + const UK2Node_Event* EventNode = Cast(ThisNode); + if (EventNode && (EventNode->EventReference.GetMemberName() == StartEventName || EventNode->CustomFunctionName == StartEventName)) + { + StartNode = ThisNode; + } + } } } // can't start import if provided graph doesn't have required start node - // todo: do we really needs this? if (StartNode == nullptr) { return; @@ -146,30 +168,50 @@ void UFlowImportUtils::ImportBlueprintFunction_Recursive(UEdGraphNode* Preceding UEdGraph* Graph = PrecedingGraphNode->GetGraph(); const FGraphNodeImport& PrecedingNode = SourceNodes.FindRef(PrecedingGraphNode->NodeGuid); - for (const FConnectedPin& Output : PrecedingNode.Outputs) + const UFlowGraphNode* PrecedingFlowGraphNode = Cast(PrecedingGraphNode); + if (PrecedingFlowGraphNode == nullptr || PrecedingFlowGraphNode->OutputPins.IsEmpty()) { - // todo: support multiple output pins - //UFlowNode* LinkedNodeDefaults = MatchingFlowNodeClass.GetDefaultObject(); - //const FName PinName = LinkedNodeDefaults->OutputPins[0].PinName; - UEdGraphPin* OutputPin = Cast(PrecedingGraphNode)->OutputPins[0]; + return; + } - const FGuid& LinkedNodeGuid = Output.NodeGuid; + for (const TPair& Connection : PrecedingNode.Connections) + { + const FGuid& LinkedNodeGuid = Connection.Value.NodeGuid; const FGraphNodeImport& LinkedNodeImport = SourceNodes.FindRef(LinkedNodeGuid); - // we only accept here blueprint nodes representing functions - const UK2Node_CallFunction* FunctionNode = Cast(LinkedNodeImport.SourceGraphNode); - if (FunctionNode == nullptr) + // find FlowNode class matching provided UFunction name + TSubclassOf MatchingFlowNodeClass = nullptr; + if (const UK2Node_CallFunction* FunctionNode = Cast(LinkedNodeImport.SourceGraphNode)) { - continue; + // find FlowNode class matching provided UFunction name + MatchingFlowNodeClass = UFlowSettings::Get()->BlueprintFunctionsToFlowNodes.FindRef(FunctionNode->GetFunctionName()); + } + else if (const UK2Node_BaseAsyncTask* AsyncTaskNode = Cast(LinkedNodeImport.SourceGraphNode)) + { + // find FlowNode class matching provided AsyncTask name + MatchingFlowNodeClass = UFlowSettings::Get()->BlueprintFunctionsToFlowNodes.FindRef(AsyncTaskNode->GetProxyFactoryFunctionName()); } - // find FlowNode class matching provided UFunction name - const TSubclassOf MatchingFlowNodeClass = UFlowSettings::Get()->BlueprintFunctionsToFlowNodes.FindRef(FunctionNode->GetFunctionName()); if (MatchingFlowNodeClass == nullptr) { continue; } + // find output pin of preceding node + UEdGraphPin* OutputPin = nullptr; + for (UEdGraphPin* Pin : PrecedingFlowGraphNode->OutputPins) + { + if (Pin->PinName == Connection.Key || PrecedingFlowGraphNode->OutputPins.Num() == 1) + { + OutputPin = Pin; + break; + } + } + if (OutputPin == nullptr) + { + continue; + } + // check if already imported connected node // todo: optimize? UFlowGraphNode* LinkedGraphNode = nullptr; @@ -193,7 +235,7 @@ void UFlowImportUtils::ImportBlueprintFunction_Recursive(UEdGraphNode* Preceding UEdGraphPin* LinkedPin = nullptr; for (UEdGraphPin* InputPin : LinkedGraphNode->InputPins) { - if (InputPin->PinName == Output.PinName) + if (InputPin->PinName == Connection.Value.PinName || LinkedGraphNode->InputPins.Num() == 1) { LinkedPin = InputPin; break; @@ -201,7 +243,10 @@ void UFlowImportUtils::ImportBlueprintFunction_Recursive(UEdGraphNode* Preceding } // just link the pin to existing node - Graph->GetSchema()->TryCreateConnection(OutputPin, LinkedPin); + if (LinkedPin) + { + Graph->GetSchema()->TryCreateConnection(OutputPin, LinkedPin); + } } if (LinkedGraphNode) @@ -209,7 +254,7 @@ void UFlowImportUtils::ImportBlueprintFunction_Recursive(UEdGraphNode* Preceding // transfer properties from UFunction input parameters to Flow Node properties { TMap InputPins; - for (UEdGraphPin* Pin : FunctionNode->Pins) + for (UEdGraphPin* Pin : LinkedNodeImport.SourceGraphNode->Pins) { if (Pin->Direction == EGPD_Input && !Pin->bHidden && !Pin->bOrphanedPin) { @@ -233,6 +278,8 @@ void UFlowImportUtils::ImportBlueprintFunction_Recursive(UEdGraphNode* Preceding } } + LinkedGraphNode->GetFlowNode()->PostImport(); + // iterate next nodes ImportBlueprintFunction_Recursive(LinkedGraphNode, SourceNodes); } diff --git a/Source/FlowEditor/Public/Asset/FlowImportUtils.h b/Source/FlowEditor/Public/Asset/FlowImportUtils.h index a040802d2..e3d20e0e8 100644 --- a/Source/FlowEditor/Public/Asset/FlowImportUtils.h +++ b/Source/FlowEditor/Public/Asset/FlowImportUtils.h @@ -5,6 +5,22 @@ #include "FlowAsset.h" #include "FlowImportUtils.generated.h" +USTRUCT() +struct FLOWEDITOR_API FGraphNodeImport +{ + GENERATED_USTRUCT_BODY() + + UPROPERTY() + UEdGraphNode* SourceGraphNode; + + TMap Connections + + FGraphNodeImport() + : SourceGraphNode(nullptr) + { + } +}; + /** * */ From 717170b831e5a414ae2e7f40a5d650fe2488e8e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Mon, 9 Jan 2023 00:56:49 +0100 Subject: [PATCH 098/485] Context Pins now can are constructed directly as FFlowPin --- .../Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp | 12 ++++++------ .../Nodes/World/FlowNode_PlayLevelSequence.cpp | 10 +++++----- Source/Flow/Public/Nodes/FlowNode.h | 8 ++++---- Source/Flow/Public/Nodes/Route/FlowNode_SubGraph.h | 4 ++-- .../Public/Nodes/World/FlowNode_PlayLevelSequence.h | 2 +- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp index ab6dfa5bb..006c15dda 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp @@ -117,9 +117,9 @@ EDataValidationResult UFlowNode_SubGraph::ValidateNode() return EDataValidationResult::Valid; } -TArray UFlowNode_SubGraph::GetContextInputs() +TArray UFlowNode_SubGraph::GetContextInputs() { - TArray EventNames; + TArray EventNames; if (!Asset.IsNull()) { @@ -136,9 +136,9 @@ TArray UFlowNode_SubGraph::GetContextInputs() return EventNames; } -TArray UFlowNode_SubGraph::GetContextOutputs() +TArray UFlowNode_SubGraph::GetContextOutputs() { - TArray EventNames; + TArray Pins; if (!Asset.IsNull()) { @@ -147,12 +147,12 @@ TArray UFlowNode_SubGraph::GetContextOutputs() { if (!PinName.IsNone()) { - EventNames.Emplace(PinName); + Pins.Emplace(PinName); } } } - return EventNames; + return Pins; } void UFlowNode_SubGraph::PostLoad() diff --git a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp index 727d7e0f4..1927105d2 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp @@ -46,14 +46,14 @@ UFlowNode_PlayLevelSequence::UFlowNode_PlayLevelSequence(const FObjectInitialize } #if WITH_EDITOR -TArray UFlowNode_PlayLevelSequence::GetContextOutputs() +TArray UFlowNode_PlayLevelSequence::GetContextOutputs() { if (Sequence.IsNull()) { - return TArray(); + return TArray(); } - TArray PinNames = {}; + TArray Pins = {}; Sequence = Sequence.LoadSynchronous(); if (Sequence && Sequence->GetMovieScene()) @@ -70,7 +70,7 @@ TArray UFlowNode_PlayLevelSequence::GetContextOutputs() { if (!EventName.IsEmpty()) { - PinNames.Emplace(EventName); + Pins.Emplace(EventName); } } } @@ -79,7 +79,7 @@ TArray UFlowNode_PlayLevelSequence::GetContextOutputs() } } - return PinNames; + return Pins; } void UFlowNode_PlayLevelSequence::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 2d135054d..dc0a0ce48 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -149,8 +149,8 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UPROPERTY(EditDefaultsOnly, Category = "FlowNode") TArray OutputPins; - void AddInputPins(TArray PinNames); - void AddOutputPins(TArray PinNames); + void AddInputPins(TArray Pins); + void AddOutputPins(TArray Pins); // always use default range for nodes with user-created outputs i.e. Execution Sequence void SetNumberedInputPins(const uint8 FirstNumber = 0, const uint8 LastNumber = 1); @@ -175,8 +175,8 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte // Be careful, enabling it might cause loading gigabytes of data as nodes would load all related data (i.e. Level Sequences) virtual bool CanRefreshContextPinsOnLoad() const { return false; } - virtual TArray GetContextInputs() { return TArray(); } - virtual TArray GetContextOutputs() { return TArray(); } + virtual TArray GetContextInputs() { return TArray(); } + virtual TArray GetContextOutputs() { return TArray(); } virtual bool CanUserAddInput() const; virtual bool CanUserAddOutput() const; diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_SubGraph.h b/Source/Flow/Public/Nodes/Route/FlowNode_SubGraph.h index d6356e1a0..394e631f9 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_SubGraph.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_SubGraph.h @@ -56,8 +56,8 @@ class FLOW_API UFlowNode_SubGraph : public UFlowNode virtual bool SupportsContextPins() const override { return true; } - virtual TArray GetContextInputs() override; - virtual TArray GetContextOutputs() override; + virtual TArray GetContextInputs() override; + virtual TArray GetContextOutputs() override; // UObject virtual void PostLoad() override; diff --git a/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h b/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h index eb24d35ac..ad118d008 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h @@ -82,7 +82,7 @@ class FLOW_API UFlowNode_PlayLevelSequence : public UFlowNode public: #if WITH_EDITOR virtual bool SupportsContextPins() const override { return true; } - virtual TArray GetContextOutputs() override; + virtual TArray GetContextOutputs() override; virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; #endif From 382be1608bf879a03f2e7414cc8f1731a2c9306d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Tue, 10 Jan 2023 00:01:08 +0100 Subject: [PATCH 099/485] Flow Import improvements -Iterating all nodes in simple for-loop instead of recursive loop that would abort graph import if single node class wouldn't recreated. - Moved map functions-nodes to blutility param as Flow Settings could not serialize classes from other modules. Plus, that potentially allow to have a few different import scripts. - Added logging if matching Flow Node class can't be found. --- Source/Flow/Public/FlowSettings.h | 4 - .../Private/Asset/FlowImportUtils.cpp | 236 ++++++++++-------- .../FlowEditor/Public/Asset/FlowImportUtils.h | 17 +- 3 files changed, 143 insertions(+), 114 deletions(-) diff --git a/Source/Flow/Public/FlowSettings.h b/Source/Flow/Public/FlowSettings.h index c4c21dcc8..6b9e66f18 100644 --- a/Source/Flow/Public/FlowSettings.h +++ b/Source/Flow/Public/FlowSettings.h @@ -29,8 +29,4 @@ class FLOW_API UFlowSettings : public UDeveloperSettings UPROPERTY(Config, EditAnywhere, Category = "SaveSystem") bool bWarnAboutMissingIdentityTags; - - // How many nodes of given class should be preloaded with the Flow Asset instance? - UPROPERTY(Config, EditAnywhere, Category = "Importer") - TMap> BlueprintFunctionsToFlowNodes; }; diff --git a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp index 3c6a2bffd..991166d6c 100644 --- a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp +++ b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp @@ -3,11 +3,11 @@ #include "Asset/FlowImportUtils.h" #include "Asset/FlowAssetFactory.h" +#include "FlowEditorModule.h" #include "Graph/FlowGraphSchema_Actions.h" #include "Graph/FlowGraph.h" #include "FlowAsset.h" -#include "FlowSettings.h" #include "Nodes/FlowPin.h" #include "Nodes/Route/FlowNode_Start.h" @@ -22,7 +22,9 @@ #define LOCTEXT_NAMESPACE "FlowImportUtils" -UFlowAsset* UFlowImportUtils::ImportBlueprintGraph(UObject* BlueprintAsset, TSubclassOf FlowAssetClass, FString FlowAssetName, const FName StartEventName) +TMap> UFlowImportUtils::FunctionsToFlowNodes = TMap>(); + +UFlowAsset* UFlowImportUtils::ImportBlueprintGraph(UObject* BlueprintAsset, TSubclassOf FlowAssetClass, FString FlowAssetName, TMap> BlueprintFunctionsToFlowNodes, const FName StartEventName) { if (BlueprintAsset == nullptr || FlowAssetClass == nullptr || FlowAssetName.IsEmpty() || StartEventName.IsNone()) { @@ -58,7 +60,9 @@ UFlowAsset* UFlowImportUtils::ImportBlueprintGraph(UObject* BlueprintAsset, TSub // import graph if (FlowAsset) { + FunctionsToFlowNodes = BlueprintFunctionsToFlowNodes; ImportBlueprintGraph(Blueprint, FlowAsset, StartEventName); + FunctionsToFlowNodes.Empty(); Cast(FlowAsset->GetGraph())->RefreshGraph(); UEditorAssetLibrary::SaveLoadedAsset(FlowAsset->GetPackage()); @@ -67,7 +71,7 @@ UFlowAsset* UFlowImportUtils::ImportBlueprintGraph(UObject* BlueprintAsset, TSub return FlowAsset; } -void UFlowImportUtils::ImportBlueprintGraph(UBlueprint* Blueprint, const UFlowAsset* FlowAsset, const FName StartEventName) +void UFlowImportUtils::ImportBlueprintGraph(UBlueprint* Blueprint, UFlowAsset* FlowAsset, const FName StartEventName) { ensureAlways(Blueprint && FlowAsset); @@ -80,7 +84,7 @@ void UFlowImportUtils::ImportBlueprintGraph(UBlueprint* Blueprint, const UFlowAs FScopedSlowTask ExecuteAssetTask(BlueprintGraph->Nodes.Num(), FText::Format(LOCTEXT("FFlowGraphUtils::ImportBlueprintGraph", "Reading {0}"), FText::FromString(Blueprint->GetFriendlyName()))); ExecuteAssetTask.MakeDialog(); - TMap ImportedNodes; + TMap SourceNodes; UEdGraphNode* StartNode = nullptr; for (UEdGraphNode* ThisNode : BlueprintGraph->Nodes) @@ -108,26 +112,25 @@ void UFlowImportUtils::ImportBlueprintGraph(UBlueprint* Blueprint, const UFlowAs const UK2Node* K2Node = Cast(ThisNode); if (K2Node && !K2Node->IsNodePure()) { + FImportedGraphNode& NodeImport = SourceNodes.FindOrAdd(ThisNode->NodeGuid); + NodeImport.SourceGraphNode = ThisNode; + // create map of all non-pure blueprint nodes with theirs pin connections for (const UEdGraphPin* ThisPin : ThisNode->Pins) { - if (ThisPin->Direction == EGPD_Output && ThisPin->LinkedTo.Num() > 0) + for (const UEdGraphPin* LinkedPin : ThisPin->LinkedTo) { - if (const UEdGraphPin* LinkedPin = ThisPin->LinkedTo[0]) + if (LinkedPin && LinkedPin->GetOwningNode()) { - UEdGraphNode* LinkedNode = LinkedPin->GetOwningNode(); + const FConnectedPin ConnectedPin(LinkedPin->GetOwningNode()->NodeGuid, LinkedPin->PinName); - // we assume all imported nodes except the entry point node represent functions - // only the first node from the left in the blueprint uber-graph has to be the Event node (UK2Node_Event) - const UK2Node_CallFunction* LinkedFunctionNode = Cast(LinkedNode); - const UK2Node_BaseAsyncTask* LinkedAsyncTaskNode = Cast(LinkedNode); - if ((LinkedFunctionNode && !LinkedFunctionNode->IsNodePure()) || LinkedAsyncTaskNode) + if (ThisPin->Direction == EGPD_Input) { - FGraphNodeImport& ThisNodeImport = ImportedNodes.FindOrAdd(ThisNode->NodeGuid); - ThisNodeImport.Connections.Add(ThisPin->PinName, FConnectedPin(LinkedNode->NodeGuid, LinkedPin->PinName)); - - FGraphNodeImport& LinkedNodeImport = ImportedNodes.FindOrAdd(LinkedNode->NodeGuid); - LinkedNodeImport.SourceGraphNode = LinkedNode; + NodeImport.Incoming.Add(ThisPin->PinName, ConnectedPin); + } + else + { + NodeImport.Outgoing.Add(ThisPin->PinName, ConnectedPin); } } } @@ -150,138 +153,165 @@ void UFlowImportUtils::ImportBlueprintGraph(UBlueprint* Blueprint, const UFlowAs } // clear existing graph - UEdGraph* FlowGraph = FlowAsset->GetGraph(); + UFlowGraph* FlowGraph = Cast(FlowAsset->GetGraph()); FlowGraph->Nodes.Empty(); + TMap TargetNodes; + // recreated UFlowNode_Start, assign it a blueprint node FGuid - UFlowGraphNode* NewGraphNode = FFlowGraphSchemaAction_NewNode::CreateNode(FlowGraph, nullptr, UFlowNode_Start::StaticClass(), FVector2D::ZeroVector); - FlowGraph->GetSchema()->SetNodeMetaData(NewGraphNode, FNodeMetadata::DefaultGraphNode); - NewGraphNode->NodeGuid = StartNode->NodeGuid; - NewGraphNode->GetFlowNode()->SetGuid(StartNode->NodeGuid); + UFlowGraphNode* StartGraphNode = FFlowGraphSchemaAction_NewNode::CreateNode(FlowGraph, nullptr, UFlowNode_Start::StaticClass(), FVector2D::ZeroVector); + FlowGraph->GetSchema()->SetNodeMetaData(StartGraphNode, FNodeMetadata::DefaultGraphNode); + StartGraphNode->NodeGuid = StartNode->NodeGuid; + StartGraphNode->GetFlowNode()->SetGuid(StartNode->NodeGuid); + TargetNodes.Add(StartGraphNode->NodeGuid, StartGraphNode); // execute graph import - ImportBlueprintFunction_Recursive(FlowGraph->Nodes[0], ImportedNodes); + // iterate all nodes separately, ensures we import all possible nodes and connect them together + for (const TPair& SourceNode : SourceNodes) + { + ImportBlueprintFunction(FlowAsset, SourceNode.Value, SourceNodes, TargetNodes); + } } -void UFlowImportUtils::ImportBlueprintFunction_Recursive(UEdGraphNode* PrecedingGraphNode, const TMap SourceNodes) +void UFlowImportUtils::ImportBlueprintFunction(UFlowAsset* FlowAsset, const FImportedGraphNode& NodeImport, const TMap& SourceNodes, TMap& TargetNodes) { - UEdGraph* Graph = PrecedingGraphNode->GetGraph(); - const FGraphNodeImport& PrecedingNode = SourceNodes.FindRef(PrecedingGraphNode->NodeGuid); + ensureAlways(NodeImport.SourceGraphNode); + TSubclassOf MatchingFlowNodeClass = nullptr; - const UFlowGraphNode* PrecedingFlowGraphNode = Cast(PrecedingGraphNode); - if (PrecedingFlowGraphNode == nullptr || PrecedingFlowGraphNode->OutputPins.IsEmpty()) + // find FlowNode class matching provided UFunction name + FName FunctionName = NAME_None; + if (const UK2Node_CallFunction* FunctionNode = Cast(NodeImport.SourceGraphNode)) { - return; + FunctionName = FunctionNode->GetFunctionName(); } - - for (const TPair& Connection : PrecedingNode.Connections) + else if (const UK2Node_BaseAsyncTask* AsyncTaskNode = Cast(NodeImport.SourceGraphNode)) { - const FGuid& LinkedNodeGuid = Connection.Value.NodeGuid; - const FGraphNodeImport& LinkedNodeImport = SourceNodes.FindRef(LinkedNodeGuid); + FunctionName = AsyncTaskNode->GetProxyFactoryFunctionName(); + } + if (!FunctionName.IsNone()) + { // find FlowNode class matching provided UFunction name - TSubclassOf MatchingFlowNodeClass = nullptr; - if (const UK2Node_CallFunction* FunctionNode = Cast(LinkedNodeImport.SourceGraphNode)) - { - // find FlowNode class matching provided UFunction name - MatchingFlowNodeClass = UFlowSettings::Get()->BlueprintFunctionsToFlowNodes.FindRef(FunctionNode->GetFunctionName()); - } - else if (const UK2Node_BaseAsyncTask* AsyncTaskNode = Cast(LinkedNodeImport.SourceGraphNode)) - { - // find FlowNode class matching provided AsyncTask name - MatchingFlowNodeClass = UFlowSettings::Get()->BlueprintFunctionsToFlowNodes.FindRef(AsyncTaskNode->GetProxyFactoryFunctionName()); - } + MatchingFlowNodeClass = FunctionsToFlowNodes.FindRef(FunctionName); + } + + if (MatchingFlowNodeClass == nullptr) + { + UE_LOG(LogFlowEditor, Error, TEXT("Can't find Flow Node class for K2Node, function name %s"), *FunctionName.ToString()); + return; + } - if (MatchingFlowNodeClass == nullptr) - { - continue; - } + const FGuid& NodeGuid = NodeImport.SourceGraphNode->NodeGuid; + + // create a new Flow Graph node + const FVector2d Location = FVector2D(NodeImport.SourceGraphNode->NodePosX, NodeImport.SourceGraphNode->NodePosY); + UFlowGraphNode* FlowGraphNode = FFlowGraphSchemaAction_NewNode::ImportNode(FlowAsset->GetGraph(), nullptr, MatchingFlowNodeClass, NodeGuid, Location); + + if (FlowGraphNode == nullptr) + { + return; + } + TargetNodes.Add(NodeGuid, FlowGraphNode); - // find output pin of preceding node - UEdGraphPin* OutputPin = nullptr; - for (UEdGraphPin* Pin : PrecedingFlowGraphNode->OutputPins) + // transfer properties from UFunction input parameters to Flow Node properties + { + TMap InputPins; + for (UEdGraphPin* Pin : NodeImport.SourceGraphNode->Pins) { - if (Pin->PinName == Connection.Key || PrecedingFlowGraphNode->OutputPins.Num() == 1) + if (Pin->Direction == EGPD_Input && !Pin->bHidden && !Pin->bOrphanedPin) { - OutputPin = Pin; - break; + InputPins.Add(Pin->PinName, Pin); } } - if (OutputPin == nullptr) + + for (TFieldIterator PropIt(FlowGraphNode->GetFlowNode()->GetClass(), EFieldIteratorFlags::IncludeSuper); PropIt && (PropIt->PropertyFlags & CPF_Edit); ++PropIt) { - continue; + const FProperty* Param = *PropIt; + const bool bIsEditable = !Param->HasAnyPropertyFlags(CPF_Deprecated); + if (bIsEditable) + { + if (const UEdGraphPin* InputPin = InputPins.FindRef(*Param->GetAuthoredName())) + { + FString const PinValue = InputPin->GetDefaultAsString(); + uint8* Offset = Param->ContainerPtrToValuePtr(FlowGraphNode->GetFlowNode()); + Param->ImportText_Direct(*PinValue, Offset, FlowGraphNode->GetFlowNode(), PPF_Copy, GLog); + } + } } + } - // check if already imported connected node - // todo: optimize? - UFlowGraphNode* LinkedGraphNode = nullptr; - for (const TObjectPtr ExistingGraphNode : Graph->Nodes) + // Flow Nodes with Context Pins needs to update related data and call OnReconstructionRequested.ExecuteIfBound() in order to fully construct a graph node + FlowGraphNode->GetFlowNode()->PostImport(); + + // connect new node to all already recreated nodes + for (const TPair& Connection : NodeImport.Incoming) + { + UEdGraphPin* ThisPin = nullptr; + for (UEdGraphPin* Pin : FlowGraphNode->InputPins) { - if (ExistingGraphNode->NodeGuid == LinkedNodeGuid) + if (Pin->PinName == Connection.Key || FlowGraphNode->InputPins.Num() == 1) { - LinkedGraphNode = Cast(ExistingGraphNode); + ThisPin = Pin; break; } } - - if (LinkedGraphNode == nullptr) + if (ThisPin == nullptr) { - // create a new Flow Graph node - const FVector2d Location = FVector2D(LinkedNodeImport.SourceGraphNode->NodePosX, LinkedNodeImport.SourceGraphNode->NodePosY); - LinkedGraphNode = FFlowGraphSchemaAction_NewNode::ImportNode(PrecedingGraphNode->GetGraph(), OutputPin, MatchingFlowNodeClass, LinkedNodeGuid, Location); + continue; } - else + + UEdGraphPin* ConnectedPin = nullptr; + if (UFlowGraphNode* ConnectedNode = TargetNodes.FindRef(Connection.Value.NodeGuid)) { - UEdGraphPin* LinkedPin = nullptr; - for (UEdGraphPin* InputPin : LinkedGraphNode->InputPins) + for (UEdGraphPin* Pin : ConnectedNode->OutputPins) { - if (InputPin->PinName == Connection.Value.PinName || LinkedGraphNode->InputPins.Num() == 1) + if (ConnectedNode->OutputPins.Num() == 1 || Pin->PinName == Connection.Value.PinName) { - LinkedPin = InputPin; + ConnectedPin = Pin; break; } } + } - // just link the pin to existing node - if (LinkedPin) + // link the pin to existing node + if (ConnectedPin) + { + FlowAsset->GetGraph()->GetSchema()->TryCreateConnection(ThisPin, ConnectedPin); + } + } + for (const TPair& Connection : NodeImport.Outgoing) + { + UEdGraphPin* ThisPin = nullptr; + for (UEdGraphPin* Pin : FlowGraphNode->OutputPins) + { + if (Pin->PinName == Connection.Key || FlowGraphNode->OutputPins.Num() == 1) { - Graph->GetSchema()->TryCreateConnection(OutputPin, LinkedPin); + ThisPin = Pin; + break; } } - - if (LinkedGraphNode) + if (ThisPin == nullptr) { - // transfer properties from UFunction input parameters to Flow Node properties + continue; + } + + UEdGraphPin* ConnectedPin = nullptr; + if (UFlowGraphNode* ConnectedNode = TargetNodes.FindRef(Connection.Value.NodeGuid)) + { + for (UEdGraphPin* Pin : ConnectedNode->InputPins) { - TMap InputPins; - for (UEdGraphPin* Pin : LinkedNodeImport.SourceGraphNode->Pins) - { - if (Pin->Direction == EGPD_Input && !Pin->bHidden && !Pin->bOrphanedPin) - { - InputPins.Add(Pin->PinName, Pin); - } - } - - for (TFieldIterator PropIt(LinkedGraphNode->GetFlowNode()->GetClass(), EFieldIteratorFlags::IncludeSuper); PropIt && (PropIt->PropertyFlags & CPF_Edit); ++PropIt) + if (ConnectedNode->InputPins.Num() == 1 || Pin->PinName == Connection.Value.PinName) { - const FProperty* Param = *PropIt; - const bool bIsEditable = !Param->HasAnyPropertyFlags(CPF_Deprecated); - if (bIsEditable) - { - if (const UEdGraphPin* InputPin = InputPins.FindRef(*Param->GetAuthoredName())) - { - FString const PinValue = InputPin->GetDefaultAsString(); - uint8* Offset = Param->ContainerPtrToValuePtr(LinkedGraphNode->GetFlowNode()); - Param->ImportText_Direct(*PinValue, Offset, LinkedGraphNode->GetFlowNode(), PPF_Copy, GLog); - } - } + ConnectedPin = Pin; + break; } } + } - LinkedGraphNode->GetFlowNode()->PostImport(); - - // iterate next nodes - ImportBlueprintFunction_Recursive(LinkedGraphNode, SourceNodes); + // link the pin to existing node + if (ConnectedPin) + { + FlowAsset->GetGraph()->GetSchema()->TryCreateConnection(ThisPin, ConnectedPin); } } } diff --git a/Source/FlowEditor/Public/Asset/FlowImportUtils.h b/Source/FlowEditor/Public/Asset/FlowImportUtils.h index e3d20e0e8..540c06990 100644 --- a/Source/FlowEditor/Public/Asset/FlowImportUtils.h +++ b/Source/FlowEditor/Public/Asset/FlowImportUtils.h @@ -6,16 +6,17 @@ #include "FlowImportUtils.generated.h" USTRUCT() -struct FLOWEDITOR_API FGraphNodeImport +struct FLOWEDITOR_API FImportedGraphNode { GENERATED_USTRUCT_BODY() UPROPERTY() UEdGraphNode* SourceGraphNode; - TMap Connections + TMultiMap Incoming; + TMultiMap Outgoing; - FGraphNodeImport() + FImportedGraphNode() : SourceGraphNode(nullptr) { } @@ -25,14 +26,16 @@ struct FLOWEDITOR_API FGraphNodeImport * */ UCLASS(meta = (ScriptName = "FlowImportUtils")) -class FLOWEDITOR_API UFlowImportUtils final : public UBlueprintFunctionLibrary +class FLOWEDITOR_API UFlowImportUtils : public UBlueprintFunctionLibrary { GENERATED_BODY() public: + static TMap> FunctionsToFlowNodes; + UFUNCTION(BlueprintCallable, Category = "FlowImportUtils") - static UFlowAsset* ImportBlueprintGraph(UObject* BlueprintAsset, TSubclassOf FlowAssetClass, FString FlowAssetName, const FName StartEventName = TEXT("BeginPlay")); + static UFlowAsset* ImportBlueprintGraph(UObject* BlueprintAsset, TSubclassOf FlowAssetClass, FString FlowAssetName, TMap> BlueprintFunctionsToFlowNodes, const FName StartEventName = TEXT("BeginPlay")); - static void ImportBlueprintGraph(UBlueprint* Blueprint, const UFlowAsset* FlowAsset, const FName StartEventName = TEXT("BeginPlay")); - static void ImportBlueprintFunction_Recursive(UEdGraphNode* PrecedingGraphNode, const TMap SourceNodes); + static void ImportBlueprintGraph(UBlueprint* Blueprint, UFlowAsset* FlowAsset, const FName StartEventName = TEXT("BeginPlay")); + static void ImportBlueprintFunction(UFlowAsset* FlowAsset, const FImportedGraphNode& NodeImport, const TMap& SourceNodes, TMap& TargetNodes); }; From c1904bb5b67c8f160d825ada50097e31bb834105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Tue, 10 Jan 2023 00:20:52 +0100 Subject: [PATCH 100/485] merge fix --- Source/FlowEditor/Private/Asset/FlowImportUtils.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp index 991166d6c..ca260ee54 100644 --- a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp +++ b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp @@ -15,7 +15,6 @@ #include "AssetToolsModule.h" #include "EdGraphNode_Comment.h" #include "EditorAssetLibrary.h" -#include "K2Node_BaseAsyncTask.h" #include "K2Node_CallFunction.h" #include "K2Node_Event.h" #include "Misc/ScopedSlowTask.h" @@ -184,10 +183,6 @@ void UFlowImportUtils::ImportBlueprintFunction(UFlowAsset* FlowAsset, const FImp { FunctionName = FunctionNode->GetFunctionName(); } - else if (const UK2Node_BaseAsyncTask* AsyncTaskNode = Cast(NodeImport.SourceGraphNode)) - { - FunctionName = AsyncTaskNode->GetProxyFactoryFunctionName(); - } if (!FunctionName.IsNone()) { From 79ac5c8488b281a700b25c08d2f9e635c28d9e4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Thu, 12 Jan 2023 19:39:50 +0100 Subject: [PATCH 101/485] Flow Import utils improvements - Matching blueprint Pins with Flow Node properties named differently. Name mappings need to provided by user, would messy to hardcode such information in Flow Nodes. - Iterating to get pin values from the first pure K2Node connected to the input pins. Supports simple situations like literal value nodes. - Special case to recognize blueprint Branch node. Added special pin mapping since actual Branch pin names are "then" and "else", not "True" and "False". --- .../Private/Asset/FlowImportUtils.cpp | 161 ++++++++++++++---- .../FlowEditor/Public/Asset/FlowImportUtils.h | 28 ++- 2 files changed, 153 insertions(+), 36 deletions(-) diff --git a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp index ca260ee54..ae94869bf 100644 --- a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp +++ b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp @@ -15,15 +15,19 @@ #include "AssetToolsModule.h" #include "EdGraphNode_Comment.h" #include "EditorAssetLibrary.h" +#include "K2Node_BaseAsyncTask.h" #include "K2Node_CallFunction.h" #include "K2Node_Event.h" +#include "K2Node_IfThenElse.h" #include "Misc/ScopedSlowTask.h" #define LOCTEXT_NAMESPACE "FlowImportUtils" TMap> UFlowImportUtils::FunctionsToFlowNodes = TMap>(); +TMap, FBlueprintToFlowPinName> UFlowImportUtils::PinMappings = TMap, FBlueprintToFlowPinName>(); -UFlowAsset* UFlowImportUtils::ImportBlueprintGraph(UObject* BlueprintAsset, TSubclassOf FlowAssetClass, FString FlowAssetName, TMap> BlueprintFunctionsToFlowNodes, const FName StartEventName) +UFlowAsset* UFlowImportUtils::ImportBlueprintGraph(UObject* BlueprintAsset, const TSubclassOf FlowAssetClass, const FString FlowAssetName, + const TMap> InFunctionsToFlowNodes, const TMap, FBlueprintToFlowPinName> InPinMappings, const FName StartEventName) { if (BlueprintAsset == nullptr || FlowAssetClass == nullptr || FlowAssetName.IsEmpty() || StartEventName.IsNone()) { @@ -33,7 +37,7 @@ UFlowAsset* UFlowImportUtils::ImportBlueprintGraph(UObject* BlueprintAsset, TSub UBlueprint* Blueprint = Cast(BlueprintAsset); UFlowAsset* FlowAsset = nullptr; - // we assume that users want to have a converted asset in the same folder as the legacy blueprint + // we assume that users want to have a converted asset in the same folder as the legacy blueprint const FString PackageFolder = FPaths::GetPath(Blueprint->GetOuter()->GetPathName()); if (!FPackageName::DoesPackageExist(PackageFolder / FlowAssetName, nullptr)) // create a new asset @@ -59,7 +63,9 @@ UFlowAsset* UFlowImportUtils::ImportBlueprintGraph(UObject* BlueprintAsset, TSub // import graph if (FlowAsset) { - FunctionsToFlowNodes = BlueprintFunctionsToFlowNodes; + FunctionsToFlowNodes = InFunctionsToFlowNodes; + PinMappings = InPinMappings; + ImportBlueprintGraph(Blueprint, FlowAsset, StartEventName); FunctionsToFlowNodes.Empty(); @@ -156,7 +162,7 @@ void UFlowImportUtils::ImportBlueprintGraph(UBlueprint* Blueprint, UFlowAsset* F FlowGraph->Nodes.Empty(); TMap TargetNodes; - + // recreated UFlowNode_Start, assign it a blueprint node FGuid UFlowGraphNode* StartGraphNode = FFlowGraphSchemaAction_NewNode::CreateNode(FlowGraph, nullptr, UFlowNode_Start::StaticClass(), FVector2D::ZeroVector); FlowGraph->GetSchema()->SetNodeMetaData(StartGraphNode, FNodeMetadata::DefaultGraphNode); @@ -172,7 +178,7 @@ void UFlowImportUtils::ImportBlueprintGraph(UBlueprint* Blueprint, UFlowAsset* F } } -void UFlowImportUtils::ImportBlueprintFunction(UFlowAsset* FlowAsset, const FImportedGraphNode& NodeImport, const TMap& SourceNodes, TMap& TargetNodes) +void UFlowImportUtils::ImportBlueprintFunction(const UFlowAsset* FlowAsset, const FImportedGraphNode& NodeImport, const TMap& SourceNodes, TMap& TargetNodes) { ensureAlways(NodeImport.SourceGraphNode); TSubclassOf MatchingFlowNodeClass = nullptr; @@ -183,13 +189,21 @@ void UFlowImportUtils::ImportBlueprintFunction(UFlowAsset* FlowAsset, const FImp { FunctionName = FunctionNode->GetFunctionName(); } + else if (const UK2Node_BaseAsyncTask* AsyncTaskNode = Cast(NodeImport.SourceGraphNode)) + { + FunctionName = AsyncTaskNode->GetProxyFactoryFunctionName(); + } + else if (Cast(NodeImport.SourceGraphNode)) + { + FunctionName = TEXT("Branch"); + } if (!FunctionName.IsNone()) { // find FlowNode class matching provided UFunction name MatchingFlowNodeClass = FunctionsToFlowNodes.FindRef(FunctionName); } - + if (MatchingFlowNodeClass == nullptr) { UE_LOG(LogFlowEditor, Error, TEXT("Can't find Flow Node class for K2Node, function name %s"), *FunctionName.ToString()); @@ -210,26 +224,67 @@ void UFlowImportUtils::ImportBlueprintFunction(UFlowAsset* FlowAsset, const FImp // transfer properties from UFunction input parameters to Flow Node properties { - TMap InputPins; - for (UEdGraphPin* Pin : NodeImport.SourceGraphNode->Pins) - { - if (Pin->Direction == EGPD_Input && !Pin->bHidden && !Pin->bOrphanedPin) - { - InputPins.Add(Pin->PinName, Pin); - } - } + TMap InputPins; + GetValidInputPins(NodeImport.SourceGraphNode, InputPins); - for (TFieldIterator PropIt(FlowGraphNode->GetFlowNode()->GetClass(), EFieldIteratorFlags::IncludeSuper); PropIt && (PropIt->PropertyFlags & CPF_Edit); ++PropIt) + UClass* FlowNodeClass = FlowGraphNode->GetFlowNode()->GetClass(); + for (TFieldIterator PropIt(FlowNodeClass, EFieldIteratorFlags::IncludeSuper); PropIt && (PropIt->PropertyFlags & CPF_Edit); ++PropIt) { const FProperty* Param = *PropIt; const bool bIsEditable = !Param->HasAnyPropertyFlags(CPF_Deprecated); if (bIsEditable) { - if (const UEdGraphPin* InputPin = InputPins.FindRef(*Param->GetAuthoredName())) + if (const UEdGraphPin* MatchingInputPin = FindPinMatchingToProperty(FlowNodeClass, Param, InputPins)) + { + if (MatchingInputPin->LinkedTo.Num() == 0) // nothing connected to pin, so user can set value directly on this pin + { + FString const PinValue = MatchingInputPin->GetDefaultAsString(); + uint8* Offset = Param->ContainerPtrToValuePtr(FlowGraphNode->GetFlowNode()); + Param->ImportText_Direct(*PinValue, Offset, FlowGraphNode->GetFlowNode(), PPF_Copy, GLog); + } + } + else // try to find matching Pin in connected pure nodes { - FString const PinValue = InputPin->GetDefaultAsString(); - uint8* Offset = Param->ContainerPtrToValuePtr(FlowGraphNode->GetFlowNode()); - Param->ImportText_Direct(*PinValue, Offset, FlowGraphNode->GetFlowNode(), PPF_Copy, GLog); + bool bPinFound = false; + for (const TPair InputPin : InputPins) + { + for (const UEdGraphPin* LinkedPin : InputPin.Value->LinkedTo) + { + if (LinkedPin && LinkedPin->GetOwningNode()) // try to read value from the first pure node connected to the pin + { + // in theory, we could put this part in recursive loop, iterating pure nodes until we find one with matching Pin Name + // in practice, iterating blueprint graph isn't that easy as might encounter Make/Break nodes, array builders + // if someone is willing put work to it, you're welcome to make a pull request + + UK2Node* LinkedK2Node = Cast(LinkedPin->GetOwningNode()); + if (LinkedK2Node && LinkedK2Node->IsNodePure()) + { + TMap PureNodePins; + GetValidInputPins(LinkedK2Node, PureNodePins); + + if (const UEdGraphPin* PureInputPin = FindPinMatchingToProperty(FlowNodeClass, Param, PureNodePins)) + { + if (PureInputPin->LinkedTo.Num() == 0) // nothing connected to pin, so user can set value directly on this pin + { + FString const PinValue = PureInputPin->GetDefaultAsString(); + uint8* Offset = Param->ContainerPtrToValuePtr(FlowGraphNode->GetFlowNode()); + Param->ImportText_Direct(*PinValue, Offset, FlowGraphNode->GetFlowNode(), PPF_Copy, GLog); + + bPinFound = true; + } + } + } + + // there can be only single valid connection on input parameter pin + break; + } + } + + if (bPinFound) + { + break; + } + } } } } @@ -242,11 +297,11 @@ void UFlowImportUtils::ImportBlueprintFunction(UFlowAsset* FlowAsset, const FImp for (const TPair& Connection : NodeImport.Incoming) { UEdGraphPin* ThisPin = nullptr; - for (UEdGraphPin* Pin : FlowGraphNode->InputPins) + for (UEdGraphPin* FlowInputPin : FlowGraphNode->InputPins) { - if (Pin->PinName == Connection.Key || FlowGraphNode->InputPins.Num() == 1) + if (FlowGraphNode->InputPins.Num() == 1 || Connection.Key == FlowInputPin->PinName) { - ThisPin = Pin; + ThisPin = FlowInputPin; break; } } @@ -254,15 +309,17 @@ void UFlowImportUtils::ImportBlueprintFunction(UFlowAsset* FlowAsset, const FImp { continue; } - + UEdGraphPin* ConnectedPin = nullptr; if (UFlowGraphNode* ConnectedNode = TargetNodes.FindRef(Connection.Value.NodeGuid)) { - for (UEdGraphPin* Pin : ConnectedNode->OutputPins) + for (UEdGraphPin* FlowOutputPin : ConnectedNode->OutputPins) { - if (ConnectedNode->OutputPins.Num() == 1 || Pin->PinName == Connection.Value.PinName) + if (ConnectedNode->OutputPins.Num() == 1 || Connection.Value.PinName == FlowOutputPin->PinName + || (Connection.Value.PinName == UEdGraphSchema_K2::PN_Then && FlowOutputPin->PinName == FName("TRUE")) + || (Connection.Value.PinName == UEdGraphSchema_K2::PN_Else && FlowOutputPin->PinName == FName("FALSE"))) { - ConnectedPin = Pin; + ConnectedPin = FlowOutputPin; break; } } @@ -277,11 +334,13 @@ void UFlowImportUtils::ImportBlueprintFunction(UFlowAsset* FlowAsset, const FImp for (const TPair& Connection : NodeImport.Outgoing) { UEdGraphPin* ThisPin = nullptr; - for (UEdGraphPin* Pin : FlowGraphNode->OutputPins) + for (UEdGraphPin* FlowOutputPin : FlowGraphNode->OutputPins) { - if (Pin->PinName == Connection.Key || FlowGraphNode->OutputPins.Num() == 1) + if (FlowGraphNode->OutputPins.Num() == 1 || Connection.Key == FlowOutputPin->PinName + || (Connection.Key == UEdGraphSchema_K2::PN_Then && FlowOutputPin->PinName == FName("TRUE")) + || (Connection.Key == UEdGraphSchema_K2::PN_Else && FlowOutputPin->PinName == FName("FALSE"))) { - ThisPin = Pin; + ThisPin = FlowOutputPin; break; } } @@ -289,15 +348,15 @@ void UFlowImportUtils::ImportBlueprintFunction(UFlowAsset* FlowAsset, const FImp { continue; } - + UEdGraphPin* ConnectedPin = nullptr; if (UFlowGraphNode* ConnectedNode = TargetNodes.FindRef(Connection.Value.NodeGuid)) { - for (UEdGraphPin* Pin : ConnectedNode->InputPins) + for (UEdGraphPin* FlowInputPin : ConnectedNode->InputPins) { - if (ConnectedNode->InputPins.Num() == 1 || Pin->PinName == Connection.Value.PinName) + if (ConnectedNode->InputPins.Num() == 1 || Connection.Value.PinName == FlowInputPin->PinName) { - ConnectedPin = Pin; + ConnectedPin = FlowInputPin; break; } } @@ -311,4 +370,40 @@ void UFlowImportUtils::ImportBlueprintFunction(UFlowAsset* FlowAsset, const FImp } } +void UFlowImportUtils::GetValidInputPins(const UEdGraphNode* GraphNode, TMap& Result) +{ + for (const UEdGraphPin* Pin : GraphNode->Pins) + { + if (Pin->Direction == EGPD_Input && !Pin->bHidden && !Pin->bOrphanedPin) + { + Result.Add(Pin->PinName, Pin); + } + } +} + +const UEdGraphPin* UFlowImportUtils::FindPinMatchingToProperty(UClass* FlowNodeClass, const FProperty* Property, const TMap Pins) +{ + const FName& PropertyAuthoredName = *Property->GetAuthoredName(); + + // if Pin Name is exactly the same as Flow Node property name + if (const UEdGraphPin* Pin = Pins.FindRef(PropertyAuthoredName)) + { + return Pin; + } + + // if not, check if appropriate Pin Mapping has been provided + if (const FBlueprintToFlowPinName* PinMapping = PinMappings.Find(FlowNodeClass)) + { + if (const FName* MappedPinName = PinMapping->NodePropertiesToFunctionPins.Find(PropertyAuthoredName)) + { + if (const UEdGraphPin* Pin = Pins.FindRef(*MappedPinName)) + { + return Pin; + } + } + } + + return nullptr; +} + #undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Public/Asset/FlowImportUtils.h b/Source/FlowEditor/Public/Asset/FlowImportUtils.h index 540c06990..a18917b5c 100644 --- a/Source/FlowEditor/Public/Asset/FlowImportUtils.h +++ b/Source/FlowEditor/Public/Asset/FlowImportUtils.h @@ -5,6 +5,7 @@ #include "FlowAsset.h" #include "FlowImportUtils.generated.h" +// Helper structure allowing to recreate blueprint graph as Flow Graph USTRUCT() struct FLOWEDITOR_API FImportedGraphNode { @@ -22,6 +23,22 @@ struct FLOWEDITOR_API FImportedGraphNode } }; +// Helper structure allowing to copy properties from blueprint function pin to the Flow Node property of different name +USTRUCT(BlueprintType) +struct FLOWEDITOR_API FBlueprintToFlowPinName +{ + GENERATED_USTRUCT_BODY() + + // Key represents Flow Node property name + // Value represents Input Pin name of blueprint function + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TMap NodePropertiesToFunctionPins; + + FBlueprintToFlowPinName() + { + } +}; + /** * */ @@ -32,10 +49,15 @@ class FLOWEDITOR_API UFlowImportUtils : public UBlueprintFunctionLibrary public: static TMap> FunctionsToFlowNodes; - + static TMap, FBlueprintToFlowPinName> PinMappings; + UFUNCTION(BlueprintCallable, Category = "FlowImportUtils") - static UFlowAsset* ImportBlueprintGraph(UObject* BlueprintAsset, TSubclassOf FlowAssetClass, FString FlowAssetName, TMap> BlueprintFunctionsToFlowNodes, const FName StartEventName = TEXT("BeginPlay")); + static UFlowAsset* ImportBlueprintGraph(UObject* BlueprintAsset, const TSubclassOf FlowAssetClass, const FString FlowAssetName, + const TMap> InFunctionsToFlowNodes, const TMap, FBlueprintToFlowPinName> InPinMappings, const FName StartEventName = TEXT("BeginPlay")); static void ImportBlueprintGraph(UBlueprint* Blueprint, UFlowAsset* FlowAsset, const FName StartEventName = TEXT("BeginPlay")); - static void ImportBlueprintFunction(UFlowAsset* FlowAsset, const FImportedGraphNode& NodeImport, const TMap& SourceNodes, TMap& TargetNodes); + static void ImportBlueprintFunction(const UFlowAsset* FlowAsset, const FImportedGraphNode& NodeImport, const TMap& SourceNodes, TMap& TargetNodes); + + static void GetValidInputPins(const UEdGraphNode* GraphNode, TMap& Result); + static const UEdGraphPin* FindPinMatchingToProperty(UClass* FlowNodeClass, const FProperty* Property, const TMapPins); }; From af7c1278c25f874aa70f379c9a4c0a11f04d350b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Thu, 12 Jan 2023 21:34:43 +0100 Subject: [PATCH 102/485] added define and docs on optional check --- Source/FlowEditor/Private/Asset/FlowImportUtils.cpp | 9 +++++++-- Source/FlowEditor/Public/FlowEditorDefines.h | 6 ++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp index ae94869bf..6ed9c16f0 100644 --- a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp +++ b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp @@ -15,11 +15,14 @@ #include "AssetToolsModule.h" #include "EdGraphNode_Comment.h" #include "EditorAssetLibrary.h" +#include "Misc/ScopedSlowTask.h" + +#if ENABLE_ASYNC_NODES_IMPORT #include "K2Node_BaseAsyncTask.h" +#endif #include "K2Node_CallFunction.h" #include "K2Node_Event.h" #include "K2Node_IfThenElse.h" -#include "Misc/ScopedSlowTask.h" #define LOCTEXT_NAMESPACE "FlowImportUtils" @@ -37,7 +40,7 @@ UFlowAsset* UFlowImportUtils::ImportBlueprintGraph(UObject* BlueprintAsset, cons UBlueprint* Blueprint = Cast(BlueprintAsset); UFlowAsset* FlowAsset = nullptr; - // we assume that users want to have a converted asset in the same folder as the legacy blueprint + // we assume that users want to have a converted asset in the same folder as the legacy blueprint const FString PackageFolder = FPaths::GetPath(Blueprint->GetOuter()->GetPathName()); if (!FPackageName::DoesPackageExist(PackageFolder / FlowAssetName, nullptr)) // create a new asset @@ -189,10 +192,12 @@ void UFlowImportUtils::ImportBlueprintFunction(const UFlowAsset* FlowAsset, cons { FunctionName = FunctionNode->GetFunctionName(); } +#if ENABLE_ASYNC_NODES_IMPORT else if (const UK2Node_BaseAsyncTask* AsyncTaskNode = Cast(NodeImport.SourceGraphNode)) { FunctionName = AsyncTaskNode->GetProxyFactoryFunctionName(); } +#endif else if (Cast(NodeImport.SourceGraphNode)) { FunctionName = TEXT("Branch"); diff --git a/Source/FlowEditor/Public/FlowEditorDefines.h b/Source/FlowEditor/Public/FlowEditorDefines.h index 62cdd64fd..d035e3cad 100644 --- a/Source/FlowEditor/Public/FlowEditorDefines.h +++ b/Source/FlowEditor/Public/FlowEditorDefines.h @@ -13,3 +13,9 @@ * Set macro value to 1, if you made these changes to the engine: https://github.com/EpicGames/UnrealEngine/pull/9943 */ #define ENABLE_SEARCH_IN_ASSET_EDITOR 0 + +/** + * Documentation: https://github.com/MothCocoon/FlowGraph/wiki/Import-Utils + * Set macro value to 1, if you made these changes to the engine: https://github.com/EpicGames/UnrealEngine/pull/10004 + */ +#define ENABLE_ASYNC_NODES_IMPORT 0 From c7dd3eaa24b5d6608637f871f9b060c95aa60000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Thu, 12 Jan 2023 21:55:41 +0100 Subject: [PATCH 103/485] plugin-only compilation fix --- Source/FlowEditor/Public/Asset/FlowImportUtils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/FlowEditor/Public/Asset/FlowImportUtils.h b/Source/FlowEditor/Public/Asset/FlowImportUtils.h index a18917b5c..211bb0671 100644 --- a/Source/FlowEditor/Public/Asset/FlowImportUtils.h +++ b/Source/FlowEditor/Public/Asset/FlowImportUtils.h @@ -31,7 +31,7 @@ struct FLOWEDITOR_API FBlueprintToFlowPinName // Key represents Flow Node property name // Value represents Input Pin name of blueprint function - UPROPERTY(EditAnywhere, BlueprintReadWrite) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pins") TMap NodePropertiesToFunctionPins; FBlueprintToFlowPinName() From 8cc848cd37eebf671a36369046514aac594e9240 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Fri, 13 Jan 2023 14:34:51 +0100 Subject: [PATCH 104/485] fix crash on importing comment node --- .../FlowEditor/Private/Asset/FlowImportUtils.cpp | 3 +++ .../Private/Graph/FlowGraphSchema_Actions.cpp | 14 +++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp index 6ed9c16f0..9584db188 100644 --- a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp +++ b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp @@ -71,6 +71,7 @@ UFlowAsset* UFlowImportUtils::ImportBlueprintGraph(UObject* BlueprintAsset, cons ImportBlueprintGraph(Blueprint, FlowAsset, StartEventName); FunctionsToFlowNodes.Empty(); + PinMappings.Empty(); Cast(FlowAsset->GetGraph())->RefreshGraph(); UEditorAssetLibrary::SaveLoadedAsset(FlowAsset->GetPackage()); @@ -107,6 +108,8 @@ void UFlowImportUtils::ImportBlueprintGraph(UBlueprint* Blueprint, UFlowAsset* F if (UEdGraphNode_Comment* CommentCopy = Cast(NewNode)) { CommentCopy->NodeComment = CommentNode->NodeComment; + CommentCopy->NodeWidth = CommentNode->NodeWidth; + CommentCopy->NodeHeight = CommentNode->NodeHeight; CommentCopy->CommentColor = CommentNode->CommentColor; CommentCopy->FontSize = CommentNode->FontSize; diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp index 4d8464b26..7aea403a3 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp @@ -220,12 +220,16 @@ UEdGraphNode* FFlowGraphSchemaAction_NewComment::PerformAction(class UEdGraph* P UEdGraphNode_Comment* CommentTemplate = NewObject(); FVector2D SpawnLocation = Location; - FSlateRect Bounds; - if (FFlowGraphUtils::GetFlowAssetEditor(ParentGraph)->GetBoundsForSelectedNodes(Bounds, 50.0f)) + const TSharedPtr FlowAssetEditor = FFlowGraphUtils::GetFlowAssetEditor(ParentGraph); + if (FlowAssetEditor.IsValid()) { - CommentTemplate->SetBounds(Bounds); - SpawnLocation.X = CommentTemplate->NodePosX; - SpawnLocation.Y = CommentTemplate->NodePosY; + FSlateRect Bounds; + if (FlowAssetEditor->GetBoundsForSelectedNodes(Bounds, 50.0f)) + { + CommentTemplate->SetBounds(Bounds); + SpawnLocation.X = CommentTemplate->NodePosX; + SpawnLocation.Y = CommentTemplate->NodePosY; + } } return FEdGraphSchemaAction_NewNode::SpawnNodeFromTemplate(ParentGraph, CommentTemplate, SpawnLocation); From b5bd5169f39785b1bf220c94b1a49c6c645f98dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sat, 14 Jan 2023 22:40:31 +0100 Subject: [PATCH 105/485] exposed GetStartedNode, per community request --- Source/Flow/Private/FlowAsset.cpp | 15 +++++++++++++++ Source/Flow/Public/FlowAsset.h | 2 ++ 2 files changed, 17 insertions(+) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 45c330ac6..1d0c25b10 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -196,6 +196,21 @@ void UFlowAsset::HarvestNodeConnections() } #endif +UFlowNode_Start* UFlowAsset::GetStartNode() const +{ + for (const TPair& Node : Nodes) + { + // there can be only one, automatically added while creating graph + if (UFlowNode_Start* TestedNode = Cast(Node.Value)) + { + return TestedNode; + } + } + + // shouldn't ever get here, Start Node is a default node that can't be deleted by user + return nullptr; +} + void UFlowAsset::AddInstance(UFlowAsset* Instance) { ActiveInstances.Add(Instance); diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index efb57a00e..d5d529fe5 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -151,6 +151,8 @@ class FLOW_API UFlowAsset : public UObject TArray GetCustomInputs() const { return CustomInputs; } TArray GetCustomOutputs() const { return CustomOutputs; } + UFlowNode_Start* GetStartNode() const; + ////////////////////////////////////////////////////////////////////////// // Instances of the template asset From 8badd30e71f1698cadda253a6245d7e7923f20b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sat, 14 Jan 2023 22:41:02 +0100 Subject: [PATCH 106/485] exposed access to connections --- Source/Flow/Private/Nodes/FlowNode.cpp | 25 +++++++++++++++++++------ Source/Flow/Public/Nodes/FlowNode.h | 4 +++- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index dbd275761..36a5b481b 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -271,6 +271,19 @@ TSet UFlowNode::GetConnectedNodes() const return Result; } +FName UFlowNode::GetPinConnectedToNode(const FGuid& OtherNodeGuid) +{ + for (const TPair& Connection : Connections) + { + if (Connection.Value.NodeGuid == OtherNodeGuid) + { + return Connection.Key; + } + } + + return NAME_None; +} + bool UFlowNode::IsInputConnected(const FName& PinName) const { if (GetFlowAsset()) @@ -676,11 +689,11 @@ void UFlowNode::LogError(FString Message, const EFlowOnScreenMessageType OnScree GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, Message); } -#if WITH_EDITOR +#if WITH_EDITOR // Message Log - not yet functional { Log.Error(*Message, GetGraphNode()); - + FMessageLog MessageLog("FlowGraph"); MessageLog.AddMessages(Log.Messages); } @@ -696,11 +709,11 @@ void UFlowNode::LogWarning(FString Message) #if !UE_BUILD_SHIPPING BuildMessage(Message); -#if WITH_EDITOR +#if WITH_EDITOR // Message Log - not yet functional { Log.Warning(*Message, GetGraphNode()); - + FMessageLog MessageLog("FlowGraph"); MessageLog.AddMessages(Log.Messages); } @@ -716,11 +729,11 @@ void UFlowNode::LogNote(FString Message) #if !UE_BUILD_SHIPPING BuildMessage(Message); -#if WITH_EDITOR +#if WITH_EDITOR // Message Log - not yet functional { Log.Note(*Message, GetGraphNode()); - + FMessageLog MessageLog("FlowGraph"); MessageLog.AddMessages(Log.Messages); } diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index dc0a0ce48..694eda80f 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -195,7 +195,7 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte ////////////////////////////////////////////////////////////////////////// // Connections to other nodes -private: +protected: // Map outputs to the connected node and input pin UPROPERTY() TMap Connections; @@ -203,7 +203,9 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte public: void SetConnections(const TMap& InConnections) { Connections = InConnections; } FConnectedPin GetConnection(const FName OutputName) const { return Connections.FindRef(OutputName); } + TSet GetConnectedNodes() const; + FName GetPinConnectedToNode(const FGuid& OtherNodeGuid); UFUNCTION(BlueprintPure, Category= "FlowNode") bool IsInputConnected(const FName& PinName) const; From 420fbdac909cbe3b8b8dfc90da660ce3f02d0783 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sat, 14 Jan 2023 22:56:48 +0100 Subject: [PATCH 107/485] Flow Import improvements - Added Reroute node conversion. - Removed Comment node conversion as it doesn't work fully. Added lengthy description explaining intention behind this utility. --- .../Private/Asset/FlowImportUtils.cpp | 76 ++++++++----------- .../FlowEditor/Public/Asset/FlowImportUtils.h | 8 +- 2 files changed, 36 insertions(+), 48 deletions(-) diff --git a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp index 9584db188..df729514f 100644 --- a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp +++ b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp @@ -13,7 +13,6 @@ #include "AssetRegistry/AssetRegistryModule.h" #include "AssetToolsModule.h" -#include "EdGraphNode_Comment.h" #include "EditorAssetLibrary.h" #include "Misc/ScopedSlowTask.h" @@ -23,6 +22,7 @@ #include "K2Node_CallFunction.h" #include "K2Node_Event.h" #include "K2Node_IfThenElse.h" +#include "K2Node_Knot.h" #define LOCTEXT_NAMESPACE "FlowImportUtils" @@ -100,59 +100,39 @@ void UFlowImportUtils::ImportBlueprintGraph(UBlueprint* Blueprint, UFlowAsset* F { ExecuteAssetTask.EnterProgressFrame(1, FText::Format(LOCTEXT("FFlowGraphUtils::ImportBlueprintGraph", "Processing blueprint node: {0}"), ThisNode->GetNodeTitle(ENodeTitleType::ListView))); - if (UEdGraphNode_Comment* CommentNode = Cast(ThisNode)) + // non-pure K2Nodes or UK2Node_Knot + const UK2Node* K2Node = Cast(ThisNode); + if (K2Node && (!K2Node->IsNodePure() || Cast(K2Node))) { - // special case: recreate Comment node - FFlowGraphSchemaAction_NewComment CommentAction; - UEdGraphNode* NewNode = CommentAction.PerformAction(FlowAsset->GetGraph(), nullptr, FVector2D(CommentNode->NodePosX, CommentNode->NodePosY), false); - if (UEdGraphNode_Comment* CommentCopy = Cast(NewNode)) - { - CommentCopy->NodeComment = CommentNode->NodeComment; - CommentCopy->NodeWidth = CommentNode->NodeWidth; - CommentCopy->NodeHeight = CommentNode->NodeHeight; - - CommentCopy->CommentColor = CommentNode->CommentColor; - CommentCopy->FontSize = CommentNode->FontSize; - CommentCopy->bCommentBubbleVisible_InDetailsPanel = CommentNode->bCommentBubbleVisible_InDetailsPanel; - CommentCopy->bColorCommentBubble = CommentNode->bColorCommentBubble; - CommentCopy->MoveMode = CommentNode->MoveMode; - } - } - else // non-pure K2Nodes - { - const UK2Node* K2Node = Cast(ThisNode); - if (K2Node && !K2Node->IsNodePure()) - { - FImportedGraphNode& NodeImport = SourceNodes.FindOrAdd(ThisNode->NodeGuid); - NodeImport.SourceGraphNode = ThisNode; + FImportedGraphNode& NodeImport = SourceNodes.FindOrAdd(ThisNode->NodeGuid); + NodeImport.SourceGraphNode = ThisNode; - // create map of all non-pure blueprint nodes with theirs pin connections - for (const UEdGraphPin* ThisPin : ThisNode->Pins) + // create map of all non-pure blueprint nodes with theirs pin connections + for (const UEdGraphPin* ThisPin : ThisNode->Pins) + { + for (const UEdGraphPin* LinkedPin : ThisPin->LinkedTo) { - for (const UEdGraphPin* LinkedPin : ThisPin->LinkedTo) + if (LinkedPin && LinkedPin->GetOwningNode()) { - if (LinkedPin && LinkedPin->GetOwningNode()) - { - const FConnectedPin ConnectedPin(LinkedPin->GetOwningNode()->NodeGuid, LinkedPin->PinName); + const FConnectedPin ConnectedPin(LinkedPin->GetOwningNode()->NodeGuid, LinkedPin->PinName); - if (ThisPin->Direction == EGPD_Input) - { - NodeImport.Incoming.Add(ThisPin->PinName, ConnectedPin); - } - else - { - NodeImport.Outgoing.Add(ThisPin->PinName, ConnectedPin); - } + if (ThisPin->Direction == EGPD_Input) + { + NodeImport.Incoming.Add(ThisPin->PinName, ConnectedPin); + } + else + { + NodeImport.Outgoing.Add(ThisPin->PinName, ConnectedPin); } } } + } - // we need to know the default entry point of blueprint graph - const UK2Node_Event* EventNode = Cast(ThisNode); - if (EventNode && (EventNode->EventReference.GetMemberName() == StartEventName || EventNode->CustomFunctionName == StartEventName)) - { - StartNode = ThisNode; - } + // we need to know the default entry point of blueprint graph + const UK2Node_Event* EventNode = Cast(ThisNode); + if (EventNode && (EventNode->EventReference.GetMemberName() == StartEventName || EventNode->CustomFunctionName == StartEventName)) + { + StartNode = ThisNode; } } } @@ -184,7 +164,7 @@ void UFlowImportUtils::ImportBlueprintGraph(UBlueprint* Blueprint, UFlowAsset* F } } -void UFlowImportUtils::ImportBlueprintFunction(const UFlowAsset* FlowAsset, const FImportedGraphNode& NodeImport, const TMap& SourceNodes, TMap& TargetNodes) +void UFlowImportUtils::ImportBlueprintFunction(const UFlowAsset* FlowAsset, const FImportedGraphNode& NodeImport, const TMap& SourceNodes, TMap& TargetNodes) { ensureAlways(NodeImport.SourceGraphNode); TSubclassOf MatchingFlowNodeClass = nullptr; @@ -201,6 +181,10 @@ void UFlowImportUtils::ImportBlueprintFunction(const UFlowAsset* FlowAsset, cons FunctionName = AsyncTaskNode->GetProxyFactoryFunctionName(); } #endif + else if (Cast(NodeImport.SourceGraphNode)) + { + FunctionName = TEXT("Reroute"); + } else if (Cast(NodeImport.SourceGraphNode)) { FunctionName = TEXT("Branch"); diff --git a/Source/FlowEditor/Public/Asset/FlowImportUtils.h b/Source/FlowEditor/Public/Asset/FlowImportUtils.h index 211bb0671..63e4c08e1 100644 --- a/Source/FlowEditor/Public/Asset/FlowImportUtils.h +++ b/Source/FlowEditor/Public/Asset/FlowImportUtils.h @@ -3,6 +3,7 @@ #pragma once #include "FlowAsset.h" +#include "Nodes/FlowPin.h" #include "FlowImportUtils.generated.h" // Helper structure allowing to recreate blueprint graph as Flow Graph @@ -40,7 +41,10 @@ struct FLOWEDITOR_API FBlueprintToFlowPinName }; /** - * + * Groundwork for converting blueprint graphs to Flow Graph. + * It's NOT meant to be universal, out-of-box solution as complexity of blueprint graphs conflicts with simplicity of Flow Graph. + * However, it might useful to provide this basic utility to anyone who would like to batch-convert their custom blueprint-based event system to Flow Graph. + * Pull requests are welcome if you able to improve this utility w/o with minimal amount of code. */ UCLASS(meta = (ScriptName = "FlowImportUtils")) class FLOWEDITOR_API UFlowImportUtils : public UBlueprintFunctionLibrary @@ -56,7 +60,7 @@ class FLOWEDITOR_API UFlowImportUtils : public UBlueprintFunctionLibrary const TMap> InFunctionsToFlowNodes, const TMap, FBlueprintToFlowPinName> InPinMappings, const FName StartEventName = TEXT("BeginPlay")); static void ImportBlueprintGraph(UBlueprint* Blueprint, UFlowAsset* FlowAsset, const FName StartEventName = TEXT("BeginPlay")); - static void ImportBlueprintFunction(const UFlowAsset* FlowAsset, const FImportedGraphNode& NodeImport, const TMap& SourceNodes, TMap& TargetNodes); + static void ImportBlueprintFunction(const UFlowAsset* FlowAsset, const FImportedGraphNode& NodeImport, const TMap& SourceNodes, TMap& TargetNodes); static void GetValidInputPins(const UEdGraphNode* GraphNode, TMap& Result); static const UEdGraphPin* FindPinMatchingToProperty(UClass* FlowNodeClass, const FProperty* Property, const TMapPins); From c95554116bc49daa5326ef790d4a352f117ffedd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 15 Jan 2023 01:38:36 +0100 Subject: [PATCH 108/485] exposed blueprintable functions also to C++ --- Source/Flow/Public/Nodes/FlowNode.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 694eda80f..fc7711410 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -162,13 +162,13 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte TArray GetInputPins() const { return InputPins; } TArray GetOutputPins() const { return OutputPins; } +public: UFUNCTION(BlueprintPure, Category = "FlowNode") TArray GetInputNames() const; UFUNCTION(BlueprintPure, Category = "FlowNode") TArray GetOutputNames() const; -public: #if WITH_EDITOR virtual bool SupportsContextPins() const { return false; } From 84af7957b7d667360c9d5a216ab91927f31f47e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Mon, 16 Jan 2023 01:39:42 +0100 Subject: [PATCH 109/485] correct graph cleanup while importing blueprint graph --- Source/FlowEditor/Private/Asset/FlowImportUtils.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp index df729514f..446ff7661 100644 --- a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp +++ b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp @@ -145,7 +145,16 @@ void UFlowImportUtils::ImportBlueprintGraph(UBlueprint* Blueprint, UFlowAsset* F // clear existing graph UFlowGraph* FlowGraph = Cast(FlowAsset->GetGraph()); - FlowGraph->Nodes.Empty(); + for (const TPair& Node : FlowAsset->GetNodes()) + { + if (UFlowGraphNode* FlowGraphNode = Cast(Node.Value->GetGraphNode())) + { + FlowGraph->GetSchema()->BreakNodeLinks(*FlowGraphNode); + FlowGraphNode->DestroyNode(); + } + + FlowAsset->UnregisterNode(Node.Key); + } TMap TargetNodes; From 27cfb4ad1ca984b6da46969fded5abdc51efd1df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Tue, 17 Jan 2023 21:15:12 +0100 Subject: [PATCH 110/485] removed LoadAsset template method as it was redundant --- .../Private/Nodes/Route/FlowNode_SubGraph.cpp | 2 +- .../Nodes/World/FlowNode_PlayLevelSequence.cpp | 8 ++++---- Source/Flow/Public/Nodes/FlowNode.h | 16 +--------------- 3 files changed, 6 insertions(+), 20 deletions(-) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp index 006c15dda..c55d13b21 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp @@ -103,7 +103,7 @@ FString UFlowNode_SubGraph::GetNodeDescription() const UObject* UFlowNode_SubGraph::GetAssetToEdit() { - return Asset.IsNull() ? nullptr : LoadAsset(Asset); + return Asset.IsNull() ? nullptr : Asset.LoadSynchronous(); } EDataValidationResult UFlowNode_SubGraph::ValidateNode() diff --git a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp index 1927105d2..93b3112ae 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp @@ -127,7 +127,7 @@ void UFlowNode_PlayLevelSequence::InitializeInstance() void UFlowNode_PlayLevelSequence::CreatePlayer() { - LoadedSequence = LoadAsset(Sequence); + LoadedSequence = Sequence.LoadSynchronous(); if (LoadedSequence) { ALevelSequenceActor* SequenceActor; @@ -175,7 +175,7 @@ void UFlowNode_PlayLevelSequence::ExecuteInput(const FName& PinName) { if (PinName == TEXT("Start")) { - LoadedSequence = LoadAsset(Sequence); + LoadedSequence = Sequence.LoadSynchronous(); if (GetFlowSubsystem()->GetWorld() && LoadedSequence) { @@ -220,7 +220,7 @@ void UFlowNode_PlayLevelSequence::OnLoad_Implementation() { if (ElapsedTime != 0.0f) { - LoadedSequence = LoadAsset(Sequence); + LoadedSequence = Sequence.LoadSynchronous(); if (GetFlowSubsystem()->GetWorld() && LoadedSequence) { CreatePlayer(); @@ -332,7 +332,7 @@ FString UFlowNode_PlayLevelSequence::GetStatusString() const UObject* UFlowNode_PlayLevelSequence::GetAssetToEdit() { - return Sequence.IsNull() ? nullptr : LoadAsset(Sequence); + return Sequence.IsNull() ? nullptr : Sequence.LoadSynchronous(); } #endif diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index fc7711410..4e2826f17 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -5,8 +5,8 @@ #include "EdGraph/EdGraphNode.h" #include "Engine/StreamableManager.h" #include "GameplayTagContainer.h" -#include "VisualLogger/VisualLoggerDebugSnapshotInterface.h" #include "Templates/SubclassOf.h" +#include "VisualLogger/VisualLoggerDebugSnapshotInterface.h" #include "FlowMessageLog.h" #include "FlowTypes.h" @@ -361,20 +361,6 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Get Actor To Focus")) AActor* K2_GetActorToFocus(); - template - T* LoadAsset(TSoftObjectPtr AssetPtr) - { - ensure(!AssetPtr.IsNull()); - - if (AssetPtr.IsPending()) - { - const FSoftObjectPath& AssetRef = AssetPtr.ToSoftObjectPath(); - AssetPtr = Cast(StreamableManager.LoadSynchronous(AssetRef, false)); - } - - return Cast(AssetPtr.Get()); - } - public: UFUNCTION(BlueprintPure, Category = "FlowNode") static FString GetIdentityTagDescription(const FGameplayTag& Tag); From aa911ab71348fdb646f168890c153d13d4628b54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Wed, 18 Jan 2023 00:13:47 +0100 Subject: [PATCH 111/485] initial version of runtime message log! --- Source/Flow/Private/FlowAsset.cpp | 43 +++++- Source/Flow/Private/FlowSubsystem.cpp | 16 +- Source/Flow/Private/Nodes/FlowNode.cpp | 46 +++--- .../Nodes/Route/FlowNode_CustomInput.cpp | 2 +- .../Nodes/Route/FlowNode_CustomOutput.cpp | 2 +- .../Private/Nodes/Route/FlowNode_SubGraph.cpp | 2 +- .../World/FlowNode_ComponentObserver.cpp | 2 +- .../Nodes/World/FlowNode_NotifyActor.cpp | 2 +- .../World/FlowNode_PlayLevelSequence.cpp | 2 +- Source/Flow/Public/FlowAsset.h | 16 +- Source/Flow/Public/FlowMessageLog.h | 9 +- Source/Flow/Public/FlowSubsystem.h | 4 +- Source/Flow/Public/Nodes/FlowNode.h | 2 +- .../Private/Asset/FlowAssetEditor.cpp | 139 ++++++++++++------ .../Private/Asset/FlowMessageLogListing.cpp | 17 ++- .../Private/Graph/Widgets/SFlowGraphNode.cpp | 4 +- .../FlowEditor/Public/Asset/FlowAssetEditor.h | 23 ++- .../Public/Asset/FlowMessageLogListing.h | 15 +- 18 files changed, 233 insertions(+), 113 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 1d0c25b10..7f3be67f2 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -77,10 +77,10 @@ EDataValidationResult UFlowAsset::ValidateAsset(FFlowMessageLog& MessageLog) { if (Node.Value) { - Node.Value->Log.Messages.Empty(); + Node.Value->ValidationLog.Messages.Empty(); if (Node.Value->ValidateNode() == EDataValidationResult::Invalid) { - MessageLog.Messages.Append(Node.Value->Log.Messages); + MessageLog.Messages.Append(Node.Value->ValidationLog.Messages); } } } @@ -281,6 +281,16 @@ void UFlowAsset::SetInspectedInstance(const FName& NewInspectedInstanceName) BroadcastDebuggerRefresh(); } + +void UFlowAsset::BroadcastDebuggerRefresh() const +{ + RefreshDebuggerEvent.Broadcast(); +} + +void UFlowAsset::BroadcastRuntimeMessageAdded(const UFlowAsset* AssetInstance, const TSharedRef& Message) const +{ + RuntimeMessageEvent.Broadcast(AssetInstance, Message); +} #endif void UFlowAsset::InitializeInstance(const TWeakObjectPtr InOwner, UFlowAsset* InTemplateAsset) @@ -497,6 +507,35 @@ UFlowAsset* UFlowAsset::GetParentInstance() const return NodeOwningThisAssetInstance.IsValid() ? NodeOwningThisAssetInstance.Get()->GetFlowAsset() : nullptr; } +#if WITH_EDITOR +void UFlowAsset::LogError(const FString& MessageToLog, UFlowNode* Node) const +{ + if (RuntimeLog.IsValid()) + { + const TSharedRef TokenizedMessage = RuntimeLog.Get()->Error(*MessageToLog, Node); + BroadcastRuntimeMessageAdded(this, TokenizedMessage); + } +} + +void UFlowAsset::LogWarning(const FString& MessageToLog, UFlowNode* Node) const +{ + if (RuntimeLog.IsValid()) + { + const TSharedRef TokenizedMessage = RuntimeLog.Get()->Warning(*MessageToLog, Node); + BroadcastRuntimeMessageAdded(this, TokenizedMessage); + } +} + +void UFlowAsset::LogNote(const FString& MessageToLog, UFlowNode* Node) const +{ + if (RuntimeLog.IsValid()) + { + const TSharedRef TokenizedMessage = RuntimeLog.Get()->Note(*MessageToLog, Node); + BroadcastRuntimeMessageAdded(this, TokenizedMessage); + } +} +#endif + FFlowAssetSaveData UFlowAsset::SaveInstance(TArray& SavedFlowInstances) { FFlowAssetSaveData AssetRecord; diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index 405673b25..a40defd61 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -101,7 +101,7 @@ UFlowAsset* UFlowSubsystem::CreateRootFlow(UObject* Owner, UFlowAsset* FlowAsset void UFlowSubsystem::FinishRootFlow(UObject* Owner, UFlowAsset* TemplateAsset, const EFlowFinishPolicy FinishPolicy) { UFlowAsset* InstanceToFinish = nullptr; - + for (TPair>& RootInstance : RootInstances) { if (Owner && Owner == RootInstance.Value.Get() && RootInstance.Key && RootInstance.Key->GetTemplateAsset() == TemplateAsset) @@ -121,7 +121,7 @@ void UFlowSubsystem::FinishRootFlow(UObject* Owner, UFlowAsset* TemplateAsset, c void UFlowSubsystem::FinishAllRootFlows(UObject* Owner, const EFlowFinishPolicy FinishPolicy) { TArray InstancesToFinish; - + for (TPair>& RootInstance : RootInstances) { if (Owner && Owner == RootInstance.Value.Get() && RootInstance.Key) @@ -194,7 +194,7 @@ UFlowAsset* UFlowSubsystem::CreateFlowInstance(const TWeakObjectPtr Own FlowAsset = Cast(Streamable.LoadSynchronous(FlowAsset.ToSoftObjectPath(), false)); } - InstancedTemplates.Add(FlowAsset.Get()); + AddInstancedTemplate(FlowAsset.Get()); #if WITH_EDITOR if (GetWorld()->WorldType != EWorldType::Game) @@ -218,8 +218,18 @@ UFlowAsset* UFlowSubsystem::CreateFlowInstance(const TWeakObjectPtr Own return NewInstance; } +void UFlowSubsystem::AddInstancedTemplate(UFlowAsset* Template) +{ + if (!InstancedTemplates.Contains(Template)) + { + InstancedTemplates.Add(Template); + Template->RuntimeLog = MakeShareable(new FFlowMessageLog()); + } +} + void UFlowSubsystem::RemoveInstancedTemplate(UFlowAsset* Template) { + Template->RuntimeLog.Reset(); InstancedTemplates.Remove(Template); } diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 36a5b481b..86b50f59d 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -3,7 +3,6 @@ #include "Nodes/FlowNode.h" #include "FlowAsset.h" -#include "FlowMessageLog.h" #include "FlowModule.h" #include "FlowSubsystem.h" #include "FlowTypes.h" @@ -666,6 +665,7 @@ FString UFlowNode::GetProgressAsString(float Value) void UFlowNode::LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType) { #if !UE_BUILD_SHIPPING + BuildMessage(Message); // OnScreen Message @@ -689,58 +689,48 @@ void UFlowNode::LogError(FString Message, const EFlowOnScreenMessageType OnScree GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, Message); } -#if WITH_EDITOR - // Message Log - not yet functional - { - Log.Error(*Message, GetGraphNode()); + // Output Log + UE_LOG(LogFlow, Error, TEXT("%s"), *Message); - FMessageLog MessageLog("FlowGraph"); - MessageLog.AddMessages(Log.Messages); - } + // Message Log +#if WITH_EDITOR + GetFlowAsset()->GetTemplateAsset()->LogError(Message, this); #endif - // Output Log - UE_LOG(LogFlow, Error, TEXT("%s"), *Message); #endif } void UFlowNode::LogWarning(FString Message) { #if !UE_BUILD_SHIPPING + BuildMessage(Message); -#if WITH_EDITOR - // Message Log - not yet functional - { - Log.Warning(*Message, GetGraphNode()); + // Output Log + UE_LOG(LogFlow, Warning, TEXT("%s"), *Message); - FMessageLog MessageLog("FlowGraph"); - MessageLog.AddMessages(Log.Messages); - } + // Message Log +#if WITH_EDITOR + GetFlowAsset()->GetTemplateAsset()->LogWarning(Message, this); #endif - // Output Log - UE_LOG(LogFlow, Warning, TEXT("%s"), *Message); #endif } void UFlowNode::LogNote(FString Message) { #if !UE_BUILD_SHIPPING + BuildMessage(Message); -#if WITH_EDITOR - // Message Log - not yet functional - { - Log.Note(*Message, GetGraphNode()); + // Output Log + UE_LOG(LogFlow, Log, TEXT("%s"), *Message); - FMessageLog MessageLog("FlowGraph"); - MessageLog.AddMessages(Log.Messages); - } + // Message Log +#if WITH_EDITOR + GetFlowAsset()->GetTemplateAsset()->LogNote(Message, this); #endif - // Output Log - UE_LOG(LogFlow, Log, TEXT("%s"), *Message); #endif } diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp index ae3946235..e7fa8c5ec 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp @@ -29,7 +29,7 @@ EDataValidationResult UFlowNode_CustomInput::ValidateNode() { if (EventName.IsNone()) { - Log.Error(TEXT("Event Name is empty!"), this); + ValidationLog.Error(TEXT("Event Name is empty!"), this); return EDataValidationResult::Invalid; } diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp index df59bc1d3..b53cbfc31 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp @@ -35,7 +35,7 @@ EDataValidationResult UFlowNode_CustomOutput::ValidateNode() { if (EventName.IsNone()) { - Log.Error(TEXT("Event Name is empty!"), this); + ValidationLog.Error(TEXT("Event Name is empty!"), this); return EDataValidationResult::Invalid; } diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp index c55d13b21..04bbe1e68 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp @@ -110,7 +110,7 @@ EDataValidationResult UFlowNode_SubGraph::ValidateNode() { if (Asset.IsNull()) { - Log.Error(TEXT("Flow Asset not assigned or invalid!"), this); + ValidationLog.Error(TEXT("Flow Asset not assigned or invalid!"), this); return EDataValidationResult::Invalid; } diff --git a/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp b/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp index 0ad362e00..73151f305 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp @@ -155,7 +155,7 @@ EDataValidationResult UFlowNode_ComponentObserver::ValidateNode() { if (IdentityTags.IsEmpty()) { - Log.Error(*UFlowNode::MissingIdentityTag, this); + ValidationLog.Error(*UFlowNode::MissingIdentityTag, this); return EDataValidationResult::Invalid; } diff --git a/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp b/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp index 12f303690..7340e5896 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp @@ -38,7 +38,7 @@ EDataValidationResult UFlowNode_NotifyActor::ValidateNode() { if (IdentityTags.IsEmpty()) { - Log.Error(*UFlowNode::MissingIdentityTag, this); + ValidationLog.Error(*UFlowNode::MissingIdentityTag, this); return EDataValidationResult::Invalid; } diff --git a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp index 93b3112ae..8a8b28740 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp @@ -318,7 +318,7 @@ EDataValidationResult UFlowNode_PlayLevelSequence::ValidateNode() { if (Sequence.IsNull()) { - Log.Error(TEXT("Level Sequence asset not assigned or invalid!"), this); + ValidationLog.Error(TEXT("Level Sequence asset not assigned or invalid!"), this); return EDataValidationResult::Invalid; } diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index d5d529fe5..21e4008ab 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -163,6 +163,9 @@ class FLOW_API UFlowAsset : public UObject #if WITH_EDITORONLY_DATA TWeakObjectPtr InspectedInstance; + + // Message log for storing runtime errors/notes/warnings that will only last until the next game run + TSharedPtr RuntimeLog; #endif public: @@ -182,8 +185,13 @@ class FLOW_API UFlowAsset : public UObject FRefreshDebuggerEvent& OnDebuggerRefresh() { return RefreshDebuggerEvent; } FRefreshDebuggerEvent RefreshDebuggerEvent; + DECLARE_EVENT_TwoParams(UFlowAsset, FRuntimeMessageEvent, const UFlowAsset*, const TSharedRef&); + FRuntimeMessageEvent& OnRuntimeMessageAdded() { return RuntimeMessageEvent; } + FRuntimeMessageEvent RuntimeMessageEvent; + private: - void BroadcastDebuggerRefresh() const { RefreshDebuggerEvent.Broadcast(); } + void BroadcastDebuggerRefresh() const; + void BroadcastRuntimeMessageAdded(const UFlowAsset* AssetInstance, const TSharedRef& Message) const;; #endif ////////////////////////////////////////////////////////////////////////// @@ -279,6 +287,12 @@ class FLOW_API UFlowAsset : public UObject UFUNCTION(BlueprintPure, Category = "Flow") TArray GetRecordedNodes() const { return RecordedNodes; } +#if WITH_EDITOR + void LogError(const FString& MessageToLog, UFlowNode* Node) const; + void LogWarning(const FString& MessageToLog, UFlowNode* Node) const; + void LogNote(const FString& MessageToLog, UFlowNode* Node) const; +#endif + ////////////////////////////////////////////////////////////////////////// // SaveGame diff --git a/Source/Flow/Public/FlowMessageLog.h b/Source/Flow/Public/FlowMessageLog.h index ee6308636..58ad0495b 100644 --- a/Source/Flow/Public/FlowMessageLog.h +++ b/Source/Flow/Public/FlowMessageLog.h @@ -57,24 +57,27 @@ class FLOW_API FFlowMessageLog } template - void Error(const TCHAR* Format, T* Object) + TSharedRef Error(const TCHAR* Format, T* Object) { TSharedRef Message = FTokenizedMessage::Create(EMessageSeverity::Error); AddMessage(NAME_None, Format, Message, Object); + return Message; } template - void Warning(const TCHAR* Format, T* Object) + TSharedRef Warning(const TCHAR* Format, T* Object) { TSharedRef Message = FTokenizedMessage::Create(EMessageSeverity::Warning); AddMessage(NAME_None, Format, Message, Object); + return Message; } template - void Note(const TCHAR* Format, T* Object) + TSharedRef Note(const TCHAR* Format, T* Object) { TSharedRef Message = FTokenizedMessage::Create(EMessageSeverity::Info); AddMessage(NAME_None, Format, Message, Object); + return Message; } protected: diff --git a/Source/Flow/Public/FlowSubsystem.h b/Source/Flow/Public/FlowSubsystem.h index c98d8c4a0..62d468384 100644 --- a/Source/Flow/Public/FlowSubsystem.h +++ b/Source/Flow/Public/FlowSubsystem.h @@ -85,7 +85,9 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem void RemoveSubFlow(UFlowNode_SubGraph* SubGraphNode, const EFlowFinishPolicy FinishPolicy); UFlowAsset* CreateFlowInstance(const TWeakObjectPtr Owner, TSoftObjectPtr FlowAsset, FString NewInstanceName = FString()); - void RemoveInstancedTemplate(UFlowAsset* Template); + + virtual void AddInstancedTemplate(UFlowAsset* Template); + virtual void RemoveInstancedTemplate(UFlowAsset* Template); public: /* Returns all assets instanced by object from another system like World Settings */ diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 4e2826f17..5b5feb3fa 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -130,7 +130,7 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte EFlowSignalMode SignalMode; #if WITH_EDITOR - FFlowMessageLog Log; + FFlowMessageLog ValidationLog; #endif ////////////////////////////////////////////////////////////////////////// diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index 1963478b1..5b27b0838 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -47,9 +47,10 @@ const FName FFlowAssetEditor::DetailsTab(TEXT("Details")); const FName FFlowAssetEditor::GraphTab(TEXT("Graph")); -const FName FFlowAssetEditor::MessagesTab(TEXT("Messages")); const FName FFlowAssetEditor::PaletteTab(TEXT("Palette")); +const FName FFlowAssetEditor::RuntimeLogTab(TEXT("RuntimeLog")); const FName FFlowAssetEditor::SearchTab(TEXT("Search")); +const FName FFlowAssetEditor::ValidationLogTab(TEXT("ValidationLog")); FFlowAssetEditor::FFlowAssetEditor() : FlowAsset(nullptr) @@ -124,26 +125,31 @@ void FFlowAssetEditor::RegisterTabSpawners(const TSharedRef& .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.Tabs.Details")); InTabManager->RegisterTabSpawner(GraphTab, FOnSpawnTab::CreateSP(this, &FFlowAssetEditor::SpawnTab_Graph)) - .SetDisplayName(LOCTEXT("GraphTab", "Viewport")) + .SetDisplayName(LOCTEXT("GraphTab", "Graph")) .SetGroup(WorkspaceMenuCategoryRef) .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "GraphEditor.EventGraph_16x")); - - InTabManager->RegisterTabSpawner(MessagesTab, FOnSpawnTab::CreateSP(this, &FFlowAssetEditor::SpawnTab_MessageLog)) - .SetDisplayName(LOCTEXT("MessagesTab", "Messages")) - .SetGroup(WorkspaceMenuCategoryRef) - .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.Tabs.CompilerResults")); InTabManager->RegisterTabSpawner(PaletteTab, FOnSpawnTab::CreateSP(this, &FFlowAssetEditor::SpawnTab_Palette)) .SetDisplayName(LOCTEXT("PaletteTab", "Palette")) .SetGroup(WorkspaceMenuCategoryRef) .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.Tabs.Palette")); + InTabManager->RegisterTabSpawner(RuntimeLogTab, FOnSpawnTab::CreateSP(this, &FFlowAssetEditor::SpawnTab_RuntimeLog)) + .SetDisplayName(LOCTEXT("RuntimeLog", "RuntimeLog")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.Tabs.CompilerResults")); + #if ENABLE_SEARCH_IN_ASSET_EDITOR InTabManager->RegisterTabSpawner(SearchTab, FOnSpawnTab::CreateSP(this, &FFlowAssetEditor::SpawnTab_Search)) .SetDisplayName(LOCTEXT("SearchTab", "Search")) .SetGroup(WorkspaceMenuCategoryRef) .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.Tabs.FindResults")); -#endif +#endif + + InTabManager->RegisterTabSpawner(ValidationLogTab, FOnSpawnTab::CreateSP(this, &FFlowAssetEditor::SpawnTab_ValidationLog)) + .SetDisplayName(LOCTEXT("ValidationLog", "Validation Log")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.Tabs.CompilerResults")); } void FFlowAssetEditor::UnregisterTabSpawners(const TSharedRef& InTabManager) @@ -152,7 +158,7 @@ void FFlowAssetEditor::UnregisterTabSpawners(const TSharedRef InTabManager->UnregisterTabSpawner(DetailsTab); InTabManager->UnregisterTabSpawner(GraphTab); - InTabManager->UnregisterTabSpawner(MessagesTab); + InTabManager->UnregisterTabSpawner(ValidationLogTab); InTabManager->UnregisterTabSpawner(PaletteTab); #if ENABLE_SEARCH_IN_ASSET_EDITOR InTabManager->UnregisterTabSpawner(SearchTab); @@ -170,23 +176,6 @@ TSharedRef FFlowAssetEditor::SpawnTab_Details(const FSpawnTabArgs& Arg ]; } -#if ENABLE_SEARCH_IN_ASSET_EDITOR -TSharedRef FFlowAssetEditor::SpawnTab_Search(const FSpawnTabArgs& Args) const -{ - check(Args.GetTabId() == SearchTab); - - return SNew(SDockTab) - .Label(LOCTEXT("FlowSearchTitle", "Search")) - [ - SNew(SBox) - .AddMetaData(FTagMetaData(TEXT("FlowSearch"))) - [ - SearchBrowser.ToSharedRef() - ] - ]; -} -#endif - TSharedRef FFlowAssetEditor::SpawnTab_Graph(const FSpawnTabArgs& Args) const { check(Args.GetTabId() == GraphTab); @@ -202,25 +191,53 @@ TSharedRef FFlowAssetEditor::SpawnTab_Graph(const FSpawnTabArgs& Args) return SpawnedTab; } -TSharedRef FFlowAssetEditor::SpawnTab_MessageLog(const FSpawnTabArgs& Args) const +TSharedRef FFlowAssetEditor::SpawnTab_Palette(const FSpawnTabArgs& Args) const { - check(Args.GetTabId() == MessagesTab); + check(Args.GetTabId() == PaletteTab); return SNew(SDockTab) - .Label(LOCTEXT("FlowMessagesTitle", "Messages")) + .Label(LOCTEXT("FlowPaletteTitle", "Palette")) [ - MessageLog.ToSharedRef() + Palette.ToSharedRef() ]; } -TSharedRef FFlowAssetEditor::SpawnTab_Palette(const FSpawnTabArgs& Args) const +TSharedRef FFlowAssetEditor::SpawnTab_RuntimeLog(const FSpawnTabArgs& Args) const { - check(Args.GetTabId() == PaletteTab); + check(Args.GetTabId() == RuntimeLogTab); return SNew(SDockTab) - .Label(LOCTEXT("FlowPaletteTitle", "Palette")) + .Label(LOCTEXT("FlowRuntimeLogTitle", "Runtime Log")) [ - Palette.ToSharedRef() + RuntimeLog.ToSharedRef() + ]; +} + +#if ENABLE_SEARCH_IN_ASSET_EDITOR +TSharedRef FFlowAssetEditor::SpawnTab_Search(const FSpawnTabArgs& Args) const +{ + check(Args.GetTabId() == SearchTab); + + return SNew(SDockTab) + .Label(LOCTEXT("FlowSearchTitle", "Search")) + [ + SNew(SBox) + .AddMetaData(FTagMetaData(TEXT("FlowSearch"))) + [ + SearchBrowser.ToSharedRef() + ] + ]; +} +#endif + +TSharedRef FFlowAssetEditor::SpawnTab_ValidationLog(const FSpawnTabArgs& Args) const +{ + check(Args.GetTabId() == ValidationLogTab); + + return SNew(SDockTab) + .Label(LOCTEXT("FlowValidationLogTitle", "Validation Log")) + [ + ValidationLog.ToSharedRef() ]; } @@ -241,7 +258,10 @@ void FFlowAssetEditor::InitFlowAssetEditor(const EToolkitMode::Type Mode, const BindGraphCommands(); CreateWidgets(); - const TSharedRef StandaloneDefaultLayout = FTabManager::NewLayout("FlowAssetEditor_Layout_v5") + FlowAsset->OnRuntimeMessageAdded().AddSP(this, &FFlowAssetEditor::OnRuntimeMessageAdded); + FEditorDelegates::BeginPIE.AddSP(this, &FFlowAssetEditor::OnBeginPIE); + + const TSharedRef StandaloneDefaultLayout = FTabManager::NewLayout("FlowAssetEditor_Layout_v5.1") ->AddArea ( FTabManager::NewPrimaryArea()->SetOrientation(Orient_Horizontal) @@ -266,15 +286,21 @@ void FFlowAssetEditor::InitFlowAssetEditor(const EToolkitMode::Type Mode, const ->Split ( FTabManager::NewStack() - ->SetSizeCoefficient(0.2f) - ->AddTab(MessagesTab, ETabState::ClosedTab) + ->SetSizeCoefficient(0.15f) + ->AddTab(RuntimeLogTab, ETabState::ClosedTab) ) ->Split ( FTabManager::NewStack() - ->SetSizeCoefficient(0.2f) + ->SetSizeCoefficient(0.15f) ->AddTab(SearchTab, ETabState::ClosedTab) ) + ->Split + ( + FTabManager::NewStack() + ->SetSizeCoefficient(0.15f) + ->AddTab(ValidationLogTab, ETabState::ClosedTab) + ) ) ->Split ( @@ -343,13 +369,13 @@ void FFlowAssetEditor::RefreshAsset() FlowAsset->ValidateAsset(LogResults); // push messages to its window - MessageLogListing->ClearMessages(); + ValidationLogListing->ClearMessages(); if (LogResults.Messages.Num() > 0) { - TabManager->TryInvokeTab(MessagesTab); - MessageLogListing->AddMessages(LogResults.Messages); + TabManager->TryInvokeTab(ValidationLogTab); + ValidationLogListing->AddMessages(LogResults.Messages); } - MessageLogListing->OnDataChanged().Broadcast(); + ValidationLogListing->OnDataChanged().Broadcast(); } #if ENABLE_SEARCH_IN_ASSET_EDITOR @@ -394,14 +420,18 @@ void FFlowAssetEditor::CreateWidgets() #if ENABLE_SEARCH_IN_ASSET_EDITOR SearchBrowser = SNew(SSearchBrowser, GetFlowAsset()); -#endif +#endif + FMessageLogModule& MessageLogModule = FModuleManager::LoadModuleChecked("MessageLog"); { - MessageLogListing = FFlowMessageLogListing::GetLogListing(FlowAsset); - MessageLogListing->OnMessageTokenClicked().AddSP(this, &FFlowAssetEditor::OnLogTokenClicked); - - FMessageLogModule& MessageLogModule = FModuleManager::LoadModuleChecked("MessageLog"); - MessageLog = MessageLogModule.CreateLogListingWidget(MessageLogListing.ToSharedRef()); + RuntimeLogListing = FFlowMessageLogListing::GetLogListing(FlowAsset, EFlowLogType::Runtime); + RuntimeLogListing->OnMessageTokenClicked().AddSP(this, &FFlowAssetEditor::OnLogTokenClicked); + RuntimeLog = MessageLogModule.CreateLogListingWidget(RuntimeLogListing.ToSharedRef()); + } + { + ValidationLogListing = FFlowMessageLogListing::GetLogListing(FlowAsset, EFlowLogType::Validation); + ValidationLogListing->OnMessageTokenClicked().AddSP(this, &FFlowAssetEditor::OnLogTokenClicked); + ValidationLog = MessageLogModule.CreateLogListingWidget(ValidationLogListing.ToSharedRef()); } } @@ -698,6 +728,11 @@ EVisibility FFlowAssetEditor::GetDebuggerVisibility() return GEditor->PlayWorld ? EVisibility::Visible : EVisibility::Collapsed; } +void FFlowAssetEditor::OnBeginPIE(const bool bInSimulateInEditor) const +{ + RuntimeLogListing->ClearMessages(); +} + TSet FFlowAssetEditor::GetSelectedFlowNodes() const { TSet Result; @@ -772,6 +807,14 @@ void FFlowAssetEditor::JumpToInnerObject(UObject* InnerObject) } #endif +void FFlowAssetEditor::OnRuntimeMessageAdded(const UFlowAsset* AssetInstance, const TSharedRef& Message) const +{ + // push messages to its window + TabManager->TryInvokeTab(RuntimeLogTab); + RuntimeLogListing->AddMessage(Message); + RuntimeLogListing->OnDataChanged().Broadcast(); +} + void FFlowAssetEditor::OnLogTokenClicked(const TSharedRef& Token) const { if (Token->GetType() == EMessageToken::Object) diff --git a/Source/FlowEditor/Private/Asset/FlowMessageLogListing.cpp b/Source/FlowEditor/Private/Asset/FlowMessageLogListing.cpp index 516630d1d..ce4d264f7 100644 --- a/Source/FlowEditor/Private/Asset/FlowMessageLogListing.cpp +++ b/Source/FlowEditor/Private/Asset/FlowMessageLogListing.cpp @@ -6,8 +6,8 @@ #define LOCTEXT_NAMESPACE "FlowMessageLogListing" -FFlowMessageLogListing::FFlowMessageLogListing(const UFlowAsset* InFlowAsset) - : Log(RegisterLogListing(InFlowAsset)) +FFlowMessageLogListing::FFlowMessageLogListing(const UFlowAsset* InFlowAsset, const EFlowLogType Type) + : Log(RegisterLogListing(InFlowAsset, Type)) { } @@ -21,11 +21,11 @@ FFlowMessageLogListing::~FFlowMessageLogListing() } } -TSharedRef FFlowMessageLogListing::RegisterLogListing(const UFlowAsset* InFlowAsset) +TSharedRef FFlowMessageLogListing::RegisterLogListing(const UFlowAsset* InFlowAsset, const EFlowLogType Type) { FMessageLogModule& MessageLogModule = FModuleManager::LoadModuleChecked("MessageLog"); - const FName LogName = GetListingName(InFlowAsset); + const FName LogName = GetListingName(InFlowAsset, Type); // Register the log (this will return an existing log if it has been used before) FMessageLogInitializationOptions LogInitOptions; @@ -34,11 +34,11 @@ TSharedRef FFlowMessageLogListing::RegisterLogListing(const return MessageLogModule.GetLogListing(LogName); } -TSharedRef FFlowMessageLogListing::GetLogListing(const UFlowAsset* InFlowAsset) +TSharedRef FFlowMessageLogListing::GetLogListing(const UFlowAsset* InFlowAsset, const EFlowLogType Type) { FMessageLogModule& MessageLogModule = FModuleManager::LoadModuleChecked("MessageLog"); - const FName LogName = GetListingName(InFlowAsset); + const FName LogName = GetListingName(InFlowAsset, Type); // Reuse any existing log, or create a new one (that is not held onto bey the message log system) if(MessageLogModule.IsRegisteredLogListing(LogName)) @@ -53,12 +53,13 @@ TSharedRef FFlowMessageLogListing::GetLogListing(const UFlow } } -FName FFlowMessageLogListing::GetListingName(const UFlowAsset* InFlowAsset) +FName FFlowMessageLogListing::GetListingName(const UFlowAsset* InFlowAsset, const EFlowLogType Type) { FName LogListingName; if (InFlowAsset) { - LogListingName = *FString::Printf(TEXT("%s_%s_FlowMessageLog"), *InFlowAsset->AssetGuid.ToString(), *InFlowAsset->GetName()); + const FString TypeAsString = StaticEnum()->GetNameStringByIndex(static_cast(Type)); + LogListingName = *FString::Printf(TEXT("FlowLog_%s_%s_%s_"), *TypeAsString, *InFlowAsset->AssetGuid.ToString(), *InFlowAsset->GetName()); } else { diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp index 04ed6420a..fdcebbf35 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp @@ -360,10 +360,10 @@ void SFlowGraphNode::UpdateErrorInfo() { if (const UFlowNode* FlowNode = FlowGraphNode->GetFlowNode()) { - if (FlowNode->Log.Messages.Num() > 0) + if (FlowNode->ValidationLog.Messages.Num() > 0) { EMessageSeverity::Type MaxSeverity = EMessageSeverity::Info; - for (const TSharedRef& Message : FlowNode->Log.Messages) + for (const TSharedRef& Message : FlowNode->ValidationLog.Messages) { if (Message->GetSeverity() < MaxSeverity) { diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index c08b218e1..58ea65282 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -36,21 +36,27 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit TSharedPtr GraphEditor; TSharedPtr DetailsView; TSharedPtr Palette; + #if ENABLE_SEARCH_IN_ASSET_EDITOR TSharedPtr SearchBrowser; #endif - /** Message log, with the log listing that it reflects */ - TSharedPtr MessageLog; - TSharedPtr MessageLogListing; + /** Runtime message log, with the log listing that it reflects */ + TSharedPtr RuntimeLog; + TSharedPtr RuntimeLogListing; + + /** Asset Validation message log, with the log listing that it reflects */ + TSharedPtr ValidationLog; + TSharedPtr ValidationLogListing; public: /** The tab ids for all the tabs used */ static const FName DetailsTab; static const FName GraphTab; - static const FName MessagesTab; static const FName PaletteTab; + static const FName RuntimeLogTab; static const FName SearchTab; + static const FName ValidationLogTab; private: /** The current UI selection state of this editor */ @@ -95,11 +101,12 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit private: TSharedRef SpawnTab_Details(const FSpawnTabArgs& Args) const; TSharedRef SpawnTab_Graph(const FSpawnTabArgs& Args) const; - TSharedRef SpawnTab_MessageLog(const FSpawnTabArgs& Args) const; TSharedRef SpawnTab_Palette(const FSpawnTabArgs& Args) const; + TSharedRef SpawnTab_RuntimeLog(const FSpawnTabArgs& Args) const; #if ENABLE_SEARCH_IN_ASSET_EDITOR TSharedRef SpawnTab_Search(const FSpawnTabArgs& Args) const; #endif + TSharedRef SpawnTab_ValidationLog(const FSpawnTabArgs& Args) const; public: /** Edits the specified FlowAsset object */ @@ -107,9 +114,10 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit protected: virtual void CreateToolbar(); - virtual void BindToolbarCommands(); + virtual void RefreshAsset(); + #if ENABLE_SEARCH_IN_ASSET_EDITOR virtual void SearchInAsset(); #endif @@ -147,6 +155,8 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit static bool IsPIE(); static EVisibility GetDebuggerVisibility(); + void OnBeginPIE(const bool bInSimulateInEditor) const; + TSet GetSelectedFlowNodes() const; int32 GetNumberOfSelectedNodes() const; bool GetBoundsForSelectedNodes(class FSlateRect& Rect, float Padding) const; @@ -164,6 +174,7 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit #endif protected: + void OnRuntimeMessageAdded(const UFlowAsset* AssetInstance, const TSharedRef& Message) const; void OnLogTokenClicked(const TSharedRef& Token) const; virtual void SelectAllNodes() const; diff --git a/Source/FlowEditor/Public/Asset/FlowMessageLogListing.h b/Source/FlowEditor/Public/Asset/FlowMessageLogListing.h index b9935e8b1..5651f25c3 100644 --- a/Source/FlowEditor/Public/Asset/FlowMessageLogListing.h +++ b/Source/FlowEditor/Public/Asset/FlowMessageLogListing.h @@ -6,6 +6,13 @@ #include "FlowAsset.h" +UENUM() +enum class EFlowLogType : uint8 +{ + Runtime, + Validation +}; + /** * Scope wrapper for the message log. Ensures we don't leak logs that we dont need (i.e. those that have no messages) * Replicated after FScopedBlueprintMessageLog @@ -13,7 +20,7 @@ class FLOWEDITOR_API FFlowMessageLogListing { public: - FFlowMessageLogListing(const UFlowAsset* InFlowAsset); + FFlowMessageLogListing(const UFlowAsset* InFlowAsset, const EFlowLogType Type); ~FFlowMessageLogListing(); public: @@ -21,9 +28,9 @@ class FLOWEDITOR_API FFlowMessageLogListing FName LogName; private: - static TSharedRef RegisterLogListing(const UFlowAsset* InFlowAsset); - static FName GetListingName(const UFlowAsset* InFlowAsset); + static TSharedRef RegisterLogListing(const UFlowAsset* InFlowAsset, const EFlowLogType Type); + static FName GetListingName(const UFlowAsset* InFlowAsset, const EFlowLogType Type); public: - static TSharedRef GetLogListing(const UFlowAsset* InFlowAsset); + static TSharedRef GetLogListing(const UFlowAsset* InFlowAsset, const EFlowLogType Type); }; From 0c4c63eaae1fadbb3885a0d1dd327fbf6ab98c3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Wed, 18 Jan 2023 00:37:05 +0100 Subject: [PATCH 112/485] non-editor build fix --- Source/Flow/Private/FlowSubsystem.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index a40defd61..4913040c8 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -223,13 +223,19 @@ void UFlowSubsystem::AddInstancedTemplate(UFlowAsset* Template) if (!InstancedTemplates.Contains(Template)) { InstancedTemplates.Add(Template); + +#if WITH_EDITOR Template->RuntimeLog = MakeShareable(new FFlowMessageLog()); +#endif } } void UFlowSubsystem::RemoveInstancedTemplate(UFlowAsset* Template) { +#if WITH_EDITOR Template->RuntimeLog.Reset(); +#endif + InstancedTemplates.Remove(Template); } From 496754d160858df0ac1c9bb0f3c714f98e48cf5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 5 Feb 2023 17:41:47 +0100 Subject: [PATCH 113/485] fixed debugging toolbar not working in per-asset context --- .../Private/Asset/FlowAssetEditor.cpp | 10 ++++ .../Private/Asset/FlowAssetEditorContext.cpp | 9 +++ .../Private/Asset/FlowAssetToolbar.cpp | 59 ++++++++++--------- .../FlowEditor/Public/Asset/FlowAssetEditor.h | 6 +- .../Public/Asset/FlowAssetEditorContext.h | 21 +++++++ .../Public/Asset/FlowAssetToolbar.h | 14 ++--- 6 files changed, 79 insertions(+), 40 deletions(-) create mode 100644 Source/FlowEditor/Private/Asset/FlowAssetEditorContext.cpp create mode 100644 Source/FlowEditor/Public/Asset/FlowAssetEditorContext.h diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index 5b27b0838..a5946548e 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -6,6 +6,7 @@ #include "FlowEditorModule.h" #include "FlowMessageLog.h" +#include "Asset/FlowAssetEditorContext.h" #include "Asset/FlowAssetToolbar.h" #include "Asset/FlowDebugger.h" #include "Asset/FlowMessageLogListing.h" @@ -165,6 +166,15 @@ void FFlowAssetEditor::UnregisterTabSpawners(const TSharedRef #endif } +void FFlowAssetEditor::InitToolMenuContext(FToolMenuContext& MenuContext) +{ + FAssetEditorToolkit::InitToolMenuContext(MenuContext); + + UFlowAssetEditorContext* Context = NewObject(); + Context->FlowAssetEditor = SharedThis(this); + MenuContext.AddObject(Context); +} + TSharedRef FFlowAssetEditor::SpawnTab_Details(const FSpawnTabArgs& Args) const { check(Args.GetTabId() == DetailsTab); diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditorContext.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditorContext.cpp new file mode 100644 index 000000000..0d5501ddf --- /dev/null +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditorContext.cpp @@ -0,0 +1,9 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Asset/FlowAssetEditorContext.h" +#include "Asset/FlowAssetEditor.h" + +UFlowAsset* UFlowAssetEditorContext::GetFlowAsset() const +{ + return FlowAssetEditor.IsValid() ? FlowAssetEditor.Pin()->GetFlowAsset() : nullptr; +} diff --git a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp index 4c7f26422..497c1bfcc 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp @@ -3,6 +3,7 @@ #include "Asset/FlowAssetToolbar.h" #include "Asset/FlowAssetEditor.h" +#include "Asset/FlowAssetEditorContext.h" #include "Asset/SAssetRevisionMenu.h" #include "FlowEditorCommands.h" @@ -45,10 +46,10 @@ void SFlowAssetInstanceList::Construct(const FArguments& InArgs, const TWeakObje .OnGenerateWidget(this, &SFlowAssetInstanceList::OnGenerateWidget) .OnSelectionChanged(this, &SFlowAssetInstanceList::OnSelectionChanged) .Visibility_Static(&FFlowAssetEditor::GetDebuggerVisibility) - [ - SNew(STextBlock) - .Text(this, &SFlowAssetInstanceList::GetSelectedInstanceName) - ]; + [ + SNew(STextBlock) + .Text(this, &SFlowAssetInstanceList::GetSelectedInstanceName) + ]; ChildSlot [ @@ -134,10 +135,10 @@ void SFlowAssetBreadcrumb::Construct(const FArguments& InArgs, const TWeakObject [ SNew(SVerticalBox) + SVerticalBox::Slot() - .HAlign(HAlign_Right) - .VAlign(VAlign_Center) - .AutoHeight() - .Padding(25.0f, 10.0f) + .HAlign(HAlign_Right) + .VAlign(VAlign_Center) + .AutoHeight() + .Padding(25.0f, 10.0f) [ BreadcrumbTrail.ToSharedRef() ] @@ -147,7 +148,7 @@ void SFlowAssetBreadcrumb::Construct(const FArguments& InArgs, const TWeakObject BreadcrumbTrail->ClearCrumbs(); if (UFlowAsset* InspectedInstance = TemplateAsset->GetInspectedInstance()) { - TArray InstancesFromRoot = {InspectedInstance}; + TArray> InstancesFromRoot = {InspectedInstance}; const UFlowAsset* CheckedInstance = InspectedInstance; while (UFlowAsset* ParentInstance = CheckedInstance->GetParentInstance()) @@ -156,23 +157,21 @@ void SFlowAssetBreadcrumb::Construct(const FArguments& InArgs, const TWeakObject CheckedInstance = ParentInstance; } - for (UFlowAsset* Instance : InstancesFromRoot) + for (TWeakObjectPtr Instance : InstancesFromRoot) { - TAttribute CrumbText = MakeAttributeLambda([Instance]() + if (Instance.IsValid()) { - return Instance ? FText::FromName(Instance->GetDisplayName()) : FText(); - }); - - BreadcrumbTrail->PushCrumb(CrumbText, FFlowBreadcrumb(Instance)); + const FFlowBreadcrumb NewBreadcrumb = FFlowBreadcrumb(Instance); + BreadcrumbTrail->PushCrumb(FText::FromName(NewBreadcrumb.InstanceName), FFlowBreadcrumb(Instance)); + } } } } void SFlowAssetBreadcrumb::OnCrumbClicked(const FFlowBreadcrumb& Item) const { - ensure(TemplateAsset->GetInspectedInstance()); - - if (Item.InstanceName != TemplateAsset->GetInspectedInstance()->GetDisplayName()) + const UFlowAsset* InspectedInstance = TemplateAsset->GetInspectedInstance(); + if (InspectedInstance == nullptr || Item.InstanceName != InspectedInstance->GetDisplayName()) { GEditor->GetEditorSubsystem()->OpenEditorForAsset(Item.AssetPathName); } @@ -197,7 +196,7 @@ void FFlowAssetToolbar::BuildAssetToolbar(UToolMenu* ToolbarMenu) const Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().RefreshAsset)); #if ENABLE_SEARCH_IN_ASSET_EDITOR Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().SearchInAsset)); -#endif +#endif // Visual Diff: menu to choose asset revision compared with the current one Section.AddDynamicEntry("SourceControlCommands", FNewToolMenuSectionDelegate::CreateLambda([this](FToolMenuSection& InSection) @@ -290,22 +289,24 @@ TSharedRef FFlowAssetToolbar::MakeDiffMenu() const return MenuBuilder.MakeWidget(); } -void FFlowAssetToolbar::BuildDebuggerToolbar(UToolMenu* ToolbarMenu) +void FFlowAssetToolbar::BuildDebuggerToolbar(UToolMenu* ToolbarMenu) const { FToolMenuSection& Section = ToolbarMenu->AddSection("Debugging"); Section.InsertPosition = FToolMenuInsert("Asset", EToolMenuInsertType::After); - FPlayWorldCommands::BuildToolbar(Section); - - TWeakObjectPtr TemplateAsset = FlowAssetEditor.Pin()->GetFlowAsset(); - - AssetInstanceList = SNew(SFlowAssetInstanceList, TemplateAsset); - Section.AddEntry(FToolMenuEntry::InitWidget("AssetInstances", AssetInstanceList.ToSharedRef(), FText(), true)); + Section.AddDynamicEntry("DebuggingCommands", FNewToolMenuSectionDelegate::CreateLambda([](FToolMenuSection& InSection) + { + const UFlowAssetEditorContext* Context = InSection.FindContext(); + if (Context && Context->GetFlowAsset()) + { + FPlayWorldCommands::BuildToolbar(InSection); - Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().GoToParentInstance)); + InSection.AddEntry(FToolMenuEntry::InitWidget("AssetInstances", SNew(SFlowAssetInstanceList, Context->GetFlowAsset()), FText(), true)); - Breadcrumb = SNew(SFlowAssetBreadcrumb, TemplateAsset); - Section.AddEntry(FToolMenuEntry::InitWidget("AssetBreadcrumb", Breadcrumb.ToSharedRef(), FText(), true)); + InSection.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().GoToParentInstance)); + InSection.AddEntry(FToolMenuEntry::InitWidget("AssetBreadcrumb", SNew(SFlowAssetBreadcrumb, Context->GetFlowAsset()), FText(), true)); + } + })); } #undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index 58ea65282..04d3b3cdf 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -66,7 +66,7 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit FFlowAssetEditor(); virtual ~FFlowAssetEditor() override; - UFlowAsset* GetFlowAsset() const { return FlowAsset; }; + UFlowAsset* GetFlowAsset() const { return FlowAsset; } // FGCObject virtual void AddReferencedObjects(FReferenceCollector& Collector) override; @@ -98,6 +98,10 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit virtual void UnregisterTabSpawners(const TSharedRef& TabManager) override; // -- + // FAssetEditorToolkit + virtual void InitToolMenuContext(FToolMenuContext& MenuContext) override; + // -- + private: TSharedRef SpawnTab_Details(const FSpawnTabArgs& Args) const; TSharedRef SpawnTab_Graph(const FSpawnTabArgs& Args) const; diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditorContext.h b/Source/FlowEditor/Public/Asset/FlowAssetEditorContext.h new file mode 100644 index 000000000..f26d03ca4 --- /dev/null +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditorContext.h @@ -0,0 +1,21 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "CoreMinimal.h" +#include "FlowAssetEditorContext.generated.h" + +class UFlowAsset; +class FFlowAssetEditor; + +UCLASS() +class FLOWEDITOR_API UFlowAssetEditorContext : public UObject +{ + GENERATED_BODY() + +public: + UFUNCTION(BlueprintCallable, Category="Tool Menus") + UFlowAsset* GetFlowAsset() const; + + TWeakPtr FlowAssetEditor; +}; diff --git a/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h b/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h index 0739cb875..32bf3ce6c 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h @@ -46,15 +46,15 @@ class FLOWEDITOR_API SFlowAssetInstanceList : public SCompoundWidget */ struct FLOWEDITOR_API FFlowBreadcrumb { - FString AssetPathName; - FName InstanceName; + const FString AssetPathName; + const FName InstanceName; FFlowBreadcrumb() : AssetPathName(FString()) , InstanceName(NAME_None) {} - FFlowBreadcrumb(const UFlowAsset* FlowAsset) + explicit FFlowBreadcrumb(const TWeakObjectPtr FlowAsset) : AssetPathName(FlowAsset->GetTemplateAsset()->GetPathName()) , InstanceName(FlowAsset->GetDisplayName()) {} @@ -87,14 +87,8 @@ class FLOWEDITOR_API FFlowAssetToolbar : public TSharedFromThis MakeDiffMenu() const; - void BuildDebuggerToolbar(UToolMenu* ToolbarMenu); - -public: - TSharedPtr GetAssetInstanceList() const { return AssetInstanceList; } + void BuildDebuggerToolbar(UToolMenu* ToolbarMenu) const; private: TWeakPtr FlowAssetEditor; - - TSharedPtr AssetInstanceList; - TSharedPtr Breadcrumb; }; From c9b623b28a8669d63b8db33c19cea88ba1ef4d9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 12 Feb 2023 15:29:29 +0100 Subject: [PATCH 114/485] fixed crash on exiting game, reported by community --- Source/Flow/Private/Nodes/FlowNode.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 86b50f59d..64b92810f 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -407,7 +407,7 @@ void UFlowNode::TriggerInput(const FName& PinName, const EFlowPinActivationType #endif // UE_BUILD_SHIPPING #if WITH_EDITOR - if (GetWorld()->WorldType == EWorldType::PIE && UFlowAsset::GetFlowGraphInterface().IsValid()) + if (GEditor && UFlowAsset::GetFlowGraphInterface().IsValid()) { UFlowAsset::GetFlowGraphInterface()->OnInputTriggered(GraphNode, InputPins.IndexOfByKey(PinName)); } @@ -466,7 +466,7 @@ void UFlowNode::TriggerOutput(const FName& PinName, const bool bFinish /*= false Records.Add(FPinRecord(FApp::GetCurrentTime(), ActivationType)); #if WITH_EDITOR - if (GetWorld()->WorldType == EWorldType::PIE && UFlowAsset::GetFlowGraphInterface().IsValid()) + if (GEditor && UFlowAsset::GetFlowGraphInterface().IsValid()) { UFlowAsset::GetFlowGraphInterface()->OnOutputTriggered(GraphNode, OutputPins.IndexOfByKey(PinName)); } From c06ce49916f82dee43406de988511e3f73d6b54f Mon Sep 17 00:00:00 2001 From: Bohdon Sayre Date: Sun, 12 Feb 2023 12:45:49 -0500 Subject: [PATCH 115/485] add bExactMatch option to Notify Actor flow node --- Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp | 3 ++- Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp b/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp index 7340e5896..dd3303c7b 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp @@ -8,6 +8,7 @@ UFlowNode_NotifyActor::UFlowNode_NotifyActor(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) + , bExactMatch(true) , NetMode(EFlowNetMode::Authority) { #if WITH_EDITOR @@ -19,7 +20,7 @@ void UFlowNode_NotifyActor::ExecuteInput(const FName& PinName) { if (const UFlowSubsystem* FlowSubsystem = GetWorld()->GetGameInstance()->GetSubsystem()) { - for (const TWeakObjectPtr& Component : FlowSubsystem->GetComponents(IdentityTags, EGameplayContainerMatchType::Any)) + for (const TWeakObjectPtr& Component : FlowSubsystem->GetComponents(IdentityTags, EGameplayContainerMatchType::Any, bExactMatch)) { Component->NotifyFromGraph(NotifyTags, NetMode); } diff --git a/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h b/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h index 74219765b..63f3e8c37 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h @@ -18,6 +18,14 @@ class FLOW_API UFlowNode_NotifyActor : public UFlowNode protected: UPROPERTY(EditAnywhere, Category = "Notify") FGameplayTagContainer IdentityTags; + + /** + * If true, identity tags must be an exact match. + * Be careful, setting this to false may be very expensive, as the + * search cost is proportional to the number of registered Gameplay Tags! + */ + UPROPERTY(EditAnywhere, Category = "Notify") + bool bExactMatch; UPROPERTY(EditAnywhere, Category = "Notify") FGameplayTagContainer NotifyTags; From 5a870af98334b4cb05a500c3bb0ca380b4e3fffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Mon, 13 Feb 2023 22:13:21 +0100 Subject: [PATCH 116/485] added separate Validate toolbar action calling it together with Refresh action was always dirtying asset, and we don't want that for a validation --- Source/Flow/Private/FlowAsset.cpp | 6 --- Source/Flow/Public/FlowAsset.h | 2 - .../Private/Asset/FlowAssetEditor.cpp | 16 ++++-- .../Private/Asset/FlowAssetToolbar.cpp | 53 +++++++++++-------- .../FlowEditor/Private/FlowEditorCommands.cpp | 6 ++- Source/FlowEditor/Private/FlowEditorStyle.cpp | 2 + Source/FlowEditor/Private/Graph/FlowGraph.cpp | 5 -- .../FlowEditor/Public/Asset/FlowAssetEditor.h | 1 + Source/FlowEditor/Public/FlowEditorCommands.h | 2 + Source/FlowEditor/Public/Graph/FlowGraph.h | 2 - 10 files changed, 53 insertions(+), 42 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 7f3be67f2..0eaaa3e58 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -66,12 +66,6 @@ void UFlowAsset::PostDuplicate(bool bDuplicateForPIE) EDataValidationResult UFlowAsset::ValidateAsset(FFlowMessageLog& MessageLog) { - // first attempt to refresh graph, fix common issues automatically - if (GetFlowGraphInterface().IsValid()) - { - GetFlowGraphInterface()->RefreshGraph(this); - } - // validate nodes for (const TPair& Node : Nodes) { diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 21e4008ab..ab8e78e0b 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -26,8 +26,6 @@ class FLOW_API IFlowGraphInterface IFlowGraphInterface() {} virtual ~IFlowGraphInterface() {} - virtual void RefreshGraph(UFlowAsset* FlowAsset) {} - virtual void OnInputTriggered(UEdGraphNode* GraphNode, const int32 Index) const {} virtual void OnOutputTriggered(UEdGraphNode* GraphNode, const int32 Index) const {} }; diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index a5946548e..cd992dc36 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -10,6 +10,7 @@ #include "Asset/FlowAssetToolbar.h" #include "Asset/FlowDebugger.h" #include "Asset/FlowMessageLogListing.h" +#include "Graph/FlowGraph.h" #include "Graph/FlowGraphEditorSettings.h" #include "Graph/FlowGraphSchema.h" #include "Graph/FlowGraphSchema_Actions.h" @@ -136,7 +137,7 @@ void FFlowAssetEditor::RegisterTabSpawners(const TSharedRef& .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.Tabs.Palette")); InTabManager->RegisterTabSpawner(RuntimeLogTab, FOnSpawnTab::CreateSP(this, &FFlowAssetEditor::SpawnTab_RuntimeLog)) - .SetDisplayName(LOCTEXT("RuntimeLog", "RuntimeLog")) + .SetDisplayName(LOCTEXT("RuntimeLog", "Runtime Log")) .SetGroup(WorkspaceMenuCategoryRef) .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.Tabs.CompilerResults")); @@ -150,7 +151,7 @@ void FFlowAssetEditor::RegisterTabSpawners(const TSharedRef& InTabManager->RegisterTabSpawner(ValidationLogTab, FOnSpawnTab::CreateSP(this, &FFlowAssetEditor::SpawnTab_ValidationLog)) .SetDisplayName(LOCTEXT("ValidationLog", "Validation Log")) .SetGroup(WorkspaceMenuCategoryRef) - .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.Tabs.CompilerResults")); + .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Debug")); } void FFlowAssetEditor::UnregisterTabSpawners(const TSharedRef& InTabManager) @@ -355,6 +356,10 @@ void FFlowAssetEditor::BindToolbarCommands() FExecuteAction::CreateSP(this, &FFlowAssetEditor::RefreshAsset), FCanExecuteAction::CreateStatic(&FFlowAssetEditor::CanEdit)); + ToolkitCommands->MapAction(ToolbarCommands.ValidateAsset, + FExecuteAction::CreateSP(this, &FFlowAssetEditor::ValidateAsset), + FCanExecuteAction()); + #if ENABLE_SEARCH_IN_ASSET_EDITOR ToolkitCommands->MapAction(ToolbarCommands.SearchInAsset, FExecuteAction::CreateSP(this, &FFlowAssetEditor::SearchInAsset), @@ -374,7 +379,12 @@ void FFlowAssetEditor::BindToolbarCommands() void FFlowAssetEditor::RefreshAsset() { - // Refresh and validate asset, including graph + // attempt to refresh graph, fix common issues automatically + CastChecked(FlowAsset->GetGraph())->RefreshGraph(); +} + +void FFlowAssetEditor::ValidateAsset() +{ FFlowMessageLog LogResults; FlowAsset->ValidateAsset(LogResults); diff --git a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp index 497c1bfcc..89b42fe30 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp @@ -189,30 +189,39 @@ FFlowAssetToolbar::FFlowAssetToolbar(const TSharedPtr InAssetE void FFlowAssetToolbar::BuildAssetToolbar(UToolMenu* ToolbarMenu) const { - FToolMenuSection& Section = ToolbarMenu->AddSection("Editing"); - Section.InsertPosition = FToolMenuInsert("Asset", EToolMenuInsertType::After); + { + FToolMenuSection& Section = ToolbarMenu->AddSection("FlowAsset"); + Section.InsertPosition = FToolMenuInsert("Asset", EToolMenuInsertType::After); + + // add buttons + Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().RefreshAsset)); + Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().ValidateAsset)); + } + + { + FToolMenuSection& Section = ToolbarMenu->AddSection("View"); + Section.InsertPosition = FToolMenuInsert("FlowAsset", EToolMenuInsertType::After); - // add buttons - Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().RefreshAsset)); #if ENABLE_SEARCH_IN_ASSET_EDITOR - Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().SearchInAsset)); + Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().SearchInAsset)); #endif - // Visual Diff: menu to choose asset revision compared with the current one - Section.AddDynamicEntry("SourceControlCommands", FNewToolMenuSectionDelegate::CreateLambda([this](FToolMenuSection& InSection) - { - InSection.InsertPosition = FToolMenuInsert(); - FToolMenuEntry DiffEntry = FToolMenuEntry::InitComboButton( - "Diff", - FUIAction(), - FOnGetContent::CreateRaw(this, &FFlowAssetToolbar::MakeDiffMenu), - LOCTEXT("Diff", "Diff"), - LOCTEXT("FlowAssetEditorDiffToolTip", "Diff against previous revisions"), - FSlateIcon(FAppStyle::Get().GetStyleSetName(), "BlueprintDiff.ToolbarIcon") - ); - DiffEntry.StyleNameOverride = "CalloutToolbar"; - InSection.AddEntry(DiffEntry); - })); + // Visual Diff: menu to choose asset revision compared with the current one + Section.AddDynamicEntry("SourceControlCommands", FNewToolMenuSectionDelegate::CreateLambda([this](FToolMenuSection& InSection) + { + InSection.InsertPosition = FToolMenuInsert(); + FToolMenuEntry DiffEntry = FToolMenuEntry::InitComboButton( + "Diff", + FUIAction(), + FOnGetContent::CreateRaw(this, &FFlowAssetToolbar::MakeDiffMenu), + LOCTEXT("Diff", "Diff"), + LOCTEXT("FlowAssetEditorDiffToolTip", "Diff against previous revisions"), + FSlateIcon(FAppStyle::Get().GetStyleSetName(), "BlueprintDiff.ToolbarIcon") + ); + DiffEntry.StyleNameOverride = "CalloutToolbar"; + InSection.AddEntry(DiffEntry); + })); + } } /** Delegate called to diff a specific revision with the current */ @@ -291,8 +300,8 @@ TSharedRef FFlowAssetToolbar::MakeDiffMenu() const void FFlowAssetToolbar::BuildDebuggerToolbar(UToolMenu* ToolbarMenu) const { - FToolMenuSection& Section = ToolbarMenu->AddSection("Debugging"); - Section.InsertPosition = FToolMenuInsert("Asset", EToolMenuInsertType::After); + FToolMenuSection& Section = ToolbarMenu->AddSection("Debug"); + Section.InsertPosition = FToolMenuInsert("View", EToolMenuInsertType::After); Section.AddDynamicEntry("DebuggingCommands", FNewToolMenuSectionDelegate::CreateLambda([](FToolMenuSection& InSection) { diff --git a/Source/FlowEditor/Private/FlowEditorCommands.cpp b/Source/FlowEditor/Private/FlowEditorCommands.cpp index 530e40783..99779f654 100644 --- a/Source/FlowEditor/Private/FlowEditorCommands.cpp +++ b/Source/FlowEditor/Private/FlowEditorCommands.cpp @@ -18,7 +18,9 @@ FFlowToolbarCommands::FFlowToolbarCommands() void FFlowToolbarCommands::RegisterCommands() { UI_COMMAND(RefreshAsset, "Refresh", "Refresh asset and all nodes", EUserInterfaceActionType::Button, FInputChord()); - UI_COMMAND(SearchInAsset, "Search", "Search in the current Flow Graph", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control | EModifierKey::Shift, EKeys::F) ); + UI_COMMAND(ValidateAsset, "Validate", "Validate asset and all nodes", EUserInterfaceActionType::Button, FInputChord()); + + UI_COMMAND(SearchInAsset, "Search", "Search in the current Flow Graph", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control | EModifierKey::Shift, EKeys::F)); UI_COMMAND(GoToParentInstance, "Go To Parent", "Open editor for the Flow Asset that created this Flow instance", EUserInterfaceActionType::Button, FInputChord()); } @@ -69,7 +71,7 @@ void FFlowSpawnNodeCommands::RegisterCommands() FString ClassName; if (FParse::Value(*NodeSpawns[x], TEXT("Class="), ClassName)) { - UClass* FoundClass = FindFirstObject(*ClassName, EFindFirstObjectOptions::ExactClass, ELogVerbosity::Warning, TEXT("looking for SpawnNodes")); + UClass* FoundClass = FindFirstObject(*ClassName, EFindFirstObjectOptions::ExactClass, ELogVerbosity::Warning, TEXT("looking for SpawnNodes")); if (FoundClass && FoundClass->IsChildOf(UFlowNode::StaticClass())) { NodeClass = FoundClass; diff --git a/Source/FlowEditor/Private/FlowEditorStyle.cpp b/Source/FlowEditor/Private/FlowEditorStyle.cpp index 98bd0cd70..19d331783 100644 --- a/Source/FlowEditor/Private/FlowEditorStyle.cpp +++ b/Source/FlowEditor/Private/FlowEditorStyle.cpp @@ -32,6 +32,8 @@ void FFlowEditorStyle::Initialize() StyleSet->SetContentRoot(FPaths::EngineContentDir() / TEXT("Editor/Slate/")); StyleSet->Set("FlowToolbar.RefreshAsset", new IMAGE_BRUSH_SVG( "Starship/Common/Apply", Icon20)); + StyleSet->Set("FlowToolbar.ValidateAsset", new IMAGE_BRUSH_SVG( "Starship/Common/Debug", Icon20)); + StyleSet->Set("FlowToolbar.SearchInAsset", new IMAGE_BRUSH_SVG( "Starship/Common/Search", Icon20)); StyleSet->Set("FlowToolbar.GoToParentInstance", new IMAGE_BRUSH("Icons/icon_DebugStepOut_40x", Icon40)); diff --git a/Source/FlowEditor/Private/Graph/FlowGraph.cpp b/Source/FlowEditor/Private/Graph/FlowGraph.cpp index 93626cedb..b83c18312 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraph.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraph.cpp @@ -9,11 +9,6 @@ #include "Kismet2/BlueprintEditorUtils.h" -void FFlowGraphInterface::RefreshGraph(UFlowAsset* FlowAsset) -{ - CastChecked(FlowAsset->GetGraph())->RefreshGraph(); -} - void FFlowGraphInterface::OnInputTriggered(UEdGraphNode* GraphNode, const int32 Index) const { CastChecked(GraphNode)->OnInputTriggered(Index); diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index 04d3b3cdf..6fd99566d 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -121,6 +121,7 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit virtual void BindToolbarCommands(); virtual void RefreshAsset(); + virtual void ValidateAsset(); #if ENABLE_SEARCH_IN_ASSET_EDITOR virtual void SearchInAsset(); diff --git a/Source/FlowEditor/Public/FlowEditorCommands.h b/Source/FlowEditor/Public/FlowEditorCommands.h index 3ceff0032..a1b12da0a 100644 --- a/Source/FlowEditor/Public/FlowEditorCommands.h +++ b/Source/FlowEditor/Public/FlowEditorCommands.h @@ -13,6 +13,8 @@ class FLOWEDITOR_API FFlowToolbarCommands : public TCommands RefreshAsset; + TSharedPtr ValidateAsset; + TSharedPtr SearchInAsset; TSharedPtr GoToParentInstance; diff --git a/Source/FlowEditor/Public/Graph/FlowGraph.h b/Source/FlowEditor/Public/Graph/FlowGraph.h index de76d7949..7c1803481 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraph.h +++ b/Source/FlowEditor/Public/Graph/FlowGraph.h @@ -12,8 +12,6 @@ class FLOWEDITOR_API FFlowGraphInterface : public IFlowGraphInterface public: virtual ~FFlowGraphInterface() override {} - virtual void RefreshGraph(UFlowAsset* FlowAsset) override; - virtual void OnInputTriggered(UEdGraphNode* GraphNode, const int32 Index) const override; virtual void OnOutputTriggered(UEdGraphNode* GraphNode, const int32 Index) const override; }; From 6806e1313b80519a7dd4a4f94a2d219accf2fda8 Mon Sep 17 00:00:00 2001 From: "Satheesh (ryanjon2040)" Date: Sat, 18 Feb 2023 15:48:46 +0530 Subject: [PATCH 117/485] Fix templated version of GetOwner (#139) --- Source/Flow/Public/FlowAsset.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index ab8e78e0b..0e319101d 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -242,7 +242,7 @@ class FLOW_API UFlowAsset : public UObject UObject* GetOwner() const { return Owner.Get(); } template - TWeakObjectPtr GetOwner() const + TWeakObjectPtr GetOwner() const { return Owner.IsValid() ? Cast(Owner) : nullptr; } From 1454bdcfe3ab17c69b95bbddeff573629eaa8eb1 Mon Sep 17 00:00:00 2001 From: "Satheesh (ryanjon2040)" Date: Sat, 18 Feb 2023 15:50:11 +0530 Subject: [PATCH 118/485] Add keywords for timer to make search easier (#137) --- Source/Flow/Public/Nodes/Route/FlowNode_Timer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_Timer.h b/Source/Flow/Public/Nodes/Route/FlowNode_Timer.h index 0d9154ad2..70424bd63 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_Timer.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_Timer.h @@ -9,7 +9,7 @@ /** * Triggers outputs after time elapsed */ -UCLASS(NotBlueprintable, meta = (DisplayName = "Timer")) +UCLASS(NotBlueprintable, meta = (DisplayName = "Timer", Keywords = "delay, wait, step, tick")) class FLOW_API UFlowNode_Timer : public UFlowNode { GENERATED_UCLASS_BODY() From 9b1c5428949257082bcd87b1db4801b31fa38428 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 19 Feb 2023 13:21:54 +0100 Subject: [PATCH 119/485] Removed "wait" from search keywords as this might be confusing in some projects --- Source/Flow/Public/Nodes/Route/FlowNode_Timer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_Timer.h b/Source/Flow/Public/Nodes/Route/FlowNode_Timer.h index 70424bd63..972147d5a 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_Timer.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_Timer.h @@ -9,7 +9,7 @@ /** * Triggers outputs after time elapsed */ -UCLASS(NotBlueprintable, meta = (DisplayName = "Timer", Keywords = "delay, wait, step, tick")) +UCLASS(NotBlueprintable, meta = (DisplayName = "Timer", Keywords = "delay, step, tick")) class FLOW_API UFlowNode_Timer : public UFlowNode { GENERATED_UCLASS_BODY() From c957aa94c24234fee0128318b872969c62e5c902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 19 Feb 2023 14:15:22 +0100 Subject: [PATCH 120/485] Simplified overriding "Validate Asset" directly in the editor code + finally applying auto-format --- .../Private/Asset/FlowAssetEditor.cpp | 327 +++++++++--------- .../FlowEditor/Public/Asset/FlowAssetEditor.h | 10 +- 2 files changed, 174 insertions(+), 163 deletions(-) diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index cd992dc36..4626ddea1 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -130,7 +130,7 @@ void FFlowAssetEditor::RegisterTabSpawners(const TSharedRef& .SetDisplayName(LOCTEXT("GraphTab", "Graph")) .SetGroup(WorkspaceMenuCategoryRef) .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "GraphEditor.EventGraph_16x")); - + InTabManager->RegisterTabSpawner(PaletteTab, FOnSpawnTab::CreateSP(this, &FFlowAssetEditor::SpawnTab_Palette)) .SetDisplayName(LOCTEXT("PaletteTab", "Palette")) .SetGroup(WorkspaceMenuCategoryRef) @@ -149,9 +149,9 @@ void FFlowAssetEditor::RegisterTabSpawners(const TSharedRef& #endif InTabManager->RegisterTabSpawner(ValidationLogTab, FOnSpawnTab::CreateSP(this, &FFlowAssetEditor::SpawnTab_ValidationLog)) - .SetDisplayName(LOCTEXT("ValidationLog", "Validation Log")) - .SetGroup(WorkspaceMenuCategoryRef) - .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Debug")); + .SetDisplayName(LOCTEXT("ValidationLog", "Validation Log")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Debug")); } void FFlowAssetEditor::UnregisterTabSpawners(const TSharedRef& InTabManager) @@ -164,7 +164,7 @@ void FFlowAssetEditor::UnregisterTabSpawners(const TSharedRef InTabManager->UnregisterTabSpawner(PaletteTab); #if ENABLE_SEARCH_IN_ASSET_EDITOR InTabManager->UnregisterTabSpawner(SearchTab); -#endif +#endif } void FFlowAssetEditor::InitToolMenuContext(FToolMenuContext& MenuContext) @@ -276,49 +276,49 @@ void FFlowAssetEditor::InitFlowAssetEditor(const EToolkitMode::Type Mode, const ->AddArea ( FTabManager::NewPrimaryArea()->SetOrientation(Orient_Horizontal) - ->Split - ( - FTabManager::NewStack() - ->SetSizeCoefficient(0.225f) - ->AddTab(DetailsTab, ETabState::OpenedTab) - ) - ->Split - ( - FTabManager::NewSplitter() - ->SetSizeCoefficient(0.65f) - ->SetOrientation(Orient_Vertical) - ->Split - ( - FTabManager::NewStack() - ->SetSizeCoefficient(0.8f) - ->SetHideTabWell(true) - ->AddTab(GraphTab, ETabState::OpenedTab) - ) - ->Split - ( - FTabManager::NewStack() - ->SetSizeCoefficient(0.15f) - ->AddTab(RuntimeLogTab, ETabState::ClosedTab) - ) - ->Split - ( - FTabManager::NewStack() - ->SetSizeCoefficient(0.15f) - ->AddTab(SearchTab, ETabState::ClosedTab) - ) - ->Split - ( - FTabManager::NewStack() - ->SetSizeCoefficient(0.15f) - ->AddTab(ValidationLogTab, ETabState::ClosedTab) - ) - ) - ->Split - ( - FTabManager::NewStack() - ->SetSizeCoefficient(0.125f) - ->AddTab(PaletteTab, ETabState::OpenedTab) - ) + ->Split + ( + FTabManager::NewStack() + ->SetSizeCoefficient(0.225f) + ->AddTab(DetailsTab, ETabState::OpenedTab) + ) + ->Split + ( + FTabManager::NewSplitter() + ->SetSizeCoefficient(0.65f) + ->SetOrientation(Orient_Vertical) + ->Split + ( + FTabManager::NewStack() + ->SetSizeCoefficient(0.8f) + ->SetHideTabWell(true) + ->AddTab(GraphTab, ETabState::OpenedTab) + ) + ->Split + ( + FTabManager::NewStack() + ->SetSizeCoefficient(0.15f) + ->AddTab(RuntimeLogTab, ETabState::ClosedTab) + ) + ->Split + ( + FTabManager::NewStack() + ->SetSizeCoefficient(0.15f) + ->AddTab(SearchTab, ETabState::ClosedTab) + ) + ->Split + ( + FTabManager::NewStack() + ->SetSizeCoefficient(0.15f) + ->AddTab(ValidationLogTab, ETabState::ClosedTab) + ) + ) + ->Split + ( + FTabManager::NewStack() + ->SetSizeCoefficient(0.125f) + ->AddTab(PaletteTab, ETabState::OpenedTab) + ) ); constexpr bool bCreateDefaultStandaloneMenu = true; @@ -353,28 +353,28 @@ void FFlowAssetEditor::BindToolbarCommands() // Editing ToolkitCommands->MapAction(ToolbarCommands.RefreshAsset, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::RefreshAsset), - FCanExecuteAction::CreateStatic(&FFlowAssetEditor::CanEdit)); + FExecuteAction::CreateSP(this, &FFlowAssetEditor::RefreshAsset), + FCanExecuteAction::CreateStatic(&FFlowAssetEditor::CanEdit)); ToolkitCommands->MapAction(ToolbarCommands.ValidateAsset, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::ValidateAsset), - FCanExecuteAction()); + FExecuteAction::CreateSP(this, &FFlowAssetEditor::ValidateAsset_Internal), + FCanExecuteAction()); #if ENABLE_SEARCH_IN_ASSET_EDITOR ToolkitCommands->MapAction(ToolbarCommands.SearchInAsset, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::SearchInAsset), - FCanExecuteAction()); -#endif + FExecuteAction::CreateSP(this, &FFlowAssetEditor::SearchInAsset), + FCanExecuteAction()); +#endif // Engine's Play commands ToolkitCommands->Append(FPlayWorldCommands::GlobalPlayWorldActions.ToSharedRef()); // Debugging ToolkitCommands->MapAction(ToolbarCommands.GoToParentInstance, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::GoToParentInstance), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanGoToParentInstance), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanGoToParentInstance)); + FExecuteAction::CreateSP(this, &FFlowAssetEditor::GoToParentInstance), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanGoToParentInstance), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanGoToParentInstance)); } void FFlowAssetEditor::RefreshAsset() @@ -383,10 +383,10 @@ void FFlowAssetEditor::RefreshAsset() CastChecked(FlowAsset->GetGraph())->RefreshGraph(); } -void FFlowAssetEditor::ValidateAsset() +void FFlowAssetEditor::ValidateAsset_Internal() { FFlowMessageLog LogResults; - FlowAsset->ValidateAsset(LogResults); + ValidateAsset(LogResults); // push messages to its window ValidationLogListing->ClearMessages(); @@ -398,6 +398,11 @@ void FFlowAssetEditor::ValidateAsset() ValidationLogListing->OnDataChanged().Broadcast(); } +void FFlowAssetEditor::ValidateAsset(FFlowMessageLog& MessageLog) +{ + FlowAsset->ValidateAsset(MessageLog); +} + #if ENABLE_SEARCH_IN_ASSET_EDITOR void FFlowAssetEditor::SearchInAsset() { @@ -496,178 +501,178 @@ void FFlowAssetEditor::BindGraphCommands() FGraphEditorCommands::Register(); FFlowGraphCommands::Register(); FFlowSpawnNodeCommands::Register(); - + const FGenericCommands& GenericCommands = FGenericCommands::Get(); const FGraphEditorCommandsImpl& GraphCommands = FGraphEditorCommands::Get(); const FFlowGraphCommands& FlowGraphCommands = FFlowGraphCommands::Get(); // Graph commands ToolkitCommands->MapAction(GraphCommands.CreateComment, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnCreateComment), - FCanExecuteAction::CreateStatic(&FFlowAssetEditor::CanEdit)); + FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnCreateComment), + FCanExecuteAction::CreateStatic(&FFlowAssetEditor::CanEdit)); ToolkitCommands->MapAction(GraphCommands.StraightenConnections, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnStraightenConnections)); + FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnStraightenConnections)); // Generic Node commands ToolkitCommands->MapAction(GenericCommands.Undo, - FExecuteAction::CreateStatic(&FFlowAssetEditor::UndoGraphAction), - FCanExecuteAction::CreateStatic(&FFlowAssetEditor::CanEdit)); + FExecuteAction::CreateStatic(&FFlowAssetEditor::UndoGraphAction), + FCanExecuteAction::CreateStatic(&FFlowAssetEditor::CanEdit)); ToolkitCommands->MapAction(GenericCommands.Redo, - FExecuteAction::CreateStatic(&FFlowAssetEditor::RedoGraphAction), - FCanExecuteAction::CreateStatic(&FFlowAssetEditor::CanEdit)); + FExecuteAction::CreateStatic(&FFlowAssetEditor::RedoGraphAction), + FCanExecuteAction::CreateStatic(&FFlowAssetEditor::CanEdit)); ToolkitCommands->MapAction(GenericCommands.SelectAll, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::SelectAllNodes), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanSelectAllNodes)); + FExecuteAction::CreateSP(this, &FFlowAssetEditor::SelectAllNodes), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanSelectAllNodes)); ToolkitCommands->MapAction(GenericCommands.Delete, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::DeleteSelectedNodes), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanDeleteNodes)); + FExecuteAction::CreateSP(this, &FFlowAssetEditor::DeleteSelectedNodes), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanDeleteNodes)); ToolkitCommands->MapAction(GenericCommands.Copy, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::CopySelectedNodes), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanCopyNodes)); + FExecuteAction::CreateSP(this, &FFlowAssetEditor::CopySelectedNodes), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanCopyNodes)); ToolkitCommands->MapAction(GenericCommands.Cut, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::CutSelectedNodes), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanCutNodes)); + FExecuteAction::CreateSP(this, &FFlowAssetEditor::CutSelectedNodes), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanCutNodes)); ToolkitCommands->MapAction(GenericCommands.Paste, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::PasteNodes), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanPasteNodes)); + FExecuteAction::CreateSP(this, &FFlowAssetEditor::PasteNodes), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanPasteNodes)); ToolkitCommands->MapAction(GenericCommands.Duplicate, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::DuplicateNodes), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanDuplicateNodes)); + FExecuteAction::CreateSP(this, &FFlowAssetEditor::DuplicateNodes), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanDuplicateNodes)); // Pin commands ToolkitCommands->MapAction(FlowGraphCommands.RefreshContextPins, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::RefreshContextPins), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanRefreshContextPins)); + FExecuteAction::CreateSP(this, &FFlowAssetEditor::RefreshContextPins), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanRefreshContextPins)); ToolkitCommands->MapAction(FlowGraphCommands.AddInput, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::AddInput), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanAddInput)); + FExecuteAction::CreateSP(this, &FFlowAssetEditor::AddInput), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanAddInput)); ToolkitCommands->MapAction(FlowGraphCommands.AddOutput, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::AddOutput), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanAddOutput)); + FExecuteAction::CreateSP(this, &FFlowAssetEditor::AddOutput), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanAddOutput)); ToolkitCommands->MapAction(FlowGraphCommands.RemovePin, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::RemovePin), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanRemovePin)); + FExecuteAction::CreateSP(this, &FFlowAssetEditor::RemovePin), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanRemovePin)); // Breakpoint commands ToolkitCommands->MapAction(GraphCommands.AddBreakpoint, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnAddBreakpoint), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanAddBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanAddBreakpoint) + FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnAddBreakpoint), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanAddBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanAddBreakpoint) ); ToolkitCommands->MapAction(GraphCommands.RemoveBreakpoint, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnRemoveBreakpoint), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanRemoveBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanRemoveBreakpoint) + FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnRemoveBreakpoint), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanRemoveBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanRemoveBreakpoint) ); ToolkitCommands->MapAction(GraphCommands.EnableBreakpoint, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnEnableBreakpoint), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanEnableBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanEnableBreakpoint) + FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnEnableBreakpoint), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanEnableBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanEnableBreakpoint) ); ToolkitCommands->MapAction(GraphCommands.DisableBreakpoint, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnDisableBreakpoint), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanDisableBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanDisableBreakpoint) + FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnDisableBreakpoint), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanDisableBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanDisableBreakpoint) ); ToolkitCommands->MapAction(GraphCommands.ToggleBreakpoint, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnToggleBreakpoint), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanToggleBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanToggleBreakpoint) + FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnToggleBreakpoint), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanToggleBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanToggleBreakpoint) ); // Pin Breakpoint commands ToolkitCommands->MapAction(FlowGraphCommands.AddPinBreakpoint, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnAddPinBreakpoint), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanAddPinBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanAddPinBreakpoint) + FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnAddPinBreakpoint), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanAddPinBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanAddPinBreakpoint) ); ToolkitCommands->MapAction(FlowGraphCommands.RemovePinBreakpoint, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnRemovePinBreakpoint), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanRemovePinBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanRemovePinBreakpoint) + FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnRemovePinBreakpoint), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanRemovePinBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanRemovePinBreakpoint) ); ToolkitCommands->MapAction(FlowGraphCommands.EnablePinBreakpoint, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnEnablePinBreakpoint), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanEnablePinBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanEnablePinBreakpoint) + FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnEnablePinBreakpoint), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanEnablePinBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanEnablePinBreakpoint) ); ToolkitCommands->MapAction(FlowGraphCommands.DisablePinBreakpoint, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnDisablePinBreakpoint), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanDisablePinBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanDisablePinBreakpoint) + FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnDisablePinBreakpoint), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanDisablePinBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanDisablePinBreakpoint) ); ToolkitCommands->MapAction(FlowGraphCommands.TogglePinBreakpoint, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnTogglePinBreakpoint), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanTogglePinBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanTogglePinBreakpoint) + FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnTogglePinBreakpoint), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanTogglePinBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanTogglePinBreakpoint) ); // Execution Override commands ToolkitCommands->MapAction(FlowGraphCommands.EnableNode, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::SetSignalMode, EFlowSignalMode::Enabled), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanSetSignalMode, EFlowSignalMode::Enabled), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanSetSignalMode, EFlowSignalMode::Enabled) + FExecuteAction::CreateSP(this, &FFlowAssetEditor::SetSignalMode, EFlowSignalMode::Enabled), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanSetSignalMode, EFlowSignalMode::Enabled), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanSetSignalMode, EFlowSignalMode::Enabled) ); ToolkitCommands->MapAction(FlowGraphCommands.DisableNode, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::SetSignalMode, EFlowSignalMode::Disabled), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanSetSignalMode, EFlowSignalMode::Disabled), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanSetSignalMode, EFlowSignalMode::Disabled) + FExecuteAction::CreateSP(this, &FFlowAssetEditor::SetSignalMode, EFlowSignalMode::Disabled), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanSetSignalMode, EFlowSignalMode::Disabled), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanSetSignalMode, EFlowSignalMode::Disabled) ); - + ToolkitCommands->MapAction(FlowGraphCommands.SetPassThrough, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::SetSignalMode, EFlowSignalMode::PassThrough), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanSetSignalMode, EFlowSignalMode::PassThrough), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanSetSignalMode, EFlowSignalMode::PassThrough) + FExecuteAction::CreateSP(this, &FFlowAssetEditor::SetSignalMode, EFlowSignalMode::PassThrough), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanSetSignalMode, EFlowSignalMode::PassThrough), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanSetSignalMode, EFlowSignalMode::PassThrough) ); ToolkitCommands->MapAction(FlowGraphCommands.ForcePinActivation, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnForcePinActivation), - FCanExecuteAction::CreateStatic(&FFlowAssetEditor::IsPIE), - FIsActionChecked(), - FIsActionButtonVisible::CreateStatic(&FFlowAssetEditor::IsPIE) + FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnForcePinActivation), + FCanExecuteAction::CreateStatic(&FFlowAssetEditor::IsPIE), + FIsActionChecked(), + FIsActionButtonVisible::CreateStatic(&FFlowAssetEditor::IsPIE) ); - + // Jump commands ToolkitCommands->MapAction(FlowGraphCommands.FocusViewport, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::FocusViewport), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanFocusViewport)); + FExecuteAction::CreateSP(this, &FFlowAssetEditor::FocusViewport), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanFocusViewport)); ToolkitCommands->MapAction(FlowGraphCommands.JumpToNodeDefinition, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::JumpToNodeDefinition), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanJumpToNodeDefinition)); + FExecuteAction::CreateSP(this, &FFlowAssetEditor::JumpToNodeDefinition), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanJumpToNodeDefinition)); } void FFlowAssetEditor::UndoGraphAction() @@ -687,12 +692,12 @@ FReply FFlowAssetEditor::OnSpawnGraphNodeByShortcut(FInputChord InChord, const F if (FFlowSpawnNodeCommands::IsRegistered()) { const TSharedPtr Action = FFlowSpawnNodeCommands::Get().GetActionByChord(InChord); - if (Action.IsValid()) - { - TArray DummyPins; - Action->PerformAction(Graph, DummyPins, InPosition); - return FReply::Handled(); - } + if (Action.IsValid()) + { + TArray DummyPins; + Action->PerformAction(Graph, DummyPins, InPosition); + return FReply::Handled(); + } } return FReply::Unhandled(); @@ -756,7 +761,7 @@ void FFlowAssetEditor::OnBeginPIE(const bool bInSimulateInEditor) const TSet FFlowAssetEditor::GetSelectedFlowNodes() const { TSet Result; - + const FGraphPanelSelectionSet SelectedNodes = GraphEditor->GetSelectedNodes(); for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt) { @@ -765,7 +770,7 @@ TSet FFlowAssetEditor::GetSelectedFlowNodes() const Result.Emplace(SelectedNode); } } - + return Result; } @@ -1145,7 +1150,7 @@ void FFlowAssetEditor::OnNodeDoubleClicked(class UEdGraphNode* Node) const else if (UObject* AssetToEdit = FlowNode->GetAssetToEdit()) { GEditor->GetEditorSubsystem()->OpenEditorForAsset(AssetToEdit); - + if (IsPIE()) { if (UFlowNode_SubGraph* SubGraphNode = Cast(FlowNode)) @@ -1492,7 +1497,7 @@ bool FFlowAssetEditor::CanSetSignalMode(const EFlowSignalMode Mode) const { return false; } - + for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { return SelectedNode->CanSetSignalMode(Mode); diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index 6fd99566d..b1d053829 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -12,6 +12,7 @@ #include "FlowEditorDefines.h" #include "FlowTypes.h" +class FFlowMessageLog; class SFlowPalette; class UFlowAsset; class UFlowGraphNode; @@ -27,7 +28,7 @@ struct Rect; class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEditorUndoClient, public FGCObject, public FNotifyHook { protected: - /** The FlowAsset asset being inspected */ + /** The Flow Asset being edited */ UFlowAsset* FlowAsset; TSharedPtr AssetToolbar; @@ -121,7 +122,12 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit virtual void BindToolbarCommands(); virtual void RefreshAsset(); - virtual void ValidateAsset(); + +private: + void ValidateAsset_Internal(); + +protected: + virtual void ValidateAsset(FFlowMessageLog& MessageLog); #if ENABLE_SEARCH_IN_ASSET_EDITOR virtual void SearchInAsset(); From 240be089b561b7e10590c55a82d136570124ab1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 19 Feb 2023 14:28:41 +0100 Subject: [PATCH 121/485] auto-format applied --- Source/Flow/Public/FlowAsset.h | 42 ++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 0e319101d..e6aa39e1d 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -40,7 +40,6 @@ UCLASS(BlueprintType, hideCategories = Object) class FLOW_API UFlowAsset : public UObject { GENERATED_UCLASS_BODY() - friend class UFlowNode; friend class UFlowNode_CustomOutput; friend class UFlowNode_SubGraph; @@ -56,7 +55,7 @@ class FLOW_API UFlowAsset : public UObject // This allow to SaveGame support works properly, if owner of Root Flow would be Game Instance or its subsystem UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Flow Asset") bool bWorldBound; - + ////////////////////////////////////////////////////////////////////////// // Graph @@ -74,6 +73,7 @@ class FLOW_API UFlowAsset : public UObject // IFlowGraphInterface #if WITH_EDITORONLY_DATA + private: UPROPERTY() UEdGraph* FlowGraph; @@ -94,7 +94,7 @@ class FLOW_API UFlowAsset : public UObject // Nodes protected: - TArray> AllowedNodeClasses; + TArray> AllowedNodeClasses; TArray> DeniedNodeClasses; bool bStartNodePlacedAsGhostNode; @@ -131,20 +131,20 @@ class FLOW_API UFlowAsset : public UObject #endif TMap GetNodes() const { return Nodes; } - UFlowNode* GetNode(const FGuid& Guid) const { return Nodes.FindRef(Guid); } + UFlowNode* GetNode(const FGuid& Guid) const { return Nodes.FindRef(Guid); } - template - T* GetNode(const FGuid& Guid) const - { - static_assert(TPointerIsConvertibleFromTo::Value, "'T' template parameter to GetNode must be derived from UFlowNode"); - - if (UFlowNode* Node = Nodes.FindRef(Guid)) - { - return Cast(Node); - } + template + T* GetNode(const FGuid& Guid) const + { + static_assert(TPointerIsConvertibleFromTo::Value, "'T' template parameter to GetNode must be derived from UFlowNode"); + + if (UFlowNode* Node = Nodes.FindRef(Guid)) + { + return Cast(Node); + } - return nullptr; - } + return nullptr; + } TArray GetCustomInputs() const { return CustomInputs; } TArray GetCustomOutputs() const { return CustomOutputs; } @@ -180,10 +180,12 @@ class FLOW_API UFlowAsset : public UObject UFlowAsset* GetInspectedInstance() const { return InspectedInstance.IsValid() ? InspectedInstance.Get() : nullptr; } DECLARE_EVENT(UFlowAsset, FRefreshDebuggerEvent); + FRefreshDebuggerEvent& OnDebuggerRefresh() { return RefreshDebuggerEvent; } FRefreshDebuggerEvent RefreshDebuggerEvent; DECLARE_EVENT_TwoParams(UFlowAsset, FRuntimeMessageEvent, const UFlowAsset*, const TSharedRef&); + FRuntimeMessageEvent& OnRuntimeMessageAdded() { return RuntimeMessageEvent; } FRuntimeMessageEvent RuntimeMessageEvent; @@ -235,7 +237,7 @@ class FLOW_API UFlowAsset : public UObject virtual void DeinitializeInstance(); UFlowAsset* GetTemplateAsset() const { return TemplateAsset; } - + // Object that spawned Root Flow instance, i.e. World Settings or Player Controller // This pointer is passed to child instances: Flow Asset instances created by the SubGraph nodes UFUNCTION(BlueprintPure, Category = "Flow") @@ -251,7 +253,7 @@ class FLOW_API UFlowAsset : public UObject virtual void PreStartFlow(); virtual void StartFlow(); - + virtual void FinishFlow(const EFlowFinishPolicy InFinishPolicy, const bool bRemoveInstance = true); // Get Flow Asset instance created by the given SubGraph node @@ -293,7 +295,7 @@ class FLOW_API UFlowAsset : public UObject ////////////////////////////////////////////////////////////////////////// // SaveGame - + UFUNCTION(BlueprintCallable, Category = "SaveGame") FFlowAssetSaveData SaveInstance(TArray& SavedFlowInstances); @@ -306,11 +308,11 @@ class FLOW_API UFlowAsset : public UObject protected: UFUNCTION(BlueprintNativeEvent, Category = "SaveGame") void OnSave(); - + UFUNCTION(BlueprintNativeEvent, Category = "SaveGame") void OnLoad(); -public: +public: UFUNCTION(BlueprintNativeEvent, Category = "SaveGame") bool IsBoundToWorld(); }; From d5994305cfb79d48f9493f397008e53319c22bdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 19 Feb 2023 15:03:57 +0100 Subject: [PATCH 122/485] added "Runtime" word to runtime log methods added nullcheck on GetTemplateAsset() if someone would runtime logging on template instance --- Source/Flow/Private/Nodes/FlowNode.cpp | 33 +++++++++++++++++--------- Source/Flow/Public/Nodes/FlowNode.h | 9 ++++--- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 64b92810f..22949957b 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -416,7 +416,7 @@ void UFlowNode::TriggerInput(const FName& PinName, const EFlowPinActivationType else { #if !UE_BUILD_SHIPPING - LogError(FString::Printf(TEXT("Input Pin name %s invalid"), *PinName.ToString())); + LogRuntimeError(FString::Printf(TEXT("Input Pin name %s invalid"), *PinName.ToString())); #endif // UE_BUILD_SHIPPING return; } @@ -427,10 +427,10 @@ void UFlowNode::TriggerInput(const FName& PinName, const EFlowPinActivationType ExecuteInput(PinName); break; case EFlowSignalMode::Disabled: - LogNote(FString::Printf(TEXT("Node disabled while triggering input %s"), *PinName.ToString())); + LogRuntimeNote(FString::Printf(TEXT("Node disabled while triggering input %s"), *PinName.ToString())); break; case EFlowSignalMode::PassThrough: - LogNote(FString::Printf(TEXT("Signal pass-through on triggering input %s"), *PinName.ToString())); + LogRuntimeNote(FString::Printf(TEXT("Signal pass-through on triggering input %s"), *PinName.ToString())); OnPassThrough(); break; default: ; @@ -474,7 +474,7 @@ void UFlowNode::TriggerOutput(const FName& PinName, const bool bFinish /*= false } else { - LogError(FString::Printf(TEXT("Output Pin name %s invalid"), *PinName.ToString())); + LogRuntimeError(FString::Printf(TEXT("Output Pin name %s invalid"), *PinName.ToString())); } #endif // UE_BUILD_SHIPPING @@ -662,7 +662,12 @@ FString UFlowNode::GetProgressAsString(float Value) return TempString; } -void UFlowNode::LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType) +void UFlowNode::LogError(const FString Message, const EFlowOnScreenMessageType OnScreenMessageType) +{ + LogRuntimeError(Message, OnScreenMessageType); +} + +void UFlowNode::LogRuntimeError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType) { #if !UE_BUILD_SHIPPING @@ -694,13 +699,16 @@ void UFlowNode::LogError(FString Message, const EFlowOnScreenMessageType OnScree // Message Log #if WITH_EDITOR - GetFlowAsset()->GetTemplateAsset()->LogError(Message, this); + if (GetFlowAsset()->GetTemplateAsset()) + { + GetFlowAsset()->GetTemplateAsset()->LogError(Message, this); + } #endif #endif } -void UFlowNode::LogWarning(FString Message) +void UFlowNode::LogRuntimeWarning(FString Message) { #if !UE_BUILD_SHIPPING @@ -711,13 +719,16 @@ void UFlowNode::LogWarning(FString Message) // Message Log #if WITH_EDITOR - GetFlowAsset()->GetTemplateAsset()->LogWarning(Message, this); + if (GetFlowAsset()->GetTemplateAsset()) + { + GetFlowAsset()->GetTemplateAsset()->LogWarning(Message, this); + } #endif #endif } -void UFlowNode::LogNote(FString Message) +void UFlowNode::LogRuntimeNote(FString Message) { #if !UE_BUILD_SHIPPING @@ -770,11 +781,11 @@ void UFlowNode::LoadInstance(const FFlowNodeSaveData& NodeRecord) break; case EFlowSignalMode::Disabled: // designer doesn't want to execute this node's logic at all, so we kill it - LogNote(TEXT("Signal disabled while loading Flow Node from SaveGame")); + LogRuntimeNote(TEXT("Signal disabled while loading Flow Node from SaveGame")); Finish(); break; case EFlowSignalMode::PassThrough: - LogNote(TEXT("Signal pass-through on loading Flow Node from SaveGame")); + LogRuntimeNote(TEXT("Signal pass-through on loading Flow Node from SaveGame")); OnPassThrough(); break; default: ; diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 5b5feb3fa..bd45e1dad 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -377,14 +377,17 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UFUNCTION(BlueprintPure, Category = "FlowNode") static FString GetProgressAsString(float Value); - UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) + UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly), meta=(DeprecatedFunction, DeprecationMessage="Use LogRuntimeError instead.")) void LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType = EFlowOnScreenMessageType::Permanent); UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) - void LogWarning(FString Message); + void LogRuntimeError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType = EFlowOnScreenMessageType::Permanent); + + UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) + void LogRuntimeWarning(FString Message); UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) - void LogNote(FString Message); + void LogRuntimeNote(FString Message); #if !UE_BUILD_SHIPPING private: From a997b0b66525552131238601431aeb84d65ffc36 Mon Sep 17 00:00:00 2001 From: "Satheesh (ryanjon2040)" Date: Mon, 20 Feb 2023 15:22:18 +0530 Subject: [PATCH 123/485] Properly deprecate LogError (#141) --- Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp | 4 ++-- Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp | 6 +++--- .../Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp | 2 +- Source/Flow/Public/Nodes/FlowNode.h | 1 + 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp index 04bbe1e68..9182f0140 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp @@ -49,11 +49,11 @@ void UFlowNode_SubGraph::ExecuteInput(const FName& PinName) { if (Asset.IsNull()) { - LogError(TEXT("Missing Flow Asset")); + LogRuntimeError(TEXT("Missing Flow Asset")); } else { - LogError(FString::Printf(TEXT("Asset %s cannot be instance, probably is the same as the asset owning this SubGraph node."), *Asset->GetPathName())); + LogRuntimeError(FString::Printf(TEXT("Asset %s cannot be instance, probably is the same as the asset owning this SubGraph node."), *Asset->GetPathName())); } Finish(); diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp index 3e14f88cb..663e47240 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp @@ -31,7 +31,7 @@ void UFlowNode_Timer::ExecuteInput(const FName& PinName) { if (CompletionTime == 0.0f) { - LogError(TEXT("Invalid Timer settings")); + LogRuntimeError(TEXT("Invalid Timer settings")); TriggerOutput(TEXT("Completed"), true); return; } @@ -40,7 +40,7 @@ void UFlowNode_Timer::ExecuteInput(const FName& PinName) { if (CompletionTimerHandle.IsValid() || StepTimerHandle.IsValid()) { - LogError(TEXT("Timer already active")); + LogRuntimeError(TEXT("Timer already active")); return; } @@ -69,7 +69,7 @@ void UFlowNode_Timer::SetTimer() } else { - LogError(TEXT("No valid world")); + LogRuntimeError(TEXT("No valid world")); TriggerOutput(TEXT("Completed"), true); } } diff --git a/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp b/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp index 73151f305..f0676f895 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp @@ -33,7 +33,7 @@ void UFlowNode_ComponentObserver::ExecuteInput(const FName& PinName) } else { - LogError(MissingIdentityTag); + LogRuntimeError(MissingIdentityTag); } } diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index bd45e1dad..9eb4ce264 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -377,6 +377,7 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UFUNCTION(BlueprintPure, Category = "FlowNode") static FString GetProgressAsString(float Value); + UE_DEPRECATED(5.0, "LogError has been deprecated. Use LogRuntimeError instead.") UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly), meta=(DeprecatedFunction, DeprecationMessage="Use LogRuntimeError instead.")) void LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType = EFlowOnScreenMessageType::Permanent); From 87b53991a922d6ead9c0fdfb58eb36630c3a2b40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Mon, 20 Feb 2023 18:44:51 +0100 Subject: [PATCH 124/485] reverted naming changing that caused collision engine's defines --- Source/Flow/Private/Nodes/FlowNode.cpp | 189 +++++++++--------- .../Private/Nodes/Route/FlowNode_SubGraph.cpp | 4 +- .../Private/Nodes/Route/FlowNode_Timer.cpp | 6 +- .../World/FlowNode_ComponentObserver.cpp | 2 +- Source/Flow/Public/Nodes/FlowNode.h | 33 ++- 5 files changed, 113 insertions(+), 121 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 22949957b..8b1b2aca6 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -416,7 +416,7 @@ void UFlowNode::TriggerInput(const FName& PinName, const EFlowPinActivationType else { #if !UE_BUILD_SHIPPING - LogRuntimeError(FString::Printf(TEXT("Input Pin name %s invalid"), *PinName.ToString())); + LogError(FString::Printf(TEXT("Input Pin name %s invalid"), *PinName.ToString())); #endif // UE_BUILD_SHIPPING return; } @@ -427,10 +427,10 @@ void UFlowNode::TriggerInput(const FName& PinName, const EFlowPinActivationType ExecuteInput(PinName); break; case EFlowSignalMode::Disabled: - LogRuntimeNote(FString::Printf(TEXT("Node disabled while triggering input %s"), *PinName.ToString())); + LogNote(FString::Printf(TEXT("Node disabled while triggering input %s"), *PinName.ToString())); break; case EFlowSignalMode::PassThrough: - LogRuntimeNote(FString::Printf(TEXT("Signal pass-through on triggering input %s"), *PinName.ToString())); + LogNote(FString::Printf(TEXT("Signal pass-through on triggering input %s"), *PinName.ToString())); OnPassThrough(); break; default: ; @@ -474,7 +474,7 @@ void UFlowNode::TriggerOutput(const FName& PinName, const bool bFinish /*= false } else { - LogRuntimeError(FString::Printf(TEXT("Output Pin name %s invalid"), *PinName.ToString())); + LogError(FString::Printf(TEXT("Output Pin name %s invalid"), *PinName.ToString())); } #endif // UE_BUILD_SHIPPING @@ -662,97 +662,6 @@ FString UFlowNode::GetProgressAsString(float Value) return TempString; } -void UFlowNode::LogError(const FString Message, const EFlowOnScreenMessageType OnScreenMessageType) -{ - LogRuntimeError(Message, OnScreenMessageType); -} - -void UFlowNode::LogRuntimeError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType) -{ -#if !UE_BUILD_SHIPPING - - BuildMessage(Message); - - // OnScreen Message - if (OnScreenMessageType == EFlowOnScreenMessageType::Permanent) - { - if (GetWorld()) - { - if (UViewportStatsSubsystem* StatsSubsystem = GetWorld()->GetSubsystem()) - { - StatsSubsystem->AddDisplayDelegate([this, Message](FText& OutText, FLinearColor& OutColor) - { - OutText = FText::FromString(Message); - OutColor = FLinearColor::Red; - return IsValid(this) && ActivationState != EFlowNodeState::NeverActivated; - }); - } - } - } - else - { - GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, Message); - } - - // Output Log - UE_LOG(LogFlow, Error, TEXT("%s"), *Message); - - // Message Log -#if WITH_EDITOR - if (GetFlowAsset()->GetTemplateAsset()) - { - GetFlowAsset()->GetTemplateAsset()->LogError(Message, this); - } -#endif - -#endif -} - -void UFlowNode::LogRuntimeWarning(FString Message) -{ -#if !UE_BUILD_SHIPPING - - BuildMessage(Message); - - // Output Log - UE_LOG(LogFlow, Warning, TEXT("%s"), *Message); - - // Message Log -#if WITH_EDITOR - if (GetFlowAsset()->GetTemplateAsset()) - { - GetFlowAsset()->GetTemplateAsset()->LogWarning(Message, this); - } -#endif - -#endif -} - -void UFlowNode::LogRuntimeNote(FString Message) -{ -#if !UE_BUILD_SHIPPING - - BuildMessage(Message); - - // Output Log - UE_LOG(LogFlow, Log, TEXT("%s"), *Message); - - // Message Log -#if WITH_EDITOR - GetFlowAsset()->GetTemplateAsset()->LogNote(Message, this); -#endif - -#endif -} - -#if !UE_BUILD_SHIPPING -void UFlowNode::BuildMessage(FString& Message) const -{ - const FString TemplatePath = GetFlowAsset()->TemplateAsset->GetPathName(); - Message.Append(TEXT(" --- node ")).Append(GetName()).Append(TEXT(", asset ")).Append(FPaths::GetPath(TemplatePath) / FPaths::GetBaseFilename(TemplatePath)); -} -#endif - void UFlowNode::SaveInstance(FFlowNodeSaveData& NodeRecord) { NodeRecord.NodeGuid = NodeGuid; @@ -781,11 +690,11 @@ void UFlowNode::LoadInstance(const FFlowNodeSaveData& NodeRecord) break; case EFlowSignalMode::Disabled: // designer doesn't want to execute this node's logic at all, so we kill it - LogRuntimeNote(TEXT("Signal disabled while loading Flow Node from SaveGame")); + LogNote(TEXT("Signal disabled while loading Flow Node from SaveGame")); Finish(); break; case EFlowSignalMode::PassThrough: - LogRuntimeNote(TEXT("Signal pass-through on loading Flow Node from SaveGame")); + LogNote(TEXT("Signal pass-through on loading Flow Node from SaveGame")); OnPassThrough(); break; default: ; @@ -815,3 +724,89 @@ void UFlowNode::OnPassThrough_Implementation() // deactivate node, so it doesn't get saved to a new SaveGame Finish(); } + +void UFlowNode::LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType) +{ +#if !UE_BUILD_SHIPPING + if (BuildMessage(Message)) + { + // OnScreen Message + if (OnScreenMessageType == EFlowOnScreenMessageType::Permanent) + { + if (GetWorld()) + { + if (UViewportStatsSubsystem* StatsSubsystem = GetWorld()->GetSubsystem()) + { + StatsSubsystem->AddDisplayDelegate([this, Message](FText& OutText, FLinearColor& OutColor) + { + OutText = FText::FromString(Message); + OutColor = FLinearColor::Red; + return IsValid(this) && ActivationState != EFlowNodeState::NeverActivated; + }); + } + } + } + else + { + GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, Message); + } + + // Output Log + UE_LOG(LogFlow, Error, TEXT("%s"), *Message); + + // Message Log +#if WITH_EDITOR + GetFlowAsset()->GetTemplateAsset()->LogError(Message, this); +#endif + } +#endif +} + +void UFlowNode::LogWarning(FString Message) +{ +#if !UE_BUILD_SHIPPING + if (BuildMessage(Message)) + { + // Output Log + UE_LOG(LogFlow, Warning, TEXT("%s"), *Message); + + // Message Log +#if WITH_EDITOR + if (GetFlowAsset()->GetTemplateAsset()) + { + GetFlowAsset()->GetTemplateAsset()->LogWarning(Message, this); + } +#endif + } +#endif +} + +void UFlowNode::LogNote(FString Message) +{ +#if !UE_BUILD_SHIPPING + if (BuildMessage(Message)) + { + // Output Log + UE_LOG(LogFlow, Log, TEXT("%s"), *Message); + + // Message Log +#if WITH_EDITOR + GetFlowAsset()->GetTemplateAsset()->LogNote(Message, this); +#endif + } +#endif +} + +#if !UE_BUILD_SHIPPING +bool UFlowNode::BuildMessage(FString& Message) const +{ + if (GetFlowAsset()->TemplateAsset) // this is runtime log which is should be only called on runtime instances of asset + { + const FString TemplatePath = GetFlowAsset()->TemplateAsset->GetPathName(); + Message.Append(TEXT(" --- node ")).Append(GetName()).Append(TEXT(", asset ")).Append(FPaths::GetPath(TemplatePath) / FPaths::GetBaseFilename(TemplatePath)); + return true; + } + + return false; +} +#endif diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp index 9182f0140..04bbe1e68 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp @@ -49,11 +49,11 @@ void UFlowNode_SubGraph::ExecuteInput(const FName& PinName) { if (Asset.IsNull()) { - LogRuntimeError(TEXT("Missing Flow Asset")); + LogError(TEXT("Missing Flow Asset")); } else { - LogRuntimeError(FString::Printf(TEXT("Asset %s cannot be instance, probably is the same as the asset owning this SubGraph node."), *Asset->GetPathName())); + LogError(FString::Printf(TEXT("Asset %s cannot be instance, probably is the same as the asset owning this SubGraph node."), *Asset->GetPathName())); } Finish(); diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp index 663e47240..3e14f88cb 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp @@ -31,7 +31,7 @@ void UFlowNode_Timer::ExecuteInput(const FName& PinName) { if (CompletionTime == 0.0f) { - LogRuntimeError(TEXT("Invalid Timer settings")); + LogError(TEXT("Invalid Timer settings")); TriggerOutput(TEXT("Completed"), true); return; } @@ -40,7 +40,7 @@ void UFlowNode_Timer::ExecuteInput(const FName& PinName) { if (CompletionTimerHandle.IsValid() || StepTimerHandle.IsValid()) { - LogRuntimeError(TEXT("Timer already active")); + LogError(TEXT("Timer already active")); return; } @@ -69,7 +69,7 @@ void UFlowNode_Timer::SetTimer() } else { - LogRuntimeError(TEXT("No valid world")); + LogError(TEXT("No valid world")); TriggerOutput(TEXT("Completed"), true); } } diff --git a/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp b/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp index f0676f895..73151f305 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp @@ -33,7 +33,7 @@ void UFlowNode_ComponentObserver::ExecuteInput(const FName& PinName) } else { - LogRuntimeError(MissingIdentityTag); + LogError(MissingIdentityTag); } } diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 9eb4ce264..adeb3b63e 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -377,24 +377,6 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UFUNCTION(BlueprintPure, Category = "FlowNode") static FString GetProgressAsString(float Value); - UE_DEPRECATED(5.0, "LogError has been deprecated. Use LogRuntimeError instead.") - UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly), meta=(DeprecatedFunction, DeprecationMessage="Use LogRuntimeError instead.")) - void LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType = EFlowOnScreenMessageType::Permanent); - - UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) - void LogRuntimeError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType = EFlowOnScreenMessageType::Permanent); - - UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) - void LogRuntimeWarning(FString Message); - - UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) - void LogRuntimeNote(FString Message); - -#if !UE_BUILD_SHIPPING -private: - void BuildMessage(FString& Message) const; -#endif - public: UFUNCTION(BlueprintCallable, Category = "FlowNode") void SaveInstance(FFlowNodeSaveData& NodeRecord); @@ -411,4 +393,19 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UFUNCTION(BlueprintNativeEvent, Category = "FlowNode") void OnPassThrough(); + +public: + UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) + void LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType = EFlowOnScreenMessageType::Permanent); + + UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) + void LogWarning(FString Message); + + UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) + void LogNote(FString Message); + +#if !UE_BUILD_SHIPPING +private: + bool BuildMessage(FString& Message) const; +#endif }; From f18d390b53067a6dc89431419796954f67673213 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Mon, 20 Feb 2023 19:08:53 +0100 Subject: [PATCH 125/485] optimized building debug-only status strings --- Source/Flow/Private/Nodes/FlowNode.cpp | 35 ++----------------- .../Private/Nodes/Route/FlowNode_Timer.cpp | 8 ++--- .../World/FlowNode_PlayLevelSequence.cpp | 2 +- Source/Flow/Public/Nodes/FlowNode.h | 30 ++++++++-------- 4 files changed, 23 insertions(+), 52 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 8b1b2aca6..17325db06 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -626,40 +626,9 @@ FString UFlowNode::GetClassDescription(const TSubclassOf Class) return Class ? Class->GetName() : MissingClass; } -FString UFlowNode::GetProgressAsString(float Value) +FString UFlowNode::GetProgressAsString(const float Value) { - // Avoids negative zero - if (Value == 0) - { - Value = 0; - } - - // First create the string - FString TempString = FString::Printf(TEXT("%f"), Value); - if (!TempString.IsNumeric()) - { - // String did not format as a valid decimal number so avoid messing with it - return TempString; - } - - // Get position of the first digit after decimal separator - int32 TrimIndex = INDEX_NONE; - for (int32 CharIndex = 0; CharIndex < TempString.Len(); CharIndex++) - { - const TCHAR Char = TempString[CharIndex]; - if (Char == TEXT('.')) - { - TrimIndex = CharIndex + 2; - break; - } - if (TrimIndex == INDEX_NONE && Char != TEXT('0')) - { - TrimIndex = CharIndex + 1; - } - } - - TempString.RemoveAt(TrimIndex, TempString.Len() - TrimIndex, /*bAllowShrinking*/false); - return TempString; + return FString::Printf(TEXT("%.*f"), 2, Value); } void UFlowNode::SaveInstance(FFlowNodeSaveData& NodeRecord) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp index 3e14f88cb..bd32bf146 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp @@ -159,10 +159,10 @@ FString UFlowNode_Timer::GetNodeDescription() const { if (StepTime > 0.0f) { - return FString::SanitizeFloat(CompletionTime, 2).Append(TEXT(", step by ")).Append(FString::SanitizeFloat(StepTime, 2)); + return FString::Printf(TEXT("%.*f, step by %.*f"), 2, CompletionTime, 2, StepTime); } - return FString::SanitizeFloat(CompletionTime, 2); + return FString::Printf(TEXT("%.*f"), 2, CompletionTime); } return TEXT("Invalid settings"); @@ -172,12 +172,12 @@ FString UFlowNode_Timer::GetStatusString() const { if (StepTime > 0.0f) { - return TEXT("Progress: ") + GetProgressAsString(SumOfSteps); + return FString::Printf(TEXT("Progress: %.*f"), 2, SumOfSteps); } if (CompletionTimerHandle.IsValid() && GetWorld()) { - return TEXT("Progress: ") + GetProgressAsString(GetWorld()->GetTimerManager().GetTimerElapsed(CompletionTimerHandle)); + return FString::Printf(TEXT("Progress: %.*f"), 2, GetWorld()->GetTimerManager().GetTimerElapsed(CompletionTimerHandle)); } return FString(); diff --git a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp index 8a8b28740..00c3cce6b 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp @@ -302,7 +302,7 @@ FString UFlowNode_PlayLevelSequence::GetPlaybackProgress() const { if (SequencePlayer && SequencePlayer->IsPlaying()) { - return GetProgressAsString(SequencePlayer->GetCurrentTime().AsSeconds() - StartTime).Append(TEXT(" / ")).Append(GetProgressAsString(SequencePlayer->GetDuration().AsSeconds())); + return FString::Printf(TEXT("%.*f / %.*f"), 2, SequencePlayer->GetCurrentTime().AsSeconds() - StartTime, 2, SequencePlayer->GetDuration().AsSeconds()); } return FString(); diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index adeb3b63e..f4e83d7e2 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -27,7 +27,6 @@ UCLASS(Abstract, Blueprintable, HideCategories = Object) class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInterface { GENERATED_UCLASS_BODY() - friend class SFlowGraphNode; friend class UFlowAsset; friend class UFlowGraphNode; @@ -43,10 +42,11 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UEdGraphNode* GraphNode; #if WITH_EDITORONLY_DATA + protected: TArray> AllowedAssetClasses; TArray> DeniedAssetClasses; - + UPROPERTY() FString Category; @@ -58,7 +58,7 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UPROPERTY(EditDefaultsOnly, Category = "FlowNode") bool bNodeDeprecated; - + // If this node is deprecated, it might be replaced by another node UPROPERTY(EditDefaultsOnly, Category = "FlowNode") TSubclassOf ReplacedBy; @@ -91,7 +91,7 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte virtual FString GetNodeCategory() const; virtual FText GetNodeTitle() const; virtual FText GetNodeToolTip() const; - + // This method allows to have different for every node instance, i.e. Red if node represents enemy, Green if node represents a friend virtual bool GetDynamicTitleColor(FLinearColor& OutColor) const { return false; } @@ -123,7 +123,7 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte protected: UPROPERTY(EditDefaultsOnly, Category = "FlowNode") TArray AllowedSignalModes; - + // If enabled, signal will pass through node without calling ExecuteInput() // Designed to handle patching UPROPERTY() @@ -131,8 +131,8 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte #if WITH_EDITOR FFlowMessageLog ValidationLog; -#endif - +#endif + ////////////////////////////////////////////////////////////////////////// // All created pins (default, class-specific and added by user) @@ -158,7 +158,7 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte uint8 CountNumberedInputs() const; uint8 CountNumberedOutputs() const; - + TArray GetInputPins() const { return InputPins; } TArray GetOutputPins() const { return OutputPins; } @@ -235,10 +235,11 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UPROPERTY(SaveGame) EFlowNodeState ActivationState; -public: +public: EFlowNodeState GetActivationState() const { return ActivationState; } - + #if !UE_BUILD_SHIPPING + private: TMap> InputRecords; TMap> OutputRecords; @@ -278,7 +279,7 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "On Activate")) void K2_OnActivate(); - + // Trigger execution of input pin void TriggerInput(const FName& PinName, const EFlowPinActivationType ActivationType = EFlowPinActivationType::Default); @@ -329,6 +330,7 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte void ResetRecords(); #if WITH_EDITOR + public: UFlowNode* GetInspectedInstance() const; @@ -377,7 +379,7 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UFUNCTION(BlueprintPure, Category = "FlowNode") static FString GetProgressAsString(float Value); -public: +public: UFUNCTION(BlueprintCallable, Category = "FlowNode") void SaveInstance(FFlowNodeSaveData& NodeRecord); @@ -387,7 +389,7 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte protected: UFUNCTION(BlueprintNativeEvent, Category = "FlowNode") void OnSave(); - + UFUNCTION(BlueprintNativeEvent, Category = "FlowNode") void OnLoad(); @@ -400,7 +402,7 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) void LogWarning(FString Message); - + UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) void LogNote(FString Message); From fbe28fd9111684cf2b57500b136f05aa8c6cfc3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Mon, 20 Feb 2023 19:25:22 +0100 Subject: [PATCH 126/485] auto-format --- Source/FlowEditor/Public/Graph/FlowGraphSettings.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h index 8f5b3fa49..13eab763b 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h @@ -15,7 +15,6 @@ UCLASS(Config = Editor, defaultconfig, meta = (DisplayName = "Flow Graph")) class FLOWEDITOR_API UFlowGraphSettings : public UDeveloperSettings { GENERATED_UCLASS_BODY() - static UFlowGraphSettings* Get() { return StaticClass()->GetDefaultObject(); } /** Show Flow Asset in Flow category of "Create Asset" menu? @@ -27,7 +26,7 @@ class FLOWEDITOR_API UFlowGraphSettings : public UDeveloperSettings * Requires restart after making a change. */ UPROPERTY(EditAnywhere, config, Category = "Default UI", meta = (ConfigRestartRequired = true)) bool bExposeFlowNodeCreation; - + /** Show Flow Asset toolbar? * Requires restart after making a change. */ UPROPERTY(EditAnywhere, config, Category = "Default UI", meta = (ConfigRestartRequired = true)) @@ -43,7 +42,7 @@ class FLOWEDITOR_API UFlowGraphSettings : public UDeveloperSettings /** Flow Asset class allowed to be assigned via Level Editor toolbar*/ UPROPERTY(EditAnywhere, config, Category = "Default UI", meta = (EditCondition = "bShowAssetToolbarAboveLevelEditor")) TSubclassOf WorldAssetClass; - + /** Hide specific nodes from the Flow Palette without changing the source code. * Requires restart after making a change. */ UPROPERTY(EditAnywhere, config, Category = "Nodes") @@ -79,7 +78,7 @@ class FLOWEDITOR_API UFlowGraphSettings : public UDeveloperSettings UPROPERTY(config, EditAnywhere, Category = "Wires", meta = (EditCondition = "ConnectionDrawType == EFlowConnectionDrawType::Circuit")) FVector2D CircuitConnectionSpacing; - + UPROPERTY(EditAnywhere, config, Category = "Wires") FLinearColor InactiveWireColor; From da59c2a971a414bdf10328dc7c651ab834a9e220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Mon, 20 Feb 2023 21:35:28 +0100 Subject: [PATCH 127/485] improved filtering of given Flow Node subclasses --- .../Private/Graph/FlowGraphSchema.cpp | 17 ++++++++++------- .../FlowEditor/Public/Graph/FlowGraphSchema.h | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index a0419810a..0bcb53d78 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -24,7 +24,7 @@ bool UFlowGraphSchema::bInitialGatherPerformed = false; TArray UFlowGraphSchema::NativeFlowNodes; TMap UFlowGraphSchema::BlueprintFlowNodes; -TMap UFlowGraphSchema::AssignedGraphNodeClasses; +TMap UFlowGraphSchema::GraphNodesByFlowNodes; bool UFlowGraphSchema::bBlueprintCompilationPending; @@ -237,9 +237,12 @@ TArray> UFlowGraphSchema::GetFlowNodeCategories() UClass* UFlowGraphSchema::GetAssignedGraphNodeClass(const UClass* FlowNodeClass) { - if (UClass* AssignedGraphNode = AssignedGraphNodeClasses.FindRef(FlowNodeClass)) + for (const TPair& GraphNodeByFlowNode : GraphNodesByFlowNodes) { - return AssignedGraphNode; + if (FlowNodeClass->IsChildOf(GraphNodeByFlowNode.Key)) + { + return GraphNodeByFlowNode.Value; + } } return UFlowGraphNode::StaticClass(); @@ -422,14 +425,14 @@ void UFlowGraphSchema::GatherNativeNodes() TArray GraphNodes; GetDerivedClasses(UFlowGraphNode::StaticClass(), GraphNodes); - for (UClass* Class : GraphNodes) + for (UClass* GraphNodeClass : GraphNodes) { - const UFlowGraphNode* DefaultObject = Class->GetDefaultObject(); - for (UClass* AssignedClass : DefaultObject->AssignedNodeClasses) + const UFlowGraphNode* GraphNodeCDO = GraphNodeClass->GetDefaultObject(); + for (UClass* AssignedClass : GraphNodeCDO->AssignedNodeClasses) { if (AssignedClass->IsChildOf(UFlowNode::StaticClass())) { - AssignedGraphNodeClasses.Emplace(AssignedClass, Class); + GraphNodesByFlowNodes.Emplace(AssignedClass, GraphNodeClass); } } } diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h index 3a5b4ef58..8ceb89ff6 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h @@ -21,7 +21,7 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema static bool bInitialGatherPerformed; static TArray NativeFlowNodes; static TMap BlueprintFlowNodes; - static TMap AssignedGraphNodeClasses; + static TMap GraphNodesByFlowNodes; static bool bBlueprintCompilationPending; From 875a57f596fa832d8e83ee97ba10bc41d6d56fa2 Mon Sep 17 00:00:00 2001 From: Markus Ahrweiler Date: Sat, 11 Mar 2023 16:37:37 +0100 Subject: [PATCH 128/485] Makes LoadRootFlow & LoadSubFlow accessible to Blueprints (#149) --- Source/Flow/Public/FlowSubsystem.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Flow/Public/FlowSubsystem.h b/Source/Flow/Public/FlowSubsystem.h index 62d468384..aab9b3769 100644 --- a/Source/Flow/Public/FlowSubsystem.h +++ b/Source/Flow/Public/FlowSubsystem.h @@ -119,7 +119,9 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem UFUNCTION(BlueprintCallable, Category = "FlowSubsystem") virtual void OnGameLoaded(UFlowSaveGame* SaveGame); + UFUNCTION(BlueprintCallable, Category = "FlowSubsystem") virtual void LoadRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const FString& SavedAssetInstanceName); + UFUNCTION(BlueprintCallable, Category = "FlowSubsystem") virtual void LoadSubFlow(UFlowNode_SubGraph* SubGraphNode, const FString& SavedAssetInstanceName); UFUNCTION(BlueprintPure, Category = "FlowSubsystem") From 4b0bc5cf617096db942957591c11ee2c0d7e7188 Mon Sep 17 00:00:00 2001 From: "Satheesh (ryanjon2040)" Date: Sat, 11 Mar 2023 21:16:21 +0530 Subject: [PATCH 129/485] Add an option to hide node description when PIE is active (#147) --- Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp | 1 + Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp | 3 ++- Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp index a09ae135b..9692ff114 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp @@ -8,6 +8,7 @@ UFlowGraphEditorSettings::UFlowGraphEditorSettings(const FObjectInitializer& Obj : Super(ObjectInitializer) , NodeDoubleClickTarget(EFlowNodeDoubleClickTarget::PrimaryAsset) , bShowNodeClass(false) + , bHideNodeDescriptionOnPIE(false) , bShowSubGraphPreview(true) , bShowSubGraphPath(true) , SubGraphPreviewSize(FVector2D(640.f, 360.f)) diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp index fdcebbf35..cdb5a4fc9 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp @@ -21,6 +21,7 @@ #include "SNodePanel.h" #include "Styling/SlateColor.h" #include "TutorialMetaData.h" +#include "Graph/FlowGraphEditorSettings.h" #include "Widgets/Images/SImage.h" #include "Widgets/Input/SButton.h" #include "Widgets/Layout/SBorder.h" @@ -55,7 +56,7 @@ void SFlowGraphNode::Construct(const FArguments& InArgs, UFlowGraphNode* InNode) void SFlowGraphNode::GetNodeInfoPopups(FNodeInfoContext* Context, TArray& Popups) const { - const FString Description = FlowGraphNode->GetNodeDescription(); + const FString Description = GEditor->PlayWorld && UFlowGraphEditorSettings::Get()->bHideNodeDescriptionOnPIE ? "" : FlowGraphNode->GetNodeDescription(); if (!Description.IsEmpty()) { const FGraphInformationPopupInfo DescriptionPopup = FGraphInformationPopupInfo(nullptr, UFlowGraphSettings::Get()->NodeDescriptionBackground, Description); diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h index 6f3cb06b1..0a07f8ae8 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h @@ -30,6 +30,10 @@ class FLOWEDITOR_API UFlowGraphEditorSettings : public UDeveloperSettings UPROPERTY(config, EditAnywhere, Category = "Nodes") bool bShowNodeClass; + // Hides the node description when you play in editor and only shows node status string. + UPROPERTY(config, EditAnywhere, Category = "Nodes") + bool bHideNodeDescriptionOnPIE; + // Renders preview of entire graph while hovering over UPROPERTY(config, EditAnywhere, Category = "Nodes") bool bShowSubGraphPreview; From ed908323bfe41bd08f8c9a2d05e88c6152eb822a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sat, 11 Mar 2023 17:00:05 +0100 Subject: [PATCH 130/485] cosmetic improvements to accepted PRs --- Source/Flow/Public/FlowSubsystem.h | 1 + Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp | 2 +- Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp | 2 +- Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Source/Flow/Public/FlowSubsystem.h b/Source/Flow/Public/FlowSubsystem.h index aab9b3769..a171a245e 100644 --- a/Source/Flow/Public/FlowSubsystem.h +++ b/Source/Flow/Public/FlowSubsystem.h @@ -121,6 +121,7 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem UFUNCTION(BlueprintCallable, Category = "FlowSubsystem") virtual void LoadRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const FString& SavedAssetInstanceName); + UFUNCTION(BlueprintCallable, Category = "FlowSubsystem") virtual void LoadSubFlow(UFlowNode_SubGraph* SubGraphNode, const FString& SavedAssetInstanceName); diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp index 9692ff114..ed81e3d11 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp @@ -8,7 +8,7 @@ UFlowGraphEditorSettings::UFlowGraphEditorSettings(const FObjectInitializer& Obj : Super(ObjectInitializer) , NodeDoubleClickTarget(EFlowNodeDoubleClickTarget::PrimaryAsset) , bShowNodeClass(false) - , bHideNodeDescriptionOnPIE(false) + , bShowNodeDescriptionInPIE(true) , bShowSubGraphPreview(true) , bShowSubGraphPath(true) , SubGraphPreviewSize(FVector2D(640.f, 360.f)) diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp index cdb5a4fc9..9879c4b3e 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp @@ -56,7 +56,7 @@ void SFlowGraphNode::Construct(const FArguments& InArgs, UFlowGraphNode* InNode) void SFlowGraphNode::GetNodeInfoPopups(FNodeInfoContext* Context, TArray& Popups) const { - const FString Description = GEditor->PlayWorld && UFlowGraphEditorSettings::Get()->bHideNodeDescriptionOnPIE ? "" : FlowGraphNode->GetNodeDescription(); + const FString Description = GEditor->PlayWorld && UFlowGraphEditorSettings::Get()->bShowNodeDescriptionInPIE ? FString() : FlowGraphNode->GetNodeDescription(); if (!Description.IsEmpty()) { const FGraphInformationPopupInfo DescriptionPopup = FGraphInformationPopupInfo(nullptr, UFlowGraphSettings::Get()->NodeDescriptionBackground, Description); diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h index 0a07f8ae8..8f2a14961 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h @@ -32,7 +32,7 @@ class FLOWEDITOR_API UFlowGraphEditorSettings : public UDeveloperSettings // Hides the node description when you play in editor and only shows node status string. UPROPERTY(config, EditAnywhere, Category = "Nodes") - bool bHideNodeDescriptionOnPIE; + bool bShowNodeDescriptionInPIE; // Renders preview of entire graph while hovering over UPROPERTY(config, EditAnywhere, Category = "Nodes") From 0c622a838f69ef30542a9a05248668ad5d8c9eec Mon Sep 17 00:00:00 2001 From: "Satheesh (ryanjon2040)" Date: Sat, 11 Mar 2023 23:55:32 +0530 Subject: [PATCH 131/485] More keywords to make search easier for multiple nodes (#144) * More keywords to make search easier for multiple nodes * Add print keyword to log node * Mooarrrr keywords --- Source/Flow/Public/Nodes/Operators/FlowNode_LogicalAND.h | 2 +- Source/Flow/Public/Nodes/Operators/FlowNode_LogicalOR.h | 2 +- Source/Flow/Public/Nodes/Route/FlowNode_ExecutionMultiGate.h | 2 +- Source/Flow/Public/Nodes/Route/FlowNode_Finish.h | 2 +- Source/Flow/Public/Nodes/Utils/FlowNode_Checkpoint.h | 2 +- Source/Flow/Public/Nodes/Utils/FlowNode_Log.h | 2 +- Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h | 2 +- Source/Flow/Public/Nodes/World/FlowNode_OnActorRegistered.h | 2 +- Source/Flow/Public/Nodes/World/FlowNode_OnActorUnregistered.h | 2 +- Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Source/Flow/Public/Nodes/Operators/FlowNode_LogicalAND.h b/Source/Flow/Public/Nodes/Operators/FlowNode_LogicalAND.h index b646bf773..6296d07e1 100644 --- a/Source/Flow/Public/Nodes/Operators/FlowNode_LogicalAND.h +++ b/Source/Flow/Public/Nodes/Operators/FlowNode_LogicalAND.h @@ -9,7 +9,7 @@ * Logical AND * Output will be triggered only once */ -UCLASS(NotBlueprintable, meta = (DisplayName = "AND")) +UCLASS(NotBlueprintable, meta = (DisplayName = "AND", Keywords = "&")) class FLOW_API UFlowNode_LogicalAND final : public UFlowNode { GENERATED_UCLASS_BODY() diff --git a/Source/Flow/Public/Nodes/Operators/FlowNode_LogicalOR.h b/Source/Flow/Public/Nodes/Operators/FlowNode_LogicalOR.h index 3265478a3..70ea21e82 100644 --- a/Source/Flow/Public/Nodes/Operators/FlowNode_LogicalOR.h +++ b/Source/Flow/Public/Nodes/Operators/FlowNode_LogicalOR.h @@ -9,7 +9,7 @@ * Logical OR * Output will be triggered only once */ -UCLASS(NotBlueprintable, meta = (DisplayName = "OR")) +UCLASS(NotBlueprintable, meta = (DisplayName = "OR", Keywords = "|")) class FLOW_API UFlowNode_LogicalOR final : public UFlowNode { GENERATED_UCLASS_BODY() diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionMultiGate.h b/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionMultiGate.h index da039c221..8c440aabe 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionMultiGate.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionMultiGate.h @@ -8,7 +8,7 @@ /** * Executes a series of pins in order */ -UCLASS(NotBlueprintable, meta = (DisplayName = "Multi Gate")) +UCLASS(NotBlueprintable, meta = (DisplayName = "Multi Gate", Keywords = "series, order, loop, random")) class FLOW_API UFlowNode_ExecutionMultiGate final : public UFlowNode { GENERATED_UCLASS_BODY() diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_Finish.h b/Source/Flow/Public/Nodes/Route/FlowNode_Finish.h index a9f9299b6..2f44851fe 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_Finish.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_Finish.h @@ -9,7 +9,7 @@ * Finish execution of this Flow Asset * All active nodes and sub graphs will be deactivated */ -UCLASS(NotBlueprintable, meta = (DisplayName = "Finish")) +UCLASS(NotBlueprintable, meta = (DisplayName = "Finish", Keywords = "stop, disable, deactivate")) class FLOW_API UFlowNode_Finish : public UFlowNode { GENERATED_UCLASS_BODY() diff --git a/Source/Flow/Public/Nodes/Utils/FlowNode_Checkpoint.h b/Source/Flow/Public/Nodes/Utils/FlowNode_Checkpoint.h index cc2b4dac8..814df107c 100644 --- a/Source/Flow/Public/Nodes/Utils/FlowNode_Checkpoint.h +++ b/Source/Flow/Public/Nodes/Utils/FlowNode_Checkpoint.h @@ -9,7 +9,7 @@ * Save the state of the game to the save file * It's recommended to replace this with game-specific variant and this node to UFlowGraphSettings::HiddenNodes */ -UCLASS(NotBlueprintable, meta = (DisplayName = "Checkpoint")) +UCLASS(NotBlueprintable, meta = (DisplayName = "Checkpoint", Keywords = "save, state")) class FLOW_API UFlowNode_Checkpoint final : public UFlowNode { GENERATED_UCLASS_BODY() diff --git a/Source/Flow/Public/Nodes/Utils/FlowNode_Log.h b/Source/Flow/Public/Nodes/Utils/FlowNode_Log.h index 5c74fcc5b..89c1fef8f 100644 --- a/Source/Flow/Public/Nodes/Utils/FlowNode_Log.h +++ b/Source/Flow/Public/Nodes/Utils/FlowNode_Log.h @@ -21,7 +21,7 @@ enum class EFlowLogVerbosity : uint8 * Adds message to log * Optionally shows message on screen */ -UCLASS(NotBlueprintable, meta = (DisplayName = "Log")) +UCLASS(NotBlueprintable, meta = (DisplayName = "Log", Keywords = "print")) class FLOW_API UFlowNode_Log : public UFlowNode { GENERATED_UCLASS_BODY() diff --git a/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h b/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h index 63f3e8c37..fa7b712b1 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h @@ -10,7 +10,7 @@ /** * Finds all Flow Components with matching Identity Tag and calls ReceiveNotify event on these components */ -UCLASS(NotBlueprintable, meta = (DisplayName = "Notify Actor")) +UCLASS(NotBlueprintable, meta = (DisplayName = "Notify Actor", Keywords = "event")) class FLOW_API UFlowNode_NotifyActor : public UFlowNode { GENERATED_UCLASS_BODY() diff --git a/Source/Flow/Public/Nodes/World/FlowNode_OnActorRegistered.h b/Source/Flow/Public/Nodes/World/FlowNode_OnActorRegistered.h index 59882d377..ecf4b9e56 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_OnActorRegistered.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_OnActorRegistered.h @@ -8,7 +8,7 @@ /** * Triggers output when Flow Component with matching Identity Tag appears in the world */ -UCLASS(NotBlueprintable, meta = (DisplayName = "On Actor Registered")) +UCLASS(NotBlueprintable, meta = (DisplayName = "On Actor Registered", Keywords = "bind")) class FLOW_API UFlowNode_OnActorRegistered : public UFlowNode_ComponentObserver { GENERATED_UCLASS_BODY() diff --git a/Source/Flow/Public/Nodes/World/FlowNode_OnActorUnregistered.h b/Source/Flow/Public/Nodes/World/FlowNode_OnActorUnregistered.h index fccad314e..4d44d740f 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_OnActorUnregistered.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_OnActorUnregistered.h @@ -8,7 +8,7 @@ /** * Triggers output when Flow Component with matching Identity Tag disappears from the world */ -UCLASS(NotBlueprintable, meta = (DisplayName = "On Actor Unregistered")) +UCLASS(NotBlueprintable, meta = (DisplayName = "On Actor Unregistered", Keywords = "unbind")) class FLOW_API UFlowNode_OnActorUnregistered : public UFlowNode_ComponentObserver { GENERATED_UCLASS_BODY() diff --git a/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h b/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h index ad118d008..6825337ba 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h @@ -20,7 +20,7 @@ DECLARE_MULTICAST_DELEGATE(FFlowNodeLevelSequenceEvent); * - Out (always, even if Sequence is invalid) * - Completed */ -UCLASS(NotBlueprintable, meta = (DisplayName = "Play Level Sequence")) +UCLASS(NotBlueprintable, meta = (DisplayName = "Play Level Sequence", Keywords = "camera, cinematic, cutscene, movie")) class FLOW_API UFlowNode_PlayLevelSequence : public UFlowNode { GENERATED_UCLASS_BODY() From 56f787f263639ced6c0c19507a11c5978fa350ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sat, 11 Mar 2023 19:28:52 +0100 Subject: [PATCH 132/485] created SaveGame section in class layout --- Source/Flow/Private/Nodes/FlowNode.cpp | 126 ++++++++++++------------- Source/Flow/Public/Nodes/FlowNode.h | 41 ++++---- 2 files changed, 86 insertions(+), 81 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 17325db06..4a01ce343 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -546,6 +546,69 @@ void UFlowNode::ResetRecords() #endif } +void UFlowNode::SaveInstance(FFlowNodeSaveData& NodeRecord) +{ + NodeRecord.NodeGuid = NodeGuid; + OnSave(); + + FMemoryWriter MemoryWriter(NodeRecord.NodeData, true); + FFlowArchive Ar(MemoryWriter); + Serialize(Ar); +} + +void UFlowNode::LoadInstance(const FFlowNodeSaveData& NodeRecord) +{ + FMemoryReader MemoryReader(NodeRecord.NodeData, true); + FFlowArchive Ar(MemoryReader); + Serialize(Ar); + + if (UFlowAsset* FlowAsset = GetFlowAsset()) + { + FlowAsset->OnActivationStateLoaded(this); + } + + switch (SignalMode) + { + case EFlowSignalMode::Enabled: + OnLoad(); + break; + case EFlowSignalMode::Disabled: + // designer doesn't want to execute this node's logic at all, so we kill it + LogNote(TEXT("Signal disabled while loading Flow Node from SaveGame")); + Finish(); + break; + case EFlowSignalMode::PassThrough: + LogNote(TEXT("Signal pass-through on loading Flow Node from SaveGame")); + OnPassThrough(); + break; + default: ; + } +} + +void UFlowNode::OnSave_Implementation() +{ +} + +void UFlowNode::OnLoad_Implementation() +{ +} + +void UFlowNode::OnPassThrough_Implementation() +{ + // trigger all connected outputs + // pin connections aren't serialized to the SaveGame, so users can safely change connections post game release + for (const FFlowPin& OutputPin : OutputPins) + { + if (Connections.Contains(OutputPin.PinName)) + { + TriggerOutput(OutputPin.PinName, false, EFlowPinActivationType::PassThrough); + } + } + + // deactivate node, so it doesn't get saved to a new SaveGame + Finish(); +} + #if WITH_EDITOR UFlowNode* UFlowNode::GetInspectedInstance() const { @@ -631,69 +694,6 @@ FString UFlowNode::GetProgressAsString(const float Value) return FString::Printf(TEXT("%.*f"), 2, Value); } -void UFlowNode::SaveInstance(FFlowNodeSaveData& NodeRecord) -{ - NodeRecord.NodeGuid = NodeGuid; - OnSave(); - - FMemoryWriter MemoryWriter(NodeRecord.NodeData, true); - FFlowArchive Ar(MemoryWriter); - Serialize(Ar); -} - -void UFlowNode::LoadInstance(const FFlowNodeSaveData& NodeRecord) -{ - FMemoryReader MemoryReader(NodeRecord.NodeData, true); - FFlowArchive Ar(MemoryReader); - Serialize(Ar); - - if (UFlowAsset* FlowAsset = GetFlowAsset()) - { - FlowAsset->OnActivationStateLoaded(this); - } - - switch (SignalMode) - { - case EFlowSignalMode::Enabled: - OnLoad(); - break; - case EFlowSignalMode::Disabled: - // designer doesn't want to execute this node's logic at all, so we kill it - LogNote(TEXT("Signal disabled while loading Flow Node from SaveGame")); - Finish(); - break; - case EFlowSignalMode::PassThrough: - LogNote(TEXT("Signal pass-through on loading Flow Node from SaveGame")); - OnPassThrough(); - break; - default: ; - } -} - -void UFlowNode::OnSave_Implementation() -{ -} - -void UFlowNode::OnLoad_Implementation() -{ -} - -void UFlowNode::OnPassThrough_Implementation() -{ - // trigger all connected outputs - // pin connections aren't serialized to the SaveGame, so users can safely change connections post game release - for (const FFlowPin& OutputPin : OutputPins) - { - if (Connections.Contains(OutputPin.PinName)) - { - TriggerOutput(OutputPin.PinName, false, EFlowPinActivationType::PassThrough); - } - } - - // deactivate node, so it doesn't get saved to a new SaveGame - Finish(); -} - void UFlowNode::LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType) { #if !UE_BUILD_SHIPPING diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index f4e83d7e2..e7543d435 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -329,8 +329,30 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte private: void ResetRecords(); -#if WITH_EDITOR +////////////////////////////////////////////////////////////////////////// +// SaveGame support + +public: + UFUNCTION(BlueprintCallable, Category = "FlowNode") + void SaveInstance(FFlowNodeSaveData& NodeRecord); + + UFUNCTION(BlueprintCallable, Category = "FlowNode") + void LoadInstance(const FFlowNodeSaveData& NodeRecord); + +protected: + UFUNCTION(BlueprintNativeEvent, Category = "FlowNode") + void OnSave(); + + UFUNCTION(BlueprintNativeEvent, Category = "FlowNode") + void OnLoad(); + + UFUNCTION(BlueprintNativeEvent, Category = "FlowNode") + void OnPassThrough(); + +////////////////////////////////////////////////////////////////////////// +// Utils +#if WITH_EDITOR public: UFlowNode* GetInspectedInstance() const; @@ -379,23 +401,6 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UFUNCTION(BlueprintPure, Category = "FlowNode") static FString GetProgressAsString(float Value); -public: - UFUNCTION(BlueprintCallable, Category = "FlowNode") - void SaveInstance(FFlowNodeSaveData& NodeRecord); - - UFUNCTION(BlueprintCallable, Category = "FlowNode") - void LoadInstance(const FFlowNodeSaveData& NodeRecord); - -protected: - UFUNCTION(BlueprintNativeEvent, Category = "FlowNode") - void OnSave(); - - UFUNCTION(BlueprintNativeEvent, Category = "FlowNode") - void OnLoad(); - - UFUNCTION(BlueprintNativeEvent, Category = "FlowNode") - void OnPassThrough(); - public: UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) void LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType = EFlowOnScreenMessageType::Permanent); From 730c2b4de6ab33b2c6ff8e0d09b98cf136dab427 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sat, 11 Mar 2023 19:36:09 +0100 Subject: [PATCH 133/485] minor changes to search keywords, avoiding confusion in project with custom nodes --- Source/Flow/Public/Nodes/Route/FlowNode_ExecutionMultiGate.h | 2 +- Source/Flow/Public/Nodes/Route/FlowNode_Finish.h | 2 +- Source/Flow/Public/Nodes/Utils/FlowNode_Checkpoint.h | 2 +- Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionMultiGate.h b/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionMultiGate.h index 8c440aabe..262529cbc 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionMultiGate.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionMultiGate.h @@ -8,7 +8,7 @@ /** * Executes a series of pins in order */ -UCLASS(NotBlueprintable, meta = (DisplayName = "Multi Gate", Keywords = "series, order, loop, random")) +UCLASS(NotBlueprintable, meta = (DisplayName = "Multi Gate", Keywords = "series, loop, random")) class FLOW_API UFlowNode_ExecutionMultiGate final : public UFlowNode { GENERATED_UCLASS_BODY() diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_Finish.h b/Source/Flow/Public/Nodes/Route/FlowNode_Finish.h index 2f44851fe..a9f9299b6 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_Finish.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_Finish.h @@ -9,7 +9,7 @@ * Finish execution of this Flow Asset * All active nodes and sub graphs will be deactivated */ -UCLASS(NotBlueprintable, meta = (DisplayName = "Finish", Keywords = "stop, disable, deactivate")) +UCLASS(NotBlueprintable, meta = (DisplayName = "Finish")) class FLOW_API UFlowNode_Finish : public UFlowNode { GENERATED_UCLASS_BODY() diff --git a/Source/Flow/Public/Nodes/Utils/FlowNode_Checkpoint.h b/Source/Flow/Public/Nodes/Utils/FlowNode_Checkpoint.h index 814df107c..a4ce5605e 100644 --- a/Source/Flow/Public/Nodes/Utils/FlowNode_Checkpoint.h +++ b/Source/Flow/Public/Nodes/Utils/FlowNode_Checkpoint.h @@ -9,7 +9,7 @@ * Save the state of the game to the save file * It's recommended to replace this with game-specific variant and this node to UFlowGraphSettings::HiddenNodes */ -UCLASS(NotBlueprintable, meta = (DisplayName = "Checkpoint", Keywords = "save, state")) +UCLASS(NotBlueprintable, meta = (DisplayName = "Checkpoint", Keywords = "autosave, save")) class FLOW_API UFlowNode_Checkpoint final : public UFlowNode { GENERATED_UCLASS_BODY() diff --git a/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h b/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h index 6825337ba..ad118d008 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h @@ -20,7 +20,7 @@ DECLARE_MULTICAST_DELEGATE(FFlowNodeLevelSequenceEvent); * - Out (always, even if Sequence is invalid) * - Completed */ -UCLASS(NotBlueprintable, meta = (DisplayName = "Play Level Sequence", Keywords = "camera, cinematic, cutscene, movie")) +UCLASS(NotBlueprintable, meta = (DisplayName = "Play Level Sequence")) class FLOW_API UFlowNode_PlayLevelSequence : public UFlowNode { GENERATED_UCLASS_BODY() From 99de885f25b2ac0f5b6fab89c0813fe0b559af2c Mon Sep 17 00:00:00 2001 From: "Satheesh (ryanjon2040)" Date: Sun, 12 Mar 2023 18:55:11 +0530 Subject: [PATCH 134/485] Add an option to toggle signal mode logs (#145) --- Source/Flow/Private/FlowSettings.cpp | 2 ++ Source/Flow/Private/Nodes/FlowNode.cpp | 11 +++++++++-- Source/Flow/Public/FlowSettings.h | 8 ++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Source/Flow/Private/FlowSettings.cpp b/Source/Flow/Private/FlowSettings.cpp index c287a3aef..b670a1bec 100644 --- a/Source/Flow/Private/FlowSettings.cpp +++ b/Source/Flow/Private/FlowSettings.cpp @@ -6,5 +6,7 @@ UFlowSettings::UFlowSettings(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , bCreateFlowSubsystemOnClients(true) , bWarnAboutMissingIdentityTags(true) + , bLogOnSignalDisabled(true) + , bLogOnSignalPassthrough(true) { } diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 4a01ce343..31e3a64f7 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -4,6 +4,7 @@ #include "FlowAsset.h" #include "FlowModule.h" +#include "FlowSettings.h" #include "FlowSubsystem.h" #include "FlowTypes.h" @@ -427,10 +428,16 @@ void UFlowNode::TriggerInput(const FName& PinName, const EFlowPinActivationType ExecuteInput(PinName); break; case EFlowSignalMode::Disabled: - LogNote(FString::Printf(TEXT("Node disabled while triggering input %s"), *PinName.ToString())); + if (UFlowSettings::Get()->bLogOnSignalDisabled) + { + LogNote(FString::Printf(TEXT("Node disabled while triggering input %s"), *PinName.ToString())); + } break; case EFlowSignalMode::PassThrough: - LogNote(FString::Printf(TEXT("Signal pass-through on triggering input %s"), *PinName.ToString())); + if (UFlowSettings::Get()->bLogOnSignalPassthrough) + { + LogNote(FString::Printf(TEXT("Signal pass-through on triggering input %s"), *PinName.ToString())); + } OnPassThrough(); break; default: ; diff --git a/Source/Flow/Public/FlowSettings.h b/Source/Flow/Public/FlowSettings.h index 6b9e66f18..9ca7c3824 100644 --- a/Source/Flow/Public/FlowSettings.h +++ b/Source/Flow/Public/FlowSettings.h @@ -29,4 +29,12 @@ class FLOW_API UFlowSettings : public UDeveloperSettings UPROPERTY(Config, EditAnywhere, Category = "SaveSystem") bool bWarnAboutMissingIdentityTags; + + // If enabled, runtime logs will be added when a flow node signal mode is set to Disabled + UPROPERTY(Config, EditAnywhere, Category = "Flow") + bool bLogOnSignalDisabled; + + // If enabled, runtime logs will be added when a flow node signal mode is set to Pass-through + UPROPERTY(Config, EditAnywhere, Category = "Flow") + bool bLogOnSignalPassthrough; }; From a82b314211885614b04cdb13763f5e251e6b3e4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 19 Mar 2023 14:01:40 +0100 Subject: [PATCH 135/485] Refactored plain debugger struct into Flow Debugged Subsystem - This is editor subsystem gathering Runtime Logs is independent from Flow Asset editor instances. - Editor throws a notification on PIE end if there are any warnings or errors reported. --- Source/Flow/Private/FlowAsset.cpp | 81 ++++++----- Source/Flow/Private/FlowSubsystem.cpp | 5 + Source/Flow/Private/Nodes/FlowNode.cpp | 5 +- Source/Flow/Public/FlowAsset.h | 28 ++-- Source/Flow/Public/FlowSubsystem.h | 15 +- Source/FlowEditor/FlowEditor.Build.cs | 5 +- .../Private/Asset/FlowAssetEditor.cpp | 21 +-- .../FlowEditor/Private/Asset/FlowDebugger.cpp | 60 -------- .../Private/Asset/FlowDebuggerSubsystem.cpp | 129 ++++++++++++++++++ .../Private/Asset/FlowMessageLogListing.cpp | 26 ++-- .../Private/Graph/Nodes/FlowGraphNode.cpp | 4 +- .../FlowEditor/Public/Asset/FlowAssetEditor.h | 4 - Source/FlowEditor/Public/Asset/FlowDebugger.h | 19 --- .../Public/Asset/FlowDebuggerSubsystem.h | 36 +++++ .../Public/Asset/FlowMessageLogListing.h | 1 + 15 files changed, 272 insertions(+), 167 deletions(-) delete mode 100644 Source/FlowEditor/Private/Asset/FlowDebugger.cpp create mode 100644 Source/FlowEditor/Private/Asset/FlowDebuggerSubsystem.cpp delete mode 100644 Source/FlowEditor/Public/Asset/FlowDebugger.h create mode 100644 Source/FlowEditor/Public/Asset/FlowDebuggerSubsystem.h diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 0eaaa3e58..13574003e 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -3,6 +3,7 @@ #include "FlowAsset.h" #include "FlowMessageLog.h" +#include "FlowModule.h" #include "FlowSettings.h" #include "FlowSubsystem.h" @@ -281,9 +282,9 @@ void UFlowAsset::BroadcastDebuggerRefresh() const RefreshDebuggerEvent.Broadcast(); } -void UFlowAsset::BroadcastRuntimeMessageAdded(const UFlowAsset* AssetInstance, const TSharedRef& Message) const +void UFlowAsset::BroadcastRuntimeMessageAdded(const TSharedRef& Message) { - RuntimeMessageEvent.Broadcast(AssetInstance, Message); + RuntimeMessageEvent.Broadcast(this, Message); } #endif @@ -501,35 +502,6 @@ UFlowAsset* UFlowAsset::GetParentInstance() const return NodeOwningThisAssetInstance.IsValid() ? NodeOwningThisAssetInstance.Get()->GetFlowAsset() : nullptr; } -#if WITH_EDITOR -void UFlowAsset::LogError(const FString& MessageToLog, UFlowNode* Node) const -{ - if (RuntimeLog.IsValid()) - { - const TSharedRef TokenizedMessage = RuntimeLog.Get()->Error(*MessageToLog, Node); - BroadcastRuntimeMessageAdded(this, TokenizedMessage); - } -} - -void UFlowAsset::LogWarning(const FString& MessageToLog, UFlowNode* Node) const -{ - if (RuntimeLog.IsValid()) - { - const TSharedRef TokenizedMessage = RuntimeLog.Get()->Warning(*MessageToLog, Node); - BroadcastRuntimeMessageAdded(this, TokenizedMessage); - } -} - -void UFlowAsset::LogNote(const FString& MessageToLog, UFlowNode* Node) const -{ - if (RuntimeLog.IsValid()) - { - const TSharedRef TokenizedMessage = RuntimeLog.Get()->Note(*MessageToLog, Node); - BroadcastRuntimeMessageAdded(this, TokenizedMessage); - } -} -#endif - FFlowAssetSaveData UFlowAsset::SaveInstance(TArray& SavedFlowInstances) { FFlowAssetSaveData AssetRecord; @@ -616,3 +588,50 @@ bool UFlowAsset::IsBoundToWorld_Implementation() { return bWorldBound; } + +#if WITH_EDITOR +void UFlowAsset::LogError(const FString& MessageToLog, UFlowNode* Node) +{ + // this is runtime log which is should be only called on runtime instances of asset + if (TemplateAsset == nullptr) + { + UE_LOG(LogFlow, Log, TEXT("Attempted to use Runtime Log on template asset %s"), *MessageToLog); + } + + if (RuntimeLog.Get()) + { + const TSharedRef TokenizedMessage = RuntimeLog.Get()->Error(*MessageToLog, Node); + BroadcastRuntimeMessageAdded(TokenizedMessage); + } +} + +void UFlowAsset::LogWarning(const FString& MessageToLog, UFlowNode* Node) +{ + // this is runtime log which is should be only called on runtime instances of asset + if (TemplateAsset == nullptr) + { + UE_LOG(LogFlow, Log, TEXT("Attempted to use Runtime Log on template asset %s"), *MessageToLog); + } + + if (RuntimeLog.Get()) + { + const TSharedRef TokenizedMessage = RuntimeLog.Get()->Warning(*MessageToLog, Node); + BroadcastRuntimeMessageAdded(TokenizedMessage); + } +} + +void UFlowAsset::LogNote(const FString& MessageToLog, UFlowNode* Node) +{ + // this is runtime log which is should be only called on runtime instances of asset + if (TemplateAsset == nullptr) + { + UE_LOG(LogFlow, Log, TEXT("Attempted to use Runtime Log on template asset %s"), *MessageToLog); + } + + if (RuntimeLog.Get()) + { + const TSharedRef TokenizedMessage = RuntimeLog.Get()->Note(*MessageToLog, Node); + BroadcastRuntimeMessageAdded(TokenizedMessage); + } +} +#endif diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index 4913040c8..4cd30e5b3 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -14,6 +14,9 @@ #include "Misc/Paths.h" #include "UObject/UObjectHash.h" +FNativeFlowAssetEvent UFlowSubsystem::OnInstancedTemplateAdded; +FNativeFlowAssetEvent UFlowSubsystem::OnInstancedTemplateRemoved; + UFlowSubsystem::UFlowSubsystem() : UGameInstanceSubsystem() { @@ -226,6 +229,7 @@ void UFlowSubsystem::AddInstancedTemplate(UFlowAsset* Template) #if WITH_EDITOR Template->RuntimeLog = MakeShareable(new FFlowMessageLog()); + OnInstancedTemplateAdded.ExecuteIfBound(Template); #endif } } @@ -233,6 +237,7 @@ void UFlowSubsystem::AddInstancedTemplate(UFlowAsset* Template) void UFlowSubsystem::RemoveInstancedTemplate(UFlowAsset* Template) { #if WITH_EDITOR + OnInstancedTemplateRemoved.ExecuteIfBound(Template); Template->RuntimeLog.Reset(); #endif diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 31e3a64f7..463ac3149 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -748,10 +748,7 @@ void UFlowNode::LogWarning(FString Message) // Message Log #if WITH_EDITOR - if (GetFlowAsset()->GetTemplateAsset()) - { - GetFlowAsset()->GetTemplateAsset()->LogWarning(Message, this); - } + GetFlowAsset()->GetTemplateAsset()->LogWarning(Message, this); #endif } #endif diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index e6aa39e1d..7e4f87bd0 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -30,7 +30,8 @@ class FLOW_API IFlowGraphInterface virtual void OnOutputTriggered(UEdGraphNode* GraphNode, const int32 Index) const {} }; -DECLARE_DELEGATE(FFlowAssetEvent); +DECLARE_DELEGATE(FFlowGraphEvent); + #endif /** @@ -119,7 +120,7 @@ class FLOW_API UFlowAsset : public UObject public: #if WITH_EDITOR - FFlowAssetEvent OnSubGraphReconstructionRequested; + FFlowGraphEvent OnSubGraphReconstructionRequested; UFlowNode* CreateNode(const UClass* NodeClass, UEdGraphNode* GraphNode); @@ -163,6 +164,7 @@ class FLOW_API UFlowAsset : public UObject TWeakObjectPtr InspectedInstance; // Message log for storing runtime errors/notes/warnings that will only last until the next game run + // Log lives in the asset template, so it can be inspected after ending the PIE TSharedPtr RuntimeLog; #endif @@ -184,14 +186,14 @@ class FLOW_API UFlowAsset : public UObject FRefreshDebuggerEvent& OnDebuggerRefresh() { return RefreshDebuggerEvent; } FRefreshDebuggerEvent RefreshDebuggerEvent; - DECLARE_EVENT_TwoParams(UFlowAsset, FRuntimeMessageEvent, const UFlowAsset*, const TSharedRef&); + DECLARE_EVENT_TwoParams(UFlowAsset, FRuntimeMessageEvent, UFlowAsset*, const TSharedRef&); FRuntimeMessageEvent& OnRuntimeMessageAdded() { return RuntimeMessageEvent; } FRuntimeMessageEvent RuntimeMessageEvent; private: void BroadcastDebuggerRefresh() const; - void BroadcastRuntimeMessageAdded(const UFlowAsset* AssetInstance, const TSharedRef& Message) const;; + void BroadcastRuntimeMessageAdded(const TSharedRef& Message); #endif ////////////////////////////////////////////////////////////////////////// @@ -287,14 +289,8 @@ class FLOW_API UFlowAsset : public UObject UFUNCTION(BlueprintPure, Category = "Flow") TArray GetRecordedNodes() const { return RecordedNodes; } -#if WITH_EDITOR - void LogError(const FString& MessageToLog, UFlowNode* Node) const; - void LogWarning(const FString& MessageToLog, UFlowNode* Node) const; - void LogNote(const FString& MessageToLog, UFlowNode* Node) const; -#endif - ////////////////////////////////////////////////////////////////////////// -// SaveGame +// SaveGame support UFUNCTION(BlueprintCallable, Category = "SaveGame") FFlowAssetSaveData SaveInstance(TArray& SavedFlowInstances); @@ -315,4 +311,14 @@ class FLOW_API UFlowAsset : public UObject public: UFUNCTION(BlueprintNativeEvent, Category = "SaveGame") bool IsBoundToWorld(); + +////////////////////////////////////////////////////////////////////////// +// Utils + +#if WITH_EDITOR +public: + void LogError(const FString& MessageToLog, UFlowNode* Node); + void LogWarning(const FString& MessageToLog, UFlowNode* Node); + void LogNote(const FString& MessageToLog, UFlowNode* Node); +#endif }; diff --git a/Source/Flow/Public/FlowSubsystem.h b/Source/Flow/Public/FlowSubsystem.h index a171a245e..2255519de 100644 --- a/Source/Flow/Public/FlowSubsystem.h +++ b/Source/Flow/Public/FlowSubsystem.h @@ -17,6 +17,8 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE(FSimpleFlowEvent); DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSimpleFlowComponentEvent, UFlowComponent*, Component); DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FTaggedFlowComponentEvent, UFlowComponent*, Component, const FGameplayTagContainer&, Tags); +DECLARE_DELEGATE_OneParam(FNativeFlowAssetEvent, class UFlowAsset*); + /** * Flow Subsystem * - manages lifetime of Flow Graphs @@ -31,11 +33,11 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem public: UFlowSubsystem(); -private: friend class UFlowAsset; friend class UFlowComponent; friend class UFlowNode_SubGraph; +private: /* All asset templates with active instances */ UPROPERTY() TArray InstancedTemplates; @@ -50,6 +52,15 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem FStreamableManager Streamable; +#if WITH_EDITOR +public: + /* Called after creating the first instance of given Flow Asset */ + static FNativeFlowAssetEvent OnInstancedTemplateAdded; + + /* Called just before removing the last instance of given Flow Asset */ + static FNativeFlowAssetEvent OnInstancedTemplateRemoved; +#endif + protected: UPROPERTY() UFlowSaveGame* LoadedSaveGame; @@ -108,7 +119,7 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem virtual UWorld* GetWorld() const override; ////////////////////////////////////////////////////////////////////////// -// SaveGame +// SaveGame support UPROPERTY(BlueprintAssignable, Category = "FlowSubsystem") FSimpleFlowEvent OnSaveGame; diff --git a/Source/FlowEditor/FlowEditor.Build.cs b/Source/FlowEditor/FlowEditor.Build.cs index 6a7901f69..cf1e47c04 100644 --- a/Source/FlowEditor/FlowEditor.Build.cs +++ b/Source/FlowEditor/FlowEditor.Build.cs @@ -26,9 +26,10 @@ public FlowEditor(ReadOnlyTargetRules target) : base(target) "CoreUObject", "DetailCustomizations", "DeveloperSettings", - "EditorScriptingUtilities", "EditorFramework", + "EditorScriptingUtilities", "EditorStyle", + "EditorSubsystem", "Engine", "GraphEditor", "InputCore", @@ -39,8 +40,8 @@ public FlowEditor(ReadOnlyTargetRules target) : base(target) "LevelEditor", "LevelSequence", "MovieScene", - "MovieSceneTracks", "MovieSceneTools", + "MovieSceneTracks", "Projects", "PropertyEditor", "PropertyPath", diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index 4626ddea1..ebf6c08fb 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -8,7 +8,7 @@ #include "Asset/FlowAssetEditorContext.h" #include "Asset/FlowAssetToolbar.h" -#include "Asset/FlowDebugger.h" +#include "Asset/FlowDebuggerSubsystem.h" #include "Asset/FlowMessageLogListing.h" #include "Graph/FlowGraph.h" #include "Graph/FlowGraphEditorSettings.h" @@ -261,7 +261,6 @@ void FFlowAssetEditor::InitFlowAssetEditor(const EToolkitMode::Type Mode, const GEditor->RegisterForUndo(this); UFlowGraphSchema::SubscribeToAssetChanges(); - FlowDebugger = MakeShareable(new FFlowDebugger); BindToolbarCommands(); CreateToolbar(); @@ -269,9 +268,6 @@ void FFlowAssetEditor::InitFlowAssetEditor(const EToolkitMode::Type Mode, const BindGraphCommands(); CreateWidgets(); - FlowAsset->OnRuntimeMessageAdded().AddSP(this, &FFlowAssetEditor::OnRuntimeMessageAdded); - FEditorDelegates::BeginPIE.AddSP(this, &FFlowAssetEditor::OnBeginPIE); - const TSharedRef StandaloneDefaultLayout = FTabManager::NewLayout("FlowAssetEditor_Layout_v5.1") ->AddArea ( @@ -483,7 +479,7 @@ FGraphAppearanceInfo FFlowAssetEditor::GetGraphAppearanceInfo() const FGraphAppearanceInfo AppearanceInfo; AppearanceInfo.CornerText = GetCornerText(); - if (FlowDebugger.IsValid() && FFlowDebugger::IsPlaySessionPaused()) + if (UFlowDebuggerSubsystem::IsPlaySessionPaused()) { AppearanceInfo.PIENotifyText = LOCTEXT("PausedLabel", "PAUSED"); } @@ -753,11 +749,6 @@ EVisibility FFlowAssetEditor::GetDebuggerVisibility() return GEditor->PlayWorld ? EVisibility::Visible : EVisibility::Collapsed; } -void FFlowAssetEditor::OnBeginPIE(const bool bInSimulateInEditor) const -{ - RuntimeLogListing->ClearMessages(); -} - TSet FFlowAssetEditor::GetSelectedFlowNodes() const { TSet Result; @@ -832,14 +823,6 @@ void FFlowAssetEditor::JumpToInnerObject(UObject* InnerObject) } #endif -void FFlowAssetEditor::OnRuntimeMessageAdded(const UFlowAsset* AssetInstance, const TSharedRef& Message) const -{ - // push messages to its window - TabManager->TryInvokeTab(RuntimeLogTab); - RuntimeLogListing->AddMessage(Message); - RuntimeLogListing->OnDataChanged().Broadcast(); -} - void FFlowAssetEditor::OnLogTokenClicked(const TSharedRef& Token) const { if (Token->GetType() == EMessageToken::Object) diff --git a/Source/FlowEditor/Private/Asset/FlowDebugger.cpp b/Source/FlowEditor/Private/Asset/FlowDebugger.cpp deleted file mode 100644 index f7e2aea37..000000000 --- a/Source/FlowEditor/Private/Asset/FlowDebugger.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#include "Asset/FlowDebugger.h" - -#include "Engine/Engine.h" -#include "Engine/World.h" -#include "Templates/Function.h" -#include "UnrealEd.h" - -FFlowDebugger::FFlowDebugger() -{ -} - -FFlowDebugger::~FFlowDebugger() -{ -} - -void ForEachGameWorld(const TFunction& Func) -{ - for (const FWorldContext& PieContext : GUnrealEd->GetWorldContexts()) - { - UWorld* PlayWorld = PieContext.World(); - if (PlayWorld && PlayWorld->IsGameWorld()) - { - Func(PlayWorld); - } - } -} - -bool AreAllGameWorldPaused() -{ - bool bPaused = true; - ForEachGameWorld([&](const UWorld* World) - { - bPaused = bPaused && World->bDebugPauseExecution; - }); - return bPaused; -} - -void FFlowDebugger::PausePlaySession() -{ - bool bPaused = false; - ForEachGameWorld([&](UWorld* World) - { - if (!World->bDebugPauseExecution) - { - World->bDebugPauseExecution = true; - bPaused = true; - } - }); - if (bPaused) - { - GUnrealEd->PlaySessionPaused(); - } -} - -bool FFlowDebugger::IsPlaySessionPaused() -{ - return AreAllGameWorldPaused(); -} diff --git a/Source/FlowEditor/Private/Asset/FlowDebuggerSubsystem.cpp b/Source/FlowEditor/Private/Asset/FlowDebuggerSubsystem.cpp new file mode 100644 index 000000000..635882999 --- /dev/null +++ b/Source/FlowEditor/Private/Asset/FlowDebuggerSubsystem.cpp @@ -0,0 +1,129 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Asset/FlowDebuggerSubsystem.h" +#include "Asset/FlowAssetEditor.h" +#include "Asset/FlowMessageLogListing.h" + +#include "FlowSubsystem.h" + +#include "Engine/Engine.h" +#include "Engine/World.h" +#include "Framework/Notifications/NotificationManager.h" +#include "Templates/Function.h" +#include "UnrealEd.h" +#include "Widgets/Notifications/SNotificationList.h" + +#define LOCTEXT_NAMESPACE "FlowDebuggerSubsystem" + +UFlowDebuggerSubsystem::UFlowDebuggerSubsystem() +{ + FEditorDelegates::BeginPIE.AddUObject(this, &UFlowDebuggerSubsystem::OnBeginPIE); + FEditorDelegates::EndPIE.AddUObject(this, &UFlowDebuggerSubsystem::OnEndPIE); + + UFlowSubsystem::OnInstancedTemplateAdded.BindUObject(this, &UFlowDebuggerSubsystem::OnInstancedTemplateAdded); + UFlowSubsystem::OnInstancedTemplateRemoved.BindUObject(this, &UFlowDebuggerSubsystem::OnInstancedTemplateRemoved); +} + +void UFlowDebuggerSubsystem::OnInstancedTemplateAdded(UFlowAsset* FlowAsset) +{ + if (!RuntimeLogs.Contains(FlowAsset)) + { + RuntimeLogs.Add(FlowAsset, FFlowMessageLogListing::GetLogListing(FlowAsset, EFlowLogType::Runtime)); + FlowAsset->OnRuntimeMessageAdded().AddUObject(this, &UFlowDebuggerSubsystem::OnRuntimeMessageAdded); + } +} + +void UFlowDebuggerSubsystem::OnInstancedTemplateRemoved(UFlowAsset* FlowAsset) +{ + FlowAsset->OnRuntimeMessageAdded().RemoveAll(this); +} + +void UFlowDebuggerSubsystem::OnRuntimeMessageAdded(UFlowAsset* FlowAsset, const TSharedRef& Message) const +{ + const TSharedPtr Log = RuntimeLogs.FindRef(FlowAsset); + if (Log.IsValid()) + { + Log->AddMessage(Message); + Log->OnDataChanged().Broadcast(); + } +} + +void UFlowDebuggerSubsystem::OnBeginPIE(const bool bIsSimulating) +{ + // clear all logs from a previous session + RuntimeLogs.Empty(); +} + +void UFlowDebuggerSubsystem::OnEndPIE(const bool bIsSimulating) +{ + for (const TPair, TSharedPtr>& Log : RuntimeLogs) + { + if (Log.Key.IsValid() && Log.Value->NumMessages(EMessageSeverity::Warning) > 0) + { + FNotificationInfo Info{FText::FromString(TEXT("Flow Graph reported in-game issues"))}; + Info.ExpireDuration = 15.0; + + Info.HyperlinkText = FText::Format(LOCTEXT("OpenFlowAssetHyperlink", "Open {0}"), FText::FromString(Log.Key->GetName())); + Info.Hyperlink = FSimpleDelegate::CreateLambda([this, Log]() + { + UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem(); + if (AssetEditorSubsystem->OpenEditorForAsset(Log.Key.Get())) + { + AssetEditorSubsystem->FindEditorForAsset(Log.Key.Get(), true)->InvokeTab(FFlowAssetEditor::RuntimeLogTab); + } + }); + + const TSharedPtr Notification = FSlateNotificationManager::Get().AddNotification(Info); + if (Notification.IsValid()) + { + Notification->SetCompletionState(SNotificationItem::CS_Fail); + } + } + } +} + +void ForEachGameWorld(const TFunction& Func) +{ + for (const FWorldContext& PieContext : GUnrealEd->GetWorldContexts()) + { + UWorld* PlayWorld = PieContext.World(); + if (PlayWorld && PlayWorld->IsGameWorld()) + { + Func(PlayWorld); + } + } +} + +bool AreAllGameWorldPaused() +{ + bool bPaused = true; + ForEachGameWorld([&](const UWorld* World) + { + bPaused = bPaused && World->bDebugPauseExecution; + }); + return bPaused; +} + +void UFlowDebuggerSubsystem::PausePlaySession() +{ + bool bPaused = false; + ForEachGameWorld([&](UWorld* World) + { + if (!World->bDebugPauseExecution) + { + World->bDebugPauseExecution = true; + bPaused = true; + } + }); + if (bPaused) + { + GUnrealEd->PlaySessionPaused(); + } +} + +bool UFlowDebuggerSubsystem::IsPlaySessionPaused() +{ + return AreAllGameWorldPaused(); +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/Asset/FlowMessageLogListing.cpp b/Source/FlowEditor/Private/Asset/FlowMessageLogListing.cpp index ce4d264f7..9bd7a6f73 100644 --- a/Source/FlowEditor/Private/Asset/FlowMessageLogListing.cpp +++ b/Source/FlowEditor/Private/Asset/FlowMessageLogListing.cpp @@ -14,7 +14,7 @@ FFlowMessageLogListing::FFlowMessageLogListing(const UFlowAsset* InFlowAsset, co FFlowMessageLogListing::~FFlowMessageLogListing() { // Unregister the log so it will be ref-counted to zero if it has no messages - if(Log->NumMessages(EMessageSeverity::Info) == 0) + if (Log->NumMessages(EMessageSeverity::Info) == 0) { FMessageLogModule& MessageLogModule = FModuleManager::LoadModuleChecked("MessageLog"); MessageLogModule.UnregisterLogListing(Log->GetName()); @@ -37,20 +37,21 @@ TSharedRef FFlowMessageLogListing::RegisterLogListing(const TSharedRef FFlowMessageLogListing::GetLogListing(const UFlowAsset* InFlowAsset, const EFlowLogType Type) { FMessageLogModule& MessageLogModule = FModuleManager::LoadModuleChecked("MessageLog"); - const FName LogName = GetListingName(InFlowAsset, Type); - // Reuse any existing log, or create a new one (that is not held onto bey the message log system) - if(MessageLogModule.IsRegisteredLogListing(LogName)) + // Create a new message log + if (!MessageLogModule.IsRegisteredLogListing(LogName)) { - return MessageLogModule.GetLogListing(LogName); - } - else - { - FMessageLogInitializationOptions LogInitOptions; - LogInitOptions.bShowInLogWindow = false; - return MessageLogModule.CreateLogListing(LogName, LogInitOptions); + MessageLogModule.RegisterLogListing(LogName, FText::FromString(GetLogLabel(Type))); } + + return MessageLogModule.GetLogListing(LogName); +} + +FString FFlowMessageLogListing::GetLogLabel(const EFlowLogType Type) +{ + const FString TypeAsString = StaticEnum()->GetNameStringByIndex(static_cast(Type)); + return FString::Printf(TEXT("Flow%sLog"), *TypeAsString); } FName FFlowMessageLogListing::GetListingName(const UFlowAsset* InFlowAsset, const EFlowLogType Type) @@ -58,8 +59,7 @@ FName FFlowMessageLogListing::GetListingName(const UFlowAsset* InFlowAsset, cons FName LogListingName; if (InFlowAsset) { - const FString TypeAsString = StaticEnum()->GetNameStringByIndex(static_cast(Type)); - LogListingName = *FString::Printf(TEXT("FlowLog_%s_%s_%s_"), *TypeAsString, *InFlowAsset->AssetGuid.ToString(), *InFlowAsset->GetName()); + LogListingName = *FString::Printf(TEXT("%s::%s::%s"), *GetLogLabel(Type), *InFlowAsset->GetName(), *InFlowAsset->AssetGuid.ToString()); } else { diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index d72ae9260..1fc3797c8 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -2,7 +2,7 @@ #include "Graph/Nodes/FlowGraphNode.h" -#include "Asset/FlowDebugger.h" +#include "Asset/FlowDebuggerSubsystem.h" #include "FlowEditorCommands.h" #include "Graph/FlowGraph.h" #include "Graph/FlowGraphEditorSettings.h" @@ -1029,7 +1029,7 @@ void UFlowGraphNode::TryPausingSession(bool bPauseSession) FEditorDelegates::ResumePIE.AddUObject(this, &UFlowGraphNode::OnResumePIE); FEditorDelegates::EndPIE.AddUObject(this, &UFlowGraphNode::OnEndPIE); - FFlowDebugger::PausePlaySession(); + UFlowDebuggerSubsystem::PausePlaySession(); } } diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index b1d053829..fb8814331 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -32,7 +32,6 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit UFlowAsset* FlowAsset; TSharedPtr AssetToolbar; - TSharedPtr FlowDebugger; TSharedPtr GraphEditor; TSharedPtr DetailsView; @@ -166,8 +165,6 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit static bool IsPIE(); static EVisibility GetDebuggerVisibility(); - void OnBeginPIE(const bool bInSimulateInEditor) const; - TSet GetSelectedFlowNodes() const; int32 GetNumberOfSelectedNodes() const; bool GetBoundsForSelectedNodes(class FSlateRect& Rect, float Padding) const; @@ -185,7 +182,6 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit #endif protected: - void OnRuntimeMessageAdded(const UFlowAsset* AssetInstance, const TSharedRef& Message) const; void OnLogTokenClicked(const TSharedRef& Token) const; virtual void SelectAllNodes() const; diff --git a/Source/FlowEditor/Public/Asset/FlowDebugger.h b/Source/FlowEditor/Public/Asset/FlowDebugger.h deleted file mode 100644 index 75a5ef47b..000000000 --- a/Source/FlowEditor/Public/Asset/FlowDebugger.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#pragma once - -#include "CoreMinimal.h" - -/** -** Minimalistic form of breakpoint debugger -** See BehaviorTreeDebugger for a more complex example - */ -class FLOWEDITOR_API FFlowDebugger -{ -public: - FFlowDebugger(); - ~FFlowDebugger(); - - static void PausePlaySession(); - static bool IsPlaySessionPaused(); -}; diff --git a/Source/FlowEditor/Public/Asset/FlowDebuggerSubsystem.h b/Source/FlowEditor/Public/Asset/FlowDebuggerSubsystem.h new file mode 100644 index 000000000..da10e3032 --- /dev/null +++ b/Source/FlowEditor/Public/Asset/FlowDebuggerSubsystem.h @@ -0,0 +1,36 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "EditorSubsystem.h" +#include "FlowDebuggerSubsystem.generated.h" + +class UFlowAsset; +class FFlowMessageLog; + +/** +** Persistent subsystem supporting Flow Graph debugging + */ +UCLASS() +class FLOWEDITOR_API UFlowDebuggerSubsystem : public UEditorSubsystem +{ + GENERATED_BODY() + +public: + UFlowDebuggerSubsystem(); + +protected: + TMap, TSharedPtr> RuntimeLogs; + + void OnInstancedTemplateAdded(UFlowAsset* FlowAsset); + void OnInstancedTemplateRemoved(UFlowAsset* FlowAsset); + + void OnRuntimeMessageAdded(UFlowAsset* FlowAsset, const TSharedRef& Message) const; + + void OnBeginPIE(const bool bIsSimulating); + void OnEndPIE(const bool bIsSimulating); + +public: + static void PausePlaySession(); + static bool IsPlaySessionPaused(); +}; diff --git a/Source/FlowEditor/Public/Asset/FlowMessageLogListing.h b/Source/FlowEditor/Public/Asset/FlowMessageLogListing.h index 5651f25c3..b7e7aafaf 100644 --- a/Source/FlowEditor/Public/Asset/FlowMessageLogListing.h +++ b/Source/FlowEditor/Public/Asset/FlowMessageLogListing.h @@ -33,4 +33,5 @@ class FLOWEDITOR_API FFlowMessageLogListing public: static TSharedRef GetLogListing(const UFlowAsset* InFlowAsset, const EFlowLogType Type); + static FString GetLogLabel(const EFlowLogType Type); }; From 17b2e30cd7c073e18251b294b58c8457273f7a19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 19 Mar 2023 17:41:26 +0100 Subject: [PATCH 136/485] non-editor compilation fix --- Source/Flow/Private/FlowSubsystem.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index 4cd30e5b3..205f87f0f 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -14,8 +14,10 @@ #include "Misc/Paths.h" #include "UObject/UObjectHash.h" +#if WITH_EDITOR FNativeFlowAssetEvent UFlowSubsystem::OnInstancedTemplateAdded; FNativeFlowAssetEvent UFlowSubsystem::OnInstancedTemplateRemoved; +#endif UFlowSubsystem::UFlowSubsystem() : UGameInstanceSubsystem() From 0c54f52edf468f2576ac21a486d8c6c2a80114fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Wed, 22 Mar 2023 19:52:34 +0100 Subject: [PATCH 137/485] fixed compilation in some projects --- Source/FlowEditor/FlowEditor.Build.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/FlowEditor/FlowEditor.Build.cs b/Source/FlowEditor/FlowEditor.Build.cs index cf1e47c04..00d4faf7d 100644 --- a/Source/FlowEditor/FlowEditor.Build.cs +++ b/Source/FlowEditor/FlowEditor.Build.cs @@ -10,6 +10,7 @@ public FlowEditor(ReadOnlyTargetRules target) : base(target) PublicDependencyModuleNames.AddRange(new[] { + "EditorSubsystem", "Flow", "MessageLog" }); @@ -29,7 +30,6 @@ public FlowEditor(ReadOnlyTargetRules target) : base(target) "EditorFramework", "EditorScriptingUtilities", "EditorStyle", - "EditorSubsystem", "Engine", "GraphEditor", "InputCore", From 0f52d4a06270de85e8e0d7395d90509c21a0ce07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Wed, 22 Mar 2023 22:07:38 +0100 Subject: [PATCH 138/485] --- UE 5.2 --- --- Flow.uplugin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.uplugin b/Flow.uplugin index 4879ba3f1..63fdda0b8 100644 --- a/Flow.uplugin +++ b/Flow.uplugin @@ -8,7 +8,7 @@ "DocsURL" : "https://github.com/MothCocoon/FlowGraph/wiki", "MarketplaceURL" : "", "SupportURL": "https://discord.gg/zMtMQ2vUUa", - "EngineAssociation": "5.1", + "EngineAssociation": "5.2", "EnabledByDefault" : true, "CanContainContent" : false, "IsBetaVersion" : false, From 83ce9b84a9c70eca215412e1fbb7976b9457873e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Wed, 22 Mar 2023 22:41:34 +0100 Subject: [PATCH 139/485] UE 5.2 compilation fixes --- Source/FlowEditor/Private/Asset/FlowDiffControl.cpp | 4 ++-- Source/FlowEditor/Private/MovieScene/FlowSection.cpp | 6 +++--- Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp | 2 +- Source/FlowEditor/Public/Asset/FlowDiffControl.h | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp b/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp index 752de02a4..b53f4cf61 100644 --- a/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp +++ b/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp @@ -14,14 +14,14 @@ /// FFlowAssetDiffControl FFlowAssetDiffControl::FFlowAssetDiffControl(const UFlowAsset* InOldFlowAsset, const UFlowAsset* InNewFlowAsset, FOnDiffEntryFocused InSelectionCallback) - : TDetailsDiffControl(InOldFlowAsset, InNewFlowAsset, InSelectionCallback) + : FDetailsDiffControl(InOldFlowAsset, InNewFlowAsset, InSelectionCallback, false) { } // TDetailsDiffControl::GenerateTreeEntries + "NoDifferences" entry + category label void FFlowAssetDiffControl::GenerateTreeEntries(TArray>& OutTreeEntries, TArray>& OutRealDifferences) { - TDetailsDiffControl::GenerateTreeEntries(OutTreeEntries, OutRealDifferences); + FDetailsDiffControl::GenerateTreeEntries(OutTreeEntries, OutRealDifferences); const bool bHasDifferences = Children.Num() != 0; if (!bHasDifferences) diff --git a/Source/FlowEditor/Private/MovieScene/FlowSection.cpp b/Source/FlowEditor/Private/MovieScene/FlowSection.cpp index 72d77a793..eb563e508 100644 --- a/Source/FlowEditor/Private/MovieScene/FlowSection.cpp +++ b/Source/FlowEditor/Private/MovieScene/FlowSection.cpp @@ -64,7 +64,7 @@ void FFlowSectionBase::PaintEventName(FSequencerSectionPainter& Painter, int32 L FSlateDrawElement::MakeBox( Painter.DrawElements, LayerId + 1, - Painter.SectionGeometry.ToPaintGeometry(BoxOffset, BoxSize), + Painter.SectionGeometry.ToPaintGeometry(BoxSize, FSlateLayoutTransform(1.0f, TransformPoint(1.0f, UE::Slate::CastToVector2f(BoxOffset)))), FAppStyle::GetBrush("WhiteBrush"), ESlateDrawEffect::None, FLinearColor::Black.CopyWithNewOpacity(0.5f) @@ -76,7 +76,7 @@ void FFlowSectionBase::PaintEventName(FSequencerSectionPainter& Painter, int32 L FSlateDrawElement::MakeText( Painter.DrawElements, LayerId + 2, - Painter.SectionGeometry.ToPaintGeometry(BoxOffset + IconOffset, IconSize), + Painter.SectionGeometry.ToPaintGeometry(IconSize, FSlateLayoutTransform(1.0f, TransformPoint(1.0f, UE::Slate::CastToVector2f(BoxOffset + IconOffset)))), WarningString, FontAwesomeFont, Painter.bParentEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect, @@ -87,7 +87,7 @@ void FFlowSectionBase::PaintEventName(FSequencerSectionPainter& Painter, int32 L FSlateDrawElement::MakeText( Painter.DrawElements, LayerId + 2, - Painter.SectionGeometry.ToPaintGeometry(BoxOffset + TextOffset, TextSize), + Painter.SectionGeometry.ToPaintGeometry(TextSize, FSlateLayoutTransform(1.0f, TransformPoint(1.0f, UE::Slate::CastToVector2f(BoxOffset + TextOffset)))), InEventString, SmallLayoutFont, Painter.bParentEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect, diff --git a/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp b/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp index 28a687ffd..eed63ac07 100644 --- a/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp +++ b/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp @@ -163,7 +163,7 @@ void FFlowTrackEditor::HandleAddFlowTrackMenuEntryExecute(UClass* SectionType) c TArray NewTracks; - UMovieSceneFlowTrack* NewMasterTrack = FocusedMovieScene->AddMasterTrack(); + UMovieSceneFlowTrack* NewMasterTrack = FocusedMovieScene->AddTrack(); NewTracks.Add(NewMasterTrack); if (GetSequencer().IsValid()) { diff --git a/Source/FlowEditor/Public/Asset/FlowDiffControl.h b/Source/FlowEditor/Public/Asset/FlowDiffControl.h index 410aaa083..90a4dd203 100644 --- a/Source/FlowEditor/Public/Asset/FlowDiffControl.h +++ b/Source/FlowEditor/Public/Asset/FlowDiffControl.h @@ -15,7 +15,7 @@ class SFlowDiff; ///////////////////////////////////////////////////////////////////////////// /// FFlowAssetDiffControl -class FLOWEDITOR_API FFlowAssetDiffControl : public TDetailsDiffControl +class FLOWEDITOR_API FFlowAssetDiffControl : public FDetailsDiffControl { public: FFlowAssetDiffControl(const UFlowAsset* InOldFlowAsset, const UFlowAsset* InNewFlowAsset, FOnDiffEntryFocused InSelectionCallback); From 8d243520b56b75a78b78eb7abd5756640268a704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 26 Mar 2023 12:32:47 +0200 Subject: [PATCH 140/485] formatting fix --- Source/Flow/Private/Nodes/FlowNode.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 463ac3149..a588ccbdd 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -578,16 +578,16 @@ void UFlowNode::LoadInstance(const FFlowNodeSaveData& NodeRecord) { case EFlowSignalMode::Enabled: OnLoad(); - break; + break; case EFlowSignalMode::Disabled: // designer doesn't want to execute this node's logic at all, so we kill it LogNote(TEXT("Signal disabled while loading Flow Node from SaveGame")); - Finish(); - break; + Finish(); + break; case EFlowSignalMode::PassThrough: LogNote(TEXT("Signal pass-through on loading Flow Node from SaveGame")); - OnPassThrough(); - break; + OnPassThrough(); + break; default: ; } } From 3e71bcf704f3866838c90db7c378a40b3750f649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 26 Mar 2023 12:37:47 +0200 Subject: [PATCH 141/485] Fixed rare issue with loading saves - prevent triggering input on not-yet-loaded node --- Source/Flow/Private/FlowAsset.cpp | 21 ++++++++++------- Source/Flow/Public/FlowAsset.h | 39 ++++++++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 13574003e..e10a30108 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -511,12 +511,15 @@ FFlowAssetSaveData UFlowAsset::SaveInstance(TArray& SavedFlo // opportunity to collect data before serializing asset OnSave(); - // iterate SubGraphs - for (const TPair& Node : Nodes) + // iterate nodes + TArray NodesInExecutionOrder; + GetNodesInExecutionOrder(NodesInExecutionOrder); + for (UFlowNode* Node : NodesInExecutionOrder) { - if (Node.Value && Node.Value->ActivationState == EFlowNodeState::Active) + if (Node && Node->ActivationState == EFlowNodeState::Active) { - if (UFlowNode_SubGraph* SubGraphNode = Cast(Node.Value)) + // iterate SubGraphs + if (UFlowNode_SubGraph* SubGraphNode = Cast(Node)) { const TWeakObjectPtr SubFlowInstance = GetFlowInstance(SubGraphNode); if (SubFlowInstance.IsValid()) @@ -527,7 +530,7 @@ FFlowAssetSaveData UFlowAsset::SaveInstance(TArray& SavedFlo } FFlowNodeSaveData NodeRecord; - Node.Value->SaveInstance(NodeRecord); + Node->SaveInstance(NodeRecord); AssetRecord.NodeRecords.Emplace(NodeRecord); } @@ -552,11 +555,13 @@ void UFlowAsset::LoadInstance(const FFlowAssetSaveData& AssetRecord) PreStartFlow(); - for (const FFlowNodeSaveData& NodeRecord : AssetRecord.NodeRecords) + // iterate nodes from "the end" of graph, backwards to execution order + // prevents issue when preceding node would instantly fire output to not-yet-loaded node + for (int32 i = AssetRecord.NodeRecords.Num() - 1; i >= 0; i--) { - if (UFlowNode* Node = Nodes.FindRef(NodeRecord.NodeGuid)) + if (UFlowNode* Node = Nodes.FindRef(AssetRecord.NodeRecords[i].NodeGuid)) { - Node->LoadInstance(NodeRecord); + Node->LoadInstance(AssetRecord.NodeRecords[i]); } } diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 7e4f87bd0..8c4af61a9 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -5,9 +5,9 @@ #include "FlowMessageLog.h" #include "FlowSave.h" #include "FlowTypes.h" +#include "Nodes/FlowNode.h" #include "FlowAsset.generated.h" -class UFlowNode; class UFlowNode_CustomInput; class UFlowNode_Start; class UFlowNode_SubGraph; @@ -147,11 +147,44 @@ class FLOW_API UFlowAsset : public UObject return nullptr; } + UFlowNode_Start* GetStartNode() const; + + template + void GetNodesInExecutionOrder(TArray& OutNodes) + { + static_assert(TPointerIsConvertibleFromTo::Value, "'T' template parameter to GetNodesInExecutionOrder must be derived from UFlowNode"); + + if (UFlowNode_Start* FoundStartNode = GetStartNode()) + { + TSet> IteratedNodes; + GetNodesInExecutionOrder_Recursive(FoundStartNode, IteratedNodes, OutNodes); + } + } + +protected: + template + void GetNodesInExecutionOrder_Recursive(UFlowNode* Node, TSet>& IteratedNodes, TArray& OutNodes) + { + IteratedNodes.Add(Node); + + if (T* NodeOfRequiredType = Cast(Node)) + { + OutNodes.Emplace(NodeOfRequiredType); + } + + for (UFlowNode* ConnectedNode : Node->GetConnectedNodes()) + { + if (ConnectedNode && !IteratedNodes.Contains(ConnectedNode)) + { + GetNodesInExecutionOrder_Recursive(ConnectedNode, IteratedNodes, OutNodes); + } + } + } + +public: TArray GetCustomInputs() const { return CustomInputs; } TArray GetCustomOutputs() const { return CustomOutputs; } - UFlowNode_Start* GetStartNode() const; - ////////////////////////////////////////////////////////////////////////// // Instances of the template asset From 95531a4fb900cc1dd97a634367198e1f4e5f6574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 26 Mar 2023 12:42:10 +0200 Subject: [PATCH 142/485] wording corrected --- Source/Flow/Private/FlowAsset.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index e10a30108..2dea57c62 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -555,8 +555,8 @@ void UFlowAsset::LoadInstance(const FFlowAssetSaveData& AssetRecord) PreStartFlow(); - // iterate nodes from "the end" of graph, backwards to execution order - // prevents issue when preceding node would instantly fire output to not-yet-loaded node + // iterate graph "from the end", backward to execution order + // prevents issue when the preceding node would instantly fire output to a not-yet-loaded node for (int32 i = AssetRecord.NodeRecords.Num() - 1; i >= 0; i--) { if (UFlowNode* Node = Nodes.FindRef(AssetRecord.NodeRecords[i].NodeGuid)) From 8048218be4c03b07697fe8d7936f643ea8eb71d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 26 Mar 2023 17:23:04 +0200 Subject: [PATCH 143/485] extracted graph-related code from the FFlowAssetEditor to new SFlowGraphEditor class added FFlowAssetEditor::IsTabFocused() to prevent delete/paste/copy nodes if graph tab isn't focused --- .../Private/Asset/FlowAssetEditor.cpp | 1052 +---------------- .../Private/Asset/FlowAssetToolbar.cpp | 9 +- .../FlowGraphConnectionDrawingPolicy.cpp | 8 +- .../Private/Graph/FlowGraphEditor.cpp | 1029 ++++++++++++++++ .../Private/Graph/FlowGraphSchema.cpp | 7 +- .../Private/Graph/FlowGraphSchema_Actions.cpp | 16 +- .../Private/Graph/FlowGraphUtils.cpp | 19 +- .../Private/Graph/Widgets/SFlowPalette.cpp | 17 +- .../FlowEditor/Public/Asset/FlowAssetEditor.h | 141 +-- .../Public/Asset/FlowAssetToolbar.h | 4 +- .../FlowEditor/Public/Graph/FlowGraphEditor.h | 144 +++ .../FlowEditor/Public/Graph/FlowGraphUtils.h | 4 +- .../Public/Graph/Widgets/SFlowPalette.h | 2 +- 13 files changed, 1267 insertions(+), 1185 deletions(-) create mode 100644 Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp create mode 100644 Source/FlowEditor/Public/Graph/FlowGraphEditor.h diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index ebf6c08fb..4c0b65c49 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -8,23 +8,17 @@ #include "Asset/FlowAssetEditorContext.h" #include "Asset/FlowAssetToolbar.h" -#include "Asset/FlowDebuggerSubsystem.h" #include "Asset/FlowMessageLogListing.h" -#include "Graph/FlowGraph.h" -#include "Graph/FlowGraphEditorSettings.h" +#include "Graph/FlowGraphEditor.h" #include "Graph/FlowGraphSchema.h" -#include "Graph/FlowGraphSchema_Actions.h" -#include "Graph/Nodes/FlowGraphNode.h" #include "Graph/Widgets/SFlowPalette.h" #include "FlowAsset.h" #include "Nodes/FlowNode.h" -#include "Nodes/Route/FlowNode_SubGraph.h" #include "EdGraphUtilities.h" #include "EdGraph/EdGraphNode.h" #include "Editor.h" -#include "Framework/Commands/GenericCommands.h" #include "GraphEditor.h" #include "GraphEditorActions.h" #include "HAL/PlatformApplicationMisc.h" @@ -36,7 +30,6 @@ #include "Misc/UObjectToken.h" #include "Modules/ModuleManager.h" #include "PropertyEditorModule.h" -#include "ScopedTransaction.h" #include "SNodePanel.h" #include "ToolMenus.h" #include "Widgets/Docking/SDockTab.h" @@ -176,6 +169,16 @@ void FFlowAssetEditor::InitToolMenuContext(FToolMenuContext& MenuContext) MenuContext.AddObject(Context); } +bool FFlowAssetEditor::IsTabFocused(const FTabId& TabId) const +{ + if (const TSharedPtr CurrentGraphTab = GetToolkitHost()->GetTabManager()->FindExistingLiveTab(TabId)) + { + return CurrentGraphTab->IsActive(); + } + + return false; +} + TSharedRef FFlowAssetEditor::SpawnTab_Details(const FSpawnTabArgs& Args) const { check(Args.GetTabId() == DetailsTab); @@ -265,7 +268,6 @@ void FFlowAssetEditor::InitFlowAssetEditor(const EToolkitMode::Type Mode, const BindToolbarCommands(); CreateToolbar(); - BindGraphCommands(); CreateWidgets(); const TSharedRef StandaloneDefaultLayout = FTabManager::NewLayout("FlowAssetEditor_Layout_v5.1") @@ -422,8 +424,7 @@ bool FFlowAssetEditor::CanGoToParentInstance() void FFlowAssetEditor::CreateWidgets() { - GraphEditor = CreateGraphWidget(); - + // Details View { FDetailsViewArgs Args; Args.bHideSelectionTip = true; @@ -437,12 +438,19 @@ void FFlowAssetEditor::CreateWidgets() DetailsView->SetObject(FlowAsset); } + // Graph + CreateGraphWidget(); + GraphEditor->OnSelectionChangedEvent.BindRaw(this, &FFlowAssetEditor::OnSelectedNodesChanged); + + // Palette Palette = SNew(SFlowPalette, SharedThis(this)); + // Search #if ENABLE_SEARCH_IN_ASSET_EDITOR SearchBrowser = SNew(SSearchBrowser, GetFlowAsset()); #endif + // Logs FMessageLogModule& MessageLogModule = FModuleManager::LoadModuleChecked("MessageLog"); { RuntimeLogListing = FFlowMessageLogListing::GetLogListing(FlowAsset, EFlowLogType::Runtime); @@ -456,247 +464,15 @@ void FFlowAssetEditor::CreateWidgets() } } -TSharedRef FFlowAssetEditor::CreateGraphWidget() -{ - SGraphEditor::FGraphEditorEvents InEvents; - InEvents.OnSelectionChanged = SGraphEditor::FOnSelectionChanged::CreateSP(this, &FFlowAssetEditor::OnSelectedNodesChanged); - InEvents.OnNodeDoubleClicked = FSingleNodeEvent::CreateSP(this, &FFlowAssetEditor::OnNodeDoubleClicked); - InEvents.OnTextCommitted = FOnNodeTextCommitted::CreateSP(this, &FFlowAssetEditor::OnNodeTitleCommitted); - InEvents.OnSpawnNodeByShortcut = SGraphEditor::FOnSpawnNodeByShortcut::CreateStatic(&FFlowAssetEditor::OnSpawnGraphNodeByShortcut, static_cast(FlowAsset->GetGraph())); - - return SNew(SGraphEditor) - .AdditionalCommands(ToolkitCommands) - .IsEditable(true) - .Appearance(GetGraphAppearanceInfo()) - .GraphToEdit(FlowAsset->GetGraph()) - .GraphEvents(InEvents) - .AutoExpandActionMenu(true) - .ShowGraphStateOverlay(false); -} - -FGraphAppearanceInfo FFlowAssetEditor::GetGraphAppearanceInfo() const -{ - FGraphAppearanceInfo AppearanceInfo; - AppearanceInfo.CornerText = GetCornerText(); - - if (UFlowDebuggerSubsystem::IsPlaySessionPaused()) - { - AppearanceInfo.PIENotifyText = LOCTEXT("PausedLabel", "PAUSED"); - } - - return AppearanceInfo; -} - -FText FFlowAssetEditor::GetCornerText() const -{ - return LOCTEXT("AppearanceCornerText_FlowAsset", "FLOW"); -} - -void FFlowAssetEditor::BindGraphCommands() -{ - FGraphEditorCommands::Register(); - FFlowGraphCommands::Register(); - FFlowSpawnNodeCommands::Register(); - - const FGenericCommands& GenericCommands = FGenericCommands::Get(); - const FGraphEditorCommandsImpl& GraphCommands = FGraphEditorCommands::Get(); - const FFlowGraphCommands& FlowGraphCommands = FFlowGraphCommands::Get(); - - // Graph commands - ToolkitCommands->MapAction(GraphCommands.CreateComment, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnCreateComment), - FCanExecuteAction::CreateStatic(&FFlowAssetEditor::CanEdit)); - - ToolkitCommands->MapAction(GraphCommands.StraightenConnections, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnStraightenConnections)); - - // Generic Node commands - ToolkitCommands->MapAction(GenericCommands.Undo, - FExecuteAction::CreateStatic(&FFlowAssetEditor::UndoGraphAction), - FCanExecuteAction::CreateStatic(&FFlowAssetEditor::CanEdit)); - - ToolkitCommands->MapAction(GenericCommands.Redo, - FExecuteAction::CreateStatic(&FFlowAssetEditor::RedoGraphAction), - FCanExecuteAction::CreateStatic(&FFlowAssetEditor::CanEdit)); - - ToolkitCommands->MapAction(GenericCommands.SelectAll, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::SelectAllNodes), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanSelectAllNodes)); - - ToolkitCommands->MapAction(GenericCommands.Delete, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::DeleteSelectedNodes), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanDeleteNodes)); - - ToolkitCommands->MapAction(GenericCommands.Copy, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::CopySelectedNodes), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanCopyNodes)); - - ToolkitCommands->MapAction(GenericCommands.Cut, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::CutSelectedNodes), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanCutNodes)); - - ToolkitCommands->MapAction(GenericCommands.Paste, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::PasteNodes), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanPasteNodes)); - - ToolkitCommands->MapAction(GenericCommands.Duplicate, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::DuplicateNodes), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanDuplicateNodes)); - - // Pin commands - ToolkitCommands->MapAction(FlowGraphCommands.RefreshContextPins, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::RefreshContextPins), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanRefreshContextPins)); - - ToolkitCommands->MapAction(FlowGraphCommands.AddInput, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::AddInput), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanAddInput)); - - ToolkitCommands->MapAction(FlowGraphCommands.AddOutput, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::AddOutput), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanAddOutput)); - - ToolkitCommands->MapAction(FlowGraphCommands.RemovePin, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::RemovePin), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanRemovePin)); - - // Breakpoint commands - ToolkitCommands->MapAction(GraphCommands.AddBreakpoint, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnAddBreakpoint), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanAddBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanAddBreakpoint) - ); - - ToolkitCommands->MapAction(GraphCommands.RemoveBreakpoint, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnRemoveBreakpoint), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanRemoveBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanRemoveBreakpoint) - ); - - ToolkitCommands->MapAction(GraphCommands.EnableBreakpoint, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnEnableBreakpoint), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanEnableBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanEnableBreakpoint) - ); - - ToolkitCommands->MapAction(GraphCommands.DisableBreakpoint, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnDisableBreakpoint), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanDisableBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanDisableBreakpoint) - ); - - ToolkitCommands->MapAction(GraphCommands.ToggleBreakpoint, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnToggleBreakpoint), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanToggleBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanToggleBreakpoint) - ); - - // Pin Breakpoint commands - ToolkitCommands->MapAction(FlowGraphCommands.AddPinBreakpoint, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnAddPinBreakpoint), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanAddPinBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanAddPinBreakpoint) - ); - - ToolkitCommands->MapAction(FlowGraphCommands.RemovePinBreakpoint, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnRemovePinBreakpoint), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanRemovePinBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanRemovePinBreakpoint) - ); - - ToolkitCommands->MapAction(FlowGraphCommands.EnablePinBreakpoint, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnEnablePinBreakpoint), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanEnablePinBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanEnablePinBreakpoint) - ); - - ToolkitCommands->MapAction(FlowGraphCommands.DisablePinBreakpoint, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnDisablePinBreakpoint), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanDisablePinBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanDisablePinBreakpoint) - ); - - ToolkitCommands->MapAction(FlowGraphCommands.TogglePinBreakpoint, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnTogglePinBreakpoint), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanTogglePinBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanTogglePinBreakpoint) - ); - - // Execution Override commands - ToolkitCommands->MapAction(FlowGraphCommands.EnableNode, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::SetSignalMode, EFlowSignalMode::Enabled), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanSetSignalMode, EFlowSignalMode::Enabled), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanSetSignalMode, EFlowSignalMode::Enabled) - ); - - ToolkitCommands->MapAction(FlowGraphCommands.DisableNode, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::SetSignalMode, EFlowSignalMode::Disabled), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanSetSignalMode, EFlowSignalMode::Disabled), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanSetSignalMode, EFlowSignalMode::Disabled) - ); - - ToolkitCommands->MapAction(FlowGraphCommands.SetPassThrough, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::SetSignalMode, EFlowSignalMode::PassThrough), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanSetSignalMode, EFlowSignalMode::PassThrough), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanSetSignalMode, EFlowSignalMode::PassThrough) - ); - - ToolkitCommands->MapAction(FlowGraphCommands.ForcePinActivation, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::OnForcePinActivation), - FCanExecuteAction::CreateStatic(&FFlowAssetEditor::IsPIE), - FIsActionChecked(), - FIsActionButtonVisible::CreateStatic(&FFlowAssetEditor::IsPIE) - ); - - // Jump commands - ToolkitCommands->MapAction(FlowGraphCommands.FocusViewport, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::FocusViewport), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanFocusViewport)); - - ToolkitCommands->MapAction(FlowGraphCommands.JumpToNodeDefinition, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::JumpToNodeDefinition), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanJumpToNodeDefinition)); -} - -void FFlowAssetEditor::UndoGraphAction() -{ - GEditor->UndoTransaction(); -} - -void FFlowAssetEditor::RedoGraphAction() +void FFlowAssetEditor::CreateGraphWidget() { - GEditor->RedoTransaction(); + SAssignNew(GraphEditor, SFlowGraphEditor, SharedThis(this)) + .DetailsView(DetailsView); } -FReply FFlowAssetEditor::OnSpawnGraphNodeByShortcut(FInputChord InChord, const FVector2D& InPosition, UEdGraph* InGraph) +bool FFlowAssetEditor::CanEdit() { - UEdGraph* Graph = InGraph; - - if (FFlowSpawnNodeCommands::IsRegistered()) - { - const TSharedPtr Action = FFlowSpawnNodeCommands::Get().GetActionByChord(InChord); - if (Action.IsValid()) - { - TArray DummyPins; - Action->PerformAction(Graph, DummyPins, InPosition); - return FReply::Handled(); - } - } - - return FReply::Unhandled(); + return GEditor->PlayWorld == nullptr; } void FFlowAssetEditor::SetUISelectionState(const FName SelectionOwner) @@ -723,94 +499,9 @@ void FFlowAssetEditor::ClearSelectionStateFor(const FName SelectionOwner) } } -void FFlowAssetEditor::OnCreateComment() const -{ - FFlowGraphSchemaAction_NewComment CommentAction; - CommentAction.PerformAction(FlowAsset->GetGraph(), nullptr, GraphEditor->GetPasteLocation()); -} - -void FFlowAssetEditor::OnStraightenConnections() const -{ - GraphEditor->OnStraightenConnections(); -} - -bool FFlowAssetEditor::CanEdit() -{ - return GEditor->PlayWorld == nullptr; -} - -bool FFlowAssetEditor::IsPIE() -{ - return GEditor->PlayWorld != nullptr; -} - -EVisibility FFlowAssetEditor::GetDebuggerVisibility() -{ - return GEditor->PlayWorld ? EVisibility::Visible : EVisibility::Collapsed; -} - -TSet FFlowAssetEditor::GetSelectedFlowNodes() const -{ - TSet Result; - - const FGraphPanelSelectionSet SelectedNodes = GraphEditor->GetSelectedNodes(); - for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt) - { - if (UFlowGraphNode* SelectedNode = Cast(*NodeIt)) - { - Result.Emplace(SelectedNode); - } - } - - return Result; -} - -int32 FFlowAssetEditor::GetNumberOfSelectedNodes() const -{ - return GraphEditor->GetSelectedNodes().Num(); -} - -bool FFlowAssetEditor::GetBoundsForSelectedNodes(class FSlateRect& Rect, float Padding) const -{ - return GraphEditor->GetBoundsForSelectedNodes(Rect, Padding); -} - -void FFlowAssetEditor::OnSelectedNodesChanged(const TSet& Nodes) -{ - TArray SelectedObjects; - - if (Nodes.Num() > 0) - { - SetUISelectionState(GraphTab); - - for (TSet::TConstIterator SetIt(Nodes); SetIt; ++SetIt) - { - if (const UFlowGraphNode* GraphNode = Cast(*SetIt)) - { - SelectedObjects.Add(Cast(GraphNode->GetFlowNode())); - } - else - { - SelectedObjects.Add(*SetIt); - } - } - } - else - { - SetUISelectionState(NAME_None); - SelectedObjects.Add(GetFlowAsset()); - } - - if (DetailsView.IsValid()) - { - DetailsView->SetObjects(SelectedObjects); - } -} - -void FFlowAssetEditor::SelectSingleNode(UEdGraphNode* Node) const +FName FFlowAssetEditor::GetUISelectionState() const { - GraphEditor->ClearSelectionSet(); - GraphEditor->SetNodeSelection(Node, true); + return CurrentUISelection; } #if ENABLE_JUMP_TO_INNER_OBJECT @@ -858,695 +549,4 @@ void FFlowAssetEditor::OnLogTokenClicked(const TSharedRef& Token) } } -void FFlowAssetEditor::SelectAllNodes() const -{ - GraphEditor->SelectAllNodes(); -} - -bool FFlowAssetEditor::CanSelectAllNodes() const -{ - return true; -} - -void FFlowAssetEditor::DeleteSelectedNodes() -{ - const FScopedTransaction Transaction(LOCTEXT("DeleteSelectedNode", "Delete Selected Node")); - GraphEditor->GetCurrentGraph()->Modify(); - FlowAsset->Modify(); - - const FGraphPanelSelectionSet SelectedNodes = GraphEditor->GetSelectedNodes(); - SetUISelectionState(NAME_None); - - for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt) - { - UEdGraphNode* Node = CastChecked(*NodeIt); - if (Node->CanUserDeleteNode()) - { - if (const UFlowGraphNode* FlowGraphNode = Cast(Node)) - { - if (FlowGraphNode->GetFlowNode()) - { - const FGuid NodeGuid = FlowGraphNode->GetFlowNode()->GetGuid(); - - GraphEditor->GetCurrentGraph()->GetSchema()->BreakNodeLinks(*Node); - Node->DestroyNode(); - - FlowAsset->UnregisterNode(NodeGuid); - continue; - } - } - - GraphEditor->GetCurrentGraph()->GetSchema()->BreakNodeLinks(*Node); - Node->DestroyNode(); - } - } -} - -void FFlowAssetEditor::DeleteSelectedDuplicableNodes() -{ - // Cache off the old selection - const FGraphPanelSelectionSet OldSelectedNodes = GraphEditor->GetSelectedNodes(); - - // Clear the selection and only select the nodes that can be duplicated - FGraphPanelSelectionSet RemainingNodes; - GraphEditor->ClearSelectionSet(); - - for (FGraphPanelSelectionSet::TConstIterator SelectedIt(OldSelectedNodes); SelectedIt; ++SelectedIt) - { - if (UEdGraphNode* Node = Cast(*SelectedIt)) - { - if (Node->CanDuplicateNode()) - { - GraphEditor->SetNodeSelection(Node, true); - } - else - { - RemainingNodes.Add(Node); - } - } - } - - // Delete the duplicable nodes - DeleteSelectedNodes(); - - for (FGraphPanelSelectionSet::TConstIterator SelectedIt(RemainingNodes); SelectedIt; ++SelectedIt) - { - if (UEdGraphNode* Node = Cast(*SelectedIt)) - { - GraphEditor->SetNodeSelection(Node, true); - } - } -} - -bool FFlowAssetEditor::CanDeleteNodes() const -{ - if (CanEdit()) - { - const FGraphPanelSelectionSet SelectedNodes = GraphEditor->GetSelectedNodes(); - for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt) - { - if (const UEdGraphNode* Node = Cast(*NodeIt)) - { - if (!Node->CanUserDeleteNode()) - { - return false; - } - } - } - - return SelectedNodes.Num() > 0; - } - - return false; -} - -void FFlowAssetEditor::CutSelectedNodes() -{ - CopySelectedNodes(); - - // Cut should only delete nodes that can be duplicated - DeleteSelectedDuplicableNodes(); -} - -bool FFlowAssetEditor::CanCutNodes() const -{ - return CanCopyNodes() && CanDeleteNodes(); -} - -void FFlowAssetEditor::CopySelectedNodes() const -{ - const FGraphPanelSelectionSet SelectedNodes = GraphEditor->GetSelectedNodes(); - for (FGraphPanelSelectionSet::TConstIterator SelectedIt(SelectedNodes); SelectedIt; ++SelectedIt) - { - if (UFlowGraphNode* Node = Cast(*SelectedIt)) - { - Node->PrepareForCopying(); - } - } - - // Export the selected nodes and place the text on the clipboard - FString ExportedText; - FEdGraphUtilities::ExportNodesToText(SelectedNodes, /*out*/ ExportedText); - FPlatformApplicationMisc::ClipboardCopy(*ExportedText); - - for (FGraphPanelSelectionSet::TConstIterator SelectedIt(SelectedNodes); SelectedIt; ++SelectedIt) - { - if (UFlowGraphNode* Node = Cast(*SelectedIt)) - { - Node->PostCopyNode(); - } - } -} - -bool FFlowAssetEditor::CanCopyNodes() const -{ - if (CanEdit()) - { - const FGraphPanelSelectionSet SelectedNodes = GraphEditor->GetSelectedNodes(); - for (FGraphPanelSelectionSet::TConstIterator SelectedIt(SelectedNodes); SelectedIt; ++SelectedIt) - { - const UEdGraphNode* Node = Cast(*SelectedIt); - if (Node && Node->CanDuplicateNode()) - { - return true; - } - } - } - - return false; -} - -void FFlowAssetEditor::PasteNodes() -{ - PasteNodesHere(GraphEditor->GetPasteLocation()); -} - -void FFlowAssetEditor::PasteNodesHere(const FVector2D& Location) -{ - SetUISelectionState(NAME_None); - - // Undo/Redo support - const FScopedTransaction Transaction(LOCTEXT("PasteNode", "Paste Node")); - FlowAsset->GetGraph()->Modify(); - FlowAsset->Modify(); - - // Clear the selection set (newly pasted stuff will be selected) - GraphEditor->ClearSelectionSet(); - - // Grab the text to paste from the clipboard. - FString TextToImport; - FPlatformApplicationMisc::ClipboardPaste(TextToImport); - - // Import the nodes - TSet PastedNodes; - FEdGraphUtilities::ImportNodesFromText(FlowAsset->GetGraph(), TextToImport, /*out*/ PastedNodes); - - //Average position of nodes so we can move them while still maintaining relative distances to each other - FVector2D AvgNodePosition(0.0f, 0.0f); - - for (TSet::TIterator It(PastedNodes); It; ++It) - { - const UEdGraphNode* Node = *It; - AvgNodePosition.X += Node->NodePosX; - AvgNodePosition.Y += Node->NodePosY; - } - - if (PastedNodes.Num() > 0) - { - const float InvNumNodes = 1.0f / static_cast(PastedNodes.Num()); - AvgNodePosition.X *= InvNumNodes; - AvgNodePosition.Y *= InvNumNodes; - } - - for (TSet::TIterator It(PastedNodes); It; ++It) - { - UEdGraphNode* Node = *It; - - // Give new node a different Guid from the old one - Node->CreateNewGuid(); - - if (const UFlowGraphNode* FlowGraphNode = Cast(Node)) - { - FlowAsset->RegisterNode(Node->NodeGuid, FlowGraphNode->GetFlowNode()); - } - - // Select the newly pasted stuff - GraphEditor->SetNodeSelection(Node, true); - - Node->NodePosX = (Node->NodePosX - AvgNodePosition.X) + Location.X; - Node->NodePosY = (Node->NodePosY - AvgNodePosition.Y) + Location.Y; - - Node->SnapToGrid(SNodePanel::GetSnapGridSize()); - } - - // Force new pasted FlowNodes to have same connections as graph nodes - FlowAsset->HarvestNodeConnections(); - - // Update UI - GraphEditor->NotifyGraphChanged(); - - FlowAsset->PostEditChange(); - FlowAsset->MarkPackageDirty(); -} - -bool FFlowAssetEditor::CanPasteNodes() const -{ - if (CanEdit()) - { - FString ClipboardContent; - FPlatformApplicationMisc::ClipboardPaste(ClipboardContent); - - return FEdGraphUtilities::CanImportNodesFromText(FlowAsset->GetGraph(), ClipboardContent); - } - - return false; -} - -void FFlowAssetEditor::DuplicateNodes() -{ - CopySelectedNodes(); - PasteNodes(); -} - -bool FFlowAssetEditor::CanDuplicateNodes() const -{ - return CanCopyNodes(); -} - -void FFlowAssetEditor::OnNodeDoubleClicked(class UEdGraphNode* Node) const -{ - UFlowNode* FlowNode = Cast(Node)->GetFlowNode(); - - if (FlowNode) - { - if (UFlowGraphEditorSettings::Get()->NodeDoubleClickTarget == EFlowNodeDoubleClickTarget::NodeDefinition) - { - Node->JumpToDefinition(); - } - else - { - const FString AssetPath = FlowNode->GetAssetPath(); - if (!AssetPath.IsEmpty()) - { - GEditor->GetEditorSubsystem()->OpenEditorForAsset(AssetPath); - } - else if (UObject* AssetToEdit = FlowNode->GetAssetToEdit()) - { - GEditor->GetEditorSubsystem()->OpenEditorForAsset(AssetToEdit); - - if (IsPIE()) - { - if (UFlowNode_SubGraph* SubGraphNode = Cast(FlowNode)) - { - const TWeakObjectPtr SubFlowInstance = SubGraphNode->GetFlowAsset()->GetFlowInstance(SubGraphNode); - if (SubFlowInstance.IsValid()) - { - SubGraphNode->GetFlowAsset()->GetTemplateAsset()->SetInspectedInstance(SubFlowInstance->GetDisplayName()); - } - } - } - } - } - } -} - -void FFlowAssetEditor::OnNodeTitleCommitted(const FText& NewText, ETextCommit::Type CommitInfo, UEdGraphNode* NodeBeingChanged) -{ - if (NodeBeingChanged) - { - const FScopedTransaction Transaction(LOCTEXT("RenameNode", "Rename Node")); - NodeBeingChanged->Modify(); - NodeBeingChanged->OnRenameNode(NewText.ToString()); - } -} - -void FFlowAssetEditor::RefreshContextPins() const -{ - for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) - { - SelectedNode->RefreshContextPins(true); - } -} - -bool FFlowAssetEditor::CanRefreshContextPins() const -{ - if (CanEdit() && GetSelectedFlowNodes().Num() == 1) - { - for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) - { - return SelectedNode->SupportsContextPins(); - } - } - - return false; -} - -void FFlowAssetEditor::AddInput() const -{ - for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) - { - SelectedNode->AddUserInput(); - } -} - -bool FFlowAssetEditor::CanAddInput() const -{ - if (CanEdit() && GetSelectedFlowNodes().Num() == 1) - { - for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) - { - return SelectedNode->CanUserAddInput(); - } - } - - return false; -} - -void FFlowAssetEditor::AddOutput() const -{ - for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) - { - SelectedNode->AddUserOutput(); - } -} - -bool FFlowAssetEditor::CanAddOutput() const -{ - if (CanEdit() && GetSelectedFlowNodes().Num() == 1) - { - for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) - { - return SelectedNode->CanUserAddOutput(); - } - } - - return false; -} - -void FFlowAssetEditor::RemovePin() const -{ - if (UEdGraphPin* SelectedPin = GraphEditor->GetGraphPinForMenu()) - { - if (UFlowGraphNode* SelectedNode = Cast(SelectedPin->GetOwningNode())) - { - SelectedNode->RemoveInstancePin(SelectedPin); - } - } -} - -bool FFlowAssetEditor::CanRemovePin() const -{ - if (CanEdit() && GetSelectedFlowNodes().Num() == 1) - { - if (const UEdGraphPin* Pin = GraphEditor->GetGraphPinForMenu()) - { - if (const UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) - { - if (Pin->Direction == EGPD_Input) - { - return GraphNode->CanUserRemoveInput(Pin); - } - else - { - return GraphNode->CanUserRemoveOutput(Pin); - } - } - } - } - - return false; -} - -void FFlowAssetEditor::OnAddBreakpoint() const -{ - for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) - { - SelectedNode->NodeBreakpoint.AddBreakpoint(); - } -} - -void FFlowAssetEditor::OnAddPinBreakpoint() const -{ - if (UEdGraphPin* Pin = GraphEditor->GetGraphPinForMenu()) - { - if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) - { - GraphNode->PinBreakpoints.Add(Pin, FFlowBreakpoint()); - GraphNode->PinBreakpoints[Pin].AddBreakpoint(); - } - } -} - -bool FFlowAssetEditor::CanAddBreakpoint() const -{ - for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) - { - return !SelectedNode->NodeBreakpoint.HasBreakpoint(); - } - - return false; -} - -bool FFlowAssetEditor::CanAddPinBreakpoint() const -{ - if (UEdGraphPin* Pin = GraphEditor->GetGraphPinForMenu()) - { - if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) - { - return !GraphNode->PinBreakpoints.Contains(Pin) || !GraphNode->PinBreakpoints[Pin].HasBreakpoint(); - } - } - - return false; -} - -void FFlowAssetEditor::OnRemoveBreakpoint() const -{ - for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) - { - SelectedNode->NodeBreakpoint.RemoveBreakpoint(); - } -} - -void FFlowAssetEditor::OnRemovePinBreakpoint() const -{ - if (UEdGraphPin* Pin = GraphEditor->GetGraphPinForMenu()) - { - if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) - { - GraphNode->PinBreakpoints.Remove(Pin); - } - } -} - -bool FFlowAssetEditor::CanRemoveBreakpoint() const -{ - for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) - { - return SelectedNode->NodeBreakpoint.HasBreakpoint(); - } - - return false; -} - -bool FFlowAssetEditor::CanRemovePinBreakpoint() const -{ - if (UEdGraphPin* Pin = GraphEditor->GetGraphPinForMenu()) - { - if (const UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) - { - return GraphNode->PinBreakpoints.Contains(Pin); - } - } - - return false; -} - -void FFlowAssetEditor::OnEnableBreakpoint() const -{ - for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) - { - SelectedNode->NodeBreakpoint.EnableBreakpoint(); - } -} - -void FFlowAssetEditor::OnEnablePinBreakpoint() const -{ - if (UEdGraphPin* Pin = GraphEditor->GetGraphPinForMenu()) - { - if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) - { - GraphNode->PinBreakpoints[Pin].EnableBreakpoint(); - } - } -} - -bool FFlowAssetEditor::CanEnableBreakpoint() const -{ - if (UEdGraphPin* Pin = GraphEditor->GetGraphPinForMenu()) - { - if (const UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) - { - return GraphNode->PinBreakpoints.Contains(Pin); - } - } - - for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) - { - return SelectedNode->NodeBreakpoint.CanEnableBreakpoint(); - } - - return false; -} - -bool FFlowAssetEditor::CanEnablePinBreakpoint() const -{ - if (UEdGraphPin* Pin = GraphEditor->GetGraphPinForMenu()) - { - if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) - { - return GraphNode->PinBreakpoints.Contains(Pin) && GraphNode->PinBreakpoints[Pin].CanEnableBreakpoint(); - } - } - - return false; -} - -void FFlowAssetEditor::OnDisableBreakpoint() const -{ - for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) - { - SelectedNode->NodeBreakpoint.DisableBreakpoint(); - } -} - -void FFlowAssetEditor::OnDisablePinBreakpoint() const -{ - if (UEdGraphPin* Pin = GraphEditor->GetGraphPinForMenu()) - { - if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) - { - GraphNode->PinBreakpoints[Pin].DisableBreakpoint(); - } - } -} - -bool FFlowAssetEditor::CanDisableBreakpoint() const -{ - for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) - { - return SelectedNode->NodeBreakpoint.IsBreakpointEnabled(); - } - - return false; -} - -bool FFlowAssetEditor::CanDisablePinBreakpoint() const -{ - if (UEdGraphPin* Pin = GraphEditor->GetGraphPinForMenu()) - { - if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) - { - return GraphNode->PinBreakpoints.Contains(Pin) && GraphNode->PinBreakpoints[Pin].IsBreakpointEnabled(); - } - } - - return false; -} - -void FFlowAssetEditor::OnToggleBreakpoint() const -{ - for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) - { - SelectedNode->NodeBreakpoint.ToggleBreakpoint(); - } -} - -void FFlowAssetEditor::OnTogglePinBreakpoint() const -{ - if (UEdGraphPin* Pin = GraphEditor->GetGraphPinForMenu()) - { - if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) - { - GraphNode->PinBreakpoints.Add(Pin, FFlowBreakpoint()); - GraphNode->PinBreakpoints[Pin].ToggleBreakpoint(); - } - } -} - -bool FFlowAssetEditor::CanToggleBreakpoint() const -{ - return GetSelectedFlowNodes().Num() > 0; -} - -bool FFlowAssetEditor::CanTogglePinBreakpoint() const -{ - return GraphEditor->GetGraphPinForMenu() != nullptr; -} - -void FFlowAssetEditor::SetSignalMode(const EFlowSignalMode Mode) const -{ - for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) - { - SelectedNode->SetSignalMode(Mode); - } - - FlowAsset->Modify(); -} - -bool FFlowAssetEditor::CanSetSignalMode(const EFlowSignalMode Mode) const -{ - if (IsPIE()) - { - return false; - } - - for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) - { - return SelectedNode->CanSetSignalMode(Mode); - } - - return false; -} - -void FFlowAssetEditor::OnForcePinActivation() const -{ - if (UEdGraphPin* Pin = GraphEditor->GetGraphPinForMenu()) - { - if (const UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) - { - GraphNode->ForcePinActivation(Pin); - } - } -} - -void FFlowAssetEditor::FocusViewport() const -{ - // Iterator used but should only contain one node - for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) - { - const UFlowNode* FlowNode = Cast(SelectedNode)->GetFlowNode(); - if (UFlowNode* NodeInstance = FlowNode->GetInspectedInstance()) - { - if (AActor* ActorToFocus = NodeInstance->GetActorToFocus()) - { - GEditor->SelectNone(false, false, false); - GEditor->SelectActor(ActorToFocus, true, true, true); - GEditor->NoteSelectionChange(); - - GEditor->MoveViewportCamerasToActor(*ActorToFocus, false); - - const FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked("LevelEditor"); - const TSharedPtr LevelEditorTab = LevelEditorModule.GetLevelEditorInstanceTab().Pin(); - if (LevelEditorTab.IsValid()) - { - LevelEditorTab->DrawAttention(); - } - } - } - - return; - } -} - -bool FFlowAssetEditor::CanFocusViewport() const -{ - return GetSelectedFlowNodes().Num() == 1; -} - -void FFlowAssetEditor::JumpToNodeDefinition() const -{ - // Iterator used but should only contain one node - for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) - { - SelectedNode->JumpToDefinition(); - return; - } -} - -bool FFlowAssetEditor::CanJumpToNodeDefinition() const -{ - return GetSelectedFlowNodes().Num() == 1; -} - #undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp index 89b42fe30..222767fa1 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp @@ -43,9 +43,9 @@ void SFlowAssetInstanceList::Construct(const FArguments& InArgs, const TWeakObje // create dropdown SAssignNew(Dropdown, SComboBox>) .OptionsSource(&InstanceNames) + .Visibility_Static(&SFlowAssetInstanceList::GetDebuggerVisibility) .OnGenerateWidget(this, &SFlowAssetInstanceList::OnGenerateWidget) .OnSelectionChanged(this, &SFlowAssetInstanceList::OnSelectionChanged) - .Visibility_Static(&FFlowAssetEditor::GetDebuggerVisibility) [ SNew(STextBlock) .Text(this, &SFlowAssetInstanceList::GetSelectedInstanceName) @@ -91,6 +91,11 @@ void SFlowAssetInstanceList::RefreshInstances() } } +EVisibility SFlowAssetInstanceList::GetDebuggerVisibility() +{ + return GEditor->PlayWorld ? EVisibility::Visible : EVisibility::Collapsed; +} + TSharedRef SFlowAssetInstanceList::OnGenerateWidget(const TSharedPtr Item) const { return SNew(STextBlock).Text(FText::FromName(*Item.Get())); @@ -125,7 +130,7 @@ void SFlowAssetBreadcrumb::Construct(const FArguments& InArgs, const TWeakObject // create breadcrumb SAssignNew(BreadcrumbTrail, SBreadcrumbTrail) .OnCrumbClicked(this, &SFlowAssetBreadcrumb::OnCrumbClicked) - .Visibility_Static(&FFlowAssetEditor::GetDebuggerVisibility) + .Visibility_Static(&SFlowAssetInstanceList::GetDebuggerVisibility) .ButtonStyle(FAppStyle::Get(), "FlatButton") .DelimiterImage(FAppStyle::GetBrush("Sequencer.BreadcrumbIcon")) .PersistentBreadcrumbs(true) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp b/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp index 78ee5c179..42510dddd 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp @@ -2,8 +2,8 @@ #include "Graph/FlowGraphConnectionDrawingPolicy.h" -#include "Asset/FlowAssetEditor.h" #include "Graph/FlowGraph.h" +#include "Graph/FlowGraphEditor.h" #include "Graph/FlowGraphEditorSettings.h" #include "Graph/FlowGraphSchema.h" #include "Graph/FlowGraphSettings.h" @@ -81,10 +81,10 @@ void FFlowGraphConnectionDrawingPolicy::BuildPaths() if (GraphObj && (UFlowGraphEditorSettings::Get()->bHighlightInputWiresOfSelectedNodes || UFlowGraphEditorSettings::Get()->bHighlightOutputWiresOfSelectedNodes)) { - const TSharedPtr FlowAssetEditor = FFlowGraphUtils::GetFlowAssetEditor(GraphObj); - if (FlowAssetEditor.IsValid()) + const TSharedPtr FlowGraphEditor = FFlowGraphUtils::GetFlowGraphEditor(GraphObj); + if (FlowGraphEditor.IsValid()) { - for (UFlowGraphNode* SelectedNode : FlowAssetEditor->GetSelectedFlowNodes()) + for (UFlowGraphNode* SelectedNode : FlowGraphEditor->GetSelectedFlowNodes()) { for (UEdGraphPin* Pin : SelectedNode->Pins) { diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp new file mode 100644 index 000000000..b7f1a63f8 --- /dev/null +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -0,0 +1,1029 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Graph/FlowGraphEditor.h" + +#include "FlowEditorCommands.h" + +#include "Asset/FlowAssetEditor.h" +#include "Asset/FlowDebuggerSubsystem.h" +#include "Graph/FlowGraphEditorSettings.h" +#include "Graph/FlowGraphSchema_Actions.h" +#include "Graph/Nodes/FlowGraphNode.h" + +#include "Nodes/Route/FlowNode_SubGraph.h" + +#include "Framework/Commands/GenericCommands.h" +#include "HAL/PlatformApplicationMisc.h" +#include "LevelEditor.h" + +#define LOCTEXT_NAMESPACE "FlowGraphEditor" + +void SFlowGraphEditor::Construct(const FArguments& InArgs, const TSharedPtr InAssetEditor) +{ + FlowAssetEditor = InAssetEditor; + FlowAsset = FlowAssetEditor.Pin()->GetFlowAsset(); + + DetailsView = InArgs._DetailsView; + + BindGraphCommands(); + + SGraphEditor::FArguments Arguments; + Arguments._Appearance = GetGraphAppearanceInfo(); + Arguments._GraphToEdit = FlowAsset->GetGraph(); + Arguments._GraphEvents = InArgs._GraphEvents; + Arguments._AutoExpandActionMenu = true; + //Arguments._ShowGraphStateOverlay = false; + + Arguments._GraphEvents.OnSelectionChanged = FOnSelectionChanged::CreateSP(this, &SFlowGraphEditor::OnSelectedNodesChanged); + Arguments._GraphEvents.OnNodeDoubleClicked = FSingleNodeEvent::CreateSP(this, &SFlowGraphEditor::OnNodeDoubleClicked); + Arguments._GraphEvents.OnTextCommitted = FOnNodeTextCommitted::CreateSP(this, &SFlowGraphEditor::OnNodeTitleCommitted); + Arguments._GraphEvents.OnSpawnNodeByShortcut = FOnSpawnNodeByShortcut::CreateStatic(&SFlowGraphEditor::OnSpawnGraphNodeByShortcut, static_cast(FlowAsset->GetGraph())); + + SGraphEditor::Construct(Arguments); +} + +void SFlowGraphEditor::BindGraphCommands() +{ + FGraphEditorCommands::Register(); + FFlowGraphCommands::Register(); + FFlowSpawnNodeCommands::Register(); + + const TSharedRef& ToolkitCommands = FlowAssetEditor.Pin()->GetToolkitCommands(); + const FGenericCommands& GenericCommands = FGenericCommands::Get(); + const FGraphEditorCommandsImpl& GraphCommands = FGraphEditorCommands::Get(); + const FFlowGraphCommands& FlowGraphCommands = FFlowGraphCommands::Get(); + + // Graph commands + ToolkitCommands->MapAction(GraphCommands.CreateComment, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnCreateComment), + FCanExecuteAction::CreateStatic(&SFlowGraphEditor::CanEdit)); + + ToolkitCommands->MapAction(GraphCommands.StraightenConnections, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnStraightenConnections)); + + // Generic Node commands + ToolkitCommands->MapAction(GenericCommands.Undo, + FExecuteAction::CreateStatic(&SFlowGraphEditor::UndoGraphAction), + FCanExecuteAction::CreateStatic(&SFlowGraphEditor::CanEdit)); + + ToolkitCommands->MapAction(GenericCommands.Redo, + FExecuteAction::CreateStatic(&SFlowGraphEditor::RedoGraphAction), + FCanExecuteAction::CreateStatic(&SFlowGraphEditor::CanEdit)); + + ToolkitCommands->MapAction(GenericCommands.SelectAll, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::SelectAllNodes), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanSelectAllNodes)); + + ToolkitCommands->MapAction(GenericCommands.Delete, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::DeleteSelectedNodes), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanDeleteNodes)); + + ToolkitCommands->MapAction(GenericCommands.Copy, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::CopySelectedNodes), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanCopyNodes)); + + ToolkitCommands->MapAction(GenericCommands.Cut, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::CutSelectedNodes), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanCutNodes)); + + ToolkitCommands->MapAction(GenericCommands.Paste, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::PasteNodes), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanPasteNodes)); + + ToolkitCommands->MapAction(GenericCommands.Duplicate, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::DuplicateNodes), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanDuplicateNodes)); + + // Pin commands + ToolkitCommands->MapAction(FlowGraphCommands.RefreshContextPins, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::RefreshContextPins), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanRefreshContextPins)); + + ToolkitCommands->MapAction(FlowGraphCommands.AddInput, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::AddInput), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanAddInput)); + + ToolkitCommands->MapAction(FlowGraphCommands.AddOutput, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::AddOutput), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanAddOutput)); + + ToolkitCommands->MapAction(FlowGraphCommands.RemovePin, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::RemovePin), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanRemovePin)); + + // Breakpoint commands + ToolkitCommands->MapAction(GraphCommands.AddBreakpoint, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnAddBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanAddBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanAddBreakpoint) + ); + + ToolkitCommands->MapAction(GraphCommands.RemoveBreakpoint, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnRemoveBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanRemoveBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanRemoveBreakpoint) + ); + + ToolkitCommands->MapAction(GraphCommands.EnableBreakpoint, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnEnableBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanEnableBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanEnableBreakpoint) + ); + + ToolkitCommands->MapAction(GraphCommands.DisableBreakpoint, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnDisableBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanDisableBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanDisableBreakpoint) + ); + + ToolkitCommands->MapAction(GraphCommands.ToggleBreakpoint, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnToggleBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanToggleBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanToggleBreakpoint) + ); + + // Pin Breakpoint commands + ToolkitCommands->MapAction(FlowGraphCommands.AddPinBreakpoint, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnAddPinBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanAddPinBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanAddPinBreakpoint) + ); + + ToolkitCommands->MapAction(FlowGraphCommands.RemovePinBreakpoint, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnRemovePinBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanRemovePinBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanRemovePinBreakpoint) + ); + + ToolkitCommands->MapAction(FlowGraphCommands.EnablePinBreakpoint, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnEnablePinBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanEnablePinBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanEnablePinBreakpoint) + ); + + ToolkitCommands->MapAction(FlowGraphCommands.DisablePinBreakpoint, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnDisablePinBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanDisablePinBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanDisablePinBreakpoint) + ); + + ToolkitCommands->MapAction(FlowGraphCommands.TogglePinBreakpoint, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnTogglePinBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanTogglePinBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanTogglePinBreakpoint) + ); + + // Execution Override commands + ToolkitCommands->MapAction(FlowGraphCommands.EnableNode, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::SetSignalMode, EFlowSignalMode::Enabled), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::Enabled), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::Enabled) + ); + + ToolkitCommands->MapAction(FlowGraphCommands.DisableNode, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::SetSignalMode, EFlowSignalMode::Disabled), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::Disabled), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::Disabled) + ); + + ToolkitCommands->MapAction(FlowGraphCommands.SetPassThrough, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::SetSignalMode, EFlowSignalMode::PassThrough), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::PassThrough), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::PassThrough) + ); + + ToolkitCommands->MapAction(FlowGraphCommands.ForcePinActivation, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnForcePinActivation), + FCanExecuteAction::CreateStatic(&SFlowGraphEditor::IsPIE), + FIsActionChecked(), + FIsActionButtonVisible::CreateStatic(&SFlowGraphEditor::IsPIE) + ); + + // Jump commands + ToolkitCommands->MapAction(FlowGraphCommands.FocusViewport, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::FocusViewport), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanFocusViewport)); + + ToolkitCommands->MapAction(FlowGraphCommands.JumpToNodeDefinition, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::JumpToNodeDefinition), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanJumpToNodeDefinition)); +} + +FGraphAppearanceInfo SFlowGraphEditor::GetGraphAppearanceInfo() const +{ + FGraphAppearanceInfo AppearanceInfo; + AppearanceInfo.CornerText = GetCornerText(); + + if (UFlowDebuggerSubsystem::IsPlaySessionPaused()) + { + AppearanceInfo.PIENotifyText = LOCTEXT("PausedLabel", "PAUSED"); + } + + return AppearanceInfo; +} + +FText SFlowGraphEditor::GetCornerText() const +{ + return LOCTEXT("AppearanceCornerText_FlowAsset", "FLOW"); +} + +void SFlowGraphEditor::UndoGraphAction() +{ + GEditor->UndoTransaction(); +} + +void SFlowGraphEditor::RedoGraphAction() +{ + GEditor->RedoTransaction(); +} + +FReply SFlowGraphEditor::OnSpawnGraphNodeByShortcut(FInputChord InChord, const FVector2D& InPosition, UEdGraph* InGraph) +{ + UEdGraph* Graph = InGraph; + + if (FFlowSpawnNodeCommands::IsRegistered()) + { + const TSharedPtr Action = FFlowSpawnNodeCommands::Get().GetActionByChord(InChord); + if (Action.IsValid()) + { + TArray DummyPins; + Action->PerformAction(Graph, DummyPins, InPosition); + return FReply::Handled(); + } + } + + return FReply::Unhandled(); +} + +void SFlowGraphEditor::OnCreateComment() const +{ + FFlowGraphSchemaAction_NewComment CommentAction; + CommentAction.PerformAction(FlowAsset->GetGraph(), nullptr, GetPasteLocation()); +} + +bool SFlowGraphEditor::CanEdit() +{ + return GEditor->PlayWorld == nullptr; +} + +bool SFlowGraphEditor::IsPIE() +{ + return GEditor->PlayWorld != nullptr; +} + +bool SFlowGraphEditor::IsTabFocused() const +{ + return FlowAssetEditor.Pin()->IsTabFocused(FFlowAssetEditor::GraphTab); +} + +void SFlowGraphEditor::SelectSingleNode(UEdGraphNode* Node) +{ + ClearSelectionSet(); + SetNodeSelection(Node, true); +} + +void SFlowGraphEditor::OnSelectedNodesChanged(const TSet& Nodes) +{ + TArray SelectedObjects; + + if (Nodes.Num() > 0) + { + FlowAssetEditor.Pin()->SetUISelectionState(FFlowAssetEditor::GraphTab); + + for (TSet::TConstIterator SetIt(Nodes); SetIt; ++SetIt) + { + if (const UFlowGraphNode* GraphNode = Cast(*SetIt)) + { + SelectedObjects.Add(Cast(GraphNode->GetFlowNode())); + } + else + { + SelectedObjects.Add(*SetIt); + } + } + } + else + { + FlowAssetEditor.Pin()->SetUISelectionState(NAME_None); + SelectedObjects.Add(FlowAsset.Get()); + } + + if (DetailsView.IsValid()) + { + DetailsView->SetObjects(SelectedObjects); + } + + OnSelectionChangedEvent.ExecuteIfBound(Nodes); +} + +TSet SFlowGraphEditor::GetSelectedFlowNodes() const +{ + TSet Result; + + const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes(); + for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt) + { + if (UFlowGraphNode* SelectedNode = Cast(*NodeIt)) + { + Result.Emplace(SelectedNode); + } + } + + return Result; +} + +void SFlowGraphEditor::DeleteSelectedNodes() +{ + const FScopedTransaction Transaction(LOCTEXT("DeleteSelectedNode", "Delete Selected Node")); + GetCurrentGraph()->Modify(); + FlowAsset->Modify(); + + const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes(); + FlowAssetEditor.Pin()->SetUISelectionState(NAME_None); + + for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt) + { + UEdGraphNode* Node = CastChecked(*NodeIt); + if (Node->CanUserDeleteNode()) + { + if (const UFlowGraphNode* FlowGraphNode = Cast(Node)) + { + if (FlowGraphNode->GetFlowNode()) + { + const FGuid NodeGuid = FlowGraphNode->GetFlowNode()->GetGuid(); + + GetCurrentGraph()->GetSchema()->BreakNodeLinks(*Node); + Node->DestroyNode(); + + FlowAsset->UnregisterNode(NodeGuid); + continue; + } + } + + GetCurrentGraph()->GetSchema()->BreakNodeLinks(*Node); + Node->DestroyNode(); + } + } +} + +void SFlowGraphEditor::DeleteSelectedDuplicableNodes() +{ + // Cache off the old selection + const FGraphPanelSelectionSet OldSelectedNodes = GetSelectedNodes(); + + // Clear the selection and only select the nodes that can be duplicated + FGraphPanelSelectionSet RemainingNodes; + ClearSelectionSet(); + + for (FGraphPanelSelectionSet::TConstIterator SelectedIt(OldSelectedNodes); SelectedIt; ++SelectedIt) + { + if (UEdGraphNode* Node = Cast(*SelectedIt)) + { + if (Node->CanDuplicateNode()) + { + SetNodeSelection(Node, true); + } + else + { + RemainingNodes.Add(Node); + } + } + } + + // Delete the duplicable nodes + DeleteSelectedNodes(); + + for (FGraphPanelSelectionSet::TConstIterator SelectedIt(RemainingNodes); SelectedIt; ++SelectedIt) + { + if (UEdGraphNode* Node = Cast(*SelectedIt)) + { + SetNodeSelection(Node, true); + } + } +} + +bool SFlowGraphEditor::CanDeleteNodes() const +{ + if (CanEdit() && IsTabFocused()) + { + const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes(); + for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt) + { + if (const UEdGraphNode* Node = Cast(*NodeIt)) + { + if (!Node->CanUserDeleteNode()) + { + return false; + } + } + } + + return SelectedNodes.Num() > 0; + } + + return false; +} + +void SFlowGraphEditor::CutSelectedNodes() +{ + CopySelectedNodes(); + + // Cut should only delete nodes that can be duplicated + DeleteSelectedDuplicableNodes(); +} + +bool SFlowGraphEditor::CanCutNodes() const +{ + return CanCopyNodes() && CanDeleteNodes(); +} + +void SFlowGraphEditor::CopySelectedNodes() const +{ + const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes(); + for (FGraphPanelSelectionSet::TConstIterator SelectedIt(SelectedNodes); SelectedIt; ++SelectedIt) + { + if (UFlowGraphNode* Node = Cast(*SelectedIt)) + { + Node->PrepareForCopying(); + } + } + + // Export the selected nodes and place the text on the clipboard + FString ExportedText; + FEdGraphUtilities::ExportNodesToText(SelectedNodes, /*out*/ ExportedText); + FPlatformApplicationMisc::ClipboardCopy(*ExportedText); + + for (FGraphPanelSelectionSet::TConstIterator SelectedIt(SelectedNodes); SelectedIt; ++SelectedIt) + { + if (UFlowGraphNode* Node = Cast(*SelectedIt)) + { + Node->PostCopyNode(); + } + } +} + +bool SFlowGraphEditor::CanCopyNodes() const +{ + if (CanEdit() && IsTabFocused()) + { + const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes(); + for (FGraphPanelSelectionSet::TConstIterator SelectedIt(SelectedNodes); SelectedIt; ++SelectedIt) + { + const UEdGraphNode* Node = Cast(*SelectedIt); + if (Node && Node->CanDuplicateNode()) + { + return true; + } + } + } + + return false; +} + +void SFlowGraphEditor::PasteNodes() +{ + PasteNodesHere(GetPasteLocation()); +} + +void SFlowGraphEditor::PasteNodesHere(const FVector2D& Location) +{ + FlowAssetEditor.Pin()->SetUISelectionState(NAME_None); + + // Undo/Redo support + const FScopedTransaction Transaction(LOCTEXT("PasteNode", "Paste Node")); + FlowAsset->GetGraph()->Modify(); + FlowAsset->Modify(); + + // Clear the selection set (newly pasted stuff will be selected) + ClearSelectionSet(); + + // Grab the text to paste from the clipboard. + FString TextToImport; + FPlatformApplicationMisc::ClipboardPaste(TextToImport); + + // Import the nodes + TSet PastedNodes; + FEdGraphUtilities::ImportNodesFromText(FlowAsset->GetGraph(), TextToImport, /*out*/ PastedNodes); + + //Average position of nodes so we can move them while still maintaining relative distances to each other + FVector2D AvgNodePosition(0.0f, 0.0f); + + for (TSet::TIterator It(PastedNodes); It; ++It) + { + const UEdGraphNode* Node = *It; + AvgNodePosition.X += Node->NodePosX; + AvgNodePosition.Y += Node->NodePosY; + } + + if (PastedNodes.Num() > 0) + { + const float InvNumNodes = 1.0f / static_cast(PastedNodes.Num()); + AvgNodePosition.X *= InvNumNodes; + AvgNodePosition.Y *= InvNumNodes; + } + + for (TSet::TIterator It(PastedNodes); It; ++It) + { + UEdGraphNode* Node = *It; + + // Give new node a different Guid from the old one + Node->CreateNewGuid(); + + if (const UFlowGraphNode* FlowGraphNode = Cast(Node)) + { + FlowAsset->RegisterNode(Node->NodeGuid, FlowGraphNode->GetFlowNode()); + } + + // Select the newly pasted stuff + SetNodeSelection(Node, true); + + Node->NodePosX = (Node->NodePosX - AvgNodePosition.X) + Location.X; + Node->NodePosY = (Node->NodePosY - AvgNodePosition.Y) + Location.Y; + + Node->SnapToGrid(SNodePanel::GetSnapGridSize()); + } + + // Force new pasted FlowNodes to have same connections as graph nodes + FlowAsset->HarvestNodeConnections(); + + // Update UI + NotifyGraphChanged(); + + FlowAsset->PostEditChange(); + FlowAsset->MarkPackageDirty(); +} + +bool SFlowGraphEditor::CanPasteNodes() const +{ + if (CanEdit() && IsTabFocused()) + { + FString ClipboardContent; + FPlatformApplicationMisc::ClipboardPaste(ClipboardContent); + + return FEdGraphUtilities::CanImportNodesFromText(FlowAsset->GetGraph(), ClipboardContent); + } + + return false; +} + +void SFlowGraphEditor::DuplicateNodes() +{ + CopySelectedNodes(); + PasteNodes(); +} + +bool SFlowGraphEditor::CanDuplicateNodes() const +{ + return CanCopyNodes(); +} + +void SFlowGraphEditor::OnNodeDoubleClicked(class UEdGraphNode* Node) const +{ + UFlowNode* FlowNode = Cast(Node)->GetFlowNode(); + + if (FlowNode) + { + if (UFlowGraphEditorSettings::Get()->NodeDoubleClickTarget == EFlowNodeDoubleClickTarget::NodeDefinition) + { + Node->JumpToDefinition(); + } + else + { + const FString AssetPath = FlowNode->GetAssetPath(); + if (!AssetPath.IsEmpty()) + { + GEditor->GetEditorSubsystem()->OpenEditorForAsset(AssetPath); + } + else if (UObject* AssetToEdit = FlowNode->GetAssetToEdit()) + { + GEditor->GetEditorSubsystem()->OpenEditorForAsset(AssetToEdit); + + if (IsPIE()) + { + if (UFlowNode_SubGraph* SubGraphNode = Cast(FlowNode)) + { + const TWeakObjectPtr SubFlowInstance = SubGraphNode->GetFlowAsset()->GetFlowInstance(SubGraphNode); + if (SubFlowInstance.IsValid()) + { + SubGraphNode->GetFlowAsset()->GetTemplateAsset()->SetInspectedInstance(SubFlowInstance->GetDisplayName()); + } + } + } + } + } + } +} + +void SFlowGraphEditor::OnNodeTitleCommitted(const FText& NewText, ETextCommit::Type CommitInfo, UEdGraphNode* NodeBeingChanged) +{ + if (NodeBeingChanged) + { + const FScopedTransaction Transaction(LOCTEXT("RenameNode", "Rename Node")); + NodeBeingChanged->Modify(); + NodeBeingChanged->OnRenameNode(NewText.ToString()); + } +} + +void SFlowGraphEditor::RefreshContextPins() const +{ + for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) + { + SelectedNode->RefreshContextPins(true); + } +} + +bool SFlowGraphEditor::CanRefreshContextPins() const +{ + if (CanEdit() && GetSelectedFlowNodes().Num() == 1) + { + for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) + { + return SelectedNode->SupportsContextPins(); + } + } + + return false; +} + +void SFlowGraphEditor::AddInput() const +{ + for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) + { + SelectedNode->AddUserInput(); + } +} + +bool SFlowGraphEditor::CanAddInput() const +{ + if (CanEdit() && GetSelectedFlowNodes().Num() == 1) + { + for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) + { + return SelectedNode->CanUserAddInput(); + } + } + + return false; +} + +void SFlowGraphEditor::AddOutput() const +{ + for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) + { + SelectedNode->AddUserOutput(); + } +} + +bool SFlowGraphEditor::CanAddOutput() const +{ + if (CanEdit() && GetSelectedFlowNodes().Num() == 1) + { + for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) + { + return SelectedNode->CanUserAddOutput(); + } + } + + return false; +} + +void SFlowGraphEditor::RemovePin() +{ + if (UEdGraphPin* SelectedPin = GetGraphPinForMenu()) + { + if (UFlowGraphNode* SelectedNode = Cast(SelectedPin->GetOwningNode())) + { + SelectedNode->RemoveInstancePin(SelectedPin); + } + } +} + +bool SFlowGraphEditor::CanRemovePin() +{ + if (CanEdit() && GetSelectedFlowNodes().Num() == 1) + { + if (const UEdGraphPin* Pin = GetGraphPinForMenu()) + { + if (const UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) + { + if (Pin->Direction == EGPD_Input) + { + return GraphNode->CanUserRemoveInput(Pin); + } + else + { + return GraphNode->CanUserRemoveOutput(Pin); + } + } + } + } + + return false; +} + +void SFlowGraphEditor::OnAddBreakpoint() const +{ + for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) + { + SelectedNode->NodeBreakpoint.AddBreakpoint(); + } +} + +void SFlowGraphEditor::OnAddPinBreakpoint() +{ + if (UEdGraphPin* Pin = GetGraphPinForMenu()) + { + if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) + { + GraphNode->PinBreakpoints.Add(Pin, FFlowBreakpoint()); + GraphNode->PinBreakpoints[Pin].AddBreakpoint(); + } + } +} + +bool SFlowGraphEditor::CanAddBreakpoint() const +{ + for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) + { + return !SelectedNode->NodeBreakpoint.HasBreakpoint(); + } + + return false; +} + +bool SFlowGraphEditor::CanAddPinBreakpoint() +{ + if (UEdGraphPin* Pin = GetGraphPinForMenu()) + { + if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) + { + return !GraphNode->PinBreakpoints.Contains(Pin) || !GraphNode->PinBreakpoints[Pin].HasBreakpoint(); + } + } + + return false; +} + +void SFlowGraphEditor::OnRemoveBreakpoint() const +{ + for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) + { + SelectedNode->NodeBreakpoint.RemoveBreakpoint(); + } +} + +void SFlowGraphEditor::OnRemovePinBreakpoint() +{ + if (UEdGraphPin* Pin = GetGraphPinForMenu()) + { + if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) + { + GraphNode->PinBreakpoints.Remove(Pin); + } + } +} + +bool SFlowGraphEditor::CanRemoveBreakpoint() const +{ + for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) + { + return SelectedNode->NodeBreakpoint.HasBreakpoint(); + } + + return false; +} + +bool SFlowGraphEditor::CanRemovePinBreakpoint() +{ + if (UEdGraphPin* Pin = GetGraphPinForMenu()) + { + if (const UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) + { + return GraphNode->PinBreakpoints.Contains(Pin); + } + } + + return false; +} + +void SFlowGraphEditor::OnEnableBreakpoint() const +{ + for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) + { + SelectedNode->NodeBreakpoint.EnableBreakpoint(); + } +} + +void SFlowGraphEditor::OnEnablePinBreakpoint() +{ + if (UEdGraphPin* Pin = GetGraphPinForMenu()) + { + if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) + { + GraphNode->PinBreakpoints[Pin].EnableBreakpoint(); + } + } +} + +bool SFlowGraphEditor::CanEnableBreakpoint() +{ + if (UEdGraphPin* Pin = GetGraphPinForMenu()) + { + if (const UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) + { + return GraphNode->PinBreakpoints.Contains(Pin); + } + } + + for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) + { + return SelectedNode->NodeBreakpoint.CanEnableBreakpoint(); + } + + return false; +} + +bool SFlowGraphEditor::CanEnablePinBreakpoint() +{ + if (UEdGraphPin* Pin = GetGraphPinForMenu()) + { + if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) + { + return GraphNode->PinBreakpoints.Contains(Pin) && GraphNode->PinBreakpoints[Pin].CanEnableBreakpoint(); + } + } + + return false; +} + +void SFlowGraphEditor::OnDisableBreakpoint() const +{ + for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) + { + SelectedNode->NodeBreakpoint.DisableBreakpoint(); + } +} + +void SFlowGraphEditor::OnDisablePinBreakpoint() +{ + if (UEdGraphPin* Pin = GetGraphPinForMenu()) + { + if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) + { + GraphNode->PinBreakpoints[Pin].DisableBreakpoint(); + } + } +} + +bool SFlowGraphEditor::CanDisableBreakpoint() const +{ + for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) + { + return SelectedNode->NodeBreakpoint.IsBreakpointEnabled(); + } + + return false; +} + +bool SFlowGraphEditor::CanDisablePinBreakpoint() +{ + if (UEdGraphPin* Pin = GetGraphPinForMenu()) + { + if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) + { + return GraphNode->PinBreakpoints.Contains(Pin) && GraphNode->PinBreakpoints[Pin].IsBreakpointEnabled(); + } + } + + return false; +} + +void SFlowGraphEditor::OnToggleBreakpoint() const +{ + for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) + { + SelectedNode->NodeBreakpoint.ToggleBreakpoint(); + } +} + +void SFlowGraphEditor::OnTogglePinBreakpoint() +{ + if (UEdGraphPin* Pin = GetGraphPinForMenu()) + { + if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) + { + GraphNode->PinBreakpoints.Add(Pin, FFlowBreakpoint()); + GraphNode->PinBreakpoints[Pin].ToggleBreakpoint(); + } + } +} + +bool SFlowGraphEditor::CanToggleBreakpoint() const +{ + return GetSelectedFlowNodes().Num() > 0; +} + +bool SFlowGraphEditor::CanTogglePinBreakpoint() +{ + return GetGraphPinForMenu() != nullptr; +} + +void SFlowGraphEditor::SetSignalMode(const EFlowSignalMode Mode) const +{ + for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) + { + SelectedNode->SetSignalMode(Mode); + } + + FlowAsset->Modify(); +} + +bool SFlowGraphEditor::CanSetSignalMode(const EFlowSignalMode Mode) const +{ + if (IsPIE()) + { + return false; + } + + for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) + { + return SelectedNode->CanSetSignalMode(Mode); + } + + return false; +} + +void SFlowGraphEditor::OnForcePinActivation() +{ + if (UEdGraphPin* Pin = GetGraphPinForMenu()) + { + if (const UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) + { + GraphNode->ForcePinActivation(Pin); + } + } +} + +void SFlowGraphEditor::FocusViewport() const +{ + // Iterator used but should only contain one node + for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) + { + const UFlowNode* FlowNode = Cast(SelectedNode)->GetFlowNode(); + if (UFlowNode* NodeInstance = FlowNode->GetInspectedInstance()) + { + if (AActor* ActorToFocus = NodeInstance->GetActorToFocus()) + { + GEditor->SelectNone(false, false, false); + GEditor->SelectActor(ActorToFocus, true, true, true); + GEditor->NoteSelectionChange(); + + GEditor->MoveViewportCamerasToActor(*ActorToFocus, false); + + const FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked("LevelEditor"); + const TSharedPtr LevelEditorTab = LevelEditorModule.GetLevelEditorInstanceTab().Pin(); + if (LevelEditorTab.IsValid()) + { + LevelEditorTab->DrawAttention(); + } + } + } + + return; + } +} + +bool SFlowGraphEditor::CanFocusViewport() const +{ + return GetSelectedFlowNodes().Num() == 1; +} + +void SFlowGraphEditor::JumpToNodeDefinition() const +{ + // Iterator used but should only contain one node + for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) + { + SelectedNode->JumpToDefinition(); + return; + } +} + +bool SFlowGraphEditor::CanJumpToNodeDefinition() const +{ + return GetSelectedFlowNodes().Num() == 1; +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index 0bcb53d78..c4e32b6cb 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -4,6 +4,7 @@ #include "Asset/FlowAssetEditor.h" #include "Graph/FlowGraph.h" +#include "Graph/FlowGraphEditor.h" #include "Graph/FlowGraphSchema_Actions.h" #include "Graph/FlowGraphSettings.h" #include "Graph/FlowGraphUtils.h" @@ -62,7 +63,7 @@ void UFlowGraphSchema::GetGraphContextActions(FGraphContextMenuBuilder& ContextM GetFlowNodeActions(ContextMenuBuilder, GetAssetClassDefaults(ContextMenuBuilder.CurrentGraph), FString()); GetCommentAction(ContextMenuBuilder, ContextMenuBuilder.CurrentGraph); - if (!ContextMenuBuilder.FromPin && FFlowGraphUtils::GetFlowAssetEditor(ContextMenuBuilder.CurrentGraph)->CanPasteNodes()) + if (!ContextMenuBuilder.FromPin && FFlowGraphUtils::GetFlowGraphEditor(ContextMenuBuilder.CurrentGraph)->CanPasteNodes()) { const TSharedPtr NewAction(new FFlowGraphSchemaAction_Paste(FText::GetEmpty(), LOCTEXT("PasteHereAction", "Paste here"), FText::GetEmpty(), 0)); ContextMenuBuilder.AddAction(NewAction); @@ -172,7 +173,7 @@ void UFlowGraphSchema::BreakPinLinks(UEdGraphPin& TargetPin, bool bSendsNodeNoti int32 UFlowGraphSchema::GetNodeSelectionCount(const UEdGraph* Graph) const { - return FFlowGraphUtils::GetFlowAssetEditor(Graph)->GetNumberOfSelectedNodes(); + return FFlowGraphUtils::GetFlowGraphEditor(Graph)->GetNumberOfSelectedNodes(); } TSharedPtr UFlowGraphSchema::GetCreateCommentAction() const @@ -358,7 +359,7 @@ void UFlowGraphSchema::GetCommentAction(FGraphActionMenuBuilder& ActionMenuBuild { if (!ActionMenuBuilder.FromPin) { - const bool bIsManyNodesSelected = CurrentGraph ? (FFlowGraphUtils::GetFlowAssetEditor(CurrentGraph)->GetNumberOfSelectedNodes() > 0) : false; + const bool bIsManyNodesSelected = CurrentGraph ? (FFlowGraphUtils::GetFlowGraphEditor(CurrentGraph)->GetNumberOfSelectedNodes() > 0) : false; const FText MenuDescription = bIsManyNodesSelected ? LOCTEXT("CreateCommentAction", "Create Comment from Selection") : LOCTEXT("AddCommentAction", "Add Comment..."); const FText ToolTip = LOCTEXT("CreateCommentToolTip", "Creates a comment."); diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp index 7aea403a3..fcc34b9b9 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp @@ -2,8 +2,8 @@ #include "Graph/FlowGraphSchema_Actions.h" -#include "Asset/FlowAssetEditor.h" #include "Graph/FlowGraph.h" +#include "Graph/FlowGraphEditor.h" #include "Graph/FlowGraphSchema.h" #include "Graph/FlowGraphUtils.h" #include "Graph/Nodes/FlowGraphNode.h" @@ -81,10 +81,10 @@ UFlowGraphNode* FFlowGraphSchemaAction_NewNode::CreateNode(UEdGraph* ParentGraph // select in editor UI if (bSelectNewNode) { - const TSharedPtr FlowEditor = FFlowGraphUtils::GetFlowAssetEditor(ParentGraph); - if (FlowEditor.IsValid()) + const TSharedPtr FlowGraphEditor = FFlowGraphUtils::GetFlowGraphEditor(ParentGraph); + if (FlowGraphEditor.IsValid()) { - FlowEditor->SelectSingleNode(NewGraphNode); + FlowGraphEditor->SelectSingleNode(NewGraphNode); } } @@ -200,7 +200,7 @@ UEdGraphNode* FFlowGraphSchemaAction_Paste::PerformAction(class UEdGraph* Parent // prevent adding new nodes while playing if (GEditor->PlayWorld == nullptr) { - FFlowGraphUtils::GetFlowAssetEditor(ParentGraph)->PasteNodesHere(Location); + FFlowGraphUtils::GetFlowGraphEditor(ParentGraph)->PasteNodesHere(Location); } return nullptr; @@ -220,11 +220,11 @@ UEdGraphNode* FFlowGraphSchemaAction_NewComment::PerformAction(class UEdGraph* P UEdGraphNode_Comment* CommentTemplate = NewObject(); FVector2D SpawnLocation = Location; - const TSharedPtr FlowAssetEditor = FFlowGraphUtils::GetFlowAssetEditor(ParentGraph); - if (FlowAssetEditor.IsValid()) + const TSharedPtr FlowGraphEditor = FFlowGraphUtils::GetFlowGraphEditor(ParentGraph); + if (FlowGraphEditor.IsValid()) { FSlateRect Bounds; - if (FlowAssetEditor->GetBoundsForSelectedNodes(Bounds, 50.0f)) + if (FlowGraphEditor->GetBoundsForSelectedNodes(Bounds, 50.0f)) { CommentTemplate->SetBounds(Bounds); SpawnLocation.X = CommentTemplate->NodePosX; diff --git a/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp b/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp index b00e6a898..468ba353f 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp @@ -8,12 +8,12 @@ #include "Toolkits/ToolkitManager.h" -TSharedPtr FFlowGraphUtils::GetFlowAssetEditor(const UObject* ObjectToFocusOn) +TSharedPtr FFlowGraphUtils::GetFlowAssetEditor(const UEdGraph* Graph) { - check(ObjectToFocusOn); + check(Graph); TSharedPtr FlowAssetEditor; - if (const UFlowAsset* FlowAsset = Cast(ObjectToFocusOn)->GetFlowAsset()) + if (const UFlowAsset* FlowAsset = Cast(Graph)->GetFlowAsset()) { const TSharedPtr FoundAssetEditor = FToolkitManager::Get().FindEditorForAsset(FlowAsset); if (FoundAssetEditor.IsValid()) @@ -23,3 +23,16 @@ TSharedPtr FFlowGraphUtils::GetFlowAssetEditor(const UObject* } return FlowAssetEditor; } + +TSharedPtr FFlowGraphUtils::GetFlowGraphEditor(const UEdGraph* Graph) +{ + TSharedPtr FlowGraphEditor; + + const TSharedPtr FlowEditor = GetFlowAssetEditor(Graph); + if (FlowEditor.IsValid()) + { + FlowGraphEditor = FlowEditor->GetFlowGraph(); + } + + return FlowGraphEditor; +} diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowPalette.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowPalette.cpp index d2b27637a..ba1dca9e3 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowPalette.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowPalette.cpp @@ -100,7 +100,7 @@ FText SFlowPaletteItem::GetItemTooltip() const void SFlowPalette::Construct(const FArguments& InArgs, TWeakPtr InFlowAssetEditor) { - FlowAssetEditorPtr = InFlowAssetEditor; + FlowAssetEditor = InFlowAssetEditor; UpdateCategoryNames(); UFlowGraphSchema::OnNodeListChanged.AddSP(this, &SFlowPalette::Refresh); @@ -182,13 +182,8 @@ TSharedRef SFlowPalette::OnCreateWidgetForAction(FCreateWidgetForAction void SFlowPalette::CollectAllActions(FGraphActionListBuilderBase& OutAllActions) { - const UClass* AssetClass = UFlowAsset::StaticClass(); - - const TSharedPtr FlowAssetEditor = FlowAssetEditorPtr.Pin(); - if (FlowAssetEditor && FlowAssetEditor->GetFlowAsset()) - { - AssetClass = FlowAssetEditor->GetFlowAsset()->GetClass(); - } + ensureAlways(FlowAssetEditor.Pin() && FlowAssetEditor.Pin()->GetFlowAsset()); + const UClass* AssetClass = FlowAssetEditor.Pin()->GetFlowAsset()->GetClass(); FGraphActionMenuBuilder ActionMenuBuilder; UFlowGraphSchema::GetPaletteActions(ActionMenuBuilder, AssetClass, GetFilterCategoryName()); @@ -214,11 +209,7 @@ void SFlowPalette::OnActionSelected(const TArray FlowAssetEditor = FlowAssetEditorPtr.Pin(); - if (FlowAssetEditor) - { - FlowAssetEditor->SetUISelectionState(FFlowAssetEditor::PaletteTab); - } + FlowAssetEditor.Pin()->SetUISelectionState(FFlowAssetEditor::PaletteTab); } } diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index fb8814331..0591c9cfd 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -3,7 +3,6 @@ #pragma once #include "EditorUndoClient.h" -#include "GraphEditor.h" #include "Misc/NotifyHook.h" #include "Toolkits/AssetEditorToolkit.h" #include "Toolkits/IToolkitHost.h" @@ -13,6 +12,7 @@ #include "FlowTypes.h" class FFlowMessageLog; +class SFlowGraphEditor; class SFlowPalette; class UFlowAsset; class UFlowGraphNode; @@ -27,13 +27,22 @@ struct Rect; class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEditorUndoClient, public FGCObject, public FNotifyHook { +public: + /** The tab ids for all the tabs used */ + static const FName DetailsTab; + static const FName GraphTab; + static const FName PaletteTab; + static const FName RuntimeLogTab; + static const FName SearchTab; + static const FName ValidationLogTab; + protected: /** The Flow Asset being edited */ UFlowAsset* FlowAsset; TSharedPtr AssetToolbar; - TSharedPtr GraphEditor; + TSharedPtr GraphEditor; TSharedPtr DetailsView; TSharedPtr Palette; @@ -49,15 +58,6 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit TSharedPtr ValidationLog; TSharedPtr ValidationLogListing; -public: - /** The tab ids for all the tabs used */ - static const FName DetailsTab; - static const FName GraphTab; - static const FName PaletteTab; - static const FName RuntimeLogTab; - static const FName SearchTab; - static const FName ValidationLogTab; - private: /** The current UI selection state of this editor */ FName CurrentUISelection; @@ -67,6 +67,7 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit virtual ~FFlowAssetEditor() override; UFlowAsset* GetFlowAsset() const { return FlowAsset; } + TSharedPtr GetFlowGraph() const { return GraphEditor; } // FGCObject virtual void AddReferencedObjects(FReferenceCollector& Collector) override; @@ -102,6 +103,8 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit virtual void InitToolMenuContext(FToolMenuContext& MenuContext) override; // -- + bool IsTabFocused(const FTabId& TabId) const; + private: TSharedRef SpawnTab_Details(const FSpawnTabArgs& Args) const; TSharedRef SpawnTab_Graph(const FSpawnTabArgs& Args) const; @@ -136,44 +139,16 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit virtual bool CanGoToParentInstance(); virtual void CreateWidgets(); + virtual void CreateGraphWidget(); - virtual TSharedRef CreateGraphWidget(); - virtual FGraphAppearanceInfo GetGraphAppearanceInfo() const; - virtual FText GetCornerText() const; - - virtual void BindGraphCommands(); - -private: - static void UndoGraphAction(); - static void RedoGraphAction(); - - static FReply OnSpawnGraphNodeByShortcut(FInputChord InChord, const FVector2D& InPosition, UEdGraph* InGraph); + static bool CanEdit(); public: - /** Gets the UI selection state of this editor */ - FName GetUISelectionState() const { return CurrentUISelection; } void SetUISelectionState(const FName SelectionOwner); - virtual void ClearSelectionStateFor(const FName SelectionOwner); + FName GetUISelectionState() const; -private: - void OnCreateComment() const; - void OnStraightenConnections() const; - -public: - static bool CanEdit(); - static bool IsPIE(); - static EVisibility GetDebuggerVisibility(); - - TSet GetSelectedFlowNodes() const; - int32 GetNumberOfSelectedNodes() const; - bool GetBoundsForSelectedNodes(class FSlateRect& Rect, float Padding) const; - -protected: - virtual void OnSelectedNodesChanged(const TSet& Nodes); - -public: - virtual void SelectSingleNode(UEdGraphNode* Node) const; + virtual void OnSelectedNodesChanged(const TSet& Nodes) {} #if ENABLE_JUMP_TO_INNER_OBJECT // FAssetEditorToolkit @@ -183,84 +158,4 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit protected: void OnLogTokenClicked(const TSharedRef& Token) const; - - virtual void SelectAllNodes() const; - virtual bool CanSelectAllNodes() const; - - virtual void DeleteSelectedNodes(); - virtual void DeleteSelectedDuplicableNodes(); - virtual bool CanDeleteNodes() const; - - virtual void CopySelectedNodes() const; - virtual bool CanCopyNodes() const; - - virtual void CutSelectedNodes(); - virtual bool CanCutNodes() const; - - virtual void PasteNodes(); - -public: - virtual void PasteNodesHere(const FVector2D& Location); - virtual bool CanPasteNodes() const; - -protected: - virtual void DuplicateNodes(); - virtual bool CanDuplicateNodes() const; - - virtual void OnNodeDoubleClicked(class UEdGraphNode* Node) const; - virtual void OnNodeTitleCommitted(const FText& NewText, ETextCommit::Type CommitInfo, UEdGraphNode* NodeBeingChanged); - - virtual void RefreshContextPins() const; - virtual bool CanRefreshContextPins() const; - -private: - void AddInput() const; - bool CanAddInput() const; - - void AddOutput() const; - bool CanAddOutput() const; - - void RemovePin() const; - bool CanRemovePin() const; - - void OnAddBreakpoint() const; - void OnAddPinBreakpoint() const; - - bool CanAddBreakpoint() const; - bool CanAddPinBreakpoint() const; - - void OnRemoveBreakpoint() const; - void OnRemovePinBreakpoint() const; - - bool CanRemoveBreakpoint() const; - bool CanRemovePinBreakpoint() const; - - void OnEnableBreakpoint() const; - void OnEnablePinBreakpoint() const; - - bool CanEnableBreakpoint() const; - bool CanEnablePinBreakpoint() const; - - void OnDisableBreakpoint() const; - void OnDisablePinBreakpoint() const; - - bool CanDisableBreakpoint() const; - bool CanDisablePinBreakpoint() const; - - void OnToggleBreakpoint() const; - void OnTogglePinBreakpoint() const; - - bool CanToggleBreakpoint() const; - bool CanTogglePinBreakpoint() const; - - void SetSignalMode(const EFlowSignalMode Mode) const; - bool CanSetSignalMode(const EFlowSignalMode Mode) const; - - void OnForcePinActivation() const; - - void FocusViewport() const; - bool CanFocusViewport() const; - - void JumpToNodeDefinition() const; - bool CanJumpToNodeDefinition() const; }; diff --git a/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h b/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h index 32bf3ce6c..09198bcf2 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h @@ -22,9 +22,11 @@ class FLOWEDITOR_API SFlowAssetInstanceList : public SCompoundWidget void Construct(const FArguments& InArgs, const TWeakObjectPtr InTemplateAsset); virtual ~SFlowAssetInstanceList() override; + static EVisibility GetDebuggerVisibility(); + private: void RefreshInstances(); - + TSharedRef OnGenerateWidget(TSharedPtr Item) const; void OnSelectionChanged(TSharedPtr SelectedItem, ESelectInfo::Type SelectionType); FText GetSelectedInstanceName() const; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h new file mode 100644 index 000000000..eabbc8b82 --- /dev/null +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h @@ -0,0 +1,144 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "EdGraphUtilities.h" +#include "GraphEditor.h" +#include "GraphEditorActions.h" +#include "SGraphNode.h" +#include "Widgets/DeclarativeSyntaxSupport.h" + +#include "FlowGraph.h" + +class FFlowAssetEditor; + +/** + * + */ +class FLOWEDITOR_API SFlowGraphEditor : public SGraphEditor +{ +public: + SLATE_BEGIN_ARGS(SFlowGraphEditor) + { + } + + SLATE_ARGUMENT(FGraphEditorEvents, GraphEvents) + SLATE_ARGUMENT(TSharedPtr, DetailsView) + SLATE_END_ARGS() + +protected: + TWeakObjectPtr FlowAsset; + + TWeakPtr FlowAssetEditor; + TSharedPtr DetailsView; + +public: + void Construct(const FArguments& InArgs, const TSharedPtr InAssetEditor); + + virtual void BindGraphCommands(); + + virtual FGraphAppearanceInfo GetGraphAppearanceInfo() const; + virtual FText GetCornerText() const; + +private: + static void UndoGraphAction(); + static void RedoGraphAction(); + + static FReply OnSpawnGraphNodeByShortcut(FInputChord InChord, const FVector2D& InPosition, UEdGraph* InGraph); + void OnCreateComment() const; + +public: + static bool CanEdit(); + static bool IsPIE(); + virtual bool IsTabFocused() const; + + virtual void SelectSingleNode(UEdGraphNode* Node); + +protected: + virtual void OnSelectedNodesChanged(const TSet& Nodes); + +public: + FOnSelectionChanged OnSelectionChangedEvent; + + TSet GetSelectedFlowNodes() const; + +protected: + virtual bool CanSelectAllNodes() const { return true; } + + virtual void DeleteSelectedNodes(); + virtual void DeleteSelectedDuplicableNodes(); + virtual bool CanDeleteNodes() const; + + virtual void CopySelectedNodes() const; + virtual bool CanCopyNodes() const; + + virtual void CutSelectedNodes(); + virtual bool CanCutNodes() const; + + virtual void PasteNodes(); + +public: + virtual void PasteNodesHere(const FVector2D& Location); + virtual bool CanPasteNodes() const; + +protected: + virtual void DuplicateNodes(); + virtual bool CanDuplicateNodes() const; + + virtual void OnNodeDoubleClicked(class UEdGraphNode* Node) const; + virtual void OnNodeTitleCommitted(const FText& NewText, ETextCommit::Type CommitInfo, UEdGraphNode* NodeBeingChanged); + + virtual void RefreshContextPins() const; + virtual bool CanRefreshContextPins() const; + +private: + void AddInput() const; + bool CanAddInput() const; + + void AddOutput() const; + bool CanAddOutput() const; + + void RemovePin(); + bool CanRemovePin(); + + void OnAddBreakpoint() const; + void OnAddPinBreakpoint(); + + bool CanAddBreakpoint() const; + bool CanAddPinBreakpoint(); + + void OnRemoveBreakpoint() const; + void OnRemovePinBreakpoint(); + + bool CanRemoveBreakpoint() const; + bool CanRemovePinBreakpoint(); + + void OnEnableBreakpoint() const; + void OnEnablePinBreakpoint(); + + bool CanEnableBreakpoint(); + bool CanEnablePinBreakpoint(); + + void OnDisableBreakpoint() const; + void OnDisablePinBreakpoint(); + + bool CanDisableBreakpoint() const; + bool CanDisablePinBreakpoint(); + + void OnToggleBreakpoint() const; + void OnTogglePinBreakpoint(); + + bool CanToggleBreakpoint() const; + bool CanTogglePinBreakpoint(); + + void SetSignalMode(const EFlowSignalMode Mode) const; + bool CanSetSignalMode(const EFlowSignalMode Mode) const; + + void OnForcePinActivation(); + + void FocusViewport() const; + bool CanFocusViewport() const; + + void JumpToNodeDefinition() const; + bool CanJumpToNodeDefinition() const; +}; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphUtils.h b/Source/FlowEditor/Public/Graph/FlowGraphUtils.h index c9d8f7f8d..5747c7dc2 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphUtils.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphUtils.h @@ -5,11 +5,13 @@ #include "CoreMinimal.h" class FFlowAssetEditor; +class SFlowGraphEditor; class FLOWEDITOR_API FFlowGraphUtils { public: FFlowGraphUtils() {} - static TSharedPtr GetFlowAssetEditor(const UObject* ObjectToFocusOn); + static TSharedPtr GetFlowAssetEditor(const UEdGraph* Graph); + static TSharedPtr GetFlowGraphEditor(const UEdGraph* Graph); }; diff --git a/Source/FlowEditor/Public/Graph/Widgets/SFlowPalette.h b/Source/FlowEditor/Public/Graph/Widgets/SFlowPalette.h index 257bb200c..dff23d85f 100644 --- a/Source/FlowEditor/Public/Graph/Widgets/SFlowPalette.h +++ b/Source/FlowEditor/Public/Graph/Widgets/SFlowPalette.h @@ -48,7 +48,7 @@ class FLOWEDITOR_API SFlowPalette : public SGraphPalette void ClearGraphActionMenuSelection() const; protected: - TWeakPtr FlowAssetEditorPtr; + TWeakPtr FlowAssetEditor; TArray> CategoryNames; TSharedPtr CategoryComboBox; }; From 55c21323859c9a75c5c6c48084dabd7c4b14fcde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 26 Mar 2023 20:21:44 +0200 Subject: [PATCH 144/485] fixed graph commands --- .../Private/Graph/FlowGraphEditor.cpp | 244 +++++++++--------- .../FlowEditor/Public/Graph/FlowGraphEditor.h | 2 + 2 files changed, 125 insertions(+), 121 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index b7f1a63f8..ba61997d0 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -22,12 +22,13 @@ void SFlowGraphEditor::Construct(const FArguments& InArgs, const TSharedPtrGetFlowAsset(); - + DetailsView = InArgs._DetailsView; BindGraphCommands(); SGraphEditor::FArguments Arguments; + Arguments._AdditionalCommands = CommandList; Arguments._Appearance = GetGraphAppearanceInfo(); Arguments._GraphToEdit = FlowAsset->GetGraph(); Arguments._GraphEvents = InArgs._GraphEvents; @@ -48,178 +49,179 @@ void SFlowGraphEditor::BindGraphCommands() FFlowGraphCommands::Register(); FFlowSpawnNodeCommands::Register(); - const TSharedRef& ToolkitCommands = FlowAssetEditor.Pin()->GetToolkitCommands(); const FGenericCommands& GenericCommands = FGenericCommands::Get(); - const FGraphEditorCommandsImpl& GraphCommands = FGraphEditorCommands::Get(); + const FGraphEditorCommandsImpl& GraphEditorCommands = FGraphEditorCommands::Get(); const FFlowGraphCommands& FlowGraphCommands = FFlowGraphCommands::Get(); - + + CommandList = MakeShareable(new FUICommandList); + // Graph commands - ToolkitCommands->MapAction(GraphCommands.CreateComment, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnCreateComment), - FCanExecuteAction::CreateStatic(&SFlowGraphEditor::CanEdit)); + CommandList->MapAction(GraphEditorCommands.CreateComment, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnCreateComment), + FCanExecuteAction::CreateStatic(&SFlowGraphEditor::CanEdit)); - ToolkitCommands->MapAction(GraphCommands.StraightenConnections, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnStraightenConnections)); + CommandList->MapAction(GraphEditorCommands.StraightenConnections, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnStraightenConnections)); // Generic Node commands - ToolkitCommands->MapAction(GenericCommands.Undo, - FExecuteAction::CreateStatic(&SFlowGraphEditor::UndoGraphAction), - FCanExecuteAction::CreateStatic(&SFlowGraphEditor::CanEdit)); + CommandList->MapAction(GenericCommands.Undo, + FExecuteAction::CreateStatic(&SFlowGraphEditor::UndoGraphAction), + FCanExecuteAction::CreateStatic(&SFlowGraphEditor::CanEdit)); - ToolkitCommands->MapAction(GenericCommands.Redo, - FExecuteAction::CreateStatic(&SFlowGraphEditor::RedoGraphAction), - FCanExecuteAction::CreateStatic(&SFlowGraphEditor::CanEdit)); + CommandList->MapAction(GenericCommands.Redo, + FExecuteAction::CreateStatic(&SFlowGraphEditor::RedoGraphAction), + FCanExecuteAction::CreateStatic(&SFlowGraphEditor::CanEdit)); - ToolkitCommands->MapAction(GenericCommands.SelectAll, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::SelectAllNodes), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanSelectAllNodes)); + CommandList->MapAction(GenericCommands.SelectAll, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::SelectAllNodes), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanSelectAllNodes)); - ToolkitCommands->MapAction(GenericCommands.Delete, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::DeleteSelectedNodes), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanDeleteNodes)); + CommandList->MapAction(GenericCommands.Delete, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::DeleteSelectedNodes), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanDeleteNodes)); - ToolkitCommands->MapAction(GenericCommands.Copy, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::CopySelectedNodes), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanCopyNodes)); + CommandList->MapAction(GenericCommands.Copy, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::CopySelectedNodes), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanCopyNodes)); - ToolkitCommands->MapAction(GenericCommands.Cut, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::CutSelectedNodes), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanCutNodes)); + CommandList->MapAction(GenericCommands.Cut, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::CutSelectedNodes), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanCutNodes)); - ToolkitCommands->MapAction(GenericCommands.Paste, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::PasteNodes), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanPasteNodes)); + CommandList->MapAction(GenericCommands.Paste, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::PasteNodes), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanPasteNodes)); - ToolkitCommands->MapAction(GenericCommands.Duplicate, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::DuplicateNodes), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanDuplicateNodes)); + CommandList->MapAction(GenericCommands.Duplicate, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::DuplicateNodes), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanDuplicateNodes)); // Pin commands - ToolkitCommands->MapAction(FlowGraphCommands.RefreshContextPins, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::RefreshContextPins), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanRefreshContextPins)); + CommandList->MapAction(FlowGraphCommands.RefreshContextPins, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::RefreshContextPins), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanRefreshContextPins)); - ToolkitCommands->MapAction(FlowGraphCommands.AddInput, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::AddInput), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanAddInput)); + CommandList->MapAction(FlowGraphCommands.AddInput, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::AddInput), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanAddInput)); - ToolkitCommands->MapAction(FlowGraphCommands.AddOutput, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::AddOutput), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanAddOutput)); + CommandList->MapAction(FlowGraphCommands.AddOutput, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::AddOutput), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanAddOutput)); - ToolkitCommands->MapAction(FlowGraphCommands.RemovePin, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::RemovePin), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanRemovePin)); + CommandList->MapAction(FlowGraphCommands.RemovePin, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::RemovePin), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanRemovePin)); // Breakpoint commands - ToolkitCommands->MapAction(GraphCommands.AddBreakpoint, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnAddBreakpoint), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanAddBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanAddBreakpoint) + CommandList->MapAction(GraphEditorCommands.AddBreakpoint, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnAddBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanAddBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanAddBreakpoint) ); - ToolkitCommands->MapAction(GraphCommands.RemoveBreakpoint, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnRemoveBreakpoint), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanRemoveBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanRemoveBreakpoint) + CommandList->MapAction(GraphEditorCommands.RemoveBreakpoint, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnRemoveBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanRemoveBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanRemoveBreakpoint) ); - ToolkitCommands->MapAction(GraphCommands.EnableBreakpoint, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnEnableBreakpoint), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanEnableBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanEnableBreakpoint) + CommandList->MapAction(GraphEditorCommands.EnableBreakpoint, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnEnableBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanEnableBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanEnableBreakpoint) ); - ToolkitCommands->MapAction(GraphCommands.DisableBreakpoint, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnDisableBreakpoint), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanDisableBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanDisableBreakpoint) + CommandList->MapAction(GraphEditorCommands.DisableBreakpoint, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnDisableBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanDisableBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanDisableBreakpoint) ); - ToolkitCommands->MapAction(GraphCommands.ToggleBreakpoint, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnToggleBreakpoint), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanToggleBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanToggleBreakpoint) + CommandList->MapAction(GraphEditorCommands.ToggleBreakpoint, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnToggleBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanToggleBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanToggleBreakpoint) ); // Pin Breakpoint commands - ToolkitCommands->MapAction(FlowGraphCommands.AddPinBreakpoint, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnAddPinBreakpoint), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanAddPinBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanAddPinBreakpoint) + CommandList->MapAction(FlowGraphCommands.AddPinBreakpoint, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnAddPinBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanAddPinBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanAddPinBreakpoint) ); - ToolkitCommands->MapAction(FlowGraphCommands.RemovePinBreakpoint, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnRemovePinBreakpoint), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanRemovePinBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanRemovePinBreakpoint) + CommandList->MapAction(FlowGraphCommands.RemovePinBreakpoint, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnRemovePinBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanRemovePinBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanRemovePinBreakpoint) ); - ToolkitCommands->MapAction(FlowGraphCommands.EnablePinBreakpoint, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnEnablePinBreakpoint), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanEnablePinBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanEnablePinBreakpoint) + CommandList->MapAction(FlowGraphCommands.EnablePinBreakpoint, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnEnablePinBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanEnablePinBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanEnablePinBreakpoint) ); - ToolkitCommands->MapAction(FlowGraphCommands.DisablePinBreakpoint, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnDisablePinBreakpoint), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanDisablePinBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanDisablePinBreakpoint) + CommandList->MapAction(FlowGraphCommands.DisablePinBreakpoint, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnDisablePinBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanDisablePinBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanDisablePinBreakpoint) ); - ToolkitCommands->MapAction(FlowGraphCommands.TogglePinBreakpoint, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnTogglePinBreakpoint), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanTogglePinBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanTogglePinBreakpoint) + CommandList->MapAction(FlowGraphCommands.TogglePinBreakpoint, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnTogglePinBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanTogglePinBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanTogglePinBreakpoint) ); // Execution Override commands - ToolkitCommands->MapAction(FlowGraphCommands.EnableNode, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::SetSignalMode, EFlowSignalMode::Enabled), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::Enabled), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::Enabled) + CommandList->MapAction(FlowGraphCommands.EnableNode, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::SetSignalMode, EFlowSignalMode::Enabled), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::Enabled), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::Enabled) ); - ToolkitCommands->MapAction(FlowGraphCommands.DisableNode, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::SetSignalMode, EFlowSignalMode::Disabled), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::Disabled), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::Disabled) + CommandList->MapAction(FlowGraphCommands.DisableNode, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::SetSignalMode, EFlowSignalMode::Disabled), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::Disabled), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::Disabled) ); - ToolkitCommands->MapAction(FlowGraphCommands.SetPassThrough, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::SetSignalMode, EFlowSignalMode::PassThrough), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::PassThrough), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::PassThrough) + CommandList->MapAction(FlowGraphCommands.SetPassThrough, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::SetSignalMode, EFlowSignalMode::PassThrough), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::PassThrough), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::PassThrough) ); - ToolkitCommands->MapAction(FlowGraphCommands.ForcePinActivation, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnForcePinActivation), - FCanExecuteAction::CreateStatic(&SFlowGraphEditor::IsPIE), - FIsActionChecked(), - FIsActionButtonVisible::CreateStatic(&SFlowGraphEditor::IsPIE) + CommandList->MapAction(FlowGraphCommands.ForcePinActivation, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnForcePinActivation), + FCanExecuteAction::CreateStatic(&SFlowGraphEditor::IsPIE), + FIsActionChecked(), + FIsActionButtonVisible::CreateStatic(&SFlowGraphEditor::IsPIE) ); // Jump commands - ToolkitCommands->MapAction(FlowGraphCommands.FocusViewport, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::FocusViewport), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanFocusViewport)); + CommandList->MapAction(FlowGraphCommands.FocusViewport, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::FocusViewport), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanFocusViewport)); - ToolkitCommands->MapAction(FlowGraphCommands.JumpToNodeDefinition, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::JumpToNodeDefinition), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanJumpToNodeDefinition)); + CommandList->MapAction(FlowGraphCommands.JumpToNodeDefinition, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::JumpToNodeDefinition), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanJumpToNodeDefinition)); } FGraphAppearanceInfo SFlowGraphEditor::GetGraphAppearanceInfo() const diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h index eabbc8b82..5debee64c 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h @@ -32,6 +32,8 @@ class FLOWEDITOR_API SFlowGraphEditor : public SGraphEditor TWeakPtr FlowAssetEditor; TSharedPtr DetailsView; + TSharedPtr CommandList; + public: void Construct(const FArguments& InArgs, const TSharedPtr InAssetEditor); From 92f48673ca6a82f51021e5b0915f63daa9d85e4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 26 Mar 2023 20:43:19 +0200 Subject: [PATCH 145/485] #131 fixed updating pin names below removed pin --- Source/Flow/Private/Nodes/FlowNode.cpp | 28 ++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index a588ccbdd..09dbd0655 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -236,28 +236,56 @@ void UFlowNode::RemoveUserInput(const FName& PinName) { Modify(); + int32 RemovedPinIndex = INDEX_NONE; for (int32 i = 0; i < InputPins.Num(); i++) { if (InputPins[i].PinName == PinName) { InputPins.RemoveAt(i); + RemovedPinIndex = i; break; } } + + // update remaining pins + if (RemovedPinIndex > INDEX_NONE) + { + for (int32 i = RemovedPinIndex; i < InputPins.Num(); ++i) + { + if (InputPins[i].PinName.ToString().IsNumeric()) + { + InputPins[i].PinName = *FString::FromInt(i); + } + } + } } void UFlowNode::RemoveUserOutput(const FName& PinName) { Modify(); + int32 RemovedPinIndex = INDEX_NONE; for (int32 i = 0; i < OutputPins.Num(); i++) { if (OutputPins[i].PinName == PinName) { OutputPins.RemoveAt(i); + RemovedPinIndex = i; break; } } + + // update remaining pins + if (RemovedPinIndex > INDEX_NONE) + { + for (int32 i = RemovedPinIndex; i < OutputPins.Num(); ++i) + { + if (OutputPins[i].PinName.ToString().IsNumeric()) + { + OutputPins[i].PinName = *FString::FromInt(i); + } + } + } } #endif From cfbce6fdbc31d4dd4314fc261d1faa7e111b9821 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Mon, 27 Mar 2023 00:33:39 +0200 Subject: [PATCH 146/485] quick CIS compilation fix --- Source/Flow/Public/FlowAsset.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 8c4af61a9..1a6acb976 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -5,11 +5,10 @@ #include "FlowMessageLog.h" #include "FlowSave.h" #include "FlowTypes.h" -#include "Nodes/FlowNode.h" +#include "Nodes/Route/FlowNode_Start.h" #include "FlowAsset.generated.h" class UFlowNode_CustomInput; -class UFlowNode_Start; class UFlowNode_SubGraph; class UFlowSubsystem; From 518ef1f5597dde849433e78b7e79ba894897d982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Mon, 27 Mar 2023 00:42:58 +0200 Subject: [PATCH 147/485] another CIS fix attempt --- Source/Flow/Public/FlowAsset.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 1a6acb976..27aa62fe1 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -5,10 +5,13 @@ #include "FlowMessageLog.h" #include "FlowSave.h" #include "FlowTypes.h" -#include "Nodes/Route/FlowNode_Start.h" +#include "Nodes/FlowNode.h" + +#include "UObject/ObjectKey.h" #include "FlowAsset.generated.h" class UFlowNode_CustomInput; +class UFlowNode_Start; class UFlowNode_SubGraph; class UFlowSubsystem; From 84e2eda0497351fa412453b73c19cc14df8cba47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Thu, 30 Mar 2023 14:02:55 +0200 Subject: [PATCH 148/485] redundant include removed --- Source/FlowEditor/Public/Asset/FlowAssetEditor.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index 0591c9cfd..44645c432 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -9,7 +9,6 @@ #include "UObject/GCObject.h" #include "FlowEditorDefines.h" -#include "FlowTypes.h" class FFlowMessageLog; class SFlowGraphEditor; From 23e88cf595e808ac42423f61547c4dd377fa8780 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Thu, 30 Mar 2023 14:03:49 +0200 Subject: [PATCH 149/485] fixed showing static descriptions in PIE/SIE renamed flag to better reflect its role --- .../FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp | 2 +- Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp | 7 ++++++- Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp | 2 +- Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp index ed81e3d11..c555e64ab 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp @@ -8,7 +8,7 @@ UFlowGraphEditorSettings::UFlowGraphEditorSettings(const FObjectInitializer& Obj : Super(ObjectInitializer) , NodeDoubleClickTarget(EFlowNodeDoubleClickTarget::PrimaryAsset) , bShowNodeClass(false) - , bShowNodeDescriptionInPIE(true) + , bShowNodeDescriptionWhilePlaying(true) , bShowSubGraphPreview(true) , bShowSubGraphPath(true) , SubGraphPreviewSize(FVector2D(640.f, 360.f)) diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 1fc3797c8..83f26e9d9 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -653,7 +653,12 @@ FText UFlowGraphNode::GetTooltipText() const FString UFlowGraphNode::GetNodeDescription() const { - return FlowNode ? FlowNode->GetNodeDescription() : FString(); + if (FlowNode && (GEditor->PlayWorld == nullptr || UFlowGraphEditorSettings::Get()->bShowNodeDescriptionWhilePlaying)) + { + return FlowNode->GetNodeDescription(); + } + + return FString(); } UFlowNode* UFlowGraphNode::GetInspectedNodeInstance() const diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp index 9879c4b3e..bb1bf8b7b 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp @@ -56,7 +56,7 @@ void SFlowGraphNode::Construct(const FArguments& InArgs, UFlowGraphNode* InNode) void SFlowGraphNode::GetNodeInfoPopups(FNodeInfoContext* Context, TArray& Popups) const { - const FString Description = GEditor->PlayWorld && UFlowGraphEditorSettings::Get()->bShowNodeDescriptionInPIE ? FString() : FlowGraphNode->GetNodeDescription(); + const FString& Description = FlowGraphNode->GetNodeDescription(); if (!Description.IsEmpty()) { const FGraphInformationPopupInfo DescriptionPopup = FGraphInformationPopupInfo(nullptr, UFlowGraphSettings::Get()->NodeDescriptionBackground, Description); diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h index 8f2a14961..8a8d10b73 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h @@ -32,7 +32,7 @@ class FLOWEDITOR_API UFlowGraphEditorSettings : public UDeveloperSettings // Hides the node description when you play in editor and only shows node status string. UPROPERTY(config, EditAnywhere, Category = "Nodes") - bool bShowNodeDescriptionInPIE; + bool bShowNodeDescriptionWhilePlaying; // Renders preview of entire graph while hovering over UPROPERTY(config, EditAnywhere, Category = "Nodes") From dfc08eaba99b42c6396006383d7d292ab96ec141 Mon Sep 17 00:00:00 2001 From: Bohdon Sayre Date: Fri, 31 Mar 2023 11:22:44 -0400 Subject: [PATCH 150/485] add null flow asset check when calling StartRootFlow (#132) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Krzysiek Justyński --- Source/Flow/Private/FlowSubsystem.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index 205f87f0f..db5b5562a 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -19,6 +19,8 @@ FNativeFlowAssetEvent UFlowSubsystem::OnInstancedTemplateAdded; FNativeFlowAssetEvent UFlowSubsystem::OnInstancedTemplateRemoved; #endif +#define LOCTEXT_NAMESPACE "FlowSubsystem" + UFlowSubsystem::UFlowSubsystem() : UGameInstanceSubsystem() { @@ -73,6 +75,12 @@ void UFlowSubsystem::AbortActiveFlows() void UFlowSubsystem::StartRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const bool bAllowMultipleInstances /* = true */) { + if (!FlowAsset) + { + FMessageLog("PIE").Error(LOCTEXT("StartRootFlowNullAsset", "Attempted to start Root Flow with null asset.")) + ->AddToken(FUObjectToken::Create(Owner)); + return; + } UFlowAsset* NewFlow = CreateRootFlow(Owner, FlowAsset, bAllowMultipleInstances); if (NewFlow) { @@ -644,3 +652,5 @@ void UFlowSubsystem::FindComponents(const FGameplayTagContainer& Tags, const EGa } } } + +#undef LOCTEXT_NAMESPACE From ac37ae7e50f09e5fb4e4a1ad788dd094a98d6808 Mon Sep 17 00:00:00 2001 From: "Satheesh (ryanjon2040)" Date: Fri, 31 Mar 2023 21:04:36 +0530 Subject: [PATCH 151/485] Add custom color support to nodes (#135) --- Source/Flow/Private/Nodes/FlowNode.cpp | 12 ++++++++++++ Source/Flow/Public/FlowTypes.h | 3 ++- Source/Flow/Public/Nodes/FlowNode.h | 6 +++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 09dbd0655..13728d799 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -40,6 +40,7 @@ UFlowNode::UFlowNode(const FObjectInitializer& ObjectInitializer) #if WITH_EDITOR Category = TEXT("Uncategorized"); NodeStyle = EFlowNodeStyle::Default; + NodeColor = FLinearColor::Black; #endif InputPins = {DefaultInputPin}; @@ -122,6 +123,17 @@ FText UFlowNode::GetNodeToolTip() const return GetClass()->GetToolTipText(); } +bool UFlowNode::GetDynamicTitleColor(FLinearColor& OutColor) const +{ + if (NodeStyle == EFlowNodeStyle::Custom) + { + OutColor = NodeColor; + return true; + } + + return false; +} + FString UFlowNode::GetNodeDescription() const { return K2_GetNodeDescription(); diff --git a/Source/Flow/Public/FlowTypes.h b/Source/Flow/Public/FlowTypes.h index a1be09b7b..2a0f3793b 100644 --- a/Source/Flow/Public/FlowTypes.h +++ b/Source/Flow/Public/FlowTypes.h @@ -14,7 +14,8 @@ enum class EFlowNodeStyle : uint8 InOut UMETA(Hidden), Latent, Logic, - SubGraph UMETA(Hidden) + SubGraph UMETA(Hidden), + Custom }; #endif diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index e7543d435..845529b9b 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -53,6 +53,10 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UPROPERTY(EditDefaultsOnly, Category = "FlowNode") EFlowNodeStyle NodeStyle; + // Set Node Style to custom to use your own color for this node + UPROPERTY(EditDefaultsOnly, Category = "FlowNode", meta = (EditCondition = "NodeStyle == EFlowNodeStyle::Custom")) + FLinearColor NodeColor; + uint8 bCanDelete : 1; uint8 bCanDuplicate : 1; @@ -93,7 +97,7 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte virtual FText GetNodeToolTip() const; // This method allows to have different for every node instance, i.e. Red if node represents enemy, Green if node represents a friend - virtual bool GetDynamicTitleColor(FLinearColor& OutColor) const { return false; } + virtual bool GetDynamicTitleColor(FLinearColor& OutColor) const; EFlowNodeStyle GetNodeStyle() const { return NodeStyle; } From 8e01ec7721e931f94a94ea7abd644b008c00b06c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Fri, 31 Mar 2023 17:35:08 +0200 Subject: [PATCH 152/485] readability tweak --- Source/Flow/Private/FlowSubsystem.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index db5b5562a..34bbfd491 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -75,16 +75,17 @@ void UFlowSubsystem::AbortActiveFlows() void UFlowSubsystem::StartRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const bool bAllowMultipleInstances /* = true */) { - if (!FlowAsset) + if (FlowAsset) { - FMessageLog("PIE").Error(LOCTEXT("StartRootFlowNullAsset", "Attempted to start Root Flow with null asset.")) - ->AddToken(FUObjectToken::Create(Owner)); - return; + if (UFlowAsset* NewFlow = CreateRootFlow(Owner, FlowAsset, bAllowMultipleInstances)) + { + NewFlow->StartFlow(); + } } - UFlowAsset* NewFlow = CreateRootFlow(Owner, FlowAsset, bAllowMultipleInstances); - if (NewFlow) + else { - NewFlow->StartFlow(); + FMessageLog("PIE").Error(LOCTEXT("StartRootFlowNullAsset", "Attempted to start Root Flow with a null asset.")) + ->AddToken(FUObjectToken::Create(Owner)); } } From b9ef944a97ced6906858e7b81d33a240f5dfcfbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Fri, 31 Mar 2023 17:59:54 +0200 Subject: [PATCH 153/485] non-editor compilation fix --- Source/Flow/Private/FlowSubsystem.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index 34bbfd491..847c342d4 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -82,11 +82,13 @@ void UFlowSubsystem::StartRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const NewFlow->StartFlow(); } } +#if WITH_EDITOR else { FMessageLog("PIE").Error(LOCTEXT("StartRootFlowNullAsset", "Attempted to start Root Flow with a null asset.")) ->AddToken(FUObjectToken::Create(Owner)); } +#endif } UFlowAsset* UFlowSubsystem::CreateRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const bool bAllowMultipleInstances) From 3859e351941987d870a689a3db54553511ef6eff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Fri, 31 Mar 2023 22:56:54 +0200 Subject: [PATCH 154/485] UE 5.2 compilation fixes --- Source/Flow/Private/FlowSubsystem.cpp | 2 ++ Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index 847c342d4..78e2eed75 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -15,6 +15,8 @@ #include "UObject/UObjectHash.h" #if WITH_EDITOR +#include "Logging/MessageLog.h" + FNativeFlowAssetEvent UFlowSubsystem::OnInstancedTemplateAdded; FNativeFlowAssetEvent UFlowSubsystem::OnInstancedTemplateRemoved; #endif diff --git a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp index 00c3cce6b..ca261d845 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp @@ -58,7 +58,7 @@ TArray UFlowNode_PlayLevelSequence::GetContextOutputs() Sequence = Sequence.LoadSynchronous(); if (Sequence && Sequence->GetMovieScene()) { - for (const UMovieSceneTrack* Track : Sequence->GetMovieScene()->GetMasterTracks()) + for (const UMovieSceneTrack* Track : Sequence->GetMovieScene()->GetTracks()) { if (Track->GetClass() == UMovieSceneFlowTrack::StaticClass()) { From 4521bbd076e71be22c8311dbbc227d564d50cf2c Mon Sep 17 00:00:00 2001 From: LindyHopperGT <91915878+LindyHopperGT@users.noreply.github.com> Date: Wed, 26 Apr 2023 09:12:30 -0700 Subject: [PATCH 155/485] Added includes to fix compiler errors (#152) When compiling for UE 5.1 (Win64) we ran into some compile errors due to missing header includes. --- Source/Flow/Private/FlowSubsystem.cpp | 1 + Source/Flow/Private/Nodes/FlowNode.cpp | 3 +++ Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp | 1 + Source/FlowEditor/Private/Asset/FlowImportUtils.cpp | 1 + Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp | 1 + Source/FlowEditor/Public/Asset/FlowDebuggerSubsystem.h | 1 + Source/FlowEditor/Public/Asset/FlowImportUtils.h | 1 + 7 files changed, 9 insertions(+) diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index 78e2eed75..9ccb506bf 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -11,6 +11,7 @@ #include "Engine/GameInstance.h" #include "Engine/World.h" +#include "Logging/MessageLog.h" #include "Misc/Paths.h" #include "UObject/UObjectHash.h" diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 13728d799..c9c58c9d0 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -8,6 +8,9 @@ #include "FlowSubsystem.h" #include "FlowTypes.h" +#if WITH_EDITOR +#include "Editor.h" +#endif #include "Engine/Engine.h" #include "Engine/ViewportStatsSubsystem.h" #include "Engine/World.h" diff --git a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp index 222767fa1..1ccee3ac6 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp @@ -11,6 +11,7 @@ #include "Kismet2/DebuggerCommands.h" #include "Misc/Attribute.h" +#include "Misc/MessageDialog.h" #include "Subsystems/AssetEditorSubsystem.h" #include "ToolMenu.h" #include "ToolMenuSection.h" diff --git a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp index 446ff7661..70bc763b2 100644 --- a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp +++ b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp @@ -13,6 +13,7 @@ #include "AssetRegistry/AssetRegistryModule.h" #include "AssetToolsModule.h" +#include "EdGraphSchema_K2.h" #include "EditorAssetLibrary.h" #include "Misc/ScopedSlowTask.h" diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 83f26e9d9..9f79b50b7 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -20,6 +20,7 @@ #include "Editor/EditorEngine.h" #include "Framework/Commands/GenericCommands.h" #include "GraphEditorActions.h" +#include "HAL/FileManager.h" #include "Kismet2/KismetEditorUtilities.h" #include "ScopedTransaction.h" #include "SourceCodeNavigation.h" diff --git a/Source/FlowEditor/Public/Asset/FlowDebuggerSubsystem.h b/Source/FlowEditor/Public/Asset/FlowDebuggerSubsystem.h index da10e3032..dae10ebae 100644 --- a/Source/FlowEditor/Public/Asset/FlowDebuggerSubsystem.h +++ b/Source/FlowEditor/Public/Asset/FlowDebuggerSubsystem.h @@ -3,6 +3,7 @@ #pragma once #include "EditorSubsystem.h" +#include "Logging/TokenizedMessage.h" #include "FlowDebuggerSubsystem.generated.h" class UFlowAsset; diff --git a/Source/FlowEditor/Public/Asset/FlowImportUtils.h b/Source/FlowEditor/Public/Asset/FlowImportUtils.h index 63e4c08e1..de38c1888 100644 --- a/Source/FlowEditor/Public/Asset/FlowImportUtils.h +++ b/Source/FlowEditor/Public/Asset/FlowImportUtils.h @@ -3,6 +3,7 @@ #pragma once #include "FlowAsset.h" +#include "Kismet/BlueprintFunctionLibrary.h" #include "Nodes/FlowPin.h" #include "FlowImportUtils.generated.h" From 87cb725158cd433308f0b35282d1b13c286015f6 Mon Sep 17 00:00:00 2001 From: LindyHopperGT <91915878+LindyHopperGT@users.noreply.github.com> Date: Sun, 30 Apr 2023 05:10:19 -0700 Subject: [PATCH 156/485] Fixed bug where Owner parameter wasn't being used (#153) * Added includes to fix compiler errors When compiling for UE 5.1 (Win64) we ran into some compile errors due to missing header includes. * Fixed bug where Owner parameter wasn't being used Owner was passed into this function, presumably to be used instead of 'this', but it was just using 'this'. Changed the code to use Owner if specified, and default to 'this' otherwise. --- Source/Flow/Private/FlowComponent.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Flow/Private/FlowComponent.cpp b/Source/Flow/Private/FlowComponent.cpp index 8635991ab..6575e49f6 100644 --- a/Source/Flow/Private/FlowComponent.cpp +++ b/Source/Flow/Private/FlowComponent.cpp @@ -385,9 +385,11 @@ void UFlowComponent::FinishRootFlow(UFlowAsset* TemplateAsset, const EFlowFinish TSet UFlowComponent::GetRootInstances(const UObject* Owner) const { + const UObject* OwnerToCheck = IsValid(Owner) ? Owner : this; + if (const UFlowSubsystem* FlowSubsystem = GetFlowSubsystem()) { - return FlowSubsystem->GetRootInstancesByOwner(this); + return FlowSubsystem->GetRootInstancesByOwner(OwnerToCheck); } return TSet(); From b498a0c56d69923f027f06f4ea26901c6d472f7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 30 Apr 2023 14:19:26 +0200 Subject: [PATCH 157/485] comforting convention of separating engine headers from plugin headers --- Source/FlowEditor/Public/Asset/FlowImportUtils.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/FlowEditor/Public/Asset/FlowImportUtils.h b/Source/FlowEditor/Public/Asset/FlowImportUtils.h index de38c1888..183361ff1 100644 --- a/Source/FlowEditor/Public/Asset/FlowImportUtils.h +++ b/Source/FlowEditor/Public/Asset/FlowImportUtils.h @@ -2,8 +2,9 @@ #pragma once -#include "FlowAsset.h" #include "Kismet/BlueprintFunctionLibrary.h" + +#include "FlowAsset.h" #include "Nodes/FlowPin.h" #include "FlowImportUtils.generated.h" From 8c50d93d80bbac5b7c6853d4281441e3ad7cbc80 Mon Sep 17 00:00:00 2001 From: Alex van Mansom <45290811+Vi-So@users.noreply.github.com> Date: Sun, 30 Apr 2023 14:26:41 +0200 Subject: [PATCH 158/485] Extending the "GetAssignedGraphNodeClass()" function to support to return the correc "EdGraphNode" for grand child classes of "UFlowNode" (#151) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously it would return the first found "IsChildOf" and return the EdGraphNode of the foundclass. But lets say we got this situation just for example: UFlowNode->UMyBaseNode UFlowNode->UMyBaseNode->UMyQuestNode Both "UMyBaseNode" and "UMyQuestNode" could be in the list if we want different EdGraphNodes for both of them. If the "UMyQuestNode" goes into the itteration and it would find "UMyBaseNode" first it will be returned without checking the rest, because "UMyQuestNode" is dirived from "UMyBaseNode" (IsChildOf == true) and thus it returns the EdGraphNode of the base node directly. So that would mean I got the wrong EdGraphNode for my special quest node. This modification continues to try and find parent classes and returns the closest parent version. So lets say we have this situation: UFlowNode->UMyBaseNode UFlowNode->UMyBaseNode->UMyQuestNode->MyOtherQuestNode(BP class) Both “UMyBaseNode” and “UMyQuestNode” would be found, but the closest parent to “MyOtherQuestNode” would be returned, in this case the EdGraphNode of “UMyQuestNode” It only does this if we have found 2 or more parents though. If only 1 is found we would return that one, and if the exact class match is found lets say: "UMyQuestNode" finds "UMyQuestNode" it will return the EdGraphNode right away aswell. Hopefully this makes sence :P Otherwise feel free to ask. --- .../Private/Graph/FlowGraphSchema.cpp | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index c4e32b6cb..5465115e7 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -238,15 +238,54 @@ TArray> UFlowGraphSchema::GetFlowNodeCategories() UClass* UFlowGraphSchema::GetAssignedGraphNodeClass(const UClass* FlowNodeClass) { + TArray FoundParentClasses; + UClass* ReturnClass = nullptr; + + // Collect all possible parents and their corresponding GraphNodeClasses for (const TPair& GraphNodeByFlowNode : GraphNodesByFlowNodes) { - if (FlowNodeClass->IsChildOf(GraphNodeByFlowNode.Key)) + if (FlowNodeClass == GraphNodeByFlowNode.Key) { return GraphNodeByFlowNode.Value; } + + if (FlowNodeClass->IsChildOf(GraphNodeByFlowNode.Key)) + { + FoundParentClasses.Add(GraphNodeByFlowNode.Key); + } + } + + // Of only one parent found set the return to its GraphNodeClass + if (FoundParentClasses.Num() == 1) + { + ReturnClass = GraphNodesByFlowNodes.FindRef(FoundParentClasses[0]); } + + // If multiple parents found, find the closest one and set the return to its GraphNodeClass + else if (!FoundParentClasses.IsEmpty()) + { + TPair ClosestParentMatch = {1000, nullptr}; + for (const auto& ParentClass : FoundParentClasses) + { + int32 StepsTillExactMatch = 0; + const UClass* LocalParentClass = FlowNodeClass; + + while (IsValid(LocalParentClass) && LocalParentClass != ParentClass && LocalParentClass != UFlowNode::StaticClass()) + { + StepsTillExactMatch++; + LocalParentClass = LocalParentClass->GetSuperClass(); + } + + if (StepsTillExactMatch != 0 && StepsTillExactMatch < ClosestParentMatch.Key) + { + ClosestParentMatch = {StepsTillExactMatch, ParentClass}; + } + } - return UFlowGraphNode::StaticClass(); + ReturnClass = GraphNodesByFlowNodes.FindRef(ClosestParentMatch.Value); + } + + return IsValid(ReturnClass) ? ReturnClass : UFlowGraphNode::StaticClass(); } void UFlowGraphSchema::ApplyNodeFilter(const UFlowAsset* AssetClassDefaults, const UClass* FlowNodeClass, TArray& FilteredNodes) From 3fb5564db0c5285ac3269b6d5e4e14c1365982ef Mon Sep 17 00:00:00 2001 From: "Satheesh (ryanjon2040)" Date: Sun, 30 Apr 2023 18:33:00 +0530 Subject: [PATCH 159/485] Show pretty readable pin name even if friendly name is not provided (#140) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Show pretty readable pin name even if friendly name is not provided * Add missing else. Silly mistake 😋 --- .../Private/Graph/Nodes/FlowGraphNode.cpp | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 9f79b50b7..28f63727d 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -26,6 +26,7 @@ #include "SourceCodeNavigation.h" #include "Textures/SlateIcon.h" #include "ToolMenuSection.h" +#include "Settings/EditorStyleSettings.h" #define LOCTEXT_NAMESPACE "FlowGraphNode" @@ -775,7 +776,15 @@ void UFlowGraphNode::CreateInputPin(const FFlowPin& FlowPin, const int32 Index / UEdGraphPin* NewPin = CreatePin(EGPD_Input, PinType, FlowPin.PinName, Index); check(NewPin); - if (!FlowPin.PinFriendlyName.IsEmpty()) + if (FlowPin.PinFriendlyName.IsEmpty()) + { + if (GetDefault()->bShowFriendlyNames) + { + NewPin->bAllowFriendlyName = true; + NewPin->PinFriendlyName = FText::FromString(FName::NameToDisplayString(FlowPin.PinName.ToString(), false)); + } + } + else { NewPin->bAllowFriendlyName = true; NewPin->PinFriendlyName = FlowPin.PinFriendlyName; @@ -797,7 +806,15 @@ void UFlowGraphNode::CreateOutputPin(const FFlowPin& FlowPin, const int32 Index UEdGraphPin* NewPin = CreatePin(EGPD_Output, PinType, FlowPin.PinName, Index); check(NewPin); - if (!FlowPin.PinFriendlyName.IsEmpty()) + if (FlowPin.PinFriendlyName.IsEmpty()) + { + if (GetDefault()->bShowFriendlyNames) + { + NewPin->bAllowFriendlyName = true; + NewPin->PinFriendlyName = FText::FromString(FName::NameToDisplayString(FlowPin.PinName.ToString(), false)); + } + } + else { NewPin->bAllowFriendlyName = true; NewPin->PinFriendlyName = FlowPin.PinFriendlyName; From 61cbc2f582786dceae24e8464c8c068b9eeff546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 30 Apr 2023 15:04:47 +0200 Subject: [PATCH 160/485] cosmetic tweaks of last PR --- Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index 5465115e7..5128357da 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -2,7 +2,6 @@ #include "Graph/FlowGraphSchema.h" -#include "Asset/FlowAssetEditor.h" #include "Graph/FlowGraph.h" #include "Graph/FlowGraphEditor.h" #include "Graph/FlowGraphSchema_Actions.h" @@ -248,7 +247,7 @@ UClass* UFlowGraphSchema::GetAssignedGraphNodeClass(const UClass* FlowNodeClass) { return GraphNodeByFlowNode.Value; } - + if (FlowNodeClass->IsChildOf(GraphNodeByFlowNode.Key)) { FoundParentClasses.Add(GraphNodeByFlowNode.Key); @@ -260,22 +259,21 @@ UClass* UFlowGraphSchema::GetAssignedGraphNodeClass(const UClass* FlowNodeClass) { ReturnClass = GraphNodesByFlowNodes.FindRef(FoundParentClasses[0]); } - // If multiple parents found, find the closest one and set the return to its GraphNodeClass - else if (!FoundParentClasses.IsEmpty()) + else if (FoundParentClasses.Num() > 1) { TPair ClosestParentMatch = {1000, nullptr}; for (const auto& ParentClass : FoundParentClasses) { int32 StepsTillExactMatch = 0; const UClass* LocalParentClass = FlowNodeClass; - + while (IsValid(LocalParentClass) && LocalParentClass != ParentClass && LocalParentClass != UFlowNode::StaticClass()) { StepsTillExactMatch++; LocalParentClass = LocalParentClass->GetSuperClass(); } - + if (StepsTillExactMatch != 0 && StepsTillExactMatch < ClosestParentMatch.Key) { ClosestParentMatch = {StepsTillExactMatch, ParentClass}; @@ -284,7 +282,7 @@ UClass* UFlowGraphSchema::GetAssignedGraphNodeClass(const UClass* FlowNodeClass) ReturnClass = GraphNodesByFlowNodes.FindRef(ClosestParentMatch.Value); } - + return IsValid(ReturnClass) ? ReturnClass : UFlowGraphNode::StaticClass(); } From c8ed5c1cc6d330f886d26c7a3cdd56f89e6a0538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 30 Apr 2023 15:47:44 +0200 Subject: [PATCH 161/485] rework of PR #140 - added bEnforceFriendlyPinNames as separate flag from the Unreal editor config - moved creating a display name to `UFlowGraphSchema::GetPinDisplayName` without modifying graph node's instance --- .../Private/Graph/FlowGraphEditorSettings.cpp | 3 +- .../Private/Graph/FlowGraphSchema.cpp | 35 +++++++++++++++++++ .../Private/Graph/Nodes/FlowGraphNode.cpp | 21 ++--------- .../Private/Graph/Widgets/SFlowGraphNode.cpp | 1 - .../Public/Graph/FlowGraphEditorSettings.h | 6 +++- .../FlowEditor/Public/Graph/FlowGraphSchema.h | 1 + 6 files changed, 44 insertions(+), 23 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp index c555e64ab..0ecffa9fc 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp @@ -2,13 +2,12 @@ #include "Graph/FlowGraphEditorSettings.h" -#include "FlowAsset.h" - UFlowGraphEditorSettings::UFlowGraphEditorSettings(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , NodeDoubleClickTarget(EFlowNodeDoubleClickTarget::PrimaryAsset) , bShowNodeClass(false) , bShowNodeDescriptionWhilePlaying(true) + , bEnforceFriendlyPinNames(false) , bShowSubGraphPreview(true) , bShowSubGraphPath(true) , SubGraphPreviewSize(FVector2D(640.f, 360.f)) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index 5128357da..bcd7ce402 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -4,6 +4,7 @@ #include "Graph/FlowGraph.h" #include "Graph/FlowGraphEditor.h" +#include "Graph/FlowGraphEditorSettings.h" #include "Graph/FlowGraphSchema_Actions.h" #include "Graph/FlowGraphSettings.h" #include "Graph/FlowGraphUtils.h" @@ -146,6 +147,40 @@ FLinearColor UFlowGraphSchema::GetPinTypeColor(const FEdGraphPinType& PinType) c return FLinearColor::White; } +FText UFlowGraphSchema::GetPinDisplayName(const UEdGraphPin* Pin) const +{ + FText ResultPinName; + check(Pin != nullptr); + if (Pin->PinFriendlyName.IsEmpty()) + { + // We don't want to display "None" for no name + if (Pin->PinName.IsNone()) + { + return FText::GetEmpty(); + } + if (GetDefault()->bEnforceFriendlyPinNames) // this option is only difference between this override and UEdGraphSchema::GetPinDisplayName + { + ResultPinName = FText::FromString(FName::NameToDisplayString(Pin->PinName.ToString(), true)); + } + else + { + ResultPinName = FText::FromName(Pin->PinName); + } + } + else + { + ResultPinName = Pin->PinFriendlyName; + + bool bShouldUseLocalizedNodeAndPinNames = false; + GConfig->GetBool(TEXT("Internationalization"), TEXT("ShouldUseLocalizedNodeAndPinNames"), bShouldUseLocalizedNodeAndPinNames, GEditorSettingsIni); + if (!bShouldUseLocalizedNodeAndPinNames) + { + ResultPinName = FText::FromString(ResultPinName.BuildSourceString()); + } + } + return ResultPinName; +} + void UFlowGraphSchema::BreakNodeLinks(UEdGraphNode& TargetNode) const { Super::BreakNodeLinks(TargetNode); diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 28f63727d..9f79b50b7 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -26,7 +26,6 @@ #include "SourceCodeNavigation.h" #include "Textures/SlateIcon.h" #include "ToolMenuSection.h" -#include "Settings/EditorStyleSettings.h" #define LOCTEXT_NAMESPACE "FlowGraphNode" @@ -776,15 +775,7 @@ void UFlowGraphNode::CreateInputPin(const FFlowPin& FlowPin, const int32 Index / UEdGraphPin* NewPin = CreatePin(EGPD_Input, PinType, FlowPin.PinName, Index); check(NewPin); - if (FlowPin.PinFriendlyName.IsEmpty()) - { - if (GetDefault()->bShowFriendlyNames) - { - NewPin->bAllowFriendlyName = true; - NewPin->PinFriendlyName = FText::FromString(FName::NameToDisplayString(FlowPin.PinName.ToString(), false)); - } - } - else + if (!FlowPin.PinFriendlyName.IsEmpty()) { NewPin->bAllowFriendlyName = true; NewPin->PinFriendlyName = FlowPin.PinFriendlyName; @@ -806,15 +797,7 @@ void UFlowGraphNode::CreateOutputPin(const FFlowPin& FlowPin, const int32 Index UEdGraphPin* NewPin = CreatePin(EGPD_Output, PinType, FlowPin.PinName, Index); check(NewPin); - if (FlowPin.PinFriendlyName.IsEmpty()) - { - if (GetDefault()->bShowFriendlyNames) - { - NewPin->bAllowFriendlyName = true; - NewPin->PinFriendlyName = FText::FromString(FName::NameToDisplayString(FlowPin.PinName.ToString(), false)); - } - } - else + if (!FlowPin.PinFriendlyName.IsEmpty()) { NewPin->bAllowFriendlyName = true; NewPin->PinFriendlyName = FlowPin.PinFriendlyName; diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp index bb1bf8b7b..c5508d109 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp @@ -21,7 +21,6 @@ #include "SNodePanel.h" #include "Styling/SlateColor.h" #include "TutorialMetaData.h" -#include "Graph/FlowGraphEditorSettings.h" #include "Widgets/Images/SImage.h" #include "Widgets/Input/SButton.h" #include "Widgets/Layout/SBorder.h" diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h index 8a8d10b73..54bd78465 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h @@ -30,10 +30,14 @@ class FLOWEDITOR_API UFlowGraphEditorSettings : public UDeveloperSettings UPROPERTY(config, EditAnywhere, Category = "Nodes") bool bShowNodeClass; - // Hides the node description when you play in editor and only shows node status string. + // Shows the node description when you play in editor UPROPERTY(config, EditAnywhere, Category = "Nodes") bool bShowNodeDescriptionWhilePlaying; + // Pin names will will be displayed in a format that is easier to read, even if PinFriendlyName wasn't set + UPROPERTY(EditAnywhere, config, Category = "Nodes") + bool bEnforceFriendlyPinNames; + // Renders preview of entire graph while hovering over UPROPERTY(config, EditAnywhere, Category = "Nodes") bool bShowSubGraphPreview; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h index 8ceb89ff6..099700bc4 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h @@ -36,6 +36,7 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema virtual bool TryCreateConnection(UEdGraphPin* A, UEdGraphPin* B) const override; virtual bool ShouldHidePinDefaultValue(UEdGraphPin* Pin) const override; virtual FLinearColor GetPinTypeColor(const FEdGraphPinType& PinType) const override; + virtual FText GetPinDisplayName(const UEdGraphPin* Pin) const override; virtual void BreakNodeLinks(UEdGraphNode& TargetNode) const override; virtual void BreakPinLinks(UEdGraphPin& TargetPin, bool bSendsNodeNotification) const override; virtual int32 GetNodeSelectionCount(const UEdGraph* Graph) const override; From b6ea67a611b0b27fa4eed6a928088647a621f229 Mon Sep 17 00:00:00 2001 From: LindyHopperGT <91915878+LindyHopperGT@users.noreply.github.com> Date: Mon, 8 May 2023 09:16:46 -0700 Subject: [PATCH 162/485] CustomInput & Output Details Customization and other cleanup (#154) * Added includes to fix compiler errors When compiling for UE 5.1 (Win64) we ran into some compile errors due to missing header includes. * Fixed bug where Owner parameter wasn't being used Owner was passed into this function, presumably to be used instead of 'this', but it was just using 'this'. Changed the code to use Owner if specified, and default to 'this' otherwise. * CustomInput & Output Details Customization and other cleanup * Added forward declarations & includes that were needed to compile * Changed some functions to return const &'s rather than doing a full deep copy of a member container * Explicitly calling MoveTemp() to use move semantics (supported by UE container classes (eg TArray)) to move the array without an extra copy. Some compilers might be able to do this optimization automatically, but being explicit here. * Introduced a superclass to UFlowNode_CustomInput and Output. The diff is easier if you compare them to the new superclass, since most of the code moved into it. * The "details" superclass for CustomInput/Output fixes a discovered bug where the list of EventNames was not recaching correctly. * Moved a bit of code from PlayLevelSequence to FlowNode.h (TryGetRootFlowActorOwner) so that it can be used in other contexts. Also provided a component version of this same code. * The FlowGraphSchema will now create default nodes for any CustomInputs that exist when the asset is first created. * Added a UseAdaptiveNodeTitles option to optionally make CustomInputs integrate their EventName into the title for the node. This defaults to false (to preserve previous behavior by default). * Exposed CustomInput Add/Remove functions on UFlowNode to allow subclasses to modify the CustomInputs array. * Additional includes for compiler errors * Update FlowAsset.cpp non-editor configurations compile fix for previous changelist. * Removing MoveTemp for local variables According to this guidance, it's no longer a performance improvement. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#f48-dont-return-stdmovelocal --- Source/Flow/Private/FlowAsset.cpp | 17 +- Source/Flow/Private/FlowSettings.cpp | 1 + Source/Flow/Private/Nodes/FlowNode.cpp | 41 +++++ .../Nodes/Route/FlowNode_CustomInput.cpp | 29 ++-- .../Nodes/Route/FlowNode_CustomNodeBase.cpp | 56 +++++++ .../Nodes/Route/FlowNode_CustomOutput.cpp | 31 ++-- .../World/FlowNode_PlayLevelSequence.cpp | 16 +- Source/Flow/Public/FlowAsset.h | 14 +- Source/Flow/Public/FlowComponent.h | 2 +- Source/Flow/Public/FlowSettings.h | 5 + Source/Flow/Public/FlowSubsystem.h | 2 +- Source/Flow/Public/Nodes/FlowNode.h | 15 +- .../Public/Nodes/Route/FlowNode_CustomInput.h | 11 +- .../Nodes/Route/FlowNode_CustomNodeBase.h | 30 ++++ .../Nodes/Route/FlowNode_CustomOutput.h | 11 +- .../Flow/Public/Nodes/Route/FlowNode_Start.h | 2 + .../Private/Asset/FlowAssetFactory.cpp | 3 +- .../Private/Asset/FlowImportUtils.cpp | 1 + .../FlowNode_CustomInputDetails.cpp | 92 ++--------- .../FlowNode_CustomNodeBaseDetails.cpp | 152 ++++++++++++++++++ .../FlowNode_CustomOutputDetails.cpp | 84 ++-------- .../Private/Graph/FlowGraphEditor.cpp | 8 + .../Private/Graph/FlowGraphSchema.cpp | 41 ++++- .../FlowEditor/Public/Asset/FlowImportUtils.h | 1 + .../FlowNode_CustomInputDetails.h | 18 +-- .../FlowNode_CustomNodeBaseDetails.h | 40 +++++ .../FlowNode_CustomOutputDetails.h | 18 +-- .../FlowEditor/Public/Graph/FlowGraphEditor.h | 1 + .../FlowEditor/Public/Graph/FlowGraphSchema.h | 5 + .../FlowEditor/Public/Graph/FlowGraphUtils.h | 2 + 30 files changed, 500 insertions(+), 249 deletions(-) create mode 100644 Source/Flow/Private/Nodes/Route/FlowNode_CustomNodeBase.cpp create mode 100644 Source/Flow/Public/Nodes/Route/FlowNode_CustomNodeBase.h create mode 100644 Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomNodeBaseDetails.cpp create mode 100644 Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomNodeBaseDetails.h diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 2dea57c62..0bb41ef19 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -189,7 +189,22 @@ void UFlowAsset::HarvestNodeConnections() } } } -#endif + +#endif // WITH_EDITOR + +void UFlowAsset::AddCustomInput(const FName& InName) +{ + check(!CustomInputs.Contains(InName)); + + CustomInputs.Add(InName); +} + +void UFlowAsset::RemoveCustomInput(const FName& InName) +{ + check(CustomInputs.Contains(InName)); + + CustomInputs.Remove(InName); +} UFlowNode_Start* UFlowAsset::GetStartNode() const { diff --git a/Source/Flow/Private/FlowSettings.cpp b/Source/Flow/Private/FlowSettings.cpp index b670a1bec..9c1070e4a 100644 --- a/Source/Flow/Private/FlowSettings.cpp +++ b/Source/Flow/Private/FlowSettings.cpp @@ -8,5 +8,6 @@ UFlowSettings::UFlowSettings(const FObjectInitializer& ObjectInitializer) , bWarnAboutMissingIdentityTags(true) , bLogOnSignalDisabled(true) , bLogOnSignalPassthrough(true) + , bUseAdaptiveNodeTitles(false) { } diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index c9c58c9d0..e6523372e 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -8,12 +8,14 @@ #include "FlowSubsystem.h" #include "FlowTypes.h" +#include "Components/ActorComponent.h" #if WITH_EDITOR #include "Editor.h" #endif #include "Engine/Engine.h" #include "Engine/ViewportStatsSubsystem.h" #include "Engine/World.h" +#include "GameFramework/Actor.h" #include "Misc/App.h" #include "Misc/Paths.h" #include "Serialization/MemoryReader.h" @@ -148,6 +150,45 @@ UFlowAsset* UFlowNode::GetFlowAsset() const return GetOuter() ? Cast(GetOuter()) : nullptr; } +AActor* UFlowNode::TryGetRootFlowActorOwner() const +{ + AActor* OwningActor = nullptr; + + UObject* RootFlowOwner = TryGetRootFlowObjectOwner(); + + if (IsValid(RootFlowOwner)) + { + // Check if the immediate parent is an AActor + OwningActor = Cast(RootFlowOwner); + + if (!IsValid(OwningActor)) + { + // Check if the if the immediate parent is an UActorComponent + // and return that Component's Owning actor + if (const UActorComponent* OwningComponent = Cast(RootFlowOwner)) + { + OwningActor = OwningComponent->GetOwner(); + } + } + } + + return OwningActor; +} + +UObject* UFlowNode::TryGetRootFlowObjectOwner() const +{ + const UFlowAsset* FlowAsset = GetFlowAsset(); + + if (IsValid(FlowAsset)) + { + return FlowAsset->GetOwner(); + } + else + { + return nullptr; + } +} + void UFlowNode::AddInputPins(TArray Pins) { for (const FFlowPin& Pin : Pins) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp index e7fa8c5ec..59982dc64 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp @@ -1,17 +1,14 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "Nodes/Route/FlowNode_CustomInput.h" +#include "FlowSettings.h" + +#define LOCTEXT_NAMESPACE "FlowNode" UFlowNode_CustomInput::UFlowNode_CustomInput(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { -#if WITH_EDITOR - Category = TEXT("Route"); - NodeStyle = EFlowNodeStyle::InOut; -#endif - InputPins.Empty(); - AllowedSignalModes = {EFlowSignalMode::Enabled, EFlowSignalMode::Disabled}; } void UFlowNode_CustomInput::ExecuteInput(const FName& PinName) @@ -20,19 +17,19 @@ void UFlowNode_CustomInput::ExecuteInput(const FName& PinName) } #if WITH_EDITOR -FString UFlowNode_CustomInput::GetNodeDescription() const +FText UFlowNode_CustomInput::GetNodeTitle() const { - return EventName.ToString(); -} + const bool bUseAdaptiveNodeTitles = UFlowSettings::Get()->bUseAdaptiveNodeTitles; -EDataValidationResult UFlowNode_CustomInput::ValidateNode() -{ - if (EventName.IsNone()) + if (bUseAdaptiveNodeTitles && !EventName.IsNone()) { - ValidationLog.Error(TEXT("Event Name is empty!"), this); - return EDataValidationResult::Invalid; + return FText::Format(LOCTEXT("CustomInputTitle", "{0} Input"), { FText::FromString(EventName.ToString()) }); + } + else + { + return Super::GetNodeTitle(); } - - return EDataValidationResult::Valid; } #endif + +#undef LOCTEXT_NAMESPACE diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_CustomNodeBase.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_CustomNodeBase.cpp new file mode 100644 index 000000000..b38f67786 --- /dev/null +++ b/Source/Flow/Private/Nodes/Route/FlowNode_CustomNodeBase.cpp @@ -0,0 +1,56 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Nodes/Route/FlowNode_CustomNodeBase.h" +#include "FlowSettings.h" + +UFlowNode_CustomNodeBase::UFlowNode_CustomNodeBase(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ +#if WITH_EDITOR + Category = TEXT("Route"); + NodeStyle = EFlowNodeStyle::InOut; +#endif + + AllowedSignalModes = {EFlowSignalMode::Enabled, EFlowSignalMode::Disabled}; +} + +void UFlowNode_CustomNodeBase::SetEventName(const FName& InEventName) +{ + if (EventName != InEventName) + { + EventName = InEventName; + +#if WITH_EDITOR + // Must reconstruct the Graph Visuals if anything that is included in AdaptiveNodeTitles changes + OnReconstructionRequested.ExecuteIfBound(); +#endif // WITH_EDITOR + } +} + +#if WITH_EDITOR + +FString UFlowNode_CustomNodeBase::GetNodeDescription() const +{ + const bool bUseAdaptiveNodeTitles = UFlowSettings::Get()->bUseAdaptiveNodeTitles; + + if (bUseAdaptiveNodeTitles) + { + return Super::GetNodeDescription(); + } + else + { + return EventName.ToString(); + } +} + +EDataValidationResult UFlowNode_CustomNodeBase::ValidateNode() +{ + if (EventName.IsNone()) + { + ValidationLog.Error(TEXT("Event Name is empty!"), this); + return EDataValidationResult::Invalid; + } + + return EDataValidationResult::Valid; +} +#endif diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp index b53cbfc31..bab95e170 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp @@ -1,20 +1,15 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "Nodes/Route/FlowNode_CustomOutput.h" - #include "FlowAsset.h" -#include "Nodes/Route/FlowNode_SubGraph.h" +#include "FlowSettings.h" + +#define LOCTEXT_NAMESPACE "FlowNode" UFlowNode_CustomOutput::UFlowNode_CustomOutput(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { -#if WITH_EDITOR - Category = TEXT("Route"); - NodeStyle = EFlowNodeStyle::InOut; -#endif - OutputPins.Empty(); - AllowedSignalModes = {EFlowSignalMode::Enabled, EFlowSignalMode::Disabled}; } void UFlowNode_CustomOutput::ExecuteInput(const FName& PinName) @@ -26,19 +21,19 @@ void UFlowNode_CustomOutput::ExecuteInput(const FName& PinName) } #if WITH_EDITOR -FString UFlowNode_CustomOutput::GetNodeDescription() const +FText UFlowNode_CustomOutput::GetNodeTitle() const { - return EventName.ToString(); -} + const bool bUseAdaptiveNodeTitles = UFlowSettings::Get()->bUseAdaptiveNodeTitles; -EDataValidationResult UFlowNode_CustomOutput::ValidateNode() -{ - if (EventName.IsNone()) + if (bUseAdaptiveNodeTitles && !EventName.IsNone()) { - ValidationLog.Error(TEXT("Event Name is empty!"), this); - return EDataValidationResult::Invalid; + return FText::Format(LOCTEXT("CustomOutputTitle", "{0} Output"), { FText::FromString(EventName.ToString()) }); + } + else + { + return Super::GetNodeTitle(); } - - return EDataValidationResult::Valid; } #endif + +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp index ca261d845..b8d3e246d 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp @@ -132,21 +132,7 @@ void UFlowNode_PlayLevelSequence::CreatePlayer() { ALevelSequenceActor* SequenceActor; - AActor* OwningActor = nullptr; - if (GetFlowAsset()) - { - if (UObject* RootFlowOwner = GetFlowAsset()->GetOwner()) - { - OwningActor = Cast(RootFlowOwner); // in case Root Flow was created directly from some actor - if (OwningActor == nullptr) - { - if (const UActorComponent* OwningComponent = Cast(RootFlowOwner)) - { - OwningActor = OwningComponent->GetOwner(); - } - } - } - } + AActor* OwningActor = TryGetRootFlowActorOwner(); // Apply AActor::CustomTimeDilation from owner of the Root Flow if (IsValid(OwningActor)) diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 27aa62fe1..86d8b8219 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -133,7 +133,7 @@ class FLOW_API UFlowAsset : public UObject void HarvestNodeConnections(); #endif - TMap GetNodes() const { return Nodes; } + const TMap& GetNodes() const { return Nodes; } UFlowNode* GetNode(const FGuid& Guid) const { return Nodes.FindRef(Guid); } template @@ -184,8 +184,12 @@ class FLOW_API UFlowAsset : public UObject } public: - TArray GetCustomInputs() const { return CustomInputs; } - TArray GetCustomOutputs() const { return CustomOutputs; } + const TArray& GetCustomInputs() const { return CustomInputs; } + const TArray& GetCustomOutputs() const { return CustomOutputs; } + +protected: + void AddCustomInput(const FName& InName); + void RemoveCustomInput(const FName& InName); ////////////////////////////////////////////////////////////////////////// // Instances of the template asset @@ -318,11 +322,11 @@ class FLOW_API UFlowAsset : public UObject // Returns nodes that have any work left, not marked as Finished yet UFUNCTION(BlueprintPure, Category = "Flow") - TArray GetActiveNodes() const { return ActiveNodes; } + const TArray& GetActiveNodes() const { return ActiveNodes; } // Returns nodes active in the past, done their work UFUNCTION(BlueprintPure, Category = "Flow") - TArray GetRecordedNodes() const { return RecordedNodes; } + const TArray& GetRecordedNodes() const { return RecordedNodes; } ////////////////////////////////////////////////////////////////////////// // SaveGame support diff --git a/Source/Flow/Public/FlowComponent.h b/Source/Flow/Public/FlowComponent.h index abc83704a..f815e9cd5 100644 --- a/Source/Flow/Public/FlowComponent.h +++ b/Source/Flow/Public/FlowComponent.h @@ -109,7 +109,7 @@ class FLOW_API UFlowComponent : public UActorComponent FGameplayTagContainer RecentlySentNotifyTags; public: - FGameplayTagContainer GetRecentlySentNotifyTags() const { return RecentlySentNotifyTags; } + const FGameplayTagContainer& GetRecentlySentNotifyTags() const { return RecentlySentNotifyTags; } // Send single notification from the actor to Flow graphs // If set on server, it always going to be replicated to clients diff --git a/Source/Flow/Public/FlowSettings.h b/Source/Flow/Public/FlowSettings.h index 9ca7c3824..a206b4640 100644 --- a/Source/Flow/Public/FlowSettings.h +++ b/Source/Flow/Public/FlowSettings.h @@ -37,4 +37,9 @@ class FLOW_API UFlowSettings : public UDeveloperSettings // If enabled, runtime logs will be added when a flow node signal mode is set to Pass-through UPROPERTY(Config, EditAnywhere, Category = "Flow") bool bLogOnSignalPassthrough; + + // Adjust the Titles for FlowNodes to be more expressive than default + // by incorporating data that would otherwise go in the Description + UPROPERTY(EditAnywhere, config, Category = "Nodes") + bool bUseAdaptiveNodeTitles; }; diff --git a/Source/Flow/Public/FlowSubsystem.h b/Source/Flow/Public/FlowSubsystem.h index 2255519de..3a3b5b47d 100644 --- a/Source/Flow/Public/FlowSubsystem.h +++ b/Source/Flow/Public/FlowSubsystem.h @@ -114,7 +114,7 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem /* Returns assets instanced by Sub Graph nodes */ UFUNCTION(BlueprintPure, Category = "FlowSubsystem") - TMap GetInstancedSubFlows() const { return InstancedSubFlows; } + const TMap& GetInstancedSubFlows() const { return InstancedSubFlows; } virtual UWorld* GetWorld() const override; diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 845529b9b..0d6478c96 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -44,7 +44,10 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte #if WITH_EDITORONLY_DATA protected: + UPROPERTY() TArray> AllowedAssetClasses; + + UPROPERTY() TArray> DeniedAssetClasses; UPROPERTY() @@ -121,7 +124,15 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UFUNCTION(BlueprintPure, Category = "FlowNode") UFlowAsset* GetFlowAsset() const; + // Gets the Owning Actor for this Node's RootFlow + // (if the immediate parent is an UActorComponent, it will get that Component's actor) + AActor* TryGetRootFlowActorOwner() const; + protected: + + // Gets the Owning Object for this Node's RootFlow + UObject* TryGetRootFlowObjectOwner() const; + virtual bool CanFinishGraph() const { return false; } protected: @@ -163,8 +174,8 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte uint8 CountNumberedInputs() const; uint8 CountNumberedOutputs() const; - TArray GetInputPins() const { return InputPins; } - TArray GetOutputPins() const { return OutputPins; } + const TArray& GetInputPins() const { return InputPins; } + const TArray& GetOutputPins() const { return OutputPins; } public: UFUNCTION(BlueprintPure, Category = "FlowNode") diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_CustomInput.h b/Source/Flow/Public/Nodes/Route/FlowNode_CustomInput.h index e1919866e..28a50e134 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_CustomInput.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_CustomInput.h @@ -2,26 +2,25 @@ #pragma once -#include "Nodes/FlowNode.h" +#include "FlowNode_CustomNodeBase.h" + #include "FlowNode_CustomInput.generated.h" /** * Triggers output upon activation of Input (matching this EventName) on the SubGraph node containing this graph */ UCLASS(NotBlueprintable, meta = (DisplayName = "Custom Input")) -class FLOW_API UFlowNode_CustomInput : public UFlowNode +class FLOW_API UFlowNode_CustomInput : public UFlowNode_CustomNodeBase { GENERATED_UCLASS_BODY() - UPROPERTY() - FName EventName; + friend class UFlowAsset; protected: virtual void ExecuteInput(const FName& PinName) override; #if WITH_EDITOR public: - virtual FString GetNodeDescription() const override; - virtual EDataValidationResult ValidateNode() override; + virtual FText GetNodeTitle() const override; #endif }; diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_CustomNodeBase.h b/Source/Flow/Public/Nodes/Route/FlowNode_CustomNodeBase.h new file mode 100644 index 000000000..fb5b76974 --- /dev/null +++ b/Source/Flow/Public/Nodes/Route/FlowNode_CustomNodeBase.h @@ -0,0 +1,30 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Nodes/FlowNode.h" + +#include "FlowNode_CustomNodeBase.generated.h" + +/** + * Base-class for CustomInput and CustomOutput node types + */ +UCLASS(Abstract, NotBlueprintable) +class FLOW_API UFlowNode_CustomNodeBase : public UFlowNode +{ + GENERATED_UCLASS_BODY() + +protected: + UPROPERTY() + FName EventName; + +public: + void SetEventName(const FName& InEventName); + const FName& GetEventName() const { return EventName; } + +#if WITH_EDITOR +public: + virtual FString GetNodeDescription() const override; + virtual EDataValidationResult ValidateNode() override; +#endif +}; diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_CustomOutput.h b/Source/Flow/Public/Nodes/Route/FlowNode_CustomOutput.h index c2624cb2f..e6d3dee53 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_CustomOutput.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_CustomOutput.h @@ -2,7 +2,8 @@ #pragma once -#include "Nodes/FlowNode.h" +#include "FlowNode_CustomNodeBase.h" + #include "FlowNode_CustomOutput.generated.h" /** @@ -10,18 +11,14 @@ * Triggered output name matches EventName selected on this node */ UCLASS(NotBlueprintable, meta = (DisplayName = "Custom Output")) -class FLOW_API UFlowNode_CustomOutput final : public UFlowNode +class FLOW_API UFlowNode_CustomOutput final : public UFlowNode_CustomNodeBase { GENERATED_UCLASS_BODY() - UPROPERTY() - FName EventName; - protected: virtual void ExecuteInput(const FName& PinName) override; #if WITH_EDITOR - virtual FString GetNodeDescription() const override; - virtual EDataValidationResult ValidateNode() override; + virtual FText GetNodeTitle() const override; #endif }; diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_Start.h b/Source/Flow/Public/Nodes/Route/FlowNode_Start.h index 9a0a09525..803cf42df 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_Start.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_Start.h @@ -13,6 +13,8 @@ class FLOW_API UFlowNode_Start : public UFlowNode { GENERATED_UCLASS_BODY() + friend class UFlowAsset; + protected: virtual void ExecuteInput(const FName& PinName) override; }; diff --git a/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp b/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp index 65ab06e68..d111fa79b 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp @@ -9,6 +9,7 @@ #include "ClassViewerModule.h" #include "Kismet2/KismetEditorUtilities.h" #include "Kismet2/SClassPickerDialog.h" +#include "Modules/ModuleManager.h" #define LOCTEXT_NAMESPACE "FlowAssetFactory" @@ -116,4 +117,4 @@ UObject* UFlowAssetFactory::FactoryCreateNew(UClass* Class, UObject* InParent, F return NewFlowAsset; } -#undef LOCTEXT_NAMESPACE \ No newline at end of file +#undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp index 70bc763b2..5886b64f1 100644 --- a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp +++ b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp @@ -3,6 +3,7 @@ #include "Asset/FlowImportUtils.h" #include "Asset/FlowAssetFactory.h" +#include "FlowEditorDefines.h" #include "FlowEditorModule.h" #include "Graph/FlowGraphSchema_Actions.h" #include "Graph/FlowGraph.h" diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomInputDetails.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomInputDetails.cpp index ca37aa4fb..5bcb3b60c 100644 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomInputDetails.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomInputDetails.cpp @@ -1,101 +1,35 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "DetailCustomizations/FlowNode_CustomInputDetails.h" +#include "DetailLayoutBuilder.h" #include "FlowAsset.h" -#include "Nodes/Route/FlowNode_CustomInput.h" - -#include "DetailCategoryBuilder.h" -#include "DetailWidgetRow.h" -#include "PropertyEditing.h" -#include "Widgets/Input/SComboBox.h" -#include "Widgets/Text/STextBlock.h" #define LOCTEXT_NAMESPACE "FlowNode_CustomInputDetails" -void FFlowNode_CustomInputDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) +FFlowNode_CustomInputDetails::FFlowNode_CustomInputDetails() { - DetailLayout.GetObjectsBeingCustomized(ObjectsBeingEdited); - GetEventNames(); - - IDetailCategoryBuilder& Category = DetailLayout.EditCategory("CustomInput", LOCTEXT("CustomInputCategory", "Custom Event")); - Category.AddCustomRow(LOCTEXT("CustomRowName", "Event Name")) - .NameContent() - [ - SNew(STextBlock) - .Text(LOCTEXT("EventName", "Event Name")) - ] - .ValueContent() - .HAlign(HAlign_Fill) - [ - SNew(SComboBox>) - .OptionsSource(&EventNames) - .OnGenerateWidget(this, &FFlowNode_CustomInputDetails::GenerateEventWidget) - .OnSelectionChanged(this, &FFlowNode_CustomInputDetails::PinSelectionChanged) - [ - SNew(STextBlock) - .Text(this, &FFlowNode_CustomInputDetails::GetSelectedEventText) - ] - ]; + bExcludeReferencedEvents = true; } -void FFlowNode_CustomInputDetails::GetEventNames() +void FFlowNode_CustomInputDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) { - EventNames.Empty(); - EventNames.Emplace(MakeShareable(new FName(NAME_None))); - - if (ObjectsBeingEdited[0].IsValid() && ObjectsBeingEdited[0].Get()->GetOuter()) - { - const UFlowAsset* FlowAsset = Cast(ObjectsBeingEdited[0].Get()->GetOuter()); - TArray SortedNames = FlowAsset->GetCustomInputs(); - - for (const TPair& Node : FlowAsset->GetNodes()) - { - if (Node.Value->GetClass()->IsChildOf(UFlowNode_CustomInput::StaticClass())) - { - SortedNames.Remove(Cast(Node.Value)->EventName); - } - } + // For backward compatability, these localized texts are in FlowNode_CustomInputDetails, + // not FlowNode_CustomNodeBase, so passing them in to a common function. - SortedNames.Sort([](const FName& A, const FName& B) - { - return A.LexicalLess(B); - }); + static const FText CustomRowNameText = LOCTEXT("CustomRowName", "Event Name"); + static const FText EventNameText = LOCTEXT("EventName", "Event Name"); - for (const FName& EventName : SortedNames) - { - if (!EventName.IsNone()) - { - EventNames.Emplace(MakeShareable(new FName(EventName))); - } - } - } + CustomizeDetailsInternal(DetailLayout, CustomRowNameText, EventNameText); } -TSharedRef FFlowNode_CustomInputDetails::GenerateEventWidget(const TSharedPtr Item) const +IDetailCategoryBuilder& FFlowNode_CustomInputDetails::CreateDetailCategory(IDetailLayoutBuilder& DetailLayout) const { - return SNew(STextBlock).Text(FText::FromName(*Item.Get())); -} - -FText FFlowNode_CustomInputDetails::GetSelectedEventText() const -{ - FText PropertyValue; - - ensure(ObjectsBeingEdited[0].IsValid()); - if (const UFlowNode_CustomInput* Node = Cast(ObjectsBeingEdited[0].Get())) - { - PropertyValue = FText::FromName(Node->EventName); - } - - return PropertyValue; + return DetailLayout.EditCategory("CustomInput", LOCTEXT("CustomInputCategory", "Custom Event")); } -void FFlowNode_CustomInputDetails::PinSelectionChanged(const TSharedPtr Item, ESelectInfo::Type SelectInfo) const +TArray FFlowNode_CustomInputDetails::BuildEventNames(const UFlowAsset& FlowAsset) const { - ensure(ObjectsBeingEdited[0].IsValid()); - if (UFlowNode_CustomInput* Node = Cast(ObjectsBeingEdited[0].Get())) - { - Node->EventName = *Item.Get(); - } + return FlowAsset.GetCustomInputs(); } #undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomNodeBaseDetails.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomNodeBaseDetails.cpp new file mode 100644 index 000000000..945b62020 --- /dev/null +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomNodeBaseDetails.cpp @@ -0,0 +1,152 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "DetailCustomizations/FlowNode_CustomNodeBaseDetails.h" +#include "FlowAsset.h" +#include "Nodes/Route/FlowNode_CustomNodeBase.h" + +#include "DetailCategoryBuilder.h" +#include "DetailWidgetRow.h" +#include "PropertyEditing.h" +#include "Widgets/Input/SComboBox.h" +#include "Widgets/Text/STextBlock.h" +#include "Widgets/SWidget.h" + + +void FFlowNode_CustomNodeBaseDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) +{ + // Subclasses must override this function (and call CustomizeDetailsInternal with the localized text) + checkNoEntry(); +} + +void FFlowNode_CustomNodeBaseDetails::CustomizeDetailsInternal(IDetailLayoutBuilder& DetailLayout, const FText& CustomRowNameText, const FText& EventNameText) +{ + DetailLayout.GetObjectsBeingCustomized(ObjectsBeingEdited); + + if (ObjectsBeingEdited[0].IsValid()) + { + UFlowNode_CustomNodeBase* FlowNodeBase = CastChecked(ObjectsBeingEdited[0]); + CachedEventNameSelected = MakeShared(FlowNodeBase->GetEventName()); + } + + IDetailCategoryBuilder& Category = CreateDetailCategory(DetailLayout); + + Category.AddCustomRow(CustomRowNameText) + .NameContent() + [ + SNew(STextBlock) + .Text(EventNameText) + ] + .ValueContent() + .HAlign(HAlign_Fill) + [ + SAssignNew(EventTextListWidget, SComboBox>) + .OptionsSource(&EventNames) + .OnGenerateWidget(this, &FFlowNode_CustomNodeBaseDetails::GenerateEventWidget) + .OnComboBoxOpening(this, &FFlowNode_CustomNodeBaseDetails::OnComboBoxOpening) + .OnSelectionChanged(this, &FFlowNode_CustomNodeBaseDetails::PinSelectionChanged) + [ + SNew(STextBlock) + .Text(this, &FFlowNode_CustomNodeBaseDetails::GetSelectedEventText) + ] + ]; +} + +void FFlowNode_CustomNodeBaseDetails::OnComboBoxOpening() +{ + RebuildEventNames(); +} + +void FFlowNode_CustomNodeBaseDetails::RebuildEventNames() +{ + EventNames.Empty(); + + check(CachedEventNameSelected.IsValid()); + EventNames.Add(CachedEventNameSelected); + + if (ObjectsBeingEdited[0].IsValid() && ObjectsBeingEdited[0].Get()->GetOuter()) + { + const UFlowAsset* FlowAsset = CastChecked(ObjectsBeingEdited[0].Get()->GetOuter()); + TArray SortedNames = BuildEventNames(*FlowAsset); + + if (bExcludeReferencedEvents) + { + for (const TPair& Node : FlowAsset->GetNodes()) + { + if (Node.Value->GetClass()->IsChildOf(UFlowNode_CustomNodeBase::StaticClass())) + { + SortedNames.Remove(Cast(Node.Value)->GetEventName()); + } + } + } + + SortedNames.Sort([](const FName& A, const FName& B) + { + return A.LexicalLess(B); + }); + + for (const FName& EventName : SortedNames) + { + const bool bIsCurrentSelection = (EventName == *CachedEventNameSelected); + if (!EventName.IsNone() && !bIsCurrentSelection) + { + EventNames.Add(MakeShared(EventName)); + } + } + } + + if (!IsInEventNames(NAME_None)) + { + EventNames.Add(MakeShared(NAME_None)); + } +} + +bool FFlowNode_CustomNodeBaseDetails::IsInEventNames(const FName& EventName) const +{ + const bool bIsInEventNames = + EventNames.ContainsByPredicate([&EventName](const TSharedPtr& ExistingName) + { + return *ExistingName == EventName; + }); + + return bIsInEventNames; +} + +TSharedRef FFlowNode_CustomNodeBaseDetails::GenerateEventWidget(const TSharedPtr Item) const +{ + return + SNew(STextBlock) + .Text(FText::FromName(*Item.Get())); +} + +FText FFlowNode_CustomNodeBaseDetails::GetSelectedEventText() const +{ + check(CachedEventNameSelected.IsValid()); + + return FText::FromName(*CachedEventNameSelected.Get()); +} + +void FFlowNode_CustomNodeBaseDetails::PinSelectionChanged(const TSharedPtr Item, ESelectInfo::Type SelectInfo) +{ + ensure(ObjectsBeingEdited[0].IsValid()); + + UFlowNode_CustomNodeBase* Node = Cast(ObjectsBeingEdited[0].Get()); + if (IsValid(Node) && Item) + { + const bool bIsChanged = (*CachedEventNameSelected != *Item); + + if (bIsChanged) + { + CachedEventNameSelected = Item; + + const FName ItemAsName = *CachedEventNameSelected; + + Node->SetEventName(ItemAsName); + + if (EventTextListWidget.IsValid()) + { + // Tell UDE to refresh the widget to show the new change + EventTextListWidget->Invalidate(EInvalidateWidgetReason::Paint); + } + } + } +} diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomOutputDetails.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomOutputDetails.cpp index d2ab37aee..652e768ae 100644 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomOutputDetails.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomOutputDetails.cpp @@ -1,93 +1,35 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "DetailCustomizations/FlowNode_CustomOutputDetails.h" +#include "DetailLayoutBuilder.h" #include "FlowAsset.h" -#include "Nodes/Route/FlowNode_CustomOutput.h" - -#include "DetailCategoryBuilder.h" -#include "DetailWidgetRow.h" -#include "PropertyEditing.h" -#include "Widgets/Input/SComboBox.h" -#include "Widgets/Text/STextBlock.h" #define LOCTEXT_NAMESPACE "FlowNode_CustomOutputDetails" -void FFlowNode_CustomOutputDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) +FFlowNode_CustomOutputDetails::FFlowNode_CustomOutputDetails() { - DetailLayout.GetObjectsBeingCustomized(ObjectsBeingEdited); - GetEventNames(); - - IDetailCategoryBuilder& Category = DetailLayout.EditCategory("CustomOutput", LOCTEXT("CustomEventsCategory", "Custom Output")); - Category.AddCustomRow(LOCTEXT("CustomRowName", "Event Name")) - .NameContent() - [ - SNew(STextBlock) - .Text(LOCTEXT("EventName", "Event Name")) - ] - .ValueContent() - .HAlign(HAlign_Fill) - [ - SNew(SComboBox>) - .OptionsSource(&EventNames) - .OnGenerateWidget(this, &FFlowNode_CustomOutputDetails::GenerateEventWidget) - .OnSelectionChanged(this, &FFlowNode_CustomOutputDetails::PinSelectionChanged) - [ - SNew(STextBlock) - .Text(this, &FFlowNode_CustomOutputDetails::GetSelectedEventText) - ] - ]; + check(bExcludeReferencedEvents == false); } -void FFlowNode_CustomOutputDetails::GetEventNames() +void FFlowNode_CustomOutputDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) { - EventNames.Empty(); - EventNames.Emplace(MakeShareable(new FName(NAME_None))); - - if (ObjectsBeingEdited[0].IsValid() && ObjectsBeingEdited[0].Get()->GetOuter()) - { - const UFlowAsset* FlowAsset = Cast(ObjectsBeingEdited[0].Get()->GetOuter()); - TArray SortedNames = FlowAsset->GetCustomOutputs(); + // For backward compatability, these localized texts are in FlowNode_CustomOutputDetails, + // not FlowNode_CustomNodeBase, so passing them in to a common function. - SortedNames.Sort([](const FName& A, const FName& B) - { - return A.LexicalLess(B); - }); + static const FText CustomRowNameText = LOCTEXT("CustomRowName", "Event Name"); + static const FText EventNameText = LOCTEXT("EventName", "Event Name"); - for (const FName& EventName : SortedNames) - { - if (!EventName.IsNone()) - { - EventNames.Emplace(MakeShareable(new FName(EventName))); - } - } - } + CustomizeDetailsInternal(DetailLayout, CustomRowNameText, EventNameText); } -TSharedRef FFlowNode_CustomOutputDetails::GenerateEventWidget(const TSharedPtr Item) const +IDetailCategoryBuilder& FFlowNode_CustomOutputDetails::CreateDetailCategory(IDetailLayoutBuilder& DetailLayout) const { - return SNew(STextBlock).Text(FText::FromName(*Item.Get())); -} - -FText FFlowNode_CustomOutputDetails::GetSelectedEventText() const -{ - FText PropertyValue; - - ensure(ObjectsBeingEdited[0].IsValid()); - if (const UFlowNode_CustomOutput* Node = Cast(ObjectsBeingEdited[0].Get())) - { - PropertyValue = FText::FromName(Node->EventName); - } - - return PropertyValue; + return DetailLayout.EditCategory("CustomOutput", LOCTEXT("CustomEventsCategory", "Custom Output")); } -void FFlowNode_CustomOutputDetails::PinSelectionChanged(const TSharedPtr Item, ESelectInfo::Type SelectInfo) const +TArray FFlowNode_CustomOutputDetails::BuildEventNames(const UFlowAsset& FlowAsset) const { - ensure(ObjectsBeingEdited[0].IsValid()); - if (UFlowNode_CustomOutput* Node = Cast(ObjectsBeingEdited[0].Get())) - { - Node->EventName = *Item.Get(); - } + return FlowAsset.GetCustomOutputs(); } #undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index ba61997d0..e89a0ce87 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -15,6 +15,9 @@ #include "Framework/Commands/GenericCommands.h" #include "HAL/PlatformApplicationMisc.h" #include "LevelEditor.h" +#include "Modules/ModuleManager.h" +#include "ScopedTransaction.h" +#include "Widgets/Docking/SDockTab.h" #define LOCTEXT_NAMESPACE "FlowGraphEditor" @@ -33,6 +36,11 @@ void SFlowGraphEditor::Construct(const FArguments& InArgs, const TSharedPtrGetGraph(); Arguments._GraphEvents = InArgs._GraphEvents; Arguments._AutoExpandActionMenu = true; + + // QUESTION (gtaylor) Why is this code commented out? + // When commenting out code, please leave a comment as to *why* it is commented out + // and under what conditions it could be uncommented or removed. + // Stray commented code is an enigma to any future programmer trying to make sense of the code. //Arguments._ShowGraphStateOverlay = false; Arguments._GraphEvents.OnSelectionChanged = FOnSelectionChanged::CreateSP(this, &SFlowGraphEditor::OnSelectedNodesChanged); diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index bcd7ce402..e76550fe8 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -13,11 +13,13 @@ #include "FlowAsset.h" #include "Nodes/FlowNode.h" #include "Nodes/FlowNodeBlueprint.h" +#include "Nodes/Route/FlowNode_CustomInput.h" #include "Nodes/Route/FlowNode_Start.h" #include "Nodes/Route/FlowNode_Reroute.h" #include "AssetRegistry/AssetRegistryModule.h" #include "EdGraph/EdGraph.h" +#include "Editor.h" #include "ScopedTransaction.h" #define LOCTEXT_NAMESPACE "FlowGraphSchema" @@ -72,17 +74,48 @@ void UFlowGraphSchema::GetGraphContextActions(FGraphContextMenuBuilder& ContextM void UFlowGraphSchema::CreateDefaultNodesForGraph(UEdGraph& Graph) const { + const UFlowAsset* AssetClassDefaults = GetAssetClassDefaults(&Graph); + + static const FVector2D NodeOffsetIncrement = FVector2D(0, 128); + + FVector2D NodeOffset = FVector2D::ZeroVector; + // Start node - UFlowGraphNode* NewGraphNode = FFlowGraphSchemaAction_NewNode::CreateNode(&Graph, nullptr, UFlowNode_Start::StaticClass(), FVector2D::ZeroVector); + const bool bStartNodePlacedAsGhostNode = IsValid(AssetClassDefaults) ? AssetClassDefaults->bStartNodePlacedAsGhostNode : false; + UFlowGraphNode* StartGraphNode = CreateDefaultNode(Graph, AssetClassDefaults, UFlowNode_Start::StaticClass(), NodeOffset, bStartNodePlacedAsGhostNode); + check(IsValid(StartGraphNode)); + + // Add default nodes for all of the CustomInputs + if (IsValid(AssetClassDefaults)) + { + for (const FName& CustomInputName : AssetClassDefaults->CustomInputs) + { + NodeOffset += NodeOffsetIncrement; + + constexpr bool bCustomInputPlacedAsGhostNode = true; + UFlowGraphNode* NewFlowGraphNode = CreateDefaultNode(Graph, AssetClassDefaults, UFlowNode_CustomInput::StaticClass(), NodeOffset, bCustomInputPlacedAsGhostNode); + UFlowNode_CustomInput* CustomInputNode = CastChecked(NewFlowGraphNode->GetFlowNode()); + + CustomInputNode->SetEventName(CustomInputName); + } + } + + CastChecked(&Graph)->GetFlowAsset()->HarvestNodeConnections(); +} + +UFlowGraphNode* UFlowGraphSchema::CreateDefaultNode(UEdGraph& Graph, const UFlowAsset* AssetClassDefaults, const TSubclassOf& NodeClass, const FVector2D& Offset, bool bPlacedAsGhostNode) const +{ + constexpr UEdGraphPin* FromNode = nullptr; + UFlowGraphNode* NewGraphNode = FFlowGraphSchemaAction_NewNode::CreateNode(&Graph, FromNode, NodeClass, Offset); + SetNodeMetaData(NewGraphNode, FNodeMetadata::DefaultGraphNode); - const UFlowAsset* AssetClassDefaults = GetAssetClassDefaults(&Graph); - if (AssetClassDefaults && AssetClassDefaults->bStartNodePlacedAsGhostNode) + if (bPlacedAsGhostNode) { NewGraphNode->MakeAutomaticallyPlacedGhostNode(); } - CastChecked(&Graph)->GetFlowAsset()->HarvestNodeConnections(); + return NewGraphNode; } const FPinConnectionResponse UFlowGraphSchema::CanCreateConnection(const UEdGraphPin* PinA, const UEdGraphPin* PinB) const diff --git a/Source/FlowEditor/Public/Asset/FlowImportUtils.h b/Source/FlowEditor/Public/Asset/FlowImportUtils.h index 183361ff1..3ef1799b7 100644 --- a/Source/FlowEditor/Public/Asset/FlowImportUtils.h +++ b/Source/FlowEditor/Public/Asset/FlowImportUtils.h @@ -5,6 +5,7 @@ #include "Kismet/BlueprintFunctionLibrary.h" #include "FlowAsset.h" +#include "Kismet/BlueprintFunctionLibrary.h" #include "Nodes/FlowPin.h" #include "FlowImportUtils.generated.h" diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomInputDetails.h b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomInputDetails.h index 2fe9b995a..409881bfb 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomInputDetails.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomInputDetails.h @@ -2,13 +2,14 @@ #pragma once -#include "IDetailCustomization.h" +#include "FlowNode_CustomNodeBaseDetails.h" #include "Templates/SharedPointer.h" -#include "Widgets/SWidget.h" -class FFlowNode_CustomInputDetails final : public IDetailCustomization +class FFlowNode_CustomInputDetails final : public FFlowNode_CustomNodeBaseDetails { public: + FFlowNode_CustomInputDetails(); + static TSharedRef MakeInstance() { return MakeShareable(new FFlowNode_CustomInputDetails()); @@ -18,12 +19,7 @@ class FFlowNode_CustomInputDetails final : public IDetailCustomization virtual void CustomizeDetails(IDetailLayoutBuilder& DetailLayout) override; // -- -private: - void GetEventNames(); - TSharedRef GenerateEventWidget(TSharedPtr Item) const; - FText GetSelectedEventText() const; - void PinSelectionChanged(TSharedPtr Item, ESelectInfo::Type SelectInfo) const; - - TArray> ObjectsBeingEdited; - TArray> EventNames; +protected: + virtual IDetailCategoryBuilder& CreateDetailCategory(IDetailLayoutBuilder& DetailLayout) const override; + virtual TArray BuildEventNames(const UFlowAsset& FlowAsset) const override; }; diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomNodeBaseDetails.h b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomNodeBaseDetails.h new file mode 100644 index 000000000..559b06c40 --- /dev/null +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomNodeBaseDetails.h @@ -0,0 +1,40 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "IDetailCustomization.h" +#include "Templates/SharedPointer.h" +#include "Types/SlateEnums.h" +#include "Widgets/Input/SComboBox.h" + +// Forward Declarations +class IDetailCategoryBuilder; +class UFlowAsset; + +class FFlowNode_CustomNodeBaseDetails : public IDetailCustomization +{ +public: + // IDetailCustomization + virtual void CustomizeDetails(IDetailLayoutBuilder& DetailLayout) override; + // -- + +protected: + + void CustomizeDetailsInternal(IDetailLayoutBuilder& DetailLayout, const FText& CustomRowNameText, const FText& EventNameText); + + virtual IDetailCategoryBuilder& CreateDetailCategory(IDetailLayoutBuilder& DetailLayout) const = 0; + virtual TArray BuildEventNames(const UFlowAsset& FlowAsset) const = 0; + + void OnComboBoxOpening(); + void RebuildEventNames(); + TSharedRef GenerateEventWidget(TSharedPtr Item) const; + FText GetSelectedEventText() const; + void PinSelectionChanged(TSharedPtr Item, ESelectInfo::Type SelectInfo); + bool IsInEventNames(const FName& EventName) const; + + TArray> ObjectsBeingEdited; + TArray> EventNames; + TSharedPtr CachedEventNameSelected; + TSharedPtr>> EventTextListWidget; + bool bExcludeReferencedEvents = false; +}; diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomOutputDetails.h b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomOutputDetails.h index dba0de2c2..208903328 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomOutputDetails.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomOutputDetails.h @@ -2,13 +2,14 @@ #pragma once -#include "IDetailCustomization.h" +#include "FlowNode_CustomNodeBaseDetails.h" #include "Templates/SharedPointer.h" -#include "Widgets/SWidget.h" -class FFlowNode_CustomOutputDetails final : public IDetailCustomization +class FFlowNode_CustomOutputDetails final : public FFlowNode_CustomNodeBaseDetails { public: + FFlowNode_CustomOutputDetails(); + static TSharedRef MakeInstance() { return MakeShareable(new FFlowNode_CustomOutputDetails()); @@ -18,12 +19,7 @@ class FFlowNode_CustomOutputDetails final : public IDetailCustomization virtual void CustomizeDetails(IDetailLayoutBuilder& DetailLayout) override; // -- -private: - void GetEventNames(); - TSharedRef GenerateEventWidget(TSharedPtr Item) const; - FText GetSelectedEventText() const; - void PinSelectionChanged(TSharedPtr Item, ESelectInfo::Type SelectInfo) const; - - TArray> ObjectsBeingEdited; - TArray> EventNames; +protected: + virtual IDetailCategoryBuilder& CreateDetailCategory(IDetailLayoutBuilder& DetailLayout) const override; + virtual TArray BuildEventNames(const UFlowAsset& FlowAsset) const override; }; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h index 5debee64c..ccba5175c 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h @@ -11,6 +11,7 @@ #include "FlowGraph.h" class FFlowAssetEditor; +class IDetailsView; /** * diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h index 099700bc4..e52644f00 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h @@ -3,10 +3,12 @@ #pragma once #include "EdGraph/EdGraphSchema.h" +#include "Templates/SubclassOf.h" #include "FlowGraphSchema.generated.h" class UFlowAsset; class UFlowNode; +class UFlowGraphNode; DECLARE_MULTICAST_DELEGATE(FFlowGraphSchemaRefresh); @@ -47,6 +49,9 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema static TArray> GetFlowNodeCategories(); static UClass* GetAssignedGraphNodeClass(const UClass* FlowNodeClass); +protected: + UFlowGraphNode* CreateDefaultNode(UEdGraph& Graph, const UFlowAsset* AssetClassDefaults, const TSubclassOf& NodeClass, const FVector2D& Offset, bool bPlacedAsGhostNode) const; + private: static void ApplyNodeFilter(const UFlowAsset* AssetClassDefaults, const UClass* FlowNodeClass, TArray& FilteredNodes); static void GetFlowNodeActions(FGraphActionMenuBuilder& ActionMenuBuilder, const UFlowAsset* AssetClassDefaults, const FString& CategoryName); diff --git a/Source/FlowEditor/Public/Graph/FlowGraphUtils.h b/Source/FlowEditor/Public/Graph/FlowGraphUtils.h index 5747c7dc2..87b8e126c 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphUtils.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphUtils.h @@ -3,9 +3,11 @@ #pragma once #include "CoreMinimal.h" +#include "Templates/SharedPointer.h" class FFlowAssetEditor; class SFlowGraphEditor; +class UEdGraph; class FLOWEDITOR_API FFlowGraphUtils { From 2787af4055808cbf99f2fa6ad4ff8a2f8b53de25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Tue, 9 May 2023 21:12:26 +0200 Subject: [PATCH 163/485] removed a stray code --- Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index e89a0ce87..0a9230a80 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -37,12 +37,6 @@ void SFlowGraphEditor::Construct(const FArguments& InArgs, const TSharedPtr Date: Tue, 9 May 2023 21:13:11 +0200 Subject: [PATCH 164/485] renamed base class to UFlowNode_CustomEventBase --- ...eBase.cpp => FlowNode_CustomEventBase.cpp} | 22 +++---- .../Nodes/Route/FlowNode_CustomInput.cpp | 14 ++-- .../Nodes/Route/FlowNode_CustomOutput.cpp | 16 ++--- ...mNodeBase.h => FlowNode_CustomEventBase.h} | 7 +- .../Public/Nodes/Route/FlowNode_CustomInput.h | 5 +- .../Nodes/Route/FlowNode_CustomOutput.h | 5 +- ...pp => FlowNode_CustomEventBaseDetails.cpp} | 66 +++++++++---------- ...ls.h => FlowNode_CustomEventBaseDetails.h} | 4 +- .../FlowNode_CustomInputDetails.h | 4 +- .../FlowNode_CustomOutputDetails.h | 4 +- 10 files changed, 63 insertions(+), 84 deletions(-) rename Source/Flow/Private/Nodes/Route/{FlowNode_CustomNodeBase.cpp => FlowNode_CustomEventBase.cpp} (54%) rename Source/Flow/Public/Nodes/Route/{FlowNode_CustomNodeBase.h => FlowNode_CustomEventBase.h} (73%) rename Source/FlowEditor/Private/DetailCustomizations/{FlowNode_CustomNodeBaseDetails.cpp => FlowNode_CustomEventBaseDetails.cpp} (54%) rename Source/FlowEditor/Public/DetailCustomizations/{FlowNode_CustomNodeBaseDetails.h => FlowNode_CustomEventBaseDetails.h} (93%) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_CustomNodeBase.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_CustomEventBase.cpp similarity index 54% rename from Source/Flow/Private/Nodes/Route/FlowNode_CustomNodeBase.cpp rename to Source/Flow/Private/Nodes/Route/FlowNode_CustomEventBase.cpp index b38f67786..0b75bc43b 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_CustomNodeBase.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_CustomEventBase.cpp @@ -1,9 +1,9 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "Nodes/Route/FlowNode_CustomNodeBase.h" +#include "Nodes/Route/FlowNode_CustomEventBase.h" #include "FlowSettings.h" -UFlowNode_CustomNodeBase::UFlowNode_CustomNodeBase(const FObjectInitializer& ObjectInitializer) +UFlowNode_CustomEventBase::UFlowNode_CustomEventBase(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { #if WITH_EDITOR @@ -14,14 +14,14 @@ UFlowNode_CustomNodeBase::UFlowNode_CustomNodeBase(const FObjectInitializer& Obj AllowedSignalModes = {EFlowSignalMode::Enabled, EFlowSignalMode::Disabled}; } -void UFlowNode_CustomNodeBase::SetEventName(const FName& InEventName) +void UFlowNode_CustomEventBase::SetEventName(const FName& InEventName) { if (EventName != InEventName) { EventName = InEventName; #if WITH_EDITOR - // Must reconstruct the Graph Visuals if anything that is included in AdaptiveNodeTitles changes + // Must reconstruct the visual representation if anything that is included in AdaptiveNodeTitles changes OnReconstructionRequested.ExecuteIfBound(); #endif // WITH_EDITOR } @@ -29,21 +29,17 @@ void UFlowNode_CustomNodeBase::SetEventName(const FName& InEventName) #if WITH_EDITOR -FString UFlowNode_CustomNodeBase::GetNodeDescription() const +FString UFlowNode_CustomEventBase::GetNodeDescription() const { - const bool bUseAdaptiveNodeTitles = UFlowSettings::Get()->bUseAdaptiveNodeTitles; - - if (bUseAdaptiveNodeTitles) + if (UFlowSettings::Get()->bUseAdaptiveNodeTitles) { return Super::GetNodeDescription(); } - else - { - return EventName.ToString(); - } + + return EventName.ToString(); } -EDataValidationResult UFlowNode_CustomNodeBase::ValidateNode() +EDataValidationResult UFlowNode_CustomEventBase::ValidateNode() { if (EventName.IsNone()) { diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp index 59982dc64..2e1f594c8 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp @@ -3,7 +3,7 @@ #include "Nodes/Route/FlowNode_CustomInput.h" #include "FlowSettings.h" -#define LOCTEXT_NAMESPACE "FlowNode" +#define LOCTEXT_NAMESPACE "FlowNode_CustomInput" UFlowNode_CustomInput::UFlowNode_CustomInput(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) @@ -19,16 +19,12 @@ void UFlowNode_CustomInput::ExecuteInput(const FName& PinName) #if WITH_EDITOR FText UFlowNode_CustomInput::GetNodeTitle() const { - const bool bUseAdaptiveNodeTitles = UFlowSettings::Get()->bUseAdaptiveNodeTitles; - - if (bUseAdaptiveNodeTitles && !EventName.IsNone()) - { - return FText::Format(LOCTEXT("CustomInputTitle", "{0} Input"), { FText::FromString(EventName.ToString()) }); - } - else + if (!EventName.IsNone() && UFlowSettings::Get()->bUseAdaptiveNodeTitles) { - return Super::GetNodeTitle(); + return FText::Format(LOCTEXT("CustomInputTitle", "{0} Input"), {FText::FromString(EventName.ToString())}); } + + return Super::GetNodeTitle(); } #endif diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp index bab95e170..e6826702f 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp @@ -4,7 +4,7 @@ #include "FlowAsset.h" #include "FlowSettings.h" -#define LOCTEXT_NAMESPACE "FlowNode" +#define LOCTEXT_NAMESPACE "FlowNode_CustomOutput" UFlowNode_CustomOutput::UFlowNode_CustomOutput(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) @@ -23,17 +23,13 @@ void UFlowNode_CustomOutput::ExecuteInput(const FName& PinName) #if WITH_EDITOR FText UFlowNode_CustomOutput::GetNodeTitle() const { - const bool bUseAdaptiveNodeTitles = UFlowSettings::Get()->bUseAdaptiveNodeTitles; - - if (bUseAdaptiveNodeTitles && !EventName.IsNone()) - { - return FText::Format(LOCTEXT("CustomOutputTitle", "{0} Output"), { FText::FromString(EventName.ToString()) }); - } - else + if (!EventName.IsNone() && UFlowSettings::Get()->bUseAdaptiveNodeTitles) { - return Super::GetNodeTitle(); + return FText::Format(LOCTEXT("CustomOutputTitle", "{0} Output"), {FText::FromString(EventName.ToString())}); } + + return Super::GetNodeTitle(); } #endif -#undef LOCTEXT_NAMESPACE \ No newline at end of file +#undef LOCTEXT_NAMESPACE diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_CustomNodeBase.h b/Source/Flow/Public/Nodes/Route/FlowNode_CustomEventBase.h similarity index 73% rename from Source/Flow/Public/Nodes/Route/FlowNode_CustomNodeBase.h rename to Source/Flow/Public/Nodes/Route/FlowNode_CustomEventBase.h index fb5b76974..7e9944d25 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_CustomNodeBase.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_CustomEventBase.h @@ -3,14 +3,13 @@ #pragma once #include "Nodes/FlowNode.h" - -#include "FlowNode_CustomNodeBase.generated.h" +#include "FlowNode_CustomEventBase.generated.h" /** - * Base-class for CustomInput and CustomOutput node types + * Base class for nodes used to receive/send events between graphs */ UCLASS(Abstract, NotBlueprintable) -class FLOW_API UFlowNode_CustomNodeBase : public UFlowNode +class FLOW_API UFlowNode_CustomEventBase : public UFlowNode { GENERATED_UCLASS_BODY() diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_CustomInput.h b/Source/Flow/Public/Nodes/Route/FlowNode_CustomInput.h index 28a50e134..12f6012c0 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_CustomInput.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_CustomInput.h @@ -2,15 +2,14 @@ #pragma once -#include "FlowNode_CustomNodeBase.h" - +#include "FlowNode_CustomEventBase.h" #include "FlowNode_CustomInput.generated.h" /** * Triggers output upon activation of Input (matching this EventName) on the SubGraph node containing this graph */ UCLASS(NotBlueprintable, meta = (DisplayName = "Custom Input")) -class FLOW_API UFlowNode_CustomInput : public UFlowNode_CustomNodeBase +class FLOW_API UFlowNode_CustomInput : public UFlowNode_CustomEventBase { GENERATED_UCLASS_BODY() diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_CustomOutput.h b/Source/Flow/Public/Nodes/Route/FlowNode_CustomOutput.h index e6d3dee53..b0e93d422 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_CustomOutput.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_CustomOutput.h @@ -2,8 +2,7 @@ #pragma once -#include "FlowNode_CustomNodeBase.h" - +#include "FlowNode_CustomEventBase.h" #include "FlowNode_CustomOutput.generated.h" /** @@ -11,7 +10,7 @@ * Triggered output name matches EventName selected on this node */ UCLASS(NotBlueprintable, meta = (DisplayName = "Custom Output")) -class FLOW_API UFlowNode_CustomOutput final : public UFlowNode_CustomNodeBase +class FLOW_API UFlowNode_CustomOutput final : public UFlowNode_CustomEventBase { GENERATED_UCLASS_BODY() diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomNodeBaseDetails.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomEventBaseDetails.cpp similarity index 54% rename from Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomNodeBaseDetails.cpp rename to Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomEventBaseDetails.cpp index 945b62020..3a8b96b99 100644 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomNodeBaseDetails.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomEventBaseDetails.cpp @@ -1,8 +1,8 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "DetailCustomizations/FlowNode_CustomNodeBaseDetails.h" +#include "DetailCustomizations/FlowNode_CustomEventBaseDetails.h" #include "FlowAsset.h" -#include "Nodes/Route/FlowNode_CustomNodeBase.h" +#include "Nodes/Route/FlowNode_CustomEventBase.h" #include "DetailCategoryBuilder.h" #include "DetailWidgetRow.h" @@ -12,51 +12,51 @@ #include "Widgets/SWidget.h" -void FFlowNode_CustomNodeBaseDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) +void FFlowNode_CustomEventBaseDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) { // Subclasses must override this function (and call CustomizeDetailsInternal with the localized text) checkNoEntry(); } -void FFlowNode_CustomNodeBaseDetails::CustomizeDetailsInternal(IDetailLayoutBuilder& DetailLayout, const FText& CustomRowNameText, const FText& EventNameText) +void FFlowNode_CustomEventBaseDetails::CustomizeDetailsInternal(IDetailLayoutBuilder& DetailLayout, const FText& CustomRowNameText, const FText& EventNameText) { DetailLayout.GetObjectsBeingCustomized(ObjectsBeingEdited); if (ObjectsBeingEdited[0].IsValid()) { - UFlowNode_CustomNodeBase* FlowNodeBase = CastChecked(ObjectsBeingEdited[0]); - CachedEventNameSelected = MakeShared(FlowNodeBase->GetEventName()); + const UFlowNode_CustomEventBase* EventNode = CastChecked(ObjectsBeingEdited[0]); + CachedEventNameSelected = MakeShared(EventNode->GetEventName()); } IDetailCategoryBuilder& Category = CreateDetailCategory(DetailLayout); Category.AddCustomRow(CustomRowNameText) - .NameContent() + .NameContent() [ SNew(STextBlock) - .Text(EventNameText) + .Text(EventNameText) ] - .ValueContent() + .ValueContent() .HAlign(HAlign_Fill) [ SAssignNew(EventTextListWidget, SComboBox>) - .OptionsSource(&EventNames) - .OnGenerateWidget(this, &FFlowNode_CustomNodeBaseDetails::GenerateEventWidget) - .OnComboBoxOpening(this, &FFlowNode_CustomNodeBaseDetails::OnComboBoxOpening) - .OnSelectionChanged(this, &FFlowNode_CustomNodeBaseDetails::PinSelectionChanged) - [ - SNew(STextBlock) - .Text(this, &FFlowNode_CustomNodeBaseDetails::GetSelectedEventText) - ] + .OptionsSource(&EventNames) + .OnGenerateWidget(this, &FFlowNode_CustomEventBaseDetails::GenerateEventWidget) + .OnComboBoxOpening(this, &FFlowNode_CustomEventBaseDetails::OnComboBoxOpening) + .OnSelectionChanged(this, &FFlowNode_CustomEventBaseDetails::PinSelectionChanged) + [ + SNew(STextBlock) + .Text(this, &FFlowNode_CustomEventBaseDetails::GetSelectedEventText) + ] ]; } -void FFlowNode_CustomNodeBaseDetails::OnComboBoxOpening() +void FFlowNode_CustomEventBaseDetails::OnComboBoxOpening() { RebuildEventNames(); } -void FFlowNode_CustomNodeBaseDetails::RebuildEventNames() +void FFlowNode_CustomEventBaseDetails::RebuildEventNames() { EventNames.Empty(); @@ -72,9 +72,9 @@ void FFlowNode_CustomNodeBaseDetails::RebuildEventNames() { for (const TPair& Node : FlowAsset->GetNodes()) { - if (Node.Value->GetClass()->IsChildOf(UFlowNode_CustomNodeBase::StaticClass())) + if (Node.Value->GetClass()->IsChildOf(UFlowNode_CustomEventBase::StaticClass())) { - SortedNames.Remove(Cast(Node.Value)->GetEventName()); + SortedNames.Remove(Cast(Node.Value)->GetEventName()); } } } @@ -100,36 +100,33 @@ void FFlowNode_CustomNodeBaseDetails::RebuildEventNames() } } -bool FFlowNode_CustomNodeBaseDetails::IsInEventNames(const FName& EventName) const +bool FFlowNode_CustomEventBaseDetails::IsInEventNames(const FName& EventName) const { - const bool bIsInEventNames = - EventNames.ContainsByPredicate([&EventName](const TSharedPtr& ExistingName) - { - return *ExistingName == EventName; - }); + const bool bIsInEventNames = EventNames.ContainsByPredicate([&EventName](const TSharedPtr& ExistingName) + { + return *ExistingName == EventName; + }); return bIsInEventNames; } -TSharedRef FFlowNode_CustomNodeBaseDetails::GenerateEventWidget(const TSharedPtr Item) const +TSharedRef FFlowNode_CustomEventBaseDetails::GenerateEventWidget(const TSharedPtr Item) const { - return - SNew(STextBlock) + return SNew(STextBlock) .Text(FText::FromName(*Item.Get())); } -FText FFlowNode_CustomNodeBaseDetails::GetSelectedEventText() const +FText FFlowNode_CustomEventBaseDetails::GetSelectedEventText() const { check(CachedEventNameSelected.IsValid()); - return FText::FromName(*CachedEventNameSelected.Get()); } -void FFlowNode_CustomNodeBaseDetails::PinSelectionChanged(const TSharedPtr Item, ESelectInfo::Type SelectInfo) +void FFlowNode_CustomEventBaseDetails::PinSelectionChanged(const TSharedPtr Item, ESelectInfo::Type SelectInfo) { ensure(ObjectsBeingEdited[0].IsValid()); - UFlowNode_CustomNodeBase* Node = Cast(ObjectsBeingEdited[0].Get()); + UFlowNode_CustomEventBase* Node = Cast(ObjectsBeingEdited[0].Get()); if (IsValid(Node) && Item) { const bool bIsChanged = (*CachedEventNameSelected != *Item); @@ -139,7 +136,6 @@ void FFlowNode_CustomNodeBaseDetails::PinSelectionChanged(const TSharedPtrSetEventName(ItemAsName); if (EventTextListWidget.IsValid()) diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomNodeBaseDetails.h b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomEventBaseDetails.h similarity index 93% rename from Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomNodeBaseDetails.h rename to Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomEventBaseDetails.h index 559b06c40..ce5f7d4f1 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomNodeBaseDetails.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomEventBaseDetails.h @@ -7,11 +7,10 @@ #include "Types/SlateEnums.h" #include "Widgets/Input/SComboBox.h" -// Forward Declarations class IDetailCategoryBuilder; class UFlowAsset; -class FFlowNode_CustomNodeBaseDetails : public IDetailCustomization +class FFlowNode_CustomEventBaseDetails : public IDetailCustomization { public: // IDetailCustomization @@ -19,7 +18,6 @@ class FFlowNode_CustomNodeBaseDetails : public IDetailCustomization // -- protected: - void CustomizeDetailsInternal(IDetailLayoutBuilder& DetailLayout, const FText& CustomRowNameText, const FText& EventNameText); virtual IDetailCategoryBuilder& CreateDetailCategory(IDetailLayoutBuilder& DetailLayout) const = 0; diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomInputDetails.h b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomInputDetails.h index 409881bfb..7fe0c4a6e 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomInputDetails.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomInputDetails.h @@ -2,10 +2,10 @@ #pragma once -#include "FlowNode_CustomNodeBaseDetails.h" +#include "FlowNode_CustomEventBaseDetails.h" #include "Templates/SharedPointer.h" -class FFlowNode_CustomInputDetails final : public FFlowNode_CustomNodeBaseDetails +class FFlowNode_CustomInputDetails final : public FFlowNode_CustomEventBaseDetails { public: FFlowNode_CustomInputDetails(); diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomOutputDetails.h b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomOutputDetails.h index 208903328..6e90889f7 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomOutputDetails.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomOutputDetails.h @@ -2,10 +2,10 @@ #pragma once -#include "FlowNode_CustomNodeBaseDetails.h" +#include "FlowNode_CustomEventBaseDetails.h" #include "Templates/SharedPointer.h" -class FFlowNode_CustomOutputDetails final : public FFlowNode_CustomNodeBaseDetails +class FFlowNode_CustomOutputDetails final : public FFlowNode_CustomEventBaseDetails { public: FFlowNode_CustomOutputDetails(); From fbd6deb29fd212dd7a2d6ce7d2d01237e2c31953 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Tue, 9 May 2023 21:13:31 +0200 Subject: [PATCH 165/485] formatting and readability tweaks --- Source/Flow/Private/FlowAsset.cpp | 3 --- Source/Flow/Private/Nodes/FlowNode.cpp | 6 ++---- Source/Flow/Public/FlowSettings.h | 2 +- Source/Flow/Public/Nodes/FlowNode.h | 2 +- .../FlowEditor/Private/Graph/FlowGraphSchema.cpp | 16 ++++------------ Source/FlowEditor/Public/Graph/FlowGraphSchema.h | 2 +- 6 files changed, 9 insertions(+), 22 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 0bb41ef19..c560dadbe 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -189,20 +189,17 @@ void UFlowAsset::HarvestNodeConnections() } } } - #endif // WITH_EDITOR void UFlowAsset::AddCustomInput(const FName& InName) { check(!CustomInputs.Contains(InName)); - CustomInputs.Add(InName); } void UFlowAsset::RemoveCustomInput(const FName& InName) { check(CustomInputs.Contains(InName)); - CustomInputs.Remove(InName); } diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index e6523372e..d63de4cd9 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -183,10 +183,8 @@ UObject* UFlowNode::TryGetRootFlowObjectOwner() const { return FlowAsset->GetOwner(); } - else - { - return nullptr; - } + + return nullptr; } void UFlowNode::AddInputPins(TArray Pins) diff --git a/Source/Flow/Public/FlowSettings.h b/Source/Flow/Public/FlowSettings.h index a206b4640..0dc1a772e 100644 --- a/Source/Flow/Public/FlowSettings.h +++ b/Source/Flow/Public/FlowSettings.h @@ -39,7 +39,7 @@ class FLOW_API UFlowSettings : public UDeveloperSettings bool bLogOnSignalPassthrough; // Adjust the Titles for FlowNodes to be more expressive than default - // by incorporating data that would otherwise go in the Description + // by incorporating data that would otherwise go in the Description UPROPERTY(EditAnywhere, config, Category = "Nodes") bool bUseAdaptiveNodeTitles; }; diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 0d6478c96..f5bfc91b8 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -125,7 +125,7 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UFlowAsset* GetFlowAsset() const; // Gets the Owning Actor for this Node's RootFlow - // (if the immediate parent is an UActorComponent, it will get that Component's actor) + // (if the immediate parent is an UActorComponent, it will get that Component's actor) AActor* TryGetRootFlowActorOwner() const; protected: diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index e76550fe8..cd63d4377 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -75,15 +75,11 @@ void UFlowGraphSchema::GetGraphContextActions(FGraphContextMenuBuilder& ContextM void UFlowGraphSchema::CreateDefaultNodesForGraph(UEdGraph& Graph) const { const UFlowAsset* AssetClassDefaults = GetAssetClassDefaults(&Graph); - static const FVector2D NodeOffsetIncrement = FVector2D(0, 128); - FVector2D NodeOffset = FVector2D::ZeroVector; // Start node - const bool bStartNodePlacedAsGhostNode = IsValid(AssetClassDefaults) ? AssetClassDefaults->bStartNodePlacedAsGhostNode : false; - UFlowGraphNode* StartGraphNode = CreateDefaultNode(Graph, AssetClassDefaults, UFlowNode_Start::StaticClass(), NodeOffset, bStartNodePlacedAsGhostNode); - check(IsValid(StartGraphNode)); + CreateDefaultNode(Graph, AssetClassDefaults, UFlowNode_Start::StaticClass(), NodeOffset, AssetClassDefaults->bStartNodePlacedAsGhostNode); // Add default nodes for all of the CustomInputs if (IsValid(AssetClassDefaults)) @@ -91,11 +87,9 @@ void UFlowGraphSchema::CreateDefaultNodesForGraph(UEdGraph& Graph) const for (const FName& CustomInputName : AssetClassDefaults->CustomInputs) { NodeOffset += NodeOffsetIncrement; + const UFlowGraphNode* NewFlowGraphNode = CreateDefaultNode(Graph, AssetClassDefaults, UFlowNode_CustomInput::StaticClass(), NodeOffset, true); - constexpr bool bCustomInputPlacedAsGhostNode = true; - UFlowGraphNode* NewFlowGraphNode = CreateDefaultNode(Graph, AssetClassDefaults, UFlowNode_CustomInput::StaticClass(), NodeOffset, bCustomInputPlacedAsGhostNode); UFlowNode_CustomInput* CustomInputNode = CastChecked(NewFlowGraphNode->GetFlowNode()); - CustomInputNode->SetEventName(CustomInputName); } } @@ -103,11 +97,9 @@ void UFlowGraphSchema::CreateDefaultNodesForGraph(UEdGraph& Graph) const CastChecked(&Graph)->GetFlowAsset()->HarvestNodeConnections(); } -UFlowGraphNode* UFlowGraphSchema::CreateDefaultNode(UEdGraph& Graph, const UFlowAsset* AssetClassDefaults, const TSubclassOf& NodeClass, const FVector2D& Offset, bool bPlacedAsGhostNode) const +UFlowGraphNode* UFlowGraphSchema::CreateDefaultNode(UEdGraph& Graph, const UFlowAsset* AssetClassDefaults, const TSubclassOf& NodeClass, const FVector2D& Offset, const bool bPlacedAsGhostNode) { - constexpr UEdGraphPin* FromNode = nullptr; - UFlowGraphNode* NewGraphNode = FFlowGraphSchemaAction_NewNode::CreateNode(&Graph, FromNode, NodeClass, Offset); - + UFlowGraphNode* NewGraphNode = FFlowGraphSchemaAction_NewNode::CreateNode(&Graph, nullptr, NodeClass, Offset); SetNodeMetaData(NewGraphNode, FNodeMetadata::DefaultGraphNode); if (bPlacedAsGhostNode) diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h index e52644f00..95be35b9f 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h @@ -50,7 +50,7 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema static UClass* GetAssignedGraphNodeClass(const UClass* FlowNodeClass); protected: - UFlowGraphNode* CreateDefaultNode(UEdGraph& Graph, const UFlowAsset* AssetClassDefaults, const TSubclassOf& NodeClass, const FVector2D& Offset, bool bPlacedAsGhostNode) const; + static UFlowGraphNode* CreateDefaultNode(UEdGraph& Graph, const UFlowAsset* AssetClassDefaults, const TSubclassOf& NodeClass, const FVector2D& Offset, bool bPlacedAsGhostNode); private: static void ApplyNodeFilter(const UFlowAsset* AssetClassDefaults, const UClass* FlowNodeClass, TArray& FilteredNodes); From 4b4135fa92d8e9b5159acf7bf57373b98fd5f9fe Mon Sep 17 00:00:00 2001 From: "Satheesh (ryanjon2040)" Date: Wed, 10 May 2023 00:52:15 +0530 Subject: [PATCH 166/485] Timer node complete in next tick if value is closer to 0 (#142) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Timer node complete in next tick if value is closer to 0 * Add tooltip and better description --------- Co-authored-by: Krzysiek Justyński --- .../Flow/Private/Nodes/Route/FlowNode_Timer.cpp | 15 +++++++++++---- Source/Flow/Public/Nodes/Route/FlowNode_Timer.h | 1 + 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp index bd32bf146..c0d494bd6 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp @@ -29,7 +29,7 @@ UFlowNode_Timer::UFlowNode_Timer(const FObjectInitializer& ObjectInitializer) void UFlowNode_Timer::ExecuteInput(const FName& PinName) { - if (CompletionTime == 0.0f) + if (CompletionTime < 0.0f) { LogError(TEXT("Invalid Timer settings")); TriggerOutput(TEXT("Completed"), true); @@ -65,7 +65,14 @@ void UFlowNode_Timer::SetTimer() GetWorld()->GetTimerManager().SetTimer(StepTimerHandle, this, &UFlowNode_Timer::OnStep, StepTime, true); } - GetWorld()->GetTimerManager().SetTimer(CompletionTimerHandle, this, &UFlowNode_Timer::OnCompletion, CompletionTime, false); + if (CompletionTime > UE_KINDA_SMALL_NUMBER) + { + GetWorld()->GetTimerManager().SetTimer(CompletionTimerHandle, this, &UFlowNode_Timer::OnCompletion, CompletionTime, false); + } + else + { + GetWorld()->GetTimerManager().SetTimerForNextTick(this, &UFlowNode_Timer::OnCompletion); + } } else { @@ -155,7 +162,7 @@ void UFlowNode_Timer::OnLoad_Implementation() #if WITH_EDITOR FString UFlowNode_Timer::GetNodeDescription() const { - if (CompletionTime > 0.0f) + if (CompletionTime > UE_KINDA_SMALL_NUMBER) { if (StepTime > 0.0f) { @@ -165,7 +172,7 @@ FString UFlowNode_Timer::GetNodeDescription() const return FString::Printf(TEXT("%.*f"), 2, CompletionTime); } - return TEXT("Invalid settings"); + return TEXT("Completes in next tick"); } FString UFlowNode_Timer::GetStatusString() const diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_Timer.h b/Source/Flow/Public/Nodes/Route/FlowNode_Timer.h index 972147d5a..c920c8662 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_Timer.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_Timer.h @@ -15,6 +15,7 @@ class FLOW_API UFlowNode_Timer : public UFlowNode GENERATED_UCLASS_BODY() protected: + // If the value is closer to 0, Timer will complete in next tick UPROPERTY(EditAnywhere, Category = "Timer", meta = (ClampMin = 0.0f)) float CompletionTime; From ea656a263f74232121d5323d2ffde3228ea6fc91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Tue, 9 May 2023 21:24:54 +0200 Subject: [PATCH 167/485] merge fix actually we don't need to check values smaller than 0.0f, as editor prevents from entering such values --- Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp index c0d494bd6..1a0dd7c03 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp @@ -29,13 +29,6 @@ UFlowNode_Timer::UFlowNode_Timer(const FObjectInitializer& ObjectInitializer) void UFlowNode_Timer::ExecuteInput(const FName& PinName) { - if (CompletionTime < 0.0f) - { - LogError(TEXT("Invalid Timer settings")); - TriggerOutput(TEXT("Completed"), true); - return; - } - if (PinName == TEXT("In")) { if (CompletionTimerHandle.IsValid() || StepTimerHandle.IsValid()) From 6aa0598e8040719325e2f7d77b49b7bd355d21a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Thu, 11 May 2023 23:58:48 +0200 Subject: [PATCH 168/485] Flow Asset no longers requires Start Node to be always a default entry node. It's still a default behavior, but you can change it by overriding UFlowAsset::GetDefaultEntryNode()! It opens door to use Custom Inputs as default node in customized Flow Asset classes. --- Source/Flow/Private/FlowAsset.cpp | 26 ++++++++++---------------- Source/Flow/Public/FlowAsset.h | 9 ++------- 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index c560dadbe..86dd4420f 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -25,7 +25,6 @@ UFlowAsset::UFlowAsset(const FObjectInitializer& ObjectInitializer) , AllowedNodeClasses({UFlowNode::StaticClass()}) , bStartNodePlacedAsGhostNode(false) , TemplateAsset(nullptr) - , StartNode(nullptr) , FinishPolicy(EFlowFinishPolicy::Keep) { if (!AssetGuid.IsValid()) @@ -203,18 +202,17 @@ void UFlowAsset::RemoveCustomInput(const FName& InName) CustomInputs.Remove(InName); } -UFlowNode_Start* UFlowAsset::GetStartNode() const +UFlowNode* UFlowAsset::GetDefaultEntryNode() const { for (const TPair& Node : Nodes) { - // there can be only one, automatically added while creating graph - if (UFlowNode_Start* TestedNode = Cast(Node.Value)) + UFlowNode_Start* StartNode = Cast(Node.Value); + if (StartNode && StartNode->GetConnectedNodes().Num() > 0) { - return TestedNode; + return StartNode; } } - // shouldn't ever get here, Start Node is a default node that can't be deleted by user return nullptr; } @@ -310,12 +308,6 @@ void UFlowAsset::InitializeInstance(const TWeakObjectPtr InOwner, UFlow UFlowNode* NewNodeInstance = NewObject(this, Node.Value->GetClass(), NAME_None, RF_Transient, Node.Value, false, nullptr); Node.Value = NewNodeInstance; - // there can be only one, automatically added while creating graph - if (UFlowNode_Start* InNode = Cast(NewNodeInstance)) - { - StartNode = InNode; - } - if (UFlowNode_CustomInput* CustomInput = Cast(NewNodeInstance)) { if (!CustomInput->EventName.IsNone()) @@ -342,7 +334,7 @@ void UFlowAsset::DeinitializeInstance() void UFlowAsset::PreloadNodes() { - TArray GraphEntryNodes = {StartNode}; + TArray GraphEntryNodes = {GetDefaultEntryNode()}; for (UFlowNode_CustomInput* CustomInput : CustomInputNodes) { GraphEntryNodes.Emplace(CustomInput); @@ -393,9 +385,11 @@ void UFlowAsset::StartFlow() { PreStartFlow(); - ensureAlways(StartNode); - RecordedNodes.Add(StartNode); - StartNode->TriggerFirstOutput(true); + if (UFlowNode* ConnectedEntryNode = GetDefaultEntryNode()) + { + RecordedNodes.Add(ConnectedEntryNode); + ConnectedEntryNode->TriggerFirstOutput(true); + } } void UFlowAsset::FinishFlow(const EFlowFinishPolicy InFinishPolicy, const bool bRemoveInstance /*= true*/) diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 86d8b8219..4d7e02232 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -11,7 +11,6 @@ #include "FlowAsset.generated.h" class UFlowNode_CustomInput; -class UFlowNode_Start; class UFlowNode_SubGraph; class UFlowSubsystem; @@ -149,14 +148,14 @@ class FLOW_API UFlowAsset : public UObject return nullptr; } - UFlowNode_Start* GetStartNode() const; + virtual UFlowNode* GetDefaultEntryNode() const; template void GetNodesInExecutionOrder(TArray& OutNodes) { static_assert(TPointerIsConvertibleFromTo::Value, "'T' template parameter to GetNodesInExecutionOrder must be derived from UFlowNode"); - if (UFlowNode_Start* FoundStartNode = GetStartNode()) + if (UFlowNode* FoundStartNode = GetDefaultEntryNode()) { TSet> IteratedNodes; GetNodesInExecutionOrder_Recursive(FoundStartNode, IteratedNodes, OutNodes); @@ -252,10 +251,6 @@ class FLOW_API UFlowAsset : public UObject // Flow Asset instances created by SubGraph nodes placed in the current graph TMap, TWeakObjectPtr> ActiveSubGraphs; - // Execution of the graph always starts from this node, there can be only one StartNode in the graph - UPROPERTY() - UFlowNode_Start* StartNode; - // Optional entry points to the graph, similar to blueprint Custom Events UPROPERTY() TSet CustomInputNodes; From 766131cc5faba372cf403ca4da0cb284aad3fb91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Fri, 12 May 2023 00:03:09 +0200 Subject: [PATCH 169/485] tweaking #154: changes to add/remove Custom Inputs - wrap it with WITH_EDITOR - replace check with with simple if-statement, we don't editor to crash on accidentally provided invalid data --- Source/Flow/Private/FlowAsset.cpp | 14 +++++++++----- Source/Flow/Public/FlowAsset.h | 2 ++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 86dd4420f..af26ab62a 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -188,19 +188,23 @@ void UFlowAsset::HarvestNodeConnections() } } } -#endif // WITH_EDITOR void UFlowAsset::AddCustomInput(const FName& InName) { - check(!CustomInputs.Contains(InName)); - CustomInputs.Add(InName); + if (!CustomInputs.Contains(InName)) + { + CustomInputs.Add(InName); + } } void UFlowAsset::RemoveCustomInput(const FName& InName) { - check(CustomInputs.Contains(InName)); - CustomInputs.Remove(InName); + if (CustomInputs.Contains(InName)) + { + CustomInputs.Remove(InName); + } } +#endif // WITH_EDITOR UFlowNode* UFlowAsset::GetDefaultEntryNode() const { diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 4d7e02232..8ab10aaa6 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -187,8 +187,10 @@ class FLOW_API UFlowAsset : public UObject const TArray& GetCustomOutputs() const { return CustomOutputs; } protected: +#if WITH_EDITOR void AddCustomInput(const FName& InName); void RemoveCustomInput(const FName& InName); +#endif ////////////////////////////////////////////////////////////////////////// // Instances of the template asset From 04d299615b8bae8e157cf6e7a8ad5a72d445e49e Mon Sep 17 00:00:00 2001 From: twevs <77587819+twevs@users.noreply.github.com> Date: Tue, 16 May 2023 15:25:29 +0100 Subject: [PATCH 170/485] Play Level Sequence: fixed 'Pause at End' option. (#156) --- .../Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp index b8d3e246d..8e2fafdc7 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp @@ -270,7 +270,10 @@ void UFlowNode_PlayLevelSequence::Cleanup() { SequencePlayer->SetFlowEventReceiver(nullptr); SequencePlayer->OnFinished.RemoveAll(this); - SequencePlayer->Stop(); + if (!PlaybackSettings.bPauseAtEnd) + { + SequencePlayer->Stop(); + } SequencePlayer = nullptr; } From 6faefa6d4af263b993ddebcd8c7272617ab4cb8c Mon Sep 17 00:00:00 2001 From: Soraphis Date: Fri, 26 May 2023 13:01:09 +0200 Subject: [PATCH 171/485] feat: made 'AbortActiveFlows' blueprint callable (#158) --- Source/Flow/Public/FlowSubsystem.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Flow/Public/FlowSubsystem.h b/Source/Flow/Public/FlowSubsystem.h index 3a3b5b47d..ca9b3aa86 100644 --- a/Source/Flow/Public/FlowSubsystem.h +++ b/Source/Flow/Public/FlowSubsystem.h @@ -71,6 +71,7 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem virtual void Initialize(FSubsystemCollectionBase& Collection) override; virtual void Deinitialize() override; + UFUNCTION(BlueprintCallable, Category = "FlowSubsystem") virtual void AbortActiveFlows(); /* Start the root Flow, graph that will eventually instantiate next Flow Graphs through the SubGraph node */ From da929526da60366ce92a2116b933f4180ddc8757 Mon Sep 17 00:00:00 2001 From: LindyHopperGT <91915878+LindyHopperGT@users.noreply.github.com> Date: Fri, 26 May 2023 13:38:19 -0700 Subject: [PATCH 172/485] CustomOutput and quality of life changes (#157) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * formatting fix * Fixed rare issue with loading saves - prevent triggering input on not-yet-loaded node * wording corrected * extracted graph-related code from the FFlowAssetEditor to new SFlowGraphEditor class added FFlowAssetEditor::IsTabFocused() to prevent delete/paste/copy nodes if graph tab isn't focused * fixed graph commands * #131 fixed updating pin names below removed pin * quick CIS compilation fix * another CIS fix attempt * redundant include removed * fixed showing static descriptions in PIE/SIE renamed flag to better reflect its role * add null flow asset check when calling StartRootFlow (#132) Co-authored-by: Krzysiek Justyński * Add custom color support to nodes (#135) * readability tweak * non-editor compilation fix * Added includes to fix compiler errors When compiling for UE 5.1 (Win64) we ran into some compile errors due to missing header includes. * Added includes to fix compiler errors (#152) When compiling for UE 5.1 (Win64) we ran into some compile errors due to missing header includes. * Fixed bug where Owner parameter wasn't being used Owner was passed into this function, presumably to be used instead of 'this', but it was just using 'this'. Changed the code to use Owner if specified, and default to 'this' otherwise. * Fixed bug where Owner parameter wasn't being used (#153) * Added includes to fix compiler errors When compiling for UE 5.1 (Win64) we ran into some compile errors due to missing header includes. * Fixed bug where Owner parameter wasn't being used Owner was passed into this function, presumably to be used instead of 'this', but it was just using 'this'. Changed the code to use Owner if specified, and default to 'this' otherwise. * comforting convention of separating engine headers from plugin headers * Extending the "GetAssignedGraphNodeClass()" function to support to return the correc "EdGraphNode" for grand child classes of "UFlowNode" (#151) Previously it would return the first found "IsChildOf" and return the EdGraphNode of the foundclass. But lets say we got this situation just for example: UFlowNode->UMyBaseNode UFlowNode->UMyBaseNode->UMyQuestNode Both "UMyBaseNode" and "UMyQuestNode" could be in the list if we want different EdGraphNodes for both of them. If the "UMyQuestNode" goes into the itteration and it would find "UMyBaseNode" first it will be returned without checking the rest, because "UMyQuestNode" is dirived from "UMyBaseNode" (IsChildOf == true) and thus it returns the EdGraphNode of the base node directly. So that would mean I got the wrong EdGraphNode for my special quest node. This modification continues to try and find parent classes and returns the closest parent version. So lets say we have this situation: UFlowNode->UMyBaseNode UFlowNode->UMyBaseNode->UMyQuestNode->MyOtherQuestNode(BP class) Both “UMyBaseNode” and “UMyQuestNode” would be found, but the closest parent to “MyOtherQuestNode” would be returned, in this case the EdGraphNode of “UMyQuestNode” It only does this if we have found 2 or more parents though. If only 1 is found we would return that one, and if the exact class match is found lets say: "UMyQuestNode" finds "UMyQuestNode" it will return the EdGraphNode right away aswell. Hopefully this makes sence :P Otherwise feel free to ask. * Show pretty readable pin name even if friendly name is not provided (#140) * Show pretty readable pin name even if friendly name is not provided * Add missing else. Silly mistake 😋 * cosmetic tweaks of last PR * rework of PR #140 - added bEnforceFriendlyPinNames as separate flag from the Unreal editor config - moved creating a display name to `UFlowGraphSchema::GetPinDisplayName` without modifying graph node's instance * CustomInput & Output Details Customization and other cleanup * Added forward declarations & includes that were needed to compile * Changed some functions to return const &'s rather than doing a full deep copy of a member container * Explicitly calling MoveTemp() to use move semantics (supported by UE container classes (eg TArray)) to move the array without an extra copy. Some compilers might be able to do this optimization automatically, but being explicit here. * Introduced a superclass to UFlowNode_CustomInput and Output. The diff is easier if you compare them to the new superclass, since most of the code moved into it. * The "details" superclass for CustomInput/Output fixes a discovered bug where the list of EventNames was not recaching correctly. * Moved a bit of code from PlayLevelSequence to FlowNode.h (TryGetRootFlowActorOwner) so that it can be used in other contexts. Also provided a component version of this same code. * The FlowGraphSchema will now create default nodes for any CustomInputs that exist when the asset is first created. * Added a UseAdaptiveNodeTitles option to optionally make CustomInputs integrate their EventName into the title for the node. This defaults to false (to preserve previous behavior by default). * Exposed CustomInput Add/Remove functions on UFlowNode to allow subclasses to modify the CustomInputs array. * Additional includes for compiler errors * Update FlowAsset.cpp non-editor configurations compile fix for previous changelist. * Removing MoveTemp for local variables According to this guidance, it's no longer a performance improvement. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#f48-dont-return-stdmovelocal * Extended CustomOutputs and other quality of life improvements - Custom Outputs now can be listened to by the owning component - Added a DeinitializeInstance() call to FlowNode (called when its FlowAsset instance owner is deinitialized) - Added Edit Asset Defaults button to Flow Editor * Code fixes after merge with latest * Removed these replaced files * Moves some stuff around * tweaks * revert this --------- Co-authored-by: Krzysiek Justyński Co-authored-by: Bohdon Sayre Co-authored-by: Satheesh (ryanjon2040) Co-authored-by: Alex van Mansom <45290811+Vi-So@users.noreply.github.com> --- Flow.uplugin | 39 +++--- Source/Flow/Private/FlowAsset.cpp | 131 +++++++++++++++--- .../Nodes/Route/FlowNode_CustomOutput.cpp | 40 +++++- .../Private/Nodes/Route/FlowNode_SubGraph.cpp | 2 +- Source/Flow/Public/FlowAsset.h | 27 +++- Source/Flow/Public/FlowComponent.h | 27 ++++ Source/Flow/Public/Nodes/FlowNode.h | 4 + .../Private/Asset/FlowAssetEditor.cpp | 9 ++ .../Private/Asset/FlowAssetFactory.cpp | 12 +- .../Private/Asset/FlowAssetToolbar.cpp | 1 + .../Private/Asset/FlowDiffControl.cpp | 1 + Source/FlowEditor/Private/Asset/SFlowDiff.cpp | 5 + .../FlowEditor/Private/FlowEditorCommands.cpp | 2 + Source/FlowEditor/Private/FlowEditorStyle.cpp | 3 +- .../Private/Graph/FlowGraphEditor.cpp | 1 - .../FlowEditor/Public/Asset/FlowAssetEditor.h | 2 + .../Public/Asset/FlowAssetFactory.h | 5 + Source/FlowEditor/Public/Asset/SFlowDiff.h | 3 + Source/FlowEditor/Public/FlowEditorCommands.h | 1 + 19 files changed, 260 insertions(+), 55 deletions(-) diff --git a/Flow.uplugin b/Flow.uplugin index 63fdda0b8..bda540af6 100644 --- a/Flow.uplugin +++ b/Flow.uplugin @@ -1,29 +1,28 @@ { - "FileVersion" : 3, - "Version" : 1.4, - "FriendlyName" : "Flow", - "Description" : "Design-agnostic node editor for scripting game’s flow.", - "Category" : "Gameplay", - "CreatedByURL" : "https://github.com/MothCocoon/FlowGraph/graphs/contributors", - "DocsURL" : "https://github.com/MothCocoon/FlowGraph/wiki", - "MarketplaceURL" : "", + "FileVersion": 3, + "Version": 1.4, + "FriendlyName": "Flow", + "Description": "Design-agnostic node editor for scripting game’s flow.", + "Category": "Gameplay", + "CreatedByURL": "https://github.com/MothCocoon/FlowGraph/graphs/contributors", + "DocsURL": "https://github.com/MothCocoon/FlowGraph/wiki", + "MarketplaceURL": "", "SupportURL": "https://discord.gg/zMtMQ2vUUa", "EngineAssociation": "5.2", - "EnabledByDefault" : true, - "CanContainContent" : false, - "IsBetaVersion" : false, - "Installed" : false, - "Modules" : - [ + "EnabledByDefault": true, + "CanContainContent": false, + "IsBetaVersion": false, + "Installed": false, + "Modules": [ { - "Name" : "Flow", - "Type" : "Runtime", - "LoadingPhase" : "PreDefault" + "Name": "Flow", + "Type": "Runtime", + "LoadingPhase": "PreDefault" }, { - "Name" : "FlowEditor", - "Type" : "Editor", - "LoadingPhase" : "Default" + "Name": "FlowEditor", + "Type": "Editor", + "LoadingPhase": "Default" } ], "Plugins": [ diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index af26ab62a..a579b14ed 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -189,35 +189,73 @@ void UFlowAsset::HarvestNodeConnections() } } -void UFlowAsset::AddCustomInput(const FName& InName) +void UFlowAsset::AddCustomInput(const FName& EventName) { - if (!CustomInputs.Contains(InName)) + if (!CustomInputs.Contains(EventName)) { - CustomInputs.Add(InName); + CustomInputs.Add(EventName); } } -void UFlowAsset::RemoveCustomInput(const FName& InName) +void UFlowAsset::RemoveCustomInput(const FName& EventName) { - if (CustomInputs.Contains(InName)) + if (CustomInputs.Contains(EventName)) { - CustomInputs.Remove(InName); + CustomInputs.Remove(EventName); } } + +void UFlowAsset::AddCustomOutput(const FName& EventName) +{ + check(!CustomOutputs.Contains(EventName)); + + CustomOutputs.Add(EventName); +} + +void UFlowAsset::RemoveCustomOutput(const FName& EventName) +{ + check(CustomOutputs.Contains(EventName)); + + CustomOutputs.Remove(EventName); +} #endif // WITH_EDITOR +UFlowNode_CustomInput* UFlowAsset::TryFindCustomInputNodeByEventName(const FName& EventName) const +{ + for (UFlowNode_CustomInput* InputNode : CustomInputNodes) + { + if (IsValid(InputNode) && InputNode->GetEventName() == EventName) + { + return InputNode; + } + } + + return nullptr; +} + UFlowNode* UFlowAsset::GetDefaultEntryNode() const { + UFlowNode* FirstStartNode = nullptr; + for (const TPair& Node : Nodes) { UFlowNode_Start* StartNode = Cast(Node.Value); - if (StartNode && StartNode->GetConnectedNodes().Num() > 0) + if (StartNode) { - return StartNode; + if (StartNode->GetConnectedNodes().Num() > 0) + { + return StartNode; + } + else if (!FirstStartNode) + { + FirstStartNode = StartNode; + } } } - return nullptr; + // If none of the found start nodes have connections, + // fallback to the first start node we found + return FirstStartNode; } void UFlowAsset::AddInstance(UFlowAsset* Instance) @@ -300,7 +338,7 @@ void UFlowAsset::BroadcastRuntimeMessageAdded(const TSharedRef InOwner, UFlowAsset* InTemplateAsset) { @@ -326,6 +364,17 @@ void UFlowAsset::InitializeInstance(const TWeakObjectPtr InOwner, UFlow void UFlowAsset::DeinitializeInstance() { + for (auto& KV : Nodes) + { + const FGuid& Guid = KV.Key; + UFlowNode* Node = KV.Value; + + if (IsValid(Node)) + { + Node->DeinitializeInstance(); + } + } + if (TemplateAsset) { const int32 ActiveInstancesLeft = TemplateAsset->RemoveInstance(this); @@ -421,34 +470,76 @@ void UFlowAsset::FinishFlow(const EFlowFinishPolicy InFinishPolicy, const bool b } } +bool UFlowAsset::HasStartedFlow() const +{ + return RecordedNodes.Num() > 0; +} + +AActor* UFlowAsset::TryFindActorOwner() const +{ + UActorComponent* OwnerAsComponent = Cast(GetOwner()); + if (IsValid(OwnerAsComponent)) + { + return Cast(OwnerAsComponent->GetOwner()); + } + + return nullptr; +} + TWeakObjectPtr UFlowAsset::GetFlowInstance(UFlowNode_SubGraph* SubGraphNode) const { return ActiveSubGraphs.FindRef(SubGraphNode); } -void UFlowAsset::TriggerCustomEvent(UFlowNode_SubGraph* Node, const FName& EventName) const +void UFlowAsset::TriggerSubgraphCustomInput(UFlowNode_SubGraph& Node, const FName& EventName) const { - const TWeakObjectPtr FlowInstance = ActiveSubGraphs.FindRef(Node); + check(HasStartedFlow()); + + const TWeakObjectPtr FlowInstance = ActiveSubGraphs.FindRef(&Node); if (FlowInstance.IsValid()) { - for (UFlowNode_CustomInput* CustomInput : FlowInstance->CustomInputNodes) + FlowInstance->TriggerCustomInput(EventName); + } +} + +void UFlowAsset::TriggerCustomInput(const FName& EventName) +{ + check(HasStartedFlow()); + + for (UFlowNode_CustomInput* CustomInput : CustomInputNodes) + { + if (CustomInput->EventName == EventName) { - if (CustomInput->EventName == EventName) - { - FlowInstance->RecordedNodes.Add(CustomInput); - CustomInput->TriggerFirstOutput(true); - } + RecordedNodes.Add(CustomInput); + + CustomInput->ExecuteInput(EventName); } } } -void UFlowAsset::TriggerCustomOutput(const FName& EventName) const +void UFlowAsset::TriggerCustomOutput(const FName& EventName) { - NodeOwningThisAssetInstance->TriggerOutput(EventName); + check(HasStartedFlow()); + + const bool bIsSubgraph = NodeOwningThisAssetInstance.IsValid(); + if (bIsSubgraph) + { + NodeOwningThisAssetInstance->TriggerOutput(EventName); + } + else + { + // Non-subgraphs are Root instances, so call the OnTriggerCustomOutputEventDispatcher + if (UFlowComponent* FlowComponent = Cast(GetOwner())) + { + FlowComponent->OnTriggerRootFlowOutputEventDispatcher(*this, EventName); + } + } } void UFlowAsset::TriggerInput(const FGuid& NodeGuid, const FName& PinName) { + check(HasStartedFlow()); + if (UFlowNode* Node = Nodes.FindRef(NodeGuid)) { if (!ActiveNodes.Contains(Node)) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp index e6826702f..4fe382e5f 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp @@ -2,6 +2,7 @@ #include "Nodes/Route/FlowNode_CustomOutput.h" #include "FlowAsset.h" +#include "FlowMessageLog.h" #include "FlowSettings.h" #define LOCTEXT_NAMESPACE "FlowNode_CustomOutput" @@ -14,9 +15,44 @@ UFlowNode_CustomOutput::UFlowNode_CustomOutput(const FObjectInitializer& ObjectI void UFlowNode_CustomOutput::ExecuteInput(const FName& PinName) { - if (!EventName.IsNone() && GetFlowAsset()->GetCustomOutputs().Contains(EventName) && GetFlowAsset()->GetNodeOwningThisAssetInstance()) + UFlowAsset* FlowAsset = GetFlowAsset(); + check(IsValid(FlowAsset)); + + if (EventName.IsNone()) + { + LogWarning( + FString::Printf( + TEXT("Attempted to trigger a CustomOutput (Node %s, Asset %s), with no EventName"), + *GetName(), + *FlowAsset->GetPathName(), + *EventName.ToString())); + } + else if (!FlowAsset->GetCustomOutputs().Contains(EventName)) + { + const TArray& CustomOutputNames = FlowAsset->GetCustomOutputs(); + + FString CustomOutputsString; + for (const FName& OutputName : CustomOutputNames) + { + if (!CustomOutputsString.IsEmpty()) + { + CustomOutputsString += TEXT(", "); + } + + CustomOutputsString += OutputName.ToString(); + } + + LogWarning( + FString::Printf( + TEXT("Attempted to trigger a CustomOutput (Node %s, Asset %s), with EventName %s, which is not a listed CustomOutput { %s }"), + *GetName(), + *FlowAsset->GetPathName(), + *EventName.ToString(), + *CustomOutputsString)); + } + else { - GetFlowAsset()->TriggerCustomOutput(EventName); + FlowAsset->TriggerCustomOutput(EventName); } } diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp index 04bbe1e68..6f4d5af8d 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp @@ -69,7 +69,7 @@ void UFlowNode_SubGraph::ExecuteInput(const FName& PinName) } else if (!PinName.IsNone()) { - GetFlowAsset()->TriggerCustomEvent(this, PinName); + GetFlowAsset()->TriggerSubgraphCustomInput(*this, PinName); } } diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 8ab10aaa6..859b73040 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -186,12 +186,17 @@ class FLOW_API UFlowAsset : public UObject const TArray& GetCustomInputs() const { return CustomInputs; } const TArray& GetCustomOutputs() const { return CustomOutputs; } -protected: + UFlowNode_CustomInput* TryFindCustomInputNodeByEventName(const FName& EventName) const; + #if WITH_EDITOR - void AddCustomInput(const FName& InName); - void RemoveCustomInput(const FName& InName); -#endif +protected: + void AddCustomInput(const FName& EventName); + void RemoveCustomInput(const FName& EventName); + void AddCustomOutput(const FName& EventName); + void RemoveCustomOutput(const FName& EventName); +#endif // WITH_EDITOR + ////////////////////////////////////////////////////////////////////////// // Instances of the template asset @@ -287,6 +292,10 @@ class FLOW_API UFlowAsset : public UObject return Owner.IsValid() ? Cast(Owner) : nullptr; } + // Returns the Owner as an Actor, or if Owner is a Component, return its Owner as an Actor + UFUNCTION(BlueprintPure, Category = "Flow") + AActor* TryFindActorOwner() const; + virtual void PreloadNodes(); virtual void PreStartFlow(); @@ -294,12 +303,16 @@ class FLOW_API UFlowAsset : public UObject virtual void FinishFlow(const EFlowFinishPolicy InFinishPolicy, const bool bRemoveInstance = true); + bool HasStartedFlow() const; + void TriggerCustomInput(const FName& EventName); + // Get Flow Asset instance created by the given SubGraph node TWeakObjectPtr GetFlowInstance(UFlowNode_SubGraph* SubGraphNode) const; -private: - void TriggerCustomEvent(UFlowNode_SubGraph* Node, const FName& EventName) const; - void TriggerCustomOutput(const FName& EventName) const; +protected: + // Call TriggerCustomInput on the subgraph for Node + void TriggerSubgraphCustomInput(UFlowNode_SubGraph& Node, const FName& EventName) const; + void TriggerCustomOutput(const FName& EventName); void TriggerInput(const FGuid& NodeGuid, const FName& PinName); diff --git a/Source/Flow/Public/FlowComponent.h b/Source/Flow/Public/FlowComponent.h index f815e9cd5..d8ff6809f 100644 --- a/Source/Flow/Public/FlowComponent.h +++ b/Source/Flow/Public/FlowComponent.h @@ -203,9 +203,23 @@ class FLOW_API UFlowComponent : public UActorComponent UFUNCTION(BlueprintPure, Category = "RootFlow", meta = (DeprecatedFunction, DeprecationMessage="Use GetRootInstances() instead.")) UFlowAsset* GetRootFlowInstance() const; +////////////////////////////////////////////////////////////////////////// +// UFlowComponent overrideable events + +public: + // Called when a Root flow asset triggers a CustomOutput + UFUNCTION(BlueprintImplementableEvent, DisplayName = "OnTriggerRootFlowOutputEvent") + void BP_OnTriggerRootFlowOutputEvent(UFlowAsset* RootFlowInstance, const FName& EventName); + virtual void OnTriggerRootFlowOutputEvent(UFlowAsset& RootFlowInstance, const FName& EventName) { } + + //~Begin UFlowAsset-only access + FORCEINLINE void OnTriggerRootFlowOutputEventDispatcher(UFlowAsset& RootFlowInstance, const FName& EventName); + //~End UFlowAsset-only access + ////////////////////////////////////////////////////////////////////////// // SaveGame +public: UFUNCTION(BlueprintCallable, Category = "SaveGame") virtual void SaveRootFlow(TArray& SavedFlowInstances); @@ -232,3 +246,16 @@ class FLOW_API UFlowComponent : public UActorComponent UFlowSubsystem* GetFlowSubsystem() const; bool IsFlowNetMode(const EFlowNetMode NetMode) const; }; + + +// Inline Implementations + +void UFlowComponent::OnTriggerRootFlowOutputEventDispatcher(UFlowAsset& RootFlowInstance, const FName& EventName) +{ + // Call the blueprint overrideable function + BP_OnTriggerRootFlowOutputEvent(&RootFlowInstance, EventName); + + // Call the C++ overrideable function + OnTriggerRootFlowOutputEvent(RootFlowInstance, EventName); +} + diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index f5bfc91b8..f40e87a42 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -332,6 +332,10 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Cleanup")) void K2_Cleanup(); + // Method called from UFlowAsset::DeinitializeInstance() + // (ie, when deinitializing the flow asset itself) + virtual void DeinitializeInstance() { } + public: // Define what happens when node is terminated from the outside virtual void ForceFinishNode(); diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index 4c0b65c49..e3e519038 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -358,6 +358,10 @@ void FFlowAssetEditor::BindToolbarCommands() FExecuteAction::CreateSP(this, &FFlowAssetEditor::ValidateAsset_Internal), FCanExecuteAction()); + ToolkitCommands->MapAction(ToolbarCommands.EditAssetDefaults, + FExecuteAction::CreateSP(this, &FFlowAssetEditor::EditAssetDefaults_Clicked), + FCanExecuteAction()); + #if ENABLE_SEARCH_IN_ASSET_EDITOR ToolkitCommands->MapAction(ToolbarCommands.SearchInAsset, FExecuteAction::CreateSP(this, &FFlowAssetEditor::SearchInAsset), @@ -401,6 +405,11 @@ void FFlowAssetEditor::ValidateAsset(FFlowMessageLog& MessageLog) FlowAsset->ValidateAsset(MessageLog); } +void FFlowAssetEditor::EditAssetDefaults_Clicked() +{ + DetailsView->SetObject(FlowAsset); +} + #if ENABLE_SEARCH_IN_ASSET_EDITOR void FFlowAssetEditor::SearchInAsset() { diff --git a/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp b/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp index d111fa79b..c0900979a 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp @@ -68,6 +68,13 @@ UFlowAssetFactory::UFlowAssetFactory(const FObjectInitializer& ObjectInitializer } bool UFlowAssetFactory::ConfigureProperties() +{ + const FText TitleText = LOCTEXT("CreateFlowAssetOptions", "Pick Flow Asset Class"); + + return ConfigurePropertiesInternal(TitleText); +} + +bool UFlowAssetFactory::ConfigurePropertiesInternal(const FText& TitleText) { AssetClass = UFlowGraphSettings::Get()->DefaultFlowAssetClass; if (AssetClass) // Class was set in settings @@ -84,13 +91,12 @@ bool UFlowAssetFactory::ConfigureProperties() const TSharedPtr Filter = MakeShareable(new FAssetClassParentFilter); Filter->DisallowedClassFlags = CLASS_Abstract | CLASS_Deprecated | CLASS_NewerVersionExists | CLASS_HideDropDown; - Filter->AllowedChildrenOfClasses.Add(UFlowAsset::StaticClass()); + Filter->AllowedChildrenOfClasses.Add(SupportedClass); Options.ClassFilters = {Filter.ToSharedRef()}; - const FText TitleText = LOCTEXT("CreateFlowAssetOptions", "Pick Flow Asset Class"); UClass* ChosenClass = nullptr; - const bool bPressedOk = SClassPickerDialog::PickClass(TitleText, Options, ChosenClass, UFlowAsset::StaticClass()); + const bool bPressedOk = SClassPickerDialog::PickClass(TitleText, Options, ChosenClass, SupportedClass); if (bPressedOk) { diff --git a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp index 1ccee3ac6..1bf3644f7 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp @@ -202,6 +202,7 @@ void FFlowAssetToolbar::BuildAssetToolbar(UToolMenu* ToolbarMenu) const // add buttons Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().RefreshAsset)); Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().ValidateAsset)); + Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().EditAssetDefaults)); } { diff --git a/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp b/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp index b53f4cf61..b770a4b4c 100644 --- a/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp +++ b/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp @@ -5,6 +5,7 @@ #include "FlowAsset.h" +#include "EdGraph/EdGraph.h" #include "GraphDiffControl.h" #include "SBlueprintDiff.h" diff --git a/Source/FlowEditor/Private/Asset/SFlowDiff.cpp b/Source/FlowEditor/Private/Asset/SFlowDiff.cpp index 3dddec73d..97499dbb4 100644 --- a/Source/FlowEditor/Private/Asset/SFlowDiff.cpp +++ b/Source/FlowEditor/Private/Asset/SFlowDiff.cpp @@ -6,12 +6,17 @@ #include "FlowAsset.h" #include "EdGraphUtilities.h" +#include "Editor.h" #include "Framework/Commands/GenericCommands.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" +#include "Framework/MultiBox/MultiBoxDefs.h" #include "GraphDiffControl.h" #include "HAL/PlatformApplicationMisc.h" #include "Internationalization/Text.h" #include "SBlueprintDiff.h" #include "SlateOptMacros.h" +#include "Subsystems/AssetEditorSubsystem.h" +#include "Widgets/Layout/SSpacer.h" #define LOCTEXT_NAMESPACE "SFlowDiff" diff --git a/Source/FlowEditor/Private/FlowEditorCommands.cpp b/Source/FlowEditor/Private/FlowEditorCommands.cpp index 99779f654..4a2b4c33c 100644 --- a/Source/FlowEditor/Private/FlowEditorCommands.cpp +++ b/Source/FlowEditor/Private/FlowEditorCommands.cpp @@ -7,6 +7,7 @@ #include "Nodes/FlowNode.h" #include "Misc/ConfigCacheIni.h" +#include "Styling/AppStyle.h" #define LOCTEXT_NAMESPACE "FlowGraphCommands" @@ -19,6 +20,7 @@ void FFlowToolbarCommands::RegisterCommands() { UI_COMMAND(RefreshAsset, "Refresh", "Refresh asset and all nodes", EUserInterfaceActionType::Button, FInputChord()); UI_COMMAND(ValidateAsset, "Validate", "Validate asset and all nodes", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(EditAssetDefaults, "Asset Defaults", "Edit the FlowAsset default properties", EUserInterfaceActionType::Button, FInputChord()); UI_COMMAND(SearchInAsset, "Search", "Search in the current Flow Graph", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control | EModifierKey::Shift, EKeys::F)); UI_COMMAND(GoToParentInstance, "Go To Parent", "Open editor for the Flow Asset that created this Flow instance", EUserInterfaceActionType::Button, FInputChord()); diff --git a/Source/FlowEditor/Private/FlowEditorStyle.cpp b/Source/FlowEditor/Private/FlowEditorStyle.cpp index 19d331783..ddc63376b 100644 --- a/Source/FlowEditor/Private/FlowEditorStyle.cpp +++ b/Source/FlowEditor/Private/FlowEditorStyle.cpp @@ -33,7 +33,8 @@ void FFlowEditorStyle::Initialize() StyleSet->Set("FlowToolbar.RefreshAsset", new IMAGE_BRUSH_SVG( "Starship/Common/Apply", Icon20)); StyleSet->Set("FlowToolbar.ValidateAsset", new IMAGE_BRUSH_SVG( "Starship/Common/Debug", Icon20)); - + StyleSet->Set("FlowToolbar.EditAssetDefaults", new IMAGE_BRUSH_SVG("Starship/Common/Details", Icon20)); + StyleSet->Set("FlowToolbar.SearchInAsset", new IMAGE_BRUSH_SVG( "Starship/Common/Search", Icon20)); StyleSet->Set("FlowToolbar.GoToParentInstance", new IMAGE_BRUSH("Icons/icon_DebugStepOut_40x", Icon40)); diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index 0a9230a80..7759ef52e 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -36,7 +36,6 @@ void SFlowGraphEditor::Construct(const FArguments& InArgs, const TSharedPtrGetGraph(); Arguments._GraphEvents = InArgs._GraphEvents; Arguments._AutoExpandActionMenu = true; - Arguments._GraphEvents.OnSelectionChanged = FOnSelectionChanged::CreateSP(this, &SFlowGraphEditor::OnSelectedNodesChanged); Arguments._GraphEvents.OnNodeDoubleClicked = FSingleNodeEvent::CreateSP(this, &SFlowGraphEditor::OnNodeDoubleClicked); Arguments._GraphEvents.OnTextCommitted = FOnNodeTextCommitted::CreateSP(this, &SFlowGraphEditor::OnNodeTitleCommitted); diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index 44645c432..96b82dddf 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -124,6 +124,8 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit virtual void RefreshAsset(); + void EditAssetDefaults_Clicked(); + private: void ValidateAsset_Internal(); diff --git a/Source/FlowEditor/Public/Asset/FlowAssetFactory.h b/Source/FlowEditor/Public/Asset/FlowAssetFactory.h index e1c3f5274..b4297672d 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetFactory.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetFactory.h @@ -15,4 +15,9 @@ class FLOWEDITOR_API UFlowAssetFactory : public UFactory virtual bool ConfigureProperties() override; virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; + +protected: + + // Parameterized guts of ConfigureProperties() + bool ConfigurePropertiesInternal(const FText& TitleText); }; diff --git a/Source/FlowEditor/Public/Asset/SFlowDiff.h b/Source/FlowEditor/Public/Asset/SFlowDiff.h index e65f27018..b9ac9e6d9 100644 --- a/Source/FlowEditor/Public/Asset/SFlowDiff.h +++ b/Source/FlowEditor/Public/Asset/SFlowDiff.h @@ -2,7 +2,10 @@ #pragma once +#include "IDetailsView.h" +#include "DiffResults.h" #include "SDetailsDiff.h" +#include "Textures/SlateIcon.h" struct FFlowGraphToDiff; class UFlowAsset; diff --git a/Source/FlowEditor/Public/FlowEditorCommands.h b/Source/FlowEditor/Public/FlowEditorCommands.h index a1b12da0a..356245d76 100644 --- a/Source/FlowEditor/Public/FlowEditorCommands.h +++ b/Source/FlowEditor/Public/FlowEditorCommands.h @@ -14,6 +14,7 @@ class FLOWEDITOR_API FFlowToolbarCommands : public TCommands RefreshAsset; TSharedPtr ValidateAsset; + TSharedPtr EditAssetDefaults; TSharedPtr SearchInAsset; TSharedPtr GoToParentInstance; From 0fbd1bb6d60e42c49a1525147499593879aa1791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sat, 27 May 2023 00:17:01 +0200 Subject: [PATCH 173/485] mostly cosmetic tweaks to the last pull request - removed check() as this plugin avoids it as much as possible - moved the Asset Defaults button after Diff and Search buttons, this is more consistent with a blueprint toolbar --- Flow.uplugin | 41 +++++++------- Source/Flow/Private/FlowAsset.cpp | 54 +++++++------------ Source/Flow/Private/FlowComponent.cpp | 6 +++ Source/Flow/Private/Nodes/FlowNode.cpp | 5 ++ .../Nodes/Route/FlowNode_CustomOutput.cpp | 26 ++++----- .../Private/Nodes/Route/FlowNode_SubGraph.cpp | 2 +- Source/Flow/Public/FlowAsset.h | 3 +- Source/Flow/Public/FlowComponent.h | 22 ++------ Source/Flow/Public/Nodes/FlowNode.h | 9 ++-- .../Private/Asset/FlowAssetEditor.cpp | 24 ++++----- .../Private/Asset/FlowAssetToolbar.cpp | 11 ++-- .../FlowEditor/Private/FlowEditorCommands.cpp | 3 +- Source/FlowEditor/Private/FlowEditorStyle.cpp | 3 +- .../FlowEditor/Public/Asset/FlowAssetEditor.h | 4 +- .../Public/Asset/FlowAssetFactory.h | 1 - Source/FlowEditor/Public/FlowEditorCommands.h | 3 +- 16 files changed, 97 insertions(+), 120 deletions(-) diff --git a/Flow.uplugin b/Flow.uplugin index bda540af6..4879ba3f1 100644 --- a/Flow.uplugin +++ b/Flow.uplugin @@ -1,28 +1,29 @@ { - "FileVersion": 3, - "Version": 1.4, - "FriendlyName": "Flow", - "Description": "Design-agnostic node editor for scripting game’s flow.", - "Category": "Gameplay", - "CreatedByURL": "https://github.com/MothCocoon/FlowGraph/graphs/contributors", - "DocsURL": "https://github.com/MothCocoon/FlowGraph/wiki", - "MarketplaceURL": "", + "FileVersion" : 3, + "Version" : 1.4, + "FriendlyName" : "Flow", + "Description" : "Design-agnostic node editor for scripting game’s flow.", + "Category" : "Gameplay", + "CreatedByURL" : "https://github.com/MothCocoon/FlowGraph/graphs/contributors", + "DocsURL" : "https://github.com/MothCocoon/FlowGraph/wiki", + "MarketplaceURL" : "", "SupportURL": "https://discord.gg/zMtMQ2vUUa", - "EngineAssociation": "5.2", - "EnabledByDefault": true, - "CanContainContent": false, - "IsBetaVersion": false, - "Installed": false, - "Modules": [ + "EngineAssociation": "5.1", + "EnabledByDefault" : true, + "CanContainContent" : false, + "IsBetaVersion" : false, + "Installed" : false, + "Modules" : + [ { - "Name": "Flow", - "Type": "Runtime", - "LoadingPhase": "PreDefault" + "Name" : "Flow", + "Type" : "Runtime", + "LoadingPhase" : "PreDefault" }, { - "Name": "FlowEditor", - "Type": "Editor", - "LoadingPhase": "Default" + "Name" : "FlowEditor", + "Type" : "Editor", + "LoadingPhase" : "Default" } ], "Plugins": [ diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index a579b14ed..f32ff95c5 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -207,16 +207,18 @@ void UFlowAsset::RemoveCustomInput(const FName& EventName) void UFlowAsset::AddCustomOutput(const FName& EventName) { - check(!CustomOutputs.Contains(EventName)); - - CustomOutputs.Add(EventName); + if (!CustomOutputs.Contains(EventName)) + { + CustomOutputs.Add(EventName); + } } void UFlowAsset::RemoveCustomOutput(const FName& EventName) { - check(CustomOutputs.Contains(EventName)); - - CustomOutputs.Remove(EventName); + if (CustomOutputs.Contains(EventName)) + { + CustomOutputs.Remove(EventName); + } } #endif // WITH_EDITOR @@ -239,22 +241,20 @@ UFlowNode* UFlowAsset::GetDefaultEntryNode() const for (const TPair& Node : Nodes) { - UFlowNode_Start* StartNode = Cast(Node.Value); - if (StartNode) + if (UFlowNode_Start* StartNode = Cast(Node.Value)) { if (StartNode->GetConnectedNodes().Num() > 0) { return StartNode; } - else if (!FirstStartNode) + else if (FirstStartNode == nullptr) { FirstStartNode = StartNode; } } } - // If none of the found start nodes have connections, - // fallback to the first start node we found + // If none of the found start nodes have connections, fallback to the first start node we found return FirstStartNode; } @@ -364,14 +364,11 @@ void UFlowAsset::InitializeInstance(const TWeakObjectPtr InOwner, UFlow void UFlowAsset::DeinitializeInstance() { - for (auto& KV : Nodes) + for (const TPair& Node : Nodes) { - const FGuid& Guid = KV.Key; - UFlowNode* Node = KV.Value; - - if (IsValid(Node)) + if (IsValid(Node.Value)) { - Node->DeinitializeInstance(); + Node.Value->DeinitializeInstance(); } } @@ -477,7 +474,7 @@ bool UFlowAsset::HasStartedFlow() const AActor* UFlowAsset::TryFindActorOwner() const { - UActorComponent* OwnerAsComponent = Cast(GetOwner()); + const UActorComponent* OwnerAsComponent = Cast(GetOwner()); if (IsValid(OwnerAsComponent)) { return Cast(OwnerAsComponent->GetOwner()); @@ -491,11 +488,9 @@ TWeakObjectPtr UFlowAsset::GetFlowInstance(UFlowNode_SubGraph* SubGr return ActiveSubGraphs.FindRef(SubGraphNode); } -void UFlowAsset::TriggerSubgraphCustomInput(UFlowNode_SubGraph& Node, const FName& EventName) const +void UFlowAsset::TriggerCustomInput_FromSubGraph(UFlowNode_SubGraph* Node, const FName& EventName) const { - check(HasStartedFlow()); - - const TWeakObjectPtr FlowInstance = ActiveSubGraphs.FindRef(&Node); + const TWeakObjectPtr FlowInstance = ActiveSubGraphs.FindRef(Node); if (FlowInstance.IsValid()) { FlowInstance->TriggerCustomInput(EventName); @@ -504,14 +499,11 @@ void UFlowAsset::TriggerSubgraphCustomInput(UFlowNode_SubGraph& Node, const FNam void UFlowAsset::TriggerCustomInput(const FName& EventName) { - check(HasStartedFlow()); - for (UFlowNode_CustomInput* CustomInput : CustomInputNodes) { if (CustomInput->EventName == EventName) { RecordedNodes.Add(CustomInput); - CustomInput->ExecuteInput(EventName); } } @@ -519,27 +511,21 @@ void UFlowAsset::TriggerCustomInput(const FName& EventName) void UFlowAsset::TriggerCustomOutput(const FName& EventName) { - check(HasStartedFlow()); - - const bool bIsSubgraph = NodeOwningThisAssetInstance.IsValid(); - if (bIsSubgraph) + if (NodeOwningThisAssetInstance.IsValid()) // it's a SubGraph { NodeOwningThisAssetInstance->TriggerOutput(EventName); } - else + else // it's a Root Flow, so the intention here might be to call event on the Flow Component { - // Non-subgraphs are Root instances, so call the OnTriggerCustomOutputEventDispatcher if (UFlowComponent* FlowComponent = Cast(GetOwner())) { - FlowComponent->OnTriggerRootFlowOutputEventDispatcher(*this, EventName); + FlowComponent->OnTriggerRootFlowOutputEventDispatcher(this, EventName); } } } void UFlowAsset::TriggerInput(const FGuid& NodeGuid, const FName& PinName) { - check(HasStartedFlow()); - if (UFlowNode* Node = Nodes.FindRef(NodeGuid)) { if (!ActiveNodes.Contains(Node)) diff --git a/Source/Flow/Private/FlowComponent.cpp b/Source/Flow/Private/FlowComponent.cpp index 6575e49f6..7864a6461 100644 --- a/Source/Flow/Private/FlowComponent.cpp +++ b/Source/Flow/Private/FlowComponent.cpp @@ -409,6 +409,12 @@ UFlowAsset* UFlowComponent::GetRootFlowInstance() const return nullptr; } +void UFlowComponent::OnTriggerRootFlowOutputEventDispatcher(UFlowAsset* RootFlowInstance, const FName& EventName) +{ + BP_OnTriggerRootFlowOutputEvent(RootFlowInstance, EventName); + OnTriggerRootFlowOutputEvent(RootFlowInstance, EventName); +} + void UFlowComponent::SaveRootFlow(TArray& SavedFlowInstances) { if (UFlowAsset* FlowAssetInstance = GetRootFlowInstance()) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index d63de4cd9..3747503c9 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -620,6 +620,11 @@ void UFlowNode::Cleanup() K2_Cleanup(); } +void UFlowNode::DeinitializeInstance() +{ + K2_DeinitializeInstance(); +} + void UFlowNode::ForceFinishNode() { K2_ForceFinishNode(); diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp index 4fe382e5f..a8a8a96cf 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp @@ -2,7 +2,6 @@ #include "Nodes/Route/FlowNode_CustomOutput.h" #include "FlowAsset.h" -#include "FlowMessageLog.h" #include "FlowSettings.h" #define LOCTEXT_NAMESPACE "FlowNode_CustomOutput" @@ -20,19 +19,14 @@ void UFlowNode_CustomOutput::ExecuteInput(const FName& PinName) if (EventName.IsNone()) { - LogWarning( - FString::Printf( - TEXT("Attempted to trigger a CustomOutput (Node %s, Asset %s), with no EventName"), - *GetName(), - *FlowAsset->GetPathName(), - *EventName.ToString())); + LogWarning(FString::Printf(TEXT("Attempted to trigger a CustomOutput (Node %s, Asset %s), with no EventName"), + *GetName(), + *FlowAsset->GetPathName())); } else if (!FlowAsset->GetCustomOutputs().Contains(EventName)) { - const TArray& CustomOutputNames = FlowAsset->GetCustomOutputs(); - FString CustomOutputsString; - for (const FName& OutputName : CustomOutputNames) + for (const FName& OutputName : FlowAsset->GetCustomOutputs()) { if (!CustomOutputsString.IsEmpty()) { @@ -42,13 +36,11 @@ void UFlowNode_CustomOutput::ExecuteInput(const FName& PinName) CustomOutputsString += OutputName.ToString(); } - LogWarning( - FString::Printf( - TEXT("Attempted to trigger a CustomOutput (Node %s, Asset %s), with EventName %s, which is not a listed CustomOutput { %s }"), - *GetName(), - *FlowAsset->GetPathName(), - *EventName.ToString(), - *CustomOutputsString)); + LogWarning(FString::Printf(TEXT("Attempted to trigger a CustomOutput (Node %s, Asset %s), with EventName %s, which is not a listed CustomOutput { %s }"), + *GetName(), + *FlowAsset->GetPathName(), + *EventName.ToString(), + *CustomOutputsString)); } else { diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp index 6f4d5af8d..1745624f1 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp @@ -69,7 +69,7 @@ void UFlowNode_SubGraph::ExecuteInput(const FName& PinName) } else if (!PinName.IsNone()) { - GetFlowAsset()->TriggerSubgraphCustomInput(*this, PinName); + GetFlowAsset()->TriggerCustomInput_FromSubGraph(this, PinName); } } diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 859b73040..a2e1f9aed 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -310,8 +310,7 @@ class FLOW_API UFlowAsset : public UObject TWeakObjectPtr GetFlowInstance(UFlowNode_SubGraph* SubGraphNode) const; protected: - // Call TriggerCustomInput on the subgraph for Node - void TriggerSubgraphCustomInput(UFlowNode_SubGraph& Node, const FName& EventName) const; + void TriggerCustomInput_FromSubGraph(UFlowNode_SubGraph* Node, const FName& EventName) const; void TriggerCustomOutput(const FName& EventName); void TriggerInput(const FGuid& NodeGuid, const FName& PinName); diff --git a/Source/Flow/Public/FlowComponent.h b/Source/Flow/Public/FlowComponent.h index d8ff6809f..2c74fac96 100644 --- a/Source/Flow/Public/FlowComponent.h +++ b/Source/Flow/Public/FlowComponent.h @@ -210,11 +210,12 @@ class FLOW_API UFlowComponent : public UActorComponent // Called when a Root flow asset triggers a CustomOutput UFUNCTION(BlueprintImplementableEvent, DisplayName = "OnTriggerRootFlowOutputEvent") void BP_OnTriggerRootFlowOutputEvent(UFlowAsset* RootFlowInstance, const FName& EventName); - virtual void OnTriggerRootFlowOutputEvent(UFlowAsset& RootFlowInstance, const FName& EventName) { } - //~Begin UFlowAsset-only access - FORCEINLINE void OnTriggerRootFlowOutputEventDispatcher(UFlowAsset& RootFlowInstance, const FName& EventName); - //~End UFlowAsset-only access + virtual void OnTriggerRootFlowOutputEvent(UFlowAsset* RootFlowInstance, const FName& EventName) {} + + // UFlowAsset-only access + FORCEINLINE void OnTriggerRootFlowOutputEventDispatcher(UFlowAsset* RootFlowInstance, const FName& EventName); + // --- ////////////////////////////////////////////////////////////////////////// // SaveGame @@ -246,16 +247,3 @@ class FLOW_API UFlowComponent : public UActorComponent UFlowSubsystem* GetFlowSubsystem() const; bool IsFlowNetMode(const EFlowNetMode NetMode) const; }; - - -// Inline Implementations - -void UFlowComponent::OnTriggerRootFlowOutputEventDispatcher(UFlowAsset& RootFlowInstance, const FName& EventName) -{ - // Call the blueprint overrideable function - BP_OnTriggerRootFlowOutputEvent(&RootFlowInstance, EventName); - - // Call the C++ overrideable function - OnTriggerRootFlowOutputEvent(RootFlowInstance, EventName); -} - diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index f40e87a42..e46f92fe6 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -332,9 +332,12 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Cleanup")) void K2_Cleanup(); - // Method called from UFlowAsset::DeinitializeInstance() - // (ie, when deinitializing the flow asset itself) - virtual void DeinitializeInstance() { } + // Method called from UFlowAsset::DeinitializeInstance() + virtual void DeinitializeInstance(); + + // Event called from UFlowAsset::DeinitializeInstance() + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "DeinitializeInstance")) + void K2_DeinitializeInstance(); public: // Define what happens when node is terminated from the outside diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index e3e519038..5eba227c0 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -14,23 +14,17 @@ #include "Graph/Widgets/SFlowPalette.h" #include "FlowAsset.h" -#include "Nodes/FlowNode.h" -#include "EdGraphUtilities.h" #include "EdGraph/EdGraphNode.h" #include "Editor.h" #include "GraphEditor.h" -#include "GraphEditorActions.h" -#include "HAL/PlatformApplicationMisc.h" #include "IDetailsView.h" #include "IMessageLogListing.h" #include "Kismet2/DebuggerCommands.h" -#include "LevelEditor.h" #include "MessageLogModule.h" #include "Misc/UObjectToken.h" #include "Modules/ModuleManager.h" #include "PropertyEditorModule.h" -#include "SNodePanel.h" #include "ToolMenus.h" #include "Widgets/Docking/SDockTab.h" @@ -358,16 +352,16 @@ void FFlowAssetEditor::BindToolbarCommands() FExecuteAction::CreateSP(this, &FFlowAssetEditor::ValidateAsset_Internal), FCanExecuteAction()); - ToolkitCommands->MapAction(ToolbarCommands.EditAssetDefaults, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::EditAssetDefaults_Clicked), - FCanExecuteAction()); - #if ENABLE_SEARCH_IN_ASSET_EDITOR ToolkitCommands->MapAction(ToolbarCommands.SearchInAsset, FExecuteAction::CreateSP(this, &FFlowAssetEditor::SearchInAsset), FCanExecuteAction()); #endif + ToolkitCommands->MapAction(ToolbarCommands.EditAssetDefaults, + FExecuteAction::CreateSP(this, &FFlowAssetEditor::EditAssetDefaults_Clicked), + FCanExecuteAction()); + // Engine's Play commands ToolkitCommands->Append(FPlayWorldCommands::GlobalPlayWorldActions.ToSharedRef()); @@ -405,11 +399,6 @@ void FFlowAssetEditor::ValidateAsset(FFlowMessageLog& MessageLog) FlowAsset->ValidateAsset(MessageLog); } -void FFlowAssetEditor::EditAssetDefaults_Clicked() -{ - DetailsView->SetObject(FlowAsset); -} - #if ENABLE_SEARCH_IN_ASSET_EDITOR void FFlowAssetEditor::SearchInAsset() { @@ -418,6 +407,11 @@ void FFlowAssetEditor::SearchInAsset() } #endif +void FFlowAssetEditor::EditAssetDefaults_Clicked() const +{ + DetailsView->SetObject(FlowAsset); +} + void FFlowAssetEditor::GoToParentInstance() { const UFlowAsset* AssetThatInstancedThisAsset = FlowAsset->GetInspectedInstance()->GetParentInstance(); diff --git a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp index 1bf3644f7..d023a4e8c 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp @@ -202,17 +202,12 @@ void FFlowAssetToolbar::BuildAssetToolbar(UToolMenu* ToolbarMenu) const // add buttons Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().RefreshAsset)); Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().ValidateAsset)); - Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().EditAssetDefaults)); } { FToolMenuSection& Section = ToolbarMenu->AddSection("View"); Section.InsertPosition = FToolMenuInsert("FlowAsset", EToolMenuInsertType::After); -#if ENABLE_SEARCH_IN_ASSET_EDITOR - Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().SearchInAsset)); -#endif - // Visual Diff: menu to choose asset revision compared with the current one Section.AddDynamicEntry("SourceControlCommands", FNewToolMenuSectionDelegate::CreateLambda([this](FToolMenuSection& InSection) { @@ -228,6 +223,12 @@ void FFlowAssetToolbar::BuildAssetToolbar(UToolMenu* ToolbarMenu) const DiffEntry.StyleNameOverride = "CalloutToolbar"; InSection.AddEntry(DiffEntry); })); + +#if ENABLE_SEARCH_IN_ASSET_EDITOR + Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().SearchInAsset)); +#endif + + Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().EditAssetDefaults)); } } diff --git a/Source/FlowEditor/Private/FlowEditorCommands.cpp b/Source/FlowEditor/Private/FlowEditorCommands.cpp index 4a2b4c33c..da038b917 100644 --- a/Source/FlowEditor/Private/FlowEditorCommands.cpp +++ b/Source/FlowEditor/Private/FlowEditorCommands.cpp @@ -20,9 +20,10 @@ void FFlowToolbarCommands::RegisterCommands() { UI_COMMAND(RefreshAsset, "Refresh", "Refresh asset and all nodes", EUserInterfaceActionType::Button, FInputChord()); UI_COMMAND(ValidateAsset, "Validate", "Validate asset and all nodes", EUserInterfaceActionType::Button, FInputChord()); - UI_COMMAND(EditAssetDefaults, "Asset Defaults", "Edit the FlowAsset default properties", EUserInterfaceActionType::Button, FInputChord()); UI_COMMAND(SearchInAsset, "Search", "Search in the current Flow Graph", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control | EModifierKey::Shift, EKeys::F)); + UI_COMMAND(EditAssetDefaults, "Asset Defaults", "Edit the FlowAsset default properties", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(GoToParentInstance, "Go To Parent", "Open editor for the Flow Asset that created this Flow instance", EUserInterfaceActionType::Button, FInputChord()); } diff --git a/Source/FlowEditor/Private/FlowEditorStyle.cpp b/Source/FlowEditor/Private/FlowEditorStyle.cpp index ddc63376b..f39409450 100644 --- a/Source/FlowEditor/Private/FlowEditorStyle.cpp +++ b/Source/FlowEditor/Private/FlowEditorStyle.cpp @@ -33,9 +33,10 @@ void FFlowEditorStyle::Initialize() StyleSet->Set("FlowToolbar.RefreshAsset", new IMAGE_BRUSH_SVG( "Starship/Common/Apply", Icon20)); StyleSet->Set("FlowToolbar.ValidateAsset", new IMAGE_BRUSH_SVG( "Starship/Common/Debug", Icon20)); - StyleSet->Set("FlowToolbar.EditAssetDefaults", new IMAGE_BRUSH_SVG("Starship/Common/Details", Icon20)); StyleSet->Set("FlowToolbar.SearchInAsset", new IMAGE_BRUSH_SVG( "Starship/Common/Search", Icon20)); + StyleSet->Set("FlowToolbar.EditAssetDefaults", new IMAGE_BRUSH_SVG("Starship/Common/Details", Icon20)); + StyleSet->Set("FlowToolbar.GoToParentInstance", new IMAGE_BRUSH("Icons/icon_DebugStepOut_40x", Icon40)); StyleSet->Set("FlowGraph.BreakpointEnabled", new IMAGE_BRUSH("Old/Kismet2/Breakpoint_Valid", FVector2D(24.0f, 24.0f))); diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index 96b82dddf..3b14d32f8 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -124,8 +124,6 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit virtual void RefreshAsset(); - void EditAssetDefaults_Clicked(); - private: void ValidateAsset_Internal(); @@ -136,6 +134,8 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit virtual void SearchInAsset(); #endif + void EditAssetDefaults_Clicked() const; + virtual void GoToParentInstance(); virtual bool CanGoToParentInstance(); diff --git a/Source/FlowEditor/Public/Asset/FlowAssetFactory.h b/Source/FlowEditor/Public/Asset/FlowAssetFactory.h index b4297672d..5e6fd011a 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetFactory.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetFactory.h @@ -17,7 +17,6 @@ class FLOWEDITOR_API UFlowAssetFactory : public UFactory virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; protected: - // Parameterized guts of ConfigureProperties() bool ConfigurePropertiesInternal(const FText& TitleText); }; diff --git a/Source/FlowEditor/Public/FlowEditorCommands.h b/Source/FlowEditor/Public/FlowEditorCommands.h index 356245d76..3f4e873ab 100644 --- a/Source/FlowEditor/Public/FlowEditorCommands.h +++ b/Source/FlowEditor/Public/FlowEditorCommands.h @@ -14,9 +14,10 @@ class FLOWEDITOR_API FFlowToolbarCommands : public TCommands RefreshAsset; TSharedPtr ValidateAsset; - TSharedPtr EditAssetDefaults; TSharedPtr SearchInAsset; + TSharedPtr EditAssetDefaults; + TSharedPtr GoToParentInstance; virtual void RegisterCommands() override; From ae009490f8f145e5d2eac93459a6aca6454aca21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 28 May 2023 21:45:35 +0200 Subject: [PATCH 174/485] removed empty overrides --- .../Flow/Private/Nodes/World/FlowNode_OnActorRegistered.cpp | 5 ----- .../Private/Nodes/World/FlowNode_OnActorUnregistered.cpp | 5 ----- .../Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp | 5 ----- Source/Flow/Public/Nodes/World/FlowNode_OnActorRegistered.h | 2 -- .../Flow/Public/Nodes/World/FlowNode_OnActorUnregistered.h | 2 -- Source/Flow/Public/Nodes/World/FlowNode_OnNotifyFromActor.h | 2 -- 6 files changed, 21 deletions(-) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_OnActorRegistered.cpp b/Source/Flow/Private/Nodes/World/FlowNode_OnActorRegistered.cpp index 6ad8ca537..ba6a712ef 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_OnActorRegistered.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_OnActorRegistered.cpp @@ -7,11 +7,6 @@ UFlowNode_OnActorRegistered::UFlowNode_OnActorRegistered(const FObjectInitialize { } -void UFlowNode_OnActorRegistered::ExecuteInput(const FName& PinName) -{ - Super::ExecuteInput(PinName); -} - void UFlowNode_OnActorRegistered::ObserveActor(TWeakObjectPtr Actor, TWeakObjectPtr Component) { if (!RegisteredActors.Contains(Actor)) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_OnActorUnregistered.cpp b/Source/Flow/Private/Nodes/World/FlowNode_OnActorUnregistered.cpp index a802ff41e..488e63b2f 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_OnActorUnregistered.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_OnActorUnregistered.cpp @@ -7,11 +7,6 @@ UFlowNode_OnActorUnregistered::UFlowNode_OnActorUnregistered(const FObjectInitia { } -void UFlowNode_OnActorUnregistered::ExecuteInput(const FName& PinName) -{ - Super::ExecuteInput(PinName); -} - void UFlowNode_OnActorUnregistered::ObserveActor(TWeakObjectPtr Actor, TWeakObjectPtr Component) { if (!RegisteredActors.Contains(Actor)) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp b/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp index ae59ecf3a..477f1a1ed 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp @@ -13,11 +13,6 @@ UFlowNode_OnNotifyFromActor::UFlowNode_OnNotifyFromActor(const FObjectInitialize #endif } -void UFlowNode_OnNotifyFromActor::ExecuteInput(const FName& PinName) -{ - Super::ExecuteInput(PinName); -} - void UFlowNode_OnNotifyFromActor::ObserveActor(TWeakObjectPtr Actor, TWeakObjectPtr Component) { if (!RegisteredActors.Contains(Actor)) diff --git a/Source/Flow/Public/Nodes/World/FlowNode_OnActorRegistered.h b/Source/Flow/Public/Nodes/World/FlowNode_OnActorRegistered.h index ecf4b9e56..d4045c667 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_OnActorRegistered.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_OnActorRegistered.h @@ -14,7 +14,5 @@ class FLOW_API UFlowNode_OnActorRegistered : public UFlowNode_ComponentObserver GENERATED_UCLASS_BODY() protected: - virtual void ExecuteInput(const FName& PinName) override; - virtual void ObserveActor(TWeakObjectPtr Actor, TWeakObjectPtr Component) override; }; diff --git a/Source/Flow/Public/Nodes/World/FlowNode_OnActorUnregistered.h b/Source/Flow/Public/Nodes/World/FlowNode_OnActorUnregistered.h index 4d44d740f..655e98082 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_OnActorUnregistered.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_OnActorUnregistered.h @@ -14,8 +14,6 @@ class FLOW_API UFlowNode_OnActorUnregistered : public UFlowNode_ComponentObserve GENERATED_UCLASS_BODY() protected: - virtual void ExecuteInput(const FName& PinName) override; - virtual void ObserveActor(TWeakObjectPtr Actor, TWeakObjectPtr Component) override; virtual void ForgetActor(TWeakObjectPtr Actor, TWeakObjectPtr Component) override; }; diff --git a/Source/Flow/Public/Nodes/World/FlowNode_OnNotifyFromActor.h b/Source/Flow/Public/Nodes/World/FlowNode_OnNotifyFromActor.h index 9c5f77a7d..b64c4068b 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_OnNotifyFromActor.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_OnNotifyFromActor.h @@ -22,8 +22,6 @@ class FLOW_API UFlowNode_OnNotifyFromActor : public UFlowNode_ComponentObserver UPROPERTY(EditAnywhere, Category = "Notify") bool bRetroactive; - virtual void ExecuteInput(const FName& PinName) override; - virtual void ObserveActor(TWeakObjectPtr Actor, TWeakObjectPtr Component) override; virtual void ForgetActor(TWeakObjectPtr Actor, TWeakObjectPtr Component) override; From 9703e7db2b2dc01c4d4ca48e196b377ec6ef3249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Mon, 5 Jun 2023 19:14:36 +0200 Subject: [PATCH 175/485] exposed GetNodesInExecutionOrder() to blueprints --- Source/Flow/Private/FlowAsset.cpp | 18 ++++++++++++++++++ Source/Flow/Public/FlowAsset.h | 3 +++ 2 files changed, 21 insertions(+) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index f32ff95c5..e7a1a1a09 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -258,6 +258,24 @@ UFlowNode* UFlowAsset::GetDefaultEntryNode() const return FirstStartNode; } +TArray UFlowAsset::GetNodesInExecutionOrder(const TSubclassOf FlowNodeClass) +{ + TArray FoundNodes; + GetNodesInExecutionOrder(FoundNodes); + + // filter out nodes by class + for (int32 i = FoundNodes.Num() - 1; i >= 0; i--) + { + if (!FoundNodes[i]->GetClass()->IsChildOf(FlowNodeClass)) + { + FoundNodes.RemoveAt(i); + } + } + FoundNodes.Shrink(); + + return FoundNodes; +} + void UFlowAsset::AddInstance(UFlowAsset* Instance) { ActiveInstances.Add(Instance); diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index a2e1f9aed..ff417c168 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -150,6 +150,9 @@ class FLOW_API UFlowAsset : public UObject virtual UFlowNode* GetDefaultEntryNode() const; + UFUNCTION(BlueprintPure, Category = "FlowAsset", meta = (DeterminesOutputType = "FlowNodeClass")) + TArray GetNodesInExecutionOrder(const TSubclassOf FlowNodeClass); + template void GetNodesInExecutionOrder(TArray& OutNodes) { From 3d9dfba68baedd8692dcbb6dfba86fe1c75143b4 Mon Sep 17 00:00:00 2001 From: Guy Lundvall <47373221+Drakynfly@users.noreply.github.com> Date: Sun, 18 Jun 2023 11:04:52 -0700 Subject: [PATCH 176/485] Add class hyperlink (#161) --- .../Private/Asset/FlowAssetEditor.cpp | 25 +++++++++++++++++++ .../FlowEditor/Public/Asset/FlowAssetEditor.h | 1 + 2 files changed, 26 insertions(+) diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index 5eba227c0..508bcf9bf 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -17,6 +17,7 @@ #include "EdGraph/EdGraphNode.h" #include "Editor.h" +#include "EditorClassUtils.h" #include "GraphEditor.h" #include "IDetailsView.h" #include "IMessageLogListing.h" @@ -163,6 +164,30 @@ void FFlowAssetEditor::InitToolMenuContext(FToolMenuContext& MenuContext) MenuContext.AddObject(Context); } +void FFlowAssetEditor::PostRegenerateMenusAndToolbars() +{ + // Provide a hyperlink to view our class + const TSharedRef MenuOverlayBox = SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Center) + [ + SNew(STextBlock) + .ColorAndOpacity(FSlateColor::UseSubduedForeground()) + .ShadowOffset(FVector2D::UnitVector) + .Text(LOCTEXT("FlowAssetEditor_AssetType", "Asset Type: ")) + ] + + SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Center) + .Padding(0.0f, 0.0f, 8.0f, 0.0f) + [ + FEditorClassUtils::GetSourceLink(FlowAsset->GetClass()) + ]; + + SetMenuOverlay(MenuOverlayBox); +} + bool FFlowAssetEditor::IsTabFocused(const FTabId& TabId) const { if (const TSharedPtr CurrentGraphTab = GetToolkitHost()->GetTabManager()->FindExistingLiveTab(TabId)) diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index 3b14d32f8..93a8c80c0 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -100,6 +100,7 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit // FAssetEditorToolkit virtual void InitToolMenuContext(FToolMenuContext& MenuContext) override; + virtual void PostRegenerateMenusAndToolbars() override; // -- bool IsTabFocused(const FTabId& TabId) const; From d6e4db16f995ff16b84f231c6d04ffcbb5108b60 Mon Sep 17 00:00:00 2001 From: twevs <77587819+twevs@users.noreply.github.com> Date: Sun, 18 Jun 2023 19:05:02 +0100 Subject: [PATCH 177/485] Added Pause and Resume input pins. (#162) --- .../Private/Nodes/World/FlowNode_PlayLevelSequence.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp index 8e2fafdc7..aabf92412 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp @@ -38,6 +38,8 @@ UFlowNode_PlayLevelSequence::UFlowNode_PlayLevelSequence(const FObjectInitialize InputPins.Empty(); InputPins.Add(FFlowPin(TEXT("Start"))); InputPins.Add(FFlowPin(TEXT("Stop"))); + InputPins.Add(FFlowPin(TEXT("Pause"))); + InputPins.Add(FFlowPin(TEXT("Resume"))); OutputPins.Add(FFlowPin(TEXT("PreStart"))); OutputPins.Add(FFlowPin(TEXT("Started"))); @@ -192,6 +194,14 @@ void UFlowNode_PlayLevelSequence::ExecuteInput(const FName& PinName) { StopPlayback(); } + else if (PinName == TEXT("Pause")) + { + SequencePlayer->Pause(); + } + else if (PinName == TEXT("Resume") && SequencePlayer->IsPaused()) + { + SequencePlayer->Play(); + } } void UFlowNode_PlayLevelSequence::OnSave_Implementation() From aa89b0982a87ca7eaacdaee5fe95323ae2d53e5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 18 Jun 2023 20:15:25 +0200 Subject: [PATCH 178/485] moved Stop input to the bottom --- Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp index aabf92412..d058ab6c3 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp @@ -37,9 +37,9 @@ UFlowNode_PlayLevelSequence::UFlowNode_PlayLevelSequence(const FObjectInitialize InputPins.Empty(); InputPins.Add(FFlowPin(TEXT("Start"))); - InputPins.Add(FFlowPin(TEXT("Stop"))); InputPins.Add(FFlowPin(TEXT("Pause"))); InputPins.Add(FFlowPin(TEXT("Resume"))); + InputPins.Add(FFlowPin(TEXT("Stop"))); OutputPins.Add(FFlowPin(TEXT("PreStart"))); OutputPins.Add(FFlowPin(TEXT("Started"))); From 6e02439fb0c93a332d0cac41d80b83f5013998ba Mon Sep 17 00:00:00 2001 From: Eddie Ataberk <48696693+eddieataberk@users.noreply.github.com> Date: Wed, 21 Jun 2023 18:26:38 +0300 Subject: [PATCH 179/485] It makes GetConnectedNodes accesible from Blueprints (#163) --- Source/Flow/Public/Nodes/FlowNode.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index e46f92fe6..929cb7696 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -219,7 +219,9 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte void SetConnections(const TMap& InConnections) { Connections = InConnections; } FConnectedPin GetConnection(const FName OutputName) const { return Connections.FindRef(OutputName); } + UFUNCTION(BlueprintPure, Category= "FlowNode") TSet GetConnectedNodes() const; + FName GetPinConnectedToNode(const FGuid& OtherNodeGuid); UFUNCTION(BlueprintPure, Category= "FlowNode") From 909a68e6323fc39dabc565c280103f0a8b06624f Mon Sep 17 00:00:00 2001 From: James Keats Date: Wed, 21 Jun 2023 11:28:34 -0400 Subject: [PATCH 180/485] Fix JumpToInnerObject not working when SearchTree provides the GraphNode itself (#164) Co-authored-by: James Keats --- Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index 508bcf9bf..6efbfd44e 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -539,6 +539,10 @@ void FFlowAssetEditor::JumpToInnerObject(UObject* InnerObject) { GraphEditor->JumpToNode(FlowNode->GetGraphNode(), true); } + else if (const UEdGraphNode* GraphNode = Cast(InnerObject)) + { + GraphEditor->JumpToNode(GraphNode, true); + } } #endif From 0f1ece98e35054d2318e42376214e8dd8f40dbd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl?= Date: Sat, 8 Jul 2023 19:10:27 +0300 Subject: [PATCH 181/485] Add validation error on FlowAsset for disallowed node classes (#165) --- Source/Flow/Private/FlowAsset.cpp | 78 +++++++++++++++++++ Source/Flow/Public/FlowAsset.h | 5 ++ .../Private/Graph/FlowGraphSchema.cpp | 59 ++------------ 3 files changed, 89 insertions(+), 53 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index e7a1a1a09..4af039564 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -16,6 +16,10 @@ #include "Serialization/MemoryReader.h" #include "Serialization/MemoryWriter.h" +#if WITH_EDITOR +FString UFlowAsset::ValidationError_NodeClassNotAllowed = TEXT("Node class {0} is not allowed in this asset."); +#endif + UFlowAsset::UFlowAsset(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , bWorldBound(true) @@ -71,6 +75,12 @@ EDataValidationResult UFlowAsset::ValidateAsset(FFlowMessageLog& MessageLog) { if (Node.Value) { + if (!IsNodeClassAllowed(Node.Value->GetClass())) + { + const FString ErrorMsg = FString::Format(*ValidationError_NodeClassNotAllowed, {*Node.Value->GetClass()->GetName()}); + MessageLog.Error(*ErrorMsg, Node.Value); + } + Node.Value->ValidationLog.Messages.Empty(); if (Node.Value->ValidateNode() == EDataValidationResult::Invalid) { @@ -82,6 +92,74 @@ EDataValidationResult UFlowAsset::ValidateAsset(FFlowMessageLog& MessageLog) return MessageLog.Messages.Num() > 0 ? EDataValidationResult::Invalid : EDataValidationResult::Valid; } +bool UFlowAsset::IsNodeClassAllowed(const UClass* FlowNodeClass) const +{ + if (FlowNodeClass == nullptr) + { + return false; + } + + UFlowNode* NodeDefaults = FlowNodeClass->GetDefaultObject(); + + // UFlowNode class limits which UFlowAsset class can use it + { + for (const UClass* DeniedAssetClass : NodeDefaults->DeniedAssetClasses) + { + if (DeniedAssetClass && GetClass()->IsChildOf(DeniedAssetClass)) + { + return false; + } + } + + if (NodeDefaults->AllowedAssetClasses.Num() > 0) + { + bool bAllowedInAsset = false; + for (const UClass* AllowedAssetClass : NodeDefaults->AllowedAssetClasses) + { + if (AllowedAssetClass && GetClass()->IsChildOf(AllowedAssetClass)) + { + bAllowedInAsset = true; + break; + } + } + if (!bAllowedInAsset) + { + return false; + } + } + } + + // UFlowAsset class can limit which UFlowNode classes can be used + { + for (const UClass* DeniedNodeClass : DeniedNodeClasses) + { + if (DeniedNodeClass && FlowNodeClass->IsChildOf(DeniedNodeClass)) + { + return false; + } + } + + if (AllowedNodeClasses.Num() > 0) + { + bool bAllowedInAsset = false; + for (const UClass* AllowedNodeClass : AllowedNodeClasses) + { + if (AllowedNodeClass && FlowNodeClass->IsChildOf(AllowedNodeClass)) + { + bAllowedInAsset = true; + break; + } + } + if (!bAllowedInAsset) + { + return false; + } + } + } + + return true; +} + TSharedPtr UFlowAsset::FlowGraphInterface = nullptr; void UFlowAsset::SetFlowGraphInterface(TSharedPtr InFlowAssetEditor) diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index ff417c168..36904eb07 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -71,6 +71,11 @@ class FLOW_API UFlowAsset : public UObject // -- virtual EDataValidationResult ValidateAsset(FFlowMessageLog& MessageLog); + + // Returns whether the node class is allowed in this flow asset + bool IsNodeClassAllowed(const UClass* FlowNodeClass) const; + + static FString ValidationError_NodeClassNotAllowed; #endif // IFlowGraphInterface diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index cd63d4377..5e6772a74 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -353,64 +353,17 @@ void UFlowGraphSchema::ApplyNodeFilter(const UFlowAsset* AssetClassDefaults, con return; } - UFlowNode* NodeDefaults = FlowNodeClass->GetDefaultObject(); - - // UFlowNode class limits which UFlowAsset class can use it + if (AssetClassDefaults == nullptr) { - for (const UClass* DeniedAssetClass : NodeDefaults->DeniedAssetClasses) - { - if (DeniedAssetClass && AssetClassDefaults->GetClass()->IsChildOf(DeniedAssetClass)) - { - return; - } - } - - if (NodeDefaults->AllowedAssetClasses.Num() > 0) - { - bool bAllowedInAsset = false; - for (const UClass* AllowedAssetClass : NodeDefaults->AllowedAssetClasses) - { - if (AllowedAssetClass && AssetClassDefaults->GetClass()->IsChildOf(AllowedAssetClass)) - { - bAllowedInAsset = true; - break; - } - } - if (!bAllowedInAsset) - { - return; - } - } + return; } - // UFlowAsset class can limit which UFlowNode classes can be used + if (!AssetClassDefaults->IsNodeClassAllowed(FlowNodeClass)) { - for (const UClass* DeniedNodeClass : AssetClassDefaults->DeniedNodeClasses) - { - if (DeniedNodeClass && FlowNodeClass->IsChildOf(DeniedNodeClass)) - { - return; - } - } - - if (AssetClassDefaults->AllowedNodeClasses.Num() > 0) - { - bool bAllowedInAsset = false; - for (const UClass* AllowedNodeClass : AssetClassDefaults->AllowedNodeClasses) - { - if (AllowedNodeClass && FlowNodeClass->IsChildOf(AllowedNodeClass)) - { - bAllowedInAsset = true; - break; - } - } - if (!bAllowedInAsset) - { - return; - } - } + return; } - + + UFlowNode* NodeDefaults = FlowNodeClass->GetDefaultObject(); FilteredNodes.Emplace(NodeDefaults); } From a7ab00db14aaef4b24c51e03499b882815c3d9ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl?= Date: Sat, 8 Jul 2023 19:17:41 +0300 Subject: [PATCH 182/485] Add ability to restrict which flow asset classes can be used in a subgraph node class (#166) --- Source/Flow/Private/FlowAsset.cpp | 1 + .../Private/Nodes/Route/FlowNode_SubGraph.cpp | 2 + Source/Flow/Public/FlowAsset.h | 4 ++ .../Public/Nodes/Route/FlowNode_SubGraph.h | 15 +++- .../FlowNode_SubGraphDetails.cpp | 70 +++++++++++++++++++ .../FlowEditor/Private/FlowEditorModule.cpp | 3 + .../FlowNode_SubGraphDetails.h | 16 +++++ 7 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 Source/FlowEditor/Private/DetailCustomizations/FlowNode_SubGraphDetails.cpp create mode 100644 Source/FlowEditor/Public/DetailCustomizations/FlowNode_SubGraphDetails.h diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 4af039564..cb80b5359 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -27,6 +27,7 @@ UFlowAsset::UFlowAsset(const FObjectInitializer& ObjectInitializer) , FlowGraph(nullptr) #endif , AllowedNodeClasses({UFlowNode::StaticClass()}) + , AllowedInSubgraphNodeClasses({UFlowNode_SubGraph::StaticClass()}) , bStartNodePlacedAsGhostNode(false) , TemplateAsset(nullptr) , FinishPolicy(EFlowFinishPolicy::Keep) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp index 1745624f1..f82a76aeb 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp @@ -16,6 +16,8 @@ UFlowNode_SubGraph::UFlowNode_SubGraph(const FObjectInitializer& ObjectInitializ #if WITH_EDITOR Category = TEXT("Route"); NodeStyle = EFlowNodeStyle::SubGraph; + + AllowedAssignedAssetClasses = {UFlowAsset::StaticClass()}; #endif InputPins = {StartPin}; diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 36904eb07..5e419347c 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -48,6 +48,7 @@ class FLOW_API UFlowAsset : public UObject friend class UFlowSubsystem; friend class FFlowAssetDetails; + friend class FFlowNode_SubGraphDetails; friend class UFlowGraphSchema; UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Flow Asset") @@ -104,6 +105,9 @@ class FLOW_API UFlowAsset : public UObject TArray> AllowedNodeClasses; TArray> DeniedNodeClasses; + TArray> AllowedInSubgraphNodeClasses; + TArray> DeniedInSubgraphNodeClasses; + bool bStartNodePlacedAsGhostNode; private: diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_SubGraph.h b/Source/Flow/Public/Nodes/Route/FlowNode_SubGraph.h index 394e631f9..49dbcf6d1 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_SubGraph.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_SubGraph.h @@ -14,6 +14,7 @@ class FLOW_API UFlowNode_SubGraph : public UFlowNode GENERATED_UCLASS_BODY() friend class UFlowAsset; + friend class FFlowNode_SubGraphDetails; friend class UFlowSubsystem; static FFlowPin StartPin; @@ -48,8 +49,20 @@ class FLOW_API UFlowNode_SubGraph : public UFlowNode protected: virtual void OnLoad_Implementation() override; -public: + +#if WITH_EDITORONLY_DATA +protected: + // All the classes allowed to be used as assets on this subgraph node + UPROPERTY() + TArray> AllowedAssignedAssetClasses; + + // All the classes disallowed to be used as assets on this subgraph node + UPROPERTY() + TArray> DeniedAssignedAssetClasses; +#endif + #if WITH_EDITOR +public: virtual FString GetNodeDescription() const override; virtual UObject* GetAssetToEdit() override; virtual EDataValidationResult ValidateNode() override; diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowNode_SubGraphDetails.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_SubGraphDetails.cpp new file mode 100644 index 000000000..1b6cf0f3f --- /dev/null +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_SubGraphDetails.cpp @@ -0,0 +1,70 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "DetailCustomizations/FlowNode_SubGraphDetails.h" + +#include "DetailLayoutBuilder.h" +#include "FlowAsset.h" +#include "Nodes/Route/FlowNode_SubGraph.h" + +void FFlowNode_SubGraphDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) +{ + TArray> ObjectsBeingEdited; + DetailLayout.GetObjectsBeingCustomized(ObjectsBeingEdited); + + if (ObjectsBeingEdited[0].IsValid()) + { + const UFlowNode_SubGraph* SubGraphNode = CastChecked(ObjectsBeingEdited[0]); + + // Generate the list of asset classes allowed or disallowed + TArray FlowAssetClasses; + GetDerivedClasses(UFlowAsset::StaticClass(), FlowAssetClasses, true); + FlowAssetClasses.Add(UFlowAsset::StaticClass()); + + TArray DisallowedClasses; + TArray AllowedClasses; + for (auto FlowAssetClass : FlowAssetClasses) + { + if (const UFlowAsset* DefaultAsset = Cast(FlowAssetClass->GetDefaultObject())) + { + if (DefaultAsset->DeniedInSubgraphNodeClasses.Contains(SubGraphNode->GetClass())) + { + DisallowedClasses.Add(FlowAssetClass); + } + + if (DefaultAsset->AllowedInSubgraphNodeClasses.Contains(SubGraphNode->GetClass())) + { + AllowedClasses.Add(FlowAssetClass); + } + } + } + + DisallowedClasses.Append(SubGraphNode->DeniedAssignedAssetClasses); + + for (auto FlowAssetClass : SubGraphNode->AllowedAssignedAssetClasses) + { + if (!DisallowedClasses.Contains(FlowAssetClass)) + { + AllowedClasses.AddUnique(FlowAssetClass); + } + } + + FString AllowedClassesString; + for (UClass* Class : AllowedClasses) + { + AllowedClassesString.Append(FString::Printf(TEXT("%s,"), *Class->GetClassPathName().ToString())); + } + + FString DisallowedClassesString; + for (UClass* Class : DisallowedClasses) + { + DisallowedClassesString.Append(FString::Printf(TEXT("%s,"), *Class->GetClassPathName().ToString())); + } + + const auto AssetProperty = DetailLayout.GetProperty(FName("Asset"), UFlowNode_SubGraph::StaticClass()); + if (FProperty* MetaDataProperty = AssetProperty->GetMetaDataProperty()) + { + MetaDataProperty->SetMetaData(TEXT("AllowedClasses"), *AllowedClassesString); + MetaDataProperty->SetMetaData(TEXT("DisallowedClasses"), *DisallowedClassesString); + } + } +} diff --git a/Source/FlowEditor/Private/FlowEditorModule.cpp b/Source/FlowEditor/Private/FlowEditorModule.cpp index 4c91cd4f9..9ea22f098 100644 --- a/Source/FlowEditor/Private/FlowEditorModule.cpp +++ b/Source/FlowEditor/Private/FlowEditorModule.cpp @@ -20,10 +20,12 @@ #include "DetailCustomizations/FlowNode_CustomInputDetails.h" #include "DetailCustomizations/FlowNode_CustomOutputDetails.h" #include "DetailCustomizations/FlowNode_PlayLevelSequenceDetails.h" +#include "DetailCustomizations/FlowNode_SubGraphDetails.h" #include "FlowAsset.h" #include "Nodes/Route/FlowNode_CustomInput.h" #include "Nodes/Route/FlowNode_CustomOutput.h" +#include "Nodes/Route/FlowNode_SubGraph.h" #include "Nodes/World/FlowNode_ComponentObserver.h" #include "Nodes/World/FlowNode_PlayLevelSequence.h" @@ -77,6 +79,7 @@ void FFlowEditorModule::StartupModule() RegisterCustomClassLayout(UFlowNode_CustomInput::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_CustomInputDetails::MakeInstance)); RegisterCustomClassLayout(UFlowNode_CustomOutput::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_CustomOutputDetails::MakeInstance)); RegisterCustomClassLayout(UFlowNode_PlayLevelSequence::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_PlayLevelSequenceDetails::MakeInstance)); + RegisterCustomClassLayout(UFlowNode_SubGraph::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_SubGraphDetails::MakeInstance)); FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked("PropertyEditor"); PropertyModule.NotifyCustomizationModuleChanged(); diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowNode_SubGraphDetails.h b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_SubGraphDetails.h new file mode 100644 index 000000000..323e0b8d1 --- /dev/null +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_SubGraphDetails.h @@ -0,0 +1,16 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "IDetailCustomization.h" + +class FFlowNode_SubGraphDetails final : public IDetailCustomization +{ +public: + static TSharedRef MakeInstance() + { + return MakeShareable(new FFlowNode_SubGraphDetails); + } + + virtual void CustomizeDetails(IDetailLayoutBuilder& DetailLayout) override; +}; From 8e4d57c75cf32f67d9d779c320cca19e89c408dc Mon Sep 17 00:00:00 2001 From: LindyHopperGT <91915878+LindyHopperGT@users.noreply.github.com> Date: Sat, 8 Jul 2023 09:23:09 -0700 Subject: [PATCH 183/485] Updated code to build in 5.1 and 5.2 (#160) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Updated code to build in 5.1 and 5.2 * Added Call Owner Function Node Added Call Owner Function now and supporting code --------- Co-authored-by: Krzysiek Justyński --- Source/Flow/Private/FlowAsset.cpp | 2 + .../Flow/Private/FlowOwnerFunctionParams.cpp | 60 +++ Source/Flow/Private/FlowOwnerFunctionRef.cpp | 87 ++++ Source/Flow/Private/FlowSettings.cpp | 18 + Source/Flow/Private/Nodes/FlowNode.cpp | 75 +++ .../World/FlowNode_CallOwnerFunction.cpp | 477 ++++++++++++++++++ .../World/FlowNode_PlayLevelSequence.cpp | 5 + Source/Flow/Public/FlowAsset.h | 14 + Source/Flow/Public/FlowComponent.h | 5 +- Source/Flow/Public/FlowOwnerFunctionParams.h | 83 +++ Source/Flow/Public/FlowOwnerFunctionRef.h | 59 +++ Source/Flow/Public/FlowOwnerInterface.h | 22 + Source/Flow/Public/FlowSettings.h | 10 + Source/Flow/Public/Nodes/FlowNode.h | 9 + .../Nodes/World/FlowNode_CallOwnerFunction.h | 95 ++++ .../Private/Asset/FlowDiffControl.cpp | 5 + .../FlowOwnerFunctionRefCustomization.cpp | 158 ++++++ .../FlowEditor/Private/FlowEditorModule.cpp | 97 ++-- .../Private/MovieScene/FlowTrackEditor.cpp | 5 + .../IFlowCuratedNamePropertyCustomization.cpp | 262 ++++++++++ ...IFlowExtendedPropertyTypeCustomization.cpp | 82 +++ .../FlowOwnerFunctionRefCustomization.h | 53 ++ Source/FlowEditor/Public/FlowEditorModule.h | 6 +- .../IFlowCuratedNamePropertyCustomization.h | 65 +++ .../IFlowExtendedPropertyTypeCustomization.h | 78 +++ 25 files changed, 1794 insertions(+), 38 deletions(-) create mode 100644 Source/Flow/Private/FlowOwnerFunctionParams.cpp create mode 100644 Source/Flow/Private/FlowOwnerFunctionRef.cpp create mode 100644 Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp create mode 100644 Source/Flow/Public/FlowOwnerFunctionParams.h create mode 100644 Source/Flow/Public/FlowOwnerFunctionRef.h create mode 100644 Source/Flow/Public/FlowOwnerInterface.h create mode 100644 Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h create mode 100644 Source/FlowEditor/Private/DetailCustomizations/FlowOwnerFunctionRefCustomization.cpp create mode 100644 Source/FlowEditor/Private/UnrealExtensions/IFlowCuratedNamePropertyCustomization.cpp create mode 100644 Source/FlowEditor/Private/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.cpp create mode 100644 Source/FlowEditor/Public/DetailCustomizations/FlowOwnerFunctionRefCustomization.h create mode 100644 Source/FlowEditor/Public/UnrealExtensions/IFlowCuratedNamePropertyCustomization.h create mode 100644 Source/FlowEditor/Public/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index cb80b5359..e9ad55237 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -36,6 +36,8 @@ UFlowAsset::UFlowAsset(const FObjectInitializer& ObjectInitializer) { AssetGuid = FGuid::NewGuid(); } + + ExpectedOwnerClass = UFlowSettings::Get()->GetDefaultExpectedOwnerClass(); } #if WITH_EDITOR diff --git a/Source/Flow/Private/FlowOwnerFunctionParams.cpp b/Source/Flow/Private/FlowOwnerFunctionParams.cpp new file mode 100644 index 000000000..5c4af749e --- /dev/null +++ b/Source/Flow/Private/FlowOwnerFunctionParams.cpp @@ -0,0 +1,60 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "FlowOwnerFunctionParams.h" +#include "Nodes/FlowNode.h" +#include "Nodes/World/FlowNode_CallOwnerFunction.h" + + +// UFlowOwnerFunctionParams Implementation + +UFlowOwnerFunctionParams::UFlowOwnerFunctionParams() + : Super() +{ +#if WITH_EDITOR + InputNames.Add(UFlowNode::DefaultInputPin.PinName); + OutputNames.Add(UFlowNode::DefaultOutputPin.PinName); +#endif //WITH_EDITOR +} + +void UFlowOwnerFunctionParams::PreExecute(UFlowNode_CallOwnerFunction& InSourceNode, const FName& InputPinName) +{ + SourceNode = &InSourceNode; + ExecutedInputPinName = InputPinName; + + BP_PreExecute(); +} + +void UFlowOwnerFunctionParams::PostExecute() +{ + BP_PostExecute(); + + SourceNode = nullptr; + ExecutedInputPinName = NAME_None; +} + +bool UFlowOwnerFunctionParams::ShouldFinishForOutputName_Implementation(const FName& OutputName) const +{ + // Blueprint subclasses may want to declare certain outputs as "non-finishing" + // but by default, all outputs are 'finishing' + return true; +} + +TArray UFlowOwnerFunctionParams::BP_GetInputNames() const +{ + if (IsValid(SourceNode)) + { + return SourceNode->GetInputNames(); + } + + return TArray(); +} + +TArray UFlowOwnerFunctionParams::BP_GetOutputNames() const +{ + if (IsValid(SourceNode)) + { + return SourceNode->GetOutputNames(); + } + + return TArray(); +} diff --git a/Source/Flow/Private/FlowOwnerFunctionRef.cpp b/Source/Flow/Private/FlowOwnerFunctionRef.cpp new file mode 100644 index 000000000..d49bfbf2a --- /dev/null +++ b/Source/Flow/Private/FlowOwnerFunctionRef.cpp @@ -0,0 +1,87 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "FlowOwnerFunctionRef.h" +#include "FlowOwnerFunctionParams.h" +#include "FlowModule.h" +#include "FlowOwnerInterface.h" + +#include "UObject/Class.h" +#include "Logging/LogMacros.h" + + +// FFlowOwnerFunctionRef Implementation + +UFunction* FFlowOwnerFunctionRef::TryResolveFunction(const UClass& InClass) +{ + if (IsConfigured()) + { + Function = InClass.FindFunctionByName(FunctionName); + } + else + { + Function = nullptr; + } + + return Function; +} + +FName FFlowOwnerFunctionRef::CallFunction(IFlowOwnerInterface& InFlowOwnerInterface, UFlowOwnerFunctionParams& InParams) const +{ + if (!IsResolved()) + { + const UObject* FlowOwnerObject = CastChecked(&InFlowOwnerInterface); + + UE_LOG( + LogFlow, + Error, + TEXT("Could not resolve function named %s with flow owner class %s"), + *FunctionName.ToString(), + *FlowOwnerObject->GetClass()->GetName()); + + return NAME_None; + } + + UObject* FlowOwnerObject = CastChecked(&InFlowOwnerInterface); + + struct FFlowOwnerFunctionRef_Parms + { + // Single FunctionParams object parameter + UFlowOwnerFunctionParams* Params; + + // Return value + FName OutputPinName; + }; + + FFlowOwnerFunctionRef_Parms Parms = { &InParams, NAME_None }; + + // Call the owner function itself + FlowOwnerObject->ProcessEvent(Function, &Parms); + + // Ensure the return value is valid + if (!Parms.OutputPinName.IsNone()) + { + const TArray OutputNames = InParams.GatherOutputNames(); + + if (!OutputNames.Contains(Parms.OutputPinName)) + { + FString OutputNamesStr = TEXT("None"); + for (const FName& OutputName : OutputNames) + { + OutputNamesStr += TEXT(", ") + OutputName.ToString(); + } + + UE_LOG( + LogFlow, + Error, + TEXT("Flow Owner Function %s returned an invalid OutputPinName '%s', which is not in the valid outputs: { %s }"), + *FunctionName.ToString(), + *Parms.OutputPinName.ToString(), + *OutputNamesStr); + + // Replace the invalid output pin name with None + Parms.OutputPinName = NAME_None; + } + } + + return Parms.OutputPinName; +} diff --git a/Source/Flow/Private/FlowSettings.cpp b/Source/Flow/Private/FlowSettings.cpp index 9c1070e4a..56fff80ce 100644 --- a/Source/Flow/Private/FlowSettings.cpp +++ b/Source/Flow/Private/FlowSettings.cpp @@ -1,6 +1,8 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "FlowSettings.h" +#include "FlowComponent.h" +#include "FlowOwnerInterface.h" UFlowSettings::UFlowSettings(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) @@ -9,5 +11,21 @@ UFlowSettings::UFlowSettings(const FObjectInitializer& ObjectInitializer) , bLogOnSignalDisabled(true) , bLogOnSignalPassthrough(true) , bUseAdaptiveNodeTitles(false) + , DefaultExpectedOwnerClass(UFlowComponent::StaticClass()) { } + +UClass* UFlowSettings::GetDefaultExpectedOwnerClass() const +{ + return CastChecked(TryResolveOrLoadSoftClass(DefaultExpectedOwnerClass), ECastCheckedType::NullAllowed); +} + +UClass* UFlowSettings::TryResolveOrLoadSoftClass(const FSoftClassPath& SoftClassPath) +{ + if (UClass* Resolved = SoftClassPath.ResolveClass()) + { + return Resolved; + } + + return SoftClassPath.TryLoadClass(); +} \ No newline at end of file diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 3747503c9..2de2943ab 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -4,6 +4,7 @@ #include "FlowAsset.h" #include "FlowModule.h" +#include "FlowOwnerInterface.h" #include "FlowSettings.h" #include "FlowSubsystem.h" #include "FlowTypes.h" @@ -187,6 +188,80 @@ UObject* UFlowNode::TryGetRootFlowObjectOwner() const return nullptr; } +IFlowOwnerInterface* UFlowNode::GetFlowOwnerInterface() const +{ + const UFlowAsset* FlowAsset = GetFlowAsset(); + if (!IsValid(FlowAsset)) + { + return nullptr; + } + + const UClass* ExpectedOwnerClass = FlowAsset->GetExpectedOwnerClass(); + if (!IsValid(ExpectedOwnerClass)) + { + return nullptr; + } + + UObject* RootFlowOwner = FlowAsset->GetOwner(); + if (!IsValid(RootFlowOwner)) + { + return nullptr; + } + + if (IFlowOwnerInterface* FlowOwnerInterface = TryGetFlowOwnerInterfaceFromRootFlowOwner(*RootFlowOwner, *ExpectedOwnerClass)) + { + return FlowOwnerInterface; + } + + if (IFlowOwnerInterface* FlowOwnerInterface = TryGetFlowOwnerInterfaceActor(*RootFlowOwner, *ExpectedOwnerClass)) + { + return FlowOwnerInterface; + } + + return nullptr; +} + +IFlowOwnerInterface* UFlowNode::TryGetFlowOwnerInterfaceFromRootFlowOwner(UObject& RootFlowOwner, const UClass& ExpectedOwnerClass) const +{ + const UClass* RootFlowOwnerClass = RootFlowOwner.GetClass(); + if (!IsValid(RootFlowOwnerClass)) + { + return nullptr; + } + + if (!RootFlowOwnerClass->IsChildOf(&ExpectedOwnerClass)) + { + return nullptr; + } + + // If the immediate owner is the expected class type, return its FlowOwnerInterface + return CastChecked(&RootFlowOwner); +} + +IFlowOwnerInterface* UFlowNode::TryGetFlowOwnerInterfaceActor(UObject& RootFlowOwner, const UClass& ExpectedOwnerClass) const +{ + // Special case if the immediate owner is a component, also consider the component's owning actor + const UActorComponent* FlowComponent = Cast(&RootFlowOwner); + if (!IsValid(FlowComponent)) + { + return nullptr; + } + + AActor* ActorOwner = FlowComponent->GetOwner(); + if (!IsValid(ActorOwner)) + { + return nullptr; + } + + const UClass* ActorOwnerClass = ActorOwner->GetClass(); + if (!ActorOwnerClass->IsChildOf(&ExpectedOwnerClass)) + { + return nullptr; + } + + return CastChecked(ActorOwner); +} + void UFlowNode::AddInputPins(TArray Pins) { for (const FFlowPin& Pin : Pins) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp b/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp new file mode 100644 index 000000000..048d56391 --- /dev/null +++ b/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp @@ -0,0 +1,477 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Nodes/World/FlowNode_CallOwnerFunction.h" +#include "FlowAsset.h" +#include "FlowModule.h" +#include "FlowOwnerInterface.h" +#include "FlowOwnerFunctionParams.h" +#include "FlowSettings.h" + + +#define LOCTEXT_NAMESPACE "FlowNode" + +// UFlowNode_CallOwnerFunction Implementation + +UFlowNode_CallOwnerFunction::UFlowNode_CallOwnerFunction() + : Super() +{ +#if WITH_EDITOR + NodeStyle = EFlowNodeStyle::Default; + Category = TEXT("World"); +#endif // WITH_EDITOR +} + +void UFlowNode_CallOwnerFunction::ExecuteInput(const FName& PinName) +{ + Super::ExecuteInput(PinName); + + if (!IsValid(Params)) + { + UE_LOG(LogFlow, Error, TEXT("Expected a valid Params object")); + + return; + } + + IFlowOwnerInterface* FlowOwnerInterface = GetFlowOwnerInterface(); + if (!FlowOwnerInterface) + { + UE_LOG(LogFlow, Error, TEXT("Expected an owner that implements the IFlowOwnerInterface")); + + return; + } + + UObject* FlowOwnerObject = CastChecked(FlowOwnerInterface); + UClass* FlowOwnerClass = FlowOwnerObject->GetClass(); + check(IsValid(FlowOwnerClass)); + + if (!FunctionRef.TryResolveFunction(*FlowOwnerClass)) + { + UE_LOG( + LogFlow, + Error, + TEXT("Could not resolve function named %s with flow owner class %s"), + *FunctionRef.GetFunctionName().ToString(), + *FlowOwnerClass->GetName()); + + return; + } + + Params->PreExecute(*this, PinName); + + const FName ResultOutputName = FunctionRef.CallFunction(*FlowOwnerInterface, *Params); + + Params->PostExecute(); + + (void) TryExecuteOutputPin(ResultOutputName); +} + +bool UFlowNode_CallOwnerFunction::TryExecuteOutputPin(const FName& OutputName) +{ + if (OutputName.IsNone()) + { + return false; + } + + const bool bFinish = ShouldFinishForOutputName(OutputName); + TriggerOutput(OutputName, bFinish); + + return true; +} + +bool UFlowNode_CallOwnerFunction::ShouldFinishForOutputName(const FName& OutputName) const +{ + if (ensure(IsValid(Params))) + { + return Params->ShouldFinishForOutputName(OutputName); + } + + return true; +} + +#if WITH_EDITOR + +void UFlowNode_CallOwnerFunction::PostLoad() +{ + Super::PostLoad(); + + FObjectPropertyBase* ParamsProperty = FindFProperty(GetClass(), GET_MEMBER_NAME_CHECKED(UFlowNode_CallOwnerFunction, Params)); + check(ParamsProperty); + + UClass* RequiredParamsClass = GetRequiredParamsClass(); + if (IsValid(RequiredParamsClass)) + { + // Update the property to filter for just this class (and its subclasses) + ParamsProperty->SetPropertyClass(RequiredParamsClass); + } +} + +void UFlowNode_CallOwnerFunction::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + const FName MemberPropertyName = PropertyChangedEvent.MemberProperty->GetFName(); + + if (MemberPropertyName == GET_MEMBER_NAME_CHECKED(UFlowNode_CallOwnerFunction, Params)) + { + OnChangedParamsObject(); + } + + const FName PropertyName = PropertyChangedEvent.Property->GetFName(); + if (PropertyName == GET_MEMBER_NAME_CHECKED(FFlowOwnerFunctionRef, FunctionName)) + { + if (TryAllocateParamsInstance()) + { + OnChangedParamsObject(); + } + } +} + +void UFlowNode_CallOwnerFunction::OnChangedParamsObject() +{ + bool bChangedPins = false; + + if (IsValid(Params)) + { + bChangedPins = RebuildPinArray(Params->GetInputNames(), InputPins, DefaultInputPin) || bChangedPins; + bChangedPins = RebuildPinArray(Params->GetOutputNames(), OutputPins, DefaultOutputPin) || bChangedPins; + } + else + { + bChangedPins = RebuildPinArray(TArray(&DefaultInputPin.PinName, 1), InputPins, DefaultInputPin) || bChangedPins; + bChangedPins = RebuildPinArray(TArray(&DefaultOutputPin.PinName, 1), OutputPins, DefaultOutputPin) || bChangedPins; + } + + if (bChangedPins) + { + OnReconstructionRequested.ExecuteIfBound(); + } +} + +bool UFlowNode_CallOwnerFunction::RebuildPinArray(const TArray& NewPinNames, TArray& InOutPins, const FFlowPin& DefaultPin) +{ + bool bIsChanged = false; + + TArray NewPins; + + if (NewPinNames.Num() == 0) + { + bIsChanged = true; + + NewPins.Reserve(1); + + NewPins.Add(DefaultPin); + } + else + { + const bool bIsSameNum = (NewPinNames.Num() == InOutPins.Num()); + + bIsChanged = !bIsSameNum; + + NewPins.Reserve(NewPinNames.Num()); + + for (int32 NewPinIndex = 0; NewPinIndex < NewPinNames.Num(); ++NewPinIndex) + { + const FName& NewPinName = NewPinNames[NewPinIndex]; + NewPins.Add(FFlowPin(NewPinName)); + + if (bIsSameNum) + { + bIsChanged = bIsChanged || (NewPinName != InOutPins[NewPinIndex].PinName); + } + } + } + + if (bIsChanged) + { + InOutPins.Reset(); + + check(NewPins.Num() > 0); + + if (&InOutPins == &InputPins) + { + AddInputPins(NewPins); + } + else + { + checkf(&InOutPins == &OutputPins, TEXT("Only expected to be called with one or the other of the pin arrays")); + + AddOutputPins(NewPins); + } + } + + return bIsChanged; +} + +EDataValidationResult UFlowNode_CallOwnerFunction::ValidateNode() +{ + const bool bHasFunction = FunctionRef.IsConfigured(); + if (!bHasFunction) + { + ValidationLog.Error(TEXT("CallOwnerFunction requires a valid Function reference"), this); + + return EDataValidationResult::Invalid; + } + + const bool bHasParams = IsValid(Params); + if (!bHasParams) + { + ValidationLog.Error(TEXT("CallOwnerFunction requires a valid Params object"), this); + + return EDataValidationResult::Invalid; + } + + checkf(bHasParams && bHasFunction, TEXT("This should be assured by the preceding logic")); + + const UClass* ExpectedOwnerClass = TryGetExpectedOwnerClass(); + if (!IsValid(ExpectedOwnerClass)) + { + ValidationLog.Error(TEXT("Invalid or null Expected Owner Class for this Flow Asset"), this); + + return EDataValidationResult::Invalid; + } + + // Check if the function can be found on the expected owner + const UFunction* Function = FunctionRef.TryResolveFunction(*ExpectedOwnerClass); + if (!IsValid(Function)) + { + ValidationLog.Error(TEXT("Could not resolve function for flow owner"), this); + + return EDataValidationResult::Invalid; + } + + // Check the function signature + if (!DoesFunctionHaveValidFlowOwnerFunctionSignature(*Function)) + { + ValidationLog.Error(TEXT("Flow Owner Function has an invalid signature"), this); + + return EDataValidationResult::Invalid; + } + + const UClass* RequiredParamsClass = GetRequiredParamsClass(); + checkf(IsValid(RequiredParamsClass), TEXT("GetRequiredParamsClass() cannot return null if DoesFunctionHaveValidFlowOwnerFunctionSignature() is true")); + + const UClass* ExistingParamsClass = GetExistingParamsClass(); + checkf(IsValid(ExistingParamsClass), TEXT("This should be assured, if bHasParams == true")); + + // Check if the params (existing) are compatible with the function's expected (required) params + if (!ExistingParamsClass->IsChildOf(RequiredParamsClass)) + { + ValidationLog.Error(TEXT("Params object is not of the correct type for the flow owner function"), this); + + return EDataValidationResult::Invalid; + } + + return EDataValidationResult::Valid; +} + +FString UFlowNode_CallOwnerFunction::GetStatusString() const +{ + if (ActivationState != EFlowNodeState::NeverActivated) + { + return UEnum::GetDisplayValueAsText(ActivationState).ToString(); + } + + return Super::GetStatusString(); +} + +UClass* UFlowNode_CallOwnerFunction::TryGetExpectedOwnerClass() const +{ + const UFlowAsset* FlowAsset = GetFlowAsset(); + if (IsValid(FlowAsset)) + { + return FlowAsset->GetExpectedOwnerClass(); + } + + return nullptr; +} + +bool UFlowNode_CallOwnerFunction::DoesFunctionHaveValidFlowOwnerFunctionSignature(const UFunction& Function) +{ + if (GetParamsClassForFunction(Function) == nullptr) + { + return false; + } + + checkf(Function.NumParms == 2, TEXT("This should be checked in GetParamsClassForFunction()")); + + if (!DoesFunctionHaveNameReturnType(Function)) + { + return false; + } + + return true; +} + +bool UFlowNode_CallOwnerFunction::DoesFunctionHaveNameReturnType(const UFunction& Function) +{ + checkf(Function.NumParms == 2, TEXT("This should have already been checked in DoesFunctionHaveValidFlowOwnerFunctionSignature()")); + + TFieldIterator Iterator(&Function); + + while (Iterator) + { + const bool bIsOutParm = EnumHasAllFlags(Iterator->PropertyFlags, CPF_Parm | CPF_OutParm); + + return bIsOutParm; + } + + return false; +} + +UClass* UFlowNode_CallOwnerFunction::GetParamsClassForFunction(const UFunction& Function) +{ + if (Function.NumParms != 2) + { + // Flow Owner Functions expect exactly two parameters: + // - FFlowOwnerFunctionParams* + // - FName (return) + // See FFlowOwnerFunctionSignature + + return nullptr; + } + + TFieldIterator Iterator(&Function); + + while (Iterator && (Iterator->PropertyFlags & CPF_Parm)) + { + const FObjectPropertyBase* Prop = *Iterator; + check(Prop); + + UClass* PropertyClass = Prop->PropertyClass; + + if (!IsValid(PropertyClass)) + { + return nullptr; + } + + if (!PropertyClass->IsChildOf()) + { + return nullptr; + } + + return PropertyClass; + } + + return nullptr; +} + +UClass* UFlowNode_CallOwnerFunction::GetParamsClassForFunctionName(const UClass& ExpectedOwnerClass, const FName& FunctionName) +{ + const UFunction* Function = ExpectedOwnerClass.FindFunctionByName(FunctionName); + if (IsValid(Function)) + { + return GetParamsClassForFunction(*Function); + } + + return nullptr; +} + +bool UFlowNode_CallOwnerFunction::TryAllocateParamsInstance() +{ + FObjectPropertyBase* ParamsProperty = FindFProperty(GetClass(), GET_MEMBER_NAME_CHECKED(UFlowNode_CallOwnerFunction, Params)); + check(ParamsProperty); + + UClass* RequiredParamsClass = GetRequiredParamsClass(); + + if (ParamsProperty) + { + // Update the property to filter for just this class (and its subclasses) + ParamsProperty->SetPropertyClass(RequiredParamsClass); + } + + if (FunctionRef.GetFunctionName().IsNone()) + { + return false; + } + + const UClass* ExistingParamsClass = GetExistingParamsClass(); + + const bool bNeedsAllocateParams = + !IsValid(ExistingParamsClass) || + !ExistingParamsClass->IsChildOf(RequiredParamsClass); + + if (!bNeedsAllocateParams) + { + return false; + } + + // Throw out the old params object (if any) + Params = nullptr; + + // Create the new params object + Params = NewObject(this, RequiredParamsClass); + + return true; +} + +UClass* UFlowNode_CallOwnerFunction::GetRequiredParamsClass() const +{ + const UClass* ExpectedOwnerClass = TryGetExpectedOwnerClass(); + if (!IsValid(ExpectedOwnerClass)) + { + return UFlowOwnerFunctionParams::StaticClass(); + } + + const FName FunctionNameAsName = FunctionRef.GetFunctionName(); + + if (FunctionNameAsName.IsNone()) + { + return UFlowOwnerFunctionParams::StaticClass(); + } + + UClass* RequiredParamsClass = GetParamsClassForFunctionName(*ExpectedOwnerClass, FunctionNameAsName); + return RequiredParamsClass; +} + +UClass* UFlowNode_CallOwnerFunction::GetExistingParamsClass() const +{ + if (!IsValid(Params)) + { + return nullptr; + } + + UClass* ExistingParamsClass = Params->GetClass(); + return ExistingParamsClass; +} + +bool UFlowNode_CallOwnerFunction::IsAcceptableParamsPropertyClass(const UClass* ParamsClass) const +{ + if (!IsValid(ParamsClass)) + { + return false; + } + + if (!ParamsClass->IsChildOf()) + { + return false; + } + + const UClass* ExistingParamsClass = GetExistingParamsClass(); + + if (IsValid(ExistingParamsClass) && ParamsClass != ExistingParamsClass) + { + return false; + } + + return true; +} + +FText UFlowNode_CallOwnerFunction::GetNodeTitle() const +{ + const bool bUseAdaptiveNodeTitles = UFlowSettings::Get()->bUseAdaptiveNodeTitles; + + if (bUseAdaptiveNodeTitles && !FunctionRef.GetFunctionName().IsNone()) + { + const FText FunctionNameText = FText::FromName(FunctionRef.FunctionName); + + return FText::Format(LOCTEXT("CallOwnerFunction", "Call {0}"), { FunctionNameText }); + } + else + { + return Super::GetNodeTitle(); + } +} + +#endif // WITH_EDITOR + +#undef LOCTEXT_NAMESPACE diff --git a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp index d058ab6c3..d02464a90 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp @@ -5,6 +5,7 @@ #include "FlowAsset.h" #include "FlowModule.h" #include "FlowSubsystem.h" +#include "Launch/Resources/Version.h" #include "LevelSequence/FlowLevelSequencePlayer.h" #include "MovieScene/MovieSceneFlowTrack.h" #include "MovieScene/MovieSceneFlowTriggerSection.h" @@ -60,7 +61,11 @@ TArray UFlowNode_PlayLevelSequence::GetContextOutputs() Sequence = Sequence.LoadSynchronous(); if (Sequence && Sequence->GetMovieScene()) { +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 2 + for (const UMovieSceneTrack* Track : Sequence->GetMovieScene()->GetMasterTracks()) +#else for (const UMovieSceneTrack* Track : Sequence->GetMovieScene()->GetTracks()) +#endif { if (Track->GetClass() == UMovieSceneFlowTrack::StaticClass()) { diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 5e419347c..57467169a 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -349,9 +349,23 @@ class FLOW_API UFlowAsset : public UObject UFUNCTION(BlueprintPure, Category = "Flow") const TArray& GetRecordedNodes() const { return RecordedNodes; } +////////////////////////////////////////////////////////////////////////// +// Expected Owner Class support (for use with CallOwnerFunction nodes) + +public: + UClass* GetExpectedOwnerClass() const { return ExpectedOwnerClass; } + +protected: + // Expects to be owned (at runtime) by an object with this class (or one of its subclasses) + // NOTE - If the class is an AActor, and the flow asset is owned by a component, + // it will consider the component's owner for the AActor + UPROPERTY(EditAnywhere, meta = (MustImplement = "FlowOwnerInterface")) + TSubclassOf ExpectedOwnerClass; + ////////////////////////////////////////////////////////////////////////// // SaveGame support +public: UFUNCTION(BlueprintCallable, Category = "SaveGame") FFlowAssetSaveData SaveInstance(TArray& SavedFlowInstances); diff --git a/Source/Flow/Public/FlowComponent.h b/Source/Flow/Public/FlowComponent.h index 2c74fac96..b800c2b07 100644 --- a/Source/Flow/Public/FlowComponent.h +++ b/Source/Flow/Public/FlowComponent.h @@ -7,6 +7,7 @@ #include "FlowSave.h" #include "FlowTypes.h" +#include "FlowOwnerInterface.h" #include "FlowComponent.generated.h" class UFlowAsset; @@ -41,7 +42,7 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FFlowComponentDynamicNotify, class * Base component of Flow System - makes possible to communicate between Actor, Flow Subsystem and Flow Graphs */ UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent)) -class FLOW_API UFlowComponent : public UActorComponent +class FLOW_API UFlowComponent : public UActorComponent, public IFlowOwnerInterface { GENERATED_UCLASS_BODY() @@ -214,7 +215,7 @@ class FLOW_API UFlowComponent : public UActorComponent virtual void OnTriggerRootFlowOutputEvent(UFlowAsset* RootFlowInstance, const FName& EventName) {} // UFlowAsset-only access - FORCEINLINE void OnTriggerRootFlowOutputEventDispatcher(UFlowAsset* RootFlowInstance, const FName& EventName); + void OnTriggerRootFlowOutputEventDispatcher(UFlowAsset* RootFlowInstance, const FName& EventName); // --- ////////////////////////////////////////////////////////////////////////// diff --git a/Source/Flow/Public/FlowOwnerFunctionParams.h b/Source/Flow/Public/FlowOwnerFunctionParams.h new file mode 100644 index 000000000..f3f49797f --- /dev/null +++ b/Source/Flow/Public/FlowOwnerFunctionParams.h @@ -0,0 +1,83 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "CoreMinimal.h" + +#include "FlowOwnerFunctionParams.generated.h" + + +// Forward Declarations +class UFlowNode_CallOwnerFunction; + + +UCLASS(BlueprintType, Blueprintable, EditInlineNew) +class FLOW_API UFlowOwnerFunctionParams : public UObject +{ + GENERATED_BODY() + +public: + + UFlowOwnerFunctionParams(); + + void PreExecute(UFlowNode_CallOwnerFunction& InSourceNode, const FName& InputPinName); + void PostExecute(); + + UFUNCTION(BlueprintNativeEvent, BlueprintPure) + bool ShouldFinishForOutputName(const FName& OutputName) const; + +#if WITH_EDITORONLY_DATA + const TArray& GetInputNames() const { return InputNames; } + const TArray& GetOutputNames() const { return OutputNames; } +#endif // WITH_EDITORONLY_DATA + + // Get the input/output pin names for the SourceNode. + // Slightly slower than the editor-only counterparts owing to the deep copy of the array. + // Valid only if called between PreExecute() and PostExecute(), inclusive + TArray GatherInputNames() const { return BP_GetInputNames(); } + TArray GatherOutputNames() const { return BP_GetOutputNames(); } + +protected: + + // Called prior to the owner executing the function described by this object. + // Can be overridden to prepare the stateful data before execution. + UFUNCTION(BlueprintImplementableEvent, DisplayName = "PreExecute") + void BP_PreExecute(); + + // Cleans up the stateful data in this Params struct. + // Can be overridden to cleanup the stateful data after execution. + UFUNCTION(BlueprintImplementableEvent, DisplayName = "PostExecute") + void BP_PostExecute(); + + // Get the input pin names for the SourceNode + // Valid only if called between PreExecute() and PostExecute(), inclusive + UFUNCTION(BlueprintCallable, BlueprintPure, DisplayName = "GetInputNames") + TArray BP_GetInputNames() const; + + // Get the output pin names for the SourceNode + // Valid only if called between PreExecute() and PostExecute(), inclusive + UFUNCTION(BlueprintCallable, BlueprintPure, DisplayName = "GetOutputNames") + TArray BP_GetOutputNames() const; + +protected: + + // CallOwnerObjectFunction node that is executing this set of function params. + // Valid only if called between PreExecute() and PostExecute(), inclusive + UPROPERTY(Transient, BlueprintReadOnly) + UFlowNode_CallOwnerFunction* SourceNode = nullptr; + + // This is the Name from the Input Pin that caused this node to Execute. + // Valid only if called between PreExecute() and PostExecute(), inclusive + UPROPERTY(Transient, BlueprintReadOnly) + FName ExecutedInputPinName; + +#if WITH_EDITORONLY_DATA + // Input pin names for this function + UPROPERTY(EditDefaultsOnly) + TArray InputNames; + + // Output pin names for this function + UPROPERTY(EditDefaultsOnly) + TArray OutputNames; +#endif // WITH_EDITORONLY_DATA +}; diff --git a/Source/Flow/Public/FlowOwnerFunctionRef.h b/Source/Flow/Public/FlowOwnerFunctionRef.h new file mode 100644 index 000000000..4e628d1ab --- /dev/null +++ b/Source/Flow/Public/FlowOwnerFunctionRef.h @@ -0,0 +1,59 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "CoreMinimal.h" +#include "Templates/SubclassOf.h" + +#include "FlowOwnerFunctionRef.generated.h" + + +// Forward Declarations +class UFlowOwnerFunctionParams; +class IFlowOwnerInterface; + + +// Similar to FAnimNodeFunctionRef, providing a FName-based function binding +// that is resolved at runtime +USTRUCT(BlueprintType) +struct FFlowOwnerFunctionRef +{ + GENERATED_BODY() + + // For GET_MEMBER_NAME_CHECKED access + friend class UFlowNode_CallOwnerFunction; + friend class FFlowOwnerFunctionRefCustomization; + +public: + + // Resolves the function and returns the UFunction + UFunction* TryResolveFunction(const UClass& InClass); + + // Returns a the resolved function + // (assumes TryResolveFunction was called previously) + UFunction* GetResolvedFunction() const { return Function; } + + // Call the function and return the Output Pin Name result + FName CallFunction(IFlowOwnerInterface& InFlowOwnerInterface, UFlowOwnerFunctionParams& InParams) const; + + // Accessors + FName GetFunctionName() const { return FunctionName; } + bool IsConfigured() const { return !FunctionName.IsNone(); } + bool IsResolved() const { return ::IsValid(Function); } + +protected: + + // The name of the function to call + UPROPERTY(VisibleAnywhere) + FName FunctionName = NAME_None; + + // The function to call + // (resolved by looking for a function named FunctionName on the ExpectedOwnerClass) + UPROPERTY(Transient) + TObjectPtr Function = nullptr; + +#if WITH_EDITORONLY_DATA + UPROPERTY(VisibleAnywhere, meta = (DisplayName = "Function Parameters Class")) + TSubclassOf ParamsClass; +#endif // WITH_EDITORONLY_DATA +}; diff --git a/Source/Flow/Public/FlowOwnerInterface.h b/Source/Flow/Public/FlowOwnerInterface.h new file mode 100644 index 000000000..a6ad53675 --- /dev/null +++ b/Source/Flow/Public/FlowOwnerInterface.h @@ -0,0 +1,22 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "CoreMinimal.h" + +#include "UObject/Interface.h" + +#include "FlowOwnerInterface.generated.h" + + +// (optional) interface to enable a Flow owner object to execute CallOwnerFunction nodes +UINTERFACE(MinimalAPI, Blueprintable, BlueprintType) +class UFlowOwnerInterface : public UInterface +{ + GENERATED_BODY() +}; + +class FLOW_API IFlowOwnerInterface +{ + GENERATED_BODY() +}; diff --git a/Source/Flow/Public/FlowSettings.h b/Source/Flow/Public/FlowSettings.h index 0dc1a772e..4d266cd8e 100644 --- a/Source/Flow/Public/FlowSettings.h +++ b/Source/Flow/Public/FlowSettings.h @@ -4,6 +4,7 @@ #include "Engine/DeveloperSettings.h" #include "Templates/SubclassOf.h" +#include "UObject/SoftObjectPath.h" #include "FlowSettings.generated.h" class UFlowNode; @@ -42,4 +43,13 @@ class FLOW_API UFlowSettings : public UDeveloperSettings // by incorporating data that would otherwise go in the Description UPROPERTY(EditAnywhere, config, Category = "Nodes") bool bUseAdaptiveNodeTitles; + + // Default class to use as a FlowAsset's "ExpectedOwnerClass" + UPROPERTY(EditAnywhere, Config, meta = (MustImplement = "FlowOwnerInterface")) + FSoftClassPath DefaultExpectedOwnerClass; + +public: + UClass* GetDefaultExpectedOwnerClass() const; + + static UClass* TryResolveOrLoadSoftClass(const FSoftClassPath& SoftClassPath); }; diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 929cb7696..49a59dc5e 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -15,6 +15,7 @@ class UFlowAsset; class UFlowSubsystem; +class IFlowOwnerInterface; #if WITH_EDITOR DECLARE_DELEGATE(FFlowNodeEvent); @@ -128,8 +129,16 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte // (if the immediate parent is an UActorComponent, it will get that Component's actor) AActor* TryGetRootFlowActorOwner() const; + // Returns the IFlowOwnerInterface for the owner object (if implemented) + // NOTE - will consider a UActorComponent owner's owning actor if appropriate + IFlowOwnerInterface* GetFlowOwnerInterface() const; + protected: + // Helper functions for GetFlowOwnerInterface() + IFlowOwnerInterface* TryGetFlowOwnerInterfaceFromRootFlowOwner(UObject& RootFlowOwner, const UClass& ExpectedOwnerClass) const; + IFlowOwnerInterface* TryGetFlowOwnerInterfaceActor(UObject& RootFlowOwner, const UClass& ExpectedOwnerClass) const; + // Gets the Owning Object for this Node's RootFlow UObject* TryGetRootFlowObjectOwner() const; diff --git a/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h b/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h new file mode 100644 index 000000000..2f3302af5 --- /dev/null +++ b/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h @@ -0,0 +1,95 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "CoreMinimal.h" + +#include "GameplayTagContainer.h" + +#include "FlowOwnerFunctionRef.h" +#include "Nodes/FlowNode.h" + +#include "FlowNode_CallOwnerFunction.generated.h" + + +// Forward Declarations +class UFlowOwnerFunctionParams; +class IFlowOwnerInterface; + + +// Example signature for valid Flow Owner Functions +typedef TFunction FFlowOwnerFunctionSignature; + + +/** + * FlowNode to call an owner function + * - Owner must implement IFlowOwnerInterface + * - Callable functions must take a single input parameter deriving from UFlowOwnerFunctionParams + * and return FName for the Output event to trigger (or "None" to trigger none of the outputs) + */ +UCLASS(NotBlueprintable, meta = (DisplayName = "Call Owner Function")) +class FLOW_API UFlowNode_CallOwnerFunction : public UFlowNode +{ + GENERATED_BODY() + +public: + +#if WITH_EDITOR + //Begin UObject + virtual void PostLoad() override; + virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override; + //End UObject + + //Begin UFlowNode public + virtual FText GetNodeTitle() const override; + virtual EDataValidationResult ValidateNode() override; + + virtual FString GetStatusString() const override; + //End UFlowNode public +#endif // WITH_EDITOR + + UFlowNode_CallOwnerFunction(); + + UClass* GetRequiredParamsClass() const; + UClass* GetExistingParamsClass() const; + + bool IsAcceptableParamsPropertyClass(const UClass* ParamsClass) const; + + UClass* TryGetExpectedOwnerClass() const; + + static bool DoesFunctionHaveValidFlowOwnerFunctionSignature(const UFunction& Function); + + static UClass* GetParamsClassForFunctionName(const UClass& ExpectedOwnerClass, const FName& FunctionName); + static UClass* GetParamsClassForFunction(const UFunction& Function); + +protected: + +#if WITH_EDITOR + // returns true if the InOutPins array was rebuilt + bool RebuildPinArray(const TArray& NewPinNames, TArray& InOutPins, const FFlowPin& DefaultPin); + + void OnChangedParamsObject(); +#endif // WITH_EDITOR + + //Begin UFlowNode protected + virtual void ExecuteInput(const FName& PinName) override; + //End UFlowNode protected + + bool ShouldFinishForOutputName(const FName& OutputName) const; + bool TryExecuteOutputPin(const FName& OutputName); + + bool TryAllocateParamsInstance(); + + // Helper function for DoesFunctionHaveValidFlowOwnerFunctionSignature() + static bool DoesFunctionHaveNameReturnType(const UFunction& Function); + +protected: + + // Function reference on the expected owner to call + UPROPERTY(EditAnywhere, meta = (DisplayName = "Function")) + FFlowOwnerFunctionRef FunctionRef; + + // Parameter object to pass to the function when called + UPROPERTY(EditAnywhere, Instanced) + UFlowOwnerFunctionParams* Params; +}; diff --git a/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp b/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp index b770a4b4c..85cf96d50 100644 --- a/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp +++ b/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp @@ -7,6 +7,7 @@ #include "EdGraph/EdGraph.h" #include "GraphDiffControl.h" +#include "Launch/Resources/Version.h" #include "SBlueprintDiff.h" #define LOCTEXT_NAMESPACE "SFlowDiffControl" @@ -15,7 +16,11 @@ /// FFlowAssetDiffControl FFlowAssetDiffControl::FFlowAssetDiffControl(const UFlowAsset* InOldFlowAsset, const UFlowAsset* InNewFlowAsset, FOnDiffEntryFocused InSelectionCallback) +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 2 + : FDetailsDiffControl(InOldFlowAsset, InNewFlowAsset, InSelectionCallback) +#else : FDetailsDiffControl(InOldFlowAsset, InNewFlowAsset, InSelectionCallback, false) +#endif { } diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowOwnerFunctionRefCustomization.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowOwnerFunctionRefCustomization.cpp new file mode 100644 index 000000000..6d48f71e4 --- /dev/null +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowOwnerFunctionRefCustomization.cpp @@ -0,0 +1,158 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "DetailCustomizations/FlowOwnerFunctionRefCustomization.h" + +#include "FlowAsset.h" +#include "FlowOwnerInterface.h" +#include "Nodes/FlowNode.h" +#include "Nodes/World/FlowNode_CallOwnerFunction.h" + +#include "UObject/UnrealType.h" +#include "FlowOwnerFunctionParams.h" + + +// FFlowOwnerFunctionRefCustomization Implementation + +void FFlowOwnerFunctionRefCustomization::CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) +{ + // Do not include children properties (the header is all we need to show for this struct) +} + +TSharedPtr FFlowOwnerFunctionRefCustomization::GetCuratedNamePropertyHandle() const +{ + check(StructPropertyHandle->IsValidHandle()); + + TSharedPtr FoundHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowOwnerFunctionRef, FunctionName)); + check(FoundHandle); + + return FoundHandle; +} + +TArray FFlowOwnerFunctionRefCustomization::GetCuratedNameOptions() const +{ + TArray Results; + + const UClass* ExpectedOwnerClass = TryGetExpectedOwnerClass(); + if (!IsValid(ExpectedOwnerClass)) + { + return Results; + } + + const UFlowNode_CallOwnerFunction* FlowNodeOwner = Cast(TryGetFlowNodeOuter()); + if (!IsValid(FlowNodeOwner)) + { + return Results; + } + + Results = GetFlowOwnerFunctionRefs(*FlowNodeOwner, *ExpectedOwnerClass); + + return Results; +} + +const UClass* FFlowOwnerFunctionRefCustomization::TryGetExpectedOwnerClass() const +{ + const UFlowNode* NodeOwner = TryGetFlowNodeOuter(); + const UFlowNode_CallOwnerFunction* CallOwnerFunctionNode = Cast(NodeOwner); + + if (IsValid(CallOwnerFunctionNode)) + { + return CallOwnerFunctionNode->TryGetExpectedOwnerClass(); + } + + return nullptr; +} + +TArray FFlowOwnerFunctionRefCustomization::GetFlowOwnerFunctionRefs( + const UFlowNode_CallOwnerFunction& FlowNodeOwner, + const UClass& ExpectedOwnerClass) +{ + TArray ValidFunctionNames; + + // Gather a list of potential functions + TArray PotentialFunctionNames; + ExpectedOwnerClass.GenerateFunctionList(PotentialFunctionNames); + + if (PotentialFunctionNames.Num() == 0) + { + return ValidFunctionNames; + } + + ValidFunctionNames.Reserve(PotentialFunctionNames.Num()); + + // Filter out any unusable functions (that do not match the expected signature) + for (const FName& PotentialFunctionName : PotentialFunctionNames) + { + const UFunction* PotentialFunction = ExpectedOwnerClass.FindFunctionByName(PotentialFunctionName); + check(IsValid(PotentialFunction)); + + if (IsFunctionUsable(*PotentialFunction, FlowNodeOwner)) + { + ValidFunctionNames.Add(PotentialFunctionName); + } + } + + return ValidFunctionNames; +} + +bool FFlowOwnerFunctionRefCustomization::IsFunctionUsable(const UFunction& Function, const UFlowNode_CallOwnerFunction& FlowNodeOwner) +{ + if (!UFlowNode_CallOwnerFunction::DoesFunctionHaveValidFlowOwnerFunctionSignature(Function)) + { + return false; + } + + if (!DoesFunctionHaveExpectedParamType(Function, FlowNodeOwner)) + { + return false; + } + + return true; +} + +bool FFlowOwnerFunctionRefCustomization::DoesFunctionHaveExpectedParamType(const UFunction& Function, const UFlowNode_CallOwnerFunction& FlowNodeOwner) +{ + const UClass* PropertyClass = UFlowNode_CallOwnerFunction::GetParamsClassForFunction(Function); + + return FlowNodeOwner.IsAcceptableParamsPropertyClass(PropertyClass); +} + +void FFlowOwnerFunctionRefCustomization::SetCuratedName(const FName& NewFunctionName) +{ + TSharedPtr FunctionNameHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowOwnerFunctionRef, FunctionName)); + + check(FunctionNameHandle); + + FunctionNameHandle->SetPerObjectValue(0, NewFunctionName.ToString()); +} + +FName FFlowOwnerFunctionRefCustomization::GetCuratedName() const +{ + const FFlowOwnerFunctionRef* FlowOwnerFunction = GetFlowOwnerFunctionRef(); + if (FlowOwnerFunction) + { + return FlowOwnerFunction->FunctionName; + } + else + { + return NAME_None; + } +} + +UFlowNode* FFlowOwnerFunctionRefCustomization::TryGetFlowNodeOuter() const +{ + check(StructPropertyHandle->IsValidHandle()); + + TArray OuterObjects; + StructPropertyHandle->GetOuterObjects(OuterObjects); + + for (UObject* OuterObject : OuterObjects) + { + UFlowNode* FlowNodeOuter = Cast(OuterObject); + if (IsValid(FlowNodeOuter)) + { + return FlowNodeOuter; + } + } + + return nullptr; +} diff --git a/Source/FlowEditor/Private/FlowEditorModule.cpp b/Source/FlowEditor/Private/FlowEditorModule.cpp index 9ea22f098..c36010c36 100644 --- a/Source/FlowEditor/Private/FlowEditorModule.cpp +++ b/Source/FlowEditor/Private/FlowEditorModule.cpp @@ -20,6 +20,7 @@ #include "DetailCustomizations/FlowNode_CustomInputDetails.h" #include "DetailCustomizations/FlowNode_CustomOutputDetails.h" #include "DetailCustomizations/FlowNode_PlayLevelSequenceDetails.h" +#include "DetailCustomizations/FlowOwnerFunctionRefCustomization.h" #include "DetailCustomizations/FlowNode_SubGraphDetails.h" #include "FlowAsset.h" @@ -70,19 +71,7 @@ void FFlowEditorModule::StartupModule() ISequencerModule& SequencerModule = FModuleManager::Get().LoadModuleChecked("Sequencer"); FlowTrackCreateEditorHandle = SequencerModule.RegisterTrackEditor(FOnCreateTrackEditor::CreateStatic(&FFlowTrackEditor::CreateTrackEditor)); - RegisterPropertyCustomizations(); - - // register detail customizations - RegisterCustomClassLayout(UFlowAsset::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowAssetDetails::MakeInstance)); - RegisterCustomClassLayout(UFlowNode::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_Details::MakeInstance)); - RegisterCustomClassLayout(UFlowNode_ComponentObserver::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_ComponentObserverDetails::MakeInstance)); - RegisterCustomClassLayout(UFlowNode_CustomInput::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_CustomInputDetails::MakeInstance)); - RegisterCustomClassLayout(UFlowNode_CustomOutput::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_CustomOutputDetails::MakeInstance)); - RegisterCustomClassLayout(UFlowNode_PlayLevelSequence::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_PlayLevelSequenceDetails::MakeInstance)); - RegisterCustomClassLayout(UFlowNode_SubGraph::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_SubGraphDetails::MakeInstance)); - - FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked("PropertyEditor"); - PropertyModule.NotifyCustomizationModuleChanged(); + RegisterDetailCustomizations(); // register asset indexers if (FModuleManager::Get().IsModuleLoaded(AssetSearchModuleName)) @@ -96,26 +85,14 @@ void FFlowEditorModule::ShutdownModule() { FFlowEditorStyle::Shutdown(); + UnregisterDetailCustomizations(); + UnregisterAssets(); // unregister track editors ISequencerModule& SequencerModule = FModuleManager::Get().LoadModuleChecked("Sequencer"); SequencerModule.UnRegisterTrackEditor(FlowTrackCreateEditorHandle); - // unregister details customizations - if (FModuleManager::Get().IsModuleLoaded("PropertyEditor")) - { - FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked("PropertyEditor"); - - for (auto It = CustomClassLayouts.CreateConstIterator(); It; ++It) - { - if (It->IsValid()) - { - PropertyModule.UnregisterCustomClassLayout(*It); - } - } - } - FModuleManager::Get().OnModulesChanged().Remove(ModulesChangedHandle); } @@ -171,22 +148,72 @@ void FFlowEditorModule::UnregisterAssets() RegisteredAssetActions.Empty(); } -void FFlowEditorModule::RegisterPropertyCustomizations() const +void FFlowEditorModule::RegisterCustomClassLayout(const TSubclassOf Class, const FOnGetDetailCustomizationInstance DetailLayout) { - FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor"); + if (Class) + { + FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked("PropertyEditor"); + PropertyModule.RegisterCustomClassLayout(Class->GetFName(), DetailLayout); - // notify on customization change - PropertyModule.NotifyCustomizationModuleChanged(); + CustomClassLayouts.Add(Class->GetFName()); + } } -void FFlowEditorModule::RegisterCustomClassLayout(const TSubclassOf Class, const FOnGetDetailCustomizationInstance DetailLayout) +void FFlowEditorModule::RegisterCustomStructLayout(const UScriptStruct& Struct, const FOnGetPropertyTypeCustomizationInstance DetailLayout) { - if (Class) + if (FModuleManager::Get().IsModuleLoaded("PropertyEditor")) { - CustomClassLayouts.Add(Class->GetFName()); + FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked("PropertyEditor"); + PropertyModule.RegisterCustomPropertyTypeLayout(Struct.GetFName(), DetailLayout); + CustomStructLayouts.Add(Struct.GetFName()); + } +} + +void FFlowEditorModule::RegisterDetailCustomizations() +{ + // register detail customizations + if (FModuleManager::Get().IsModuleLoaded("PropertyEditor")) + { FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked("PropertyEditor"); - PropertyModule.RegisterCustomClassLayout(Class->GetFName(), DetailLayout); + + RegisterCustomClassLayout(UFlowAsset::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowAssetDetails::MakeInstance)); + RegisterCustomClassLayout(UFlowNode::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_Details::MakeInstance)); + RegisterCustomClassLayout(UFlowNode_ComponentObserver::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_ComponentObserverDetails::MakeInstance)); + RegisterCustomClassLayout(UFlowNode_CustomInput::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_CustomInputDetails::MakeInstance)); + RegisterCustomClassLayout(UFlowNode_CustomOutput::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_CustomOutputDetails::MakeInstance)); + RegisterCustomClassLayout(UFlowNode_PlayLevelSequence::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_PlayLevelSequenceDetails::MakeInstance)); + RegisterCustomClassLayout(UFlowNode_SubGraph::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_SubGraphDetails::MakeInstance)); + RegisterCustomStructLayout(*FFlowOwnerFunctionRef::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowOwnerFunctionRefCustomization::MakeInstance)); + + PropertyModule.NotifyCustomizationModuleChanged(); + } +} + +void FFlowEditorModule::UnregisterDetailCustomizations() +{ + // unregister details customizations + if (FModuleManager::Get().IsModuleLoaded("PropertyEditor")) + { + FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked("PropertyEditor"); + + for (auto It = CustomClassLayouts.CreateConstIterator(); It; ++It) + { + if (It->IsValid()) + { + PropertyModule.UnregisterCustomClassLayout(*It); + } + } + + for (auto It = CustomStructLayouts.CreateConstIterator(); It; ++It) + { + if (It->IsValid()) + { + PropertyModule.UnregisterCustomPropertyTypeLayout(*It); + } + } + + PropertyModule.NotifyCustomizationModuleChanged(); } } diff --git a/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp b/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp index eed63ac07..5472762c2 100644 --- a/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp +++ b/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp @@ -9,6 +9,7 @@ #include "Framework/MultiBox/MultiBoxBuilder.h" #include "ISequencerSection.h" +#include "Launch/Resources/Version.h" #include "LevelSequence.h" #include "MovieSceneSequenceEditor.h" #include "Sections/MovieSceneEventSection.h" @@ -163,7 +164,11 @@ void FFlowTrackEditor::HandleAddFlowTrackMenuEntryExecute(UClass* SectionType) c TArray NewTracks; +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 2 + UMovieSceneFlowTrack* NewMasterTrack = FocusedMovieScene->AddMasterTrack(); +#else UMovieSceneFlowTrack* NewMasterTrack = FocusedMovieScene->AddTrack(); +#endif NewTracks.Add(NewMasterTrack); if (GetSequencer().IsValid()) { diff --git a/Source/FlowEditor/Private/UnrealExtensions/IFlowCuratedNamePropertyCustomization.cpp b/Source/FlowEditor/Private/UnrealExtensions/IFlowCuratedNamePropertyCustomization.cpp new file mode 100644 index 000000000..30d9da6cc --- /dev/null +++ b/Source/FlowEditor/Private/UnrealExtensions/IFlowCuratedNamePropertyCustomization.cpp @@ -0,0 +1,262 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +// NOTE (gtaylor) This class is planned for submission to Epic to include in baseline UE. +// If/when that happens, we will want to remove this version and update to the latest one in the PropertyModule + +#include "UnrealExtensions/IFlowCuratedNamePropertyCustomization.h" + +#include "DetailLayoutBuilder.h" +#include "DetailWidgetRow.h" +#include "EditorClassUtils.h" +#include "IDetailPropertyRow.h" +#include "IDetailChildrenBuilder.h" +#include "Internationalization/Text.h" +#include "PropertyHandle.h" +#include "Widgets/Input/SComboBox.h" +#include "Widgets/Text/STextBlock.h" + + +// IFlowCuratedNamePropertyCustomization Implementation + +TSharedPtr IFlowCuratedNamePropertyCustomization::NoneAsText = nullptr; + +void IFlowCuratedNamePropertyCustomization::Initialize() +{ + // Cache off "None" as a sharable FText, for use later + if (!NoneAsText.IsValid()) + { + NoneAsText = MakeShared(FText::FromName(NAME_None)); + } + + // Cache the Name property handle + check(StructPropertyHandle.IsValid()); + CachedNameHandle = GetCuratedNamePropertyHandle(); + check(CachedNameHandle->IsValidHandle()); + + // Initial setup the CachedTextSelected and CachedTextList + // (via SetCuratedNameWithSideEffects) + check(!CachedTextSelected.IsValid()); + check(CachedTextList.IsEmpty()); + + const FName CuratedName = GetCuratedName(); + const bool bChangedValue = TrySetCuratedNameWithSideEffects(CuratedName); + + check(bChangedValue); + check(CachedTextSelected.IsValid()); + check(CachedTextList.Num() == 1); +} + +void IFlowCuratedNamePropertyCustomization::CreateHeaderRowWidget(FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils & StructCustomizationUtils) +{ + // Do one-time setup first + Initialize(); + + // Replace the default HeaderRow widget with one of our own + HeaderRow + .NameContent() + [ + SAssignNew(HeaderTextBlock, STextBlock) + .Text(BuildHeaderText()) + ] + .ValueContent() + .MaxDesiredWidth(250.0f) + [ + SAssignNew(TextListWidget, SComboBox>) + .OptionsSource(&CachedTextList) + .OnGenerateWidget(this, &IFlowCuratedNamePropertyCustomization::GenerateTextListWidget) + .OnComboBoxOpening(this, &IFlowCuratedNamePropertyCustomization::OnTextListComboBoxOpening) + .OnSelectionChanged(this, &IFlowCuratedNamePropertyCustomization::OnTextSelected) + [ + SNew(STextBlock) + .Text(this, &IFlowCuratedNamePropertyCustomization::GetCachedText) + .Font(IDetailLayoutBuilder::GetDetailFont()) + .ToolTipText(this, &IFlowCuratedNamePropertyCustomization::GetCachedText) + ] + ]; + + // Hook-up the ResetToDefault overrides + FIsResetToDefaultVisible IsResetVisible = + FIsResetToDefaultVisible::CreateSP( + this, + &IFlowCuratedNamePropertyCustomization::CustomIsResetToDefaultVisible); + FResetToDefaultHandler ResetHandler = + FResetToDefaultHandler::CreateSP( + this, + &IFlowCuratedNamePropertyCustomization::CustomResetToDefault); + FResetToDefaultOverride ResetOverride = FResetToDefaultOverride::Create(IsResetVisible, ResetHandler); + + HeaderRow.OverrideResetToDefault(ResetOverride); +} + +bool IFlowCuratedNamePropertyCustomization::CustomIsResetToDefaultVisible(TSharedPtr Property) const +{ + const FName CuratedName = GetCuratedName(); + return !CuratedName.IsNone(); +} + +void IFlowCuratedNamePropertyCustomization::CustomResetToDefault(TSharedPtr Property) +{ + if (TrySetCuratedNameWithSideEffects(NAME_None)) + { + RepaintTextListWidget(); + } +} + +bool IFlowCuratedNamePropertyCustomization::TrySetCuratedNameWithSideEffects(const FName& NewName) +{ + const FName ExistingName = GetCuratedName(); + + if (ExistingName != NewName) + { + // Set the new name on the actual struct first + SetCuratedName(NewName); + } + + // Ensure the FText representations are up to date + + TSharedPtr NewText = FindCachedOrCreateText(NewName); + + const bool bIsChanged = (NewText != CachedTextSelected); + + CachedTextSelected = NewText; + + InsertAtHeadOfCachedTextList(CachedTextSelected); + + // Set the Name property to the new value + check(CachedNameHandle.IsValid()); + CachedNameHandle->SetValue(NewName); + + return bIsChanged; +} + +FText IFlowCuratedNamePropertyCustomization::GetCachedText() const +{ + check(CachedTextSelected.IsValid()); + + return *CachedTextSelected.Get(); +} + +TSharedRef IFlowCuratedNamePropertyCustomization::GenerateTextListWidget(TSharedPtr InItem) +{ + return + SNew(STextBlock) + .Text(*InItem) + .ColorAndOpacity(FSlateColor::UseForeground()) + .Font(IDetailLayoutBuilder::GetDetailFont()); +} + +void IFlowCuratedNamePropertyCustomization::OnTextListComboBoxOpening() +{ + check(CachedTextSelected.IsValid()); + + // Create a dictionary of Names to their shared FTexts + // (to preserve the shared FText objects, if they already exist) + TMap> MapNameToText; + + const FName CurrentName = GetCuratedName(); + MapNameToText.Add(CurrentName, CachedTextSelected); + + for (TSharedPtr& Text : CachedTextList) + { + (void) MapNameToText.FindOrAdd(FName(Text.Get()->ToString()), Text); + } + + TArray CuratedNameOptions = GetCuratedNameOptions(); + + // (+2 to reserve space for the Selected and None entry) + CachedTextList.Empty(CuratedNameOptions.Num() + 2); + + // Populate the current selection at the top of the list + if (CuratedNameOptions.Contains(CurrentName) || CurrentName.IsNone()) + { + CachedTextList.Add(CachedTextSelected); + } + + // Populate the other curated name options + for (const FName& NameOption : CuratedNameOptions) + { + if (!NameOption.IsNone() && NameOption != CurrentName) + { + AddToCachedTextList(FindCachedOrCreateText(NameOption)); + } + } + + // Ensure "None" is in the list (if CurrentName is not None) + if (!CurrentName.IsNone()) + { + check(!CachedTextList.Contains(NoneAsText)); + + CachedTextList.Add(NoneAsText); + } +} + +void IFlowCuratedNamePropertyCustomization::OnTextSelected(TSharedPtr NewSelection, ESelectInfo::Type SelectInfo) +{ + // Called when the combo box has selected a new element + + // Process NewSelection and derive the matching Name + // (NewSelection can be null) + + FName NewName; + + if (NewSelection.IsValid()) + { + // Ensure NewSelection is in the CachedTextList + AddToCachedTextList(NewSelection); + + NewName = FName(NewSelection->ToString()); + } + else + { + NewName = NAME_None; + } + + if (TrySetCuratedNameWithSideEffects(NewName)) + { + RepaintTextListWidget(); + } +} + +TSharedPtr IFlowCuratedNamePropertyCustomization::FindCachedOrCreateText(const FName& NewName) +{ + if (NewName.IsNone()) + { + return NoneAsText; + } + + const FText NewText = FText::FromName(NewName); + + for (int32 Index = 0; Index < CachedTextList.Num(); ++Index) + { + const TSharedPtr& TextCur = CachedTextList[Index]; + + if (TextCur->EqualTo(NewText, ETextComparisonLevel::Default)) + { + return TextCur; + } + } + + TSharedPtr Result = MakeShareable(new FText(NewText)); + return Result; +} + +void IFlowCuratedNamePropertyCustomization::InsertAtHeadOfCachedTextList(TSharedPtr Text) +{ + CachedTextList.Remove(Text); + + CachedTextList.Insert(Text, 0); +} + +void IFlowCuratedNamePropertyCustomization::AddToCachedTextList(TSharedPtr Text) +{ + CachedTextList.AddUnique(Text); +} + +void IFlowCuratedNamePropertyCustomization::RepaintTextListWidget() +{ + if (TextListWidget.IsValid()) + { + // Prod UDE to refresh the widget to show the new change + TextListWidget->Invalidate(EInvalidateWidgetReason::Paint); + } +} diff --git a/Source/FlowEditor/Private/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.cpp b/Source/FlowEditor/Private/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.cpp new file mode 100644 index 000000000..033a27179 --- /dev/null +++ b/Source/FlowEditor/Private/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.cpp @@ -0,0 +1,82 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +// NOTE (gtaylor) This class is planned for submission to Epic to include in baseline UE. +// If/when that happens, we will want to remove this version and update to the latest one in the PropertyModule + +#include "UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h" + +#include "DetailWidgetRow.h" +#include "IDetailChildrenBuilder.h" +#include "IDetailPropertyRow.h" +#include "Widgets/Text/STextBlock.h" + + +// IFlowExtendedPropertyTypeCustomization Implementation + +void IFlowExtendedPropertyTypeCustomization::CustomizeHeader(TSharedRef InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) +{ + StructPropertyHandle = InStructPropertyHandle; + + // Connect our property callback to any of the children properties changing + uint32 NumChildren; + StructPropertyHandle->GetNumChildren(NumChildren); + + for (uint32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex) + { + const TSharedRef ChildHandle = StructPropertyHandle->GetChildHandle(ChildIndex).ToSharedRef(); + + ChildHandle->SetOnPropertyValueChanged( + FSimpleDelegate::CreateSP(this, &IFlowExtendedPropertyTypeCustomization::OnAnyChildPropertyChanged)); + } + + CreateHeaderRowWidget(HeaderRow, StructCustomizationUtils); +} + +void IFlowExtendedPropertyTypeCustomization::CustomizeChildrenDefaultImpl(TSharedRef PropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) +{ + // A 'default' implementation of CustomizeChildren + + uint32 NumChildren = 0; + PropertyHandle->GetNumChildren(NumChildren); + + for (uint32 ChildNum = 0; ChildNum < NumChildren; ++ChildNum) + { + StructBuilder.AddProperty(PropertyHandle->GetChildHandle(ChildNum).ToSharedRef()); + } +} + +void IFlowExtendedPropertyTypeCustomization::CreateHeaderRowWidget(FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) +{ + // Build the Slate widget for the header row + HeaderRow + .NameContent() + [ + SAssignNew(HeaderTextBlock, STextBlock) + .Text(BuildHeaderText()) + ]; +} + +void IFlowExtendedPropertyTypeCustomization::OnAnyChildPropertyChanged() +{ + RefreshHeader(); +} + +void IFlowExtendedPropertyTypeCustomization::RefreshHeader() const +{ + if (HeaderTextBlock.IsValid() && StructPropertyHandle.IsValid()) + { + HeaderTextBlock->SetText(BuildHeaderText()); + } +} + +FText IFlowExtendedPropertyTypeCustomization::BuildHeaderText() const +{ + if (StructPropertyHandle.IsValid()) + { + return StructPropertyHandle->GetPropertyDisplayName(); + } + else + { + return FText(); + } +} diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowOwnerFunctionRefCustomization.h b/Source/FlowEditor/Public/DetailCustomizations/FlowOwnerFunctionRefCustomization.h new file mode 100644 index 000000000..d3cb3f2a2 --- /dev/null +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowOwnerFunctionRefCustomization.h @@ -0,0 +1,53 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "UnrealExtensions/IFlowCuratedNamePropertyCustomization.h" + +#include "FlowOwnerFunctionRef.h" + + +// Forward Declaration +class UFlowAsset; +class UFlowNode; +class UObject; +class UClass; +class UFunction; +class UFlowNode_CallOwnerFunction; + + +// Details customization for FFlowOwnerFunctionRef +class FFlowOwnerFunctionRefCustomization : public IFlowCuratedNamePropertyCustomization +{ +private: + typedef IFlowCuratedNamePropertyCustomization Super; + +public: + static TSharedRef MakeInstance() { return MakeShareable(new FFlowOwnerFunctionRefCustomization()); } + +protected: + + //~Begin IPropertyTypeCustomization + virtual void CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; + //~End IPropertyTypeCustomization + + //~Begin ICuratedNamePropertyCustomization + virtual TSharedPtr GetCuratedNamePropertyHandle() const override; + virtual void SetCuratedName(const FName& NewName) override; + virtual FName GetCuratedName() const override; + virtual TArray GetCuratedNameOptions() const override; + //~End ICuratedNamePropertyCustomization + + // Accessor to return the actual struct being edited + FORCEINLINE FFlowOwnerFunctionRef* GetFlowOwnerFunctionRef() const + { return IFlowExtendedPropertyTypeCustomization::TryGetTypedStructValue(StructPropertyHandle); } + + const UClass* TryGetExpectedOwnerClass() const; + UFlowNode* TryGetFlowNodeOuter() const; + + static TArray GetFlowOwnerFunctionRefs(const UFlowNode_CallOwnerFunction& FlowNodeOwner, const UClass& ExpectedOwnerClass); + + static bool IsFunctionUsable(const UFunction& Function, const UFlowNode_CallOwnerFunction& FlowNodeOwner); + static bool DoesFunctionHaveExpectedParamType(const UFunction& Function, const UFlowNode_CallOwnerFunction& FlowNodeOwner); + +}; diff --git a/Source/FlowEditor/Public/FlowEditorModule.h b/Source/FlowEditor/Public/FlowEditorModule.h index 129f2c679..81839af3f 100644 --- a/Source/FlowEditor/Public/FlowEditorModule.h +++ b/Source/FlowEditor/Public/FlowEditorModule.h @@ -24,6 +24,7 @@ class FLOWEDITOR_API FFlowEditorModule : public IModuleInterface private: TArray> RegisteredAssetActions; TSet CustomClassLayouts; + TSet CustomStructLayouts; public: virtual void StartupModule() override; @@ -33,8 +34,11 @@ class FLOWEDITOR_API FFlowEditorModule : public IModuleInterface void RegisterAssets(); void UnregisterAssets(); - void RegisterPropertyCustomizations() const; + void RegisterDetailCustomizations(); + void UnregisterDetailCustomizations(); + void RegisterCustomClassLayout(const TSubclassOf Class, const FOnGetDetailCustomizationInstance DetailLayout); + void RegisterCustomStructLayout(const UScriptStruct& Struct, const FOnGetPropertyTypeCustomizationInstance DetailLayout); public: FDelegateHandle FlowTrackCreateEditorHandle; diff --git a/Source/FlowEditor/Public/UnrealExtensions/IFlowCuratedNamePropertyCustomization.h b/Source/FlowEditor/Public/UnrealExtensions/IFlowCuratedNamePropertyCustomization.h new file mode 100644 index 000000000..034f26edc --- /dev/null +++ b/Source/FlowEditor/Public/UnrealExtensions/IFlowCuratedNamePropertyCustomization.h @@ -0,0 +1,65 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +// NOTE (gtaylor) This class is planned for submission to Epic to include in baseline UE. +// If/when that happens, we will want to remove this version and update to the latest one in the PropertyModule + +#pragma once + +#include "IFlowExtendedPropertyTypeCustomization.h" +#include "Widgets/Input/SComboBox.h" + + +// A base-class to do property Customization for a struct that presents a curated list of FNames for selection +class FLOWEDITOR_API IFlowCuratedNamePropertyCustomization : public IFlowExtendedPropertyTypeCustomization +{ +public: + +protected: + + //Begin IExtendedPropertyTypeCustomization + virtual void CreateHeaderRowWidget(FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; + //End IExtendedPropertyTypeCustomization + + void Initialize(); + + // Helper function to set the property to a specified value + // (and handle all of the side-effects) + bool TrySetCuratedNameWithSideEffects(const FName& NewName); + + // Callbacks for the TextListWidget (see CreateHeaderRowWidget) + FText GetCachedText() const; + TSharedRef GenerateTextListWidget(TSharedPtr InItem); + void OnTextListComboBoxOpening(); + void OnTextSelected(TSharedPtr NewSelection, ESelectInfo::Type SelectInfo); + + void RepaintTextListWidget(); + + TSharedPtr FindCachedOrCreateText(const FName& NewName); + void AddToCachedTextList(TSharedPtr Text); + void InsertAtHeadOfCachedTextList(TSharedPtr Text); + + bool CustomIsResetToDefaultVisible(TSharedPtr Property) const; + void CustomResetToDefault(TSharedPtr Property); + + //Begin IFlowCuratedNamePropertyCustomization + virtual TSharedPtr GetCuratedNamePropertyHandle() const = 0; + virtual void SetCuratedName(const FName& NewName) = 0; + virtual FName GetCuratedName() const = 0; + virtual TArray GetCuratedNameOptions() const = 0; + //End IFlowCuratedNamePropertyCustomization + +public: + + // Cached property handle for the Curated Name property that is being customized + TSharedPtr CachedNameHandle; + + // Cache FTexts for the ComboBox dropdown & current selected + TArray> CachedTextList; + TSharedPtr CachedTextSelected; + + // Preallocated NAME_None as FText + static TSharedPtr NoneAsText; + + // Combo Box widget for displaying the curated list of Names + TSharedPtr>> TextListWidget; +}; diff --git a/Source/FlowEditor/Public/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h b/Source/FlowEditor/Public/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h new file mode 100644 index 000000000..35647cd28 --- /dev/null +++ b/Source/FlowEditor/Public/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h @@ -0,0 +1,78 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +// NOTE (gtaylor) This class is planned for submission to Epic to include in baseline UE. +// If/when that happens, we will want to remove this version and update to the latest one in the PropertyModule + +#pragma once + +#include "IPropertyTypeCustomization.h" +#include "PropertyHandle.h" +#include "Templates/SharedPointer.h" + +#include "IPropertyTypeCustomization.h" + + +// Forward Declarations +class STextBlock; +class FDetailWidgetRow; +class IDetailChildrenBuilder; +class IPropertyTypeCustomizationUtils; +class IDetailPropertyRow; +class IPropertyHandle; + + +// An extension of IPropertyTypeCustomization +// which adds some quality-of-life improvements for subclasses +class FLOWEDITOR_API IFlowExtendedPropertyTypeCustomization : public IPropertyTypeCustomization +{ +public: + + // IPropertyTypeCustomization interface + virtual void CustomizeHeader( TSharedRef InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils ) override; + virtual void CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override + { CustomizeChildrenDefaultImpl(InStructPropertyHandle, StructBuilder, StructCustomizationUtils); } + + static void CustomizeChildrenDefaultImpl(TSharedRef StructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils); + + template + static StructT* TryGetTypedStructValue(const TSharedPtr& StructPropertyHandle); + +protected: + + void RefreshHeader() const; + + virtual void CreateHeaderRowWidget(FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils); + virtual FText BuildHeaderText() const; + + // Callbacks for property editor delegates + void OnAnyChildPropertyChanged(); + +protected: + + // Cached struct property + TSharedPtr StructPropertyHandle; + + // Header property text block, (re-)built in RefreshHeader + TSharedPtr HeaderTextBlock; +}; + + +// Inline Implementations + +template +StructT* IFlowExtendedPropertyTypeCustomization::TryGetTypedStructValue(const TSharedPtr& StructPropertyHandle) +{ + if (StructPropertyHandle.IsValid()) + { + // Get the actual struct data from the handle and cast it to the correct type + TArray RawData; + StructPropertyHandle->AccessRawData(RawData); + + if (RawData.Num() > 0) + { + return reinterpret_cast(RawData[0]); + } + } + + return nullptr; +} From de403ef8260273c12fc99e819fac3a2f9ac0716f Mon Sep 17 00:00:00 2001 From: "Satheesh (ryanjon2040)" Date: Sat, 8 Jul 2023 21:58:45 +0530 Subject: [PATCH 184/485] Consolidate settings #167 --- Source/Flow/Public/FlowSettings.h | 7 +++++++ Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h | 5 +++++ Source/FlowEditor/Public/Graph/FlowGraphSettings.h | 5 +++++ 3 files changed, 17 insertions(+) diff --git a/Source/Flow/Public/FlowSettings.h b/Source/Flow/Public/FlowSettings.h index 4d266cd8e..6b8b0ea8d 100644 --- a/Source/Flow/Public/FlowSettings.h +++ b/Source/Flow/Public/FlowSettings.h @@ -52,4 +52,11 @@ class FLOW_API UFlowSettings : public UDeveloperSettings UClass* GetDefaultExpectedOwnerClass() const; static UClass* TryResolveOrLoadSoftClass(const FSoftClassPath& SoftClassPath); +public: + +#if WITH_EDITORONLY_DATA + virtual FName GetCategoryName() const override { return FName("Flow Graph"); } + virtual FText GetSectionText() const override { return INVTEXT("Settings"); } +#endif + }; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h index 54bd78465..481f3087a 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h @@ -53,4 +53,9 @@ class FLOWEDITOR_API UFlowGraphEditorSettings : public UDeveloperSettings UPROPERTY(EditAnywhere, config, Category = "Wires") bool bHighlightOutputWiresOfSelectedNodes; + +public: + + virtual FName GetCategoryName() const override { return FName("Flow Graph"); } + virtual FText GetSectionText() const override { return INVTEXT("Editor Settings"); } }; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h index 13eab763b..ac9ba8862 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h @@ -106,4 +106,9 @@ class FLOWEDITOR_API UFlowGraphSettings : public UDeveloperSettings UPROPERTY(EditAnywhere, config, Category = "Wires", meta = (ClampMin = 0.0f)) float SelectedWireThickness; + +public: + + virtual FName GetCategoryName() const override { return FName("Flow Graph"); } + virtual FText GetSectionText() const override { return INVTEXT("Graph Settings"); } }; From 16fa475617d5379fce3635de0773cb416d42c3c5 Mon Sep 17 00:00:00 2001 From: Jani Hartikainen Date: Sat, 8 Jul 2023 19:34:38 +0300 Subject: [PATCH 185/485] Allow Sequence nodes to execute new connections (#130) --- .../Route/FlowNode_ExecutionSequence.cpp | 38 +++++++++++++++++++ .../Nodes/Route/FlowNode_ExecutionSequence.h | 24 ++++++++++++ 2 files changed, 62 insertions(+) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp index e7d8d1631..c76a7533e 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp @@ -16,6 +16,12 @@ UFlowNode_ExecutionSequence::UFlowNode_ExecutionSequence(const FObjectInitialize void UFlowNode_ExecutionSequence::ExecuteInput(const FName& PinName) { + if(bSavePinExecutionState) + { + ExecuteNewConnections(); + return; + } + for (const FFlowPin& Output : OutputPins) { TriggerOutput(Output.PinName, false); @@ -23,3 +29,35 @@ void UFlowNode_ExecutionSequence::ExecuteInput(const FName& PinName) Finish(); } + +#if WITH_EDITOR +FString UFlowNode_ExecutionSequence::GetNodeDescription() const +{ + if(bSavePinExecutionState) + { + return TEXT("Saves pin execution state"); + } + + return Super::GetNodeDescription(); +} +#endif + +void UFlowNode_ExecutionSequence::OnLoad_Implementation() +{ + ExecuteNewConnections(); +} + +void UFlowNode_ExecutionSequence::ExecuteNewConnections() +{ + for (const FFlowPin& Output : OutputPins) + { + const FConnectedPin& Connection = GetConnection(Output.PinName); + if(ExecutedConnections.Contains(Connection.NodeGuid)) + { + continue; + } + + TriggerOutput(Output.PinName, false); + ExecutedConnections.Emplace(Connection.NodeGuid); + } +} diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionSequence.h b/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionSequence.h index bda6e8e57..2882b917e 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionSequence.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionSequence.h @@ -15,8 +15,32 @@ class FLOW_API UFlowNode_ExecutionSequence final : public UFlowNode #if WITH_EDITOR virtual bool CanUserAddOutput() const override { return true; } + + virtual FString GetNodeDescription() const override; #endif + virtual void OnLoad_Implementation() override; + protected: virtual void ExecuteInput(const FName& PinName) override; + + /** + * If enabled and the flowgraph is saved during gameplay, this node + * tracks and saves which pins it has executed. + * + * If you add new connections or replace old connections with with + * different nodes, this node will detect the changes. If during gameplay + * you load an old save game which had different connections, this node + * will automatically execute the updated connections you created. + * + * This is useful if you want the ability to add new parts to your + * graph after release. + */ + UPROPERTY(EditAnywhere) + bool bSavePinExecutionState = false; + + UPROPERTY(SaveGame) + TSet ExecutedConnections; + + void ExecuteNewConnections(); }; From 39d2949000d9aab92f603b571277dd8ba9cbb522 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sat, 8 Jul 2023 18:53:12 +0200 Subject: [PATCH 186/485] cosmetic tweaks to PR #167 --- Source/Flow/Public/FlowSettings.h | 2 -- Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h | 3 +-- Source/FlowEditor/Public/Graph/FlowGraphSettings.h | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Source/Flow/Public/FlowSettings.h b/Source/Flow/Public/FlowSettings.h index 6b8b0ea8d..1f7c93ad9 100644 --- a/Source/Flow/Public/FlowSettings.h +++ b/Source/Flow/Public/FlowSettings.h @@ -52,11 +52,9 @@ class FLOW_API UFlowSettings : public UDeveloperSettings UClass* GetDefaultExpectedOwnerClass() const; static UClass* TryResolveOrLoadSoftClass(const FSoftClassPath& SoftClassPath); -public: #if WITH_EDITORONLY_DATA virtual FName GetCategoryName() const override { return FName("Flow Graph"); } virtual FText GetSectionText() const override { return INVTEXT("Settings"); } #endif - }; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h index 481f3087a..51d00ef27 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h @@ -55,7 +55,6 @@ class FLOWEDITOR_API UFlowGraphEditorSettings : public UDeveloperSettings bool bHighlightOutputWiresOfSelectedNodes; public: - virtual FName GetCategoryName() const override { return FName("Flow Graph"); } - virtual FText GetSectionText() const override { return INVTEXT("Editor Settings"); } + virtual FText GetSectionText() const override { return INVTEXT("User Settings"); } }; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h index ac9ba8862..a21116bb6 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h @@ -108,7 +108,6 @@ class FLOWEDITOR_API UFlowGraphSettings : public UDeveloperSettings float SelectedWireThickness; public: - virtual FName GetCategoryName() const override { return FName("Flow Graph"); } virtual FText GetSectionText() const override { return INVTEXT("Graph Settings"); } }; From 9d551525ceb0ea6e101c91a3bb46fc204eadfe25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sat, 8 Jul 2023 19:06:10 +0200 Subject: [PATCH 187/485] compilation fix --- Source/Flow/Public/FlowAsset.h | 2 +- Source/Flow/Public/FlowOwnerFunctionParams.h | 18 +++++++++--------- Source/Flow/Public/FlowOwnerFunctionRef.h | 4 ++-- Source/Flow/Public/FlowSettings.h | 2 +- .../Nodes/Route/FlowNode_ExecutionSequence.h | 2 +- .../Nodes/World/FlowNode_CallOwnerFunction.h | 4 ++-- .../Private/Asset/FlowDiffControl.cpp | 2 +- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 57467169a..6941109bd 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -359,7 +359,7 @@ class FLOW_API UFlowAsset : public UObject // Expects to be owned (at runtime) by an object with this class (or one of its subclasses) // NOTE - If the class is an AActor, and the flow asset is owned by a component, // it will consider the component's owner for the AActor - UPROPERTY(EditAnywhere, meta = (MustImplement = "FlowOwnerInterface")) + UPROPERTY(EditAnywhere, Category = "Flow", meta = (MustImplement = "FlowOwnerInterface")) TSubclassOf ExpectedOwnerClass; ////////////////////////////////////////////////////////////////////////// diff --git a/Source/Flow/Public/FlowOwnerFunctionParams.h b/Source/Flow/Public/FlowOwnerFunctionParams.h index f3f49797f..f627fe875 100644 --- a/Source/Flow/Public/FlowOwnerFunctionParams.h +++ b/Source/Flow/Public/FlowOwnerFunctionParams.h @@ -23,7 +23,7 @@ class FLOW_API UFlowOwnerFunctionParams : public UObject void PreExecute(UFlowNode_CallOwnerFunction& InSourceNode, const FName& InputPinName); void PostExecute(); - UFUNCTION(BlueprintNativeEvent, BlueprintPure) + UFUNCTION(BlueprintNativeEvent, BlueprintPure, Category = "FlowOwnerFunction") bool ShouldFinishForOutputName(const FName& OutputName) const; #if WITH_EDITORONLY_DATA @@ -41,43 +41,43 @@ class FLOW_API UFlowOwnerFunctionParams : public UObject // Called prior to the owner executing the function described by this object. // Can be overridden to prepare the stateful data before execution. - UFUNCTION(BlueprintImplementableEvent, DisplayName = "PreExecute") + UFUNCTION(BlueprintImplementableEvent, Category = "FlowOwnerFunction", DisplayName = "PreExecute") void BP_PreExecute(); // Cleans up the stateful data in this Params struct. // Can be overridden to cleanup the stateful data after execution. - UFUNCTION(BlueprintImplementableEvent, DisplayName = "PostExecute") + UFUNCTION(BlueprintImplementableEvent, Category = "FlowOwnerFunction", DisplayName = "PostExecute") void BP_PostExecute(); // Get the input pin names for the SourceNode // Valid only if called between PreExecute() and PostExecute(), inclusive - UFUNCTION(BlueprintCallable, BlueprintPure, DisplayName = "GetInputNames") + UFUNCTION(BlueprintCallable, BlueprintPure, Category = "FlowOwnerFunction", DisplayName = "GetInputNames") TArray BP_GetInputNames() const; // Get the output pin names for the SourceNode // Valid only if called between PreExecute() and PostExecute(), inclusive - UFUNCTION(BlueprintCallable, BlueprintPure, DisplayName = "GetOutputNames") + UFUNCTION(BlueprintCallable, BlueprintPure, Category = "FlowOwnerFunction", DisplayName = "GetOutputNames") TArray BP_GetOutputNames() const; protected: // CallOwnerObjectFunction node that is executing this set of function params. // Valid only if called between PreExecute() and PostExecute(), inclusive - UPROPERTY(Transient, BlueprintReadOnly) + UPROPERTY(Transient, BlueprintReadOnly, Category = "FlowOwnerFunction") UFlowNode_CallOwnerFunction* SourceNode = nullptr; // This is the Name from the Input Pin that caused this node to Execute. // Valid only if called between PreExecute() and PostExecute(), inclusive - UPROPERTY(Transient, BlueprintReadOnly) + UPROPERTY(Transient, BlueprintReadOnly, Category = "FlowOwnerFunction") FName ExecutedInputPinName; #if WITH_EDITORONLY_DATA // Input pin names for this function - UPROPERTY(EditDefaultsOnly) + UPROPERTY(EditDefaultsOnly, Category = "FlowOwnerFunction") TArray InputNames; // Output pin names for this function - UPROPERTY(EditDefaultsOnly) + UPROPERTY(EditDefaultsOnly, Category = "FlowOwnerFunction") TArray OutputNames; #endif // WITH_EDITORONLY_DATA }; diff --git a/Source/Flow/Public/FlowOwnerFunctionRef.h b/Source/Flow/Public/FlowOwnerFunctionRef.h index 4e628d1ab..629949c8d 100644 --- a/Source/Flow/Public/FlowOwnerFunctionRef.h +++ b/Source/Flow/Public/FlowOwnerFunctionRef.h @@ -44,7 +44,7 @@ struct FFlowOwnerFunctionRef protected: // The name of the function to call - UPROPERTY(VisibleAnywhere) + UPROPERTY(VisibleAnywhere, Category = "FlowOwnerFunction") FName FunctionName = NAME_None; // The function to call @@ -53,7 +53,7 @@ struct FFlowOwnerFunctionRef TObjectPtr Function = nullptr; #if WITH_EDITORONLY_DATA - UPROPERTY(VisibleAnywhere, meta = (DisplayName = "Function Parameters Class")) + UPROPERTY(VisibleAnywhere, Category = "FlowOwnerFunction", meta = (DisplayName = "Function Parameters Class")) TSubclassOf ParamsClass; #endif // WITH_EDITORONLY_DATA }; diff --git a/Source/Flow/Public/FlowSettings.h b/Source/Flow/Public/FlowSettings.h index 1f7c93ad9..d02e9776d 100644 --- a/Source/Flow/Public/FlowSettings.h +++ b/Source/Flow/Public/FlowSettings.h @@ -45,7 +45,7 @@ class FLOW_API UFlowSettings : public UDeveloperSettings bool bUseAdaptiveNodeTitles; // Default class to use as a FlowAsset's "ExpectedOwnerClass" - UPROPERTY(EditAnywhere, Config, meta = (MustImplement = "FlowOwnerInterface")) + UPROPERTY(EditAnywhere, Config, Category = "Nodes", meta = (MustImplement = "FlowOwnerInterface")) FSoftClassPath DefaultExpectedOwnerClass; public: diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionSequence.h b/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionSequence.h index 2882b917e..ef7482ca2 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionSequence.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionSequence.h @@ -36,7 +36,7 @@ class FLOW_API UFlowNode_ExecutionSequence final : public UFlowNode * This is useful if you want the ability to add new parts to your * graph after release. */ - UPROPERTY(EditAnywhere) + UPROPERTY(EditAnywhere, Category = "Sequence") bool bSavePinExecutionState = false; UPROPERTY(SaveGame) diff --git a/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h b/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h index 2f3302af5..55d8266ce 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h @@ -86,10 +86,10 @@ class FLOW_API UFlowNode_CallOwnerFunction : public UFlowNode protected: // Function reference on the expected owner to call - UPROPERTY(EditAnywhere, meta = (DisplayName = "Function")) + UPROPERTY(EditAnywhere, Category = "Call Owner", meta = (DisplayName = "Function")) FFlowOwnerFunctionRef FunctionRef; // Parameter object to pass to the function when called - UPROPERTY(EditAnywhere, Instanced) + UPROPERTY(EditAnywhere, Category = "Call Owner", Instanced) UFlowOwnerFunctionParams* Params; }; diff --git a/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp b/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp index 85cf96d50..14727b70d 100644 --- a/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp +++ b/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp @@ -17,7 +17,7 @@ FFlowAssetDiffControl::FFlowAssetDiffControl(const UFlowAsset* InOldFlowAsset, const UFlowAsset* InNewFlowAsset, FOnDiffEntryFocused InSelectionCallback) #if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 2 - : FDetailsDiffControl(InOldFlowAsset, InNewFlowAsset, InSelectionCallback) + : TDetailsDiffControl(InOldFlowAsset, InNewFlowAsset, InSelectionCallback) #else : FDetailsDiffControl(InOldFlowAsset, InNewFlowAsset, InSelectionCallback, false) #endif From e3febad9db264e748cdddb39bed351730c185540 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 23 Jul 2023 18:30:36 +0200 Subject: [PATCH 188/485] Cleanup * `GetNodesInExecutionOrder` - added `UFlowNode* FirstIteratedNode` parameter, allowing to start iteration from any node. * Remove the default implementation of `UFlowAsset::PreloadNodes`, it was a forever prototype. You can still implement this method on your own. * Corrected class layout. --- Source/Flow/Private/FlowAsset.cpp | 87 +++++++++++-------------------- Source/Flow/Public/FlowAsset.h | 41 ++++++++------- Source/Flow/Public/FlowSettings.h | 4 -- 3 files changed, 50 insertions(+), 82 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index e9ad55237..c542f016c 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -269,7 +269,32 @@ void UFlowAsset::HarvestNodeConnections() } } } +#endif + +UFlowNode* UFlowAsset::GetDefaultEntryNode() const +{ + UFlowNode* FirstStartNode = nullptr; + + for (const TPair& Node : Nodes) + { + if (UFlowNode_Start* StartNode = Cast(Node.Value)) + { + if (StartNode->GetConnectedNodes().Num() > 0) + { + return StartNode; + } + else if (FirstStartNode == nullptr) + { + FirstStartNode = StartNode; + } + } + } + // If none of the found start nodes have connections, fallback to the first start node we found + return FirstStartNode; +} + +#if WITH_EDITOR void UFlowAsset::AddCustomInput(const FName& EventName) { if (!CustomInputs.Contains(EventName)) @@ -301,7 +326,7 @@ void UFlowAsset::RemoveCustomOutput(const FName& EventName) CustomOutputs.Remove(EventName); } } -#endif // WITH_EDITOR +#endif UFlowNode_CustomInput* UFlowAsset::TryFindCustomInputNodeByEventName(const FName& EventName) const { @@ -316,33 +341,10 @@ UFlowNode_CustomInput* UFlowAsset::TryFindCustomInputNodeByEventName(const FName return nullptr; } -UFlowNode* UFlowAsset::GetDefaultEntryNode() const -{ - UFlowNode* FirstStartNode = nullptr; - - for (const TPair& Node : Nodes) - { - if (UFlowNode_Start* StartNode = Cast(Node.Value)) - { - if (StartNode->GetConnectedNodes().Num() > 0) - { - return StartNode; - } - else if (FirstStartNode == nullptr) - { - FirstStartNode = StartNode; - } - } - } - - // If none of the found start nodes have connections, fallback to the first start node we found - return FirstStartNode; -} - -TArray UFlowAsset::GetNodesInExecutionOrder(const TSubclassOf FlowNodeClass) +TArray UFlowAsset::GetNodesInExecutionOrder(UFlowNode* FirstIteratedNode, const TSubclassOf FlowNodeClass) { TArray FoundNodes; - GetNodesInExecutionOrder(FoundNodes); + GetNodesInExecutionOrder(FirstIteratedNode, FoundNodes); // filter out nodes by class for (int32 i = FoundNodes.Num() - 1; i >= 0; i--) @@ -481,37 +483,6 @@ void UFlowAsset::DeinitializeInstance() } } -void UFlowAsset::PreloadNodes() -{ - TArray GraphEntryNodes = {GetDefaultEntryNode()}; - for (UFlowNode_CustomInput* CustomInput : CustomInputNodes) - { - GraphEntryNodes.Emplace(CustomInput); - } - - // NOTE: this is just the example algorithm of gathering nodes for pre-load - for (UFlowNode* EntryNode : GraphEntryNodes) - { - for (const TPair, int32>& Node : UFlowSettings::Get()->DefaultPreloadDepth) - { - if (Node.Value > 0) - { - TArray FoundNodes; - UFlowNode::RecursiveFindNodesByClass(EntryNode, Node.Key, Node.Value, FoundNodes); - - for (UFlowNode* FoundNode : FoundNodes) - { - if (!PreloadedNodes.Contains(FoundNode)) - { - FoundNode->TriggerPreload(); - PreloadedNodes.Emplace(FoundNode); - } - } - } - } - } -} - void UFlowAsset::PreStartFlow() { ResetNodes(); @@ -699,7 +670,7 @@ FFlowAssetSaveData UFlowAsset::SaveInstance(TArray& SavedFlo // iterate nodes TArray NodesInExecutionOrder; - GetNodesInExecutionOrder(NodesInExecutionOrder); + GetNodesInExecutionOrder(GetDefaultEntryNode(), NodesInExecutionOrder); for (UFlowNode* Node : NodesInExecutionOrder) { if (Node && Node->ActivationState == EFlowNodeState::Active) diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 6941109bd..56a0a1548 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -159,18 +159,33 @@ class FLOW_API UFlowAsset : public UObject virtual UFlowNode* GetDefaultEntryNode() const; +#if WITH_EDITOR +protected: + void AddCustomInput(const FName& EventName); + void RemoveCustomInput(const FName& EventName); + + void AddCustomOutput(const FName& EventName); + void RemoveCustomOutput(const FName& EventName); +#endif + +public: + const TArray& GetCustomInputs() const { return CustomInputs; } + const TArray& GetCustomOutputs() const { return CustomOutputs; } + + UFlowNode_CustomInput* TryFindCustomInputNodeByEventName(const FName& EventName) const; + UFUNCTION(BlueprintPure, Category = "FlowAsset", meta = (DeterminesOutputType = "FlowNodeClass")) - TArray GetNodesInExecutionOrder(const TSubclassOf FlowNodeClass); + TArray GetNodesInExecutionOrder(UFlowNode* FirstIteratedNode, const TSubclassOf FlowNodeClass); template - void GetNodesInExecutionOrder(TArray& OutNodes) + void GetNodesInExecutionOrder(UFlowNode* FirstIteratedNode, TArray& OutNodes) { static_assert(TPointerIsConvertibleFromTo::Value, "'T' template parameter to GetNodesInExecutionOrder must be derived from UFlowNode"); - if (UFlowNode* FoundStartNode = GetDefaultEntryNode()) + if (FirstIteratedNode) { TSet> IteratedNodes; - GetNodesInExecutionOrder_Recursive(FoundStartNode, IteratedNodes, OutNodes); + GetNodesInExecutionOrder_Recursive(FirstIteratedNode, IteratedNodes, OutNodes); } } @@ -194,21 +209,6 @@ class FLOW_API UFlowAsset : public UObject } } -public: - const TArray& GetCustomInputs() const { return CustomInputs; } - const TArray& GetCustomOutputs() const { return CustomOutputs; } - - UFlowNode_CustomInput* TryFindCustomInputNodeByEventName(const FName& EventName) const; - -#if WITH_EDITOR -protected: - void AddCustomInput(const FName& EventName); - void RemoveCustomInput(const FName& EventName); - - void AddCustomOutput(const FName& EventName); - void RemoveCustomOutput(const FName& EventName); -#endif // WITH_EDITOR - ////////////////////////////////////////////////////////////////////////// // Instances of the template asset @@ -308,7 +308,8 @@ class FLOW_API UFlowAsset : public UObject UFUNCTION(BlueprintPure, Category = "Flow") AActor* TryFindActorOwner() const; - virtual void PreloadNodes(); + // Opportunity to preload content of project-specific nodes + virtual void PreloadNodes() {} virtual void PreStartFlow(); virtual void StartFlow(); diff --git a/Source/Flow/Public/FlowSettings.h b/Source/Flow/Public/FlowSettings.h index d02e9776d..ddb491d13 100644 --- a/Source/Flow/Public/FlowSettings.h +++ b/Source/Flow/Public/FlowSettings.h @@ -24,10 +24,6 @@ class FLOW_API UFlowSettings : public UDeveloperSettings UPROPERTY(Config, EditAnywhere, Category = "Networking") bool bCreateFlowSubsystemOnClients; - // How many nodes of given class should be preloaded with the Flow Asset instance? - UPROPERTY(Config, EditAnywhere, Category = "Preload") - TMap, int32> DefaultPreloadDepth; - UPROPERTY(Config, EditAnywhere, Category = "SaveSystem") bool bWarnAboutMissingIdentityTags; From 17e4569a90d439b902ce9876285a9d888f53866e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 23 Jul 2023 18:31:10 +0200 Subject: [PATCH 189/485] Removed RecursiveFindNodesByClass method, superseded by UFlowAsset::GetNodesInExecutionOrder --- Source/Flow/Private/Nodes/FlowNode.cpp | 23 ----------------------- Source/Flow/Public/Nodes/FlowNode.h | 2 -- 2 files changed, 25 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 2de2943ab..6ab023a92 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -468,29 +468,6 @@ bool UFlowNode::IsOutputConnected(const FName& PinName) const return OutputPins.Contains(PinName) && Connections.Contains(PinName); } -void UFlowNode::RecursiveFindNodesByClass(UFlowNode* Node, const TSubclassOf Class, uint8 Depth, TArray& OutNodes) -{ - if (Node) - { - // Record the node if it is the desired type - if (Node->GetClass() == Class) - { - OutNodes.AddUnique(Node); - } - - if (OutNodes.Num() == Depth) - { - return; - } - - // Recurse - for (UFlowNode* ConnectedNode : Node->GetConnectedNodes()) - { - RecursiveFindNodesByClass(ConnectedNode, Class, Depth, OutNodes); - } - } -} - UFlowSubsystem* UFlowNode::GetFlowSubsystem() const { return GetFlowAsset() ? GetFlowAsset()->GetFlowSubsystem() : nullptr; diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 49a59dc5e..a6260736f 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -239,8 +239,6 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UFUNCTION(BlueprintPure, Category= "FlowNode") bool IsOutputConnected(const FName& PinName) const; - static void RecursiveFindNodesByClass(UFlowNode* Node, const TSubclassOf Class, uint8 Depth, TArray& OutNodes); - ////////////////////////////////////////////////////////////////////////// // Debugger protected: From e4f8c0cd1df57c79ba8d0ba34e3ba1d399199162 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 23 Jul 2023 18:31:34 +0200 Subject: [PATCH 190/485] Removed the use of FStreamableManager --- Source/Flow/Private/FlowSubsystem.cpp | 31 +++++++++++---------------- Source/Flow/Public/FlowSubsystem.h | 3 --- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index 9ccb506bf..404d19042 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -16,8 +16,6 @@ #include "UObject/UObjectHash.h" #if WITH_EDITOR -#include "Logging/MessageLog.h" - FNativeFlowAssetEvent UFlowSubsystem::OnInstancedTemplateAdded; FNativeFlowAssetEvent UFlowSubsystem::OnInstancedTemplateRemoved; #endif @@ -85,7 +83,7 @@ void UFlowSubsystem::StartRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const NewFlow->StartFlow(); } } -#if WITH_EDITOR +#if WITH_EDITOR else { FMessageLog("PIE").Error(LOCTEXT("StartRootFlowNullAsset", "Attempted to start Root Flow with a null asset.")) @@ -206,33 +204,32 @@ void UFlowSubsystem::RemoveSubFlow(UFlowNode_SubGraph* SubGraphNode, const EFlow UFlowAsset* UFlowSubsystem::CreateFlowInstance(const TWeakObjectPtr Owner, TSoftObjectPtr FlowAsset, FString NewInstanceName) { - check(!FlowAsset.IsNull()); - - if (FlowAsset.IsPending() || !FlowAsset.IsValid()) + UFlowAsset* LoadedFlowAsset = FlowAsset.LoadSynchronous(); + if (!ensureAlways(LoadedFlowAsset)) { - FlowAsset = Cast(Streamable.LoadSynchronous(FlowAsset.ToSoftObjectPath(), false)); + return nullptr; } - AddInstancedTemplate(FlowAsset.Get()); + AddInstancedTemplate(LoadedFlowAsset); #if WITH_EDITOR if (GetWorld()->WorldType != EWorldType::Game) { // Fix connections - even in packaged game if assets haven't been re-saved in the editor after changing node's definition - FlowAsset.Get()->HarvestNodeConnections(); + LoadedFlowAsset->HarvestNodeConnections(); } #endif // it won't be empty, if we're restoring Flow Asset instance from the SaveGame if (NewInstanceName.IsEmpty()) { - NewInstanceName = MakeUniqueObjectName(this, UFlowAsset::StaticClass(), *FPaths::GetBaseFilename(FlowAsset.Get()->GetPathName())).ToString(); + NewInstanceName = MakeUniqueObjectName(this, UFlowAsset::StaticClass(), *FPaths::GetBaseFilename(LoadedFlowAsset->GetPathName())).ToString(); } - UFlowAsset* NewInstance = NewObject(this, FlowAsset->GetClass(), *NewInstanceName, RF_Transient, FlowAsset.Get(), false, nullptr); - NewInstance->InitializeInstance(Owner, FlowAsset.Get()); + UFlowAsset* NewInstance = NewObject(this, LoadedFlowAsset->GetClass(), *NewInstanceName, RF_Transient, LoadedFlowAsset, false, nullptr); + NewInstance->InitializeInstance(Owner, LoadedFlowAsset); - FlowAsset.Get()->AddInstance(NewInstance); + LoadedFlowAsset->AddInstance(NewInstance); return NewInstance; } @@ -395,16 +392,12 @@ void UFlowSubsystem::LoadSubFlow(UFlowNode_SubGraph* SubGraphNode, const FString return; } - if (SubGraphNode->Asset.IsPending()) - { - const FSoftObjectPath& AssetRef = SubGraphNode->Asset.ToSoftObjectPath(); - Streamable.LoadSynchronous(AssetRef, false); - } + UFlowAsset* SubGraphAsset = SubGraphNode->Asset.LoadSynchronous(); for (const FFlowAssetSaveData& AssetRecord : LoadedSaveGame->FlowInstances) { if (AssetRecord.InstanceName == SavedAssetInstanceName - && ((SubGraphNode->Asset && SubGraphNode->Asset->IsBoundToWorld() == false) || AssetRecord.WorldName == GetWorld()->GetName())) + && ((SubGraphAsset && SubGraphAsset->IsBoundToWorld() == false) || AssetRecord.WorldName == GetWorld()->GetName())) { UFlowAsset* LoadedInstance = CreateSubFlow(SubGraphNode, SavedAssetInstanceName); if (LoadedInstance) diff --git a/Source/Flow/Public/FlowSubsystem.h b/Source/Flow/Public/FlowSubsystem.h index ca9b3aa86..4633eef40 100644 --- a/Source/Flow/Public/FlowSubsystem.h +++ b/Source/Flow/Public/FlowSubsystem.h @@ -2,7 +2,6 @@ #pragma once -#include "Engine/StreamableManager.h" #include "GameFramework/Actor.h" #include "GameplayTagContainer.h" #include "Subsystems/GameInstanceSubsystem.h" @@ -50,8 +49,6 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem UPROPERTY() TMap InstancedSubFlows; - FStreamableManager Streamable; - #if WITH_EDITOR public: /* Called after creating the first instance of given Flow Asset */ From 0a42d9987a9d325fcd1dc7f0360863a1822387a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 23 Jul 2023 18:46:14 +0200 Subject: [PATCH 191/485] Moved declaration of FStreamableManager to only Flow Node class actually using it --- Source/Flow/Public/Nodes/FlowNode.h | 3 --- Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index a6260736f..ef47720b5 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -3,7 +3,6 @@ #pragma once #include "EdGraph/EdGraphNode.h" -#include "Engine/StreamableManager.h" #include "GameplayTagContainer.h" #include "Templates/SubclassOf.h" #include "VisualLogger/VisualLoggerDebugSnapshotInterface.h" @@ -254,8 +253,6 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte bool bPreloaded; protected: - FStreamableManager StreamableManager; - UPROPERTY(SaveGame) EFlowNodeState ActivationState; diff --git a/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h b/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h index ad118d008..d7ef98831 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h @@ -3,6 +3,7 @@ #pragma once #include "EngineDefines.h" +#include "Engine/StreamableManager.h" #include "LevelSequencePlayer.h" #include "MovieSceneSequencePlayer.h" @@ -26,6 +27,7 @@ class FLOW_API UFlowNode_PlayLevelSequence : public UFlowNode GENERATED_UCLASS_BODY() friend struct FFlowTrackExecutionToken; +public: static FFlowNodeLevelSequenceEvent OnPlaybackStarted; static FFlowNodeLevelSequenceEvent OnPlaybackCompleted; @@ -79,6 +81,8 @@ class FLOW_API UFlowNode_PlayLevelSequence : public UFlowNode UPROPERTY(SaveGame) float TimeDilation; + FStreamableManager StreamableManager; + public: #if WITH_EDITOR virtual bool SupportsContextPins() const override { return true; } From 11bbdb33706b3c2c99d4658b800e04411033a06e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 23 Jul 2023 19:48:27 +0200 Subject: [PATCH 192/485] removed unused include --- Source/Flow/Private/FlowSettings.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Flow/Private/FlowSettings.cpp b/Source/Flow/Private/FlowSettings.cpp index 56fff80ce..0e44fd35c 100644 --- a/Source/Flow/Private/FlowSettings.cpp +++ b/Source/Flow/Private/FlowSettings.cpp @@ -2,7 +2,6 @@ #include "FlowSettings.h" #include "FlowComponent.h" -#include "FlowOwnerInterface.h" UFlowSettings::UFlowSettings(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) From 143e6bb1c1826f4e31d9ffc6bcc4cae89ab68621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 23 Jul 2023 19:54:02 +0200 Subject: [PATCH 193/485] reworked FFlowBreakpoint into FFlowPinTrait - inviting to use for things other than breakpoints --- Config/BaseFlow.ini | 2 + Config/DefaultFlow.ini | 2 + Source/Flow/Private/Nodes/FlowPin.cpp | 87 ++++++++++++++++++- Source/Flow/Public/Nodes/FlowPin.h | 44 ++++++++++ .../Private/Graph/FlowGraphEditor.cpp | 35 ++++---- .../Private/Graph/Nodes/FlowGraphNode.cpp | 85 ++---------------- .../Private/Graph/Widgets/SFlowGraphNode.cpp | 16 ++-- .../Public/Graph/Nodes/FlowGraphNode.h | 36 +------- .../Public/Graph/Widgets/SFlowGraphNode.h | 2 +- 9 files changed, 169 insertions(+), 140 deletions(-) diff --git a/Config/BaseFlow.ini b/Config/BaseFlow.ini index f42430df5..7ec2bf1d9 100644 --- a/Config/BaseFlow.ini +++ b/Config/BaseFlow.ini @@ -1,3 +1,5 @@ [CoreRedirects] +ClassRedirects=(OldName="/Script/Flow.FlowNode_CustomEvent",NewName="/Script/Flow.FlowNode_CustomInput") +PropertyRedirects=(OldName="FlowAsset.CustomEvents",NewName="CustomInputs") ++StructRedirects=(OldName="/Script/Flow.FlowBreakpoint",NewName="/Script/Flow.FlowPinTrait") ++PropertyRedirects=(OldName="FlowPinTrait.bHasBreakpoint",NewName="bAllowed") \ No newline at end of file diff --git a/Config/DefaultFlow.ini b/Config/DefaultFlow.ini index f42430df5..7ec2bf1d9 100644 --- a/Config/DefaultFlow.ini +++ b/Config/DefaultFlow.ini @@ -1,3 +1,5 @@ [CoreRedirects] +ClassRedirects=(OldName="/Script/Flow.FlowNode_CustomEvent",NewName="/Script/Flow.FlowNode_CustomInput") +PropertyRedirects=(OldName="FlowAsset.CustomEvents",NewName="CustomInputs") ++StructRedirects=(OldName="/Script/Flow.FlowBreakpoint",NewName="/Script/Flow.FlowPinTrait") ++PropertyRedirects=(OldName="FlowPinTrait.bHasBreakpoint",NewName="bAllowed") \ No newline at end of file diff --git a/Source/Flow/Private/Nodes/FlowPin.cpp b/Source/Flow/Private/Nodes/FlowPin.cpp index d072bd140..368ffa312 100644 --- a/Source/Flow/Private/Nodes/FlowPin.cpp +++ b/Source/Flow/Private/Nodes/FlowPin.cpp @@ -2,8 +2,10 @@ #include "Nodes/FlowPin.h" -#if !UE_BUILD_SHIPPING +////////////////////////////////////////////////////////////////////////// +// Pin Record +#if !UE_BUILD_SHIPPING FString FPinRecord::NoActivations = TEXT("No activations"); FString FPinRecord::PinActivations = TEXT("Pin activations"); FString FPinRecord::ForcedActivation = TEXT(" (forced activation)"); @@ -31,5 +33,86 @@ FORCEINLINE FString FPinRecord::DoubleDigit(const int32 Number) { return Number > 9 ? FString::FromInt(Number) : TEXT("0") + FString::FromInt(Number); } - #endif + +////////////////////////////////////////////////////////////////////////// +// Pin Trait + +void FFlowPinTrait::AllowTrait() +{ + if (!bTraitAllowed) + { + bTraitAllowed = true; + bEnabled = true; + } +} + +void FFlowPinTrait::DisallowTrait() +{ + if (bTraitAllowed) + { + bTraitAllowed = false; + bEnabled = false; + } +} + +bool FFlowPinTrait::IsAllowed() const +{ + return bTraitAllowed; +} + +void FFlowPinTrait::EnableTrait() +{ + if (bTraitAllowed && !bEnabled) + { + bEnabled = true; + } +} + +void FFlowPinTrait::DisableTrait() +{ + if (bTraitAllowed && bEnabled) + { + bEnabled = false; + } +} + +void FFlowPinTrait::ToggleTrait() +{ + if (bTraitAllowed) + { + bTraitAllowed = false; + bEnabled = false; + } + else + { + bTraitAllowed = true; + bEnabled = true; + } +} + +bool FFlowPinTrait::CanEnable() const +{ + return bTraitAllowed && !bEnabled; +} + +bool FFlowPinTrait::IsEnabled() const +{ + return bTraitAllowed && bEnabled; +} + +void FFlowPinTrait::MarkAsHit() +{ + bHit = true; +} + +void FFlowPinTrait::ResetHit() +{ + bHit = false; +} + +bool FFlowPinTrait::IsHit() const +{ + return bHit; +} + diff --git a/Source/Flow/Public/Nodes/FlowPin.h b/Source/Flow/Public/Nodes/FlowPin.h index 1f5c2d82b..950e9373f 100644 --- a/Source/Flow/Public/Nodes/FlowPin.h +++ b/Source/Flow/Public/Nodes/FlowPin.h @@ -210,3 +210,47 @@ struct FLOW_API FPinRecord FORCEINLINE static FString DoubleDigit(const int32 Number); }; #endif + +// It can represent any trait added on the specific node instance, i.e. breakpoint +USTRUCT() +struct FLOW_API FFlowPinTrait +{ + GENERATED_USTRUCT_BODY() + +protected: + UPROPERTY() + uint8 bTraitAllowed : 1; + + uint8 bEnabled : 1; + uint8 bHit : 1; + +public: + FFlowPinTrait() + : bTraitAllowed(false) + , bEnabled(false) + , bHit(false) + { + }; + + explicit FFlowPinTrait(const bool bInitialState) + : bTraitAllowed(bInitialState) + , bEnabled(bInitialState) + , bHit(false) + { + }; + + void AllowTrait(); + void DisallowTrait(); + bool IsAllowed() const; + + void EnableTrait(); + void DisableTrait(); + void ToggleTrait(); + + bool CanEnable() const; + bool IsEnabled() const; + + void MarkAsHit(); + void ResetHit(); + bool IsHit() const; +}; diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index 7759ef52e..2ffc2022f 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -741,7 +741,7 @@ void SFlowGraphEditor::OnAddBreakpoint() const { for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - SelectedNode->NodeBreakpoint.AddBreakpoint(); + SelectedNode->NodeBreakpoint.AllowTrait(); } } @@ -751,8 +751,7 @@ void SFlowGraphEditor::OnAddPinBreakpoint() { if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) { - GraphNode->PinBreakpoints.Add(Pin, FFlowBreakpoint()); - GraphNode->PinBreakpoints[Pin].AddBreakpoint(); + GraphNode->PinBreakpoints.Add(Pin, FFlowPinTrait(true)); } } } @@ -761,7 +760,7 @@ bool SFlowGraphEditor::CanAddBreakpoint() const { for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - return !SelectedNode->NodeBreakpoint.HasBreakpoint(); + return !SelectedNode->NodeBreakpoint.IsAllowed(); } return false; @@ -773,7 +772,7 @@ bool SFlowGraphEditor::CanAddPinBreakpoint() { if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) { - return !GraphNode->PinBreakpoints.Contains(Pin) || !GraphNode->PinBreakpoints[Pin].HasBreakpoint(); + return !GraphNode->PinBreakpoints.Contains(Pin) || !GraphNode->PinBreakpoints[Pin].IsAllowed(); } } @@ -784,7 +783,7 @@ void SFlowGraphEditor::OnRemoveBreakpoint() const { for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - SelectedNode->NodeBreakpoint.RemoveBreakpoint(); + SelectedNode->NodeBreakpoint.DisallowTrait(); } } @@ -803,7 +802,7 @@ bool SFlowGraphEditor::CanRemoveBreakpoint() const { for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - return SelectedNode->NodeBreakpoint.HasBreakpoint(); + return SelectedNode->NodeBreakpoint.IsAllowed(); } return false; @@ -826,7 +825,7 @@ void SFlowGraphEditor::OnEnableBreakpoint() const { for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - SelectedNode->NodeBreakpoint.EnableBreakpoint(); + SelectedNode->NodeBreakpoint.EnableTrait(); } } @@ -836,7 +835,7 @@ void SFlowGraphEditor::OnEnablePinBreakpoint() { if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) { - GraphNode->PinBreakpoints[Pin].EnableBreakpoint(); + GraphNode->PinBreakpoints[Pin].EnableTrait(); } } } @@ -853,7 +852,7 @@ bool SFlowGraphEditor::CanEnableBreakpoint() for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - return SelectedNode->NodeBreakpoint.CanEnableBreakpoint(); + return SelectedNode->NodeBreakpoint.CanEnable(); } return false; @@ -865,7 +864,7 @@ bool SFlowGraphEditor::CanEnablePinBreakpoint() { if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) { - return GraphNode->PinBreakpoints.Contains(Pin) && GraphNode->PinBreakpoints[Pin].CanEnableBreakpoint(); + return GraphNode->PinBreakpoints.Contains(Pin) && GraphNode->PinBreakpoints[Pin].CanEnable(); } } @@ -876,7 +875,7 @@ void SFlowGraphEditor::OnDisableBreakpoint() const { for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - SelectedNode->NodeBreakpoint.DisableBreakpoint(); + SelectedNode->NodeBreakpoint.DisableTrait(); } } @@ -886,7 +885,7 @@ void SFlowGraphEditor::OnDisablePinBreakpoint() { if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) { - GraphNode->PinBreakpoints[Pin].DisableBreakpoint(); + GraphNode->PinBreakpoints[Pin].DisableTrait(); } } } @@ -895,7 +894,7 @@ bool SFlowGraphEditor::CanDisableBreakpoint() const { for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - return SelectedNode->NodeBreakpoint.IsBreakpointEnabled(); + return SelectedNode->NodeBreakpoint.IsEnabled(); } return false; @@ -907,7 +906,7 @@ bool SFlowGraphEditor::CanDisablePinBreakpoint() { if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) { - return GraphNode->PinBreakpoints.Contains(Pin) && GraphNode->PinBreakpoints[Pin].IsBreakpointEnabled(); + return GraphNode->PinBreakpoints.Contains(Pin) && GraphNode->PinBreakpoints[Pin].IsEnabled(); } } @@ -918,7 +917,7 @@ void SFlowGraphEditor::OnToggleBreakpoint() const { for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - SelectedNode->NodeBreakpoint.ToggleBreakpoint(); + SelectedNode->NodeBreakpoint.ToggleTrait(); } } @@ -928,8 +927,8 @@ void SFlowGraphEditor::OnTogglePinBreakpoint() { if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) { - GraphNode->PinBreakpoints.Add(Pin, FFlowBreakpoint()); - GraphNode->PinBreakpoints[Pin].ToggleBreakpoint(); + GraphNode->PinBreakpoints.Add(Pin, FFlowPinTrait()); + GraphNode->PinBreakpoints[Pin].ToggleTrait(); } } } diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 9f79b50b7..a6862c809 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -29,75 +29,6 @@ #define LOCTEXT_NAMESPACE "FlowGraphNode" -////////////////////////////////////////////////////////////////////////// -// Flow Breakpoint - -void FFlowBreakpoint::AddBreakpoint() -{ - if (!bHasBreakpoint) - { - bHasBreakpoint = true; - bBreakpointEnabled = true; - } -} - -void FFlowBreakpoint::RemoveBreakpoint() -{ - if (bHasBreakpoint) - { - bHasBreakpoint = false; - bBreakpointEnabled = false; - } -} - -bool FFlowBreakpoint::HasBreakpoint() const -{ - return bHasBreakpoint; -} - -void FFlowBreakpoint::EnableBreakpoint() -{ - if (bHasBreakpoint && !bBreakpointEnabled) - { - bBreakpointEnabled = true; - } -} - -bool FFlowBreakpoint::CanEnableBreakpoint() const -{ - return bHasBreakpoint && !bBreakpointEnabled; -} - -void FFlowBreakpoint::DisableBreakpoint() -{ - if (bHasBreakpoint && bBreakpointEnabled) - { - bBreakpointEnabled = false; - } -} - -bool FFlowBreakpoint::IsBreakpointEnabled() const -{ - return bHasBreakpoint && bBreakpointEnabled; -} - -void FFlowBreakpoint::ToggleBreakpoint() -{ - if (bHasBreakpoint) - { - bHasBreakpoint = false; - bBreakpointEnabled = false; - } - else - { - bHasBreakpoint = true; - bBreakpointEnabled = true; - } -} - -////////////////////////////////////////////////////////////////////////// -// Flow Graph Node - UFlowGraphNode::UFlowGraphNode(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , FlowNode(nullptr) @@ -450,7 +381,7 @@ void UFlowGraphNode::ReconstructSinglePin(UEdGraphPin* NewPin, UEdGraphPin* OldP NewPin->MovePersistentDataFromOldPin(*OldPin); // Update the in breakpoints as the old pin will be going the way of the dodo - for (TPair& PinBreakpoint : PinBreakpoints) + for (TPair& PinBreakpoint : PinBreakpoints) { if (PinBreakpoint.Key.Get() == OldPin) { @@ -1003,7 +934,7 @@ void UFlowGraphNode::OnInputTriggered(const int32 Index) { if (InputPins.IsValidIndex(Index) && PinBreakpoints.Contains(InputPins[Index])) { - PinBreakpoints[InputPins[Index]].bBreakpointHit = true; + PinBreakpoints[InputPins[Index]].MarkAsHit(); TryPausingSession(true); } @@ -1014,7 +945,7 @@ void UFlowGraphNode::OnOutputTriggered(const int32 Index) { if (OutputPins.IsValidIndex(Index) && PinBreakpoints.Contains(OutputPins[Index])) { - PinBreakpoints[OutputPins[Index]].bBreakpointHit = true; + PinBreakpoints[OutputPins[Index]].MarkAsHit(); TryPausingSession(true); } @@ -1024,9 +955,9 @@ void UFlowGraphNode::OnOutputTriggered(const int32 Index) void UFlowGraphNode::TryPausingSession(bool bPauseSession) { // Node breakpoints waits on any pin triggered - if (NodeBreakpoint.IsBreakpointEnabled()) + if (NodeBreakpoint.IsEnabled()) { - NodeBreakpoint.bBreakpointHit = true; + NodeBreakpoint.MarkAsHit(); bPauseSession = true; } @@ -1054,10 +985,10 @@ void UFlowGraphNode::ResetBreakpoints() FEditorDelegates::ResumePIE.RemoveAll(this); FEditorDelegates::EndPIE.RemoveAll(this); - NodeBreakpoint.bBreakpointHit = false; - for (TPair& PinBreakpoint : PinBreakpoints) + NodeBreakpoint.ResetHit(); + for (TPair& PinBreakpoint : PinBreakpoints) { - PinBreakpoint.Value.bBreakpointHit = false; + PinBreakpoint.Value.ResetHit(); } } diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp index c5508d109..bdf51c635 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp @@ -101,18 +101,18 @@ const FSlateBrush* SFlowGraphNode::GetShadowBrush(bool bSelected) const void SFlowGraphNode::GetOverlayBrushes(bool bSelected, const FVector2D WidgetSize, TArray& Brushes) const { // Node breakpoint - if (FlowGraphNode->NodeBreakpoint.bHasBreakpoint) + if (FlowGraphNode->NodeBreakpoint.IsAllowed()) { FOverlayBrushInfo NodeBrush; - if (FlowGraphNode->NodeBreakpoint.bBreakpointHit) + if (FlowGraphNode->NodeBreakpoint.IsHit()) { NodeBrush.Brush = FFlowEditorStyle::Get()->GetBrush(TEXT("FlowGraph.BreakpointHit")); NodeBrush.OverlayOffset.X = WidgetSize.X - 12.0f; } else { - NodeBrush.Brush = FFlowEditorStyle::Get()->GetBrush(FlowGraphNode->NodeBreakpoint.bBreakpointEnabled ? TEXT("FlowGraph.BreakpointEnabled") : TEXT("FlowGraph.BreakpointDisabled")); + NodeBrush.Brush = FFlowEditorStyle::Get()->GetBrush(FlowGraphNode->NodeBreakpoint.IsEnabled() ? TEXT("FlowGraph.BreakpointEnabled") : TEXT("FlowGraph.BreakpointDisabled")); NodeBrush.OverlayOffset.X = WidgetSize.X; } @@ -122,7 +122,7 @@ void SFlowGraphNode::GetOverlayBrushes(bool bSelected, const FVector2D WidgetSiz } // Pin breakpoints - for (const TPair& PinBreakpoint : FlowGraphNode->PinBreakpoints) + for (const TPair& PinBreakpoint : FlowGraphNode->PinBreakpoints) { if (PinBreakpoint.Key.Get()->Direction == EGPD_Input) { @@ -135,13 +135,13 @@ void SFlowGraphNode::GetOverlayBrushes(bool bSelected, const FVector2D WidgetSiz } } -void SFlowGraphNode::GetPinBrush(const bool bLeftSide, const float WidgetWidth, const int32 PinIndex, const FFlowBreakpoint& Breakpoint, TArray& Brushes) const +void SFlowGraphNode::GetPinBrush(const bool bLeftSide, const float WidgetWidth, const int32 PinIndex, const FFlowPinTrait& Breakpoint, TArray& Brushes) const { - if (Breakpoint.bHasBreakpoint) + if (Breakpoint.IsAllowed()) { FOverlayBrushInfo PinBrush; - if (Breakpoint.bBreakpointHit) + if (Breakpoint.IsHit()) { PinBrush.Brush = FFlowEditorStyle::Get()->GetBrush(TEXT("FlowGraph.PinBreakpointHit")); PinBrush.OverlayOffset.X = bLeftSide ? 0.0f : (WidgetWidth - 36.0f); @@ -149,7 +149,7 @@ void SFlowGraphNode::GetPinBrush(const bool bLeftSide, const float WidgetWidth, } else { - PinBrush.Brush = FFlowEditorStyle::Get()->GetBrush(Breakpoint.bBreakpointEnabled ? TEXT("FlowGraph.BreakpointEnabled") : TEXT("FlowGraph.BreakpointDisabled")); + PinBrush.Brush = FFlowEditorStyle::Get()->GetBrush(Breakpoint.IsEnabled() ? TEXT("FlowGraph.BreakpointEnabled") : TEXT("FlowGraph.BreakpointDisabled")); PinBrush.OverlayOffset.X = bLeftSide ? -24.0f : WidgetWidth; PinBrush.OverlayOffset.Y = 16.0f + PinIndex * 28.0f; } diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h index 35733f77d..8dba75821 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h @@ -11,40 +11,8 @@ #include "FlowGraphNode.generated.h" class UEdGraphSchema; - class UFlowNode; -USTRUCT() -struct FLOWEDITOR_API FFlowBreakpoint -{ - GENERATED_USTRUCT_BODY() - - UPROPERTY() - bool bHasBreakpoint; - - bool bBreakpointEnabled; - bool bBreakpointHit; - - FFlowBreakpoint() - { - bHasBreakpoint = false; - bBreakpointEnabled = false; - bBreakpointHit = false; - }; - - void AddBreakpoint(); - void RemoveBreakpoint(); - bool HasBreakpoint() const; - - void EnableBreakpoint(); - bool CanEnableBreakpoint() const; - - void DisableBreakpoint(); - bool IsBreakpointEnabled() const; - - void ToggleBreakpoint(); -}; - DECLARE_DELEGATE(FFlowGraphNodeEvent); /** @@ -106,7 +74,7 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode public: UPROPERTY() - FFlowBreakpoint NodeBreakpoint; + FFlowPinTrait NodeBreakpoint; // UEdGraphNode virtual bool CanCreateUnderSpecifiedSchema(const UEdGraphSchema* Schema) const override; @@ -179,7 +147,7 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode TArray OutputPins; UPROPERTY() - TMap PinBreakpoints; + TMap PinBreakpoints; void CreateInputPin(const FFlowPin& FlowPin, const int32 Index = INDEX_NONE); void CreateOutputPin(const FFlowPin& FlowPin, const int32 Index = INDEX_NONE); diff --git a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h index cc33a3e78..9ddda3fe3 100644 --- a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h @@ -33,7 +33,7 @@ class FLOWEDITOR_API SFlowGraphNode : public SGraphNode virtual void GetOverlayBrushes(bool bSelected, const FVector2D WidgetSize, TArray& Brushes) const override; // -- - virtual void GetPinBrush(const bool bLeftSide, const float WidgetWidth, const int32 PinIndex, const FFlowBreakpoint& Breakpoint, TArray& Brushes) const; + virtual void GetPinBrush(const bool bLeftSide, const float WidgetWidth, const int32 PinIndex, const FFlowPinTrait& Breakpoint, TArray& Brushes) const; // SGraphNode virtual void UpdateGraphNode() override; From 6e97c48e258e27d5a5b7c4b4ba5aa878ae5fb9cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Wed, 26 Jul 2023 13:14:07 +0200 Subject: [PATCH 194/485] prevent crash on missing or invalid asset --- Source/Flow/Private/FlowSubsystem.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index 404d19042..4f43e7250 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -110,7 +110,10 @@ UFlowAsset* UFlowSubsystem::CreateRootFlow(UObject* Owner, UFlowAsset* FlowAsset } UFlowAsset* NewFlow = CreateFlowInstance(Owner, FlowAsset); - RootInstances.Add(NewFlow, Owner); + if (NewFlow) + { + RootInstances.Add(NewFlow, Owner); + } return NewFlow; } @@ -162,15 +165,19 @@ UFlowAsset* UFlowSubsystem::CreateSubFlow(UFlowNode_SubGraph* SubGraphNode, cons { const TWeakObjectPtr Owner = SubGraphNode->GetFlowAsset() ? SubGraphNode->GetFlowAsset()->GetOwner() : nullptr; NewInstance = CreateFlowInstance(Owner, SubGraphNode->Asset, SavedInstanceName); - InstancedSubFlows.Add(SubGraphNode, NewInstance); - if (bPreloading) + if (NewInstance) { - NewInstance->PreloadNodes(); + InstancedSubFlows.Add(SubGraphNode, NewInstance); + + if (bPreloading) + { + NewInstance->PreloadNodes(); + } } } - if (!bPreloading) + if (InstancedSubFlows.Contains(SubGraphNode) && !bPreloading) { // get instanced asset from map - in case it was already instanced by calling CreateSubFlow() with bPreloading == true UFlowAsset* AssetInstance = InstancedSubFlows[SubGraphNode]; @@ -205,7 +212,7 @@ void UFlowSubsystem::RemoveSubFlow(UFlowNode_SubGraph* SubGraphNode, const EFlow UFlowAsset* UFlowSubsystem::CreateFlowInstance(const TWeakObjectPtr Owner, TSoftObjectPtr FlowAsset, FString NewInstanceName) { UFlowAsset* LoadedFlowAsset = FlowAsset.LoadSynchronous(); - if (!ensureAlways(LoadedFlowAsset)) + if (LoadedFlowAsset == nullptr) { return nullptr; } From d56fc9d9199b263fb65dac709264966a25a13e0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Wed, 26 Jul 2023 20:55:36 +0200 Subject: [PATCH 195/485] undef IMAGE_BRUSH_SVG --- Source/FlowEditor/Private/FlowEditorStyle.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Source/FlowEditor/Private/FlowEditorStyle.cpp b/Source/FlowEditor/Private/FlowEditorStyle.cpp index f39409450..23c067776 100644 --- a/Source/FlowEditor/Private/FlowEditorStyle.cpp +++ b/Source/FlowEditor/Private/FlowEditorStyle.cpp @@ -5,9 +5,9 @@ #include "Interfaces/IPluginManager.h" #include "Styling/SlateStyleRegistry.h" -#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 BOX_BRUSH( RelativePath, ... ) FSlateBoxBrush( StyleSet->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ ) +#define IMAGE_BRUSH( RelativePath, ... ) FSlateImageBrush( StyleSet->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ ) #define IMAGE_BRUSH_SVG( RelativePath, ... ) FSlateVectorImageBrush(StyleSet->RootToContentDir(RelativePath, TEXT(".svg")), __VA_ARGS__) TSharedPtr FFlowEditorStyle::StyleSet = nullptr; @@ -67,6 +67,7 @@ void FFlowEditorStyle::Shutdown() StyleSet.Reset(); } -#undef IMAGE_BRUSH -#undef BOX_BRUSH #undef BORDER_BRUSH +#undef BOX_BRUSH +#undef IMAGE_BRUSH +#undef IMAGE_BRUSH_SVG From 6c84991fadd626e858882121119a90daf1814225 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Wed, 26 Jul 2023 21:04:50 +0200 Subject: [PATCH 196/485] merge in UE 5.1 version of code --- Source/FlowEditor/Private/Asset/FlowDiffControl.cpp | 4 ++++ Source/FlowEditor/Private/MovieScene/FlowSection.cpp | 12 ++++++++++++ .../Private/MovieScene/FlowTrackEditor.cpp | 1 + Source/FlowEditor/Public/Asset/FlowDiffControl.h | 4 ++++ 4 files changed, 21 insertions(+) diff --git a/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp b/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp index 14727b70d..b0c7dc6c6 100644 --- a/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp +++ b/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp @@ -27,7 +27,11 @@ FFlowAssetDiffControl::FFlowAssetDiffControl(const UFlowAsset* InOldFlowAsset, c // TDetailsDiffControl::GenerateTreeEntries + "NoDifferences" entry + category label void FFlowAssetDiffControl::GenerateTreeEntries(TArray>& OutTreeEntries, TArray>& OutRealDifferences) { +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 2 + TDetailsDiffControl::GenerateTreeEntries(OutTreeEntries, OutRealDifferences); +#else FDetailsDiffControl::GenerateTreeEntries(OutTreeEntries, OutRealDifferences); +#endif const bool bHasDifferences = Children.Num() != 0; if (!bHasDifferences) diff --git a/Source/FlowEditor/Private/MovieScene/FlowSection.cpp b/Source/FlowEditor/Private/MovieScene/FlowSection.cpp index eb563e508..22473865e 100644 --- a/Source/FlowEditor/Private/MovieScene/FlowSection.cpp +++ b/Source/FlowEditor/Private/MovieScene/FlowSection.cpp @@ -64,7 +64,11 @@ void FFlowSectionBase::PaintEventName(FSequencerSectionPainter& Painter, int32 L FSlateDrawElement::MakeBox( Painter.DrawElements, LayerId + 1, +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 2 + Painter.SectionGeometry.ToPaintGeometry(BoxOffset, BoxSize), +#else Painter.SectionGeometry.ToPaintGeometry(BoxSize, FSlateLayoutTransform(1.0f, TransformPoint(1.0f, UE::Slate::CastToVector2f(BoxOffset)))), +#endif FAppStyle::GetBrush("WhiteBrush"), ESlateDrawEffect::None, FLinearColor::Black.CopyWithNewOpacity(0.5f) @@ -76,7 +80,11 @@ void FFlowSectionBase::PaintEventName(FSequencerSectionPainter& Painter, int32 L FSlateDrawElement::MakeText( Painter.DrawElements, LayerId + 2, +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 2 + Painter.SectionGeometry.ToPaintGeometry(BoxOffset + IconOffset, IconSize), +#else Painter.SectionGeometry.ToPaintGeometry(IconSize, FSlateLayoutTransform(1.0f, TransformPoint(1.0f, UE::Slate::CastToVector2f(BoxOffset + IconOffset)))), +#endif WarningString, FontAwesomeFont, Painter.bParentEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect, @@ -87,7 +95,11 @@ void FFlowSectionBase::PaintEventName(FSequencerSectionPainter& Painter, int32 L FSlateDrawElement::MakeText( Painter.DrawElements, LayerId + 2, +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 2 + Painter.SectionGeometry.ToPaintGeometry(BoxOffset + TextOffset, TextSize), +#else Painter.SectionGeometry.ToPaintGeometry(TextSize, FSlateLayoutTransform(1.0f, TransformPoint(1.0f, UE::Slate::CastToVector2f(BoxOffset + TextOffset)))), +#endif InEventString, SmallLayoutFont, Painter.bParentEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect, diff --git a/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp b/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp index 5472762c2..b4e5f3e12 100644 --- a/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp +++ b/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp @@ -169,6 +169,7 @@ void FFlowTrackEditor::HandleAddFlowTrackMenuEntryExecute(UClass* SectionType) c #else UMovieSceneFlowTrack* NewMasterTrack = FocusedMovieScene->AddTrack(); #endif + NewTracks.Add(NewMasterTrack); if (GetSequencer().IsValid()) { diff --git a/Source/FlowEditor/Public/Asset/FlowDiffControl.h b/Source/FlowEditor/Public/Asset/FlowDiffControl.h index 90a4dd203..b262f3fd6 100644 --- a/Source/FlowEditor/Public/Asset/FlowDiffControl.h +++ b/Source/FlowEditor/Public/Asset/FlowDiffControl.h @@ -15,7 +15,11 @@ class SFlowDiff; ///////////////////////////////////////////////////////////////////////////// /// FFlowAssetDiffControl +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 2 +class FLOWEDITOR_API FFlowAssetDiffControl : public TDetailsDiffControl +#else class FLOWEDITOR_API FFlowAssetDiffControl : public FDetailsDiffControl +#endif { public: FFlowAssetDiffControl(const UFlowAsset* InOldFlowAsset, const UFlowAsset* InNewFlowAsset, FOnDiffEntryFocused InSelectionCallback); From 3f119178b1c50c3195db17101eae064c017df5ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Wed, 26 Jul 2023 21:38:50 +0200 Subject: [PATCH 197/485] removed engine association --- Flow.uplugin | 1 - 1 file changed, 1 deletion(-) diff --git a/Flow.uplugin b/Flow.uplugin index 4879ba3f1..72c5ec9c9 100644 --- a/Flow.uplugin +++ b/Flow.uplugin @@ -8,7 +8,6 @@ "DocsURL" : "https://github.com/MothCocoon/FlowGraph/wiki", "MarketplaceURL" : "", "SupportURL": "https://discord.gg/zMtMQ2vUUa", - "EngineAssociation": "5.1", "EnabledByDefault" : true, "CanContainContent" : false, "IsBetaVersion" : false, From 69d54ed1ec12b0c60bf9ff9c379b396adbcaabae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Wed, 26 Jul 2023 22:07:52 +0200 Subject: [PATCH 198/485] version set to 1.5 --- Flow.uplugin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.uplugin b/Flow.uplugin index 72c5ec9c9..2e246c093 100644 --- a/Flow.uplugin +++ b/Flow.uplugin @@ -1,6 +1,6 @@ { "FileVersion" : 3, - "Version" : 1.4, + "Version" : 1.5, "FriendlyName" : "Flow", "Description" : "Design-agnostic node editor for scripting game’s flow.", "Category" : "Gameplay", From 7c4b0fe796ab80bdac84e4f7390a09e7fe04b760 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Wed, 26 Jul 2023 23:13:27 +0200 Subject: [PATCH 199/485] compilation fixes against UE 5.3 --- .../World/FlowNode_PlayLevelSequence.cpp | 1 - .../Private/Asset/FlowDiffControl.cpp | 5 +++- Source/FlowEditor/Private/Asset/SFlowDiff.cpp | 29 +++++++++++++++++++ .../Private/MovieScene/FlowTrackEditor.cpp | 1 - .../FlowEditor/Public/Asset/FlowDiffControl.h | 2 +- .../Public/MovieScene/FlowTrackEditor.h | 3 +- 6 files changed, 35 insertions(+), 6 deletions(-) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp index d02464a90..3ce251a0c 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp @@ -5,7 +5,6 @@ #include "FlowAsset.h" #include "FlowModule.h" #include "FlowSubsystem.h" -#include "Launch/Resources/Version.h" #include "LevelSequence/FlowLevelSequencePlayer.h" #include "MovieScene/MovieSceneFlowTrack.h" #include "MovieScene/MovieSceneFlowTriggerSection.h" diff --git a/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp b/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp index b0c7dc6c6..71f8a282b 100644 --- a/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp +++ b/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp @@ -7,7 +7,6 @@ #include "EdGraph/EdGraph.h" #include "GraphDiffControl.h" -#include "Launch/Resources/Version.h" #include "SBlueprintDiff.h" #define LOCTEXT_NAMESPACE "SFlowDiffControl" @@ -170,6 +169,7 @@ void FFlowGraphToDiff::BuildDiffSourceArray() FoundDiffs->Empty(); FGraphDiffControl::DiffGraphs(GraphOld, GraphNew, *FoundDiffs); +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 3 struct SortDiff { bool operator ()(const FDiffSingleResult& A, const FDiffSingleResult& B) const @@ -179,6 +179,9 @@ void FFlowGraphToDiff::BuildDiffSourceArray() }; Sort(FoundDiffs->GetData(), FoundDiffs->Num(), SortDiff()); +#else + Algo::SortBy(*FoundDiffs, &FDiffSingleResult::Diff); +#endif DiffListSource.Empty(); for (const FDiffSingleResult& Diff : *FoundDiffs) diff --git a/Source/FlowEditor/Private/Asset/SFlowDiff.cpp b/Source/FlowEditor/Private/Asset/SFlowDiff.cpp index 97499dbb4..6b0f49be7 100644 --- a/Source/FlowEditor/Private/Asset/SFlowDiff.cpp +++ b/Source/FlowEditor/Private/Asset/SFlowDiff.cpp @@ -18,6 +18,10 @@ #include "Subsystems/AssetEditorSubsystem.h" #include "Widgets/Layout/SSpacer.h" +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION > 2 +#include "SDetailsSplitter.h" +#endif + #define LOCTEXT_NAMESPACE "SFlowDiff" static const FName DetailsMode = FName(TEXT("DetailsMode")); @@ -695,8 +699,32 @@ SFlowDiff::FDiffControl SFlowDiff::GenerateDetailsPanel() const TSharedPtr NewDiffControl = MakeShared(PanelOld.FlowAsset, PanelNew.FlowAsset, FOnDiffEntryFocused::CreateRaw(this, &SFlowDiff::SetCurrentMode, DetailsMode)); NewDiffControl->GenerateTreeEntries(PrimaryDifferencesList, RealDifferences); +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 3 + const TSharedRef Splitter = SNew(SDetailsSplitter); + if (PanelOld.FlowAsset) + { + Splitter->AddSlot( + SDetailsSplitter::Slot() + .Value(0.5f) + .DetailsView(NewDiffControl->GetDetailsWidget(PanelOld.FlowAsset)) + .DifferencesWithRightPanel(NewDiffControl.ToSharedRef(), &FFlowAssetDiffControl::GetDifferencesWithRight, Cast(PanelOld.FlowAsset)) + ); + } + if (PanelNew.FlowAsset) + { + Splitter->AddSlot( + SDetailsSplitter::Slot() + .Value(0.5f) + .DetailsView(NewDiffControl->GetDetailsWidget(PanelNew.FlowAsset)) + .DifferencesWithRightPanel(NewDiffControl.ToSharedRef(), &FFlowAssetDiffControl::GetDifferencesWithRight, Cast(PanelOld.FlowAsset)) + ); + } +#endif + SFlowDiff::FDiffControl Ret; Ret.DiffControl = NewDiffControl; + +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 3 Ret.Widget = SNew(SSplitter) .PhysicalSplitterHandleSize(10.0f) + SSplitter::Slot() @@ -709,6 +737,7 @@ SFlowDiff::FDiffControl SFlowDiff::GenerateDetailsPanel() [ NewDiffControl->NewDetailsWidget() ]; +#endif return Ret; } diff --git a/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp b/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp index b4e5f3e12..1f65d18bf 100644 --- a/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp +++ b/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp @@ -9,7 +9,6 @@ #include "Framework/MultiBox/MultiBoxBuilder.h" #include "ISequencerSection.h" -#include "Launch/Resources/Version.h" #include "LevelSequence.h" #include "MovieSceneSequenceEditor.h" #include "Sections/MovieSceneEventSection.h" diff --git a/Source/FlowEditor/Public/Asset/FlowDiffControl.h b/Source/FlowEditor/Public/Asset/FlowDiffControl.h index b262f3fd6..c62d40633 100644 --- a/Source/FlowEditor/Public/Asset/FlowDiffControl.h +++ b/Source/FlowEditor/Public/Asset/FlowDiffControl.h @@ -4,7 +4,7 @@ #include "DiffResults.h" #include "IAssetTypeActions.h" -#include "Kismet/Private/DiffControl.h" +#include "Editor/Kismet/Private/DiffControl.h" struct FDiffResultItem; class UEdGraph; diff --git a/Source/FlowEditor/Public/MovieScene/FlowTrackEditor.h b/Source/FlowEditor/Public/MovieScene/FlowTrackEditor.h index c6e32d5a0..2316db26b 100644 --- a/Source/FlowEditor/Public/MovieScene/FlowTrackEditor.h +++ b/Source/FlowEditor/Public/MovieScene/FlowTrackEditor.h @@ -2,8 +2,7 @@ #pragma once -#include "CoreMinimal.h" -#include "Sequencer/Public/ISequencer.h" +#include "Editor/Sequencer/Public/ISequencer.h" #include "MovieSceneTrack.h" #include "ISequencerSection.h" #include "ISequencerTrackEditor.h" From bd2a46210bb0c6fcd0a6aa5f8327175c9d1abd47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Thu, 27 Jul 2023 20:35:57 +0200 Subject: [PATCH 200/485] fixed engine launch warnings --- Source/Flow/Public/FlowAsset.h | 2 +- Source/Flow/Public/FlowSettings.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 56a0a1548..57f6771fc 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -360,7 +360,7 @@ class FLOW_API UFlowAsset : public UObject // Expects to be owned (at runtime) by an object with this class (or one of its subclasses) // NOTE - If the class is an AActor, and the flow asset is owned by a component, // it will consider the component's owner for the AActor - UPROPERTY(EditAnywhere, Category = "Flow", meta = (MustImplement = "FlowOwnerInterface")) + UPROPERTY(EditAnywhere, Category = "Flow", meta = (MustImplement = "/Script.Flow.FlowOwnerInterface")) TSubclassOf ExpectedOwnerClass; ////////////////////////////////////////////////////////////////////////// diff --git a/Source/Flow/Public/FlowSettings.h b/Source/Flow/Public/FlowSettings.h index ddb491d13..c5f3150a2 100644 --- a/Source/Flow/Public/FlowSettings.h +++ b/Source/Flow/Public/FlowSettings.h @@ -41,7 +41,7 @@ class FLOW_API UFlowSettings : public UDeveloperSettings bool bUseAdaptiveNodeTitles; // Default class to use as a FlowAsset's "ExpectedOwnerClass" - UPROPERTY(EditAnywhere, Config, Category = "Nodes", meta = (MustImplement = "FlowOwnerInterface")) + UPROPERTY(EditAnywhere, Config, Category = "Nodes", meta = (MustImplement = "/Script.Flow.FlowOwnerInterface")) FSoftClassPath DefaultExpectedOwnerClass; public: From 98d8fdc415a2936795103086e9ebf6b17d05c54e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Fri, 1 Sep 2023 10:59:35 +0200 Subject: [PATCH 201/485] exposed GetDefaultEntryNode to blueprints --- Source/Flow/Public/FlowAsset.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 57f6771fc..b24dc0495 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -157,6 +157,7 @@ class FLOW_API UFlowAsset : public UObject return nullptr; } + UFUNCTION(BlueprintPure, Category = "FlowAsset") virtual UFlowNode* GetDefaultEntryNode() const; #if WITH_EDITOR From 6d00a2deb29444e3e93012a0c6b8e1bc83062668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Fri, 1 Sep 2023 11:01:10 +0200 Subject: [PATCH 202/485] Sequence node: bSavePinExecutionState is now True by default This addressed a common issue with handling SaveGame for a directed graph. --- .../Route/FlowNode_ExecutionSequence.cpp | 19 ++++++++++--------- .../Nodes/Route/FlowNode_ExecutionSequence.h | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp index c76a7533e..55402fe21 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp @@ -4,6 +4,7 @@ UFlowNode_ExecutionSequence::UFlowNode_ExecutionSequence(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) + , bSavePinExecutionState(true) { #if WITH_EDITOR Category = TEXT("Route"); @@ -16,12 +17,12 @@ UFlowNode_ExecutionSequence::UFlowNode_ExecutionSequence(const FObjectInitialize void UFlowNode_ExecutionSequence::ExecuteInput(const FName& PinName) { - if(bSavePinExecutionState) + if (bSavePinExecutionState) { ExecuteNewConnections(); return; } - + for (const FFlowPin& Output : OutputPins) { TriggerOutput(Output.PinName, false); @@ -33,11 +34,11 @@ void UFlowNode_ExecutionSequence::ExecuteInput(const FName& PinName) #if WITH_EDITOR FString UFlowNode_ExecutionSequence::GetNodeDescription() const { - if(bSavePinExecutionState) + if (bSavePinExecutionState) { return TEXT("Saves pin execution state"); } - + return Super::GetNodeDescription(); } #endif @@ -52,12 +53,12 @@ void UFlowNode_ExecutionSequence::ExecuteNewConnections() for (const FFlowPin& Output : OutputPins) { const FConnectedPin& Connection = GetConnection(Output.PinName); - if(ExecutedConnections.Contains(Connection.NodeGuid)) + if (!ExecutedConnections.Contains(Connection.NodeGuid)) { - continue; + ExecutedConnections.Emplace(Connection.NodeGuid); + TriggerOutput(Output.PinName, false); } - - TriggerOutput(Output.PinName, false); - ExecutedConnections.Emplace(Connection.NodeGuid); } + + Finish(); } diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionSequence.h b/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionSequence.h index ef7482ca2..ea7f4f5a8 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionSequence.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionSequence.h @@ -37,7 +37,7 @@ class FLOW_API UFlowNode_ExecutionSequence final : public UFlowNode * graph after release. */ UPROPERTY(EditAnywhere, Category = "Sequence") - bool bSavePinExecutionState = false; + bool bSavePinExecutionState; UPROPERTY(SaveGame) TSet ExecutedConnections; From 1b055213002446f9f445562e8367aeb584d5c0ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Mon, 11 Sep 2023 00:18:11 +0200 Subject: [PATCH 203/485] version set to 1.6 --- Flow.uplugin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.uplugin b/Flow.uplugin index 2e246c093..5535fef5d 100644 --- a/Flow.uplugin +++ b/Flow.uplugin @@ -1,6 +1,6 @@ { "FileVersion" : 3, - "Version" : 1.5, + "Version" : 1.6, "FriendlyName" : "Flow", "Description" : "Design-agnostic node editor for scripting game’s flow.", "Category" : "Gameplay", From 1c09328f24206787efac515864da5021dcf289eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Thu, 14 Sep 2023 12:06:20 +0200 Subject: [PATCH 204/485] exposed 2 methods to other C++ classes --- Source/Flow/Public/Nodes/FlowNode.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index ef47720b5..731873e1d 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -141,6 +141,7 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte // Gets the Owning Object for this Node's RootFlow UObject* TryGetRootFlowObjectOwner() const; +public: virtual bool CanFinishGraph() const { return false; } protected: @@ -325,10 +326,12 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (HidePin = "ActivationType")) void TriggerOutputPin(const FFlowOutputPinHandle Pin, const bool bFinish = false, const EFlowPinActivationType ActivationType = EFlowPinActivationType::Default); +public: // Finish execution of node, it will call Cleanup UFUNCTION(BlueprintCallable, Category = "FlowNode") void Finish(); +protected: void Deactivate(); // Method called after node finished the work From d49397142549cefc90450f1659f604e51719f0fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Thu, 14 Sep 2023 12:23:10 +0200 Subject: [PATCH 205/485] Change the C++ protection level of remaining runtime methods from private to protected --- Source/Flow/Public/FlowAsset.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index b24dc0495..07aee9876 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -257,7 +257,7 @@ class FLOW_API UFlowAsset : public UObject ////////////////////////////////////////////////////////////////////////// // Executing asset instance -private: +protected: UPROPERTY() UFlowAsset* TemplateAsset; @@ -374,10 +374,9 @@ class FLOW_API UFlowAsset : public UObject UFUNCTION(BlueprintCallable, Category = "SaveGame") void LoadInstance(const FFlowAssetSaveData& AssetRecord); -private: - void OnActivationStateLoaded(UFlowNode* Node); - protected: + virtual void OnActivationStateLoaded(UFlowNode* Node); + UFUNCTION(BlueprintNativeEvent, Category = "SaveGame") void OnSave(); From 0148aee6867f5c8629009080a7b51cb54f4050e6 Mon Sep 17 00:00:00 2001 From: Mark Price Date: Tue, 26 Sep 2023 11:37:18 -0700 Subject: [PATCH 206/485] Update SFlowDiff.cpp (#174) Fixed crash when doing a diff vs Perforce. The crash occurred inside of the SetContent method called from SFlowDiff::SetCurrentMode. When the Widget is not set to SNullWidget::NullWidget it assumes that the widget exists and attempts to dereference it, but in the former code the Widget was unset (nullptr) causing the crash. --- Source/FlowEditor/Private/Asset/SFlowDiff.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/FlowEditor/Private/Asset/SFlowDiff.cpp b/Source/FlowEditor/Private/Asset/SFlowDiff.cpp index 6b0f49be7..3790364ec 100644 --- a/Source/FlowEditor/Private/Asset/SFlowDiff.cpp +++ b/Source/FlowEditor/Private/Asset/SFlowDiff.cpp @@ -722,6 +722,7 @@ SFlowDiff::FDiffControl SFlowDiff::GenerateDetailsPanel() #endif SFlowDiff::FDiffControl Ret; + Ret.Widget = SNullWidget::NullWidget; Ret.DiffControl = NewDiffControl; #if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 3 From 1908edbf455f2cbf263dfe5c210b939e59c12fb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Tue, 26 Sep 2023 21:32:55 +0200 Subject: [PATCH 207/485] clean execution information once Sequence node finished the work --- .../Route/FlowNode_ExecutionSequence.cpp | 40 +++++++++++-------- Source/Flow/Public/Nodes/FlowNode.h | 2 +- .../Nodes/Route/FlowNode_ExecutionSequence.h | 34 ++++++++-------- 3 files changed, 42 insertions(+), 34 deletions(-) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp index 55402fe21..8c97852ec 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp @@ -20,34 +20,28 @@ void UFlowNode_ExecutionSequence::ExecuteInput(const FName& PinName) if (bSavePinExecutionState) { ExecuteNewConnections(); - return; } - - for (const FFlowPin& Output : OutputPins) + else { - TriggerOutput(Output.PinName, false); - } - - Finish(); -} + for (const FFlowPin& Output : OutputPins) + { + TriggerOutput(Output.PinName, false); + } -#if WITH_EDITOR -FString UFlowNode_ExecutionSequence::GetNodeDescription() const -{ - if (bSavePinExecutionState) - { - return TEXT("Saves pin execution state"); + Finish(); } - - return Super::GetNodeDescription(); } -#endif void UFlowNode_ExecutionSequence::OnLoad_Implementation() { ExecuteNewConnections(); } +void UFlowNode_ExecutionSequence::Cleanup() +{ + ExecutedConnections.Empty(); +} + void UFlowNode_ExecutionSequence::ExecuteNewConnections() { for (const FFlowPin& Output : OutputPins) @@ -62,3 +56,15 @@ void UFlowNode_ExecutionSequence::ExecuteNewConnections() Finish(); } + +#if WITH_EDITOR +FString UFlowNode_ExecutionSequence::GetNodeDescription() const +{ + if (bSavePinExecutionState) + { + return TEXT("Saves pin execution state"); + } + + return Super::GetNodeDescription(); +} +#endif diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 731873e1d..c2d787a16 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -326,7 +326,7 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (HidePin = "ActivationType")) void TriggerOutputPin(const FFlowOutputPinHandle Pin, const bool bFinish = false, const EFlowPinActivationType ActivationType = EFlowPinActivationType::Default); -public: +public: // Finish execution of node, it will call Cleanup UFUNCTION(BlueprintCallable, Category = "FlowNode") void Finish(); diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionSequence.h b/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionSequence.h index ea7f4f5a8..73c081755 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionSequence.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionSequence.h @@ -13,34 +13,36 @@ class FLOW_API UFlowNode_ExecutionSequence final : public UFlowNode { GENERATED_UCLASS_BODY() -#if WITH_EDITOR - virtual bool CanUserAddOutput() const override { return true; } - - virtual FString GetNodeDescription() const override; -#endif - - virtual void OnLoad_Implementation() override; - protected: - virtual void ExecuteInput(const FName& PinName) override; - /** - * If enabled and the flowgraph is saved during gameplay, this node + * If enabled and the graph is saved during gameplay, this node * tracks and saves which pins it has executed. * * If you add new connections or replace old connections with with * different nodes, this node will detect the changes. If during gameplay * you load an old save game which had different connections, this node * will automatically execute the updated connections you created. - * - * This is useful if you want the ability to add new parts to your - * graph after release. */ UPROPERTY(EditAnywhere, Category = "Sequence") bool bSavePinExecutionState; - + UPROPERTY(SaveGame) TSet ExecutedConnections; - + +public: +#if WITH_EDITOR + virtual bool CanUserAddOutput() const override { return true; } +#endif + +protected: + virtual void ExecuteInput(const FName& PinName) override; + virtual void OnLoad_Implementation() override; + virtual void Cleanup() override; + void ExecuteNewConnections(); + +#if WITH_EDITOR +public: + virtual FString GetNodeDescription() const override; +#endif }; From 420694b99557138425aded2cbdf7128f36dc92f6 Mon Sep 17 00:00:00 2001 From: Mark Price Date: Sun, 12 Nov 2023 03:56:55 -0800 Subject: [PATCH 208/485] Non-unity mode build fixes (#178) * Update SFlowDiff.cpp Fixed crash when doing a diff vs Perforce. The crash occurred inside of the SetContent method called from SFlowDiff::SetCurrentMode. When the Widget is not set to SNullWidget::NullWidget it assumes that the widget exists and attempts to dereference it, but in the former code the Widget was unset (nullptr) causing the crash. * Non-unity mode build fixes --- Source/Flow/Private/Nodes/FlowNode.cpp | 1 + Source/Flow/Private/Nodes/FlowPin.cpp | 1 + Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp | 1 + .../Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp | 1 + Source/FlowEditor/Private/Asset/FlowAssetIndexer.cpp | 1 + Source/FlowEditor/Private/Asset/FlowMessageLogListing.cpp | 1 + Source/FlowEditor/Private/Asset/SAssetRevisionMenu.cpp | 4 ++++ Source/FlowEditor/Private/Asset/SFlowDiff.cpp | 2 ++ Source/FlowEditor/Private/Graph/FlowGraph.cpp | 1 + Source/FlowEditor/Private/MovieScene/FlowSection.cpp | 1 + Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp | 2 ++ Source/FlowEditor/Private/Pins/SFlowInputPinHandle.cpp | 3 +++ Source/FlowEditor/Private/Pins/SFlowOutputPinHandle.cpp | 3 +++ Source/FlowEditor/Private/Pins/SFlowPinHandle.cpp | 2 ++ Source/FlowEditor/Public/Asset/FlowDiffControl.h | 1 + Source/FlowEditor/Public/Asset/SAssetRevisionMenu.h | 1 + Source/FlowEditor/Public/Asset/SFlowDiff.h | 1 + Source/FlowEditor/Public/FlowEditorModule.h | 1 + 18 files changed, 28 insertions(+) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 6ab023a92..515f503fd 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -13,6 +13,7 @@ #if WITH_EDITOR #include "Editor.h" #endif +#include "Engine/Blueprint.h" #include "Engine/Engine.h" #include "Engine/ViewportStatsSubsystem.h" #include "Engine/World.h" diff --git a/Source/Flow/Private/Nodes/FlowPin.cpp b/Source/Flow/Private/Nodes/FlowPin.cpp index 368ffa312..8b28f6c2c 100644 --- a/Source/Flow/Private/Nodes/FlowPin.cpp +++ b/Source/Flow/Private/Nodes/FlowPin.cpp @@ -1,6 +1,7 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "Nodes/FlowPin.h" +#include "Misc/DateTime.h" ////////////////////////////////////////////////////////////////////////// // Pin Record diff --git a/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp b/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp index dd3303c7b..22bdeb206 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp @@ -3,6 +3,7 @@ #include "Nodes/World/FlowNode_NotifyActor.h" #include "FlowComponent.h" #include "FlowSubsystem.h" +#include "Engine/GameInstance.h" #include "Engine/World.h" diff --git a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp index 3ce251a0c..5edd3aee4 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp @@ -11,6 +11,7 @@ #include "LevelSequence.h" #include "LevelSequenceActor.h" +#include "Runtime/Launch/Resources/Version.h" #include "VisualLogger/VisualLogger.h" FFlowNodeLevelSequenceEvent UFlowNode_PlayLevelSequence::OnPlaybackStarted; diff --git a/Source/FlowEditor/Private/Asset/FlowAssetIndexer.cpp b/Source/FlowEditor/Private/Asset/FlowAssetIndexer.cpp index 5bf0bf94f..8c22f6ded 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetIndexer.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetIndexer.cpp @@ -7,6 +7,7 @@ #include "Graph/Nodes/FlowGraphNode_Reroute.h" +#include "EdGraph/EdGraph.h" #include "EdGraph/EdGraphPin.h" #include "EdGraphNode_Comment.h" #include "Internationalization/Text.h" diff --git a/Source/FlowEditor/Private/Asset/FlowMessageLogListing.cpp b/Source/FlowEditor/Private/Asset/FlowMessageLogListing.cpp index 9bd7a6f73..3ca8787e2 100644 --- a/Source/FlowEditor/Private/Asset/FlowMessageLogListing.cpp +++ b/Source/FlowEditor/Private/Asset/FlowMessageLogListing.cpp @@ -3,6 +3,7 @@ #include "Asset/FlowMessageLogListing.h" #include "MessageLogModule.h" +#include "Modules/ModuleManager.h" #define LOCTEXT_NAMESPACE "FlowMessageLogListing" diff --git a/Source/FlowEditor/Private/Asset/SAssetRevisionMenu.cpp b/Source/FlowEditor/Private/Asset/SAssetRevisionMenu.cpp index e6b17761f..be23d217e 100644 --- a/Source/FlowEditor/Private/Asset/SAssetRevisionMenu.cpp +++ b/Source/FlowEditor/Private/Asset/SAssetRevisionMenu.cpp @@ -6,7 +6,11 @@ #include "ISourceControlModule.h" #include "ISourceControlRevision.h" #include "SourceControlOperations.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" #include "Widgets/Images/SThrobber.h" +#include "Widgets/Input/SButton.h" +#include "Widgets/Layout/SBorder.h" +#include "Widgets/Text/STextBlock.h" #define LOCTEXT_NAMESPACE "SFlowRevisionMenu" diff --git a/Source/FlowEditor/Private/Asset/SFlowDiff.cpp b/Source/FlowEditor/Private/Asset/SFlowDiff.cpp index 3790364ec..cac426b4e 100644 --- a/Source/FlowEditor/Private/Asset/SFlowDiff.cpp +++ b/Source/FlowEditor/Private/Asset/SFlowDiff.cpp @@ -7,12 +7,14 @@ #include "EdGraphUtilities.h" #include "Editor.h" +#include "Framework/Application/SlateApplication.h" #include "Framework/Commands/GenericCommands.h" #include "Framework/MultiBox/MultiBoxBuilder.h" #include "Framework/MultiBox/MultiBoxDefs.h" #include "GraphDiffControl.h" #include "HAL/PlatformApplicationMisc.h" #include "Internationalization/Text.h" +#include "PropertyEditorModule.h" #include "SBlueprintDiff.h" #include "SlateOptMacros.h" #include "Subsystems/AssetEditorSubsystem.h" diff --git a/Source/FlowEditor/Private/Graph/FlowGraph.cpp b/Source/FlowEditor/Private/Graph/FlowGraph.cpp index b83c18312..e51ba7abb 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraph.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraph.cpp @@ -7,6 +7,7 @@ #include "Nodes/FlowNode.h" +#include "Editor.h" #include "Kismet2/BlueprintEditorUtils.h" void FFlowGraphInterface::OnInputTriggered(UEdGraphNode* GraphNode, const int32 Index) const diff --git a/Source/FlowEditor/Private/MovieScene/FlowSection.cpp b/Source/FlowEditor/Private/MovieScene/FlowSection.cpp index 22473865e..123856413 100644 --- a/Source/FlowEditor/Private/MovieScene/FlowSection.cpp +++ b/Source/FlowEditor/Private/MovieScene/FlowSection.cpp @@ -9,6 +9,7 @@ #include "Framework/Application/SlateApplication.h" #include "MovieSceneTrack.h" #include "Rendering/DrawElements.h" +#include "Runtime/Launch/Resources/Version.h" #include "Sections/MovieSceneEventSection.h" #include "SequencerSectionPainter.h" #include "SequencerTimeSliderController.h" diff --git a/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp b/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp index 1f65d18bf..f80304db0 100644 --- a/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp +++ b/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp @@ -7,10 +7,12 @@ #include "MovieScene/MovieSceneFlowTrack.h" #include "MovieScene/MovieSceneFlowTriggerSection.h" +#include "Components/HorizontalBox.h" #include "Framework/MultiBox/MultiBoxBuilder.h" #include "ISequencerSection.h" #include "LevelSequence.h" #include "MovieSceneSequenceEditor.h" +#include "Runtime/Launch/Resources/Version.h" #include "Sections/MovieSceneEventSection.h" #include "SequencerUtilities.h" diff --git a/Source/FlowEditor/Private/Pins/SFlowInputPinHandle.cpp b/Source/FlowEditor/Private/Pins/SFlowInputPinHandle.cpp index c8e41ae44..aca04ac50 100644 --- a/Source/FlowEditor/Private/Pins/SFlowInputPinHandle.cpp +++ b/Source/FlowEditor/Private/Pins/SFlowInputPinHandle.cpp @@ -3,6 +3,9 @@ #include "Pins/SFlowInputPinHandle.h" #include "Nodes/FlowNode.h" +#include "EdGraphSchema_K2.h" +#include "Engine/Blueprint.h" + void SFlowInputPinHandle::RefreshNameList() { PinNames.Empty(); diff --git a/Source/FlowEditor/Private/Pins/SFlowOutputPinHandle.cpp b/Source/FlowEditor/Private/Pins/SFlowOutputPinHandle.cpp index 5fca65021..984c58f76 100644 --- a/Source/FlowEditor/Private/Pins/SFlowOutputPinHandle.cpp +++ b/Source/FlowEditor/Private/Pins/SFlowOutputPinHandle.cpp @@ -3,6 +3,9 @@ #include "Pins/SFlowOutputPinHandle.h" #include "Nodes/FlowNode.h" +#include "EdGraphSchema_K2.h" +#include "Engine/Blueprint.h" + void SFlowOutputPinHandle::RefreshNameList() { PinNames.Empty(); diff --git a/Source/FlowEditor/Private/Pins/SFlowPinHandle.cpp b/Source/FlowEditor/Private/Pins/SFlowPinHandle.cpp index 1a85544c9..eb45360b9 100644 --- a/Source/FlowEditor/Private/Pins/SFlowPinHandle.cpp +++ b/Source/FlowEditor/Private/Pins/SFlowPinHandle.cpp @@ -2,6 +2,8 @@ #include "Pins/SFlowPinHandle.h" +#include "ScopedTransaction.h" + #define LOCTEXT_NAMESPACE "SFlowPinHandle" SFlowPinHandle::SFlowPinHandle() diff --git a/Source/FlowEditor/Public/Asset/FlowDiffControl.h b/Source/FlowEditor/Public/Asset/FlowDiffControl.h index c62d40633..05fecb39e 100644 --- a/Source/FlowEditor/Public/Asset/FlowDiffControl.h +++ b/Source/FlowEditor/Public/Asset/FlowDiffControl.h @@ -5,6 +5,7 @@ #include "DiffResults.h" #include "IAssetTypeActions.h" #include "Editor/Kismet/Private/DiffControl.h" +#include "Runtime/Launch/Resources/Version.h" struct FDiffResultItem; class UEdGraph; diff --git a/Source/FlowEditor/Public/Asset/SAssetRevisionMenu.h b/Source/FlowEditor/Public/Asset/SAssetRevisionMenu.h index 4bd540150..2ee609388 100644 --- a/Source/FlowEditor/Public/Asset/SAssetRevisionMenu.h +++ b/Source/FlowEditor/Public/Asset/SAssetRevisionMenu.h @@ -2,6 +2,7 @@ #pragma once +#include "Components/VerticalBox.h" #include "ISourceControlProvider.h" #include "Widgets/SCompoundWidget.h" diff --git a/Source/FlowEditor/Public/Asset/SFlowDiff.h b/Source/FlowEditor/Public/Asset/SFlowDiff.h index b9ac9e6d9..4999cb14a 100644 --- a/Source/FlowEditor/Public/Asset/SFlowDiff.h +++ b/Source/FlowEditor/Public/Asset/SFlowDiff.h @@ -2,6 +2,7 @@ #pragma once +#include "Graph/FlowGraphConnectionDrawingPolicy.h" #include "IDetailsView.h" #include "DiffResults.h" #include "SDetailsDiff.h" diff --git a/Source/FlowEditor/Public/FlowEditorModule.h b/Source/FlowEditor/Public/FlowEditorModule.h index 81839af3f..d096ff0df 100644 --- a/Source/FlowEditor/Public/FlowEditorModule.h +++ b/Source/FlowEditor/Public/FlowEditorModule.h @@ -9,6 +9,7 @@ #include "Toolkits/IToolkit.h" class FSlateStyleSet; +class FToolBarBuilder; struct FGraphPanelPinConnectionFactory; class FFlowAssetEditor; From 6f510d8df8778780d63ce9c75bf94545b0e4a926 Mon Sep 17 00:00:00 2001 From: Patryk Wysocki Date: Sun, 12 Nov 2023 12:57:39 +0100 Subject: [PATCH 209/485] Exposing match type on NotifyActor node (#175) * Exposing match type on NotifyActor node * Adding missing constructor --- Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp | 3 ++- Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp b/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp index 22bdeb206..7e3747ac9 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp @@ -9,6 +9,7 @@ UFlowNode_NotifyActor::UFlowNode_NotifyActor(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) + , MatchType(EGameplayContainerMatchType::All) , bExactMatch(true) , NetMode(EFlowNetMode::Authority) { @@ -21,7 +22,7 @@ void UFlowNode_NotifyActor::ExecuteInput(const FName& PinName) { if (const UFlowSubsystem* FlowSubsystem = GetWorld()->GetGameInstance()->GetSubsystem()) { - for (const TWeakObjectPtr& Component : FlowSubsystem->GetComponents(IdentityTags, EGameplayContainerMatchType::Any, bExactMatch)) + for (const TWeakObjectPtr& Component : FlowSubsystem->GetComponents(IdentityTags, MatchType, bExactMatch)) { Component->NotifyFromGraph(NotifyTags, NetMode); } diff --git a/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h b/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h index fa7b712b1..6dca801f0 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h @@ -18,7 +18,8 @@ class FLOW_API UFlowNode_NotifyActor : public UFlowNode protected: UPROPERTY(EditAnywhere, Category = "Notify") FGameplayTagContainer IdentityTags; - + UPROPERTY(EditAnywhere, Category = "Notify") + EGameplayContainerMatchType MatchType; /** * If true, identity tags must be an exact match. * Be careful, setting this to false may be very expensive, as the From e9d013adb2f487ddfc364ca34c3d77d14cc5dc2d Mon Sep 17 00:00:00 2001 From: Dylan Dumesnil Date: Sun, 12 Nov 2023 03:57:54 -0800 Subject: [PATCH 210/485] Add EFlowNodeDoubleClickTarget::PrimaryAssetOrNodeDefinition to support opening the Asset and falling back to opening the Node Class if the node doesn't have an asset. (#177) * Add EFlowNodeDoubleClickTarget::PrimaryAssetOrNodeDefinition to support opening the Asset and falling back to opening the Node Class if the node doesn't have an asset. * Use spaces to align UMETA for EFlowNodeDoubleClickTarget so it aligns with any Tab Size --- Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp | 4 ++++ Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp | 2 +- Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h | 5 +++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index 2ffc2022f..bc838a926 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -626,6 +626,10 @@ void SFlowGraphEditor::OnNodeDoubleClicked(class UEdGraphNode* Node) const } } } + else if (UFlowGraphEditorSettings::Get()->NodeDoubleClickTarget == EFlowNodeDoubleClickTarget::PrimaryAssetOrNodeDefinition) + { + Node->JumpToDefinition(); + } } } } diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp index 0ecffa9fc..de7461d6e 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp @@ -4,7 +4,7 @@ UFlowGraphEditorSettings::UFlowGraphEditorSettings(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) - , NodeDoubleClickTarget(EFlowNodeDoubleClickTarget::PrimaryAsset) + , NodeDoubleClickTarget(EFlowNodeDoubleClickTarget::PrimaryAssetOrNodeDefinition) , bShowNodeClass(false) , bShowNodeDescriptionWhilePlaying(true) , bEnforceFriendlyPinNames(false) diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h index 51d00ef27..a15fcbded 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h @@ -8,8 +8,9 @@ UENUM() enum class EFlowNodeDoubleClickTarget : uint8 { - NodeDefinition UMETA(Tooltip = "Open node class: either blueprint or C++ class"), - PrimaryAsset UMETA(Tooltip = "Open asset defined as primary asset, i.e. Dialogue asset for PlayDialogue node") + NodeDefinition UMETA(Tooltip = "Open node class: either blueprint or C++ class"), + PrimaryAsset UMETA(Tooltip = "Open asset defined as primary asset, i.e. Dialogue asset for PlayDialogue node"), + PrimaryAssetOrNodeDefinition UMETA(Tooltip = "First try opening the asset then if there is none, open the node class") }; /** From f403b3fa9349e7c619749810fd3a334312cbb40f Mon Sep 17 00:00:00 2001 From: Dylan Dumesnil Date: Sun, 12 Nov 2023 03:58:16 -0800 Subject: [PATCH 211/485] Fix incorrect MustImplement paths (#176) --- Source/Flow/Public/FlowAsset.h | 2 +- Source/Flow/Public/FlowSettings.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 07aee9876..c5fee3f9c 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -361,7 +361,7 @@ class FLOW_API UFlowAsset : public UObject // Expects to be owned (at runtime) by an object with this class (or one of its subclasses) // NOTE - If the class is an AActor, and the flow asset is owned by a component, // it will consider the component's owner for the AActor - UPROPERTY(EditAnywhere, Category = "Flow", meta = (MustImplement = "/Script.Flow.FlowOwnerInterface")) + UPROPERTY(EditAnywhere, Category = "Flow", meta = (MustImplement = "/Script/Flow.FlowOwnerInterface")) TSubclassOf ExpectedOwnerClass; ////////////////////////////////////////////////////////////////////////// diff --git a/Source/Flow/Public/FlowSettings.h b/Source/Flow/Public/FlowSettings.h index c5f3150a2..7760ce6f2 100644 --- a/Source/Flow/Public/FlowSettings.h +++ b/Source/Flow/Public/FlowSettings.h @@ -41,7 +41,7 @@ class FLOW_API UFlowSettings : public UDeveloperSettings bool bUseAdaptiveNodeTitles; // Default class to use as a FlowAsset's "ExpectedOwnerClass" - UPROPERTY(EditAnywhere, Config, Category = "Nodes", meta = (MustImplement = "/Script.Flow.FlowOwnerInterface")) + UPROPERTY(EditAnywhere, Config, Category = "Nodes", meta = (MustImplement = "/Script/Flow.FlowOwnerInterface")) FSoftClassPath DefaultExpectedOwnerClass; public: From b02847f62ea8c1d8ea56f227078f9c2b59d09084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 12 Nov 2023 13:11:29 +0100 Subject: [PATCH 212/485] cosmetic tweaks of pull requests --- Source/Flow/Private/Nodes/FlowNode.cpp | 1 + Source/Flow/Private/Nodes/FlowPin.cpp | 1 + Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp | 2 +- Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h | 1 + Source/FlowEditor/Private/Asset/SAssetRevisionMenu.cpp | 2 +- 5 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 515f503fd..4fa5b447c 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -13,6 +13,7 @@ #if WITH_EDITOR #include "Editor.h" #endif + #include "Engine/Blueprint.h" #include "Engine/Engine.h" #include "Engine/ViewportStatsSubsystem.h" diff --git a/Source/Flow/Private/Nodes/FlowPin.cpp b/Source/Flow/Private/Nodes/FlowPin.cpp index 8b28f6c2c..417ccf133 100644 --- a/Source/Flow/Private/Nodes/FlowPin.cpp +++ b/Source/Flow/Private/Nodes/FlowPin.cpp @@ -1,6 +1,7 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "Nodes/FlowPin.h" + #include "Misc/DateTime.h" ////////////////////////////////////////////////////////////////////////// diff --git a/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp b/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp index 7e3747ac9..ab468e270 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp @@ -3,8 +3,8 @@ #include "Nodes/World/FlowNode_NotifyActor.h" #include "FlowComponent.h" #include "FlowSubsystem.h" -#include "Engine/GameInstance.h" +#include "Engine/GameInstance.h" #include "Engine/World.h" UFlowNode_NotifyActor::UFlowNode_NotifyActor(const FObjectInitializer& ObjectInitializer) diff --git a/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h b/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h index 6dca801f0..aa250b4bd 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h @@ -18,6 +18,7 @@ class FLOW_API UFlowNode_NotifyActor : public UFlowNode protected: UPROPERTY(EditAnywhere, Category = "Notify") FGameplayTagContainer IdentityTags; + UPROPERTY(EditAnywhere, Category = "Notify") EGameplayContainerMatchType MatchType; /** diff --git a/Source/FlowEditor/Private/Asset/SAssetRevisionMenu.cpp b/Source/FlowEditor/Private/Asset/SAssetRevisionMenu.cpp index be23d217e..6dbb1f5ae 100644 --- a/Source/FlowEditor/Private/Asset/SAssetRevisionMenu.cpp +++ b/Source/FlowEditor/Private/Asset/SAssetRevisionMenu.cpp @@ -2,11 +2,11 @@ #include "Asset/SAssetRevisionMenu.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" #include "IAssetTypeActions.h" #include "ISourceControlModule.h" #include "ISourceControlRevision.h" #include "SourceControlOperations.h" -#include "Framework/MultiBox/MultiBoxBuilder.h" #include "Widgets/Images/SThrobber.h" #include "Widgets/Input/SButton.h" #include "Widgets/Layout/SBorder.h" From c3847232f3ccadd1cb77566c1c6697c90eb5f8b7 Mon Sep 17 00:00:00 2001 From: LindyHopperGT <91915878+LindyHopperGT@users.noreply.github.com> Date: Wed, 20 Dec 2023 10:44:19 -0800 Subject: [PATCH 213/485] Additions, refinement and bugfixes (#180) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * formatting fix * Fixed rare issue with loading saves - prevent triggering input on not-yet-loaded node * wording corrected * extracted graph-related code from the FFlowAssetEditor to new SFlowGraphEditor class added FFlowAssetEditor::IsTabFocused() to prevent delete/paste/copy nodes if graph tab isn't focused * fixed graph commands * #131 fixed updating pin names below removed pin * quick CIS compilation fix * another CIS fix attempt * redundant include removed * fixed showing static descriptions in PIE/SIE renamed flag to better reflect its role * add null flow asset check when calling StartRootFlow (#132) Co-authored-by: Krzysiek Justyński * Add custom color support to nodes (#135) * readability tweak * non-editor compilation fix * Added includes to fix compiler errors When compiling for UE 5.1 (Win64) we ran into some compile errors due to missing header includes. * Added includes to fix compiler errors (#152) When compiling for UE 5.1 (Win64) we ran into some compile errors due to missing header includes. * Fixed bug where Owner parameter wasn't being used Owner was passed into this function, presumably to be used instead of 'this', but it was just using 'this'. Changed the code to use Owner if specified, and default to 'this' otherwise. * Fixed bug where Owner parameter wasn't being used (#153) * Added includes to fix compiler errors When compiling for UE 5.1 (Win64) we ran into some compile errors due to missing header includes. * Fixed bug where Owner parameter wasn't being used Owner was passed into this function, presumably to be used instead of 'this', but it was just using 'this'. Changed the code to use Owner if specified, and default to 'this' otherwise. * comforting convention of separating engine headers from plugin headers * Extending the "GetAssignedGraphNodeClass()" function to support to return the correc "EdGraphNode" for grand child classes of "UFlowNode" (#151) Previously it would return the first found "IsChildOf" and return the EdGraphNode of the foundclass. But lets say we got this situation just for example: UFlowNode->UMyBaseNode UFlowNode->UMyBaseNode->UMyQuestNode Both "UMyBaseNode" and "UMyQuestNode" could be in the list if we want different EdGraphNodes for both of them. If the "UMyQuestNode" goes into the itteration and it would find "UMyBaseNode" first it will be returned without checking the rest, because "UMyQuestNode" is dirived from "UMyBaseNode" (IsChildOf == true) and thus it returns the EdGraphNode of the base node directly. So that would mean I got the wrong EdGraphNode for my special quest node. This modification continues to try and find parent classes and returns the closest parent version. So lets say we have this situation: UFlowNode->UMyBaseNode UFlowNode->UMyBaseNode->UMyQuestNode->MyOtherQuestNode(BP class) Both “UMyBaseNode” and “UMyQuestNode” would be found, but the closest parent to “MyOtherQuestNode” would be returned, in this case the EdGraphNode of “UMyQuestNode” It only does this if we have found 2 or more parents though. If only 1 is found we would return that one, and if the exact class match is found lets say: "UMyQuestNode" finds "UMyQuestNode" it will return the EdGraphNode right away aswell. Hopefully this makes sence :P Otherwise feel free to ask. * Show pretty readable pin name even if friendly name is not provided (#140) * Show pretty readable pin name even if friendly name is not provided * Add missing else. Silly mistake 😋 * cosmetic tweaks of last PR * rework of PR #140 - added bEnforceFriendlyPinNames as separate flag from the Unreal editor config - moved creating a display name to `UFlowGraphSchema::GetPinDisplayName` without modifying graph node's instance * CustomInput & Output Details Customization and other cleanup * Added forward declarations & includes that were needed to compile * Changed some functions to return const &'s rather than doing a full deep copy of a member container * Explicitly calling MoveTemp() to use move semantics (supported by UE container classes (eg TArray)) to move the array without an extra copy. Some compilers might be able to do this optimization automatically, but being explicit here. * Introduced a superclass to UFlowNode_CustomInput and Output. The diff is easier if you compare them to the new superclass, since most of the code moved into it. * The "details" superclass for CustomInput/Output fixes a discovered bug where the list of EventNames was not recaching correctly. * Moved a bit of code from PlayLevelSequence to FlowNode.h (TryGetRootFlowActorOwner) so that it can be used in other contexts. Also provided a component version of this same code. * The FlowGraphSchema will now create default nodes for any CustomInputs that exist when the asset is first created. * Added a UseAdaptiveNodeTitles option to optionally make CustomInputs integrate their EventName into the title for the node. This defaults to false (to preserve previous behavior by default). * Exposed CustomInput Add/Remove functions on UFlowNode to allow subclasses to modify the CustomInputs array. * Additional includes for compiler errors * Update FlowAsset.cpp non-editor configurations compile fix for previous changelist. * Removing MoveTemp for local variables According to this guidance, it's no longer a performance improvement. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#f48-dont-return-stdmovelocal * Extended CustomOutputs and other quality of life improvements - Custom Outputs now can be listened to by the owning component - Added a DeinitializeInstance() call to FlowNode (called when its FlowAsset instance owner is deinitialized) - Added Edit Asset Defaults button to Flow Editor * Code fixes after merge with latest * Removed these replaced files * Moves some stuff around * tweaks * revert this * 5.1 backward-compatible markup Added some markup to support compiling in UE 5.1. (the flow.uplugin version will need to be locally adjusted to 5.1) * need the includes too * Updates from our project Added accessors, some refactoring of interfaces and a bug fix in CallOwnerFunction * FlowNodes respect module accessibility boundaries * Removed redundant includes * Removed unnecessary deltas * Reintroduced blank line * Removed deltas * Removed extra deltas * Removed more deltas --------- Co-authored-by: Krzysiek Justyński Co-authored-by: Bohdon Sayre Co-authored-by: Satheesh (ryanjon2040) Co-authored-by: Alex van Mansom <45290811+Vi-So@users.noreply.github.com> --- Source/Flow/Private/FlowAsset.cpp | 216 ++++++++++++++---- Source/Flow/Private/FlowComponent.cpp | 14 +- Source/Flow/Private/Nodes/FlowNode.cpp | 24 ++ .../Nodes/Route/FlowNode_CustomOutput.cpp | 6 +- .../World/FlowNode_CallOwnerFunction.cpp | 44 ++-- Source/Flow/Public/FlowAsset.h | 47 ++-- Source/Flow/Public/FlowComponent.h | 4 + Source/Flow/Public/Nodes/FlowNode.h | 6 +- .../Nodes/World/FlowNode_CallOwnerFunction.h | 1 + .../Private/Asset/FlowAssetEditor.cpp | 4 +- .../Private/Asset/FlowAssetToolbar.cpp | 3 +- .../Private/Asset/SAssetRevisionMenu.cpp | 2 + .../FlowOwnerFunctionRefCustomization.cpp | 27 ++- .../FlowEditor/Private/FlowEditorCommands.cpp | 2 +- Source/FlowEditor/Private/Graph/FlowGraph.cpp | 8 +- .../Private/Graph/FlowGraphEditor.cpp | 1 + .../Private/Graph/FlowGraphSchema.cpp | 16 +- .../Private/Graph/Widgets/SFlowPalette.cpp | 4 +- .../Private/Pins/SFlowInputPinHandle.cpp | 2 +- .../Private/Pins/SFlowOutputPinHandle.cpp | 1 + .../Public/Asset/SAssetRevisionMenu.h | 1 + .../FlowOwnerFunctionRefCustomization.h | 2 - Source/FlowEditor/Public/Graph/FlowGraph.h | 1 + .../FlowEditor/Public/Graph/FlowGraphSchema.h | 4 +- 24 files changed, 326 insertions(+), 114 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index c542f016c..97983f188 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -9,6 +9,7 @@ #include "Nodes/FlowNode.h" #include "Nodes/Route/FlowNode_CustomInput.h" +#include "Nodes/Route/FlowNode_CustomOutput.h" #include "Nodes/Route/FlowNode_Start.h" #include "Nodes/Route/FlowNode_SubGraph.h" @@ -17,7 +18,11 @@ #include "Serialization/MemoryWriter.h" #if WITH_EDITOR +#include "Editor.h" +#include "Editor/EditorEngine.h" + FString UFlowAsset::ValidationError_NodeClassNotAllowed = TEXT("Node class {0} is not allowed in this asset."); +FString UFlowAsset::ValidationError_NullNodeInstance = TEXT("Node with GUID {0} is NULL"); #endif UFlowAsset::UFlowAsset(const FObjectInitializer& ObjectInitializer) @@ -71,16 +76,45 @@ void UFlowAsset::PostDuplicate(bool bDuplicateForPIE) } } +void UFlowAsset::PostLoad() +{ + Super::PostLoad(); + + // If we removed or moved a flow node blueprint (and there is no redirector) we might loose the reference to it resulting + // in null pointers in the Nodes FGUID->UFlowNode* Map. So here we iterate over all the Nodes and remove all pairs that + // are nulled out. + + TSet NodesToRemoveGUID; + + for (auto& [Guid, Node] : GetNodes()) + { + if (!IsValid(Node)) + { + NodesToRemoveGUID.Emplace(Guid); + } + } + + for (const FGuid& Guid : NodesToRemoveGUID) + { + UnregisterNode(Guid); + } +} + EDataValidationResult UFlowAsset::ValidateAsset(FFlowMessageLog& MessageLog) { // validate nodes for (const TPair& Node : Nodes) { - if (Node.Value) + if (IsValid(Node.Value)) { - if (!IsNodeClassAllowed(Node.Value->GetClass())) + FText FailureReason; + if (!IsNodeClassAllowed(Node.Value->GetClass(), &FailureReason)) { - const FString ErrorMsg = FString::Format(*ValidationError_NodeClassNotAllowed, {*Node.Value->GetClass()->GetName()}); + const FString ErrorMsg = + FailureReason.IsEmpty() ? + FString::Format(*ValidationError_NodeClassNotAllowed, {*Node.Value->GetClass()->GetName()}) : + FailureReason.ToString(); + MessageLog.Error(*ErrorMsg, Node.Value); } @@ -90,74 +124,124 @@ EDataValidationResult UFlowAsset::ValidateAsset(FFlowMessageLog& MessageLog) MessageLog.Messages.Append(Node.Value->ValidationLog.Messages); } } + else + { + const FString ErrorMsg = FString::Format(*ValidationError_NullNodeInstance, {*Node.Key.ToString()}); + MessageLog.Error(*ErrorMsg, this); + } } return MessageLog.Messages.Num() > 0 ? EDataValidationResult::Invalid : EDataValidationResult::Valid; } -bool UFlowAsset::IsNodeClassAllowed(const UClass* FlowNodeClass) const +bool UFlowAsset::IsNodeClassAllowed(const UClass* FlowNodeClass, FText* OutOptionalFailureReason) const { - if (FlowNodeClass == nullptr) + if (!IsValid(FlowNodeClass)) + { + return false; + } + + if (!CanFlowNodeClassBeUsedByFlowAsset(*FlowNodeClass)) + { + return false; + } + + if (!CanFlowAssetUseFlowNodeClass(*FlowNodeClass)) + { + return false; + } + + // Confirm plugin reference restrictions are being respected + if (!CanFlowAssetReferenceFlowNode(*FlowNodeClass, OutOptionalFailureReason)) { return false; } - UFlowNode* NodeDefaults = FlowNodeClass->GetDefaultObject(); + return true; +} + +bool UFlowAsset::CanFlowNodeClassBeUsedByFlowAsset(const UClass& FlowNodeClass) const +{ + UFlowNode* NodeDefaults = FlowNodeClass.GetDefaultObject(); // UFlowNode class limits which UFlowAsset class can use it + for (const UClass* DeniedAssetClass : NodeDefaults->DeniedAssetClasses) { - for (const UClass* DeniedAssetClass : NodeDefaults->DeniedAssetClasses) + if (DeniedAssetClass && GetClass()->IsChildOf(DeniedAssetClass)) { - if (DeniedAssetClass && GetClass()->IsChildOf(DeniedAssetClass)) - { - return false; - } + return false; } + } - if (NodeDefaults->AllowedAssetClasses.Num() > 0) + if (NodeDefaults->AllowedAssetClasses.Num() > 0) + { + bool bAllowedInAsset = false; + for (const UClass* AllowedAssetClass : NodeDefaults->AllowedAssetClasses) { - bool bAllowedInAsset = false; - for (const UClass* AllowedAssetClass : NodeDefaults->AllowedAssetClasses) + if (AllowedAssetClass && GetClass()->IsChildOf(AllowedAssetClass)) { - if (AllowedAssetClass && GetClass()->IsChildOf(AllowedAssetClass)) - { - bAllowedInAsset = true; - break; - } - } - if (!bAllowedInAsset) - { - return false; + bAllowedInAsset = true; + break; } } + if (!bAllowedInAsset) + { + return false; + } } + return true; +} + +bool UFlowAsset::CanFlowAssetUseFlowNodeClass(const UClass& FlowNodeClass) const +{ // UFlowAsset class can limit which UFlowNode classes can be used + for (const UClass* DeniedNodeClass : DeniedNodeClasses) { - for (const UClass* DeniedNodeClass : DeniedNodeClasses) + if (DeniedNodeClass && FlowNodeClass.IsChildOf(DeniedNodeClass)) { - if (DeniedNodeClass && FlowNodeClass->IsChildOf(DeniedNodeClass)) - { - return false; - } + return false; } + } - if (AllowedNodeClasses.Num() > 0) + if (AllowedNodeClasses.Num() > 0) + { + bool bAllowedInAsset = false; + for (const UClass* AllowedNodeClass : AllowedNodeClasses) { - bool bAllowedInAsset = false; - for (const UClass* AllowedNodeClass : AllowedNodeClasses) - { - if (AllowedNodeClass && FlowNodeClass->IsChildOf(AllowedNodeClass)) - { - bAllowedInAsset = true; - break; - } - } - if (!bAllowedInAsset) + if (AllowedNodeClass && FlowNodeClass.IsChildOf(AllowedNodeClass)) { - return false; + bAllowedInAsset = true; + break; } } + if (!bAllowedInAsset) + { + return false; + } + } + + return true; +} + +bool UFlowAsset::CanFlowAssetReferenceFlowNode(const UClass& FlowNodeClass, FText* OutOptionalFailureReason) const +{ + if (!GEditor || !IsValid(&FlowNodeClass)) + { + return false; + } + + FAssetData FlowNodeAssetData(&FlowNodeClass); + + FAssetReferenceFilterContext AssetReferenceFilterContext; + AssetReferenceFilterContext.ReferencingAssets.Add(FAssetData(this)); + + // Confirm plugin reference restrictions are being respected + TSharedPtr FlowAssetReferenceFilter = GEditor->MakeAssetReferenceFilter(AssetReferenceFilterContext); + if (FlowAssetReferenceFilter.IsValid() && + !FlowAssetReferenceFilter->PassesFilter(FlowNodeAssetData, OutOptionalFailureReason)) + { + return false; } return true; @@ -326,7 +410,7 @@ void UFlowAsset::RemoveCustomOutput(const FName& EventName) CustomOutputs.Remove(EventName); } } -#endif +#endif // WITH_EDITOR UFlowNode_CustomInput* UFlowAsset::TryFindCustomInputNodeByEventName(const FName& EventName) const { @@ -341,6 +425,56 @@ UFlowNode_CustomInput* UFlowAsset::TryFindCustomInputNodeByEventName(const FName return nullptr; } +UFlowNode_CustomOutput* UFlowAsset::TryFindCustomOutputNodeByEventName(const FName& EventName) const +{ + for (const TPair& Node : Nodes) + { + if (UFlowNode_CustomOutput* CustomOutput = Cast(Node.Value)) + { + if (CustomOutput->GetEventName() == EventName) + { + return CustomOutput; + } + } + } + + return nullptr; +} + +TArray UFlowAsset::GatherCustomInputNodeEventNames() const +{ + // Runtime-safe gathering of the CustomInputs (which is editor-only data) + // from the actual flow nodes + TArray Results; + + for (const TPair& Node : Nodes) + { + if (UFlowNode_CustomInput* CustomInput = Cast(Node.Value)) + { + Results.Add(CustomInput->GetEventName()); + } + } + + return Results; +} + +TArray UFlowAsset::GatherCustomOutputNodeEventNames() const +{ + // Runtime-safe gathering of the CustomOutputs (which is editor-only data) + // from the actual flow nodes + TArray Results; + + for (const TPair& Node : Nodes) + { + if (UFlowNode_CustomOutput* CustomOutput = Cast(Node.Value)) + { + Results.Add(CustomOutput->GetEventName()); + } + } + + return Results; +} + TArray UFlowAsset::GetNodesInExecutionOrder(UFlowNode* FirstIteratedNode, const TSubclassOf FlowNodeClass) { TArray FoundNodes; diff --git a/Source/Flow/Private/FlowComponent.cpp b/Source/Flow/Private/FlowComponent.cpp index 7864a6461..2cc1651b3 100644 --- a/Source/Flow/Private/FlowComponent.cpp +++ b/Source/Flow/Private/FlowComponent.cpp @@ -44,6 +44,11 @@ void UFlowComponent::BeginPlay() { Super::BeginPlay(); + RegisterWithFlowSubsystem(); +} + +void UFlowComponent::RegisterWithFlowSubsystem() +{ if (UFlowSubsystem* FlowSubsystem = GetFlowSubsystem()) { bool bComponentLoadedFromSaveGame = false; @@ -69,14 +74,19 @@ void UFlowComponent::BeginPlay() } void UFlowComponent::EndPlay(const EEndPlayReason::Type EndPlayReason) +{ + UnregisterWithFlowSubsystem(); + + Super::EndPlay(EndPlayReason); +} + +void UFlowComponent::UnregisterWithFlowSubsystem() { if (UFlowSubsystem* FlowSubsystem = GetFlowSubsystem()) { FlowSubsystem->FinishAllRootFlows(this, EFlowFinishPolicy::Keep); FlowSubsystem->UnregisterComponent(this); } - - Super::EndPlay(EndPlayReason); } void UFlowComponent::AddIdentityTag(const FGameplayTag Tag, const EFlowNetMode NetMode /* = EFlowNetMode::Authority*/) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 4fa5b447c..13a876970 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -23,6 +23,7 @@ #include "Misc/Paths.h" #include "Serialization/MemoryReader.h" #include "Serialization/MemoryWriter.h" +#include "Engine/Blueprint.h" FFlowPin UFlowNode::DefaultInputPin(TEXT("In")); FFlowPin UFlowNode::DefaultOutputPin(TEXT("Out")); @@ -470,6 +471,29 @@ bool UFlowNode::IsOutputConnected(const FName& PinName) const return OutputPins.Contains(PinName) && Connections.Contains(PinName); } +void UFlowNode::RecursiveFindNodesByClass(UFlowNode* Node, const TSubclassOf Class, uint8 Depth, TArray& OutNodes) +{ + if (Node) + { + // Record the node if it is the desired type + if (Node->GetClass() == Class) + { + OutNodes.AddUnique(Node); + } + + if (OutNodes.Num() == Depth) + { + return; + } + + // Recurse + for (UFlowNode* ConnectedNode : Node->GetConnectedNodes()) + { + RecursiveFindNodesByClass(ConnectedNode, Class, Depth, OutNodes); + } + } +} + UFlowSubsystem* UFlowNode::GetFlowSubsystem() const { return GetFlowAsset() ? GetFlowAsset()->GetFlowSubsystem() : nullptr; diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp index a8a8a96cf..22224200c 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp @@ -23,10 +23,12 @@ void UFlowNode_CustomOutput::ExecuteInput(const FName& PinName) *GetName(), *FlowAsset->GetPathName())); } - else if (!FlowAsset->GetCustomOutputs().Contains(EventName)) + else if (!FlowAsset->TryFindCustomOutputNodeByEventName(EventName)) { + const TArray OutputNames = FlowAsset->GatherCustomOutputNodeEventNames(); FString CustomOutputsString; - for (const FName& OutputName : FlowAsset->GetCustomOutputs()) + + for (const FName& OutputName : OutputNames) { if (!CustomOutputsString.IsEmpty()) { diff --git a/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp b/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp index 048d56391..19ebc8b3c 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp @@ -97,12 +97,31 @@ void UFlowNode_CallOwnerFunction::PostLoad() FObjectPropertyBase* ParamsProperty = FindFProperty(GetClass(), GET_MEMBER_NAME_CHECKED(UFlowNode_CallOwnerFunction, Params)); check(ParamsProperty); - UClass* RequiredParamsClass = GetRequiredParamsClass(); - if (IsValid(RequiredParamsClass)) + // NOTE (gtaylor) This fixes corruption in FlowNodes that could have been caused with + // a previous version of the code (which was inadvisedly calling SetPropertyClass) + // to restore the correct PropertyClass for this node. + // (it could be removed in a future release, once all assets have been updated) + if (ParamsProperty->PropertyClass != UFlowOwnerFunctionParams::StaticClass()) + { + ParamsProperty->SetPropertyClass(UFlowOwnerFunctionParams::StaticClass()); + } +} + +bool UFlowNode_CallOwnerFunction::CanEditChange(const FProperty* InProperty) const +{ + if (!Super::CanEditChange(InProperty)) { - // Update the property to filter for just this class (and its subclasses) - ParamsProperty->SetPropertyClass(RequiredParamsClass); + return false; + } + + const FName PropertyName = InProperty->GetFName(); + + if (PropertyName == GET_MEMBER_NAME_CHECKED(UFlowNode_CallOwnerFunction, Params)) + { + return false; } + + return true; } void UFlowNode_CallOwnerFunction::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) @@ -368,27 +387,20 @@ UClass* UFlowNode_CallOwnerFunction::GetParamsClassForFunctionName(const UClass& bool UFlowNode_CallOwnerFunction::TryAllocateParamsInstance() { - FObjectPropertyBase* ParamsProperty = FindFProperty(GetClass(), GET_MEMBER_NAME_CHECKED(UFlowNode_CallOwnerFunction, Params)); - check(ParamsProperty); - - UClass* RequiredParamsClass = GetRequiredParamsClass(); - - if (ParamsProperty) - { - // Update the property to filter for just this class (and its subclasses) - ParamsProperty->SetPropertyClass(RequiredParamsClass); - } - if (FunctionRef.GetFunctionName().IsNone()) { + // Throw out the old params object (if any) + Params = nullptr; + return false; } const UClass* ExistingParamsClass = GetExistingParamsClass(); + UClass* RequiredParamsClass = GetRequiredParamsClass(); const bool bNeedsAllocateParams = !IsValid(ExistingParamsClass) || - !ExistingParamsClass->IsChildOf(RequiredParamsClass); + ExistingParamsClass != RequiredParamsClass; if (!bNeedsAllocateParams) { diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index c5fee3f9c..11a981c92 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -10,6 +10,7 @@ #include "UObject/ObjectKey.h" #include "FlowAsset.generated.h" +class UFlowNode_CustomOutput; class UFlowNode_CustomInput; class UFlowNode_SubGraph; class UFlowSubsystem; @@ -69,14 +70,21 @@ class FLOW_API UFlowAsset : public UObject static void AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector); virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; virtual void PostDuplicate(bool bDuplicateForPIE) override; + virtual void PostLoad() override; // -- virtual EDataValidationResult ValidateAsset(FFlowMessageLog& MessageLog); // Returns whether the node class is allowed in this flow asset - bool IsNodeClassAllowed(const UClass* FlowNodeClass) const; + bool IsNodeClassAllowed(const UClass* FlowNodeClass, FText* OutOptionalFailureReason = nullptr) const; static FString ValidationError_NodeClassNotAllowed; + static FString ValidationError_NullNodeInstance; + +protected: + bool CanFlowNodeClassBeUsedByFlowAsset(const UClass& FlowNodeClass) const; + bool CanFlowAssetUseFlowNodeClass(const UClass& FlowNodeClass) const; + bool CanFlowAssetReferenceFlowNode(const UClass& FlowNodeClass, FText* OutOptionalFailureReason = nullptr) const; #endif // IFlowGraphInterface @@ -114,6 +122,8 @@ class FLOW_API UFlowAsset : public UObject UPROPERTY() TMap Nodes; +#if WITH_EDITORONLY_DATA +protected: /** * Custom Inputs define custom entry points in graph, it's similar to blueprint Custom Events * Sub Graph node using this Flow Asset will generate context Input Pin for every valid Event name on this list @@ -127,6 +137,7 @@ class FLOW_API UFlowAsset : public UObject */ UPROPERTY(EditAnywhere, Category = "Sub Graph") TArray CustomOutputs; +#endif // WITH_EDITORONLY_DATA public: #if WITH_EDITOR @@ -160,21 +171,6 @@ class FLOW_API UFlowAsset : public UObject UFUNCTION(BlueprintPure, Category = "FlowAsset") virtual UFlowNode* GetDefaultEntryNode() const; -#if WITH_EDITOR -protected: - void AddCustomInput(const FName& EventName); - void RemoveCustomInput(const FName& EventName); - - void AddCustomOutput(const FName& EventName); - void RemoveCustomOutput(const FName& EventName); -#endif - -public: - const TArray& GetCustomInputs() const { return CustomInputs; } - const TArray& GetCustomOutputs() const { return CustomOutputs; } - - UFlowNode_CustomInput* TryFindCustomInputNodeByEventName(const FName& EventName) const; - UFUNCTION(BlueprintPure, Category = "FlowAsset", meta = (DeterminesOutputType = "FlowNodeClass")) TArray GetNodesInExecutionOrder(UFlowNode* FirstIteratedNode, const TSubclassOf FlowNodeClass); @@ -210,6 +206,25 @@ class FLOW_API UFlowAsset : public UObject } } +public: + UFlowNode_CustomInput* TryFindCustomInputNodeByEventName(const FName& EventName) const; + UFlowNode_CustomOutput* TryFindCustomOutputNodeByEventName(const FName& EventName) const; + + TArray GatherCustomInputNodeEventNames() const; + TArray GatherCustomOutputNodeEventNames() const; + +#if WITH_EDITOR + const TArray& GetCustomInputs() const { return CustomInputs; } + const TArray& GetCustomOutputs() const { return CustomOutputs; } + +protected: + void AddCustomInput(const FName& EventName); + void RemoveCustomInput(const FName& EventName); + + void AddCustomOutput(const FName& EventName); + void RemoveCustomOutput(const FName& EventName); +#endif // WITH_EDITOR + ////////////////////////////////////////////////////////////////////////// // Instances of the template asset diff --git a/Source/Flow/Public/FlowComponent.h b/Source/Flow/Public/FlowComponent.h index b800c2b07..4c6132526 100644 --- a/Source/Flow/Public/FlowComponent.h +++ b/Source/Flow/Public/FlowComponent.h @@ -81,6 +81,10 @@ class FLOW_API UFlowComponent : public UActorComponent, public IFlowOwnerInterfa UFUNCTION(BlueprintCallable, Category = "Flow") void RemoveIdentityTags(FGameplayTagContainer Tags, const EFlowNetMode NetMode = EFlowNetMode::Authority); +protected: + void RegisterWithFlowSubsystem(); + void UnregisterWithFlowSubsystem(); + private: UFUNCTION() void OnRep_AddedIdentityTags(); diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index c2d787a16..49ef4e888 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -118,8 +118,8 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte FGuid NodeGuid; public: - void SetGuid(const FGuid NewGuid) { NodeGuid = NewGuid; } - FGuid GetGuid() const { return NodeGuid; } + void SetGuid(const FGuid& NewGuid) { NodeGuid = NewGuid; } + const FGuid& GetGuid() const { return NodeGuid; } UFUNCTION(BlueprintPure, Category = "FlowNode") UFlowAsset* GetFlowAsset() const; @@ -239,6 +239,8 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UFUNCTION(BlueprintPure, Category= "FlowNode") bool IsOutputConnected(const FName& PinName) const; + static void RecursiveFindNodesByClass(UFlowNode* Node, const TSubclassOf Class, uint8 Depth, TArray& OutNodes); + ////////////////////////////////////////////////////////////////////////// // Debugger protected: diff --git a/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h b/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h index 55d8266ce..de78a041a 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h @@ -37,6 +37,7 @@ class FLOW_API UFlowNode_CallOwnerFunction : public UFlowNode #if WITH_EDITOR //Begin UObject virtual void PostLoad() override; + virtual bool CanEditChange(const FProperty* InProperty) const override; virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override; //End UObject diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index 6efbfd44e..6626478e4 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -384,8 +384,8 @@ void FFlowAssetEditor::BindToolbarCommands() #endif ToolkitCommands->MapAction(ToolbarCommands.EditAssetDefaults, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::EditAssetDefaults_Clicked), - FCanExecuteAction()); + FExecuteAction::CreateSP(this, &FFlowAssetEditor::EditAssetDefaults_Clicked), + FCanExecuteAction()); // Engine's Play commands ToolkitCommands->Append(FPlayWorldCommands::GlobalPlayWorldActions.ToSharedRef()); diff --git a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp index d023a4e8c..c621c4109 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp @@ -202,6 +202,7 @@ void FFlowAssetToolbar::BuildAssetToolbar(UToolMenu* ToolbarMenu) const // add buttons Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().RefreshAsset)); Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().ValidateAsset)); + Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().EditAssetDefaults)); } { @@ -227,8 +228,6 @@ void FFlowAssetToolbar::BuildAssetToolbar(UToolMenu* ToolbarMenu) const #if ENABLE_SEARCH_IN_ASSET_EDITOR Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().SearchInAsset)); #endif - - Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().EditAssetDefaults)); } } diff --git a/Source/FlowEditor/Private/Asset/SAssetRevisionMenu.cpp b/Source/FlowEditor/Private/Asset/SAssetRevisionMenu.cpp index 6dbb1f5ae..b6ac1ef8c 100644 --- a/Source/FlowEditor/Private/Asset/SAssetRevisionMenu.cpp +++ b/Source/FlowEditor/Private/Asset/SAssetRevisionMenu.cpp @@ -6,10 +6,12 @@ #include "IAssetTypeActions.h" #include "ISourceControlModule.h" #include "ISourceControlRevision.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" #include "SourceControlOperations.h" #include "Widgets/Images/SThrobber.h" #include "Widgets/Input/SButton.h" #include "Widgets/Layout/SBorder.h" +#include "Widgets/SBoxPanel.h" #include "Widgets/Text/STextBlock.h" #define LOCTEXT_NAMESPACE "SFlowRevisionMenu" diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowOwnerFunctionRefCustomization.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowOwnerFunctionRefCustomization.cpp index 6d48f71e4..d6afd481c 100644 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowOwnerFunctionRefCustomization.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowOwnerFunctionRefCustomization.cpp @@ -69,8 +69,19 @@ TArray FFlowOwnerFunctionRefCustomization::GetFlowOwnerFunctionRefs( TArray ValidFunctionNames; // Gather a list of potential functions - TArray PotentialFunctionNames; - ExpectedOwnerClass.GenerateFunctionList(PotentialFunctionNames); + TSet PotentialFunctionNames; + + const UClass* CurClass = &ExpectedOwnerClass; + while (IsValid(CurClass)) + { + TArray CurClassFunctionNames; + CurClass->GenerateFunctionList(CurClassFunctionNames); + + PotentialFunctionNames.Append(CurClassFunctionNames); + + // Recurse to include all of the Super(s) names + CurClass = CurClass->GetSuperClass(); + } if (PotentialFunctionNames.Num() == 0) { @@ -101,21 +112,9 @@ bool FFlowOwnerFunctionRefCustomization::IsFunctionUsable(const UFunction& Funct return false; } - if (!DoesFunctionHaveExpectedParamType(Function, FlowNodeOwner)) - { - return false; - } - return true; } -bool FFlowOwnerFunctionRefCustomization::DoesFunctionHaveExpectedParamType(const UFunction& Function, const UFlowNode_CallOwnerFunction& FlowNodeOwner) -{ - const UClass* PropertyClass = UFlowNode_CallOwnerFunction::GetParamsClassForFunction(Function); - - return FlowNodeOwner.IsAcceptableParamsPropertyClass(PropertyClass); -} - void FFlowOwnerFunctionRefCustomization::SetCuratedName(const FName& NewFunctionName) { TSharedPtr FunctionNameHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowOwnerFunctionRef, FunctionName)); diff --git a/Source/FlowEditor/Private/FlowEditorCommands.cpp b/Source/FlowEditor/Private/FlowEditorCommands.cpp index da038b917..f57ca6561 100644 --- a/Source/FlowEditor/Private/FlowEditorCommands.cpp +++ b/Source/FlowEditor/Private/FlowEditorCommands.cpp @@ -23,7 +23,7 @@ void FFlowToolbarCommands::RegisterCommands() UI_COMMAND(SearchInAsset, "Search", "Search in the current Flow Graph", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control | EModifierKey::Shift, EKeys::F)); UI_COMMAND(EditAssetDefaults, "Asset Defaults", "Edit the FlowAsset default properties", EUserInterfaceActionType::Button, FInputChord()); - + UI_COMMAND(GoToParentInstance, "Go To Parent", "Open editor for the Flow Asset that created this Flow instance", EUserInterfaceActionType::Button, FInputChord()); } diff --git a/Source/FlowEditor/Private/Graph/FlowGraph.cpp b/Source/FlowEditor/Private/Graph/FlowGraph.cpp index e51ba7abb..8520c8bf2 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraph.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraph.cpp @@ -31,7 +31,13 @@ UFlowGraph::UFlowGraph(const FObjectInitializer& ObjectInitializer) UEdGraph* UFlowGraph::CreateGraph(UFlowAsset* InFlowAsset) { - UEdGraph* NewGraph = CastChecked(FBlueprintEditorUtils::CreateNewGraph(InFlowAsset, NAME_None, StaticClass(), UFlowGraphSchema::StaticClass())); + return CreateGraph(InFlowAsset, UFlowGraphSchema::StaticClass()); +} + +UEdGraph* UFlowGraph::CreateGraph(UFlowAsset* InFlowAsset, TSubclassOf FlowSchema) +{ + check(FlowSchema); + UEdGraph* NewGraph = CastChecked(FBlueprintEditorUtils::CreateNewGraph(InFlowAsset, NAME_None, StaticClass(), FlowSchema)); NewGraph->bAllowDeletion = false; InFlowAsset->FlowGraph = NewGraph; diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index bc838a926..d68f24e66 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -14,6 +14,7 @@ #include "Framework/Commands/GenericCommands.h" #include "HAL/PlatformApplicationMisc.h" +#include "IDetailsView.h" #include "LevelEditor.h" #include "Modules/ModuleManager.h" #include "ScopedTransaction.h" diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index 5e6772a74..bf8148f8d 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -54,9 +54,9 @@ void UFlowGraphSchema::SubscribeToAssetChanges() } } -void UFlowGraphSchema::GetPaletteActions(FGraphActionMenuBuilder& ActionMenuBuilder, const UClass* AssetClass, const FString& CategoryName) +void UFlowGraphSchema::GetPaletteActions(FGraphActionMenuBuilder& ActionMenuBuilder, const UFlowAsset* EditedFlowAsset, const FString& CategoryName) { - GetFlowNodeActions(ActionMenuBuilder, AssetClass->GetDefaultObject(), CategoryName); + GetFlowNodeActions(ActionMenuBuilder, EditedFlowAsset, CategoryName); GetCommentAction(ActionMenuBuilder); } @@ -346,19 +346,19 @@ UClass* UFlowGraphSchema::GetAssignedGraphNodeClass(const UClass* FlowNodeClass) return IsValid(ReturnClass) ? ReturnClass : UFlowGraphNode::StaticClass(); } -void UFlowGraphSchema::ApplyNodeFilter(const UFlowAsset* AssetClassDefaults, const UClass* FlowNodeClass, TArray& FilteredNodes) +void UFlowGraphSchema::ApplyNodeFilter(const UFlowAsset* EditedFlowAsset, const UClass* FlowNodeClass, TArray& FilteredNodes) { if (FlowNodeClass == nullptr) { return; } - if (AssetClassDefaults == nullptr) + if (EditedFlowAsset == nullptr) { return; } - if (!AssetClassDefaults->IsNodeClassAllowed(FlowNodeClass)) + if (!EditedFlowAsset->IsNodeClassAllowed(FlowNodeClass)) { return; } @@ -367,7 +367,7 @@ void UFlowGraphSchema::ApplyNodeFilter(const UFlowAsset* AssetClassDefaults, con FilteredNodes.Emplace(NodeDefaults); } -void UFlowGraphSchema::GetFlowNodeActions(FGraphActionMenuBuilder& ActionMenuBuilder, const UFlowAsset* AssetClassDefaults, const FString& CategoryName) +void UFlowGraphSchema::GetFlowNodeActions(FGraphActionMenuBuilder& ActionMenuBuilder, const UFlowAsset* EditedFlowAsset, const FString& CategoryName) { if (!bInitialGatherPerformed) { @@ -381,14 +381,14 @@ void UFlowGraphSchema::GetFlowNodeActions(FGraphActionMenuBuilder& ActionMenuBui for (const UClass* FlowNodeClass : NativeFlowNodes) { - ApplyNodeFilter(AssetClassDefaults, FlowNodeClass, FilteredNodes); + ApplyNodeFilter(EditedFlowAsset, FlowNodeClass, FilteredNodes); } for (const TPair& AssetData : BlueprintFlowNodes) { if (const UBlueprint* Blueprint = GetPlaceableNodeBlueprint(AssetData.Value)) { - ApplyNodeFilter(AssetClassDefaults, Blueprint->GeneratedClass, FilteredNodes); + ApplyNodeFilter(EditedFlowAsset, Blueprint->GeneratedClass, FilteredNodes); } } diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowPalette.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowPalette.cpp index ba1dca9e3..51eaf265e 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowPalette.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowPalette.cpp @@ -183,10 +183,10 @@ TSharedRef SFlowPalette::OnCreateWidgetForAction(FCreateWidgetForAction void SFlowPalette::CollectAllActions(FGraphActionListBuilderBase& OutAllActions) { ensureAlways(FlowAssetEditor.Pin() && FlowAssetEditor.Pin()->GetFlowAsset()); - const UClass* AssetClass = FlowAssetEditor.Pin()->GetFlowAsset()->GetClass(); + const UFlowAsset* EditedFlowAsset = FlowAssetEditor.Pin()->GetFlowAsset(); FGraphActionMenuBuilder ActionMenuBuilder; - UFlowGraphSchema::GetPaletteActions(ActionMenuBuilder, AssetClass, GetFilterCategoryName()); + UFlowGraphSchema::GetPaletteActions(ActionMenuBuilder, EditedFlowAsset, GetFilterCategoryName()); OutAllActions.Append(ActionMenuBuilder); } diff --git a/Source/FlowEditor/Private/Pins/SFlowInputPinHandle.cpp b/Source/FlowEditor/Private/Pins/SFlowInputPinHandle.cpp index aca04ac50..93f0fd7d1 100644 --- a/Source/FlowEditor/Private/Pins/SFlowInputPinHandle.cpp +++ b/Source/FlowEditor/Private/Pins/SFlowInputPinHandle.cpp @@ -2,7 +2,7 @@ #include "Pins/SFlowInputPinHandle.h" #include "Nodes/FlowNode.h" - +#include "Pins/SFlowPinHandle.h" #include "EdGraphSchema_K2.h" #include "Engine/Blueprint.h" diff --git a/Source/FlowEditor/Private/Pins/SFlowOutputPinHandle.cpp b/Source/FlowEditor/Private/Pins/SFlowOutputPinHandle.cpp index 984c58f76..1605bb078 100644 --- a/Source/FlowEditor/Private/Pins/SFlowOutputPinHandle.cpp +++ b/Source/FlowEditor/Private/Pins/SFlowOutputPinHandle.cpp @@ -2,6 +2,7 @@ #include "Pins/SFlowOutputPinHandle.h" #include "Nodes/FlowNode.h" +#include "Pins/SFlowPinHandle.h" #include "EdGraphSchema_K2.h" #include "Engine/Blueprint.h" diff --git a/Source/FlowEditor/Public/Asset/SAssetRevisionMenu.h b/Source/FlowEditor/Public/Asset/SAssetRevisionMenu.h index 2ee609388..4bbbe3cc1 100644 --- a/Source/FlowEditor/Public/Asset/SAssetRevisionMenu.h +++ b/Source/FlowEditor/Public/Asset/SAssetRevisionMenu.h @@ -7,6 +7,7 @@ #include "Widgets/SCompoundWidget.h" class FUpdateStatus; +class SVerticalBox; struct FRevisionInfo; // Forced to make a variant of SBlueprintRevisionMenu, only to replace to UBlueprint* parameter diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowOwnerFunctionRefCustomization.h b/Source/FlowEditor/Public/DetailCustomizations/FlowOwnerFunctionRefCustomization.h index d3cb3f2a2..6754cf829 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowOwnerFunctionRefCustomization.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowOwnerFunctionRefCustomization.h @@ -48,6 +48,4 @@ class FFlowOwnerFunctionRefCustomization : public IFlowCuratedNamePropertyCustom static TArray GetFlowOwnerFunctionRefs(const UFlowNode_CallOwnerFunction& FlowNodeOwner, const UClass& ExpectedOwnerClass); static bool IsFunctionUsable(const UFunction& Function, const UFlowNode_CallOwnerFunction& FlowNodeOwner); - static bool DoesFunctionHaveExpectedParamType(const UFunction& Function, const UFlowNode_CallOwnerFunction& FlowNodeOwner); - }; diff --git a/Source/FlowEditor/Public/Graph/FlowGraph.h b/Source/FlowEditor/Public/Graph/FlowGraph.h index 7c1803481..81251f880 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraph.h +++ b/Source/FlowEditor/Public/Graph/FlowGraph.h @@ -22,6 +22,7 @@ class FLOWEDITOR_API UFlowGraph : public UEdGraph GENERATED_UCLASS_BODY() static UEdGraph* CreateGraph(UFlowAsset* InFlowAsset); + static UEdGraph* CreateGraph(UFlowAsset* InFlowAsset, TSubclassOf FlowSchema); void RefreshGraph(); // UEdGraph diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h index 95be35b9f..cbc82ec41 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h @@ -29,7 +29,7 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema public: static void SubscribeToAssetChanges(); - static void GetPaletteActions(FGraphActionMenuBuilder& ActionMenuBuilder, const UClass* AssetClass, const FString& CategoryName); + static void GetPaletteActions(FGraphActionMenuBuilder& ActionMenuBuilder, const UFlowAsset* EditedFlowAsset, const FString& CategoryName); // EdGraphSchema virtual void GetGraphContextActions(FGraphContextMenuBuilder& ContextMenuBuilder) const override; @@ -54,7 +54,7 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema private: static void ApplyNodeFilter(const UFlowAsset* AssetClassDefaults, const UClass* FlowNodeClass, TArray& FilteredNodes); - static void GetFlowNodeActions(FGraphActionMenuBuilder& ActionMenuBuilder, const UFlowAsset* AssetClassDefaults, const FString& CategoryName); + static void GetFlowNodeActions(FGraphActionMenuBuilder& ActionMenuBuilder, const UFlowAsset* EditedFlowAsset, const FString& CategoryName); static void GetCommentAction(FGraphActionMenuBuilder& ActionMenuBuilder, const UEdGraph* CurrentGraph = nullptr); static bool IsFlowNodePlaceable(const UClass* Class); From d9dd4a5ed0b68b7c19ea707929c325111d03a627 Mon Sep 17 00:00:00 2001 From: VintageDeveloper <149346422+VintageDeveloper@users.noreply.github.com> Date: Thu, 21 Dec 2023 05:46:17 +1100 Subject: [PATCH 214/485] Fix Issue #172 - Add Alignment Features to Flow Graph Nodes (#179) * add context menu for organising nodes * initial commit for organisation context menu actions * update menu context to run graph organisation functions * remove unused header file * remove unused header file * remove unused header file * update sub menu for alignment context menu options * update context menu to only appear for nodes * update context menu to align with prior implementation * update code to align with prior implementations --- .../Private/Graph/FlowGraphEditor.cpp | 22 +++++++++++++++++++ .../Private/Graph/Nodes/FlowGraphNode.cpp | 15 +++++++++++++ .../FlowEditor/Public/Graph/FlowGraphEditor.h | 2 +- 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index d68f24e66..5cf337160 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -224,6 +224,28 @@ void SFlowGraphEditor::BindGraphCommands() CommandList->MapAction(FlowGraphCommands.JumpToNodeDefinition, FExecuteAction::CreateSP(this, &SFlowGraphEditor::JumpToNodeDefinition), FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanJumpToNodeDefinition)); + + // Organisation Commands + CommandList->MapAction(GraphEditorCommands.AlignNodesTop, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnAlignTop)); + + CommandList->MapAction(GraphEditorCommands.AlignNodesMiddle, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnAlignMiddle)); + + CommandList->MapAction(GraphEditorCommands.AlignNodesBottom, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnAlignBottom)); + + CommandList->MapAction(GraphEditorCommands.AlignNodesLeft, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnAlignLeft)); + + CommandList->MapAction(GraphEditorCommands.AlignNodesCenter, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnAlignCenter)); + + CommandList->MapAction(GraphEditorCommands.AlignNodesRight, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnAlignRight)); + + CommandList->MapAction(GraphEditorCommands.StraightenConnections, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnStraightenConnections)); } FGraphAppearanceInfo SFlowGraphEditor::GetGraphAppearanceInfo() const diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index a6862c809..033fa3b8e 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -492,6 +492,21 @@ void UFlowGraphNode::GetNodeContextMenuActions(class UToolMenu* Menu, class UGra Section.AddMenuEntry(FlowGraphCommands.JumpToNodeDefinition); } } + + { + FToolMenuSection& Section = Menu->AddSection("FlowGraphNodeOrganisation", LOCTEXT("NodeOrganisation", "Organisation")); + Section.AddSubMenu("Alignment", LOCTEXT("AlignmentHeader", "Alignment"), FText(), FNewToolMenuDelegate::CreateLambda([](UToolMenu* SubMenu) + { + FToolMenuSection& SubMenuSection = SubMenu->AddSection("EdGraphSchemaAlignment", LOCTEXT("AlignHeader", "Align")); + SubMenuSection.AddMenuEntry(FGraphEditorCommands::Get().AlignNodesTop); + SubMenuSection.AddMenuEntry(FGraphEditorCommands::Get().AlignNodesMiddle); + SubMenuSection.AddMenuEntry(FGraphEditorCommands::Get().AlignNodesBottom); + SubMenuSection.AddMenuEntry(FGraphEditorCommands::Get().AlignNodesLeft); + SubMenuSection.AddMenuEntry(FGraphEditorCommands::Get().AlignNodesCenter); + SubMenuSection.AddMenuEntry(FGraphEditorCommands::Get().AlignNodesRight); + SubMenuSection.AddMenuEntry(FGraphEditorCommands::Get().StraightenConnections); + })); + } } } diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h index ccba5175c..51bd1af38 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h @@ -144,4 +144,4 @@ class FLOWEDITOR_API SFlowGraphEditor : public SGraphEditor void JumpToNodeDefinition() const; bool CanJumpToNodeDefinition() const; -}; +}; \ No newline at end of file From 079631c920a2dae8da83e97c272bfcbc588f5fa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Thu, 21 Dec 2023 21:26:36 +0100 Subject: [PATCH 215/485] exposed SetGuid, GetGuid to blueprints --- Source/Flow/Public/Nodes/FlowNode.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 49ef4e888..bc6ff925a 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -118,7 +118,10 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte FGuid NodeGuid; public: + UFUNCTION(BlueprintCallable, Category = "FlowNode") void SetGuid(const FGuid& NewGuid) { NodeGuid = NewGuid; } + + UFUNCTION(BlueprintPure, Category = "FlowNode") const FGuid& GetGuid() const { return NodeGuid; } UFUNCTION(BlueprintPure, Category = "FlowNode") From e085276ac4e713e0a8a4bed01cbe91d89503f0c2 Mon Sep 17 00:00:00 2001 From: sergeypdev <78605863+sergeypdev@users.noreply.github.com> Date: Tue, 9 Jan 2024 22:02:05 +0400 Subject: [PATCH 216/485] Allow optional hot reload for native flow nodes (#182) --- Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp | 1 + Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp | 6 +++++- Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h | 5 +++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp index de7461d6e..a73c0b131 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp @@ -11,6 +11,7 @@ UFlowGraphEditorSettings::UFlowGraphEditorSettings(const FObjectInitializer& Obj , bShowSubGraphPreview(true) , bShowSubGraphPath(true) , SubGraphPreviewSize(FVector2D(640.f, 360.f)) + , bHotReloadNativeNodes(false) , bHighlightInputWiresOfSelectedNodes(true) , bHighlightOutputWiresOfSelectedNodes(false) { diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index bf8148f8d..9bd5e672c 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -458,12 +458,16 @@ void UFlowGraphSchema::OnHotReload(EReloadCompleteReason ReloadCompleteReason) void UFlowGraphSchema::GatherNativeNodes() { + const bool bHotReloadNativeNodes = UFlowGraphEditorSettings::Get()->bHotReloadNativeNodes; // collect C++ nodes once per editor session - if (NativeFlowNodes.Num() > 0) + if (NativeFlowNodes.Num() > 0 && !bHotReloadNativeNodes) { return; } + NativeFlowNodes.Reset(); + GraphNodesByFlowNodes.Reset(); + TArray FlowNodes; GetDerivedClasses(UFlowNode::StaticClass(), FlowNodes); for (UClass* Class : FlowNodes) diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h index a15fcbded..6da9fc25d 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h @@ -49,6 +49,11 @@ class FLOWEDITOR_API UFlowGraphEditorSettings : public UDeveloperSettings UPROPERTY(config, EditAnywhere, Category = "Nodes", meta = (EditCondition = "bShowSubGraphPreview")) FVector2D SubGraphPreviewSize; + /** Enable hot reload for native flow nodes? + * WARNING: hot reload can easily crash the editor and you can lose progress */ + UPROPERTY(EditAnywhere, Config, Category = "Nodes", AdvancedDisplay) + bool bHotReloadNativeNodes; + UPROPERTY(EditAnywhere, config, Category = "Wires") bool bHighlightInputWiresOfSelectedNodes; From fd8139346b47c6b9401d150433d1d90c796aeb0e Mon Sep 17 00:00:00 2001 From: Soraphis Date: Sat, 27 Jan 2024 12:10:00 +0100 Subject: [PATCH 217/485] fixes missing indentation in the palette panel for subcategories (#184) using a custom "RowExpander" Delegate analogous to the way the blueprint graph does it. compare it to here: https://github.com/EpicGames/UnrealEngine/blob/072300df18a94f18077ca20a14224b5d99fee872/Engine/Source/Editor/Kismet/Private/SBlueprintSubPalette.cpp#L293C54-L293C74 --- .../Private/Graph/Widgets/SFlowPalette.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowPalette.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowPalette.cpp index 51eaf265e..278b31bbf 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowPalette.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowPalette.cpp @@ -105,6 +105,17 @@ void SFlowPalette::Construct(const FArguments& InArgs, TWeakPtr CreateCustomExpanderStatic(const FCustomExpanderData& ActionMenuData, bool bShowFavoriteToggle) + { + TSharedPtr CustomExpander; + // in SBlueprintSubPalette here would be a difference depending on bShowFavoriteToggle + SAssignNew(CustomExpander, SExpanderArrow, ActionMenuData.TableRow); + return CustomExpander.ToSharedRef(); + } + }; + this->ChildSlot [ SNew(SBorder) @@ -134,6 +145,7 @@ void SFlowPalette::Construct(const FArguments& InArgs, TWeakPtr Date: Sat, 27 Jan 2024 22:12:35 +1100 Subject: [PATCH 218/485] Update FlowGraph Reroute Node Selection & Connection Highlighting to be similar to Blueprints. Resolves #171 (#185) * update input wiring to be similar to blueprints * on hover node, node now highlights * resolve merge conflict * update reroute node selection to be similar to blueprints --- .../Private/Graph/FlowGraphConnectionDrawingPolicy.cpp | 8 ++++++++ .../FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp b/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp index 42510dddd..6a39a1658 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp @@ -196,6 +196,14 @@ void FFlowGraphConnectionDrawingPolicy::DetermineWiringStyle(UEdGraphPin* Output } } } + + const bool bDeemphasizeUnhoveredPins = HoveredPins.Num() > 0; + + if (bDeemphasizeUnhoveredPins) + { + ApplyHoverDeemphasis(OutputPin, InputPin, /*inout*/ Params.WireThickness, /*inout*/ Params.WireColor); + } + } void FFlowGraphConnectionDrawingPolicy::Draw(TMap, FArrangedWidget>& InPinGeometries, FArrangedChildren& ArrangedNodes) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp index a73c0b131..33a7361d3 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp @@ -12,7 +12,7 @@ UFlowGraphEditorSettings::UFlowGraphEditorSettings(const FObjectInitializer& Obj , bShowSubGraphPath(true) , SubGraphPreviewSize(FVector2D(640.f, 360.f)) , bHotReloadNativeNodes(false) - , bHighlightInputWiresOfSelectedNodes(true) + , bHighlightInputWiresOfSelectedNodes(false) , bHighlightOutputWiresOfSelectedNodes(false) { } From e284ce069a65be38e0aa6d465d4e0bed48e1b32f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 28 Jan 2024 16:08:31 +0100 Subject: [PATCH 219/485] Added `#include UE_INLINE_GENERATED_CPP_BY_NAME` to every possible .cpp It's a new thing in UE 5.1 and it's meant to "improve compile times because less header parsing is required." --- Source/Flow/Private/FlowAsset.cpp | 2 ++ Source/Flow/Private/FlowComponent.cpp | 2 ++ Source/Flow/Private/FlowOwnerFunctionParams.cpp | 5 ++--- Source/Flow/Private/FlowOwnerFunctionRef.cpp | 5 ++--- Source/Flow/Private/FlowSettings.cpp | 2 ++ Source/Flow/Private/FlowSubsystem.cpp | 4 +++- Source/Flow/Private/FlowWorldSettings.cpp | 2 ++ Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp | 2 ++ .../Flow/Private/LevelSequence/FlowLevelSequencePlayer.cpp | 2 ++ .../Private/MovieScene/MovieSceneFlowRepeaterSection.cpp | 2 ++ Source/Flow/Private/MovieScene/MovieSceneFlowSectionBase.cpp | 2 ++ Source/Flow/Private/MovieScene/MovieSceneFlowTemplate.cpp | 2 ++ Source/Flow/Private/MovieScene/MovieSceneFlowTrack.cpp | 2 ++ .../Flow/Private/MovieScene/MovieSceneFlowTriggerSection.cpp | 2 ++ Source/Flow/Private/Nodes/FlowNode.cpp | 2 ++ Source/Flow/Private/Nodes/FlowNodeBlueprint.cpp | 2 ++ Source/Flow/Private/Nodes/FlowPin.cpp | 2 ++ Source/Flow/Private/Nodes/Operators/FlowNode_LogicalAND.cpp | 2 ++ Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp | 2 ++ Source/Flow/Private/Nodes/Route/FlowNode_Counter.cpp | 2 ++ Source/Flow/Private/Nodes/Route/FlowNode_CustomEventBase.cpp | 2 ++ Source/Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp | 2 ++ Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp | 2 ++ .../Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp | 2 ++ .../Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp | 2 ++ Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp | 2 ++ Source/Flow/Private/Nodes/Route/FlowNode_Reroute.cpp | 2 ++ Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp | 2 ++ Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp | 2 ++ Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp | 2 ++ Source/Flow/Private/Nodes/Utils/FlowNode_Checkpoint.cpp | 2 ++ Source/Flow/Private/Nodes/Utils/FlowNode_Log.cpp | 2 ++ .../Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp | 2 ++ Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp | 2 ++ .../Flow/Private/Nodes/World/FlowNode_OnActorRegistered.cpp | 2 ++ .../Private/Nodes/World/FlowNode_OnActorUnregistered.cpp | 2 ++ .../Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp | 2 ++ .../Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp | 2 ++ Source/FlowEditor/Private/Asset/FlowAssetEditorContext.cpp | 2 ++ Source/FlowEditor/Private/Asset/FlowDebuggerSubsystem.cpp | 4 +++- Source/FlowEditor/Private/Asset/FlowImportUtils.cpp | 2 ++ Source/FlowEditor/Private/Asset/FlowMessageLogListing.cpp | 2 ++ .../DetailCustomizations/FlowNode_CustomEventBaseDetails.cpp | 1 - Source/FlowEditor/Private/Graph/FlowGraph.cpp | 2 ++ .../Private/Graph/FlowGraphConnectionDrawingPolicy.cpp | 2 ++ Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp | 2 ++ Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp | 2 ++ Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp | 2 ++ Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp | 2 ++ Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp | 2 ++ .../Private/Graph/Nodes/FlowGraphNode_ExecutionSequence.cpp | 2 ++ .../FlowEditor/Private/Graph/Nodes/FlowGraphNode_Finish.cpp | 2 ++ .../FlowEditor/Private/Graph/Nodes/FlowGraphNode_Reroute.cpp | 2 ++ .../FlowEditor/Private/Graph/Nodes/FlowGraphNode_Start.cpp | 2 ++ .../Private/Graph/Nodes/FlowGraphNode_SubGraph.cpp | 2 ++ Source/FlowEditor/Private/Nodes/FlowNodeBlueprintFactory.cpp | 2 ++ Source/FlowEditor/Public/Asset/FlowDebuggerSubsystem.h | 2 +- 57 files changed, 113 insertions(+), 10 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 97983f188..f0b7f3af0 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -21,6 +21,8 @@ #include "Editor.h" #include "Editor/EditorEngine.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowAsset) + FString UFlowAsset::ValidationError_NodeClassNotAllowed = TEXT("Node class {0} is not allowed in this asset."); FString UFlowAsset::ValidationError_NullNodeInstance = TEXT("Node with GUID {0} is NULL"); #endif diff --git a/Source/Flow/Private/FlowComponent.cpp b/Source/Flow/Private/FlowComponent.cpp index 2cc1651b3..4d2477d1a 100644 --- a/Source/Flow/Private/FlowComponent.cpp +++ b/Source/Flow/Private/FlowComponent.cpp @@ -15,6 +15,8 @@ #include "Serialization/MemoryReader.h" #include "Serialization/MemoryWriter.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowComponent) + UFlowComponent::UFlowComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , RootFlow(nullptr) diff --git a/Source/Flow/Private/FlowOwnerFunctionParams.cpp b/Source/Flow/Private/FlowOwnerFunctionParams.cpp index 5c4af749e..168e8187f 100644 --- a/Source/Flow/Private/FlowOwnerFunctionParams.cpp +++ b/Source/Flow/Private/FlowOwnerFunctionParams.cpp @@ -4,8 +4,7 @@ #include "Nodes/FlowNode.h" #include "Nodes/World/FlowNode_CallOwnerFunction.h" - -// UFlowOwnerFunctionParams Implementation +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowOwnerFunctionParams) UFlowOwnerFunctionParams::UFlowOwnerFunctionParams() : Super() @@ -13,7 +12,7 @@ UFlowOwnerFunctionParams::UFlowOwnerFunctionParams() #if WITH_EDITOR InputNames.Add(UFlowNode::DefaultInputPin.PinName); OutputNames.Add(UFlowNode::DefaultOutputPin.PinName); -#endif //WITH_EDITOR +#endif } void UFlowOwnerFunctionParams::PreExecute(UFlowNode_CallOwnerFunction& InSourceNode, const FName& InputPinName) diff --git a/Source/Flow/Private/FlowOwnerFunctionRef.cpp b/Source/Flow/Private/FlowOwnerFunctionRef.cpp index d49bfbf2a..98f87a0a1 100644 --- a/Source/Flow/Private/FlowOwnerFunctionRef.cpp +++ b/Source/Flow/Private/FlowOwnerFunctionRef.cpp @@ -8,8 +8,7 @@ #include "UObject/Class.h" #include "Logging/LogMacros.h" - -// FFlowOwnerFunctionRef Implementation +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowOwnerFunctionRef) UFunction* FFlowOwnerFunctionRef::TryResolveFunction(const UClass& InClass) { @@ -52,7 +51,7 @@ FName FFlowOwnerFunctionRef::CallFunction(IFlowOwnerInterface& InFlowOwnerInterf FName OutputPinName; }; - FFlowOwnerFunctionRef_Parms Parms = { &InParams, NAME_None }; + FFlowOwnerFunctionRef_Parms Parms = {&InParams, NAME_None}; // Call the owner function itself FlowOwnerObject->ProcessEvent(Function, &Parms); diff --git a/Source/Flow/Private/FlowSettings.cpp b/Source/Flow/Private/FlowSettings.cpp index 0e44fd35c..9463b94be 100644 --- a/Source/Flow/Private/FlowSettings.cpp +++ b/Source/Flow/Private/FlowSettings.cpp @@ -3,6 +3,8 @@ #include "FlowSettings.h" #include "FlowComponent.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowSettings) + UFlowSettings::UFlowSettings(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , bCreateFlowSubsystemOnClients(true) diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index 4f43e7250..ac14d0934 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -15,6 +15,8 @@ #include "Misc/Paths.h" #include "UObject/UObjectHash.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowSubsystem) + #if WITH_EDITOR FNativeFlowAssetEvent UFlowSubsystem::OnInstancedTemplateAdded; FNativeFlowAssetEvent UFlowSubsystem::OnInstancedTemplateRemoved; @@ -23,7 +25,7 @@ FNativeFlowAssetEvent UFlowSubsystem::OnInstancedTemplateRemoved; #define LOCTEXT_NAMESPACE "FlowSubsystem" UFlowSubsystem::UFlowSubsystem() - : UGameInstanceSubsystem() + : LoadedSaveGame(nullptr) { } diff --git a/Source/Flow/Private/FlowWorldSettings.cpp b/Source/Flow/Private/FlowWorldSettings.cpp index b0f03d6ec..e56c38926 100644 --- a/Source/Flow/Private/FlowWorldSettings.cpp +++ b/Source/Flow/Private/FlowWorldSettings.cpp @@ -3,6 +3,8 @@ #include "FlowWorldSettings.h" #include "FlowComponent.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowWorldSettings) + AFlowWorldSettings::AFlowWorldSettings(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { diff --git a/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp b/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp index 7af3e47e2..afc016165 100644 --- a/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp +++ b/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp @@ -4,6 +4,8 @@ #include "LevelSequence/FlowLevelSequencePlayer.h" #include "Net/UnrealNetwork.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowLevelSequenceActor) + AFlowLevelSequenceActor::AFlowLevelSequenceActor(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer.SetDefaultSubobjectClass("AnimationPlayer")) , ReplicatedLevelSequenceAsset(nullptr) diff --git a/Source/Flow/Private/LevelSequence/FlowLevelSequencePlayer.cpp b/Source/Flow/Private/LevelSequence/FlowLevelSequencePlayer.cpp index bc2223ad8..530ed0a51 100644 --- a/Source/Flow/Private/LevelSequence/FlowLevelSequencePlayer.cpp +++ b/Source/Flow/Private/LevelSequence/FlowLevelSequencePlayer.cpp @@ -6,6 +6,8 @@ #include "DefaultLevelSequenceInstanceData.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowLevelSequencePlayer) + UFlowLevelSequencePlayer::UFlowLevelSequencePlayer(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , FlowEventReceiver(nullptr) diff --git a/Source/Flow/Private/MovieScene/MovieSceneFlowRepeaterSection.cpp b/Source/Flow/Private/MovieScene/MovieSceneFlowRepeaterSection.cpp index 6aaaa8afc..5abdb2b83 100644 --- a/Source/Flow/Private/MovieScene/MovieSceneFlowRepeaterSection.cpp +++ b/Source/Flow/Private/MovieScene/MovieSceneFlowRepeaterSection.cpp @@ -1,3 +1,5 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "MovieScene/MovieSceneFlowRepeaterSection.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(MovieSceneFlowRepeaterSection) diff --git a/Source/Flow/Private/MovieScene/MovieSceneFlowSectionBase.cpp b/Source/Flow/Private/MovieScene/MovieSceneFlowSectionBase.cpp index 0016b8255..377a20a0f 100644 --- a/Source/Flow/Private/MovieScene/MovieSceneFlowSectionBase.cpp +++ b/Source/Flow/Private/MovieScene/MovieSceneFlowSectionBase.cpp @@ -1,3 +1,5 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "MovieScene/MovieSceneFlowSectionBase.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(MovieSceneFlowSectionBase) diff --git a/Source/Flow/Private/MovieScene/MovieSceneFlowTemplate.cpp b/Source/Flow/Private/MovieScene/MovieSceneFlowTemplate.cpp index 6e953a52c..8ac6bdfc5 100644 --- a/Source/Flow/Private/MovieScene/MovieSceneFlowTemplate.cpp +++ b/Source/Flow/Private/MovieScene/MovieSceneFlowTemplate.cpp @@ -7,6 +7,8 @@ #include "Evaluation/MovieSceneEvaluation.h" #include "IMovieScenePlayer.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(MovieSceneFlowTemplate) + #define LOCTEXT_NAMESPACE "MovieSceneFlowTemplate" DECLARE_CYCLE_STAT(TEXT("Flow Track Token Execute"), MovieSceneEval_FlowTrack_TokenExecute, STATGROUP_MovieSceneEval); diff --git a/Source/Flow/Private/MovieScene/MovieSceneFlowTrack.cpp b/Source/Flow/Private/MovieScene/MovieSceneFlowTrack.cpp index cb47fe513..24c4d6c76 100644 --- a/Source/Flow/Private/MovieScene/MovieSceneFlowTrack.cpp +++ b/Source/Flow/Private/MovieScene/MovieSceneFlowTrack.cpp @@ -8,6 +8,8 @@ #include "Evaluation/MovieSceneEvaluationTrack.h" #include "IMovieSceneTracksModule.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(MovieSceneFlowTrack) + #define LOCTEXT_NAMESPACE "MovieSceneFlowTrack" void UMovieSceneFlowTrack::AddSection(UMovieSceneSection& Section) diff --git a/Source/Flow/Private/MovieScene/MovieSceneFlowTriggerSection.cpp b/Source/Flow/Private/MovieScene/MovieSceneFlowTriggerSection.cpp index bc86dd921..ea43065e8 100644 --- a/Source/Flow/Private/MovieScene/MovieSceneFlowTriggerSection.cpp +++ b/Source/Flow/Private/MovieScene/MovieSceneFlowTriggerSection.cpp @@ -4,6 +4,8 @@ #include "Channels/MovieSceneChannelProxy.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(MovieSceneFlowTriggerSection) + UMovieSceneFlowTriggerSection::UMovieSceneFlowTriggerSection(const FObjectInitializer& ObjInit) : Super(ObjInit) { diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 13a876970..cac934225 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -25,6 +25,8 @@ #include "Serialization/MemoryWriter.h" #include "Engine/Blueprint.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode) + FFlowPin UFlowNode::DefaultInputPin(TEXT("In")); FFlowPin UFlowNode::DefaultOutputPin(TEXT("Out")); diff --git a/Source/Flow/Private/Nodes/FlowNodeBlueprint.cpp b/Source/Flow/Private/Nodes/FlowNodeBlueprint.cpp index 8d9b7969b..5cb028cb0 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBlueprint.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBlueprint.cpp @@ -2,6 +2,8 @@ #include "Nodes/FlowNodeBlueprint.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNodeBlueprint) + UFlowNodeBlueprint::UFlowNodeBlueprint(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { diff --git a/Source/Flow/Private/Nodes/FlowPin.cpp b/Source/Flow/Private/Nodes/FlowPin.cpp index 417ccf133..b6808ece0 100644 --- a/Source/Flow/Private/Nodes/FlowPin.cpp +++ b/Source/Flow/Private/Nodes/FlowPin.cpp @@ -4,6 +4,8 @@ #include "Misc/DateTime.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowPin) + ////////////////////////////////////////////////////////////////////////// // Pin Record diff --git a/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalAND.cpp b/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalAND.cpp index 753bed54a..fa85414c9 100644 --- a/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalAND.cpp +++ b/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalAND.cpp @@ -2,6 +2,8 @@ #include "Nodes/Operators/FlowNode_LogicalAND.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_LogicalAND) + UFlowNode_LogicalAND::UFlowNode_LogicalAND(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { diff --git a/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp b/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp index 402758b77..772cb2209 100644 --- a/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp +++ b/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp @@ -2,6 +2,8 @@ #include "Nodes/Operators/FlowNode_LogicalOR.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_LogicalOR) + UFlowNode_LogicalOR::UFlowNode_LogicalOR(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , bEnabled(true) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Counter.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Counter.cpp index 5a4eaf50c..4456c45ee 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Counter.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Counter.cpp @@ -2,6 +2,8 @@ #include "Nodes/Route/FlowNode_Counter.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_Counter) + UFlowNode_Counter::UFlowNode_Counter(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , Goal(2) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_CustomEventBase.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_CustomEventBase.cpp index 0b75bc43b..453673523 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_CustomEventBase.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_CustomEventBase.cpp @@ -3,6 +3,8 @@ #include "Nodes/Route/FlowNode_CustomEventBase.h" #include "FlowSettings.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_CustomEventBase) + UFlowNode_CustomEventBase::UFlowNode_CustomEventBase(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp index 2e1f594c8..1090a3310 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp @@ -3,6 +3,8 @@ #include "Nodes/Route/FlowNode_CustomInput.h" #include "FlowSettings.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_CustomInput) + #define LOCTEXT_NAMESPACE "FlowNode_CustomInput" UFlowNode_CustomInput::UFlowNode_CustomInput(const FObjectInitializer& ObjectInitializer) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp index 22224200c..26f647d89 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp @@ -4,6 +4,8 @@ #include "FlowAsset.h" #include "FlowSettings.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_CustomOutput) + #define LOCTEXT_NAMESPACE "FlowNode_CustomOutput" UFlowNode_CustomOutput::UFlowNode_CustomOutput(const FObjectInitializer& ObjectInitializer) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp index 455989751..e413813ed 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp @@ -2,6 +2,8 @@ #include "Nodes/Route/FlowNode_ExecutionMultiGate.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_ExecutionMultiGate) + UFlowNode_ExecutionMultiGate::UFlowNode_ExecutionMultiGate(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , StartIndex(INDEX_NONE) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp index 8c97852ec..56eda0348 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp @@ -2,6 +2,8 @@ #include "Nodes/Route/FlowNode_ExecutionSequence.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_ExecutionSequence) + UFlowNode_ExecutionSequence::UFlowNode_ExecutionSequence(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , bSavePinExecutionState(true) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp index 7001d8617..a32dffdd4 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp @@ -2,6 +2,8 @@ #include "Nodes/Route/FlowNode_Finish.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_Finish) + UFlowNode_Finish::UFlowNode_Finish(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Reroute.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Reroute.cpp index cf9d012d3..2c24b9900 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Reroute.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Reroute.cpp @@ -2,6 +2,8 @@ #include "Nodes/Route/FlowNode_Reroute.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_Reroute) + UFlowNode_Reroute::UFlowNode_Reroute(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp index 01fcae6fb..cd6fa6b97 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp @@ -2,6 +2,8 @@ #include "Nodes/Route/FlowNode_Start.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_Start) + UFlowNode_Start::UFlowNode_Start(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp index f82a76aeb..0f5f34a71 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp @@ -6,6 +6,8 @@ #include "FlowMessageLog.h" #include "FlowSubsystem.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_SubGraph) + FFlowPin UFlowNode_SubGraph::StartPin(TEXT("Start")); FFlowPin UFlowNode_SubGraph::FinishPin(TEXT("Finish")); diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp index 1a0dd7c03..87fcfc9d5 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp @@ -5,6 +5,8 @@ #include "Engine/World.h" #include "TimerManager.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_Timer) + UFlowNode_Timer::UFlowNode_Timer(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , CompletionTime(1.0f) diff --git a/Source/Flow/Private/Nodes/Utils/FlowNode_Checkpoint.cpp b/Source/Flow/Private/Nodes/Utils/FlowNode_Checkpoint.cpp index 1cdd9e280..f56715549 100644 --- a/Source/Flow/Private/Nodes/Utils/FlowNode_Checkpoint.cpp +++ b/Source/Flow/Private/Nodes/Utils/FlowNode_Checkpoint.cpp @@ -5,6 +5,8 @@ #include "Kismet/GameplayStatics.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_Checkpoint) + UFlowNode_Checkpoint::UFlowNode_Checkpoint(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { diff --git a/Source/Flow/Private/Nodes/Utils/FlowNode_Log.cpp b/Source/Flow/Private/Nodes/Utils/FlowNode_Log.cpp index 5da89a4e4..33e5f210f 100644 --- a/Source/Flow/Private/Nodes/Utils/FlowNode_Log.cpp +++ b/Source/Flow/Private/Nodes/Utils/FlowNode_Log.cpp @@ -5,6 +5,8 @@ #include "Engine/Engine.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_Log) + UFlowNode_Log::UFlowNode_Log(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , Message(TEXT("Log!")) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp b/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp index 73151f305..7cdc68ec2 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp @@ -3,6 +3,8 @@ #include "Nodes/World/FlowNode_ComponentObserver.h" #include "FlowSubsystem.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_ComponentObserver) + UFlowNode_ComponentObserver::UFlowNode_ComponentObserver(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , IdentityMatchType(EFlowTagContainerMatchType::HasAnyExact) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp b/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp index ab468e270..5b5733fb8 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp @@ -7,6 +7,8 @@ #include "Engine/GameInstance.h" #include "Engine/World.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_NotifyActor) + UFlowNode_NotifyActor::UFlowNode_NotifyActor(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , MatchType(EGameplayContainerMatchType::All) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_OnActorRegistered.cpp b/Source/Flow/Private/Nodes/World/FlowNode_OnActorRegistered.cpp index ba6a712ef..5ae82eeee 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_OnActorRegistered.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_OnActorRegistered.cpp @@ -2,6 +2,8 @@ #include "Nodes/World/FlowNode_OnActorRegistered.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_OnActorRegistered) + UFlowNode_OnActorRegistered::UFlowNode_OnActorRegistered(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { diff --git a/Source/Flow/Private/Nodes/World/FlowNode_OnActorUnregistered.cpp b/Source/Flow/Private/Nodes/World/FlowNode_OnActorUnregistered.cpp index 488e63b2f..988d4677b 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_OnActorUnregistered.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_OnActorUnregistered.cpp @@ -2,6 +2,8 @@ #include "Nodes/World/FlowNode_OnActorUnregistered.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_OnActorUnregistered) + UFlowNode_OnActorUnregistered::UFlowNode_OnActorUnregistered(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { diff --git a/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp b/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp index 477f1a1ed..8c32203a2 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp @@ -3,6 +3,8 @@ #include "Nodes/World/FlowNode_OnNotifyFromActor.h" #include "FlowComponent.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_OnNotifyFromActor) + UFlowNode_OnNotifyFromActor::UFlowNode_OnNotifyFromActor(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , bRetroactive(false) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp index 5edd3aee4..fe99d9ede 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp @@ -14,6 +14,8 @@ #include "Runtime/Launch/Resources/Version.h" #include "VisualLogger/VisualLogger.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_PlayLevelSequence) + FFlowNodeLevelSequenceEvent UFlowNode_PlayLevelSequence::OnPlaybackStarted; FFlowNodeLevelSequenceEvent UFlowNode_PlayLevelSequence::OnPlaybackCompleted; diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditorContext.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditorContext.cpp index 0d5501ddf..745571e60 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditorContext.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditorContext.cpp @@ -3,6 +3,8 @@ #include "Asset/FlowAssetEditorContext.h" #include "Asset/FlowAssetEditor.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowAssetEditorContext) + UFlowAsset* UFlowAssetEditorContext::GetFlowAsset() const { return FlowAssetEditor.IsValid() ? FlowAssetEditor.Pin()->GetFlowAsset() : nullptr; diff --git a/Source/FlowEditor/Private/Asset/FlowDebuggerSubsystem.cpp b/Source/FlowEditor/Private/Asset/FlowDebuggerSubsystem.cpp index 635882999..3bb324bb9 100644 --- a/Source/FlowEditor/Private/Asset/FlowDebuggerSubsystem.cpp +++ b/Source/FlowEditor/Private/Asset/FlowDebuggerSubsystem.cpp @@ -13,6 +13,8 @@ #include "UnrealEd.h" #include "Widgets/Notifications/SNotificationList.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowDebuggerSubsystem) + #define LOCTEXT_NAMESPACE "FlowDebuggerSubsystem" UFlowDebuggerSubsystem::UFlowDebuggerSubsystem() @@ -33,7 +35,7 @@ void UFlowDebuggerSubsystem::OnInstancedTemplateAdded(UFlowAsset* FlowAsset) } } -void UFlowDebuggerSubsystem::OnInstancedTemplateRemoved(UFlowAsset* FlowAsset) +void UFlowDebuggerSubsystem::OnInstancedTemplateRemoved(UFlowAsset* FlowAsset) const { FlowAsset->OnRuntimeMessageAdded().RemoveAll(this); } diff --git a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp index 5886b64f1..eaf7073b1 100644 --- a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp +++ b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp @@ -26,6 +26,8 @@ #include "K2Node_IfThenElse.h" #include "K2Node_Knot.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowImportUtils) + #define LOCTEXT_NAMESPACE "FlowImportUtils" TMap> UFlowImportUtils::FunctionsToFlowNodes = TMap>(); diff --git a/Source/FlowEditor/Private/Asset/FlowMessageLogListing.cpp b/Source/FlowEditor/Private/Asset/FlowMessageLogListing.cpp index 3ca8787e2..5c89ebe50 100644 --- a/Source/FlowEditor/Private/Asset/FlowMessageLogListing.cpp +++ b/Source/FlowEditor/Private/Asset/FlowMessageLogListing.cpp @@ -5,6 +5,8 @@ #include "MessageLogModule.h" #include "Modules/ModuleManager.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowMessageLogListing) + #define LOCTEXT_NAMESPACE "FlowMessageLogListing" FFlowMessageLogListing::FFlowMessageLogListing(const UFlowAsset* InFlowAsset, const EFlowLogType Type) diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomEventBaseDetails.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomEventBaseDetails.cpp index 3a8b96b99..b4ffa4bec 100644 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomEventBaseDetails.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomEventBaseDetails.cpp @@ -11,7 +11,6 @@ #include "Widgets/Text/STextBlock.h" #include "Widgets/SWidget.h" - void FFlowNode_CustomEventBaseDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) { // Subclasses must override this function (and call CustomizeDetailsInternal with the localized text) diff --git a/Source/FlowEditor/Private/Graph/FlowGraph.cpp b/Source/FlowEditor/Private/Graph/FlowGraph.cpp index 8520c8bf2..48e2b2d98 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraph.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraph.cpp @@ -10,6 +10,8 @@ #include "Editor.h" #include "Kismet2/BlueprintEditorUtils.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraph) + void FFlowGraphInterface::OnInputTriggered(UEdGraphNode* GraphNode, const int32 Index) const { CastChecked(GraphNode)->OnInputTriggered(Index); diff --git a/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp b/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp index 6a39a1658..805441b77 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp @@ -16,6 +16,8 @@ #include "Misc/App.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraphConnectionDrawingPolicy) + FConnectionDrawingPolicy* FFlowGraphConnectionDrawingPolicyFactory::CreateConnectionPolicy(const class UEdGraphSchema* Schema, int32 InBackLayerID, int32 InFrontLayerID, float ZoomFactor, const class FSlateRect& InClippingRect, class FSlateWindowElementList& InDrawElements, class UEdGraph* InGraphObj) const { if (Schema->IsA(UFlowGraphSchema::StaticClass())) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp index 33a7361d3..4bd2477fb 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp @@ -2,6 +2,8 @@ #include "Graph/FlowGraphEditorSettings.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraphEditorSettings) + UFlowGraphEditorSettings::UFlowGraphEditorSettings(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , NodeDoubleClickTarget(EFlowNodeDoubleClickTarget::PrimaryAssetOrNodeDefinition) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index 9bd5e672c..60f5097a9 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -22,6 +22,8 @@ #include "Editor.h" #include "ScopedTransaction.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraphSchema) + #define LOCTEXT_NAMESPACE "FlowGraphSchema" bool UFlowGraphSchema::bInitialGatherPerformed = false; diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp index fcc34b9b9..7b81daed2 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp @@ -16,6 +16,8 @@ #include "Editor.h" #include "ScopedTransaction.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraphSchema_Actions) + #define LOCTEXT_NAMESPACE "FlowGraphSchema_Actions" ///////////////////////////////////////////////////// diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp index 7c54658fb..69587ba58 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp @@ -4,6 +4,8 @@ #include "FlowAsset.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraphSettings) + #define LOCTEXT_NAMESPACE "FlowGraphSettings" UFlowGraphSettings::UFlowGraphSettings(const FObjectInitializer& ObjectInitializer) diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 033fa3b8e..d87dfa694 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -27,6 +27,8 @@ #include "Textures/SlateIcon.h" #include "ToolMenuSection.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraphNode) + #define LOCTEXT_NAMESPACE "FlowGraphNode" UFlowGraphNode::UFlowGraphNode(const FObjectInitializer& ObjectInitializer) diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_ExecutionSequence.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_ExecutionSequence.cpp index 5ba3e06f2..bcea74fe1 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_ExecutionSequence.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_ExecutionSequence.cpp @@ -6,6 +6,8 @@ #include "Textures/SlateIcon.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraphNode_ExecutionSequence) + UFlowGraphNode_ExecutionSequence::UFlowGraphNode_ExecutionSequence(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Finish.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Finish.cpp index 1092260e4..cfe7600cf 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Finish.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Finish.cpp @@ -5,6 +5,8 @@ #include "Nodes/Route/FlowNode_Finish.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraphNode_Finish) + UFlowGraphNode_Finish::UFlowGraphNode_Finish(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Reroute.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Reroute.cpp index cac81f6d4..90fca7064 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Reroute.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Reroute.cpp @@ -5,6 +5,8 @@ #include "Nodes/Route/FlowNode_Reroute.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraphNode_Reroute) + UFlowGraphNode_Reroute::UFlowGraphNode_Reroute(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Start.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Start.cpp index 19fde8ba8..9169a33b4 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Start.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Start.cpp @@ -5,6 +5,8 @@ #include "Nodes/Route/FlowNode_Start.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraphNode_Start) + UFlowGraphNode_Start::UFlowGraphNode_Start(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_SubGraph.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_SubGraph.cpp index 2e22bc80b..85757e71b 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_SubGraph.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_SubGraph.cpp @@ -5,6 +5,8 @@ #include "Nodes/Route/FlowNode_SubGraph.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraphNode_SubGraph) + UFlowGraphNode_SubGraph::UFlowGraphNode_SubGraph(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { diff --git a/Source/FlowEditor/Private/Nodes/FlowNodeBlueprintFactory.cpp b/Source/FlowEditor/Private/Nodes/FlowNodeBlueprintFactory.cpp index 751c8e6ca..f23237513 100644 --- a/Source/FlowEditor/Private/Nodes/FlowNodeBlueprintFactory.cpp +++ b/Source/FlowEditor/Private/Nodes/FlowNodeBlueprintFactory.cpp @@ -21,6 +21,8 @@ #include "Widgets/SCompoundWidget.h" #include "Widgets/SWindow.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNodeBlueprintFactory) + #define LOCTEXT_NAMESPACE "FlowNodeBlueprintFactory" // ------------------------------------------------------------------------------ diff --git a/Source/FlowEditor/Public/Asset/FlowDebuggerSubsystem.h b/Source/FlowEditor/Public/Asset/FlowDebuggerSubsystem.h index dae10ebae..0200ba010 100644 --- a/Source/FlowEditor/Public/Asset/FlowDebuggerSubsystem.h +++ b/Source/FlowEditor/Public/Asset/FlowDebuggerSubsystem.h @@ -24,7 +24,7 @@ class FLOWEDITOR_API UFlowDebuggerSubsystem : public UEditorSubsystem TMap, TSharedPtr> RuntimeLogs; void OnInstancedTemplateAdded(UFlowAsset* FlowAsset); - void OnInstancedTemplateRemoved(UFlowAsset* FlowAsset); + void OnInstancedTemplateRemoved(UFlowAsset* FlowAsset) const; void OnRuntimeMessageAdded(UFlowAsset* FlowAsset, const TSharedRef& Message) const; From 07adb6e9ce4bfd936dfddeacee6a40bad853f83d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 28 Jan 2024 16:41:42 +0100 Subject: [PATCH 220/485] removed redundant includes Wrapped editor-only inluces with the `WITH_EDITOR`. Moved a few includes to .cpp files. Added forward declaration where needed. --- Source/Flow/Private/FlowAsset.cpp | 7 +++++-- Source/Flow/Private/FlowMessageLog.cpp | 5 +++-- Source/Flow/Private/FlowOwnerFunctionParams.cpp | 1 - Source/Flow/Private/FlowOwnerFunctionRef.cpp | 2 +- Source/Flow/Private/Nodes/FlowNode.cpp | 11 +++++------ .../Nodes/World/FlowNode_PlayLevelSequence.cpp | 3 +++ Source/Flow/Public/FlowAsset.h | 5 ++++- Source/Flow/Public/FlowMessageLog.h | 3 +-- Source/Flow/Public/FlowOwnerFunctionParams.h | 8 +------- Source/Flow/Public/FlowOwnerFunctionRef.h | 4 ---- Source/Flow/Public/FlowOwnerInterface.h | 3 --- .../LevelSequence/FlowLevelSequenceActor.h | 2 ++ Source/Flow/Public/Nodes/FlowNode.h | 3 ++- Source/Flow/Public/Nodes/FlowNodeBlueprint.h | 2 -- .../Private/Asset/FlowAssetFactory.cpp | 2 +- .../FlowOwnerFunctionRefCustomization.cpp | 6 ------ .../Private/Graph/FlowGraphEditor.cpp | 3 +++ .../Private/MovieScene/FlowTrackEditor.cpp | 1 - .../Private/Utils/SLevelEditorFlow.cpp | 3 +-- .../Public/Asset/FlowAssetEditorContext.h | 1 + .../FlowEditor/Public/Asset/FlowAssetIndexer.h | 1 + Source/FlowEditor/Public/Asset/SFlowDiff.h | 1 - .../FlowOwnerFunctionRefCustomization.h | 17 +++++++---------- .../FlowEditor/Public/Graph/FlowGraphEditor.h | 3 --- .../FlowEditor/Public/Utils/SLevelEditorFlow.h | 5 ++--- 25 files changed, 43 insertions(+), 59 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index f0b7f3af0..41a2de2d8 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -2,8 +2,6 @@ #include "FlowAsset.h" -#include "FlowMessageLog.h" -#include "FlowModule.h" #include "FlowSettings.h" #include "FlowSubsystem.h" @@ -18,11 +16,16 @@ #include "Serialization/MemoryWriter.h" #if WITH_EDITOR +#include "FlowMessageLog.h" +#include "FlowModule.h" + #include "Editor.h" #include "Editor/EditorEngine.h" +#endif #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowAsset) +#if WITH_EDITOR FString UFlowAsset::ValidationError_NodeClassNotAllowed = TEXT("Node class {0} is not allowed in this asset."); FString UFlowAsset::ValidationError_NullNodeInstance = TEXT("Node with GUID {0} is NULL"); #endif diff --git a/Source/Flow/Private/FlowMessageLog.cpp b/Source/Flow/Private/FlowMessageLog.cpp index f4e1ef621..f6e171af9 100644 --- a/Source/Flow/Private/FlowMessageLog.cpp +++ b/Source/Flow/Private/FlowMessageLog.cpp @@ -1,10 +1,10 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "FlowMessageLog.h" -#include "Nodes/FlowNode.h" -#include "FlowAsset.h" #if WITH_EDITOR +#include "Nodes/FlowNode.h" +#include "FlowAsset.h" #define LOCTEXT_NAMESPACE "FlowMessageLog" @@ -84,4 +84,5 @@ TSharedPtr FFlowGraphToken::Create(const UEdGraphPin* InPin, FTok } #undef LOCTEXT_NAMESPACE + #endif // WITH_EDITOR diff --git a/Source/Flow/Private/FlowOwnerFunctionParams.cpp b/Source/Flow/Private/FlowOwnerFunctionParams.cpp index 168e8187f..666c24eb5 100644 --- a/Source/Flow/Private/FlowOwnerFunctionParams.cpp +++ b/Source/Flow/Private/FlowOwnerFunctionParams.cpp @@ -7,7 +7,6 @@ #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowOwnerFunctionParams) UFlowOwnerFunctionParams::UFlowOwnerFunctionParams() - : Super() { #if WITH_EDITOR InputNames.Add(UFlowNode::DefaultInputPin.PinName); diff --git a/Source/Flow/Private/FlowOwnerFunctionRef.cpp b/Source/Flow/Private/FlowOwnerFunctionRef.cpp index 98f87a0a1..8d5b54621 100644 --- a/Source/Flow/Private/FlowOwnerFunctionRef.cpp +++ b/Source/Flow/Private/FlowOwnerFunctionRef.cpp @@ -5,8 +5,8 @@ #include "FlowModule.h" #include "FlowOwnerInterface.h" -#include "UObject/Class.h" #include "Logging/LogMacros.h" +#include "UObject/Class.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowOwnerFunctionRef) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index cac934225..6f67bcf3b 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -10,10 +10,6 @@ #include "FlowTypes.h" #include "Components/ActorComponent.h" -#if WITH_EDITOR -#include "Editor.h" -#endif - #include "Engine/Blueprint.h" #include "Engine/Engine.h" #include "Engine/ViewportStatsSubsystem.h" @@ -23,7 +19,10 @@ #include "Misc/Paths.h" #include "Serialization/MemoryReader.h" #include "Serialization/MemoryWriter.h" -#include "Engine/Blueprint.h" + +#if WITH_EDITOR +#include "Editor.h" +#endif #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode) @@ -37,8 +36,8 @@ FString UFlowNode::NoActorsFound = TEXT("No actors found"); UFlowNode::UFlowNode(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) -#if WITH_EDITOR , GraphNode(nullptr) +#if WITH_EDITOR , bCanDelete(true) , bCanDuplicate(true) , bNodeDeprecated(false) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp index fe99d9ede..f3c010205 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp @@ -6,8 +6,11 @@ #include "FlowModule.h" #include "FlowSubsystem.h" #include "LevelSequence/FlowLevelSequencePlayer.h" + +#if WITH_EDITOR #include "MovieScene/MovieSceneFlowTrack.h" #include "MovieScene/MovieSceneFlowTriggerSection.h" +#endif #include "LevelSequence.h" #include "LevelSequenceActor.h" diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 11a981c92..9d3199243 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -2,11 +2,14 @@ #pragma once -#include "FlowMessageLog.h" #include "FlowSave.h" #include "FlowTypes.h" #include "Nodes/FlowNode.h" +#if WITH_EDITOR +#include "FlowMessageLog.h" +#endif + #include "UObject/ObjectKey.h" #include "FlowAsset.generated.h" diff --git a/Source/Flow/Public/FlowMessageLog.h b/Source/Flow/Public/FlowMessageLog.h index 58ad0495b..7e11cafb9 100644 --- a/Source/Flow/Public/FlowMessageLog.h +++ b/Source/Flow/Public/FlowMessageLog.h @@ -2,6 +2,7 @@ #pragma once +#if WITH_EDITOR #include "EdGraph/EdGraphNode.h" #include "EdGraph/EdGraphPin.h" #include "Logging/TokenizedMessage.h" @@ -10,8 +11,6 @@ class UFlowAsset; class UFlowNode; -#if WITH_EDITOR - /** * Message Log token that links to an element in Flow Graph */ diff --git a/Source/Flow/Public/FlowOwnerFunctionParams.h b/Source/Flow/Public/FlowOwnerFunctionParams.h index f627fe875..1995c8c66 100644 --- a/Source/Flow/Public/FlowOwnerFunctionParams.h +++ b/Source/Flow/Public/FlowOwnerFunctionParams.h @@ -6,18 +6,14 @@ #include "FlowOwnerFunctionParams.generated.h" - -// Forward Declarations class UFlowNode_CallOwnerFunction; - UCLASS(BlueprintType, Blueprintable, EditInlineNew) class FLOW_API UFlowOwnerFunctionParams : public UObject { GENERATED_BODY() - -public: +public: UFlowOwnerFunctionParams(); void PreExecute(UFlowNode_CallOwnerFunction& InSourceNode, const FName& InputPinName); @@ -38,7 +34,6 @@ class FLOW_API UFlowOwnerFunctionParams : public UObject TArray GatherOutputNames() const { return BP_GetOutputNames(); } protected: - // Called prior to the owner executing the function described by this object. // Can be overridden to prepare the stateful data before execution. UFUNCTION(BlueprintImplementableEvent, Category = "FlowOwnerFunction", DisplayName = "PreExecute") @@ -60,7 +55,6 @@ class FLOW_API UFlowOwnerFunctionParams : public UObject TArray BP_GetOutputNames() const; protected: - // CallOwnerObjectFunction node that is executing this set of function params. // Valid only if called between PreExecute() and PostExecute(), inclusive UPROPERTY(Transient, BlueprintReadOnly, Category = "FlowOwnerFunction") diff --git a/Source/Flow/Public/FlowOwnerFunctionRef.h b/Source/Flow/Public/FlowOwnerFunctionRef.h index 629949c8d..e3f5c5e80 100644 --- a/Source/Flow/Public/FlowOwnerFunctionRef.h +++ b/Source/Flow/Public/FlowOwnerFunctionRef.h @@ -2,17 +2,13 @@ #pragma once -#include "CoreMinimal.h" #include "Templates/SubclassOf.h" #include "FlowOwnerFunctionRef.generated.h" - -// Forward Declarations class UFlowOwnerFunctionParams; class IFlowOwnerInterface; - // Similar to FAnimNodeFunctionRef, providing a FName-based function binding // that is resolved at runtime USTRUCT(BlueprintType) diff --git a/Source/Flow/Public/FlowOwnerInterface.h b/Source/Flow/Public/FlowOwnerInterface.h index a6ad53675..28a00f5e0 100644 --- a/Source/Flow/Public/FlowOwnerInterface.h +++ b/Source/Flow/Public/FlowOwnerInterface.h @@ -2,13 +2,10 @@ #pragma once -#include "CoreMinimal.h" - #include "UObject/Interface.h" #include "FlowOwnerInterface.generated.h" - // (optional) interface to enable a Flow owner object to execute CallOwnerFunction nodes UINTERFACE(MinimalAPI, Blueprintable, BlueprintType) class UFlowOwnerInterface : public UInterface diff --git a/Source/Flow/Public/LevelSequence/FlowLevelSequenceActor.h b/Source/Flow/Public/LevelSequence/FlowLevelSequenceActor.h index 9a74c4081..506dafb40 100644 --- a/Source/Flow/Public/LevelSequence/FlowLevelSequenceActor.h +++ b/Source/Flow/Public/LevelSequence/FlowLevelSequenceActor.h @@ -5,6 +5,8 @@ #include "LevelSequenceActor.h" #include "FlowLevelSequenceActor.generated.h" +class ULevelSequence; + /** * Custom ALevelSequenceActor is needed to override ULevelSequencePlayer class */ diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index bc6ff925a..7778011c5 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -12,9 +12,10 @@ #include "Nodes/FlowPin.h" #include "FlowNode.generated.h" +class IFlowOwnerInterface; class UFlowAsset; class UFlowSubsystem; -class IFlowOwnerInterface; +struct FFlowNodeSaveData; #if WITH_EDITOR DECLARE_DELEGATE(FFlowNodeEvent); diff --git a/Source/Flow/Public/Nodes/FlowNodeBlueprint.h b/Source/Flow/Public/Nodes/FlowNodeBlueprint.h index d939925b6..26b2fc5f2 100644 --- a/Source/Flow/Public/Nodes/FlowNodeBlueprint.h +++ b/Source/Flow/Public/Nodes/FlowNodeBlueprint.h @@ -2,7 +2,6 @@ #pragma once -#include "CoreMinimal.h" #include "Engine/Blueprint.h" #include "FlowNodeBlueprint.generated.h" @@ -17,7 +16,6 @@ class FLOW_API UFlowNodeBlueprint : public UBlueprint #if WITH_EDITOR // UBlueprint virtual bool SupportedByDefaultBlueprintFactory() const override { return false; } - virtual bool SupportsDelegates() const override { return false; } // -- #endif diff --git a/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp b/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp index c0900979a..d78daa59f 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp @@ -13,7 +13,7 @@ #define LOCTEXT_NAMESPACE "FlowAssetFactory" -class FAssetClassParentFilter : public IClassViewerFilter +class FAssetClassParentFilter final : public IClassViewerFilter { public: FAssetClassParentFilter() diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowOwnerFunctionRefCustomization.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowOwnerFunctionRefCustomization.cpp index d6afd481c..c8e1379cd 100644 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowOwnerFunctionRefCustomization.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowOwnerFunctionRefCustomization.cpp @@ -2,16 +2,10 @@ #include "DetailCustomizations/FlowOwnerFunctionRefCustomization.h" -#include "FlowAsset.h" -#include "FlowOwnerInterface.h" #include "Nodes/FlowNode.h" #include "Nodes/World/FlowNode_CallOwnerFunction.h" #include "UObject/UnrealType.h" -#include "FlowOwnerFunctionParams.h" - - -// FFlowOwnerFunctionRefCustomization Implementation void FFlowOwnerFunctionRefCustomization::CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) { diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index 5cf337160..32d3624e4 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -12,12 +12,15 @@ #include "Nodes/Route/FlowNode_SubGraph.h" +#include "EdGraphUtilities.h" #include "Framework/Commands/GenericCommands.h" +#include "GraphEditorActions.h" #include "HAL/PlatformApplicationMisc.h" #include "IDetailsView.h" #include "LevelEditor.h" #include "Modules/ModuleManager.h" #include "ScopedTransaction.h" +#include "SNodePanel.h" #include "Widgets/Docking/SDockTab.h" #define LOCTEXT_NAMESPACE "FlowGraphEditor" diff --git a/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp b/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp index f80304db0..277765d34 100644 --- a/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp +++ b/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp @@ -7,7 +7,6 @@ #include "MovieScene/MovieSceneFlowTrack.h" #include "MovieScene/MovieSceneFlowTriggerSection.h" -#include "Components/HorizontalBox.h" #include "Framework/MultiBox/MultiBoxBuilder.h" #include "ISequencerSection.h" #include "LevelSequence.h" diff --git a/Source/FlowEditor/Private/Utils/SLevelEditorFlow.cpp b/Source/FlowEditor/Private/Utils/SLevelEditorFlow.cpp index 25c01b159..6179af939 100644 --- a/Source/FlowEditor/Private/Utils/SLevelEditorFlow.cpp +++ b/Source/FlowEditor/Private/Utils/SLevelEditorFlow.cpp @@ -3,7 +3,6 @@ #include "Utils/SLevelEditorFlow.h" #include "FlowAsset.h" #include "FlowComponent.h" -#include "FlowWorldSettings.h" #include "Graph/FlowGraphSettings.h" @@ -74,7 +73,7 @@ void SLevelEditorFlow::OnFlowChanged(const FAssetData& NewAsset) } } -UFlowComponent* SLevelEditorFlow::FindFlowComponent() const +UFlowComponent* SLevelEditorFlow::FindFlowComponent() { if (const UWorld* World = GEditor->GetEditorWorldContext().World()) { diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditorContext.h b/Source/FlowEditor/Public/Asset/FlowAssetEditorContext.h index f26d03ca4..89c628aa7 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditorContext.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditorContext.h @@ -3,6 +3,7 @@ #pragma once #include "CoreMinimal.h" + #include "FlowAssetEditorContext.generated.h" class UFlowAsset; diff --git a/Source/FlowEditor/Public/Asset/FlowAssetIndexer.h b/Source/FlowEditor/Public/Asset/FlowAssetIndexer.h index acac56d80..12d6d316a 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetIndexer.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetIndexer.h @@ -3,6 +3,7 @@ #pragma once #include "CoreMinimal.h" + #include "IAssetIndexer.h" class UFlowAsset; diff --git a/Source/FlowEditor/Public/Asset/SFlowDiff.h b/Source/FlowEditor/Public/Asset/SFlowDiff.h index 4999cb14a..b9ac9e6d9 100644 --- a/Source/FlowEditor/Public/Asset/SFlowDiff.h +++ b/Source/FlowEditor/Public/Asset/SFlowDiff.h @@ -2,7 +2,6 @@ #pragma once -#include "Graph/FlowGraphConnectionDrawingPolicy.h" #include "IDetailsView.h" #include "DiffResults.h" #include "SDetailsDiff.h" diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowOwnerFunctionRefCustomization.h b/Source/FlowEditor/Public/DetailCustomizations/FlowOwnerFunctionRefCustomization.h index 6754cf829..1bdc1c979 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowOwnerFunctionRefCustomization.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowOwnerFunctionRefCustomization.h @@ -6,8 +6,6 @@ #include "FlowOwnerFunctionRef.h" - -// Forward Declaration class UFlowAsset; class UFlowNode; class UObject; @@ -15,8 +13,6 @@ class UClass; class UFunction; class UFlowNode_CallOwnerFunction; - -// Details customization for FFlowOwnerFunctionRef class FFlowOwnerFunctionRefCustomization : public IFlowCuratedNamePropertyCustomization { private: @@ -26,21 +22,22 @@ class FFlowOwnerFunctionRefCustomization : public IFlowCuratedNamePropertyCustom static TSharedRef MakeInstance() { return MakeShareable(new FFlowOwnerFunctionRefCustomization()); } protected: - - //~Begin IPropertyTypeCustomization + // IPropertyTypeCustomization virtual void CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; - //~End IPropertyTypeCustomization + // --- - //~Begin ICuratedNamePropertyCustomization + // ICuratedNamePropertyCustomization virtual TSharedPtr GetCuratedNamePropertyHandle() const override; virtual void SetCuratedName(const FName& NewName) override; virtual FName GetCuratedName() const override; virtual TArray GetCuratedNameOptions() const override; - //~End ICuratedNamePropertyCustomization + // --- // Accessor to return the actual struct being edited FORCEINLINE FFlowOwnerFunctionRef* GetFlowOwnerFunctionRef() const - { return IFlowExtendedPropertyTypeCustomization::TryGetTypedStructValue(StructPropertyHandle); } + { + return IFlowExtendedPropertyTypeCustomization::TryGetTypedStructValue(StructPropertyHandle); + } const UClass* TryGetExpectedOwnerClass() const; UFlowNode* TryGetFlowNodeOuter() const; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h index 51bd1af38..57918c877 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h @@ -2,10 +2,7 @@ #pragma once -#include "EdGraphUtilities.h" #include "GraphEditor.h" -#include "GraphEditorActions.h" -#include "SGraphNode.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "FlowGraph.h" diff --git a/Source/FlowEditor/Public/Utils/SLevelEditorFlow.h b/Source/FlowEditor/Public/Utils/SLevelEditorFlow.h index ba7c93de1..5d842eaa3 100644 --- a/Source/FlowEditor/Public/Utils/SLevelEditorFlow.h +++ b/Source/FlowEditor/Public/Utils/SLevelEditorFlow.h @@ -2,7 +2,6 @@ #pragma once -#include "CoreMinimal.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "Widgets/SCompoundWidget.h" @@ -22,8 +21,8 @@ class FLOWEDITOR_API SLevelEditorFlow : public SCompoundWidget FString GetFlowAssetPath() const; void OnFlowChanged(const FAssetData& NewAsset); - - class UFlowComponent* FindFlowComponent() const; + + static class UFlowComponent* FindFlowComponent(); FString FlowAssetPath; }; From a824eb4c932daa7a7f9c1c06b48047be55c2687e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 28 Jan 2024 16:42:22 +0100 Subject: [PATCH 221/485] Quick cosmetic pass on headers, especially a new classes from pull requests Mostly just updated class layout to this plugin convention. --- .../World/FlowNode_CallOwnerFunction.cpp | 264 +++++++++--------- .../Nodes/World/FlowNode_CallOwnerFunction.h | 88 +++--- .../IFlowCuratedNamePropertyCustomization.cpp | 60 ++-- ...IFlowExtendedPropertyTypeCustomization.cpp | 8 +- .../IFlowCuratedNamePropertyCustomization.h | 25 +- .../IFlowExtendedPropertyTypeCustomization.h | 21 +- 6 files changed, 217 insertions(+), 249 deletions(-) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp b/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp index 19ebc8b3c..9f76d55f9 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp @@ -7,18 +7,18 @@ #include "FlowOwnerFunctionParams.h" #include "FlowSettings.h" +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_CallOwnerFunction) #define LOCTEXT_NAMESPACE "FlowNode" -// UFlowNode_CallOwnerFunction Implementation - -UFlowNode_CallOwnerFunction::UFlowNode_CallOwnerFunction() - : Super() +UFlowNode_CallOwnerFunction::UFlowNode_CallOwnerFunction(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) + , Params(nullptr) { #if WITH_EDITOR NodeStyle = EFlowNodeStyle::Default; Category = TEXT("World"); -#endif // WITH_EDITOR +#endif } void UFlowNode_CallOwnerFunction::ExecuteInput(const FName& PinName) @@ -40,8 +40,8 @@ void UFlowNode_CallOwnerFunction::ExecuteInput(const FName& PinName) return; } - UObject* FlowOwnerObject = CastChecked(FlowOwnerInterface); - UClass* FlowOwnerClass = FlowOwnerObject->GetClass(); + const UObject* FlowOwnerObject = CastChecked(FlowOwnerInterface); + const UClass* FlowOwnerClass = FlowOwnerObject->GetClass(); check(IsValid(FlowOwnerClass)); if (!FunctionRef.TryResolveFunction(*FlowOwnerClass)) @@ -62,7 +62,7 @@ void UFlowNode_CallOwnerFunction::ExecuteInput(const FName& PinName) Params->PostExecute(); - (void) TryExecuteOutputPin(ResultOutputName); + (void)TryExecuteOutputPin(ResultOutputName); } bool UFlowNode_CallOwnerFunction::TryExecuteOutputPin(const FName& OutputName) @@ -98,9 +98,9 @@ void UFlowNode_CallOwnerFunction::PostLoad() check(ParamsProperty); // NOTE (gtaylor) This fixes corruption in FlowNodes that could have been caused with - // a previous version of the code (which was inadvisedly calling SetPropertyClass) - // to restore the correct PropertyClass for this node. - // (it could be removed in a future release, once all assets have been updated) + // a previous version of the code (which was inadvisedly calling SetPropertyClass) + // to restore the correct PropertyClass for this node. + // (it could be removed in a future release, once all assets have been updated) if (ParamsProperty->PropertyClass != UFlowOwnerFunctionParams::StaticClass()) { ParamsProperty->SetPropertyClass(UFlowOwnerFunctionParams::StaticClass()); @@ -145,6 +145,37 @@ void UFlowNode_CallOwnerFunction::PostEditChangeProperty(struct FPropertyChanged } } +bool UFlowNode_CallOwnerFunction::TryAllocateParamsInstance() +{ + if (FunctionRef.GetFunctionName().IsNone()) + { + // Throw out the old params object (if any) + Params = nullptr; + + return false; + } + + const UClass* ExistingParamsClass = GetExistingParamsClass(); + const UClass* RequiredParamsClass = GetRequiredParamsClass(); + + const bool bNeedsAllocateParams = + !IsValid(ExistingParamsClass) || + ExistingParamsClass != RequiredParamsClass; + + if (!bNeedsAllocateParams) + { + return false; + } + + // Throw out the old params object (if any) + Params = nullptr; + + // Create the new params object + Params = NewObject(this, RequiredParamsClass); + + return true; +} + void UFlowNode_CallOwnerFunction::OnChangedParamsObject() { bool bChangedPins = false; @@ -168,7 +199,7 @@ void UFlowNode_CallOwnerFunction::OnChangedParamsObject() bool UFlowNode_CallOwnerFunction::RebuildPinArray(const TArray& NewPinNames, TArray& InOutPins, const FFlowPin& DefaultPin) { - bool bIsChanged = false; + bool bIsChanged; TArray NewPins; @@ -221,76 +252,83 @@ bool UFlowNode_CallOwnerFunction::RebuildPinArray(const TArray& NewPinNam return bIsChanged; } -EDataValidationResult UFlowNode_CallOwnerFunction::ValidateNode() +UClass* UFlowNode_CallOwnerFunction::GetRequiredParamsClass() const { - const bool bHasFunction = FunctionRef.IsConfigured(); - if (!bHasFunction) + const UClass* ExpectedOwnerClass = TryGetExpectedOwnerClass(); + if (!IsValid(ExpectedOwnerClass)) { - ValidationLog.Error(TEXT("CallOwnerFunction requires a valid Function reference"), this); - - return EDataValidationResult::Invalid; + return UFlowOwnerFunctionParams::StaticClass(); } - const bool bHasParams = IsValid(Params); - if (!bHasParams) - { - ValidationLog.Error(TEXT("CallOwnerFunction requires a valid Params object"), this); + const FName FunctionNameAsName = FunctionRef.GetFunctionName(); - return EDataValidationResult::Invalid; + if (FunctionNameAsName.IsNone()) + { + return UFlowOwnerFunctionParams::StaticClass(); } - checkf(bHasParams && bHasFunction, TEXT("This should be assured by the preceding logic")); + UClass* RequiredParamsClass = GetParamsClassForFunctionName(*ExpectedOwnerClass, FunctionNameAsName); + return RequiredParamsClass; +} - const UClass* ExpectedOwnerClass = TryGetExpectedOwnerClass(); - if (!IsValid(ExpectedOwnerClass)) +UClass* UFlowNode_CallOwnerFunction::GetExistingParamsClass() const +{ + if (!IsValid(Params)) { - ValidationLog.Error(TEXT("Invalid or null Expected Owner Class for this Flow Asset"), this); - - return EDataValidationResult::Invalid; + return nullptr; } - // Check if the function can be found on the expected owner - const UFunction* Function = FunctionRef.TryResolveFunction(*ExpectedOwnerClass); - if (!IsValid(Function)) - { - ValidationLog.Error(TEXT("Could not resolve function for flow owner"), this); - - return EDataValidationResult::Invalid; - } + UClass* ExistingParamsClass = Params->GetClass(); + return ExistingParamsClass; +} - // Check the function signature - if (!DoesFunctionHaveValidFlowOwnerFunctionSignature(*Function)) +UClass* UFlowNode_CallOwnerFunction::GetParamsClassForFunctionName(const UClass& ExpectedOwnerClass, const FName& FunctionName) +{ + const UFunction* Function = ExpectedOwnerClass.FindFunctionByName(FunctionName); + if (IsValid(Function)) { - ValidationLog.Error(TEXT("Flow Owner Function has an invalid signature"), this); - - return EDataValidationResult::Invalid; + return GetParamsClassForFunction(*Function); } - const UClass* RequiredParamsClass = GetRequiredParamsClass(); - checkf(IsValid(RequiredParamsClass), TEXT("GetRequiredParamsClass() cannot return null if DoesFunctionHaveValidFlowOwnerFunctionSignature() is true")); + return nullptr; +} - const UClass* ExistingParamsClass = GetExistingParamsClass(); - checkf(IsValid(ExistingParamsClass), TEXT("This should be assured, if bHasParams == true")); +FText UFlowNode_CallOwnerFunction::GetNodeTitle() const +{ + const bool bUseAdaptiveNodeTitles = UFlowSettings::Get()->bUseAdaptiveNodeTitles; - // Check if the params (existing) are compatible with the function's expected (required) params - if (!ExistingParamsClass->IsChildOf(RequiredParamsClass)) + if (bUseAdaptiveNodeTitles && !FunctionRef.GetFunctionName().IsNone()) { - ValidationLog.Error(TEXT("Params object is not of the correct type for the flow owner function"), this); + const FText FunctionNameText = FText::FromName(FunctionRef.FunctionName); - return EDataValidationResult::Invalid; + return FText::Format(LOCTEXT("CallOwnerFunction", "Call {0}"), {FunctionNameText}); + } + else + { + return Super::GetNodeTitle(); } - - return EDataValidationResult::Valid; } -FString UFlowNode_CallOwnerFunction::GetStatusString() const +bool UFlowNode_CallOwnerFunction::IsAcceptableParamsPropertyClass(const UClass* ParamsClass) const { - if (ActivationState != EFlowNodeState::NeverActivated) + if (!IsValid(ParamsClass)) { - return UEnum::GetDisplayValueAsText(ActivationState).ToString(); + return false; } - return Super::GetStatusString(); + if (!ParamsClass->IsChildOf()) + { + return false; + } + + const UClass* ExistingParamsClass = GetExistingParamsClass(); + + if (IsValid(ExistingParamsClass) && ParamsClass != ExistingParamsClass) + { + return false; + } + + return true; } UClass* UFlowNode_CallOwnerFunction::TryGetExpectedOwnerClass() const @@ -329,9 +367,7 @@ bool UFlowNode_CallOwnerFunction::DoesFunctionHaveNameReturnType(const UFunction while (Iterator) { - const bool bIsOutParm = EnumHasAllFlags(Iterator->PropertyFlags, CPF_Parm | CPF_OutParm); - - return bIsOutParm; + return EnumHasAllFlags(Iterator->PropertyFlags, CPF_Parm | CPF_OutParm); } return false; @@ -374,114 +410,76 @@ UClass* UFlowNode_CallOwnerFunction::GetParamsClassForFunction(const UFunction& return nullptr; } -UClass* UFlowNode_CallOwnerFunction::GetParamsClassForFunctionName(const UClass& ExpectedOwnerClass, const FName& FunctionName) +FString UFlowNode_CallOwnerFunction::GetStatusString() const { - const UFunction* Function = ExpectedOwnerClass.FindFunctionByName(FunctionName); - if (IsValid(Function)) + if (ActivationState != EFlowNodeState::NeverActivated) { - return GetParamsClassForFunction(*Function); + return UEnum::GetDisplayValueAsText(ActivationState).ToString(); } - return nullptr; + return Super::GetStatusString(); } -bool UFlowNode_CallOwnerFunction::TryAllocateParamsInstance() +EDataValidationResult UFlowNode_CallOwnerFunction::ValidateNode() { - if (FunctionRef.GetFunctionName().IsNone()) + const bool bHasFunction = FunctionRef.IsConfigured(); + if (!bHasFunction) { - // Throw out the old params object (if any) - Params = nullptr; + ValidationLog.Error(TEXT("CallOwnerFunction requires a valid Function reference"), this); - return false; + return EDataValidationResult::Invalid; } - const UClass* ExistingParamsClass = GetExistingParamsClass(); - UClass* RequiredParamsClass = GetRequiredParamsClass(); - - const bool bNeedsAllocateParams = - !IsValid(ExistingParamsClass) || - ExistingParamsClass != RequiredParamsClass; - - if (!bNeedsAllocateParams) + const bool bHasParams = IsValid(Params); + if (!bHasParams) { - return false; - } - - // Throw out the old params object (if any) - Params = nullptr; + ValidationLog.Error(TEXT("CallOwnerFunction requires a valid Params object"), this); - // Create the new params object - Params = NewObject(this, RequiredParamsClass); + return EDataValidationResult::Invalid; + } - return true; -} + checkf(bHasParams && bHasFunction, TEXT("This should be assured by the preceding logic")); -UClass* UFlowNode_CallOwnerFunction::GetRequiredParamsClass() const -{ const UClass* ExpectedOwnerClass = TryGetExpectedOwnerClass(); if (!IsValid(ExpectedOwnerClass)) { - return UFlowOwnerFunctionParams::StaticClass(); - } - - const FName FunctionNameAsName = FunctionRef.GetFunctionName(); + ValidationLog.Error(TEXT("Invalid or null Expected Owner Class for this Flow Asset"), this); - if (FunctionNameAsName.IsNone()) - { - return UFlowOwnerFunctionParams::StaticClass(); + return EDataValidationResult::Invalid; } - UClass* RequiredParamsClass = GetParamsClassForFunctionName(*ExpectedOwnerClass, FunctionNameAsName); - return RequiredParamsClass; -} - -UClass* UFlowNode_CallOwnerFunction::GetExistingParamsClass() const -{ - if (!IsValid(Params)) + // Check if the function can be found on the expected owner + const UFunction* Function = FunctionRef.TryResolveFunction(*ExpectedOwnerClass); + if (!IsValid(Function)) { - return nullptr; - } - - UClass* ExistingParamsClass = Params->GetClass(); - return ExistingParamsClass; -} + ValidationLog.Error(TEXT("Could not resolve function for flow owner"), this); -bool UFlowNode_CallOwnerFunction::IsAcceptableParamsPropertyClass(const UClass* ParamsClass) const -{ - if (!IsValid(ParamsClass)) - { - return false; + return EDataValidationResult::Invalid; } - if (!ParamsClass->IsChildOf()) + // Check the function signature + if (!DoesFunctionHaveValidFlowOwnerFunctionSignature(*Function)) { - return false; - } - - const UClass* ExistingParamsClass = GetExistingParamsClass(); + ValidationLog.Error(TEXT("Flow Owner Function has an invalid signature"), this); - if (IsValid(ExistingParamsClass) && ParamsClass != ExistingParamsClass) - { - return false; + return EDataValidationResult::Invalid; } - return true; -} + const UClass* RequiredParamsClass = GetRequiredParamsClass(); + checkf(IsValid(RequiredParamsClass), TEXT("GetRequiredParamsClass() cannot return null if DoesFunctionHaveValidFlowOwnerFunctionSignature() is true")); -FText UFlowNode_CallOwnerFunction::GetNodeTitle() const -{ - const bool bUseAdaptiveNodeTitles = UFlowSettings::Get()->bUseAdaptiveNodeTitles; + const UClass* ExistingParamsClass = GetExistingParamsClass(); + checkf(IsValid(ExistingParamsClass), TEXT("This should be assured, if bHasParams == true")); - if (bUseAdaptiveNodeTitles && !FunctionRef.GetFunctionName().IsNone()) + // Check if the params (existing) are compatible with the function's expected (required) params + if (!ExistingParamsClass->IsChildOf(RequiredParamsClass)) { - const FText FunctionNameText = FText::FromName(FunctionRef.FunctionName); + ValidationLog.Error(TEXT("Params object is not of the correct type for the flow owner function"), this); - return FText::Format(LOCTEXT("CallOwnerFunction", "Call {0}"), { FunctionNameText }); - } - else - { - return Super::GetNodeTitle(); + return EDataValidationResult::Invalid; } + + return EDataValidationResult::Valid; } #endif // WITH_EDITOR diff --git a/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h b/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h index de78a041a..cc10cdfbb 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h @@ -2,25 +2,17 @@ #pragma once -#include "CoreMinimal.h" - -#include "GameplayTagContainer.h" - #include "FlowOwnerFunctionRef.h" #include "Nodes/FlowNode.h" #include "FlowNode_CallOwnerFunction.generated.h" - -// Forward Declarations class UFlowOwnerFunctionParams; class IFlowOwnerInterface; - // Example signature for valid Flow Owner Functions typedef TFunction FFlowOwnerFunctionSignature; - /** * FlowNode to call an owner function * - Owner must implement IFlowOwnerInterface @@ -30,67 +22,61 @@ typedef TFunction FFlowOwnerFunctionSig UCLASS(NotBlueprintable, meta = (DisplayName = "Call Owner Function")) class FLOW_API UFlowNode_CallOwnerFunction : public UFlowNode { - GENERATED_BODY() + GENERATED_UCLASS_BODY() -public: +protected: + // Function reference on the expected owner to call + UPROPERTY(EditAnywhere, Category = "Call Owner", meta = (DisplayName = "Function")) + FFlowOwnerFunctionRef FunctionRef; + + // Parameter object to pass to the function when called + UPROPERTY(EditAnywhere, Category = "Call Owner", Instanced) + UFlowOwnerFunctionParams* Params; + +protected: + // UFlowNode + virtual void ExecuteInput(const FName& PinName) override; + // --- + + bool TryExecuteOutputPin(const FName& OutputName); + bool ShouldFinishForOutputName(const FName& OutputName) const; #if WITH_EDITOR - //Begin UObject - virtual void PostLoad() override; - virtual bool CanEditChange(const FProperty* InProperty) const override; - virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override; - //End UObject - //Begin UFlowNode public +public: + // UFlowNode virtual FText GetNodeTitle() const override; + virtual FString GetStatusString() const override; virtual EDataValidationResult ValidateNode() override; + // --- - virtual FString GetStatusString() const override; - //End UFlowNode public -#endif // WITH_EDITOR + // UObject + virtual void PostLoad() override; + virtual bool CanEditChange(const FProperty* InProperty) const override; + virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override; + // --- + +protected: + bool TryAllocateParamsInstance(); + void OnChangedParamsObject(); - UFlowNode_CallOwnerFunction(); + // returns true if the InOutPins array was rebuilt + bool RebuildPinArray(const TArray& NewPinNames, TArray& InOutPins, const FFlowPin& DefaultPin); UClass* GetRequiredParamsClass() const; UClass* GetExistingParamsClass() const; + static UClass* GetParamsClassForFunctionName(const UClass& ExpectedOwnerClass, const FName& FunctionName); + static UClass* GetParamsClassForFunction(const UFunction& Function); + +public: bool IsAcceptableParamsPropertyClass(const UClass* ParamsClass) const; UClass* TryGetExpectedOwnerClass() const; - static bool DoesFunctionHaveValidFlowOwnerFunctionSignature(const UFunction& Function); - static UClass* GetParamsClassForFunctionName(const UClass& ExpectedOwnerClass, const FName& FunctionName); - static UClass* GetParamsClassForFunction(const UFunction& Function); - protected: - -#if WITH_EDITOR - // returns true if the InOutPins array was rebuilt - bool RebuildPinArray(const TArray& NewPinNames, TArray& InOutPins, const FFlowPin& DefaultPin); - - void OnChangedParamsObject(); -#endif // WITH_EDITOR - - //Begin UFlowNode protected - virtual void ExecuteInput(const FName& PinName) override; - //End UFlowNode protected - - bool ShouldFinishForOutputName(const FName& OutputName) const; - bool TryExecuteOutputPin(const FName& OutputName); - - bool TryAllocateParamsInstance(); - // Helper function for DoesFunctionHaveValidFlowOwnerFunctionSignature() static bool DoesFunctionHaveNameReturnType(const UFunction& Function); - -protected: - - // Function reference on the expected owner to call - UPROPERTY(EditAnywhere, Category = "Call Owner", meta = (DisplayName = "Function")) - FFlowOwnerFunctionRef FunctionRef; - - // Parameter object to pass to the function when called - UPROPERTY(EditAnywhere, Category = "Call Owner", Instanced) - UFlowOwnerFunctionParams* Params; +#endif // WITH_EDITOR }; diff --git a/Source/FlowEditor/Private/UnrealExtensions/IFlowCuratedNamePropertyCustomization.cpp b/Source/FlowEditor/Private/UnrealExtensions/IFlowCuratedNamePropertyCustomization.cpp index 30d9da6cc..e71ecbc2f 100644 --- a/Source/FlowEditor/Private/UnrealExtensions/IFlowCuratedNamePropertyCustomization.cpp +++ b/Source/FlowEditor/Private/UnrealExtensions/IFlowCuratedNamePropertyCustomization.cpp @@ -1,23 +1,18 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors // NOTE (gtaylor) This class is planned for submission to Epic to include in baseline UE. -// If/when that happens, we will want to remove this version and update to the latest one in the PropertyModule +// If/when that happens, we will want to remove this version and update to the latest one in the PropertyModule #include "UnrealExtensions/IFlowCuratedNamePropertyCustomization.h" #include "DetailLayoutBuilder.h" #include "DetailWidgetRow.h" -#include "EditorClassUtils.h" #include "IDetailPropertyRow.h" -#include "IDetailChildrenBuilder.h" #include "Internationalization/Text.h" #include "PropertyHandle.h" #include "Widgets/Input/SComboBox.h" #include "Widgets/Text/STextBlock.h" - -// IFlowCuratedNamePropertyCustomization Implementation - TSharedPtr IFlowCuratedNamePropertyCustomization::NoneAsText = nullptr; void IFlowCuratedNamePropertyCustomization::Initialize() @@ -46,7 +41,7 @@ void IFlowCuratedNamePropertyCustomization::Initialize() check(CachedTextList.Num() == 1); } -void IFlowCuratedNamePropertyCustomization::CreateHeaderRowWidget(FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils & StructCustomizationUtils) +void IFlowCuratedNamePropertyCustomization::CreateHeaderRowWidget(FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) { // Do one-time setup first Initialize(); @@ -59,11 +54,11 @@ void IFlowCuratedNamePropertyCustomization::CreateHeaderRowWidget(FDetailWidgetR .Text(BuildHeaderText()) ] .ValueContent() - .MaxDesiredWidth(250.0f) + .MaxDesiredWidth(250.0f) [ SAssignNew(TextListWidget, SComboBox>) .OptionsSource(&CachedTextList) - .OnGenerateWidget(this, &IFlowCuratedNamePropertyCustomization::GenerateTextListWidget) + .OnGenerateWidget_Static(&IFlowCuratedNamePropertyCustomization::GenerateTextListWidget) .OnComboBoxOpening(this, &IFlowCuratedNamePropertyCustomization::OnTextListComboBoxOpening) .OnSelectionChanged(this, &IFlowCuratedNamePropertyCustomization::OnTextSelected) [ @@ -71,21 +66,25 @@ void IFlowCuratedNamePropertyCustomization::CreateHeaderRowWidget(FDetailWidgetR .Text(this, &IFlowCuratedNamePropertyCustomization::GetCachedText) .Font(IDetailLayoutBuilder::GetDetailFont()) .ToolTipText(this, &IFlowCuratedNamePropertyCustomization::GetCachedText) - ] + ] ]; // Hook-up the ResetToDefault overrides - FIsResetToDefaultVisible IsResetVisible = - FIsResetToDefaultVisible::CreateSP( - this, - &IFlowCuratedNamePropertyCustomization::CustomIsResetToDefaultVisible); - FResetToDefaultHandler ResetHandler = - FResetToDefaultHandler::CreateSP( - this, - &IFlowCuratedNamePropertyCustomization::CustomResetToDefault); - FResetToDefaultOverride ResetOverride = FResetToDefaultOverride::Create(IsResetVisible, ResetHandler); - - HeaderRow.OverrideResetToDefault(ResetOverride); + { + const FIsResetToDefaultVisible IsResetVisible = + FIsResetToDefaultVisible::CreateSP( + this, + &IFlowCuratedNamePropertyCustomization::CustomIsResetToDefaultVisible); + + const FResetToDefaultHandler ResetHandler = + FResetToDefaultHandler::CreateSP( + this, + &IFlowCuratedNamePropertyCustomization::CustomResetToDefault); + + const FResetToDefaultOverride ResetOverride = FResetToDefaultOverride::Create(IsResetVisible, ResetHandler); + + HeaderRow.OverrideResetToDefault(ResetOverride); + } } bool IFlowCuratedNamePropertyCustomization::CustomIsResetToDefaultVisible(TSharedPtr Property) const @@ -114,18 +113,17 @@ bool IFlowCuratedNamePropertyCustomization::TrySetCuratedNameWithSideEffects(con // Ensure the FText representations are up to date - TSharedPtr NewText = FindCachedOrCreateText(NewName); - + const TSharedPtr NewText = FindCachedOrCreateText(NewName); const bool bIsChanged = (NewText != CachedTextSelected); CachedTextSelected = NewText; - + InsertAtHeadOfCachedTextList(CachedTextSelected); // Set the Name property to the new value check(CachedNameHandle.IsValid()); CachedNameHandle->SetValue(NewName); - + return bIsChanged; } @@ -136,7 +134,7 @@ FText IFlowCuratedNamePropertyCustomization::GetCachedText() const return *CachedTextSelected.Get(); } -TSharedRef IFlowCuratedNamePropertyCustomization::GenerateTextListWidget(TSharedPtr InItem) +TSharedRef IFlowCuratedNamePropertyCustomization::GenerateTextListWidget(const TSharedPtr InItem) { return SNew(STextBlock) @@ -158,7 +156,7 @@ void IFlowCuratedNamePropertyCustomization::OnTextListComboBoxOpening() for (TSharedPtr& Text : CachedTextList) { - (void) MapNameToText.FindOrAdd(FName(Text.Get()->ToString()), Text); + (void)MapNameToText.FindOrAdd(FName(Text.Get()->ToString()), Text); } TArray CuratedNameOptions = GetCuratedNameOptions(); @@ -190,7 +188,7 @@ void IFlowCuratedNamePropertyCustomization::OnTextListComboBoxOpening() } } -void IFlowCuratedNamePropertyCustomization::OnTextSelected(TSharedPtr NewSelection, ESelectInfo::Type SelectInfo) +void IFlowCuratedNamePropertyCustomization::OnTextSelected(const TSharedPtr NewSelection, ESelectInfo::Type SelectInfo) { // Called when the combo box has selected a new element @@ -240,19 +238,19 @@ TSharedPtr IFlowCuratedNamePropertyCustomization::FindCachedOrCreateText( return Result; } -void IFlowCuratedNamePropertyCustomization::InsertAtHeadOfCachedTextList(TSharedPtr Text) +void IFlowCuratedNamePropertyCustomization::InsertAtHeadOfCachedTextList(const TSharedPtr Text) { CachedTextList.Remove(Text); CachedTextList.Insert(Text, 0); } -void IFlowCuratedNamePropertyCustomization::AddToCachedTextList(TSharedPtr Text) +void IFlowCuratedNamePropertyCustomization::AddToCachedTextList(const TSharedPtr Text) { CachedTextList.AddUnique(Text); } -void IFlowCuratedNamePropertyCustomization::RepaintTextListWidget() +void IFlowCuratedNamePropertyCustomization::RepaintTextListWidget() const { if (TextListWidget.IsValid()) { diff --git a/Source/FlowEditor/Private/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.cpp b/Source/FlowEditor/Private/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.cpp index 033a27179..57e284066 100644 --- a/Source/FlowEditor/Private/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.cpp +++ b/Source/FlowEditor/Private/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.cpp @@ -1,18 +1,14 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors // NOTE (gtaylor) This class is planned for submission to Epic to include in baseline UE. -// If/when that happens, we will want to remove this version and update to the latest one in the PropertyModule +// If/when that happens, we will want to remove this version and update to the latest one in the PropertyModule #include "UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h" #include "DetailWidgetRow.h" #include "IDetailChildrenBuilder.h" -#include "IDetailPropertyRow.h" #include "Widgets/Text/STextBlock.h" - -// IFlowExtendedPropertyTypeCustomization Implementation - void IFlowExtendedPropertyTypeCustomization::CustomizeHeader(TSharedRef InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) { StructPropertyHandle = InStructPropertyHandle; @@ -56,7 +52,7 @@ void IFlowExtendedPropertyTypeCustomization::CreateHeaderRowWidget(FDetailWidget ]; } -void IFlowExtendedPropertyTypeCustomization::OnAnyChildPropertyChanged() +void IFlowExtendedPropertyTypeCustomization::OnAnyChildPropertyChanged() const { RefreshHeader(); } diff --git a/Source/FlowEditor/Public/UnrealExtensions/IFlowCuratedNamePropertyCustomization.h b/Source/FlowEditor/Public/UnrealExtensions/IFlowCuratedNamePropertyCustomization.h index 034f26edc..adbd874af 100644 --- a/Source/FlowEditor/Public/UnrealExtensions/IFlowCuratedNamePropertyCustomization.h +++ b/Source/FlowEditor/Public/UnrealExtensions/IFlowCuratedNamePropertyCustomization.h @@ -1,24 +1,20 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors // NOTE (gtaylor) This class is planned for submission to Epic to include in baseline UE. -// If/when that happens, we will want to remove this version and update to the latest one in the PropertyModule +// If/when that happens, we will want to remove this version and update to the latest one in the PropertyModule #pragma once #include "IFlowExtendedPropertyTypeCustomization.h" #include "Widgets/Input/SComboBox.h" - // A base-class to do property Customization for a struct that presents a curated list of FNames for selection class FLOWEDITOR_API IFlowCuratedNamePropertyCustomization : public IFlowExtendedPropertyTypeCustomization { -public: - protected: - - //Begin IExtendedPropertyTypeCustomization + // IExtendedPropertyTypeCustomization virtual void CreateHeaderRowWidget(FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; - //End IExtendedPropertyTypeCustomization + // --- void Initialize(); @@ -28,28 +24,27 @@ class FLOWEDITOR_API IFlowCuratedNamePropertyCustomization : public IFlowExtende // Callbacks for the TextListWidget (see CreateHeaderRowWidget) FText GetCachedText() const; - TSharedRef GenerateTextListWidget(TSharedPtr InItem); + static TSharedRef GenerateTextListWidget(const TSharedPtr InItem); void OnTextListComboBoxOpening(); - void OnTextSelected(TSharedPtr NewSelection, ESelectInfo::Type SelectInfo); + void OnTextSelected(const TSharedPtr NewSelection, ESelectInfo::Type SelectInfo); - void RepaintTextListWidget(); + void RepaintTextListWidget() const; TSharedPtr FindCachedOrCreateText(const FName& NewName); - void AddToCachedTextList(TSharedPtr Text); - void InsertAtHeadOfCachedTextList(TSharedPtr Text); + void AddToCachedTextList(const TSharedPtr Text); + void InsertAtHeadOfCachedTextList(const TSharedPtr Text); bool CustomIsResetToDefaultVisible(TSharedPtr Property) const; void CustomResetToDefault(TSharedPtr Property); - //Begin IFlowCuratedNamePropertyCustomization + // IFlowCuratedNamePropertyCustomization virtual TSharedPtr GetCuratedNamePropertyHandle() const = 0; virtual void SetCuratedName(const FName& NewName) = 0; virtual FName GetCuratedName() const = 0; virtual TArray GetCuratedNameOptions() const = 0; - //End IFlowCuratedNamePropertyCustomization + // --- public: - // Cached property handle for the Curated Name property that is being customized TSharedPtr CachedNameHandle; diff --git a/Source/FlowEditor/Public/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h b/Source/FlowEditor/Public/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h index 35647cd28..8fa1d9db0 100644 --- a/Source/FlowEditor/Public/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h +++ b/Source/FlowEditor/Public/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h @@ -1,7 +1,7 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors // NOTE (gtaylor) This class is planned for submission to Epic to include in baseline UE. -// If/when that happens, we will want to remove this version and update to the latest one in the PropertyModule +// If/when that happens, we will want to remove this version and update to the latest one in the PropertyModule #pragma once @@ -11,8 +11,6 @@ #include "IPropertyTypeCustomization.h" - -// Forward Declarations class STextBlock; class FDetailWidgetRow; class IDetailChildrenBuilder; @@ -20,17 +18,19 @@ class IPropertyTypeCustomizationUtils; class IDetailPropertyRow; class IPropertyHandle; - // An extension of IPropertyTypeCustomization // which adds some quality-of-life improvements for subclasses class FLOWEDITOR_API IFlowExtendedPropertyTypeCustomization : public IPropertyTypeCustomization { public: + // IPropertyTypeCustomization + virtual void CustomizeHeader(TSharedRef InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; - // IPropertyTypeCustomization interface - virtual void CustomizeHeader( TSharedRef InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils ) override; virtual void CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override - { CustomizeChildrenDefaultImpl(InStructPropertyHandle, StructBuilder, StructCustomizationUtils); } + { + CustomizeChildrenDefaultImpl(InStructPropertyHandle, StructBuilder, StructCustomizationUtils); + } + // --- static void CustomizeChildrenDefaultImpl(TSharedRef StructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils); @@ -38,17 +38,15 @@ class FLOWEDITOR_API IFlowExtendedPropertyTypeCustomization : public IPropertyTy static StructT* TryGetTypedStructValue(const TSharedPtr& StructPropertyHandle); protected: - void RefreshHeader() const; virtual void CreateHeaderRowWidget(FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils); virtual FText BuildHeaderText() const; // Callbacks for property editor delegates - void OnAnyChildPropertyChanged(); + void OnAnyChildPropertyChanged() const; protected: - // Cached struct property TSharedPtr StructPropertyHandle; @@ -56,9 +54,6 @@ class FLOWEDITOR_API IFlowExtendedPropertyTypeCustomization : public IPropertyTy TSharedPtr HeaderTextBlock; }; - -// Inline Implementations - template StructT* IFlowExtendedPropertyTypeCustomization::TryGetTypedStructValue(const TSharedPtr& StructPropertyHandle) { From cb594a1f84f5c754c5d5320999bab255e5be3c9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 28 Jan 2024 16:57:57 +0100 Subject: [PATCH 222/485] Moved definitions of the log channels to separate headers. --- Source/Flow/Private/FlowAsset.cpp | 2 +- Source/Flow/Private/FlowComponent.cpp | 2 +- Source/Flow/Private/FlowLogChannels.cpp | 3 +++ Source/Flow/Private/FlowModule.cpp | 1 - Source/Flow/Private/FlowOwnerFunctionRef.cpp | 3 ++- Source/Flow/Private/FlowSubsystem.cpp | 2 +- Source/Flow/Private/Nodes/FlowNode.cpp | 2 +- Source/Flow/Private/Nodes/Utils/FlowNode_Log.cpp | 2 +- .../Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp | 3 ++- .../Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp | 2 +- Source/Flow/Public/FlowLogChannels.h | 5 +++++ Source/Flow/Public/FlowModule.h | 2 -- Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp | 2 +- Source/FlowEditor/Private/Asset/FlowImportUtils.cpp | 2 +- Source/FlowEditor/Private/FlowEditorLogChannels.cpp | 3 +++ Source/FlowEditor/Private/FlowEditorModule.cpp | 1 - Source/FlowEditor/Public/FlowEditorLogChannels.h | 5 +++++ Source/FlowEditor/Public/FlowEditorModule.h | 2 -- 18 files changed, 28 insertions(+), 16 deletions(-) create mode 100644 Source/Flow/Private/FlowLogChannels.cpp create mode 100644 Source/Flow/Public/FlowLogChannels.h create mode 100644 Source/FlowEditor/Private/FlowEditorLogChannels.cpp create mode 100644 Source/FlowEditor/Public/FlowEditorLogChannels.h diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 41a2de2d8..0d07fd25b 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -17,7 +17,7 @@ #if WITH_EDITOR #include "FlowMessageLog.h" -#include "FlowModule.h" +#include "FlowLogChannels.h" #include "Editor.h" #include "Editor/EditorEngine.h" diff --git a/Source/Flow/Private/FlowComponent.cpp b/Source/Flow/Private/FlowComponent.cpp index 4d2477d1a..801a1ace9 100644 --- a/Source/Flow/Private/FlowComponent.cpp +++ b/Source/Flow/Private/FlowComponent.cpp @@ -3,7 +3,7 @@ #include "FlowComponent.h" #include "FlowAsset.h" -#include "FlowModule.h" +#include "FlowLogChannels.h" #include "FlowSettings.h" #include "FlowSubsystem.h" diff --git a/Source/Flow/Private/FlowLogChannels.cpp b/Source/Flow/Private/FlowLogChannels.cpp new file mode 100644 index 000000000..2f4c168b4 --- /dev/null +++ b/Source/Flow/Private/FlowLogChannels.cpp @@ -0,0 +1,3 @@ +#include "FlowLogChannels.h" + +DEFINE_LOG_CATEGORY(LogFlow); diff --git a/Source/Flow/Private/FlowModule.cpp b/Source/Flow/Private/FlowModule.cpp index 4c202ca89..304ebcffa 100644 --- a/Source/Flow/Private/FlowModule.cpp +++ b/Source/Flow/Private/FlowModule.cpp @@ -17,4 +17,3 @@ void FFlowModule::ShutdownModule() #undef LOCTEXT_NAMESPACE IMPLEMENT_MODULE(FFlowModule, Flow) -DEFINE_LOG_CATEGORY(LogFlow); diff --git a/Source/Flow/Private/FlowOwnerFunctionRef.cpp b/Source/Flow/Private/FlowOwnerFunctionRef.cpp index 8d5b54621..235970b2c 100644 --- a/Source/Flow/Private/FlowOwnerFunctionRef.cpp +++ b/Source/Flow/Private/FlowOwnerFunctionRef.cpp @@ -1,8 +1,9 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "FlowOwnerFunctionRef.h" + +#include "FlowLogChannels.h" #include "FlowOwnerFunctionParams.h" -#include "FlowModule.h" #include "FlowOwnerInterface.h" #include "Logging/LogMacros.h" diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index ac14d0934..a9ea955f2 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -4,7 +4,7 @@ #include "FlowAsset.h" #include "FlowComponent.h" -#include "FlowModule.h" +#include "FlowLogChannels.h" #include "FlowSave.h" #include "FlowSettings.h" #include "Nodes/Route/FlowNode_SubGraph.h" diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 6f67bcf3b..0a403a42e 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -3,7 +3,7 @@ #include "Nodes/FlowNode.h" #include "FlowAsset.h" -#include "FlowModule.h" +#include "FlowLogChannels.h" #include "FlowOwnerInterface.h" #include "FlowSettings.h" #include "FlowSubsystem.h" diff --git a/Source/Flow/Private/Nodes/Utils/FlowNode_Log.cpp b/Source/Flow/Private/Nodes/Utils/FlowNode_Log.cpp index 33e5f210f..93edb154b 100644 --- a/Source/Flow/Private/Nodes/Utils/FlowNode_Log.cpp +++ b/Source/Flow/Private/Nodes/Utils/FlowNode_Log.cpp @@ -1,7 +1,7 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "Nodes/Utils/FlowNode_Log.h" -#include "FlowModule.h" +#include "FlowLogChannels.h" #include "Engine/Engine.h" diff --git a/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp b/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp index 9f76d55f9..4b521d6de 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp @@ -1,8 +1,9 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "Nodes/World/FlowNode_CallOwnerFunction.h" + #include "FlowAsset.h" -#include "FlowModule.h" +#include "FlowLogChannels.h" #include "FlowOwnerInterface.h" #include "FlowOwnerFunctionParams.h" #include "FlowSettings.h" diff --git a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp index f3c010205..90c2cc4a1 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp @@ -3,7 +3,7 @@ #include "Nodes/World/FlowNode_PlayLevelSequence.h" #include "FlowAsset.h" -#include "FlowModule.h" +#include "FlowLogChannels.h" #include "FlowSubsystem.h" #include "LevelSequence/FlowLevelSequencePlayer.h" diff --git a/Source/Flow/Public/FlowLogChannels.h b/Source/Flow/Public/FlowLogChannels.h new file mode 100644 index 000000000..d56a50040 --- /dev/null +++ b/Source/Flow/Public/FlowLogChannels.h @@ -0,0 +1,5 @@ +#pragma once + +#include "Logging/LogMacros.h" + +FLOW_API DECLARE_LOG_CATEGORY_EXTERN(LogFlow, Log, All); diff --git a/Source/Flow/Public/FlowModule.h b/Source/Flow/Public/FlowModule.h index 9244e2be4..12ed6e1c5 100644 --- a/Source/Flow/Public/FlowModule.h +++ b/Source/Flow/Public/FlowModule.h @@ -5,8 +5,6 @@ #include "Logging/LogMacros.h" #include "Modules/ModuleInterface.h" -DECLARE_LOG_CATEGORY_EXTERN(LogFlow, Log, All) - class FFlowModule final : public IModuleInterface { public: diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index 6626478e4..c4171dce0 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -3,7 +3,7 @@ #include "Asset/FlowAssetEditor.h" #include "FlowEditorCommands.h" -#include "FlowEditorModule.h" +#include "FlowEditorLogChannels.h" #include "FlowMessageLog.h" #include "Asset/FlowAssetEditorContext.h" diff --git a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp index eaf7073b1..37d30775f 100644 --- a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp +++ b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp @@ -4,7 +4,7 @@ #include "Asset/FlowAssetFactory.h" #include "FlowEditorDefines.h" -#include "FlowEditorModule.h" +#include "FlowEditorLogChannels.h" #include "Graph/FlowGraphSchema_Actions.h" #include "Graph/FlowGraph.h" diff --git a/Source/FlowEditor/Private/FlowEditorLogChannels.cpp b/Source/FlowEditor/Private/FlowEditorLogChannels.cpp new file mode 100644 index 000000000..f41232f16 --- /dev/null +++ b/Source/FlowEditor/Private/FlowEditorLogChannels.cpp @@ -0,0 +1,3 @@ +#include "FlowEditorLogChannels.h" + +DEFINE_LOG_CATEGORY(LogFlowEditor); diff --git a/Source/FlowEditor/Private/FlowEditorModule.cpp b/Source/FlowEditor/Private/FlowEditorModule.cpp index c36010c36..21d9b3295 100644 --- a/Source/FlowEditor/Private/FlowEditorModule.cpp +++ b/Source/FlowEditor/Private/FlowEditorModule.cpp @@ -249,4 +249,3 @@ TSharedRef FFlowEditorModule::CreateFlowAssetEditor(const EToo #undef LOCTEXT_NAMESPACE IMPLEMENT_MODULE(FFlowEditorModule, FlowEditor) -DEFINE_LOG_CATEGORY(LogFlowEditor); diff --git a/Source/FlowEditor/Public/FlowEditorLogChannels.h b/Source/FlowEditor/Public/FlowEditorLogChannels.h new file mode 100644 index 000000000..684d14206 --- /dev/null +++ b/Source/FlowEditor/Public/FlowEditorLogChannels.h @@ -0,0 +1,5 @@ +#pragma once + +#include "Logging/LogMacros.h" + +FLOWEDITOR_API DECLARE_LOG_CATEGORY_EXTERN(LogFlowEditor, Log, All); diff --git a/Source/FlowEditor/Public/FlowEditorModule.h b/Source/FlowEditor/Public/FlowEditorModule.h index d096ff0df..55040f8ba 100644 --- a/Source/FlowEditor/Public/FlowEditorModule.h +++ b/Source/FlowEditor/Public/FlowEditorModule.h @@ -15,8 +15,6 @@ struct FGraphPanelPinConnectionFactory; class FFlowAssetEditor; class UFlowAsset; -DECLARE_LOG_CATEGORY_EXTERN(LogFlowEditor, Log, All) - class FLOWEDITOR_API FFlowEditorModule : public IModuleInterface { public: From d9345fc423bc88d3b538168fa676c441c06c292a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Sun, 28 Jan 2024 17:10:03 +0100 Subject: [PATCH 223/485] Removed `AFlowWorldSettings::IsValidInstance()` needed in pre-UE5 era --- Source/Flow/Private/FlowWorldSettings.cpp | 25 ----------------------- Source/Flow/Public/FlowWorldSettings.h | 5 ----- 2 files changed, 30 deletions(-) diff --git a/Source/Flow/Private/FlowWorldSettings.cpp b/Source/Flow/Private/FlowWorldSettings.cpp index e56c38926..91d805584 100644 --- a/Source/Flow/Private/FlowWorldSettings.cpp +++ b/Source/Flow/Private/FlowWorldSettings.cpp @@ -14,28 +14,3 @@ AFlowWorldSettings::AFlowWorldSettings(const FObjectInitializer& ObjectInitializ // In this case engine would call BeginPlay multiple times... for AFlowWorldSettings and every inherited AWorldSettings class... FlowComponent->bAllowMultipleInstances = false; } - -void AFlowWorldSettings::PostInitializeComponents() -{ - Super::PostInitializeComponents(); - - if (!IsValidInstance()) - { - GetFlowComponent()->bAutoStartRootFlow = false; - } -} - -bool AFlowWorldSettings::IsValidInstance() const -{ - if (const UWorld* World = GetWorld()) - { - // workaround to prevent starting Flow from stray AWorldSettings actor that still exists in the world - // cause of this issue fixed in UE 5.0: https://github.com/EpicGames/UnrealEngine/commit/001f50b8b55507940f9c2cb1349592c692aae2c1?diff=unified - if (World->GetWorldSettings() == this) - { - return true; - } - } - - return false; -} diff --git a/Source/Flow/Public/FlowWorldSettings.h b/Source/Flow/Public/FlowWorldSettings.h index ba1b1dacc..b7afd081d 100644 --- a/Source/Flow/Public/FlowWorldSettings.h +++ b/Source/Flow/Public/FlowWorldSettings.h @@ -21,9 +21,4 @@ class FLOW_API AFlowWorldSettings : public AWorldSettings public: UFlowComponent* GetFlowComponent() const { return FlowComponent; } - - virtual void PostInitializeComponents() override; - -private: - bool IsValidInstance() const; }; From b5507708f107bac3918bdfd7caefa8334a3e376d Mon Sep 17 00:00:00 2001 From: jnucc <120216868+jnucc@users.noreply.github.com> Date: Mon, 29 Jan 2024 12:24:50 -0500 Subject: [PATCH 224/485] Fix UFlowNode_SubGraph in cooked builds where Asset member could dereference to nullptr, when trying to get the PathName after converting to UObject*. Just try to get the Path Name from the SoftObjectPtr itself instead. (#186) Reason: UObjectBaseUtility::GetPathName() has a check with a if (this != NULL) and under clang, in optimized builds, this is assumed to be always true. Depending on how inlining goes, the compiler is free to eliminate if it assumes it will always be true; chcked generated UE 5.2 and UE 5.3 code in disassembly, and the checks for if(this != StopOuter && this != NULL) was outright eliminated during aggressive inlining and was assumed to be true, where in UE 5.2 there were still function calls and it cannot assume stuff can be nullptr or not. --- Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp index 0f5f34a71..7aa564cd5 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp @@ -28,7 +28,7 @@ UFlowNode_SubGraph::UFlowNode_SubGraph(const FObjectInitializer& ObjectInitializ bool UFlowNode_SubGraph::CanBeAssetInstanced() const { - return !Asset.IsNull() && (bCanInstanceIdenticalAsset || Asset->GetPathName() != GetFlowAsset()->GetTemplateAsset()->GetPathName()); + return !Asset.IsNull() && (bCanInstanceIdenticalAsset || Asset.ToString() != GetFlowAsset()->GetTemplateAsset()->GetPathName()); } void UFlowNode_SubGraph::PreloadContent() @@ -57,7 +57,7 @@ void UFlowNode_SubGraph::ExecuteInput(const FName& PinName) } else { - LogError(FString::Printf(TEXT("Asset %s cannot be instance, probably is the same as the asset owning this SubGraph node."), *Asset->GetPathName())); + LogError(FString::Printf(TEXT("Asset %s cannot be instance, probably is the same as the asset owning this SubGraph node."), *Asset.ToString())); } Finish(); From e5d4ab7fffc3c34007a8394e0549aa06d7d93729 Mon Sep 17 00:00:00 2001 From: FoolsTheoryDev Date: Sun, 25 Feb 2024 20:29:35 +0100 Subject: [PATCH 225/485] removed redundant includes --- Source/Flow/Private/FlowModule.cpp | 4 +--- Source/Flow/Public/FlowAsset.h | 2 ++ Source/Flow/Public/FlowModule.h | 1 - Source/FlowEditor/Public/Asset/FlowImportUtils.h | 1 - 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Source/Flow/Private/FlowModule.cpp b/Source/Flow/Private/FlowModule.cpp index 304ebcffa..23dbbf634 100644 --- a/Source/Flow/Private/FlowModule.cpp +++ b/Source/Flow/Private/FlowModule.cpp @@ -2,9 +2,7 @@ #include "FlowModule.h" -#include "Modules/ModuleManager.h" - -#define LOCTEXT_NAMESPACE "Flow" +#define LOCTEXT_NAMESPACE "FlowModule" void FFlowModule::StartupModule() { diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 9d3199243..2515ffd22 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -46,6 +46,8 @@ UCLASS(BlueprintType, hideCategories = Object) class FLOW_API UFlowAsset : public UObject { GENERATED_UCLASS_BODY() + +public: friend class UFlowNode; friend class UFlowNode_CustomOutput; friend class UFlowNode_SubGraph; diff --git a/Source/Flow/Public/FlowModule.h b/Source/Flow/Public/FlowModule.h index 12ed6e1c5..d74fcb98b 100644 --- a/Source/Flow/Public/FlowModule.h +++ b/Source/Flow/Public/FlowModule.h @@ -2,7 +2,6 @@ #pragma once -#include "Logging/LogMacros.h" #include "Modules/ModuleInterface.h" class FFlowModule final : public IModuleInterface diff --git a/Source/FlowEditor/Public/Asset/FlowImportUtils.h b/Source/FlowEditor/Public/Asset/FlowImportUtils.h index 3ef1799b7..183361ff1 100644 --- a/Source/FlowEditor/Public/Asset/FlowImportUtils.h +++ b/Source/FlowEditor/Public/Asset/FlowImportUtils.h @@ -5,7 +5,6 @@ #include "Kismet/BlueprintFunctionLibrary.h" #include "FlowAsset.h" -#include "Kismet/BlueprintFunctionLibrary.h" #include "Nodes/FlowPin.h" #include "FlowImportUtils.generated.h" From 1e38083091c18deb13eda972366de5f3ce6eb3e6 Mon Sep 17 00:00:00 2001 From: MaksymKapelianovych <48297221+MaksymKapelianovych@users.noreply.github.com> Date: Tue, 19 Mar 2024 18:24:14 +0200 Subject: [PATCH 226/485] Some improvements for working with CallOwnerFuntion (#188) * removed redundant includes * Some improvements for working with CallOwnerFuntion: 1. Display node description above node (as for triggers, timers etc). 2. Support for context pins (as for PlayLevelSequence). Now if Params type is changed in referenced function or pins inside Params are renamed/added/removed, CallOwnerFuntion node will not be refreshed. Pins will appear only if user changes referenced function to some other function and then returns back, which is not very convenient. After my changes, Refresh action will update node pins as well. Also, deleted some now excessive function to unify logic for pins refreshing. --------- Co-authored-by: FoolsTheoryDev --- .../World/FlowNode_CallOwnerFunction.cpp | 138 ++++++++---------- .../Nodes/World/FlowNode_CallOwnerFunction.h | 9 +- 2 files changed, 63 insertions(+), 84 deletions(-) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp b/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp index 4b521d6de..4a6286f69 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp @@ -133,15 +133,15 @@ void UFlowNode_CallOwnerFunction::PostEditChangeProperty(struct FPropertyChanged if (MemberPropertyName == GET_MEMBER_NAME_CHECKED(UFlowNode_CallOwnerFunction, Params)) { - OnChangedParamsObject(); + OnReconstructionRequested.ExecuteIfBound(); } const FName PropertyName = PropertyChangedEvent.Property->GetFName(); if (PropertyName == GET_MEMBER_NAME_CHECKED(FFlowOwnerFunctionRef, FunctionName)) { - if (TryAllocateParamsInstance()) + if (TryAllocateParamsInstance() || FunctionRef.GetFunctionName().IsNone()) { - OnChangedParamsObject(); + OnReconstructionRequested.ExecuteIfBound(); } } } @@ -177,82 +177,6 @@ bool UFlowNode_CallOwnerFunction::TryAllocateParamsInstance() return true; } -void UFlowNode_CallOwnerFunction::OnChangedParamsObject() -{ - bool bChangedPins = false; - - if (IsValid(Params)) - { - bChangedPins = RebuildPinArray(Params->GetInputNames(), InputPins, DefaultInputPin) || bChangedPins; - bChangedPins = RebuildPinArray(Params->GetOutputNames(), OutputPins, DefaultOutputPin) || bChangedPins; - } - else - { - bChangedPins = RebuildPinArray(TArray(&DefaultInputPin.PinName, 1), InputPins, DefaultInputPin) || bChangedPins; - bChangedPins = RebuildPinArray(TArray(&DefaultOutputPin.PinName, 1), OutputPins, DefaultOutputPin) || bChangedPins; - } - - if (bChangedPins) - { - OnReconstructionRequested.ExecuteIfBound(); - } -} - -bool UFlowNode_CallOwnerFunction::RebuildPinArray(const TArray& NewPinNames, TArray& InOutPins, const FFlowPin& DefaultPin) -{ - bool bIsChanged; - - TArray NewPins; - - if (NewPinNames.Num() == 0) - { - bIsChanged = true; - - NewPins.Reserve(1); - - NewPins.Add(DefaultPin); - } - else - { - const bool bIsSameNum = (NewPinNames.Num() == InOutPins.Num()); - - bIsChanged = !bIsSameNum; - - NewPins.Reserve(NewPinNames.Num()); - - for (int32 NewPinIndex = 0; NewPinIndex < NewPinNames.Num(); ++NewPinIndex) - { - const FName& NewPinName = NewPinNames[NewPinIndex]; - NewPins.Add(FFlowPin(NewPinName)); - - if (bIsSameNum) - { - bIsChanged = bIsChanged || (NewPinName != InOutPins[NewPinIndex].PinName); - } - } - } - - if (bIsChanged) - { - InOutPins.Reset(); - - check(NewPins.Num() > 0); - - if (&InOutPins == &InputPins) - { - AddInputPins(NewPins); - } - else - { - checkf(&InOutPins == &OutputPins, TEXT("Only expected to be called with one or the other of the pin arrays")); - - AddOutputPins(NewPins); - } - } - - return bIsChanged; -} - UClass* UFlowNode_CallOwnerFunction::GetRequiredParamsClass() const { const UClass* ExpectedOwnerClass = TryGetExpectedOwnerClass(); @@ -267,7 +191,7 @@ UClass* UFlowNode_CallOwnerFunction::GetRequiredParamsClass() const { return UFlowOwnerFunctionParams::StaticClass(); } - + UClass* RequiredParamsClass = GetParamsClassForFunctionName(*ExpectedOwnerClass, FunctionNameAsName); return RequiredParamsClass; } @@ -310,6 +234,16 @@ FText UFlowNode_CallOwnerFunction::GetNodeTitle() const } } +FString UFlowNode_CallOwnerFunction::GetNodeDescription() const +{ + if (UFlowSettings::Get()->bUseAdaptiveNodeTitles) + { + return Super::GetNodeDescription(); + } + + return FunctionRef.FunctionName.ToString(); +} + bool UFlowNode_CallOwnerFunction::IsAcceptableParamsPropertyClass(const UClass* ParamsClass) const { if (!IsValid(ParamsClass)) @@ -483,6 +417,50 @@ EDataValidationResult UFlowNode_CallOwnerFunction::ValidateNode() return EDataValidationResult::Valid; } +TArray UFlowNode_CallOwnerFunction::GetContextInputs() +{ + // refresh Params, just in case function argument type was changed + TryAllocateParamsInstance(); + + TArray Pins = {}; + + if (Params) + { + for (const FName& Name : Params->GetInputNames()) + { + if (InputPins.Contains(Name)) + { + continue; + } + + Pins.Emplace(Name); + } + } + return Pins; +} + +TArray UFlowNode_CallOwnerFunction::GetContextOutputs() +{ + // refresh Params, just in case function argument type was changed + TryAllocateParamsInstance(); + + TArray Pins = {}; + + if (Params) + { + for (const FName& Name : Params->GetOutputNames()) + { + if (OutputPins.Contains(Name)) + { + continue; + } + + Pins.Emplace(Name); + } + } + return Pins; +} + #endif // WITH_EDITOR #undef LOCTEXT_NAMESPACE diff --git a/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h b/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h index cc10cdfbb..0c30317e6 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h @@ -46,8 +46,13 @@ class FLOW_API UFlowNode_CallOwnerFunction : public UFlowNode public: // UFlowNode virtual FText GetNodeTitle() const override; + virtual FString GetNodeDescription() const override; virtual FString GetStatusString() const override; virtual EDataValidationResult ValidateNode() override; + + virtual bool SupportsContextPins() const override { return true; }; + virtual TArray GetContextInputs() override; + virtual TArray GetContextOutputs() override; // --- // UObject @@ -58,10 +63,6 @@ class FLOW_API UFlowNode_CallOwnerFunction : public UFlowNode protected: bool TryAllocateParamsInstance(); - void OnChangedParamsObject(); - - // returns true if the InOutPins array was rebuilt - bool RebuildPinArray(const TArray& NewPinNames, TArray& InOutPins, const FFlowPin& DefaultPin); UClass* GetRequiredParamsClass() const; UClass* GetExistingParamsClass() const; From cec338cc615670ad992ce6519adc081c5cf15c3e Mon Sep 17 00:00:00 2001 From: FoolsTheoryDev Date: Wed, 20 Mar 2024 20:21:50 +0100 Subject: [PATCH 227/485] plugin compiles against UE 5.4 --- .../Private/LevelSequence/FlowLevelSequenceActor.cpp | 2 +- .../Private/LevelSequence/FlowLevelSequencePlayer.cpp | 11 ++++++++++- Source/Flow/Public/FlowAsset.h | 2 +- Source/FlowEditor/Private/MovieScene/FlowSection.cpp | 7 ++++++- Source/FlowEditor/Public/Asset/FlowAssetEditor.h | 2 +- 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp b/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp index afc016165..9b87fa0ff 100644 --- a/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp +++ b/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp @@ -22,7 +22,7 @@ void AFlowLevelSequenceActor::GetLifetimeReplicatedProps(TArraySetPlaybackSettings(PlaybackSettings); + GetSequencePlayer()->SetPlaybackSettings(PlaybackSettings); } void AFlowLevelSequenceActor::SetReplicatedLevelSequenceAsset(ULevelSequence* Asset) diff --git a/Source/Flow/Private/LevelSequence/FlowLevelSequencePlayer.cpp b/Source/Flow/Private/LevelSequence/FlowLevelSequencePlayer.cpp index 530ed0a51..77d95906d 100644 --- a/Source/Flow/Private/LevelSequence/FlowLevelSequencePlayer.cpp +++ b/Source/Flow/Private/LevelSequence/FlowLevelSequencePlayer.cpp @@ -5,6 +5,7 @@ #include "Nodes/FlowNode.h" #include "DefaultLevelSequenceInstanceData.h" +#include "Runtime/Launch/Resources/Version.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowLevelSequencePlayer) @@ -41,7 +42,11 @@ UFlowLevelSequencePlayer* UFlowLevelSequencePlayer::CreateFlowLevelSequencePlaye { // apply Transform Origin // https://docs.unrealengine.com/5.0/en-US/creating-level-sequences-with-dynamic-transforms-in-unreal-engine/ +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION > 3 + if (TransformOriginActor->IsValidLowLevel()) +#else if (IsValid(TransformOriginActor)) +#endif { // moving Level Sequence Actor might allow proper distance-based actor replication in networked games SpawnTransform = TransformOriginActor->GetTransform(); @@ -57,7 +62,11 @@ UFlowLevelSequencePlayer* UFlowLevelSequencePlayer::CreateFlowLevelSequencePlaye Actor->CameraSettings = CameraSettings; // apply Transform Origin to spawned actor +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION > 3 + if (TransformOriginActor->IsValidLowLevel()) +#else if (IsValid(TransformOriginActor)) +#endif { if (UDefaultLevelSequenceInstanceData* InstanceData = Cast(Actor->DefaultInstanceData)) { @@ -83,7 +92,7 @@ UFlowLevelSequencePlayer* UFlowLevelSequencePlayer::CreateFlowLevelSequencePlaye OutActor = Actor; // Sequence Player is created by Level Sequence Actor - return Cast(Actor->SequencePlayer); + return Cast(Actor->GetSequencePlayer()); } TArray UFlowLevelSequencePlayer::GetEventContexts() const diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 2515ffd22..de34e45fd 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -97,7 +97,7 @@ class FLOW_API UFlowAsset : public UObject private: UPROPERTY() - UEdGraph* FlowGraph; + TObjectPtr FlowGraph; static TSharedPtr FlowGraphInterface; #endif diff --git a/Source/FlowEditor/Private/MovieScene/FlowSection.cpp b/Source/FlowEditor/Private/MovieScene/FlowSection.cpp index 123856413..474fffb02 100644 --- a/Source/FlowEditor/Private/MovieScene/FlowSection.cpp +++ b/Source/FlowEditor/Private/MovieScene/FlowSection.cpp @@ -4,7 +4,6 @@ #include "MovieScene/MovieSceneFlowRepeaterSection.h" #include "MovieScene/MovieSceneFlowTriggerSection.h" -#include "CommonMovieSceneTools.h" #include "Fonts/FontMeasure.h" #include "Framework/Application/SlateApplication.h" #include "MovieSceneTrack.h" @@ -14,6 +13,12 @@ #include "SequencerSectionPainter.h" #include "SequencerTimeSliderController.h" +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 4 +#include "CommonMovieSceneTools.h" +#else +#include "TimeToPixel.h" +#endif + #define LOCTEXT_NAMESPACE "FlowSection" bool FFlowSectionBase::IsSectionSelected() const diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index 93a8c80c0..83c6e7cd9 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -37,7 +37,7 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit protected: /** The Flow Asset being edited */ - UFlowAsset* FlowAsset; + TObjectPtr FlowAsset; TSharedPtr AssetToolbar; From 7146c49e12d543ad729282f65c310a050a1fe6c3 Mon Sep 17 00:00:00 2001 From: FoolsTheoryDev Date: Wed, 20 Mar 2024 20:44:31 +0100 Subject: [PATCH 228/485] fix CIS compilation --- Source/Flow/Private/FlowModule.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Source/Flow/Private/FlowModule.cpp b/Source/Flow/Private/FlowModule.cpp index 23dbbf634..dd7f12f14 100644 --- a/Source/Flow/Private/FlowModule.cpp +++ b/Source/Flow/Private/FlowModule.cpp @@ -2,7 +2,7 @@ #include "FlowModule.h" -#define LOCTEXT_NAMESPACE "FlowModule" +#include "Modules/ModuleManager.h" void FFlowModule::StartupModule() { @@ -12,6 +12,4 @@ void FFlowModule::ShutdownModule() { } -#undef LOCTEXT_NAMESPACE - IMPLEMENT_MODULE(FFlowModule, Flow) From 3e11ec632a6882a97adf66ca365f6a7a53dd5ef7 Mon Sep 17 00:00:00 2001 From: FoolsTheoryDev Date: Wed, 20 Mar 2024 23:36:06 +0100 Subject: [PATCH 229/485] replaced monolithic header --- Source/FlowEditor/Private/Asset/FlowDebuggerSubsystem.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/FlowEditor/Private/Asset/FlowDebuggerSubsystem.cpp b/Source/FlowEditor/Private/Asset/FlowDebuggerSubsystem.cpp index 3bb324bb9..94715f0ab 100644 --- a/Source/FlowEditor/Private/Asset/FlowDebuggerSubsystem.cpp +++ b/Source/FlowEditor/Private/Asset/FlowDebuggerSubsystem.cpp @@ -6,11 +6,12 @@ #include "FlowSubsystem.h" +#include "Editor/UnrealEdEngine.h" #include "Engine/Engine.h" #include "Engine/World.h" #include "Framework/Notifications/NotificationManager.h" #include "Templates/Function.h" -#include "UnrealEd.h" +#include "UnrealEdGlobals.h" #include "Widgets/Notifications/SNotificationList.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowDebuggerSubsystem) From be72d6d125a2312882659ad7cfb1043bacf7e971 Mon Sep 17 00:00:00 2001 From: MaksymKapelianovych <48297221+MaksymKapelianovych@users.noreply.github.com> Date: Sun, 24 Mar 2024 19:58:29 +0200 Subject: [PATCH 230/485] Fix issue https://github.com/MothCocoon/FlowGraph/issues/187 (#189) --- Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index d87dfa694..ea5e6c15e 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -171,6 +171,9 @@ void UFlowGraphNode::OnBlueprintCompiled() void UFlowGraphNode::OnExternalChange() { + // Do not create transaction here, since this function triggers from modifying UFlowNode's property, which itself already made inside of transaction. + Modify(); + bNeedsFullReconstruction = true; ReconstructNode(); From 69c893b3e18c78e1e73d9959d5fc7a752f4f3769 Mon Sep 17 00:00:00 2001 From: MaksymKapelianovych <48297221+MaksymKapelianovych@users.noreply.github.com> Date: Sun, 24 Mar 2024 20:03:07 +0200 Subject: [PATCH 231/485] 1. UFlowNode now also executes OnReconstructionRequested if pin info has been changed inside pin array (PinName, PinFriendlyName, PinToolTip). (#190) 2. Removed OnBlueprintPreCompile() and OnBlueprintCompiled() from UFlowGraphNode, because all possible node reconstructions, handled by these functions, now are triggered from UFlowNode::PostEditChangeProperty() 3. SFlowGraphNode now hides pin name only if there is one valid pin in array, not just Pins.Num() == 1. --- Source/Flow/Private/Nodes/FlowNode.cpp | 3 ++- .../Private/Graph/Nodes/FlowGraphNode.cpp | 25 ------------------- .../Private/Graph/Widgets/SFlowGraphNode.cpp | 20 +++++++++++++-- .../Public/Graph/Nodes/FlowGraphNode.h | 4 --- .../Public/Graph/Widgets/SFlowGraphNode.h | 3 +++ 5 files changed, 23 insertions(+), 32 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 0a403a42e..f9be6d897 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -63,7 +63,8 @@ void UFlowNode::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEve Super::PostEditChangeProperty(PropertyChangedEvent); if (PropertyChangedEvent.Property - && (PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(UFlowNode, InputPins) || PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(UFlowNode, OutputPins))) + && (PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(UFlowNode, InputPins) || PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(UFlowNode, OutputPins) + || PropertyChangedEvent.GetMemberPropertyName() == GET_MEMBER_NAME_CHECKED(UFlowNode, InputPins) || PropertyChangedEvent.GetMemberPropertyName() == GET_MEMBER_NAME_CHECKED(UFlowNode, OutputPins))) { OnReconstructionRequested.ExecuteIfBound(); } diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index ea5e6c15e..802c0ba39 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -141,34 +141,9 @@ void UFlowGraphNode::SubscribeToExternalChanges() if (FlowNode) { FlowNode->OnReconstructionRequested.BindUObject(this, &UFlowGraphNode::OnExternalChange); - - // blueprint nodes - if (FlowNode->GetClass()->ClassGeneratedBy && GEditor) - { - GEditor->OnBlueprintPreCompile().AddUObject(this, &UFlowGraphNode::OnBlueprintPreCompile); - GEditor->OnBlueprintCompiled().AddUObject(this, &UFlowGraphNode::OnBlueprintCompiled); - } - } -} - -void UFlowGraphNode::OnBlueprintPreCompile(UBlueprint* Blueprint) -{ - if (Blueprint && Blueprint == FlowNode->GetClass()->ClassGeneratedBy) - { - bBlueprintCompilationPending = true; } } -void UFlowGraphNode::OnBlueprintCompiled() -{ - if (bBlueprintCompilationPending) - { - OnExternalChange(); - } - - bBlueprintCompilationPending = false; -} - void UFlowGraphNode::OnExternalChange() { // Do not create transaction here, since this function triggers from modifying UFlowNode's property, which itself already made inside of transaction. diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp index bdf51c635..0038cc5f4 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp @@ -527,14 +527,16 @@ void SFlowGraphNode::CreateStandardPinWidget(UEdGraphPin* Pin) { if (Pin->Direction == EGPD_Input) { - if (FlowGraphNode->GetFlowNode()->GetInputPins().Num() == 1 && Pin->PinName == UFlowNode::DefaultInputPin.PinName) + // Pin array can have pins with name None, which will not be created. We need to check if array have only one valid pin + if (ValidPinsCount(FlowGraphNode->GetFlowNode()->GetInputPins()) == 1 && Pin->PinName == UFlowNode::DefaultInputPin.PinName) { NewPin->SetShowLabel(false); } } else { - if (FlowGraphNode->GetFlowNode()->GetOutputPins().Num() == 1 && Pin->PinName == UFlowNode::DefaultOutputPin.PinName) + // Pin array can have pins with name None, which will not be created. We need to check if array have only one valid pin + if (ValidPinsCount(FlowGraphNode->GetFlowNode()->GetOutputPins()) == 1 && Pin->PinName == UFlowNode::DefaultOutputPin.PinName) { NewPin->SetShowLabel(false); } @@ -661,4 +663,18 @@ FReply SFlowGraphNode::OnAddFlowPin(const EEdGraphPinDirection Direction) return FReply::Handled(); } +int32 SFlowGraphNode::ValidPinsCount(const TArray& Pins) +{ + int32 Count = 0; + for (const FFlowPin& Pin : Pins) + { + if (Pin.IsValid()) + { + Count += 1; + } + } + + return Count; +} + #undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h index 8dba75821..b80321ad2 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h @@ -60,10 +60,6 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode private: void SubscribeToExternalChanges(); - - void OnBlueprintPreCompile(UBlueprint* Blueprint); - void OnBlueprintCompiled(); - void OnExternalChange(); public: diff --git a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h index 9ddda3fe3..a97eade0d 100644 --- a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h @@ -63,6 +63,9 @@ class FLOWEDITOR_API SFlowGraphNode : public SGraphNode // Variant of SGraphNode::OnAddPin virtual FReply OnAddFlowPin(const EEdGraphPinDirection Direction); +private: + static int32 ValidPinsCount(const TArray& Pins); + protected: UFlowGraphNode* FlowGraphNode = nullptr; }; From 4267ee7f845c5e06eca4b172db690b30fa2cac1e Mon Sep 17 00:00:00 2001 From: FoolsTheoryDev Date: Sun, 24 Mar 2024 19:30:28 +0100 Subject: [PATCH 232/485] fixed non-unity build on UE older than 5.3 --- Source/FlowEditor/Private/Utils/SLevelEditorFlow.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/FlowEditor/Private/Utils/SLevelEditorFlow.cpp b/Source/FlowEditor/Private/Utils/SLevelEditorFlow.cpp index 6179af939..c3662ffce 100644 --- a/Source/FlowEditor/Private/Utils/SLevelEditorFlow.cpp +++ b/Source/FlowEditor/Private/Utils/SLevelEditorFlow.cpp @@ -4,10 +4,15 @@ #include "FlowAsset.h" #include "FlowComponent.h" +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 3 +#include "FlowWorldSettings.h" +#endif + #include "Graph/FlowGraphSettings.h" #include "Editor.h" #include "PropertyCustomizationHelpers.h" +#include "Runtime/Launch/Resources/Version.h" #define LOCTEXT_NAMESPACE "SLevelEditorFlow" From 4c02376ad4e329ddaf9f17041fdb3aa50d11da12 Mon Sep 17 00:00:00 2001 From: FoolsTheoryDev Date: Fri, 29 Mar 2024 20:23:00 +0100 Subject: [PATCH 233/485] removed unconditional dirtying asset which might occured on constructing graph editor --- Source/FlowEditor/Private/Graph/FlowGraph.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraph.cpp b/Source/FlowEditor/Private/Graph/FlowGraph.cpp index 48e2b2d98..465fb9c21 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraph.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraph.cpp @@ -81,7 +81,6 @@ void UFlowGraph::RefreshGraph() void UFlowGraph::NotifyGraphChanged() { GetFlowAsset()->HarvestNodeConnections(); - GetFlowAsset()->MarkPackageDirty(); Super::NotifyGraphChanged(); } From 3372a31dfdb23ef8034d8c4a61dcec32ff7b98b9 Mon Sep 17 00:00:00 2001 From: MaksymKapelianovych <48297221+MaksymKapelianovych@users.noreply.github.com> Date: Thu, 25 Apr 2024 21:12:03 +0300 Subject: [PATCH 234/485] Add RequiredParamsClass validation to prevent crash with UFlowNode_CallOwnerFunction when user change owner class to default and then press Refresh (#191) --- .../Private/Nodes/World/FlowNode_CallOwnerFunction.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp b/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp index 4a6286f69..f6506c1e4 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp @@ -159,6 +159,11 @@ bool UFlowNode_CallOwnerFunction::TryAllocateParamsInstance() const UClass* ExistingParamsClass = GetExistingParamsClass(); const UClass* RequiredParamsClass = GetRequiredParamsClass(); + if (!IsValid(RequiredParamsClass)) + { + return false; + } + const bool bNeedsAllocateParams = !IsValid(ExistingParamsClass) || ExistingParamsClass != RequiredParamsClass; @@ -182,14 +187,14 @@ UClass* UFlowNode_CallOwnerFunction::GetRequiredParamsClass() const const UClass* ExpectedOwnerClass = TryGetExpectedOwnerClass(); if (!IsValid(ExpectedOwnerClass)) { - return UFlowOwnerFunctionParams::StaticClass(); + return nullptr; } const FName FunctionNameAsName = FunctionRef.GetFunctionName(); if (FunctionNameAsName.IsNone()) { - return UFlowOwnerFunctionParams::StaticClass(); + return nullptr; } UClass* RequiredParamsClass = GetParamsClassForFunctionName(*ExpectedOwnerClass, FunctionNameAsName); From 63c40298beceafb2957789b2bfa069e7bf867ce0 Mon Sep 17 00:00:00 2001 From: MaksymKapelianovych <48297221+MaksymKapelianovych@users.noreply.github.com> Date: Thu, 25 Apr 2024 21:13:19 +0300 Subject: [PATCH 235/485] Add graph action to delete node and reconnect pins if possible (#192) --- .../Private/Graph/FlowGraphEditor.cpp | 80 +++++++++++++++++++ .../FlowEditor/Public/Graph/FlowGraphEditor.h | 1 + 2 files changed, 81 insertions(+) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index 32d3624e4..24b7e73bb 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -67,6 +67,10 @@ void SFlowGraphEditor::BindGraphCommands() CommandList->MapAction(GraphEditorCommands.StraightenConnections, FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnStraightenConnections)); + + CommandList->MapAction(GraphEditorCommands.DeleteAndReconnectNodes, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::DeleteSelectedNodes), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanDeleteNodes)); // Generic Node commands CommandList->MapAction(GenericCommands.Undo, @@ -374,6 +378,76 @@ TSet SFlowGraphEditor::GetSelectedFlowNodes() const return Result; } +void SFlowGraphEditor::ReconnectExecPins(const UFlowGraphNode* Node) +{ + if(Node == nullptr) + { + return; + } + + UEdGraphPin* InputPin = nullptr; + UEdGraphPin* OutputPin = nullptr; + + for (UEdGraphPin* Pin : Node->InputPins) + { + if (Pin->HasAnyConnections()) + { + if (InputPin) + { + // more that one connected input pins - do not reconnect anything + return; + } + + if (Pin) + { + InputPin = Pin; + } + } + else if (InputPin == nullptr) + { + // first pin doesn't have any connections - do not reconnect anything, because we probably don't know expected result for user + return; + } + } + + for (UEdGraphPin* Pin : Node->OutputPins) + { + if (Pin->HasAnyConnections()) + { + if (OutputPin) + { + // more that one connected output pins - do not reconnect anything + return; + } + + if (Pin) + { + OutputPin = Pin; + } + } + else if (OutputPin == nullptr) + { + // first pin doesn't have any connections - do not reconnect anything, because we probably don't know expected result for user + return; + } + } + + if (InputPin && OutputPin) + { + // Make a connection from every incoming exec pin to every outgoing then pin + for (UEdGraphPin* const IncomingConnectionPin : InputPin->LinkedTo) + { + if (IncomingConnectionPin) + { + for (UEdGraphPin* const ConnectedCompletePin : OutputPin->LinkedTo) + { + IncomingConnectionPin->MakeLinkTo(ConnectedCompletePin); + } + } + } + } +} + void SFlowGraphEditor::DeleteSelectedNodes() { const FScopedTransaction Transaction(LOCTEXT("DeleteSelectedNode", "Delete Selected Node")); @@ -394,6 +468,12 @@ void SFlowGraphEditor::DeleteSelectedNodes() { const FGuid NodeGuid = FlowGraphNode->GetFlowNode()->GetGuid(); + // If the user is pressing shift then try and reconnect the pins + if (FSlateApplication::Get().GetModifierKeys().IsShiftDown()) + { + ReconnectExecPins(FlowGraphNode); + } + GetCurrentGraph()->GetSchema()->BreakNodeLinks(*Node); Node->DestroyNode(); diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h index 57918c877..2d3536f4e 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h @@ -65,6 +65,7 @@ class FLOWEDITOR_API SFlowGraphEditor : public SGraphEditor protected: virtual bool CanSelectAllNodes() const { return true; } + void ReconnectExecPins(const UFlowGraphNode* Node); virtual void DeleteSelectedNodes(); virtual void DeleteSelectedDuplicableNodes(); virtual bool CanDeleteNodes() const; From c31afe134783eb9c35d2b90e8e33a34299db331a Mon Sep 17 00:00:00 2001 From: MaksymKapelianovych <48297221+MaksymKapelianovych@users.noreply.github.com> Date: Thu, 25 Apr 2024 21:13:57 +0300 Subject: [PATCH 236/485] Improve drawing for reroute nodes that go backwards when they are selected, connection is executing ot executed during PIE. (#194) --- .../Graph/FlowGraphConnectionDrawingPolicy.cpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp b/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp index 805441b77..7afc48d3d 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp @@ -150,30 +150,27 @@ void FFlowGraphConnectionDrawingPolicy::DetermineWiringStyle(UEdGraphPin* Output Params.WireColor = SelectedColor; Params.WireThickness = SelectedWireThickness; Params.bDrawBubbles = false; - return; } - // recent paths - if (RecentPaths.Contains(OutputPin) && RecentPaths[OutputPin] == InputPin) + else if (RecentPaths.Contains(OutputPin) && RecentPaths[OutputPin] == InputPin) { Params.WireColor = RecentColor; Params.WireThickness = RecentWireThickness; Params.bDrawBubbles = true; - return; } - // all paths, showing graph history - if (RecordedPaths.Contains(OutputPin) && RecordedPaths[OutputPin] == InputPin) + else if (RecordedPaths.Contains(OutputPin) && RecordedPaths[OutputPin] == InputPin) { Params.WireColor = RecordedColor; Params.WireThickness = RecordedWireThickness; Params.bDrawBubbles = false; - return; } - // It's not followed, fade it and keep it thin - Params.WireColor = InactiveColor; - Params.WireThickness = InactiveWireThickness; + else + { + Params.WireColor = InactiveColor; + Params.WireThickness = InactiveWireThickness; + } } } From 8e2a0a90fdedf1b9b01bc60386327514d29f857d Mon Sep 17 00:00:00 2001 From: MaksymKapelianovych <48297221+MaksymKapelianovych@users.noreply.github.com> Date: Fri, 26 Apr 2024 14:11:34 +0300 Subject: [PATCH 237/485] Fix crash with SequencePlayer by adding engine version check (#197) --- Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp b/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp index 9b87fa0ff..814dde15c 100644 --- a/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp +++ b/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp @@ -22,7 +22,11 @@ void AFlowLevelSequenceActor::GetLifetimeReplicatedProps(TArray 3 GetSequencePlayer()->SetPlaybackSettings(PlaybackSettings); +#else + SequencePlayer->SetPlaybackSettings(PlaybackSettings); +#endif } void AFlowLevelSequenceActor::SetReplicatedLevelSequenceAsset(ULevelSequence* Asset) From 6fcb53e493aeca68f9f4c6401f10c04d1ee59870 Mon Sep 17 00:00:00 2001 From: FoolsTheoryDev Date: Sat, 27 Apr 2024 12:04:11 +0200 Subject: [PATCH 238/485] fixed compilation in older engine versions --- Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp b/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp index 814dde15c..d3ceaf240 100644 --- a/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp +++ b/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp @@ -3,6 +3,7 @@ #include "LevelSequence/FlowLevelSequenceActor.h" #include "LevelSequence/FlowLevelSequencePlayer.h" #include "Net/UnrealNetwork.h" +#include "Runtime/Launch/Resources/Version.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowLevelSequenceActor) From c5993fc7339f01197e2ec96aeeccf15994401db1 Mon Sep 17 00:00:00 2001 From: FoolsTheoryDev Date: Sun, 16 Jun 2024 13:43:26 +0200 Subject: [PATCH 239/485] Flow 2.0 --- Flow.uplugin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.uplugin b/Flow.uplugin index 5535fef5d..b980e5120 100644 --- a/Flow.uplugin +++ b/Flow.uplugin @@ -1,6 +1,6 @@ { "FileVersion" : 3, - "Version" : 1.6, + "Version" : 2.0, "FriendlyName" : "Flow", "Description" : "Design-agnostic node editor for scripting game’s flow.", "Category" : "Gameplay", From fe9f7d76462c425123ca42ef635497e5d4a716fe Mon Sep 17 00:00:00 2001 From: LindyHopperGT <91915878+LindyHopperGT@users.noreply.github.com> Date: Sun, 16 Jun 2024 04:44:07 -0700 Subject: [PATCH 240/485] Flow AddOns (#202) * Flow AddOns Introduced FlowAddOns ~ Major rework of the Flow Editor to support AddOns ~ Also added ExecuteComponent flow node, which allows a component of the owning actor (if it implements a specific interface) to be executed via a proxy node in the Flow Graph. We use this to wrap some legacy systems in flow graph nodes. * Fix Linux compile errors and regressions on UFUNCTIONS * Fix bug with ExecuteInput for AddOns Nodes and AddOns could execute for pins they did not expect, if the AddOns introduced pins that the Node (or other AddOns) were not aware of. Also, removed some deprecation warnings because they were generating compile warnings, which could cause some projects problems with automated builds. I changed the deprecation message to a human-readable message only. * Slight adjustment to IsSupportedInputPinName() early return if the array is empty * AddOns Optimized and changed how Input/Outputs are added from AddOns * Quality of life improvements Revised the ForEachAddOn() signature, since it only ever needs to be used when iterating over all AddOns (recursively), as you can use a normal for loop over AddOns for just the AddOns of this object alone. Also moved some access functions from FlowNode to FlowNodeBase and exposed them to Blueprint, based on a question I saw on the Discord about Get Actor Owner access. * Extracted Component injection code into Flow base also updated ExecuteComponent to use it * Transferring flags from Template component to instance + Transient * Singular InjectComponent signature * Small revisions to Execute Component flownode * Fix uniqueness problem with component lookup --- Config/BaseFlow.ini | 1 + Config/DefaultFlow.ini | 1 + Source/Flow/Private/AddOns/FlowNodeAddOn.cpp | 97 +++ .../AddOns/FlowNodeAddOn_PredicateAND.cpp | 53 ++ .../AddOns/FlowNodeAddOn_PredicateNOT.cpp | 63 ++ .../AddOns/FlowNodeAddOn_PredicateOR.cpp | 66 ++ Source/Flow/Private/FlowAsset.cpp | 60 +- Source/Flow/Private/FlowMessageLog.cpp | 12 +- .../FlowContextPinSupplierInterface.cpp | 13 + .../FlowExternalExecutableInterface.cpp | 9 + .../FlowNativeExecutableInterface.cpp | 24 + .../Interfaces/FlowPredicateInterface.cpp | 20 + Source/Flow/Private/Nodes/FlowNode.cpp | 467 +++-------- .../Private/Nodes/FlowNodeAddOnBlueprint.cpp | 8 + Source/Flow/Private/Nodes/FlowNodeBase.cpp | 647 +++++++++++++++ .../Private/Nodes/Route/FlowNode_Branch.cpp | 51 ++ .../Private/Nodes/Route/FlowNode_SubGraph.cpp | 5 +- .../World/FlowNode_CallOwnerFunction.cpp | 296 +++---- .../Nodes/World/FlowNode_ExecuteComponent.cpp | 592 ++++++++++++++ .../World/FlowNode_PlayLevelSequence.cpp | 5 +- .../Types/FlowActorOwnerComponentRef.cpp | 59 ++ .../Types/FlowInjectComponentsHelper.cpp | 100 +++ .../Types/FlowInjectComponentsManager.cpp | 117 +++ .../{ => Types}/FlowOwnerFunctionParams.cpp | 7 +- .../{ => Types}/FlowOwnerFunctionRef.cpp | 13 +- Source/Flow/Public/AddOns/FlowNodeAddOn.h | 75 ++ .../AddOns/FlowNodeAddOn_PredicateAND.h | 32 + .../AddOns/FlowNodeAddOn_PredicateNOT.h | 30 + .../Public/AddOns/FlowNodeAddOn_PredicateOR.h | 32 + Source/Flow/Public/FlowAsset.h | 10 +- Source/Flow/Public/FlowComponent.h | 2 +- Source/Flow/Public/FlowMessageLog.h | 6 +- Source/Flow/Public/FlowTypes.h | 29 + .../FlowContextPinSupplierInterface.h | 45 ++ .../Interfaces/FlowCoreExecutableInterface.h | 63 ++ .../FlowExternalExecutableInterface.h | 33 + .../FlowNativeExecutableInterface.h | 44 + .../{ => Interfaces}/FlowOwnerInterface.h | 0 .../Interfaces/FlowPredicateInterface.h | 30 + Source/Flow/Public/Nodes/FlowNode.h | 217 +---- .../Public/Nodes/FlowNodeAddOnBlueprint.h | 24 + Source/Flow/Public/Nodes/FlowNodeBase.h | 233 ++++++ Source/Flow/Public/Nodes/FlowNodeBlueprint.h | 2 +- Source/Flow/Public/Nodes/FlowPin.h | 3 +- .../Flow/Public/Nodes/Route/FlowNode_Branch.h | 27 + .../Public/Nodes/Route/FlowNode_SubGraph.h | 11 +- .../Nodes/World/FlowNode_CallOwnerFunction.h | 79 +- .../Nodes/World/FlowNode_ExecuteComponent.h | 122 +++ .../Nodes/World/FlowNode_PlayLevelSequence.h | 4 +- .../Public/Types/FlowActorOwnerComponentRef.h | 48 ++ .../Public/Types/FlowInjectComponentsHelper.h | 39 + .../Types/FlowInjectComponentsManager.h | 68 ++ .../{ => Types}/FlowOwnerFunctionParams.h | 9 +- .../Public/{ => Types}/FlowOwnerFunctionRef.h | 2 + Source/FlowEditor/FlowEditor.Build.cs | 3 +- .../Private/Asset/FlowAssetEditor.cpp | 41 +- .../Private/Asset/FlowAssetIndexer.cpp | 10 +- .../Private/Asset/FlowImportUtils.cpp | 15 +- .../FlowActorOwnerComponentFilters.cpp | 186 +++++ .../FlowActorOwnerComponentFilters.h | 57 ++ ...lowActorOwnerComponentRefCustomization.cpp | 141 ++++ .../FlowNodeAddOn_Details.cpp | 14 + .../FlowOwnerFunctionRefCustomization.cpp | 24 +- .../FlowEditor/Private/FlowEditorModule.cpp | 14 +- Source/FlowEditor/Private/Graph/FlowGraph.cpp | 424 +++++++++- .../Private/Graph/FlowGraphEditor.cpp | 238 ++++-- .../Private/Graph/FlowGraphSchema.cpp | 333 ++++++-- .../Private/Graph/FlowGraphSchema_Actions.cpp | 74 +- .../Private/Graph/Nodes/FlowGraphNode.cpp | 759 ++++++++++++++++-- .../Graph/Nodes/FlowGraphNode_Branch.cpp | 18 + .../Graph/Widgets/DragFlowGraphNode.cpp | 35 + .../Private/Graph/Widgets/DragFlowGraphNode.h | 26 + .../Private/Graph/Widgets/SFlowGraphNode.cpp | 680 ++++++++++++++-- .../Graph/Widgets/SFlowGraphNode_SubGraph.cpp | 2 +- .../Widgets/SGraphEditorActionMenuFlow.cpp | 108 +++ .../Private/MovieScene/FlowTrackEditor.cpp | 1 + ...ssetTypeActions_FlowNodeAddOnBlueprint.cpp | 34 + .../AssetTypeActions_FlowNodeBlueprint.cpp | 5 +- .../Nodes/FlowNodeBlueprintFactory.cpp | 101 ++- .../Private/Pins/SFlowOutputPinHandle.cpp | 2 +- .../IFlowCuratedNamePropertyCustomization.cpp | 110 ++- ...IFlowExtendedPropertyTypeCustomization.cpp | 4 + .../Private/Utils/SLevelEditorFlow.cpp | 1 + .../FlowEditor/Public/Asset/FlowAssetEditor.h | 6 +- .../FlowActorOwnerComponentRefCustomization.h | 45 ++ .../FlowNodeAddOn_Details.h | 18 + .../FlowOwnerFunctionRefCustomization.h | 17 +- Source/FlowEditor/Public/Graph/FlowGraph.h | 57 ++ .../FlowEditor/Public/Graph/FlowGraphEditor.h | 4 + .../FlowEditor/Public/Graph/FlowGraphSchema.h | 42 +- .../Public/Graph/FlowGraphSchema_Actions.h | 39 + .../Public/Graph/Nodes/FlowGraphNode.h | 131 ++- .../Public/Graph/Nodes/FlowGraphNode_Branch.h | 16 + .../Public/Graph/Widgets/SFlowGraphNode.h | 95 ++- .../Widgets/SGraphEditorActionMenuFlow.h | 69 ++ .../AssetTypeActions_FlowNodeAddOnBlueprint.h | 21 + .../Public/Nodes/FlowNodeBlueprintFactory.h | 27 +- .../IFlowCuratedNamePropertyCustomization.h | 7 +- .../IFlowExtendedPropertyTypeCustomization.h | 3 +- 99 files changed, 6946 insertions(+), 1216 deletions(-) create mode 100644 Source/Flow/Private/AddOns/FlowNodeAddOn.cpp create mode 100644 Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateAND.cpp create mode 100644 Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateNOT.cpp create mode 100644 Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateOR.cpp create mode 100644 Source/Flow/Private/Interfaces/FlowContextPinSupplierInterface.cpp create mode 100644 Source/Flow/Private/Interfaces/FlowExternalExecutableInterface.cpp create mode 100644 Source/Flow/Private/Interfaces/FlowNativeExecutableInterface.cpp create mode 100644 Source/Flow/Private/Interfaces/FlowPredicateInterface.cpp create mode 100644 Source/Flow/Private/Nodes/FlowNodeAddOnBlueprint.cpp create mode 100644 Source/Flow/Private/Nodes/FlowNodeBase.cpp create mode 100644 Source/Flow/Private/Nodes/Route/FlowNode_Branch.cpp create mode 100644 Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp create mode 100644 Source/Flow/Private/Types/FlowActorOwnerComponentRef.cpp create mode 100644 Source/Flow/Private/Types/FlowInjectComponentsHelper.cpp create mode 100644 Source/Flow/Private/Types/FlowInjectComponentsManager.cpp rename Source/Flow/Private/{ => Types}/FlowOwnerFunctionParams.cpp (91%) rename Source/Flow/Private/{ => Types}/FlowOwnerFunctionRef.cpp (89%) create mode 100644 Source/Flow/Public/AddOns/FlowNodeAddOn.h create mode 100644 Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateAND.h create mode 100644 Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateNOT.h create mode 100644 Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateOR.h create mode 100644 Source/Flow/Public/Interfaces/FlowContextPinSupplierInterface.h create mode 100644 Source/Flow/Public/Interfaces/FlowCoreExecutableInterface.h create mode 100644 Source/Flow/Public/Interfaces/FlowExternalExecutableInterface.h create mode 100644 Source/Flow/Public/Interfaces/FlowNativeExecutableInterface.h rename Source/Flow/Public/{ => Interfaces}/FlowOwnerInterface.h (100%) create mode 100644 Source/Flow/Public/Interfaces/FlowPredicateInterface.h create mode 100644 Source/Flow/Public/Nodes/FlowNodeAddOnBlueprint.h create mode 100644 Source/Flow/Public/Nodes/FlowNodeBase.h create mode 100644 Source/Flow/Public/Nodes/Route/FlowNode_Branch.h create mode 100644 Source/Flow/Public/Nodes/World/FlowNode_ExecuteComponent.h create mode 100644 Source/Flow/Public/Types/FlowActorOwnerComponentRef.h create mode 100644 Source/Flow/Public/Types/FlowInjectComponentsHelper.h create mode 100644 Source/Flow/Public/Types/FlowInjectComponentsManager.h rename Source/Flow/Public/{ => Types}/FlowOwnerFunctionParams.h (96%) rename Source/Flow/Public/{ => Types}/FlowOwnerFunctionRef.h (97%) create mode 100644 Source/FlowEditor/Private/DetailCustomizations/FlowActorOwnerComponentFilters.cpp create mode 100644 Source/FlowEditor/Private/DetailCustomizations/FlowActorOwnerComponentFilters.h create mode 100644 Source/FlowEditor/Private/DetailCustomizations/FlowActorOwnerComponentRefCustomization.cpp create mode 100644 Source/FlowEditor/Private/DetailCustomizations/FlowNodeAddOn_Details.cpp create mode 100644 Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Branch.cpp create mode 100644 Source/FlowEditor/Private/Graph/Widgets/DragFlowGraphNode.cpp create mode 100644 Source/FlowEditor/Private/Graph/Widgets/DragFlowGraphNode.h create mode 100644 Source/FlowEditor/Private/Graph/Widgets/SGraphEditorActionMenuFlow.cpp create mode 100644 Source/FlowEditor/Private/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.cpp create mode 100644 Source/FlowEditor/Public/DetailCustomizations/FlowActorOwnerComponentRefCustomization.h create mode 100644 Source/FlowEditor/Public/DetailCustomizations/FlowNodeAddOn_Details.h create mode 100644 Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_Branch.h create mode 100644 Source/FlowEditor/Public/Graph/Widgets/SGraphEditorActionMenuFlow.h create mode 100644 Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.h diff --git a/Config/BaseFlow.ini b/Config/BaseFlow.ini index 7ec2bf1d9..b5289a16d 100644 --- a/Config/BaseFlow.ini +++ b/Config/BaseFlow.ini @@ -1,5 +1,6 @@ [CoreRedirects] +ClassRedirects=(OldName="/Script/Flow.FlowNode_CustomEvent",NewName="/Script/Flow.FlowNode_CustomInput") +PropertyRedirects=(OldName="FlowAsset.CustomEvents",NewName="CustomInputs") ++PropertyRedirects=(OldName="FlowGraphNode.FlowNode",NewName="NodeInstance") +StructRedirects=(OldName="/Script/Flow.FlowBreakpoint",NewName="/Script/Flow.FlowPinTrait") +PropertyRedirects=(OldName="FlowPinTrait.bHasBreakpoint",NewName="bAllowed") \ No newline at end of file diff --git a/Config/DefaultFlow.ini b/Config/DefaultFlow.ini index 7ec2bf1d9..b5289a16d 100644 --- a/Config/DefaultFlow.ini +++ b/Config/DefaultFlow.ini @@ -1,5 +1,6 @@ [CoreRedirects] +ClassRedirects=(OldName="/Script/Flow.FlowNode_CustomEvent",NewName="/Script/Flow.FlowNode_CustomInput") +PropertyRedirects=(OldName="FlowAsset.CustomEvents",NewName="CustomInputs") ++PropertyRedirects=(OldName="FlowGraphNode.FlowNode",NewName="NodeInstance") +StructRedirects=(OldName="/Script/Flow.FlowBreakpoint",NewName="/Script/Flow.FlowPinTrait") +PropertyRedirects=(OldName="FlowPinTrait.bHasBreakpoint",NewName="bAllowed") \ No newline at end of file diff --git a/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp b/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp new file mode 100644 index 000000000..6fea958be --- /dev/null +++ b/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp @@ -0,0 +1,97 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "AddOns/FlowNodeAddOn.h" +#include "Nodes/FlowNode.h" + +#include "Misc/RuntimeErrors.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNodeAddOn) + +void UFlowNodeAddOn::InitializeInstance() +{ + CacheFlowNode(); + + Super::InitializeInstance(); +} + +void UFlowNodeAddOn::DeinitializeInstance() +{ + Super::DeinitializeInstance(); + + FlowNode = nullptr; +} + +void UFlowNodeAddOn::TriggerFirstOutput(const bool bFinish) +{ + if (ensure(FlowNode)) + { + FlowNode->TriggerFirstOutput(bFinish); + } +} + +void UFlowNodeAddOn::TriggerOutput(const FName PinName, const bool bFinish , const EFlowPinActivationType ActivationType) +{ + if (ensure(FlowNode)) + { + FlowNode->TriggerOutput(PinName, bFinish, ActivationType); + } +} + +void UFlowNodeAddOn::Finish() +{ + if (ensure(FlowNode)) + { + FlowNode->Finish(); + } +} + +EFlowAddOnAcceptResult UFlowNodeAddOn::AcceptFlowNodeAddOnParent_Implementation(const UFlowNodeBase* ParentTemplate) const +{ + // Subclasses may override this function to opt-in to parent classes + + return EFlowAddOnAcceptResult::Undetermined; +} + +UFlowNode* UFlowNodeAddOn::GetFlowNode() const +{ + // We are making the assumption that this would addlways be known + // during runtime and that we are not calling this method before the addon has been + // initialized. + ensure(FlowNode); + + return FlowNode; +} + +bool UFlowNodeAddOn::IsSupportedInputPinName(const FName& PinName) const +{ + if (InputPins.IsEmpty()) + { + return true; + } + + if (const FFlowPin* FoundFlowPin = FindFlowPinByName(PinName, InputPins)) + { + return true; + } + else + { + return false; + } +} + +void UFlowNodeAddOn::CacheFlowNode() +{ + UObject* OuterObject = GetOuter(); + while (IsValid(OuterObject)) + { + FlowNode = Cast(OuterObject); + if (FlowNode) + { + break; + } + + OuterObject = OuterObject->GetOuter(); + } + + ensureAsRuntimeWarning(FlowNode); +} diff --git a/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateAND.cpp b/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateAND.cpp new file mode 100644 index 000000000..e176f73a4 --- /dev/null +++ b/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateAND.cpp @@ -0,0 +1,53 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "AddOns/FlowNodeAddOn_PredicateAND.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNodeAddOn_PredicateAND) + +UFlowNodeAddOn_PredicateAND::UFlowNodeAddOn_PredicateAND() + : Super() +{ +#if WITH_EDITOR + NodeStyle = EFlowNodeStyle::Logic; + Category = TEXT("Composite"); +#endif +} + +EFlowAddOnAcceptResult UFlowNodeAddOn_PredicateAND::AcceptFlowNodeAddOnChild_Implementation(const UFlowNodeAddOn* AddOnTemplate) const +{ + if (IFlowPredicateInterface::ImplementsInterfaceSafe(AddOnTemplate)) + { + return EFlowAddOnAcceptResult::TentativeAccept; + } + else + { + // All AddOn children MUST implement IFlowPredicateInterface + // (so do not return Super's implementation which will return Undetermined) + return EFlowAddOnAcceptResult::Reject; + } +} + +bool UFlowNodeAddOn_PredicateAND::EvaluatePredicate_Implementation() const +{ + return EvaluatePredicateAND(AddOns); +} + +bool UFlowNodeAddOn_PredicateAND::EvaluatePredicateAND(const TArray& AddOns) +{ + for (int Index = 0; Index < AddOns.Num(); ++Index) + { + const UFlowNodeAddOn* AddOn = AddOns[Index]; + + if (IFlowPredicateInterface::ImplementsInterfaceSafe(AddOn)) + { + const bool bResult = IFlowPredicateInterface::Execute_EvaluatePredicate(AddOn); + + if (!bResult) + { + return false; + } + } + } + + return true; +} diff --git a/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateNOT.cpp b/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateNOT.cpp new file mode 100644 index 000000000..d3c3c5ffb --- /dev/null +++ b/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateNOT.cpp @@ -0,0 +1,63 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "AddOns/FlowNodeAddOn_PredicateNOT.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNodeAddOn_PredicateNOT) + +UFlowNodeAddOn_PredicateNOT::UFlowNodeAddOn_PredicateNOT() + : Super() +{ +#if WITH_EDITOR + NodeStyle = EFlowNodeStyle::Logic; + Category = TEXT("Composite"); +#endif +} + +EFlowAddOnAcceptResult UFlowNodeAddOn_PredicateNOT::AcceptFlowNodeAddOnChild_Implementation(const UFlowNodeAddOn* AddOnTemplate) const +{ + if (AddOns.Num() >= 1) + { + // Must not have more than one child Add-On under any circumstances + return EFlowAddOnAcceptResult::Reject; + } + else if (IFlowPredicateInterface::ImplementsInterfaceSafe(AddOnTemplate)) + { + return EFlowAddOnAcceptResult::TentativeAccept; + } + else + { + return EFlowAddOnAcceptResult::Reject; + } +} + +bool UFlowNodeAddOn_PredicateNOT::EvaluatePredicate_Implementation() const +{ + if (AddOns.IsEmpty()) + { + // For parity with PredicateAND, the "no AddOns (that qualify)" case results in a "true" result + return true; + } + + if (AddOns.Num() > 1) + { + const FString Message = FString::Printf(TEXT("%s may only have a single predicate AddOn child"), *GetName()); + UFlowNodeAddOn_PredicateNOT* MutableThis = const_cast(this); + MutableThis->LogError(Message); + } + + UFlowNodeAddOn* SingleChildAddOn = AddOns[0]; + + if (!IFlowPredicateInterface::ImplementsInterfaceSafe(SingleChildAddOn)) + { + const FString Message = FString::Printf(TEXT("%s requires a child AddOn that implements the IFlowPredicateInterface interface!"), *GetName()); + UFlowNodeAddOn_PredicateNOT* MutableThis = const_cast(this); + MutableThis->LogError(Message); + + // For parity with PredicateAND, the "no AddOns (that qualify)" case results in a "true" result + return true; + } + + const bool bResult = !IFlowPredicateInterface::Execute_EvaluatePredicate(SingleChildAddOn); + + return bResult; +} \ No newline at end of file diff --git a/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateOR.cpp b/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateOR.cpp new file mode 100644 index 000000000..9cc596e9e --- /dev/null +++ b/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateOR.cpp @@ -0,0 +1,66 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "AddOns/FlowNodeAddOn_PredicateOR.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNodeAddOn_PredicateOR) + +UFlowNodeAddOn_PredicateOR::UFlowNodeAddOn_PredicateOR() + : Super() +{ +#if WITH_EDITOR + NodeStyle = EFlowNodeStyle::Logic; + Category = TEXT("Composite"); +#endif +} + +EFlowAddOnAcceptResult UFlowNodeAddOn_PredicateOR::AcceptFlowNodeAddOnChild_Implementation(const UFlowNodeAddOn* AddOnTemplate) const +{ + if (IFlowPredicateInterface::ImplementsInterfaceSafe(AddOnTemplate)) + { + return EFlowAddOnAcceptResult::TentativeAccept; + } + else + { + // All AddOn children MUST implement IFlowPredicateInterface + // (so do not return Super's implementation which will return Undetermined) + return EFlowAddOnAcceptResult::Reject; + } +} + +bool UFlowNodeAddOn_PredicateOR::EvaluatePredicate_Implementation() const +{ + return EvaluatePredicateOR(AddOns); +} + +bool UFlowNodeAddOn_PredicateOR::EvaluatePredicateOR(const TArray& AddOns) +{ + int32 FalseCount = 0; + for (int Index = 0; Index < AddOns.Num(); ++Index) + { + const UFlowNodeAddOn* AddOn = AddOns[Index]; + + if (IFlowPredicateInterface::ImplementsInterfaceSafe(AddOn)) + { + const bool bResult = IFlowPredicateInterface::Execute_EvaluatePredicate(AddOn); + + if (bResult) + { + return true; + } + else + { + ++FalseCount; + } + } + } + + if (FalseCount == 0) + { + // For parity with PredicateAND, the "no AddOns (that qualify)" case results in a "true" result + return true; + } + else + { + return false; + } +} diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 0d07fd25b..d68f4c1ab 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -2,10 +2,12 @@ #include "FlowAsset.h" +#include "FlowLogChannels.h" #include "FlowSettings.h" #include "FlowSubsystem.h" -#include "Nodes/FlowNode.h" +#include "AddOns/FlowNodeAddOn.h" +#include "Nodes/FlowNodeBase.h" #include "Nodes/Route/FlowNode_CustomInput.h" #include "Nodes/Route/FlowNode_CustomOutput.h" #include "Nodes/Route/FlowNode_Start.h" @@ -16,20 +18,15 @@ #include "Serialization/MemoryWriter.h" #if WITH_EDITOR -#include "FlowMessageLog.h" -#include "FlowLogChannels.h" - #include "Editor.h" #include "Editor/EditorEngine.h" -#endif -#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowAsset) - -#if WITH_EDITOR FString UFlowAsset::ValidationError_NodeClassNotAllowed = TEXT("Node class {0} is not allowed in this asset."); FString UFlowAsset::ValidationError_NullNodeInstance = TEXT("Node with GUID {0} is NULL"); #endif +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowAsset) + UFlowAsset::UFlowAsset(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , bWorldBound(true) @@ -113,7 +110,7 @@ EDataValidationResult UFlowAsset::ValidateAsset(FFlowMessageLog& MessageLog) if (IsValid(Node.Value)) { FText FailureReason; - if (!IsNodeClassAllowed(Node.Value->GetClass(), &FailureReason)) + if (!IsNodeOrAddOnClassAllowed(Node.Value->GetClass(), &FailureReason)) { const FString ErrorMsg = FailureReason.IsEmpty() ? @@ -139,25 +136,25 @@ EDataValidationResult UFlowAsset::ValidateAsset(FFlowMessageLog& MessageLog) return MessageLog.Messages.Num() > 0 ? EDataValidationResult::Invalid : EDataValidationResult::Valid; } -bool UFlowAsset::IsNodeClassAllowed(const UClass* FlowNodeClass, FText* OutOptionalFailureReason) const +bool UFlowAsset::IsNodeOrAddOnClassAllowed(const UClass* FlowNodeOrAddOnClass, FText* OutOptionalFailureReason) const { - if (!IsValid(FlowNodeClass)) + if (!IsValid(FlowNodeOrAddOnClass)) { return false; } - if (!CanFlowNodeClassBeUsedByFlowAsset(*FlowNodeClass)) + if (!CanFlowNodeClassBeUsedByFlowAsset(*FlowNodeOrAddOnClass)) { return false; } - if (!CanFlowAssetUseFlowNodeClass(*FlowNodeClass)) + if (!CanFlowAssetUseFlowNodeClass(*FlowNodeOrAddOnClass)) { return false; } // Confirm plugin reference restrictions are being respected - if (!CanFlowAssetReferenceFlowNode(*FlowNodeClass, OutOptionalFailureReason)) + if (!CanFlowAssetReferenceFlowNode(*FlowNodeOrAddOnClass, OutOptionalFailureReason)) { return false; } @@ -167,7 +164,15 @@ bool UFlowAsset::IsNodeClassAllowed(const UClass* FlowNodeClass, FText* OutOptio bool UFlowAsset::CanFlowNodeClassBeUsedByFlowAsset(const UClass& FlowNodeClass) const { - UFlowNode* NodeDefaults = FlowNodeClass.GetDefaultObject(); + UFlowNode* NodeDefaults = Cast(FlowNodeClass.GetDefaultObject()); + if (!NodeDefaults) + { + check(FlowNodeClass.IsChildOf()); + + // AddOns don't have the AllowedAssetClasses/DeniedAssetClasses + // (yet? maybe we move it up to the base?) + return true; + } // UFlowNode class limits which UFlowAsset class can use it for (const UClass* DeniedAssetClass : NodeDefaults->DeniedAssetClasses) @@ -200,6 +205,17 @@ bool UFlowAsset::CanFlowNodeClassBeUsedByFlowAsset(const UClass& FlowNodeClass) bool UFlowAsset::CanFlowAssetUseFlowNodeClass(const UClass& FlowNodeClass) const { + const bool bIsFlowNode = FlowNodeClass.IsChildOf(); + if (!bIsFlowNode) + { + check(FlowNodeClass.IsChildOf()); + + // AddOns don't have the AllowedAssetClasses/DeniedAssetClasses + // (yet? maybe we move it up to the base?) + + return true; + } + // UFlowAsset class can limit which UFlowNode classes can be used for (const UClass* DeniedNodeClass : DeniedNodeClasses) { @@ -209,7 +225,7 @@ bool UFlowAsset::CanFlowAssetUseFlowNodeClass(const UClass& FlowNodeClass) const } } - if (AllowedNodeClasses.Num() > 0) + if (bIsFlowNode && AllowedNodeClasses.Num() > 0) { bool bAllowedInAsset = false; for (const UClass* AllowedNodeClass : AllowedNodeClasses) @@ -229,14 +245,14 @@ bool UFlowAsset::CanFlowAssetUseFlowNodeClass(const UClass& FlowNodeClass) const return true; } -bool UFlowAsset::CanFlowAssetReferenceFlowNode(const UClass& FlowNodeClass, FText* OutOptionalFailureReason) const +bool UFlowAsset::CanFlowAssetReferenceFlowNode(const UClass& FlowNodeOrAddOnClass, FText* OutOptionalFailureReason) const { - if (!GEditor || !IsValid(&FlowNodeClass)) + if (!GEditor || !IsValid(&FlowNodeOrAddOnClass)) { return false; } - FAssetData FlowNodeAssetData(&FlowNodeClass); + FAssetData FlowNodeAssetData(&FlowNodeOrAddOnClass); FAssetReferenceFilterContext AssetReferenceFilterContext; AssetReferenceFilterContext.ReferencingAssets.Add(FAssetData(this)); @@ -891,7 +907,7 @@ bool UFlowAsset::IsBoundToWorld_Implementation() } #if WITH_EDITOR -void UFlowAsset::LogError(const FString& MessageToLog, UFlowNode* Node) +void UFlowAsset::LogError(const FString& MessageToLog, UFlowNodeBase* Node) { // this is runtime log which is should be only called on runtime instances of asset if (TemplateAsset == nullptr) @@ -906,7 +922,7 @@ void UFlowAsset::LogError(const FString& MessageToLog, UFlowNode* Node) } } -void UFlowAsset::LogWarning(const FString& MessageToLog, UFlowNode* Node) +void UFlowAsset::LogWarning(const FString& MessageToLog, UFlowNodeBase* Node) { // this is runtime log which is should be only called on runtime instances of asset if (TemplateAsset == nullptr) @@ -921,7 +937,7 @@ void UFlowAsset::LogWarning(const FString& MessageToLog, UFlowNode* Node) } } -void UFlowAsset::LogNote(const FString& MessageToLog, UFlowNode* Node) +void UFlowAsset::LogNote(const FString& MessageToLog, UFlowNodeBase* Node) { // this is runtime log which is should be only called on runtime instances of asset if (TemplateAsset == nullptr) diff --git a/Source/Flow/Private/FlowMessageLog.cpp b/Source/Flow/Private/FlowMessageLog.cpp index f6e171af9..48620b2da 100644 --- a/Source/Flow/Private/FlowMessageLog.cpp +++ b/Source/Flow/Private/FlowMessageLog.cpp @@ -15,10 +15,10 @@ FFlowGraphToken::FFlowGraphToken(const UFlowAsset* InFlowAsset) CachedText = FText::FromString(InFlowAsset->GetClass()->GetPathName()); } -FFlowGraphToken::FFlowGraphToken(const UFlowNode* InFlowNode) - : GraphNode(InFlowNode->GetGraphNode()) +FFlowGraphToken::FFlowGraphToken(const UFlowNodeBase* InFlowNodeBase) + : GraphNode(InFlowNodeBase->GetGraphNode()) { - CachedText = InFlowNode->GetNodeTitle(); + CachedText = InFlowNodeBase->GetNodeTitle(); } FFlowGraphToken::FFlowGraphToken(UEdGraphNode* InGraphNode, const UEdGraphPin* InPin) @@ -50,11 +50,11 @@ TSharedPtr FFlowGraphToken::Create(const UFlowAsset* InFlowAsset, return nullptr; } -TSharedPtr FFlowGraphToken::Create(const UFlowNode* InFlowNode, FTokenizedMessage& Message) +TSharedPtr FFlowGraphToken::Create(const UFlowNodeBase* InFlowNodeBase, FTokenizedMessage& Message) { - if (InFlowNode) + if (InFlowNodeBase) { - Message.AddToken(MakeShareable(new FFlowGraphToken(InFlowNode))); + Message.AddToken(MakeShareable(new FFlowGraphToken(InFlowNodeBase))); return Message.GetMessageTokens().Last(); } diff --git a/Source/Flow/Private/Interfaces/FlowContextPinSupplierInterface.cpp b/Source/Flow/Private/Interfaces/FlowContextPinSupplierInterface.cpp new file mode 100644 index 000000000..68eacf8d3 --- /dev/null +++ b/Source/Flow/Private/Interfaces/FlowContextPinSupplierInterface.cpp @@ -0,0 +1,13 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Interfaces/FlowContextPinSupplierInterface.h" + +TArray IFlowContextPinSupplierInterface::K2_GetContextInputs_Implementation() const +{ + return TArray(); +} + +TArray IFlowContextPinSupplierInterface::K2_GetContextOutputs_Implementation() const +{ + return TArray(); +} diff --git a/Source/Flow/Private/Interfaces/FlowExternalExecutableInterface.cpp b/Source/Flow/Private/Interfaces/FlowExternalExecutableInterface.cpp new file mode 100644 index 000000000..7ed8b1fa9 --- /dev/null +++ b/Source/Flow/Private/Interfaces/FlowExternalExecutableInterface.cpp @@ -0,0 +1,9 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Interfaces/FlowExternalExecutableInterface.h" +#include "Interfaces/FlowNativeExecutableInterface.h" + +void IFlowExternalExecutableInterface::PreActivateExternalFlowExecutable(IFlowNativeExecutableInterface& NativeExecutorProxy) +{ + Execute_K2_PreActivateExternalFlowExecutable(Cast(this), TScriptInterface(CastChecked(&NativeExecutorProxy))); +} diff --git a/Source/Flow/Private/Interfaces/FlowNativeExecutableInterface.cpp b/Source/Flow/Private/Interfaces/FlowNativeExecutableInterface.cpp new file mode 100644 index 000000000..e34a75957 --- /dev/null +++ b/Source/Flow/Private/Interfaces/FlowNativeExecutableInterface.cpp @@ -0,0 +1,24 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Interfaces/FlowNativeExecutableInterface.h" + +void IFlowNativeExecutableInterface::TriggerOutputPin(const FFlowOutputPinHandle Pin, const bool bFinish, const EFlowPinActivationType ActivationType) +{ + TriggerOutput(Pin.PinName, bFinish, ActivationType); +} + +void IFlowNativeExecutableInterface::TriggerOutput(const FString& PinName, const bool bFinish) +{ + TriggerOutput(FName(PinName), bFinish); +} + +void IFlowNativeExecutableInterface::TriggerOutput(const FText& PinName, const bool bFinish) +{ + TriggerOutput(FName(PinName.ToString()), bFinish); +} + +void IFlowNativeExecutableInterface::TriggerOutput(const TCHAR* PinName, const bool bFinish) +{ + TriggerOutput(FName(PinName), bFinish); +} + diff --git a/Source/Flow/Private/Interfaces/FlowPredicateInterface.cpp b/Source/Flow/Private/Interfaces/FlowPredicateInterface.cpp new file mode 100644 index 000000000..96d675b70 --- /dev/null +++ b/Source/Flow/Private/Interfaces/FlowPredicateInterface.cpp @@ -0,0 +1,20 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Interfaces/FlowPredicateInterface.h" +#include "AddOns/FlowNodeAddOn.h" + +bool IFlowPredicateInterface::ImplementsInterfaceSafe(const UFlowNodeAddOn* AddOnTemplate) +{ + if (!IsValid(AddOnTemplate)) + { + return false; + } + + UClass* AddOnClass = AddOnTemplate->GetClass(); + if (AddOnClass->ImplementsInterface(UFlowPredicateInterface::StaticClass())) + { + return true; + } + + return false; +} diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index f9be6d897..f07455bc8 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -1,30 +1,21 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "Nodes/FlowNode.h" +#include "Interfaces/FlowOwnerInterface.h" +#include "AddOns/FlowNodeAddOn.h" #include "FlowAsset.h" -#include "FlowLogChannels.h" -#include "FlowOwnerInterface.h" #include "FlowSettings.h" -#include "FlowSubsystem.h" -#include "FlowTypes.h" #include "Components/ActorComponent.h" -#include "Engine/Blueprint.h" -#include "Engine/Engine.h" -#include "Engine/ViewportStatsSubsystem.h" -#include "Engine/World.h" -#include "GameFramework/Actor.h" -#include "Misc/App.h" -#include "Misc/Paths.h" -#include "Serialization/MemoryReader.h" -#include "Serialization/MemoryWriter.h" - #if WITH_EDITOR #include "Editor.h" #endif -#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode) +#include "GameFramework/Actor.h" +#include "Misc/App.h" +#include "Serialization/MemoryReader.h" +#include "Serialization/MemoryWriter.h" FFlowPin UFlowNode::DefaultInputPin(TEXT("In")); FFlowPin UFlowNode::DefaultOutputPin(TEXT("Out")); @@ -36,12 +27,6 @@ FString UFlowNode::NoActorsFound = TEXT("No actors found"); UFlowNode::UFlowNode(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) - , GraphNode(nullptr) -#if WITH_EDITOR - , bCanDelete(true) - , bCanDuplicate(true) - , bNodeDeprecated(false) -#endif , AllowedSignalModes({EFlowSignalMode::Enabled, EFlowSignalMode::Disabled, EFlowSignalMode::PassThrough}) , SignalMode(EFlowSignalMode::Enabled) , bPreloaded(false) @@ -58,14 +43,22 @@ UFlowNode::UFlowNode(const FObjectInitializer& ObjectInitializer) } #if WITH_EDITOR + void UFlowNode::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { Super::PostEditChangeProperty(PropertyChangedEvent); - if (PropertyChangedEvent.Property - && (PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(UFlowNode, InputPins) || PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(UFlowNode, OutputPins) - || PropertyChangedEvent.GetMemberPropertyName() == GET_MEMBER_NAME_CHECKED(UFlowNode, InputPins) || PropertyChangedEvent.GetMemberPropertyName() == GET_MEMBER_NAME_CHECKED(UFlowNode, OutputPins))) + if (!PropertyChangedEvent.Property) + { + return; + } + + const FName PropertyName = PropertyChangedEvent.GetPropertyName(); + + if (PropertyName == GET_MEMBER_NAME_CHECKED(UFlowNode, InputPins) || + PropertyName == GET_MEMBER_NAME_CHECKED(UFlowNode, OutputPins)) { + // Potentially need to rebuild the pins from the this node OnReconstructionRequested.ExecuteIfBound(); } } @@ -78,210 +71,114 @@ void UFlowNode::PostLoad() FixNode(nullptr); } -void UFlowNode::FixNode(UEdGraphNode* NewGraphNode) +#endif + +bool UFlowNode::IsSupportedInputPinName(const FName& PinName) const { - // Fix any node pointers that may be out of date - if (NewGraphNode) + if (AddOns.IsEmpty()) { - GraphNode = NewGraphNode; - } -} + checkf(FindFlowPinByName(PinName, InputPins), TEXT("Only AddOns should introduce unknown Pins to a FlowNode, so if we have no AddOns, we should have no unknown pins")); -void UFlowNode::SetGraphNode(UEdGraphNode* NewGraph) -{ - GraphNode = NewGraph; -} + return true; + } -FString UFlowNode::GetNodeCategory() const -{ - if (GetClass()->ClassGeneratedBy) + if (const FFlowPin* FoundInputFlowPin = FindFlowPinByName(PinName, InputPins)) { - const FString& BlueprintCategory = Cast(GetClass()->ClassGeneratedBy)->BlueprintCategory; - if (!BlueprintCategory.IsEmpty()) - { - return BlueprintCategory; - } + return true; } - - return Category; -} - -FText UFlowNode::GetNodeTitle() const -{ - if (GetClass()->ClassGeneratedBy) + else { - const FString& BlueprintTitle = Cast(GetClass()->ClassGeneratedBy)->BlueprintDisplayName; - if (!BlueprintTitle.IsEmpty()) - { - return FText::FromString(BlueprintTitle); - } + return false; } - - return GetClass()->GetDisplayNameText(); } -FText UFlowNode::GetNodeToolTip() const +void UFlowNode::AddInputPins(TArray Pins) { - if (GetClass()->ClassGeneratedBy) + for (const FFlowPin& Pin : Pins) { - const FString& BlueprintToolTip = Cast(GetClass()->ClassGeneratedBy)->BlueprintDescription; - if (!BlueprintToolTip.IsEmpty()) - { - return FText::FromString(BlueprintToolTip); - } + InputPins.Emplace(Pin); } - - return GetClass()->GetToolTipText(); } -bool UFlowNode::GetDynamicTitleColor(FLinearColor& OutColor) const +void UFlowNode::AddOutputPins(TArray Pins) { - if (NodeStyle == EFlowNodeStyle::Custom) + for (const FFlowPin& Pin : Pins) { - OutColor = NodeColor; - return true; + OutputPins.Emplace(Pin); } - - return false; } -FString UFlowNode::GetNodeDescription() const -{ - return K2_GetNodeDescription(); -} -#endif - -UFlowAsset* UFlowNode::GetFlowAsset() const -{ - return GetOuter() ? Cast(GetOuter()) : nullptr; -} +#if WITH_EDITOR -AActor* UFlowNode::TryGetRootFlowActorOwner() const +bool UFlowNode::RebuildPinArray(const TArray& NewPinNames, TArray& InOutPins, const FFlowPin& DefaultPin) { - AActor* OwningActor = nullptr; + bool bIsChanged = false; - UObject* RootFlowOwner = TryGetRootFlowObjectOwner(); + TArray NewPins; - if (IsValid(RootFlowOwner)) + if (NewPinNames.Num() == 0) { - // Check if the immediate parent is an AActor - OwningActor = Cast(RootFlowOwner); - - if (!IsValid(OwningActor)) - { - // Check if the if the immediate parent is an UActorComponent - // and return that Component's Owning actor - if (const UActorComponent* OwningComponent = Cast(RootFlowOwner)) - { - OwningActor = OwningComponent->GetOwner(); - } - } - } + bIsChanged = true; - return OwningActor; -} - -UObject* UFlowNode::TryGetRootFlowObjectOwner() const -{ - const UFlowAsset* FlowAsset = GetFlowAsset(); + NewPins.Reserve(1); - if (IsValid(FlowAsset)) - { - return FlowAsset->GetOwner(); + NewPins.Add(DefaultPin); } + else + { + const bool bIsSameNum = (NewPinNames.Num() == InOutPins.Num()); - return nullptr; -} + bIsChanged = !bIsSameNum; -IFlowOwnerInterface* UFlowNode::GetFlowOwnerInterface() const -{ - const UFlowAsset* FlowAsset = GetFlowAsset(); - if (!IsValid(FlowAsset)) - { - return nullptr; - } + NewPins.Reserve(NewPinNames.Num()); - const UClass* ExpectedOwnerClass = FlowAsset->GetExpectedOwnerClass(); - if (!IsValid(ExpectedOwnerClass)) - { - return nullptr; - } + for (int32 NewPinIndex = 0; NewPinIndex < NewPinNames.Num(); ++NewPinIndex) + { + const FName& NewPinName = NewPinNames[NewPinIndex]; + NewPins.Add(FFlowPin(NewPinName)); - UObject* RootFlowOwner = FlowAsset->GetOwner(); - if (!IsValid(RootFlowOwner)) - { - return nullptr; + if (bIsSameNum) + { + bIsChanged = bIsChanged || (NewPinName != InOutPins[NewPinIndex].PinName); + } + } } - if (IFlowOwnerInterface* FlowOwnerInterface = TryGetFlowOwnerInterfaceFromRootFlowOwner(*RootFlowOwner, *ExpectedOwnerClass)) + if (bIsChanged) { - return FlowOwnerInterface; - } + InOutPins.Reset(); - if (IFlowOwnerInterface* FlowOwnerInterface = TryGetFlowOwnerInterfaceActor(*RootFlowOwner, *ExpectedOwnerClass)) - { - return FlowOwnerInterface; - } + check(NewPins.Num() > 0); - return nullptr; -} - -IFlowOwnerInterface* UFlowNode::TryGetFlowOwnerInterfaceFromRootFlowOwner(UObject& RootFlowOwner, const UClass& ExpectedOwnerClass) const -{ - const UClass* RootFlowOwnerClass = RootFlowOwner.GetClass(); - if (!IsValid(RootFlowOwnerClass)) - { - return nullptr; - } + if (&InOutPins == &InputPins) + { + AddInputPins(NewPins); + } + else + { + checkf(&InOutPins == &OutputPins, TEXT("Only expected to be called with one or the other of the pin arrays")); - if (!RootFlowOwnerClass->IsChildOf(&ExpectedOwnerClass)) - { - return nullptr; + AddOutputPins(NewPins); + } } - // If the immediate owner is the expected class type, return its FlowOwnerInterface - return CastChecked(&RootFlowOwner); + return bIsChanged; } -IFlowOwnerInterface* UFlowNode::TryGetFlowOwnerInterfaceActor(UObject& RootFlowOwner, const UClass& ExpectedOwnerClass) const +bool UFlowNode::RebuildPinArray(const TArray& NewPins, TArray& InOutPins, const FFlowPin& DefaultPin) { - // Special case if the immediate owner is a component, also consider the component's owning actor - const UActorComponent* FlowComponent = Cast(&RootFlowOwner); - if (!IsValid(FlowComponent)) - { - return nullptr; - } + TArray NewPinNames; + NewPinNames.Reserve(NewPins.Num()); - AActor* ActorOwner = FlowComponent->GetOwner(); - if (!IsValid(ActorOwner)) + for (const FFlowPin& NewPin : NewPins) { - return nullptr; + NewPinNames.Add(NewPin.PinName); } - const UClass* ActorOwnerClass = ActorOwner->GetClass(); - if (!ActorOwnerClass->IsChildOf(&ExpectedOwnerClass)) - { - return nullptr; - } - - return CastChecked(ActorOwner); -} - -void UFlowNode::AddInputPins(TArray Pins) -{ - for (const FFlowPin& Pin : Pins) - { - InputPins.Emplace(Pin); - } + return RebuildPinArray(NewPinNames, InOutPins, DefaultPin); } -void UFlowNode::AddOutputPins(TArray Pins) -{ - for (const FFlowPin& Pin : Pins) - { - OutputPins.Emplace(Pin); - } -} +#endif // WITH_EDITOR void UFlowNode::SetNumberedInputPins(const uint8 FirstNumber, const uint8 LastNumber) { @@ -356,6 +253,20 @@ TArray UFlowNode::GetOutputNames() const } #if WITH_EDITOR + +bool UFlowNode::SupportsContextPins() const +{ + for (const UFlowNodeAddOn* AddOn : AddOns) + { + if (IsValid(AddOn) && AddOn->SupportsContextPins()) + { + return true; + } + } + + return false; +} + bool UFlowNode::CanUserAddInput() const { return K2_CanUserAddInput(); @@ -496,26 +407,6 @@ void UFlowNode::RecursiveFindNodesByClass(UFlowNode* Node, const TSubclassOfGetFlowSubsystem() : nullptr; -} - -UWorld* UFlowNode::GetWorld() const -{ - if (GetFlowAsset() && GetFlowAsset()->GetFlowSubsystem()) - { - return GetFlowAsset()->GetFlowSubsystem()->GetWorld(); - } - - return nullptr; -} - -void UFlowNode::InitializeInstance() -{ - K2_InitializeInstance(); -} - void UFlowNode::TriggerPreload() { bPreloaded = true; @@ -528,21 +419,6 @@ void UFlowNode::TriggerFlush() FlushContent(); } -void UFlowNode::PreloadContent() -{ - K2_PreloadContent(); -} - -void UFlowNode::FlushContent() -{ - K2_FlushContent(); -} - -void UFlowNode::OnActivate() -{ - K2_OnActivate(); -} - void UFlowNode::TriggerInput(const FName& PinName, const EFlowPinActivationType ActivationType /*= Default*/) { if (SignalMode == EFlowSignalMode::Disabled) @@ -606,11 +482,6 @@ void UFlowNode::TriggerInput(const FName& PinName, const EFlowPinActivationType } } -void UFlowNode::ExecuteInput(const FName& PinName) -{ - K2_ExecuteInput(PinName); -} - void UFlowNode::TriggerFirstOutput(const bool bFinish) { if (OutputPins.Num() > 0) @@ -619,7 +490,7 @@ void UFlowNode::TriggerFirstOutput(const bool bFinish) } } -void UFlowNode::TriggerOutput(const FName& PinName, const bool bFinish /*= false*/, const EFlowPinActivationType ActivationType /*= Default*/) +void UFlowNode::TriggerOutput(const FName PinName, const bool bFinish /*= false*/, const EFlowPinActivationType ActivationType /*= Default*/) { // clean up node, if needed if (bFinish) @@ -655,26 +526,6 @@ void UFlowNode::TriggerOutput(const FName& PinName, const bool bFinish /*= false } } -void UFlowNode::TriggerOutputPin(const FFlowOutputPinHandle Pin, const bool bFinish, const EFlowPinActivationType ActivationType /*= Default*/) -{ - TriggerOutput(Pin.PinName, bFinish, ActivationType); -} - -void UFlowNode::TriggerOutput(const FString& PinName, const bool bFinish) -{ - TriggerOutput(*PinName, bFinish); -} - -void UFlowNode::TriggerOutput(const FText& PinName, const bool bFinish) -{ - TriggerOutput(*PinName.ToString(), bFinish); -} - -void UFlowNode::TriggerOutput(const TCHAR* PinName, const bool bFinish) -{ - TriggerOutput(FName(PinName), bFinish); -} - void UFlowNode::Finish() { Deactivate(); @@ -695,21 +546,6 @@ void UFlowNode::Deactivate() Cleanup(); } -void UFlowNode::Cleanup() -{ - K2_Cleanup(); -} - -void UFlowNode::DeinitializeInstance() -{ - K2_DeinitializeInstance(); -} - -void UFlowNode::ForceFinishNode() -{ - K2_ForceFinishNode(); -} - void UFlowNode::ResetRecords() { ActivationState = EFlowNodeState::NeverActivated; @@ -784,16 +620,6 @@ void UFlowNode::OnPassThrough_Implementation() } #if WITH_EDITOR -UFlowNode* UFlowNode::GetInspectedInstance() const -{ - if (const UFlowAsset* FlowInstance = GetFlowAsset()->GetInspectedInstance()) - { - return FlowInstance->GetNode(GetGuid()); - } - - return nullptr; -} - TMap UFlowNode::GetWireRecords() const { TMap Result; @@ -816,31 +642,6 @@ TArray UFlowNode::GetPinRecords(const FName& PinName, const EEdGraph return TArray(); } } - -FString UFlowNode::GetStatusString() const -{ - return K2_GetStatusString(); -} - -bool UFlowNode::GetStatusBackgroundColor(FLinearColor& OutColor) const -{ - return K2_GetStatusBackgroundColor(OutColor); -} - -FString UFlowNode::GetAssetPath() -{ - return K2_GetAssetPath(); -} - -UObject* UFlowNode::GetAssetToEdit() -{ - return K2_GetAssetToEdit(); -} - -AActor* UFlowNode::GetActorToFocus() -{ - return K2_GetActorToFocus(); -} #endif FString UFlowNode::GetIdentityTagDescription(const FGameplayTag& Tag) @@ -868,85 +669,39 @@ FString UFlowNode::GetProgressAsString(const float Value) return FString::Printf(TEXT("%.*f"), 2, Value); } -void UFlowNode::LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType) +#if WITH_EDITOR +UFlowNode* UFlowNode::GetInspectedInstance() const { -#if !UE_BUILD_SHIPPING - if (BuildMessage(Message)) + if (const UFlowAsset* FlowInstance = GetFlowAsset()->GetInspectedInstance()) { - // OnScreen Message - if (OnScreenMessageType == EFlowOnScreenMessageType::Permanent) - { - if (GetWorld()) - { - if (UViewportStatsSubsystem* StatsSubsystem = GetWorld()->GetSubsystem()) - { - StatsSubsystem->AddDisplayDelegate([this, Message](FText& OutText, FLinearColor& OutColor) - { - OutText = FText::FromString(Message); - OutColor = FLinearColor::Red; - return IsValid(this) && ActivationState != EFlowNodeState::NeverActivated; - }); - } - } - } - else - { - GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, Message); - } - - // Output Log - UE_LOG(LogFlow, Error, TEXT("%s"), *Message); - - // Message Log -#if WITH_EDITOR - GetFlowAsset()->GetTemplateAsset()->LogError(Message, this); -#endif + return FlowInstance->GetNode(GetGuid()); } -#endif + + return nullptr; } -void UFlowNode::LogWarning(FString Message) +FString UFlowNode::GetStatusString() const { -#if !UE_BUILD_SHIPPING - if (BuildMessage(Message)) - { - // Output Log - UE_LOG(LogFlow, Warning, TEXT("%s"), *Message); - - // Message Log -#if WITH_EDITOR - GetFlowAsset()->GetTemplateAsset()->LogWarning(Message, this); -#endif - } -#endif + return K2_GetStatusString(); } -void UFlowNode::LogNote(FString Message) +bool UFlowNode::GetStatusBackgroundColor(FLinearColor& OutColor) const { -#if !UE_BUILD_SHIPPING - if (BuildMessage(Message)) - { - // Output Log - UE_LOG(LogFlow, Log, TEXT("%s"), *Message); + return K2_GetStatusBackgroundColor(OutColor); +} - // Message Log -#if WITH_EDITOR - GetFlowAsset()->GetTemplateAsset()->LogNote(Message, this); -#endif - } -#endif +FString UFlowNode::GetAssetPath() +{ + return K2_GetAssetPath(); } -#if !UE_BUILD_SHIPPING -bool UFlowNode::BuildMessage(FString& Message) const +UObject* UFlowNode::GetAssetToEdit() { - if (GetFlowAsset()->TemplateAsset) // this is runtime log which is should be only called on runtime instances of asset - { - const FString TemplatePath = GetFlowAsset()->TemplateAsset->GetPathName(); - Message.Append(TEXT(" --- node ")).Append(GetName()).Append(TEXT(", asset ")).Append(FPaths::GetPath(TemplatePath) / FPaths::GetBaseFilename(TemplatePath)); - return true; - } + return K2_GetAssetToEdit(); +} - return false; +AActor* UFlowNode::GetActorToFocus() +{ + return K2_GetActorToFocus(); } #endif diff --git a/Source/Flow/Private/Nodes/FlowNodeAddOnBlueprint.cpp b/Source/Flow/Private/Nodes/FlowNodeAddOnBlueprint.cpp new file mode 100644 index 000000000..5dc91d674 --- /dev/null +++ b/Source/Flow/Private/Nodes/FlowNodeAddOnBlueprint.cpp @@ -0,0 +1,8 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Nodes/FlowNodeAddOnBlueprint.h" + +UFlowNodeAddOnBlueprint::UFlowNodeAddOnBlueprint(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ +} diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp new file mode 100644 index 000000000..9379b1d23 --- /dev/null +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -0,0 +1,647 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Nodes/FlowNodeBase.h" + +#include "AddOns/FlowNodeAddOn.h" +#include "FlowAsset.h" +#include "FlowLogChannels.h" +#include "FlowSettings.h" +#include "FlowSubsystem.h" +#include "FlowTypes.h" + +#include "Components/ActorComponent.h" +#if WITH_EDITOR +#include "Editor.h" +#endif + +#include "Engine/Blueprint.h" +#include "Engine/Engine.h" +#include "Engine/ViewportStatsSubsystem.h" +#include "Engine/World.h" +#include "GameFramework/Actor.h" +#include "Misc/App.h" +#include "Misc/Paths.h" +#include "Serialization/MemoryReader.h" +#include "Serialization/MemoryWriter.h" +#include "Engine/Blueprint.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNodeBase) + +UFlowNodeBase::UFlowNodeBase(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ +} + +#if WITH_EDITOR +void UFlowNodeBase::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + if (!PropertyChangedEvent.Property) + { + return; + } + + const FName PropertyName = PropertyChangedEvent.GetPropertyName(); + + if (PropertyName == GET_MEMBER_NAME_CHECKED(UFlowNode, AddOns)) + { + // Potentially need to rebuild the pins from the AddOns of this node + OnReconstructionRequested.ExecuteIfBound(); + } + + UpdateNodeConfigText(); +} + +void UFlowNodeBase::FixNode(UEdGraphNode* NewGraphNode) +{ + // Fix any node pointers that may be out of date + if (NewGraphNode) + { + GraphNode = NewGraphNode; + } +} + +void UFlowNodeBase::SetGraphNode(UEdGraphNode* NewGraphNode) +{ + GraphNode = NewGraphNode; + + UpdateNodeConfigText(); +} + +void UFlowNodeBase::SetupForEditing(UEdGraphNode& EdGraphNode) +{ + SetGraphNode(&EdGraphNode); + + // Refresh the Config text when setting up this FlowNodeBase for editing + UpdateNodeConfigText(); +} + +FString UFlowNodeBase::GetNodeCategory() const +{ + if (GetClass()->ClassGeneratedBy) + { + const FString& BlueprintCategory = Cast(GetClass()->ClassGeneratedBy)->BlueprintCategory; + if (!BlueprintCategory.IsEmpty()) + { + return BlueprintCategory; + } + } + + return Category; +} + +FText UFlowNodeBase::GetNodeTitle() const +{ + if (GetClass()->ClassGeneratedBy) + { + const FString& BlueprintTitle = Cast(GetClass()->ClassGeneratedBy)->BlueprintDisplayName; + if (!BlueprintTitle.IsEmpty()) + { + return FText::FromString(BlueprintTitle); + } + } + + return GetClass()->GetDisplayNameText(); +} + +FText UFlowNodeBase::GetNodeToolTip() const +{ + if (GetClass()->ClassGeneratedBy) + { + const FString& BlueprintToolTip = Cast(GetClass()->ClassGeneratedBy)->BlueprintDescription; + if (!BlueprintToolTip.IsEmpty()) + { + return FText::FromString(BlueprintToolTip); + } + } + + return GetClass()->GetToolTipText(); +} + +FText UFlowNodeBase::GetNodeConfigText() const +{ + return DevNodeConfigText; +} + +bool UFlowNodeBase::GetDynamicTitleColor(FLinearColor& OutColor) const +{ + if (NodeStyle == EFlowNodeStyle::Custom) + { + OutColor = NodeColor; + return true; + } + + return false; +} + +FString UFlowNodeBase::GetNodeDescription() const +{ + return K2_GetNodeDescription(); +} +#endif + +UFlowAsset* UFlowNodeBase::GetFlowAsset() const +{ + // In the case of an AddOn, we want our containing FlowNode's Outer, not our own + const UFlowNode* FlowNode = GetFlowNodeSelfOrOwner(); + return FlowNode && FlowNode->GetOuter() ? Cast(FlowNode->GetOuter()) : Cast(GetOuter()); +} + +UFlowSubsystem* UFlowNodeBase::GetFlowSubsystem() const +{ + return GetFlowAsset() ? GetFlowAsset()->GetFlowSubsystem() : nullptr; +} + +AActor* UFlowNodeBase::TryGetRootFlowActorOwner() const +{ + AActor* OwningActor = nullptr; + + UObject* RootFlowOwner = TryGetRootFlowObjectOwner(); + + if (IsValid(RootFlowOwner)) + { + // Check if the immediate parent is an AActor + OwningActor = Cast(RootFlowOwner); + + if (!IsValid(OwningActor)) + { + // Check if the if the immediate parent is an UActorComponent + // and return that Component's Owning actor + if (const UActorComponent* OwningComponent = Cast(RootFlowOwner)) + { + OwningActor = OwningComponent->GetOwner(); + } + } + } + + return OwningActor; +} + +UObject* UFlowNodeBase::TryGetRootFlowObjectOwner() const +{ + const UFlowAsset* FlowAsset = GetFlowAsset(); + + if (IsValid(FlowAsset)) + { + return FlowAsset->GetOwner(); + } + + return nullptr; +} + +IFlowOwnerInterface* UFlowNodeBase::GetFlowOwnerInterface() const +{ + const UFlowAsset* FlowAsset = GetFlowAsset(); + if (!IsValid(FlowAsset)) + { + return nullptr; + } + + const UClass* ExpectedOwnerClass = FlowAsset->GetExpectedOwnerClass(); + if (!IsValid(ExpectedOwnerClass)) + { + return nullptr; + } + + UObject* RootFlowOwner = FlowAsset->GetOwner(); + if (!IsValid(RootFlowOwner)) + { + return nullptr; + } + + if (IFlowOwnerInterface* FlowOwnerInterface = TryGetFlowOwnerInterfaceFromRootFlowOwner(*RootFlowOwner, *ExpectedOwnerClass)) + { + return FlowOwnerInterface; + } + + if (IFlowOwnerInterface* FlowOwnerInterface = TryGetFlowOwnerInterfaceActor(*RootFlowOwner, *ExpectedOwnerClass)) + { + return FlowOwnerInterface; + } + + return nullptr; +} + +IFlowOwnerInterface* UFlowNodeBase::TryGetFlowOwnerInterfaceFromRootFlowOwner(UObject& RootFlowOwner, const UClass& ExpectedOwnerClass) const +{ + const UClass* RootFlowOwnerClass = RootFlowOwner.GetClass(); + if (!IsValid(RootFlowOwnerClass)) + { + return nullptr; + } + + if (!RootFlowOwnerClass->IsChildOf(&ExpectedOwnerClass)) + { + return nullptr; + } + + // If the immediate owner is the expected class type, return its FlowOwnerInterface + return CastChecked(&RootFlowOwner); +} + +IFlowOwnerInterface* UFlowNodeBase::TryGetFlowOwnerInterfaceActor(UObject& RootFlowOwner, const UClass& ExpectedOwnerClass) const +{ + // Special case if the immediate owner is a component, also consider the component's owning actor + const UActorComponent* FlowComponent = Cast(&RootFlowOwner); + if (!IsValid(FlowComponent)) + { + return nullptr; + } + + AActor* ActorOwner = FlowComponent->GetOwner(); + if (!IsValid(ActorOwner)) + { + return nullptr; + } + + const UClass* ActorOwnerClass = ActorOwner->GetClass(); + if (!ActorOwnerClass->IsChildOf(&ExpectedOwnerClass)) + { + return nullptr; + } + + return CastChecked(ActorOwner); +} + +void UFlowNodeBase::SetNodeConfigText(const FText& NodeConfigText) +{ +#if WITH_EDITOR + if (!NodeConfigText.EqualTo(DevNodeConfigText)) + { + DevNodeConfigText = NodeConfigText; + + Modify(); + } +#endif // WITH_EDITOR +} + +void UFlowNodeBase::UpdateNodeConfigText_Implementation() +{ +} + +UWorld* UFlowNodeBase::GetWorld() const +{ + if (UFlowAsset* FlowAsset = GetFlowAsset()) + { + if (UObject* FlowAssetOwner = FlowAsset->GetOwner()) + { + return FlowAssetOwner->GetWorld(); + } + } + + if (UFlowSubsystem* FlowSubsystem = GetFlowSubsystem()) + { + return FlowSubsystem->GetWorld(); + } + + return nullptr; +} + +void UFlowNodeBase::LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType) +{ +#if !UE_BUILD_SHIPPING + if (BuildMessage(Message)) + { + // OnScreen Message + if (OnScreenMessageType == EFlowOnScreenMessageType::Permanent) + { + if (GetWorld()) + { + if (UViewportStatsSubsystem* StatsSubsystem = GetWorld()->GetSubsystem()) + { + StatsSubsystem->AddDisplayDelegate([this, Message](FText& OutText, FLinearColor& OutColor) + { + OutText = FText::FromString(Message); + OutColor = FLinearColor::Red; + + return IsValid(this) && GetFlowNodeSelfOrOwner()->GetActivationState() != EFlowNodeState::NeverActivated; + }); + } + } + } + else + { + GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, Message); + } + + // Output Log + UE_LOG(LogFlow, Error, TEXT("%s"), *Message); + + // Message Log +#if WITH_EDITOR + GetFlowAsset()->GetTemplateAsset()->LogError(Message, this); +#endif + } +#endif +} + +void UFlowNodeBase::LogWarning(FString Message) +{ +#if !UE_BUILD_SHIPPING + if (BuildMessage(Message)) + { + // Output Log + UE_LOG(LogFlow, Warning, TEXT("%s"), *Message); + + // Message Log +#if WITH_EDITOR + GetFlowAsset()->GetTemplateAsset()->LogWarning(Message, this); +#endif + } +#endif +} + +void UFlowNodeBase::LogNote(FString Message) +{ +#if !UE_BUILD_SHIPPING + if (BuildMessage(Message)) + { + // Output Log + UE_LOG(LogFlow, Log, TEXT("%s"), *Message); + + // Message Log +#if WITH_EDITOR + GetFlowAsset()->GetTemplateAsset()->LogNote(Message, this); +#endif + } +#endif +} + +#if !UE_BUILD_SHIPPING +bool UFlowNodeBase::BuildMessage(FString& Message) const +{ + if (GetFlowAsset()->GetTemplateAsset()) // this is runtime log which is should be only called on runtime instances of asset + { + const FString TemplatePath = GetFlowAsset()->GetTemplateAsset()->GetPathName(); + Message.Append(TEXT(" --- node ")).Append(GetName()).Append(TEXT(", asset ")).Append(FPaths::GetPath(TemplatePath) / FPaths::GetBaseFilename(TemplatePath)); + + return true; + } + + return false; +} + +#endif + +EFlowAddOnAcceptResult UFlowNodeBase::AcceptFlowNodeAddOnChild_Implementation(const UFlowNodeAddOn* AddOnTemplate) const +{ + // Subclasses may override this function to allow AddOn children classes + return EFlowAddOnAcceptResult::Undetermined; +} + +#if WITH_EDITOR +EFlowAddOnAcceptResult UFlowNodeBase::CheckAcceptFlowNodeAddOnChild(const UFlowNodeAddOn* AddOnTemplate) const +{ + if (!IsValid(AddOnTemplate)) + { + return EFlowAddOnAcceptResult::Reject; + } + + static_assert(static_cast<__underlying_type(EFlowAddOnAcceptResult)>(EFlowAddOnAcceptResult::Max) == 3, "This code may need updating if the enum values change"); + + EFlowAddOnAcceptResult CombinedResult = EFlowAddOnAcceptResult::Undetermined; + + // Potential parents of AddOns are allowed to decide their eligible AddOn children + const EFlowAddOnAcceptResult AsChildResult = AcceptFlowNodeAddOnChild(AddOnTemplate); + + CombinedResult = CombineFlowAddOnAcceptResult(AsChildResult, CombinedResult); + if (CombinedResult == EFlowAddOnAcceptResult::Reject) + { + return EFlowAddOnAcceptResult::Reject; + } + + // FlowNodeAddOns are allowed to opt-in to their parent + const EFlowAddOnAcceptResult AsParentResult = AddOnTemplate->AcceptFlowNodeAddOnParent(this); + + if (AsParentResult != EFlowAddOnAcceptResult::Reject && + AddOnTemplate->IsA()) + { + const FString Message = FString::Printf(TEXT("%s::AcceptFlowNodeAddOnParent must always Reject for UFlowNode subclasses"), *GetClass()->GetName()); + GetFlowAsset()->GetTemplateAsset()->LogError(Message, const_cast(this)); + + return EFlowAddOnAcceptResult::Reject; + } + + CombinedResult = CombineFlowAddOnAcceptResult(AsParentResult, CombinedResult); + + return CombinedResult; +} +#endif // WITH_EDITOR + +void UFlowNodeBase::InitializeInstance() +{ + IFlowCoreExecutableInterface::InitializeInstance(); + + if (!AddOns.IsEmpty()) + { + TArray SourceAddOns = AddOns; + AddOns.Reset(); + + for (UFlowNodeAddOn* SourceAddOn : SourceAddOns) + { + // Create a new instance of each AddOn + UFlowNodeAddOn* NewAddOnInstance = NewObject(this, SourceAddOn->GetClass(), NAME_None, RF_Transient, SourceAddOn, false, nullptr); + AddOns.Add(NewAddOnInstance); + } + + for (UFlowNodeAddOn* AddOn : AddOns) + { + // Initialize all of the AddOn instances after they are all allocated + AddOn->InitializeInstance(); + } + } +} + +void UFlowNodeBase::DeinitializeInstance() +{ + for (UFlowNodeAddOn* AddOn : AddOns) + { + AddOn->DeinitializeInstance(); + } + + IFlowCoreExecutableInterface::DeinitializeInstance(); +} + +void UFlowNodeBase::PreloadContent() +{ + IFlowCoreExecutableInterface::PreloadContent(); + + for (UFlowNodeAddOn* AddOn : AddOns) + { + AddOn->PreloadContent(); + } +} + +void UFlowNodeBase::FlushContent() +{ + for (UFlowNodeAddOn* AddOn : AddOns) + { + AddOn->FlushContent(); + } + + IFlowCoreExecutableInterface::FlushContent(); +} + +void UFlowNodeBase::OnActivate() +{ + IFlowCoreExecutableInterface::OnActivate(); + + for (UFlowNodeAddOn* AddOn : AddOns) + { + AddOn->OnActivate(); + } +} + +void UFlowNodeBase::Cleanup() +{ + for (UFlowNodeAddOn* AddOn : AddOns) + { + AddOn->Cleanup(); + } + + IFlowCoreExecutableInterface::Cleanup(); +} + +void UFlowNodeBase::ForceFinishNode() +{ + for (UFlowNodeAddOn* AddOn : AddOns) + { + AddOn->ForceFinishNode(); + } + + IFlowCoreExecutableInterface::ForceFinishNode(); +} + +void UFlowNodeBase::ExecuteInput(const FName& PinName) +{ + // AddOns can introduce input pins to Nodes without the Node being aware of the addition. + // To ensure that Nodes and AddOns only get the input pins signalled that they expect, + // we are filtering the PinName vs. the expected InputPins before carrying on with the ExecuteInput + + if (IsSupportedInputPinName(PinName)) + { + IFlowCoreExecutableInterface::ExecuteInput(PinName); + } + + for (UFlowNodeAddOn* AddOn : AddOns) + { + AddOn->ExecuteInput(PinName); + } +} + +const FFlowPin* UFlowNodeBase::FindFlowPinByName(const FName& PinName, const TArray& FlowPins) +{ + return FlowPins.FindByPredicate([&PinName](const FFlowPin& FlowPin) + { + return FlowPin.PinName == PinName; + }); +} + +void UFlowNodeBase::ForEachAddOnConst(FConstFlowNodeAddOnFunction Function) const +{ + for (UFlowNodeAddOn* AddOn : AddOns) + { + if (IsValid(AddOn)) + { + Function(*AddOn); + + AddOn->ForEachAddOnConst(Function); + } + } +} + +void UFlowNodeBase::ForEachAddOnForClassConst(const UClass& InterfaceOrClass, FConstFlowNodeAddOnFunction Function) const +{ + for (UFlowNodeAddOn* AddOn : AddOns) + { + if (IsValid(AddOn)) + { + // InterfaceOrClass can either be the AddOn's UClass (or its superclass) + // or an interface (the UClass version) that its UClass implements + if (AddOn->IsA(&InterfaceOrClass) || + AddOn->GetClass()->ImplementsInterface(&InterfaceOrClass)) + { + Function(*AddOn); + } + + AddOn->ForEachAddOnForClassConst(InterfaceOrClass, Function); + } + } +} + +void UFlowNodeBase::ForEachAddOn(FFlowNodeAddOnFunction Function) const +{ + for (UFlowNodeAddOn* AddOn : AddOns) + { + if (IsValid(AddOn)) + { + Function(*AddOn); + + AddOn->ForEachAddOn(Function); + } + } +} + +void UFlowNodeBase::ForEachAddOnForClass(const UClass& InterfaceOrClass, FFlowNodeAddOnFunction Function) const +{ + for (UFlowNodeAddOn* AddOn : AddOns) + { + if (IsValid(AddOn)) + { + // InterfaceOrClass can either be the AddOn's UClass (or its superclass) + // or an interface (the UClass version) that its UClass implements + if (AddOn->IsA(&InterfaceOrClass) || + AddOn->GetClass()->ImplementsInterface(&InterfaceOrClass)) + { + Function(*AddOn); + } + + AddOn->ForEachAddOnForClass(InterfaceOrClass, Function); + } + } +} + +#if WITH_EDITOR +TArray UFlowNodeBase::GetContextInputs() const +{ + TArray ContextInputs = IFlowContextPinSupplierInterface::GetContextInputs(); + TArray AddOnInputs; + + for (UFlowNodeAddOn* AddOn : AddOns) + { + AddOnInputs.Append(AddOn->GetContextInputs()); + } + + if (!AddOnInputs.IsEmpty()) + { + for (const FFlowPin& FlowPin : AddOnInputs) + { + ContextInputs.AddUnique(FlowPin); + } + } + + return ContextInputs; +} + +TArray UFlowNodeBase::GetContextOutputs() const +{ + TArray ContextOutputs = IFlowContextPinSupplierInterface::GetContextOutputs(); + TArray AddOnOutputs; + + for (UFlowNodeAddOn* AddOn : AddOns) + { + AddOnOutputs.Append(AddOn->GetContextOutputs()); + } + + if (!AddOnOutputs.IsEmpty()) + { + for (const FFlowPin& FlowPin : AddOnOutputs) + { + ContextOutputs.AddUnique(FlowPin); + } + } + + return ContextOutputs; +} +#endif // WITH_EDITOR diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Branch.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Branch.cpp new file mode 100644 index 000000000..fea562c69 --- /dev/null +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Branch.cpp @@ -0,0 +1,51 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Nodes/Route/FlowNode_Branch.h" +#include "AddOns/FlowNodeAddOn_PredicateAND.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_Branch) + +const FName UFlowNode_Branch::INPIN_Evaluate = TEXT("Evaluate"); +const FName UFlowNode_Branch::OUTPIN_True = TEXT("True"); +const FName UFlowNode_Branch::OUTPIN_False = TEXT("False"); + +UFlowNode_Branch::UFlowNode_Branch(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ +#if WITH_EDITOR + Category = TEXT("Route"); + NodeStyle = EFlowNodeStyle::Logic; +#endif + InputPins.Empty(); + InputPins.Add(FFlowPin(INPIN_Evaluate)); + + OutputPins.Empty(); + OutputPins.Add(FFlowPin(OUTPIN_True)); + OutputPins.Add(FFlowPin(OUTPIN_False)); + + AllowedSignalModes = { EFlowSignalMode::Enabled, EFlowSignalMode::Disabled }; +} + +EFlowAddOnAcceptResult UFlowNode_Branch::AcceptFlowNodeAddOnChild_Implementation(const UFlowNodeAddOn* AddOnTemplate) const +{ + if (IFlowPredicateInterface::ImplementsInterfaceSafe(AddOnTemplate)) + { + return EFlowAddOnAcceptResult::TentativeAccept; + } + + return Super::AcceptFlowNodeAddOnChild_Implementation(AddOnTemplate); +} + +void UFlowNode_Branch::ExecuteInput(const FName& PinName) +{ + bool bResult = UFlowNodeAddOn_PredicateAND::EvaluatePredicateAND(AddOns); + + if (bResult) + { + TriggerOutput(OUTPIN_True); + } + else + { + TriggerOutput(OUTPIN_False); + } +} diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp index 7aa564cd5..d5cda58b5 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp @@ -3,7 +3,6 @@ #include "Nodes/Route/FlowNode_SubGraph.h" #include "FlowAsset.h" -#include "FlowMessageLog.h" #include "FlowSubsystem.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_SubGraph) @@ -121,7 +120,7 @@ EDataValidationResult UFlowNode_SubGraph::ValidateNode() return EDataValidationResult::Valid; } -TArray UFlowNode_SubGraph::GetContextInputs() +TArray UFlowNode_SubGraph::GetContextInputs() const { TArray EventNames; @@ -140,7 +139,7 @@ TArray UFlowNode_SubGraph::GetContextInputs() return EventNames; } -TArray UFlowNode_SubGraph::GetContextOutputs() +TArray UFlowNode_SubGraph::GetContextOutputs() const { TArray Pins; diff --git a/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp b/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp index f6506c1e4..c7bb566ff 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp @@ -4,8 +4,8 @@ #include "FlowAsset.h" #include "FlowLogChannels.h" -#include "FlowOwnerInterface.h" -#include "FlowOwnerFunctionParams.h" +#include "Interfaces/FlowOwnerInterface.h" +#include "Types/FlowOwnerFunctionParams.h" #include "FlowSettings.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_CallOwnerFunction) @@ -19,7 +19,7 @@ UFlowNode_CallOwnerFunction::UFlowNode_CallOwnerFunction(const FObjectInitialize #if WITH_EDITOR NodeStyle = EFlowNodeStyle::Default; Category = TEXT("World"); -#endif +#endif // WITH_EDITOR } void UFlowNode_CallOwnerFunction::ExecuteInput(const FName& PinName) @@ -63,7 +63,7 @@ void UFlowNode_CallOwnerFunction::ExecuteInput(const FName& PinName) Params->PostExecute(); - (void)TryExecuteOutputPin(ResultOutputName); + (void) TryExecuteOutputPin(ResultOutputName); } bool UFlowNode_CallOwnerFunction::TryExecuteOutputPin(const FName& OutputName) @@ -133,142 +133,110 @@ void UFlowNode_CallOwnerFunction::PostEditChangeProperty(struct FPropertyChanged if (MemberPropertyName == GET_MEMBER_NAME_CHECKED(UFlowNode_CallOwnerFunction, Params)) { - OnReconstructionRequested.ExecuteIfBound(); + OnChangedParamsObject(); } const FName PropertyName = PropertyChangedEvent.Property->GetFName(); if (PropertyName == GET_MEMBER_NAME_CHECKED(FFlowOwnerFunctionRef, FunctionName)) { - if (TryAllocateParamsInstance() || FunctionRef.GetFunctionName().IsNone()) + if (TryAllocateParamsInstance()) { - OnReconstructionRequested.ExecuteIfBound(); + OnChangedParamsObject(); } } } -bool UFlowNode_CallOwnerFunction::TryAllocateParamsInstance() +void UFlowNode_CallOwnerFunction::OnChangedParamsObject() { - if (FunctionRef.GetFunctionName().IsNone()) - { - // Throw out the old params object (if any) - Params = nullptr; + bool bChangedPins = false; - return false; + if (IsValid(Params)) + { + bChangedPins = RebuildPinArray(Params->GetInputNames(), InputPins, DefaultInputPin) || bChangedPins; + bChangedPins = RebuildPinArray(Params->GetOutputNames(), OutputPins, DefaultOutputPin) || bChangedPins; } - - const UClass* ExistingParamsClass = GetExistingParamsClass(); - const UClass* RequiredParamsClass = GetRequiredParamsClass(); - - if (!IsValid(RequiredParamsClass)) + else { - return false; + bChangedPins = RebuildPinArray(TArray(&DefaultInputPin.PinName, 1), InputPins, DefaultInputPin) || bChangedPins; + bChangedPins = RebuildPinArray(TArray(&DefaultOutputPin.PinName, 1), OutputPins, DefaultOutputPin) || bChangedPins; } - const bool bNeedsAllocateParams = - !IsValid(ExistingParamsClass) || - ExistingParamsClass != RequiredParamsClass; - - if (!bNeedsAllocateParams) + if (bChangedPins) { - return false; + OnReconstructionRequested.ExecuteIfBound(); } - - // Throw out the old params object (if any) - Params = nullptr; - - // Create the new params object - Params = NewObject(this, RequiredParamsClass); - - return true; } -UClass* UFlowNode_CallOwnerFunction::GetRequiredParamsClass() const +EDataValidationResult UFlowNode_CallOwnerFunction::ValidateNode() { - const UClass* ExpectedOwnerClass = TryGetExpectedOwnerClass(); - if (!IsValid(ExpectedOwnerClass)) + const bool bHasFunction = FunctionRef.IsConfigured(); + if (!bHasFunction) { - return nullptr; - } - - const FName FunctionNameAsName = FunctionRef.GetFunctionName(); + ValidationLog.Error(TEXT("CallOwnerFunction requires a valid Function reference"), this); - if (FunctionNameAsName.IsNone()) - { - return nullptr; + return EDataValidationResult::Invalid; } - - UClass* RequiredParamsClass = GetParamsClassForFunctionName(*ExpectedOwnerClass, FunctionNameAsName); - return RequiredParamsClass; -} -UClass* UFlowNode_CallOwnerFunction::GetExistingParamsClass() const -{ - if (!IsValid(Params)) + const bool bHasParams = IsValid(Params); + if (!bHasParams) { - return nullptr; - } - - UClass* ExistingParamsClass = Params->GetClass(); - return ExistingParamsClass; -} + ValidationLog.Error(TEXT("CallOwnerFunction requires a valid Params object"), this); -UClass* UFlowNode_CallOwnerFunction::GetParamsClassForFunctionName(const UClass& ExpectedOwnerClass, const FName& FunctionName) -{ - const UFunction* Function = ExpectedOwnerClass.FindFunctionByName(FunctionName); - if (IsValid(Function)) - { - return GetParamsClassForFunction(*Function); + return EDataValidationResult::Invalid; } - return nullptr; -} - -FText UFlowNode_CallOwnerFunction::GetNodeTitle() const -{ - const bool bUseAdaptiveNodeTitles = UFlowSettings::Get()->bUseAdaptiveNodeTitles; + checkf(bHasParams && bHasFunction, TEXT("This should be assured by the preceding logic")); - if (bUseAdaptiveNodeTitles && !FunctionRef.GetFunctionName().IsNone()) + const UClass* ExpectedOwnerClass = TryGetExpectedOwnerClass(); + if (!IsValid(ExpectedOwnerClass)) { - const FText FunctionNameText = FText::FromName(FunctionRef.FunctionName); + ValidationLog.Error(TEXT("Invalid or null Expected Owner Class for this Flow Asset"), this); - return FText::Format(LOCTEXT("CallOwnerFunction", "Call {0}"), {FunctionNameText}); + return EDataValidationResult::Invalid; } - else + + // Check if the function can be found on the expected owner + const UFunction* Function = FunctionRef.TryResolveFunction(*ExpectedOwnerClass); + if (!IsValid(Function)) { - return Super::GetNodeTitle(); + ValidationLog.Error(TEXT("Could not resolve function for flow owner"), this); + + return EDataValidationResult::Invalid; } -} -FString UFlowNode_CallOwnerFunction::GetNodeDescription() const -{ - if (UFlowSettings::Get()->bUseAdaptiveNodeTitles) + // Check the function signature + if (!DoesFunctionHaveValidFlowOwnerFunctionSignature(*Function)) { - return Super::GetNodeDescription(); + ValidationLog.Error(TEXT("Flow Owner Function has an invalid signature"), this); + + return EDataValidationResult::Invalid; } - return FunctionRef.FunctionName.ToString(); -} + const UClass* RequiredParamsClass = GetRequiredParamsClass(); + checkf(IsValid(RequiredParamsClass), TEXT("GetRequiredParamsClass() cannot return null if DoesFunctionHaveValidFlowOwnerFunctionSignature() is true")); -bool UFlowNode_CallOwnerFunction::IsAcceptableParamsPropertyClass(const UClass* ParamsClass) const -{ - if (!IsValid(ParamsClass)) - { - return false; - } + const UClass* ExistingParamsClass = GetExistingParamsClass(); + checkf(IsValid(ExistingParamsClass), TEXT("This should be assured, if bHasParams == true")); - if (!ParamsClass->IsChildOf()) + // Check if the params (existing) are compatible with the function's expected (required) params + if (!ExistingParamsClass->IsChildOf(RequiredParamsClass)) { - return false; + ValidationLog.Error(TEXT("Params object is not of the correct type for the flow owner function"), this); + + return EDataValidationResult::Invalid; } - const UClass* ExistingParamsClass = GetExistingParamsClass(); + return EDataValidationResult::Valid; +} - if (IsValid(ExistingParamsClass) && ParamsClass != ExistingParamsClass) +FString UFlowNode_CallOwnerFunction::GetStatusString() const +{ + if (ActivationState != EFlowNodeState::NeverActivated) { - return false; + return UEnum::GetDisplayValueAsText(ActivationState).ToString(); } - return true; + return Super::GetStatusString(); } UClass* UFlowNode_CallOwnerFunction::TryGetExpectedOwnerClass() const @@ -307,7 +275,9 @@ bool UFlowNode_CallOwnerFunction::DoesFunctionHaveNameReturnType(const UFunction while (Iterator) { - return EnumHasAllFlags(Iterator->PropertyFlags, CPF_Parm | CPF_OutParm); + const bool bIsOutParm = EnumHasAllFlags(Iterator->PropertyFlags, CPF_Parm | CPF_OutParm); + + return bIsOutParm; } return false; @@ -350,120 +320,114 @@ UClass* UFlowNode_CallOwnerFunction::GetParamsClassForFunction(const UFunction& return nullptr; } -FString UFlowNode_CallOwnerFunction::GetStatusString() const +UClass* UFlowNode_CallOwnerFunction::GetParamsClassForFunctionName(const UClass& ExpectedOwnerClass, const FName& FunctionName) { - if (ActivationState != EFlowNodeState::NeverActivated) + const UFunction* Function = ExpectedOwnerClass.FindFunctionByName(FunctionName); + if (IsValid(Function)) { - return UEnum::GetDisplayValueAsText(ActivationState).ToString(); + return GetParamsClassForFunction(*Function); } - return Super::GetStatusString(); + return nullptr; } -EDataValidationResult UFlowNode_CallOwnerFunction::ValidateNode() +bool UFlowNode_CallOwnerFunction::TryAllocateParamsInstance() { - const bool bHasFunction = FunctionRef.IsConfigured(); - if (!bHasFunction) + if (FunctionRef.GetFunctionName().IsNone()) { - ValidationLog.Error(TEXT("CallOwnerFunction requires a valid Function reference"), this); + // Throw out the old params object (if any) + Params = nullptr; - return EDataValidationResult::Invalid; + return false; } - const bool bHasParams = IsValid(Params); - if (!bHasParams) - { - ValidationLog.Error(TEXT("CallOwnerFunction requires a valid Params object"), this); + const UClass* ExistingParamsClass = GetExistingParamsClass(); + UClass* RequiredParamsClass = GetRequiredParamsClass(); - return EDataValidationResult::Invalid; + const bool bNeedsAllocateParams = + !IsValid(ExistingParamsClass) || + ExistingParamsClass != RequiredParamsClass; + + if (!bNeedsAllocateParams) + { + return false; } - checkf(bHasParams && bHasFunction, TEXT("This should be assured by the preceding logic")); + // Throw out the old params object (if any) + Params = nullptr; + // Create the new params object + Params = NewObject(this, RequiredParamsClass); + + return true; +} + +UClass* UFlowNode_CallOwnerFunction::GetRequiredParamsClass() const +{ const UClass* ExpectedOwnerClass = TryGetExpectedOwnerClass(); if (!IsValid(ExpectedOwnerClass)) { - ValidationLog.Error(TEXT("Invalid or null Expected Owner Class for this Flow Asset"), this); - - return EDataValidationResult::Invalid; + return UFlowOwnerFunctionParams::StaticClass(); } - // Check if the function can be found on the expected owner - const UFunction* Function = FunctionRef.TryResolveFunction(*ExpectedOwnerClass); - if (!IsValid(Function)) - { - ValidationLog.Error(TEXT("Could not resolve function for flow owner"), this); + const FName FunctionNameAsName = FunctionRef.GetFunctionName(); - return EDataValidationResult::Invalid; + if (FunctionNameAsName.IsNone()) + { + return UFlowOwnerFunctionParams::StaticClass(); } - // Check the function signature - if (!DoesFunctionHaveValidFlowOwnerFunctionSignature(*Function)) + UClass* RequiredParamsClass = GetParamsClassForFunctionName(*ExpectedOwnerClass, FunctionNameAsName); + return RequiredParamsClass; +} + +UClass* UFlowNode_CallOwnerFunction::GetExistingParamsClass() const +{ + if (!IsValid(Params)) { - ValidationLog.Error(TEXT("Flow Owner Function has an invalid signature"), this); + return nullptr; + } - return EDataValidationResult::Invalid; + UClass* ExistingParamsClass = Params->GetClass(); + return ExistingParamsClass; +} + +bool UFlowNode_CallOwnerFunction::IsAcceptableParamsPropertyClass(const UClass* ParamsClass) const +{ + if (!IsValid(ParamsClass)) + { + return false; } - const UClass* RequiredParamsClass = GetRequiredParamsClass(); - checkf(IsValid(RequiredParamsClass), TEXT("GetRequiredParamsClass() cannot return null if DoesFunctionHaveValidFlowOwnerFunctionSignature() is true")); + if (!ParamsClass->IsChildOf()) + { + return false; + } const UClass* ExistingParamsClass = GetExistingParamsClass(); - checkf(IsValid(ExistingParamsClass), TEXT("This should be assured, if bHasParams == true")); - // Check if the params (existing) are compatible with the function's expected (required) params - if (!ExistingParamsClass->IsChildOf(RequiredParamsClass)) + if (IsValid(ExistingParamsClass) && ParamsClass != ExistingParamsClass) { - ValidationLog.Error(TEXT("Params object is not of the correct type for the flow owner function"), this); - - return EDataValidationResult::Invalid; + return false; } - return EDataValidationResult::Valid; + return true; } -TArray UFlowNode_CallOwnerFunction::GetContextInputs() +FText UFlowNode_CallOwnerFunction::GetNodeTitle() const { - // refresh Params, just in case function argument type was changed - TryAllocateParamsInstance(); - - TArray Pins = {}; + const bool bUseAdaptiveNodeTitles = UFlowSettings::Get()->bUseAdaptiveNodeTitles; - if (Params) + if (bUseAdaptiveNodeTitles && !FunctionRef.GetFunctionName().IsNone()) { - for (const FName& Name : Params->GetInputNames()) - { - if (InputPins.Contains(Name)) - { - continue; - } - - Pins.Emplace(Name); - } - } - return Pins; -} - -TArray UFlowNode_CallOwnerFunction::GetContextOutputs() -{ - // refresh Params, just in case function argument type was changed - TryAllocateParamsInstance(); - - TArray Pins = {}; + const FText FunctionNameText = FText::FromName(FunctionRef.FunctionName); - if (Params) + return FText::Format(LOCTEXT("CallOwnerFunction", "Call {0}"), { FunctionNameText }); + } + else { - for (const FName& Name : Params->GetOutputNames()) - { - if (OutputPins.Contains(Name)) - { - continue; - } - - Pins.Emplace(Name); - } + return Super::GetNodeTitle(); } - return Pins; } #endif // WITH_EDITOR diff --git a/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp b/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp new file mode 100644 index 000000000..a51076616 --- /dev/null +++ b/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp @@ -0,0 +1,592 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Nodes/World/FlowNode_ExecuteComponent.h" +#include "Interfaces/FlowOwnerInterface.h" +#include "Interfaces/FlowCoreExecutableInterface.h" +#include "Interfaces/FlowExternalExecutableInterface.h" +#include "Interfaces/FlowContextPinSupplierInterface.h" +#include "FlowAsset.h" +#include "FlowLogChannels.h" +#include "FlowSettings.h" +#include "Types/FlowInjectComponentsHelper.h" +#include "Types/FlowInjectComponentsManager.h" +#include "GameFramework/Actor.h" +#include "Components/ActorComponent.h" + +#define LOCTEXT_NAMESPACE "FlowNode" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_ExecuteComponent) + +UFlowNode_ExecuteComponent::UFlowNode_ExecuteComponent() + : Super() +{ +#if WITH_EDITOR + NodeStyle = EFlowNodeStyle::Default; + Category = TEXT("World"); +#endif // WITH_EDITOR +} + +void UFlowNode_ExecuteComponent::InitializeInstance() +{ + Super::InitializeInstance(); + + (void) TryInjectComponent(); + + if (UActorComponent* ResolvedComp = TryResolveComponent()) + { + if (IFlowCoreExecutableInterface* ComponentAsCoreExecutable = Cast(ResolvedComp)) + { + ComponentAsCoreExecutable->InitializeInstance(); + } + else if (ResolvedComp->Implements()) + { + IFlowCoreExecutableInterface::Execute_K2_InitializeInstance(ResolvedComp); + } + } +} + +void UFlowNode_ExecuteComponent::DeinitializeInstance() +{ + if (UActorComponent* ResolvedComp = TryResolveComponent()) + { + if (IFlowCoreExecutableInterface* ComponentAsCoreExecutable = Cast(ResolvedComp)) + { + ComponentAsCoreExecutable->DeinitializeInstance(); + } + else if (ResolvedComp->Implements()) + { + IFlowCoreExecutableInterface::Execute_K2_DeinitializeInstance(ResolvedComp); + } + } + + if (EExecuteComponentSource_Classifiers::DoesComponentSourceUseInjectManager(ComponentSource)) + { + if (IsValid(InjectComponentsManager)) + { + InjectComponentsManager->ShutdownRuntime(); + } + } + + InjectComponentsManager = nullptr; + + Super::DeinitializeInstance(); +} + +void UFlowNode_ExecuteComponent::PreloadContent() +{ + Super::PreloadContent(); + + if (UActorComponent* ResolvedComp = TryResolveComponent()) + { + if (IFlowCoreExecutableInterface* ComponentAsCoreExecutable = Cast(ResolvedComp)) + { + ComponentAsCoreExecutable->PreloadContent(); + } + else if (ResolvedComp->Implements()) + { + IFlowCoreExecutableInterface::Execute_K2_PreloadContent(ResolvedComp); + } + } +} + +void UFlowNode_ExecuteComponent::FlushContent() +{ + if (UActorComponent* ResolvedComp = TryResolveComponent()) + { + if (IFlowCoreExecutableInterface* ComponentAsCoreExecutable = Cast(ResolvedComp)) + { + ComponentAsCoreExecutable->FlushContent(); + } + else if (ResolvedComp->Implements()) + { + IFlowCoreExecutableInterface::Execute_K2_FlushContent(ResolvedComp); + } + } + + Super::FlushContent(); +} + +void UFlowNode_ExecuteComponent::OnActivate() +{ + Super::OnActivate(); + + if (UActorComponent* ResolvedComp = TryResolveComponent()) + { + IFlowNativeExecutableInterface* ThisAsNativeExecutorProxy = CastChecked(this); + + if (IFlowExternalExecutableInterface* ComponentAsExternalExecutable = Cast(ResolvedComp)) + { + // By convention, we must call the PreActivateExternalFlowExecutable() before OnActivate + // when we (this node) are acting as the proxy for an IFlowExternalExecutableInterface object + ComponentAsExternalExecutable->PreActivateExternalFlowExecutable(*ThisAsNativeExecutorProxy); + } + else if (ResolvedComp->Implements()) + { + IFlowExternalExecutableInterface::Execute_K2_PreActivateExternalFlowExecutable(ResolvedComp, TScriptInterface(this)); + } + else + { + UE_LOG(LogFlow, Error, TEXT("Expected a valid UActorComponent that implemented the IFlowExternalExecutableInterface")); + } + + if (IFlowCoreExecutableInterface* ComponentAsCoreExecutable = Cast(ResolvedComp)) + { + ComponentAsCoreExecutable->OnActivate(); + } + else if (ResolvedComp->Implements()) + { + IFlowCoreExecutableInterface::Execute_K2_OnActivate(ResolvedComp); + } + else + { + UE_LOG(LogFlow, Error, TEXT("Expected a valid UActorComponent that implemented the IFlowCoreExecutableInterface")); + } + } +} + +void UFlowNode_ExecuteComponent::Cleanup() +{ + if (UActorComponent* ResolvedComp = TryResolveComponent()) + { + if (IFlowCoreExecutableInterface* ComponentAsCoreExecutable = Cast(ResolvedComp)) + { + ComponentAsCoreExecutable->Cleanup(); + } + else if (ResolvedComp->Implements()) + { + IFlowCoreExecutableInterface::Execute_K2_Cleanup(ResolvedComp); + } + } + + Super::Cleanup(); +} + +void UFlowNode_ExecuteComponent::ForceFinishNode() +{ + if (UActorComponent* ResolvedComp = TryResolveComponent()) + { + if (IFlowCoreExecutableInterface* ComponentAsCoreExecutable = Cast(ResolvedComp)) + { + ComponentAsCoreExecutable->ForceFinishNode(); + } + else if (ResolvedComp->Implements()) + { + IFlowCoreExecutableInterface::Execute_K2_ForceFinishNode(ResolvedComp); + } + } + + Super::ForceFinishNode(); +} + +void UFlowNode_ExecuteComponent::ExecuteInput(const FName& PinName) +{ + Super::ExecuteInput(PinName); + + if (UActorComponent* ResolvedComp = TryResolveComponent()) + { + if (IFlowCoreExecutableInterface* ComponentAsCoreExecutable = Cast(ResolvedComp)) + { + ComponentAsCoreExecutable->ExecuteInput(PinName); + } + else if (ResolvedComp->Implements()) + { + IFlowCoreExecutableInterface::Execute_K2_ExecuteInput(ResolvedComp, PinName); + } + } + else + { + LogError(FString::Printf(TEXT("Could not ExecuteInput %s, because the component was missing or could not be resolved."), *PinName.ToString())); + } +} + +bool UFlowNode_ExecuteComponent::TryInjectComponent() +{ + if (!EExecuteComponentSource_Classifiers::DoesComponentSourceUseInjectManager(ComponentSource)) + { + return false; + } + + AActor* ActorOwner = TryGetRootFlowActorOwner(); + if (!IsValid(ActorOwner)) + { + return false; + } + + // Create the component instance + TArray ComponentInstances; + + static_assert(static_cast(EExecuteComponentSource::Max) == 4, TEXT("Update this code if the enum changes")); + switch (ComponentSource) + { + case EExecuteComponentSource::InjectFromTemplate: + { + if (IsValid(ComponentTemplate)) + { + if (UActorComponent* ComponentInstance = FFlowInjectComponentsHelper::TryCreateComponentInstanceForActorFromTemplate(*ActorOwner, *ComponentTemplate)) + { + ComponentInstances.Add(ComponentInstance); + } + } + } + break; + + case EExecuteComponentSource::InjectFromClass: + { + if (IsValid(ComponentClass)) + { + if (bReuseExistingComponent) + { + // Look for the component class existing already on the actor, for potential re-use + + UActorComponent* ExistingComponent = ActorOwner->FindComponentByClass(ComponentClass); + if (IsValid(ExistingComponent)) + { + // Set the ComponentRef directly (for later lookup via TryResolveComponent) + ComponentRef.SetResolvedComponentDirect(*ExistingComponent); + + return true; + } + + if (!bAllowInjectComponent) + { + return false; + } + } + + const FName InstanceBaseName = ComponentClass->GetFName(); + if (UActorComponent* ComponentInstance = FFlowInjectComponentsHelper::TryCreateComponentInstanceForActorFromClass(*ActorOwner, *ComponentClass, InstanceBaseName)) + { + ComponentInstances.Add(ComponentInstance); + } + } + } + break; + + default: + checkNoEntry(); + return false; + } + + // Create the manager object if we're injecting a component + InjectComponentsManager = NewObject(this); + InjectComponentsManager->InitializeRuntime(); + + // Inject the desired component + if (!ComponentInstances.IsEmpty()) + { + check(ComponentInstances.Num() == 1); + + InjectComponentsManager->InjectComponentsOnActor(*ActorOwner, ComponentInstances); + + // Set the ComponentRef directly (for later lookup via TryResolveComponent) + ComponentRef.SetResolvedComponentDirect(*ComponentInstances[0]); + } + + return true; +} + +UActorComponent* UFlowNode_ExecuteComponent::TryResolveComponent() +{ + UActorComponent* ResolvedComp = ComponentRef.GetResolvedComponent(); + if (IsValid(ResolvedComp)) + { + return ResolvedComp; + } + + AActor* ActorOwner = TryGetRootFlowActorOwner(); + + if (!IsValid(ActorOwner)) + { + UE_LOG(LogFlow, Error, TEXT("Expected a valid Actor owner to resolve component reference %s"), *ComponentRef.ComponentName.ToString()); + + return nullptr; + } + + // Injected components are totally optional, if they are not there, we have to assume that's intentional. + constexpr bool bAllowWarnIfFailed = true; + ResolvedComp = ComponentRef.TryResolveComponent(*ActorOwner, bAllowWarnIfFailed); + + return ResolvedComp; +} + +#if WITH_EDITOR +const UActorComponent* UFlowNode_ExecuteComponent::TryGetExpectedComponent() const +{ + TSubclassOf ExpectedOwnerClass = TryGetExpectedActorOwnerClass(); + + static_assert(static_cast(EExecuteComponentSource::Max) == 4, TEXT("Update this code if the enum changes")); + switch (ComponentSource) + { + default: + case EExecuteComponentSource::Undetermined: + { + return nullptr; + } + break; + + case EExecuteComponentSource::BindToExisting: + { + return AActor::GetActorClassDefaultComponentByName(ExpectedOwnerClass, ComponentRef.ComponentName); + } + break; + + case EExecuteComponentSource::InjectFromTemplate: + { + return ComponentTemplate; + } + break; + + case EExecuteComponentSource::InjectFromClass: + { + return IsValid(ComponentClass) ? ComponentClass->GetDefaultObject() : nullptr; + } + break; + } +} + +void UFlowNode_ExecuteComponent::PostLoad() +{ + Super::PostLoad(); + + RefreshComponentSource(); +} + +void UFlowNode_ExecuteComponent::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + const FName PropertyName = PropertyChangedEvent.Property->GetFName(); + if (PropertyName == GET_MEMBER_NAME_CHECKED(FFlowActorOwnerComponentRef, ComponentName) || + PropertyName == GET_MEMBER_NAME_CHECKED(UFlowNode_ExecuteComponent, ComponentTemplate) || + PropertyName == GET_MEMBER_NAME_CHECKED(UFlowNode_ExecuteComponent, ComponentClass)) + { + RefreshComponentSource(); + + RefreshPins(); + } +} + +void UFlowNode_ExecuteComponent::RefreshComponentSource() +{ + if (ComponentRef.IsConfigured()) + { + ComponentSource = EExecuteComponentSource::BindToExisting; + } + else if (ComponentTemplate != nullptr) + { + ComponentSource = EExecuteComponentSource::InjectFromTemplate; + } + else if (ComponentClass != nullptr) + { + ComponentSource = EExecuteComponentSource::InjectFromClass; + } + else + { + ComponentSource = EExecuteComponentSource::Undetermined; + } +} + +void UFlowNode_ExecuteComponent::RefreshPins() +{ + bool bChangedPins = false; + + const UActorComponent* ExpectedComponent = TryGetExpectedComponent(); + if (const IFlowContextPinSupplierInterface* ContextPinSupplierInterface = Cast(ExpectedComponent)) + { + const TArray NewInputPins = ContextPinSupplierInterface->GetContextInputs(); + bChangedPins = RebuildPinArray(NewInputPins, InputPins, DefaultInputPin) || bChangedPins; + + const TArray NewOutputPins = ContextPinSupplierInterface->GetContextOutputs(); + bChangedPins = RebuildPinArray(NewOutputPins, OutputPins, DefaultOutputPin) || bChangedPins; + } + else + { + bChangedPins = RebuildPinArray(TArray(&DefaultInputPin.PinName, 1), InputPins, DefaultInputPin) || bChangedPins; + bChangedPins = RebuildPinArray(TArray(&DefaultOutputPin.PinName, 1), OutputPins, DefaultOutputPin) || bChangedPins; + } + + if (bChangedPins) + { + OnReconstructionRequested.ExecuteIfBound(); + } +} + +EDataValidationResult UFlowNode_ExecuteComponent::ValidateNode() +{ + const bool bHasComponent = ComponentRef.IsConfigured(); + if (!bHasComponent) + { + ValidationLog.Error(TEXT("ExectuteComponent requires a valid Compoennt reference"), this); + + return EDataValidationResult::Invalid; + } + + TSubclassOf ExpectedActorOwnerClass = TryGetExpectedActorOwnerClass(); + if (!IsValid(ExpectedActorOwnerClass)) + { + ValidationLog.Error(TEXT("Invalid or null Expected Actor Owner Class for this Flow Asset"), this); + + return EDataValidationResult::Invalid; + } + + { + // Check if the component can be found on the expected owner + const UActorComponent* ExpectedComponent = TryGetExpectedComponent(); + if (!IsValid(ExpectedComponent)) + { + ValidationLog.Error(TEXT("Could not resolve component for flow actor owner"), this); + + return EDataValidationResult::Invalid; + } + + // Check that the component implements the expected interfaces + if (!Cast(ExpectedComponent)) + { + ValidationLog.Error(TEXT("Expected component to implement IFlowExternalExecutableInterface"), this); + + return EDataValidationResult::Invalid; + } + + if (!Cast(ExpectedComponent)) + { + ValidationLog.Error(TEXT("Expected component to implement IFlowCoreExecutableInterface"), this); + + return EDataValidationResult::Invalid; + } + } + + return EDataValidationResult::Valid; +} + +FString UFlowNode_ExecuteComponent::GetStatusString() const +{ + if (ActivationState != EFlowNodeState::NeverActivated) + { + return UEnum::GetDisplayValueAsText(ActivationState).ToString(); + } + + return Super::GetStatusString(); +} + +TSubclassOf UFlowNode_ExecuteComponent::TryGetExpectedActorOwnerClass() const +{ + const UFlowAsset* FlowAsset = GetFlowAsset(); + if (IsValid(FlowAsset)) + { + return FlowAsset->GetExpectedOwnerClass(); + } + + return nullptr; +} + +FText UFlowNode_ExecuteComponent::GetNodeTitle() const +{ + const bool bUseAdaptiveNodeTitles = UFlowSettings::Get()->bUseAdaptiveNodeTitles; + + if (bUseAdaptiveNodeTitles) + { + static_assert(static_cast(EExecuteComponentSource::Max) == 4, TEXT("Update this code if the enum changes")); + switch (ComponentSource) + { + case EExecuteComponentSource::Undetermined: + break; + + case EExecuteComponentSource::BindToExisting: + { + if (!ComponentRef.ComponentName.IsNone()) + { + const FText ComponentNameText = FText::FromName(ComponentRef.ComponentName); + + return FText::Format(LOCTEXT("ExecuteComponent", "Execute {0}"), { ComponentNameText }); + } + } + break; + + case EExecuteComponentSource::InjectFromTemplate: + { + if (IsValid(ComponentTemplate)) + { + FString ComponentNameString = ComponentTemplate->GetName(); + ComponentNameString.RemoveFromEnd(TEXT("_C")); + const FText ComponentNameText = FText::FromString(ComponentNameString); + + return FText::Format(LOCTEXT("ExecuteComponent", "Execute {0}"), { ComponentNameText }); + } + } + break; + + case EExecuteComponentSource::InjectFromClass: + { + if (IsValid(ComponentClass)) + { + FString ComponentClassString = ComponentClass->GetName(); + ComponentClassString.RemoveFromEnd(TEXT("_C")); + const FText ComponentNameText = FText::FromString(ComponentClassString); + + return FText::Format(LOCTEXT("ExecuteComponent", "Execute {0}"), { ComponentNameText }); + } + } + break; + } + } + + return Super::GetNodeTitle(); +} + +#endif // WITH_EDITOR + +void UFlowNode_ExecuteComponent::UpdateNodeConfigText_Implementation() +{ +#if WITH_EDITOR + FText ComponentNameText; + + const bool bUseAdaptiveNodeTitles = UFlowSettings::Get()->bUseAdaptiveNodeTitles; + if (!bUseAdaptiveNodeTitles) + { + + static_assert(static_cast(EExecuteComponentSource::Max) == 4, TEXT("Update this code if the enum changes")); + switch (ComponentSource) + { + case EExecuteComponentSource::Undetermined: + break; + + case EExecuteComponentSource::BindToExisting: + { + if (!ComponentRef.ComponentName.IsNone()) + { + ComponentNameText = FText::FromName(ComponentRef.ComponentName); + } + } + break; + + case EExecuteComponentSource::InjectFromTemplate: + { + if (IsValid(ComponentTemplate)) + { + FString ComponentNameString = ComponentTemplate->GetName(); + ComponentNameString.RemoveFromEnd(TEXT("_C")); + + ComponentNameText = FText::FromString(ComponentNameString); + } + } + break; + + case EExecuteComponentSource::InjectFromClass: + { + if (IsValid(ComponentClass)) + { + FString ComponentClassString = ComponentClass->GetName(); + ComponentClassString.RemoveFromEnd(TEXT("_C")); + + ComponentNameText = FText::FromString(ComponentClassString); + } + } + break; + } + } + + SetNodeConfigText(ComponentNameText); +#endif // WITH_EDITOR +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp index 90c2cc4a1..d646acaae 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp @@ -54,7 +54,7 @@ UFlowNode_PlayLevelSequence::UFlowNode_PlayLevelSequence(const FObjectInitialize } #if WITH_EDITOR -TArray UFlowNode_PlayLevelSequence::GetContextOutputs() +TArray UFlowNode_PlayLevelSequence::GetContextOutputs() const { if (Sequence.IsNull()) { @@ -63,7 +63,8 @@ TArray UFlowNode_PlayLevelSequence::GetContextOutputs() TArray Pins = {}; - Sequence = Sequence.LoadSynchronous(); + Sequence.LoadSynchronous(); + if (Sequence && Sequence->GetMovieScene()) { #if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 2 diff --git a/Source/Flow/Private/Types/FlowActorOwnerComponentRef.cpp b/Source/Flow/Private/Types/FlowActorOwnerComponentRef.cpp new file mode 100644 index 000000000..bf6393010 --- /dev/null +++ b/Source/Flow/Private/Types/FlowActorOwnerComponentRef.cpp @@ -0,0 +1,59 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Types/FlowActorOwnerComponentRef.h" +#include "Components/ActorComponent.h" +#include "GameFramework/Actor.h" +#include "Misc/RuntimeErrors.h" +#include "FlowLogChannels.h" + +UActorComponent* FFlowActorOwnerComponentRef::TryResolveComponent(const AActor& InActor, bool bWarnIfFailed) +{ + if (!IsResolved() && IsConfigured()) + { + ResolvedComponent = TryResolveComponentByName(InActor, ComponentName); + + if (bWarnIfFailed && !IsValid(ResolvedComponent)) + { + UE_LOG(LogFlow, Warning, TEXT("Could not resolve component named %s on actor %s"), *ComponentName.ToString(), *InActor.GetName()); + } + } + + return ResolvedComponent; +} + +void FFlowActorOwnerComponentRef::SetResolvedComponentDirect(UActorComponent& Component) +{ + ComponentName = Component.GetFName(); + + ResolvedComponent = &Component; +} + +UActorComponent* FFlowActorOwnerComponentRef::TryResolveComponentByName(const AActor& InActor, const FName& InComponentName) +{ + constexpr bool bIncludeFromChildActors = false; + + UActorComponent* FoundComponent = nullptr; + + // Search for the component (by name) on the given actor + InActor.ForEachComponent( + bIncludeFromChildActors, + [&FoundComponent, &InComponentName](UActorComponent* Component) + { + FString CleanedName = Component->GetName(); + CleanedName.RemoveFromEnd(TEXT("_C")); + + if ((InComponentName == Component->GetFName() || InComponentName == FName(CleanedName)) && + ensureAsRuntimeWarning(FoundComponent == nullptr)) + { + FoundComponent = Component; + } + }); + + return FoundComponent; +} + +bool FFlowActorOwnerComponentRef::IsResolved() const +{ + return IsValid(ResolvedComponent); +} + diff --git a/Source/Flow/Private/Types/FlowInjectComponentsHelper.cpp b/Source/Flow/Private/Types/FlowInjectComponentsHelper.cpp new file mode 100644 index 000000000..21a9853fa --- /dev/null +++ b/Source/Flow/Private/Types/FlowInjectComponentsHelper.cpp @@ -0,0 +1,100 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Types/FlowInjectComponentsHelper.h" +#include "Components/ActorComponent.h" +#include "GameFramework/Actor.h" +#include "FlowLogChannels.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowInjectComponentsHelper) + +TArray FFlowInjectComponentsHelper::CreateComponentInstancesForActor(AActor& Actor) +{ + TArray ComponentInstances; + + if (ComponentTemplates.IsEmpty() && ComponentClasses.IsEmpty()) + { + return ComponentInstances; + } + + // If the desired component does not already exist, add it to the ActorOwner + for (UActorComponent* ComponentTemplate : ComponentTemplates) + { + if (!IsValid(ComponentTemplate)) + { + UE_LOG(LogFlow, Warning, TEXT("Cannot inject a null component!")); + + continue; + } + + if (UActorComponent* ComponentInstance = TryCreateComponentInstanceForActorFromTemplate(Actor, *ComponentTemplate)) + { + ComponentInstances.Add(ComponentInstance); + } + } + + for (TSubclassOf ComponentClass : ComponentClasses) + { + if (!IsValid(ComponentClass)) + { + UE_LOG(LogFlow, Warning, TEXT("Cannot inject a null component class!")); + + continue; + } + + const FName InstanceBaseName = ComponentClass->GetFName(); + if (UActorComponent* ComponentInstance = TryCreateComponentInstanceForActorFromClass(Actor, ComponentClass, InstanceBaseName)) + { + ComponentInstances.Add(ComponentInstance); + } + } + + return ComponentInstances; +} + +UActorComponent* FFlowInjectComponentsHelper::TryCreateComponentInstanceForActorFromTemplate(AActor& Actor, UActorComponent& ComponentTemplate) +{ + // Following pattern from UGameFrameworkComponentManager::CreateComponentOnInstance() + UClass* ComponentClass = ComponentTemplate.GetClass(); + if (!ComponentClass->GetDefaultObject()->GetIsReplicated() || Actor.GetLocalRole() == ROLE_Authority) + { + const EObjectFlags InstanceFlags = ComponentTemplate.GetFlags() | RF_Transient; + + UActorComponent* ComponentInstance = NewObject(&Actor, ComponentTemplate.GetFName(), InstanceFlags, &ComponentTemplate); + + return ComponentInstance; + } + + return nullptr; +} + +UActorComponent* FFlowInjectComponentsHelper::TryCreateComponentInstanceForActorFromClass(AActor& Actor, TSubclassOf ComponentClass, const FName& InstanceBaseName) +{ + // Following pattern from UGameFrameworkComponentManager::CreateComponentOnInstance() + if (ComponentClass && (!ComponentClass->GetDefaultObject()->GetIsReplicated() || Actor.GetLocalRole() == ROLE_Authority)) + { + const FName UniqueName = MakeUniqueObjectName(&Actor, ComponentClass, InstanceBaseName); + UActorComponent* ComponentInstance = NewObject(&Actor, ComponentClass, UniqueName); + + return ComponentInstance; + } + + return nullptr; +} + +void FFlowInjectComponentsHelper::InjectCreatedComponent(AActor& Actor, UActorComponent& ComponentInstance) +{ + // Following pattern from UGameFrameworkComponentManager::CreateComponentOnInstance() + if (USceneComponent* SceneComponentInstance = Cast(&ComponentInstance)) + { + SceneComponentInstance->SetupAttachment(Actor.GetRootComponent()); + } + + ComponentInstance.RegisterComponent(); +} + +void FFlowInjectComponentsHelper::DestroyInjectedComponent(AActor& Actor, UActorComponent& ComponentInstance) +{ + // Following pattern from UGameFrameworkComponentManager::DestroyInstancedComponent() + ComponentInstance.DestroyComponent(); + ComponentInstance.SetFlags(RF_Transient); +} diff --git a/Source/Flow/Private/Types/FlowInjectComponentsManager.cpp b/Source/Flow/Private/Types/FlowInjectComponentsManager.cpp new file mode 100644 index 000000000..eb4ad7baf --- /dev/null +++ b/Source/Flow/Private/Types/FlowInjectComponentsManager.cpp @@ -0,0 +1,117 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Types/FlowInjectComponentsManager.h" +#include "Types/FlowInjectComponentsHelper.h" +#include "Components/ActorComponent.h" +#include "GameFramework/Actor.h" +#include "FlowLogChannels.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowInjectComponentsManager) + +void UFlowInjectComponentsManager::InitializeRuntime() +{ + check(ActorToComponentsMap.IsEmpty()); +} + +void UFlowInjectComponentsManager::ShutdownRuntime() +{ + if (bRemoveInjectedComponentsWhenDeinitializing) + { + RemoveInjectedComponents(); + } + + ActorToComponentsMap.Empty(); +} + +void UFlowInjectComponentsManager::InjectComponentsOnActor(AActor& Actor, const TArray& ComponentInstances) +{ + for (UActorComponent* ComponentInstance : ComponentInstances) + { + if (IsValid(ComponentInstance)) + { + InjectComponentOnActor(Actor, *ComponentInstance); + } + } +} + +void UFlowInjectComponentsManager::RemoveInjectedComponents() +{ + for (auto& KV : ActorToComponentsMap) + { + AActor* Actor = KV.Key; + const FFlowComponentInstances& Instances = KV.Value; + + if (!IsValid(Actor)) + { + continue; + } + + for (TWeakObjectPtr ComponentInstancePtr : Instances.Components) + { + if (UActorComponent* ComponentInstance = ComponentInstancePtr.Get()) + { + RemoveAndUnregisterComponent(*Actor, *ComponentInstance); + } + } + } +} + +void UFlowInjectComponentsManager::AddAndRegisterComponent(AActor& Actor, UActorComponent& ComponentInstance) +{ + FFlowInjectComponentsHelper::InjectCreatedComponent(Actor, ComponentInstance); + + if (bRemoveInjectedComponentsWhenDeinitializing) + { + // If we will be responsible for removing them later, + // we need to keep track of the spawned components + FFlowComponentInstances& ComponentInstances = ActorToComponentsMap.FindOrAdd(&Actor); + ComponentInstances.Components.Add(&ComponentInstance); + + RegisterOnDestroyedDelegate(Actor); + } +} + +void UFlowInjectComponentsManager::RemoveAndUnregisterComponent(AActor& Actor, UActorComponent& ComponentInstance) +{ + BeforeActorRemovedDelegate.Broadcast(&Actor); + + UnregisterOnDestroyedDelegate(Actor); + + FFlowInjectComponentsHelper::DestroyInjectedComponent(Actor, ComponentInstance); +} + +void UFlowInjectComponentsManager::RegisterOnDestroyedDelegate(AActor& Actor) +{ + Actor.OnDestroyed.AddUniqueDynamic(this, &UFlowInjectComponentsManager::OnActorDestroyed); +} + +void UFlowInjectComponentsManager::UnregisterOnDestroyedDelegate(AActor& Actor) +{ + Actor.OnDestroyed.RemoveDynamic(this, &UFlowInjectComponentsManager::OnActorDestroyed); +} + +void UFlowInjectComponentsManager::RemoveAllInjectedComponentsAndStopMonitoringActor(AActor& Actor) +{ + const FFlowComponentInstances* FoundComponentInstances = ActorToComponentsMap.Find(&Actor); + + if (ensure(FoundComponentInstances)) + { + for (TWeakObjectPtr ComponentInstancePtr : FoundComponentInstances->Components) + { + if (UActorComponent* ComponentInstance = ComponentInstancePtr.Get()) + { + RemoveAndUnregisterComponent(Actor, *ComponentInstance); + } + } + } + + ActorToComponentsMap.Remove(&Actor); +} + +void UFlowInjectComponentsManager::OnActorDestroyed(AActor* DestroyedActor) +{ + if (IsValid(DestroyedActor)) + { + RemoveAllInjectedComponentsAndStopMonitoringActor(*DestroyedActor); + } +} diff --git a/Source/Flow/Private/FlowOwnerFunctionParams.cpp b/Source/Flow/Private/Types/FlowOwnerFunctionParams.cpp similarity index 91% rename from Source/Flow/Private/FlowOwnerFunctionParams.cpp rename to Source/Flow/Private/Types/FlowOwnerFunctionParams.cpp index 666c24eb5..3e18dee4c 100644 --- a/Source/Flow/Private/FlowOwnerFunctionParams.cpp +++ b/Source/Flow/Private/Types/FlowOwnerFunctionParams.cpp @@ -1,17 +1,16 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "FlowOwnerFunctionParams.h" +#include "Types/FlowOwnerFunctionParams.h" #include "Nodes/FlowNode.h" #include "Nodes/World/FlowNode_CallOwnerFunction.h" -#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowOwnerFunctionParams) - UFlowOwnerFunctionParams::UFlowOwnerFunctionParams() + : Super() { #if WITH_EDITOR InputNames.Add(UFlowNode::DefaultInputPin.PinName); OutputNames.Add(UFlowNode::DefaultOutputPin.PinName); -#endif +#endif //WITH_EDITOR } void UFlowOwnerFunctionParams::PreExecute(UFlowNode_CallOwnerFunction& InSourceNode, const FName& InputPinName) diff --git a/Source/Flow/Private/FlowOwnerFunctionRef.cpp b/Source/Flow/Private/Types/FlowOwnerFunctionRef.cpp similarity index 89% rename from Source/Flow/Private/FlowOwnerFunctionRef.cpp rename to Source/Flow/Private/Types/FlowOwnerFunctionRef.cpp index 235970b2c..eee59d153 100644 --- a/Source/Flow/Private/FlowOwnerFunctionRef.cpp +++ b/Source/Flow/Private/Types/FlowOwnerFunctionRef.cpp @@ -1,15 +1,12 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "FlowOwnerFunctionRef.h" - +#include "Types/FlowOwnerFunctionRef.h" +#include "Types/FlowOwnerFunctionParams.h" +#include "Interfaces/FlowOwnerInterface.h" #include "FlowLogChannels.h" -#include "FlowOwnerFunctionParams.h" -#include "FlowOwnerInterface.h" -#include "Logging/LogMacros.h" #include "UObject/Class.h" - -#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowOwnerFunctionRef) +#include "Logging/LogMacros.h" UFunction* FFlowOwnerFunctionRef::TryResolveFunction(const UClass& InClass) { @@ -52,7 +49,7 @@ FName FFlowOwnerFunctionRef::CallFunction(IFlowOwnerInterface& InFlowOwnerInterf FName OutputPinName; }; - FFlowOwnerFunctionRef_Parms Parms = {&InParams, NAME_None}; + FFlowOwnerFunctionRef_Parms Parms = { &InParams, NAME_None }; // Call the owner function itself FlowOwnerObject->ProcessEvent(Function, &Parms); diff --git a/Source/Flow/Public/AddOns/FlowNodeAddOn.h b/Source/Flow/Public/AddOns/FlowNodeAddOn.h new file mode 100644 index 000000000..5791df1ac --- /dev/null +++ b/Source/Flow/Public/AddOns/FlowNodeAddOn.h @@ -0,0 +1,75 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Nodes/FlowNodeBase.h" +#include "Interfaces/FlowNativeExecutableInterface.h" +#include "Nodes/FlowPin.h" + +#include "FlowNodeAddOn.generated.h" + +// Forward Declarations +class UFlowNode; + +UCLASS(Abstract, MinimalApi, EditInlineNew, Blueprintable) +class UFlowNodeAddOn + : public UFlowNodeBase + , public IFlowNativeExecutableInterface +{ + GENERATED_BODY() + +public: + + // UFlowNodeBase + + // AddOns may opt-in to be eligible for a given parent + UFUNCTION(BlueprintNativeEvent, BlueprintPure) + FLOW_API EFlowAddOnAcceptResult AcceptFlowNodeAddOnParent(const UFlowNodeBase* ParentTemplate) const; + + FLOW_API virtual UFlowNode* GetFlowNodeSelfOrOwner() override { return FlowNode; } + FLOW_API virtual bool IsSupportedInputPinName(const FName& PinName) const override; + // -- + + // IFlowCoreExecutableInterface + FLOW_API virtual void InitializeInstance() override; + FLOW_API virtual void DeinitializeInstance() override; + // -- + + // IFlowNativeExecutableInterface + FLOW_API virtual void TriggerFirstOutput(const bool bFinish) override; + FLOW_API virtual void TriggerOutput(const FName PinName, const bool bFinish = false, const EFlowPinActivationType ActivationType = EFlowPinActivationType::Default) override; + FLOW_API virtual void Finish() override; + // -- + + // UFlowNodeAddOn + UFUNCTION(BlueprintCallable, BlueprintPure, Category = "FlowNodeAddon", DisplayName = "Get Flow Node") + FLOW_API UFlowNode* GetFlowNode() const; + // -- + +#if WITH_EDITOR + // IFlowContextPinSupplierInterface + FLOW_API virtual bool SupportsContextPins() const override { return !InputPins.IsEmpty() || !OutputPins.IsEmpty(); } + FLOW_API virtual TArray GetContextInputs() const override { return InputPins; } + FLOW_API virtual TArray GetContextOutputs() const override { return OutputPins; } + // -- +#endif // WITH_EDITOR + +protected: + void CacheFlowNode(); + +protected: + + // The FlowNode that contains this AddOn + // (accessible only when initialized, runtime only) + UPROPERTY(Transient) + TObjectPtr FlowNode; + + // Input pins to add to the owning flow node + // If defined, ExecuteInput will only be executed for these inputs + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "FlowNodeAddOn") + TArray InputPins; + + // Output pins to add to the owning flow node + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "FlowNodeAddOn") + TArray OutputPins; +}; diff --git a/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateAND.h b/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateAND.h new file mode 100644 index 000000000..5a8b363c7 --- /dev/null +++ b/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateAND.h @@ -0,0 +1,32 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "AddOns/FlowNodeAddOn.h" +#include "Interfaces/FlowPredicateInterface.h" + +#include "FlowNodeAddOn_PredicateAND.generated.h" + +// Forward Declarations +class UFlowNode; + +UCLASS(MinimalApi, NotBlueprintable, meta = (DisplayName = "AND")) +class UFlowNodeAddOn_PredicateAND + : public UFlowNodeAddOn + , public IFlowPredicateInterface +{ + GENERATED_BODY() + +public: + UFlowNodeAddOn_PredicateAND(); + + // UFlowNodeBase + virtual EFlowAddOnAcceptResult AcceptFlowNodeAddOnChild_Implementation(const UFlowNodeAddOn* AddOnTemplate) const override; + // -- + + // IFlowPredicateInterface + virtual bool EvaluatePredicate_Implementation() const override; + // -- + + static bool EvaluatePredicateAND(const TArray& AddOns); +}; diff --git a/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateNOT.h b/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateNOT.h new file mode 100644 index 000000000..9ebf63277 --- /dev/null +++ b/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateNOT.h @@ -0,0 +1,30 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "AddOns/FlowNodeAddOn.h" +#include "Interfaces/FlowPredicateInterface.h" + +#include "FlowNodeAddOn_PredicateNOT.generated.h" + +// Forward Declarations +class UFlowNode; + +UCLASS(MinimalApi, NotBlueprintable, meta = (DisplayName = "NOT")) +class UFlowNodeAddOn_PredicateNOT + : public UFlowNodeAddOn + , public IFlowPredicateInterface +{ + GENERATED_BODY() + +public: + UFlowNodeAddOn_PredicateNOT(); + + // UFlowNodeBase + virtual EFlowAddOnAcceptResult AcceptFlowNodeAddOnChild_Implementation(const UFlowNodeAddOn* AddOnTemplate) const override; + // -- + + // IFlowPredicateInterface + virtual bool EvaluatePredicate_Implementation() const override; + // -- +}; diff --git a/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateOR.h b/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateOR.h new file mode 100644 index 000000000..0d8128656 --- /dev/null +++ b/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateOR.h @@ -0,0 +1,32 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "AddOns/FlowNodeAddOn.h" +#include "Interfaces/FlowPredicateInterface.h" + +#include "FlowNodeAddOn_PredicateOR.generated.h" + +// Forward Declarations +class UFlowNode; + +UCLASS(MinimalApi, NotBlueprintable, meta = (DisplayName = "OR")) +class UFlowNodeAddOn_PredicateOR + : public UFlowNodeAddOn + , public IFlowPredicateInterface +{ + GENERATED_BODY() + +public: + UFlowNodeAddOn_PredicateOR(); + + // UFlowNodeBase + virtual EFlowAddOnAcceptResult AcceptFlowNodeAddOnChild_Implementation(const UFlowNodeAddOn* AddOnTemplate) const override; + // -- + + // IFlowPredicateInterface + virtual bool EvaluatePredicate_Implementation() const override; + // -- + + static bool EvaluatePredicateOR(const TArray& AddOns); +}; diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index de34e45fd..8382c9c5a 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -81,7 +81,7 @@ class FLOW_API UFlowAsset : public UObject virtual EDataValidationResult ValidateAsset(FFlowMessageLog& MessageLog); // Returns whether the node class is allowed in this flow asset - bool IsNodeClassAllowed(const UClass* FlowNodeClass, FText* OutOptionalFailureReason = nullptr) const; + bool IsNodeOrAddOnClassAllowed(const UClass* FlowNodeClass, FText* OutOptionalFailureReason = nullptr) const; static FString ValidationError_NodeClassNotAllowed; static FString ValidationError_NullNodeInstance; @@ -97,7 +97,7 @@ class FLOW_API UFlowAsset : public UObject private: UPROPERTY() - TObjectPtr FlowGraph; + UEdGraph* FlowGraph; static TSharedPtr FlowGraphInterface; #endif @@ -412,8 +412,8 @@ class FLOW_API UFlowAsset : public UObject #if WITH_EDITOR public: - void LogError(const FString& MessageToLog, UFlowNode* Node); - void LogWarning(const FString& MessageToLog, UFlowNode* Node); - void LogNote(const FString& MessageToLog, UFlowNode* Node); + void LogError(const FString& MessageToLog, UFlowNodeBase* Node); + void LogWarning(const FString& MessageToLog, UFlowNodeBase* Node); + void LogNote(const FString& MessageToLog, UFlowNodeBase* Node); #endif }; diff --git a/Source/Flow/Public/FlowComponent.h b/Source/Flow/Public/FlowComponent.h index 4c6132526..d95fa7abf 100644 --- a/Source/Flow/Public/FlowComponent.h +++ b/Source/Flow/Public/FlowComponent.h @@ -7,7 +7,7 @@ #include "FlowSave.h" #include "FlowTypes.h" -#include "FlowOwnerInterface.h" +#include "Interfaces/FlowOwnerInterface.h" #include "FlowComponent.generated.h" class UFlowAsset; diff --git a/Source/Flow/Public/FlowMessageLog.h b/Source/Flow/Public/FlowMessageLog.h index 7e11cafb9..d09a5a398 100644 --- a/Source/Flow/Public/FlowMessageLog.h +++ b/Source/Flow/Public/FlowMessageLog.h @@ -9,7 +9,7 @@ #include "Misc/UObjectToken.h" class UFlowAsset; -class UFlowNode; +class UFlowNodeBase; /** * Message Log token that links to an element in Flow Graph @@ -21,13 +21,13 @@ class FLOW_API FFlowGraphToken : public IMessageToken const FEdGraphPinReference GraphPin; explicit FFlowGraphToken(const UFlowAsset* InFlowAsset); - explicit FFlowGraphToken(const UFlowNode* InFlowNode); + explicit FFlowGraphToken(const UFlowNodeBase* InFlowNodeBase); explicit FFlowGraphToken(UEdGraphNode* InGraphNode, const UEdGraphPin* InPin); public: /** Factory method, tokens can only be constructed as shared refs */ static TSharedPtr Create(const UFlowAsset* InFlowAsset, FTokenizedMessage& Message); - static TSharedPtr Create(const UFlowNode* InFlowNode, FTokenizedMessage& Message); + static TSharedPtr Create(const UFlowNodeBase* InFlowNodeBase, FTokenizedMessage& Message); static TSharedPtr Create(UEdGraphNode* InGraphNode, FTokenizedMessage& Message); static TSharedPtr Create(const UEdGraphPin* InPin, FTokenizedMessage& Message); diff --git a/Source/Flow/Public/FlowTypes.h b/Source/Flow/Public/FlowTypes.h index 2a0f3793b..8ede68d16 100644 --- a/Source/Flow/Public/FlowTypes.h +++ b/Source/Flow/Public/FlowTypes.h @@ -91,3 +91,32 @@ enum class EFlowOnScreenMessageType : uint8 Temporary, Permanent }; + +UENUM(BlueprintType) +enum class EFlowAddOnAcceptResult : uint8 +{ + // Note that these enum values are ordered by priority, where greater numerical values are higher priority + // (see CombineFlowAddOnAcceptResult) + + // No result from the current operation + Undetermined, + + // Accept, if all other conditions are met + TentativeAccept, + + // Reject the AddOn outright, regardless if previously TentativelyAccept-ed + Reject, + + Max UMETA(Hidden), + Invalid UMETA(Hidden), + Min = Undetermined UMETA(Hidden), +}; + +FORCEINLINE_DEBUGGABLE EFlowAddOnAcceptResult CombineFlowAddOnAcceptResult(EFlowAddOnAcceptResult Result0, EFlowAddOnAcceptResult Result1) +{ + const __underlying_type(EFlowAddOnAcceptResult) Result0AsInt = static_cast<__underlying_type(EFlowAddOnAcceptResult)>(Result0); + const __underlying_type(EFlowAddOnAcceptResult) Result1AsInt = static_cast<__underlying_type(EFlowAddOnAcceptResult)>(Result1); + + // Prioritize the higher numerical value enum value + return static_cast(FMath::Max(Result0AsInt, Result1AsInt)); +} diff --git a/Source/Flow/Public/Interfaces/FlowContextPinSupplierInterface.h b/Source/Flow/Public/Interfaces/FlowContextPinSupplierInterface.h new file mode 100644 index 000000000..12040d23d --- /dev/null +++ b/Source/Flow/Public/Interfaces/FlowContextPinSupplierInterface.h @@ -0,0 +1,45 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "UObject/Interface.h" +#include "Nodes/FlowPin.h" + +#include "FlowContextPinSupplierInterface.generated.h" + +// A flow element (UFlowNode, UFlowNodeAddOn, etc.) that may supply context pins +// "Context Pins" are those that can be dynamically added/removed to a FlowNode by property +// settings on the flow node, by subobjects, etc. +UINTERFACE(MinimalAPI, Blueprintable, DisplayName = "Flow ContextPin Supplier Interface") +class UFlowContextPinSupplierInterface : public UInterface +{ + GENERATED_BODY() +}; + +class FLOW_API IFlowContextPinSupplierInterface +{ + GENERATED_BODY() + +public: + +#if WITH_EDITOR + virtual bool SupportsContextPins() const { return true; } + + // Be careful, enabling it might cause loading gigabytes of data as nodes would load all related data (i.e. Level Sequences) + virtual bool CanRefreshContextPinsOnLoad() const { return false; } +#endif // WITH_EDITOR + + UFUNCTION(BlueprintNativeEvent, Category = "FlowNode In-Editor Functions", DisplayName = "GetContextInputs", meta = (DevelopmentOnly)) + TArray K2_GetContextInputs() const; + virtual TArray K2_GetContextInputs_Implementation() const; +#if WITH_EDITOR + virtual TArray GetContextInputs() const { return Execute_K2_GetContextInputs(Cast(this)); } +#endif // WITH_EDITOR + + UFUNCTION(BlueprintNativeEvent, Category = "FlowNode In-Editor Functions", DisplayName = "GetContextOutputs", meta = (DevelopmentOnly)) + TArray K2_GetContextOutputs() const; + virtual TArray K2_GetContextOutputs_Implementation() const; +#if WITH_EDITOR + virtual TArray GetContextOutputs() const { return Execute_K2_GetContextOutputs(Cast(this)); } +#endif // WITH_EDITOR +}; diff --git a/Source/Flow/Public/Interfaces/FlowCoreExecutableInterface.h b/Source/Flow/Public/Interfaces/FlowCoreExecutableInterface.h new file mode 100644 index 000000000..f4a9c1271 --- /dev/null +++ b/Source/Flow/Public/Interfaces/FlowCoreExecutableInterface.h @@ -0,0 +1,63 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "UObject/Interface.h" + +#include "FlowCoreExecutableInterface.generated.h" + +// Implemented by objects that can execute within a flow graph +// (eg, UFlowNode and UFlowNodeAddOn subclasses implement this) +UINTERFACE(MinimalAPI, Blueprintable, DisplayName = "Flow Core Executable Interface") +class UFlowCoreExecutableInterface : public UInterface +{ + GENERATED_BODY() +}; + +class FLOW_API IFlowCoreExecutableInterface +{ + GENERATED_BODY() + +public: + + // Method called just after creating the node instance, while initializing the Flow Asset instance + // This happens before executing graph, only called during gameplay + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", DisplayName = "Initialize Instance") + void K2_InitializeInstance(); + virtual void InitializeInstance() { Execute_K2_InitializeInstance(Cast(this)); } + + // Event called from UMKTFlowNode::DeinitializeInstance() + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", DisplayName = "Deinitialize Instance") + void K2_DeinitializeInstance(); + virtual void DeinitializeInstance() { Execute_K2_DeinitializeInstance(Cast(this)); } + + // If preloading is enabled, will be called to preload content + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", DisplayName = "Preload Content") + void K2_PreloadContent(); + virtual void PreloadContent() { Execute_K2_PreloadContent(Cast(this)); } + + // If preloading is enabled, will be called to flush content + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", DisplayName = "Flush Content") + void K2_FlushContent(); + virtual void FlushContent() { Execute_K2_FlushContent(Cast(this)); } + + // Called immediately before the first input is triggered + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", DisplayName = "OnActivate") + void K2_OnActivate(); + virtual void OnActivate() { Execute_K2_OnActivate(Cast(this)); } + + // Event called after node finished the work + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", DisplayName = "Cleanup") + void K2_Cleanup(); + virtual void Cleanup() { Execute_K2_Cleanup(Cast(this)); } + + // Define what happens when node is terminated from the outside + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", DisplayName = "Force Finish Node") + void K2_ForceFinishNode(); + virtual void ForceFinishNode() { Execute_K2_ForceFinishNode(Cast(this)); } + + // Event reacting on triggering Input pin + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", DisplayName = "Execute Input") + void K2_ExecuteInput(const FName& PinName); + virtual void ExecuteInput(const FName& PinName) { Execute_K2_ExecuteInput(Cast(this), PinName); } +}; diff --git a/Source/Flow/Public/Interfaces/FlowExternalExecutableInterface.h b/Source/Flow/Public/Interfaces/FlowExternalExecutableInterface.h new file mode 100644 index 000000000..6579d49f4 --- /dev/null +++ b/Source/Flow/Public/Interfaces/FlowExternalExecutableInterface.h @@ -0,0 +1,33 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "UObject/Interface.h" +#include "UObject/ScriptInterface.h" + +#include "FlowExternalExecutableInterface.generated.h" + +// Forward Declarations +class IFlowNativeExecutableInterface; + +// Implemented by external objects that can execute within a flow graph +// via a FlowNode or FlowNodeAddOn proxy +UINTERFACE(MinimalAPI, Blueprintable, DisplayName = "Flow External Executable Interface") +class UFlowExternalExecutableInterface : public UInterface +{ + GENERATED_BODY() +}; + +class FLOW_API IFlowExternalExecutableInterface +{ + GENERATED_BODY() + +public: + + // Called immediately prior to OnActivate() to set the native proxy that is executing the + // external executable object in the flow graph. This is primarily done so that the external element has a + // handle to call TriggerOutput() and Finish() when it has completed its work. + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", DisplayName = "PreActivateExternalFlowExecutable") + void K2_PreActivateExternalFlowExecutable(const TScriptInterface& NativeExecutorProxy); + virtual void PreActivateExternalFlowExecutable(IFlowNativeExecutableInterface& NativeExecutorProxy); +}; diff --git a/Source/Flow/Public/Interfaces/FlowNativeExecutableInterface.h b/Source/Flow/Public/Interfaces/FlowNativeExecutableInterface.h new file mode 100644 index 000000000..63e12cee0 --- /dev/null +++ b/Source/Flow/Public/Interfaces/FlowNativeExecutableInterface.h @@ -0,0 +1,44 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "UObject/Interface.h" +#include "Nodes/FlowPin.h" + +#include "FlowNativeExecutableInterface.generated.h" + +// Implemented only by native (C++) objects that can execute within a flow graph +// (eg, UFlowNode and UFlowNodeAddOn subclasses implement this) +UINTERFACE(MinimalAPI, BlueprintType, DisplayName = "Flow Native Executable Interface", meta = (CannotImplementInterfaceInBlueprint)) +class UFlowNativeExecutableInterface : public UInterface +{ + GENERATED_BODY() +}; + +class FLOW_API IFlowNativeExecutableInterface +{ + GENERATED_BODY() + +public: + + // Simply trigger the first Output Pin, convenient to use if node has only one output + UFUNCTION(BlueprintCallable, Category = "FlowNode") + virtual void TriggerFirstOutput(const bool bFinish) = 0; + + // Cause a specific output to be triggered (by PinName) + UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (HidePin = "ActivationType")) + virtual void TriggerOutput(const FName PinName, const bool bFinish = false, const EFlowPinActivationType ActivationType = EFlowPinActivationType::Default) = 0; + + // Cause a specific output to be triggered (by PinHandle) + UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (HidePin = "ActivationType")) + virtual void TriggerOutputPin(const FFlowOutputPinHandle Pin, const bool bFinish = false, const EFlowPinActivationType ActivationType = EFlowPinActivationType::Default); + + // TriggerOutput convenience aliases + void TriggerOutput(const FString& PinName, const bool bFinish = false); + void TriggerOutput(const FText& PinName, const bool bFinish = false); + void TriggerOutput(const TCHAR* PinName, const bool bFinish = false); + + // Finish execution of node, it will call Cleanup + UFUNCTION(BlueprintCallable, Category = "FlowNode") + virtual void Finish() = 0; +}; diff --git a/Source/Flow/Public/FlowOwnerInterface.h b/Source/Flow/Public/Interfaces/FlowOwnerInterface.h similarity index 100% rename from Source/Flow/Public/FlowOwnerInterface.h rename to Source/Flow/Public/Interfaces/FlowOwnerInterface.h diff --git a/Source/Flow/Public/Interfaces/FlowPredicateInterface.h b/Source/Flow/Public/Interfaces/FlowPredicateInterface.h new file mode 100644 index 000000000..8dda3a27a --- /dev/null +++ b/Source/Flow/Public/Interfaces/FlowPredicateInterface.h @@ -0,0 +1,30 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "UObject/Interface.h" +#include "Templates/SubclassOf.h" + +#include "FlowPredicateInterface.generated.h" + +class UFlowNodeAddOn; + +// Predicate interface for AddOns +UINTERFACE(MinimalAPI, BlueprintType, Blueprintable, DisplayName = "Flow Predicate Interface") +class UFlowPredicateInterface : public UInterface +{ + GENERATED_BODY() +}; + +class FLOW_API IFlowPredicateInterface +{ + GENERATED_BODY() + +public: + + UFUNCTION(BlueprintNativeEvent) + bool EvaluatePredicate() const; + virtual bool EvaluatePredicate_Implementation() const { return true; } + + static bool ImplementsInterfaceSafe(const UFlowNodeAddOn* AddOnTemplate); +}; diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 7778011c5..e66fef2c4 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -4,44 +4,35 @@ #include "EdGraph/EdGraphNode.h" #include "GameplayTagContainer.h" -#include "Templates/SubclassOf.h" #include "VisualLogger/VisualLoggerDebugSnapshotInterface.h" -#include "FlowMessageLog.h" +#include "FlowNodeBase.h" #include "FlowTypes.h" +#include "Interfaces/FlowNativeExecutableInterface.h" #include "Nodes/FlowPin.h" -#include "FlowNode.generated.h" - -class IFlowOwnerInterface; -class UFlowAsset; -class UFlowSubsystem; -struct FFlowNodeSaveData; -#if WITH_EDITOR -DECLARE_DELEGATE(FFlowNodeEvent); -#endif +#include "FlowNode.generated.h" /** * A Flow Node is UObject-based node designed to handle entire gameplay feature within single node. */ UCLASS(Abstract, Blueprintable, HideCategories = Object) -class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInterface +class FLOW_API UFlowNode + : public UFlowNodeBase + , public IFlowNativeExecutableInterface + , public IVisualLoggerDebugSnapshotInterface { GENERATED_UCLASS_BODY() friend class SFlowGraphNode; friend class UFlowAsset; friend class UFlowGraphNode; - friend class UFlowGraphSchema; + friend class UFlowNodeAddOn; friend class SFlowInputPinHandle; friend class SFlowOutputPinHandle; ////////////////////////////////////////////////////////////////////////// // Node -private: - UPROPERTY() - UEdGraphNode* GraphNode; - #if WITH_EDITORONLY_DATA protected: @@ -50,30 +41,13 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UPROPERTY() TArray> DeniedAssetClasses; - - UPROPERTY() - FString Category; - - UPROPERTY(EditDefaultsOnly, Category = "FlowNode") - EFlowNodeStyle NodeStyle; - - // Set Node Style to custom to use your own color for this node - UPROPERTY(EditDefaultsOnly, Category = "FlowNode", meta = (EditCondition = "NodeStyle == EFlowNodeStyle::Custom")) - FLinearColor NodeColor; - - uint8 bCanDelete : 1; - uint8 bCanDuplicate : 1; - - UPROPERTY(EditDefaultsOnly, Category = "FlowNode") - bool bNodeDeprecated; - - // If this node is deprecated, it might be replaced by another node - UPROPERTY(EditDefaultsOnly, Category = "FlowNode") - TSubclassOf ReplacedBy; +#endif public: - FFlowNodeEvent OnReconstructionRequested; -#endif + // UFlowNodeBase + virtual UFlowNode* GetFlowNodeSelfOrOwner() override { return this; } + virtual bool IsSupportedInputPinName(const FName& PinName) const override; + // -- public: #if WITH_EDITOR @@ -82,38 +56,10 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte virtual void PostLoad() override; // -- - // Opportunity to update node's data before UFlowGraphNode would call ReconstructNode() - virtual void FixNode(UEdGraphNode* NewGraphNode); - virtual EDataValidationResult ValidateNode() { return EDataValidationResult::NotValidated; } - // used when import graph from another asset - virtual void PostImport() {} #endif - UEdGraphNode* GetGraphNode() const { return GraphNode; } - -#if WITH_EDITOR - void SetGraphNode(UEdGraphNode* NewGraph); - - virtual FString GetNodeCategory() const; - virtual FText GetNodeTitle() const; - virtual FText GetNodeToolTip() const; - - // This method allows to have different for every node instance, i.e. Red if node represents enemy, Green if node represents a friend - virtual bool GetDynamicTitleColor(FLinearColor& OutColor) const; - - EFlowNodeStyle GetNodeStyle() const { return NodeStyle; } - - // Short summary of node's content - displayed over node as NodeInfoPopup - virtual FString GetNodeDescription() const; -#endif - -protected: - // Short summary of node's content - displayed over node as NodeInfoPopup - UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Get Node Description")) - FString K2_GetNodeDescription() const; - // Inherits Guid after graph node UPROPERTY() FGuid NodeGuid; @@ -125,26 +71,6 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UFUNCTION(BlueprintPure, Category = "FlowNode") const FGuid& GetGuid() const { return NodeGuid; } - UFUNCTION(BlueprintPure, Category = "FlowNode") - UFlowAsset* GetFlowAsset() const; - - // Gets the Owning Actor for this Node's RootFlow - // (if the immediate parent is an UActorComponent, it will get that Component's actor) - AActor* TryGetRootFlowActorOwner() const; - - // Returns the IFlowOwnerInterface for the owner object (if implemented) - // NOTE - will consider a UActorComponent owner's owning actor if appropriate - IFlowOwnerInterface* GetFlowOwnerInterface() const; - -protected: - - // Helper functions for GetFlowOwnerInterface() - IFlowOwnerInterface* TryGetFlowOwnerInterfaceFromRootFlowOwner(UObject& RootFlowOwner, const UClass& ExpectedOwnerClass) const; - IFlowOwnerInterface* TryGetFlowOwnerInterfaceActor(UObject& RootFlowOwner, const UClass& ExpectedOwnerClass) const; - - // Gets the Owning Object for this Node's RootFlow - UObject* TryGetRootFlowObjectOwner() const; - public: virtual bool CanFinishGraph() const { return false; } @@ -157,10 +83,6 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UPROPERTY() EFlowSignalMode SignalMode; -#if WITH_EDITOR - FFlowMessageLog ValidationLog; -#endif - ////////////////////////////////////////////////////////////////////////// // All created pins (default, class-specific and added by user) @@ -180,6 +102,13 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte void AddInputPins(TArray Pins); void AddOutputPins(TArray Pins); +#if WITH_EDITOR + // Utility function to rebuild a pin array in editor (either InputPins or OutputPins, passed as InOutPins) + // returns true if the InOutPins array was rebuilt + bool RebuildPinArray(const TArray& NewPinNames, TArray& InOutPins, const FFlowPin& DefaultPin); + bool RebuildPinArray(const TArray& NewPins, TArray& InOutPins, const FFlowPin& DefaultPin); +#endif // WITH_EDITOR; + // always use default range for nodes with user-created outputs i.e. Execution Sequence void SetNumberedInputPins(const uint8 FirstNumber = 0, const uint8 LastNumber = 1); void SetNumberedOutputPins(const uint8 FirstNumber = 0, const uint8 LastNumber = 1); @@ -198,13 +127,9 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte TArray GetOutputNames() const; #if WITH_EDITOR - virtual bool SupportsContextPins() const { return false; } - - // Be careful, enabling it might cause loading gigabytes of data as nodes would load all related data (i.e. Level Sequences) - virtual bool CanRefreshContextPinsOnLoad() const { return false; } - - virtual TArray GetContextInputs() { return TArray(); } - virtual TArray GetContextOutputs() { return TArray(); } + // IFlowContextPinSupplierInterface + virtual bool SupportsContextPins() const override; + // -- virtual bool CanUserAddInput() const; virtual bool CanUserAddOutput() const; @@ -273,95 +198,24 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte TMap> OutputRecords; #endif -public: - UFUNCTION(BlueprintPure, Category = "FlowNode") - UFlowSubsystem* GetFlowSubsystem() const; - - virtual UWorld* GetWorld() const override; - -protected: - // Method called just after creating the node instance, while initializing the Flow Asset instance - // This happens before executing graph, only called during gameplay - virtual void InitializeInstance(); - - // Event called just after creating the node instance, while initializing the Flow Asset instance - // This happens before executing graph, only called during gameplay - UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Init Instance")) - void K2_InitializeInstance(); - public: void TriggerPreload(); void TriggerFlush(); protected: - virtual void PreloadContent(); - virtual void FlushContent(); - - UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Preload Content")) - void K2_PreloadContent(); - - UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Flush Content")) - void K2_FlushContent(); - - virtual void OnActivate(); - - UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "On Activate")) - void K2_OnActivate(); // Trigger execution of input pin void TriggerInput(const FName& PinName, const EFlowPinActivationType ActivationType = EFlowPinActivationType::Default); - // Method reacting on triggering Input pin - virtual void ExecuteInput(const FName& PinName); - - // Event reacting on triggering Input pin - UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Execute Input")) - void K2_ExecuteInput(const FName& PinName); - - // Simply trigger the first Output Pin, convenient to use if node has only one output - UFUNCTION(BlueprintCallable, Category = "FlowNode") - void TriggerFirstOutput(const bool bFinish); - - UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (HidePin = "bForcedActivation")) - void TriggerOutput(const FName& PinName, const bool bFinish = false, const EFlowPinActivationType ActivationType = EFlowPinActivationType::Default); - - void TriggerOutput(const FString& PinName, const bool bFinish = false); - void TriggerOutput(const FText& PinName, const bool bFinish = false); - void TriggerOutput(const TCHAR* PinName, const bool bFinish = false); - - UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (HidePin = "ActivationType")) - void TriggerOutputPin(const FFlowOutputPinHandle Pin, const bool bFinish = false, const EFlowPinActivationType ActivationType = EFlowPinActivationType::Default); - -public: - // Finish execution of node, it will call Cleanup - UFUNCTION(BlueprintCallable, Category = "FlowNode") - void Finish(); - + // IFlowNativeExecutableInterface protected: void Deactivate(); - // Method called after node finished the work - virtual void Cleanup(); - - // Event called after node finished the work - UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Cleanup")) - void K2_Cleanup(); - - // Method called from UFlowAsset::DeinitializeInstance() - virtual void DeinitializeInstance(); - - // Event called from UFlowAsset::DeinitializeInstance() - UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "DeinitializeInstance")) - void K2_DeinitializeInstance(); - + virtual void TriggerFirstOutput(const bool bFinish) override; + virtual void TriggerOutput(FName PinName, const bool bFinish = false, const EFlowPinActivationType ActivationType = EFlowPinActivationType::Default) override; public: - // Define what happens when node is terminated from the outside - virtual void ForceFinishNode(); - -protected: - // Define what happens when node is terminated from the outside - UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Force Finish Node")) - void K2_ForceFinishNode(); + virtual void Finish() override; + // -- private: void ResetRecords(); @@ -437,19 +291,4 @@ class FLOW_API UFlowNode : public UObject, public IVisualLoggerDebugSnapshotInte UFUNCTION(BlueprintPure, Category = "FlowNode") static FString GetProgressAsString(float Value); - -public: - UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) - void LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType = EFlowOnScreenMessageType::Permanent); - - UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) - void LogWarning(FString Message); - - UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) - void LogNote(FString Message); - -#if !UE_BUILD_SHIPPING -private: - bool BuildMessage(FString& Message) const; -#endif }; diff --git a/Source/Flow/Public/Nodes/FlowNodeAddOnBlueprint.h b/Source/Flow/Public/Nodes/FlowNodeAddOnBlueprint.h new file mode 100644 index 000000000..6751828d8 --- /dev/null +++ b/Source/Flow/Public/Nodes/FlowNodeAddOnBlueprint.h @@ -0,0 +1,24 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "CoreMinimal.h" +#include "Engine/Blueprint.h" +#include "FlowNodeAddOnBlueprint.generated.h" + +/** + * Flow Node AddOn Blueprint class + */ +UCLASS(BlueprintType) +class FLOW_API UFlowNodeAddOnBlueprint : public UBlueprint +{ + GENERATED_UCLASS_BODY() + +#if WITH_EDITOR + // UBlueprint + virtual bool SupportedByDefaultBlueprintFactory() const override { return false; } + + virtual bool SupportsDelegates() const override { return false; } + // -- +#endif +}; diff --git a/Source/Flow/Public/Nodes/FlowNodeBase.h b/Source/Flow/Public/Nodes/FlowNodeBase.h new file mode 100644 index 000000000..07d9c32e8 --- /dev/null +++ b/Source/Flow/Public/Nodes/FlowNodeBase.h @@ -0,0 +1,233 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Templates/SubclassOf.h" + +#include "Interfaces/FlowCoreExecutableInterface.h" +#include "Interfaces/FlowContextPinSupplierInterface.h" +#include "FlowMessageLog.h" +#include "FlowTypes.h" + +#include "FlowNodeBase.generated.h" + +class UFlowAsset; +class UFlowNode; +class UFlowNodeAddOn; +class UFlowSubsystem; +class UEdGraphNode; +class IFlowOwnerInterface; + +#if WITH_EDITOR +DECLARE_DELEGATE(FFlowNodeEvent); +#endif + +typedef TFunction FConstFlowNodeAddOnFunction; +typedef TFunction FFlowNodeAddOnFunction; + +/** + * The base class for UFlowNode and UFlowNodeAddOn, with their shared functionality + */ +UCLASS(Abstract, HideCategories = Object) +class FLOW_API UFlowNodeBase + : public UObject + , public IFlowCoreExecutableInterface + , public IFlowContextPinSupplierInterface +{ + GENERATED_UCLASS_BODY() + + // UObject + virtual UWorld* GetWorld() const override; +#if WITH_EDITOR + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; +#endif + // -- + + // IFlowCoreExecutableInterface + virtual void InitializeInstance(); + virtual void DeinitializeInstance(); + virtual void PreloadContent(); + virtual void FlushContent(); + virtual void OnActivate(); + virtual void Cleanup(); + virtual void ForceFinishNode(); + virtual void ExecuteInput(const FName& PinName); + // -- + + // UFlowNodeBase + virtual bool IsSupportedInputPinName(const FName& PinName) const PURE_VIRTUAL(IsSupportedInputPinName, return true;); + +protected: + // FlowNodes and AddOns may determine which AddOns are eligible to be their children + UFUNCTION(BlueprintNativeEvent, BlueprintPure) + EFlowAddOnAcceptResult AcceptFlowNodeAddOnChild(const UFlowNodeAddOn* AddOnTemplate) const; + +public: +#if WITH_EDITOR + EFlowAddOnAcceptResult CheckAcceptFlowNodeAddOnChild(const UFlowNodeAddOn* AddOnTemplate) const; + virtual TArray& GetFlowNodeAddOnChildrenByEditor() { return AddOns; } +#endif // WITH_EDITOR + virtual const TArray& GetFlowNodeAddOnChildren() const { return AddOns; } + + virtual UFlowNode* GetFlowNodeSelfOrOwner() PURE_VIRTUAL(GetFlowNodeSelfOrOwner, return nullptr;); + const UFlowNode* GetFlowNodeSelfOrOwner() const { return const_cast(this)->GetFlowNodeSelfOrOwner(); } + + // Call a Function for all of this object's AddOns (including all AddOn's AddOns, ie recursively) + void ForEachAddOnConst(FConstFlowNodeAddOnFunction Function) const; + void ForEachAddOn(FFlowNodeAddOnFunction Function) const; + + template + void ForEachAddOnForClassConst(FConstFlowNodeAddOnFunction Function) const { ForEachAddOnForClassConst(*TInterfaceOrClass::StaticClass(), Function); } + void ForEachAddOnForClassConst(const UClass& InterfaceOrClass, FConstFlowNodeAddOnFunction Function) const; + + template + void ForEachAddOnForClass(FFlowNodeAddOnFunction Function) const { ForEachAddOnForClass(*TInterfaceOrClass::StaticClass(), Function); } + void ForEachAddOnForClass(const UClass& InterfaceOrClass, FFlowNodeAddOnFunction Function) const; + // -- + + UFUNCTION(BlueprintPure, Category = "FlowNode") + UFlowAsset* GetFlowAsset() const; + + UFUNCTION(BlueprintPure, Category = "FlowNode") + UFlowSubsystem* GetFlowSubsystem() const; + + // Gets the Owning Actor for this Node's RootFlow + // (if the immediate parent is an UActorComponent, it will get that Component's actor) + UFUNCTION(BlueprintCallable, Category = "FlowNode") + AActor* TryGetRootFlowActorOwner() const; + + // Gets the Owning Object for this Node's RootFlow + UFUNCTION(BlueprintCallable, Category = "FlowNode") + UObject* TryGetRootFlowObjectOwner() const; + + // Returns the IFlowOwnerInterface for the owner object (if implemented) + // NOTE - will consider a UActorComponent owner's owning actor if appropriate + IFlowOwnerInterface* GetFlowOwnerInterface() const; + +protected: + + // Helper functions for GetFlowOwnerInterface() + IFlowOwnerInterface* TryGetFlowOwnerInterfaceFromRootFlowOwner(UObject& RootFlowOwner, const UClass& ExpectedOwnerClass) const; + IFlowOwnerInterface* TryGetFlowOwnerInterfaceActor(UObject& RootFlowOwner, const UClass& ExpectedOwnerClass) const; + +public: + + // Set the editor-only Config Text + // (for displaying config info on the Node in the flow graph, ignored in non-editor builds) + UFUNCTION(BlueprintCallable) + void SetNodeConfigText(const FText& NodeConfigText); + + // Called whenever a property change event occurs on this flow node object, + // giving the implementor a chance to update their NodeConfigText (via SetNodeConfigText) + UFUNCTION(BlueprintNativeEvent) + void UpdateNodeConfigText(); + +#if WITH_EDITOR + UEdGraphNode* GetGraphNode() const { return GraphNode; } + + // Opportunity to update node's data before UFlowGraphNode would call ReconstructNode() + virtual void FixNode(UEdGraphNode* NewGraphNode); + + void SetGraphNode(UEdGraphNode* NewGraphNode); + + // Setup the UFlowNodeBase when being opened for edit in the editor + virtual void SetupForEditing(UEdGraphNode& EdGraphNode); + + virtual FString GetNodeCategory() const; + virtual FText GetNodeTitle() const; + virtual FText GetNodeToolTip() const; + virtual FText GetNodeConfigText() const; + + // This method allows to have different for every node instance, i.e. Red if node represents enemy, Green if node represents a friend + virtual bool GetDynamicTitleColor(FLinearColor& OutColor) const; + + EFlowNodeStyle GetNodeStyle() const { return NodeStyle; } + + // Short summary of node's content - displayed over node as NodeInfoPopup + virtual FString GetNodeDescription() const; + + // used when import graph from another asset + virtual void PostImport() {} + + // IFlowContextPinSupplierInterface + virtual bool SupportsContextPins() const override { return false; } + virtual TArray GetContextInputs() const override; + virtual TArray GetContextOutputs() const override; + // -- + +#endif // WITH_EDITOR + + static const FFlowPin* FindFlowPinByName(const FName& PinName, const TArray& FlowPins); + + UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) + void LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType = EFlowOnScreenMessageType::Permanent); + + // LogError from constant function (allowing this to be modified only to log the error itself) + FORCEINLINE void LogErrorConst(FString Message, const EFlowOnScreenMessageType OnScreenMessageType = EFlowOnScreenMessageType::Permanent) const + { const_cast(this)->LogError(Message, OnScreenMessageType); } + + UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) + void LogWarning(FString Message); + + // LogWarning from constant function (allowing this to be modified only to log the warning itself) + FORCEINLINE void LogWarningConst(FString Message) const { const_cast(this)->LogWarning(Message); } + + UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) + void LogNote(FString Message); + + // LogNote from constant function (allowing this to be modified only to log the note itself) + FORCEINLINE void LogNoteConst(FString Message) const { const_cast(this)->LogNote(Message); } + +#if !UE_BUILD_SHIPPING +private: + bool BuildMessage(FString& Message) const; +#endif + +protected: + // Short summary of node's content - displayed over node as NodeInfoPopup + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Get Node Description")) + FString K2_GetNodeDescription() const; + +protected: + // Flow Node AddOn attachments + UPROPERTY(BlueprintReadOnly, Instanced, Category = "Configuration") + TArray AddOns; + +#if WITH_EDITORONLY_DATA + + UPROPERTY() + UEdGraphNode* GraphNode = nullptr; + +public: + UPROPERTY(EditDefaultsOnly, Category = "FlowNode") + EFlowNodeStyle NodeStyle = EFlowNodeStyle::Default; + + // Set Node Style to custom to use your own color for this node + UPROPERTY(EditDefaultsOnly, Category = "FlowNode", meta = (EditCondition = "NodeStyle == EFlowNodeStyle::Custom")) + FLinearColor NodeColor = FLinearColor::Black; + + uint8 bCanDelete : 1 = true; + uint8 bCanDuplicate : 1 = true; + + UPROPERTY() + FString Category; + + // Optional developer-facing text to explain the configuration of this node when viewed in the editor + // may be authored or set procedurally via UpdateNodeConfigText and SetNodeConfigText + UPROPERTY(EditDefaultsOnly, AdvancedDisplay, Category = "Configuration") + FText DevNodeConfigText = FText::GetEmpty(); + + UPROPERTY(EditDefaultsOnly, Category = "FlowNode") + bool bNodeDeprecated = false; + + // If this node is deprecated, it might be replaced by another node + UPROPERTY(EditDefaultsOnly, Category = "FlowNode") + TSubclassOf ReplacedBy; + + FFlowNodeEvent OnReconstructionRequested; + + FFlowMessageLog ValidationLog; + +#endif // WITH_EDITORONLY_DATA +}; + diff --git a/Source/Flow/Public/Nodes/FlowNodeBlueprint.h b/Source/Flow/Public/Nodes/FlowNodeBlueprint.h index 26b2fc5f2..48cbba250 100644 --- a/Source/Flow/Public/Nodes/FlowNodeBlueprint.h +++ b/Source/Flow/Public/Nodes/FlowNodeBlueprint.h @@ -6,7 +6,7 @@ #include "FlowNodeBlueprint.generated.h" /** - * A specialized blueprint class required for customizing Asset Type Actions + * Flow Node Blueprint class */ UCLASS(BlueprintType) class FLOW_API UFlowNodeBlueprint : public UBlueprint diff --git a/Source/Flow/Public/Nodes/FlowPin.h b/Source/Flow/Public/Nodes/FlowPin.h index 950e9373f..80f63a77a 100644 --- a/Source/Flow/Public/Nodes/FlowPin.h +++ b/Source/Flow/Public/Nodes/FlowPin.h @@ -2,9 +2,10 @@ #pragma once +#include "UObject/ObjectMacros.h" #include "FlowPin.generated.h" -USTRUCT() +USTRUCT(BlueprintType) struct FLOW_API FFlowPin { GENERATED_BODY() diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_Branch.h b/Source/Flow/Public/Nodes/Route/FlowNode_Branch.h new file mode 100644 index 000000000..04753960f --- /dev/null +++ b/Source/Flow/Public/Nodes/Route/FlowNode_Branch.h @@ -0,0 +1,27 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Nodes/FlowNode.h" + +#include "FlowNode_Branch.generated.h" + +// FEvaluates its AddOns that implement the IFlowPredicateInterface to determine the output pin to trigger +UCLASS(MinimalApi, NotBlueprintable, meta = (DisplayName = "Branch")) +class UFlowNode_Branch : public UFlowNode +{ + GENERATED_UCLASS_BODY() + +public: + + // UFlowNodeBase + virtual EFlowAddOnAcceptResult AcceptFlowNodeAddOnChild_Implementation(const UFlowNodeAddOn* AddOnTemplate) const override; + // -- + + // Event reacting on triggering Input pin + virtual void ExecuteInput(const FName& PinName) override; + + static const FName INPIN_Evaluate; + static const FName OUTPIN_True; + static const FName OUTPIN_False; +}; diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_SubGraph.h b/Source/Flow/Public/Nodes/Route/FlowNode_SubGraph.h index 49dbcf6d1..9bbd8afe7 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_SubGraph.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_SubGraph.h @@ -63,15 +63,16 @@ class FLOW_API UFlowNode_SubGraph : public UFlowNode #if WITH_EDITOR public: + // IFlowContextPinSupplierInterface + virtual bool SupportsContextPins() const override { return true; } + virtual TArray GetContextInputs() const override; + virtual TArray GetContextOutputs() const override; + // -- + virtual FString GetNodeDescription() const override; virtual UObject* GetAssetToEdit() override; virtual EDataValidationResult ValidateNode() override; - virtual bool SupportsContextPins() const override { return true; } - - virtual TArray GetContextInputs() override; - virtual TArray GetContextOutputs() override; - // UObject virtual void PostLoad() override; virtual void PreEditChange(FProperty* PropertyAboutToChange) override; diff --git a/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h b/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h index 0c30317e6..bfab6a21f 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h @@ -2,7 +2,9 @@ #pragma once -#include "FlowOwnerFunctionRef.h" +#include "GameplayTagContainer.h" + +#include "Types/FlowOwnerFunctionRef.h" #include "Nodes/FlowNode.h" #include "FlowNode_CallOwnerFunction.generated.h" @@ -24,60 +26,63 @@ class FLOW_API UFlowNode_CallOwnerFunction : public UFlowNode { GENERATED_UCLASS_BODY() -protected: - // Function reference on the expected owner to call - UPROPERTY(EditAnywhere, Category = "Call Owner", meta = (DisplayName = "Function")) - FFlowOwnerFunctionRef FunctionRef; - - // Parameter object to pass to the function when called - UPROPERTY(EditAnywhere, Category = "Call Owner", Instanced) - UFlowOwnerFunctionParams* Params; - -protected: - // UFlowNode - virtual void ExecuteInput(const FName& PinName) override; - // --- - - bool TryExecuteOutputPin(const FName& OutputName); - bool ShouldFinishForOutputName(const FName& OutputName) const; +public: #if WITH_EDITOR + // UObject + virtual void PostLoad() override; + virtual bool CanEditChange(const FProperty* InProperty) const override; + virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override; + // -- -public: // UFlowNode virtual FText GetNodeTitle() const override; - virtual FString GetNodeDescription() const override; - virtual FString GetStatusString() const override; virtual EDataValidationResult ValidateNode() override; - virtual bool SupportsContextPins() const override { return true; }; - virtual TArray GetContextInputs() override; - virtual TArray GetContextOutputs() override; - // --- - - // UObject - virtual void PostLoad() override; - virtual bool CanEditChange(const FProperty* InProperty) const override; - virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override; - // --- + virtual FString GetStatusString() const override; + // -- +#endif // WITH_EDITOR -protected: - bool TryAllocateParamsInstance(); + UFlowNode_CallOwnerFunction(); UClass* GetRequiredParamsClass() const; UClass* GetExistingParamsClass() const; - static UClass* GetParamsClassForFunctionName(const UClass& ExpectedOwnerClass, const FName& FunctionName); - static UClass* GetParamsClassForFunction(const UFunction& Function); - -public: bool IsAcceptableParamsPropertyClass(const UClass* ParamsClass) const; UClass* TryGetExpectedOwnerClass() const; + static bool DoesFunctionHaveValidFlowOwnerFunctionSignature(const UFunction& Function); + static UClass* GetParamsClassForFunctionName(const UClass& ExpectedOwnerClass, const FName& FunctionName); + static UClass* GetParamsClassForFunction(const UFunction& Function); + protected: + +#if WITH_EDITOR + void OnChangedParamsObject(); +#endif // WITH_EDITOR + + // UFlowNode + virtual void ExecuteInput(const FName& PinName) override; + // -- + + bool ShouldFinishForOutputName(const FName& OutputName) const; + bool TryExecuteOutputPin(const FName& OutputName); + + bool TryAllocateParamsInstance(); + // Helper function for DoesFunctionHaveValidFlowOwnerFunctionSignature() static bool DoesFunctionHaveNameReturnType(const UFunction& Function); -#endif // WITH_EDITOR + +protected: + + // Function reference on the expected owner to call + // DEPRECATED - Sunsetting this feature from FlowGraph with the next release. Custom FlowNodes are a better mechanism to use + UPROPERTY(EditAnywhere, Category = "Call Owner", meta = (DisplayName = "DEPRECATED - Function")) + FFlowOwnerFunctionRef FunctionRef; + + // Parameter object to pass to the function when called + UPROPERTY(EditAnywhere, Category = "Call Owner", Instanced) + UFlowOwnerFunctionParams* Params; }; diff --git a/Source/Flow/Public/Nodes/World/FlowNode_ExecuteComponent.h b/Source/Flow/Public/Nodes/World/FlowNode_ExecuteComponent.h new file mode 100644 index 000000000..a50d20105 --- /dev/null +++ b/Source/Flow/Public/Nodes/World/FlowNode_ExecuteComponent.h @@ -0,0 +1,122 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Types/FlowActorOwnerComponentRef.h" + +#include "Nodes/FlowNode.h" +#include "Types/FlowInjectComponentsHelper.h" + +#include "FlowNode_ExecuteComponent.generated.h" + +// Forward Declarations +class IFlowOwnerInterface; +class UFlowInjectComponentsManager; + +UENUM() +enum class EExecuteComponentSource : uint8 +{ + Undetermined, + + BindToExisting, + InjectFromTemplate, + InjectFromClass, + + Max UMETA(Hidden), + + UsesInjectManagerFirst = InjectFromTemplate UMETA(Hidden), + UsesInjectManagerLast = InjectFromClass UMETA(Hidden), +}; + +namespace EExecuteComponentSource_Classifiers +{ + FORCEINLINE bool DoesComponentSourceUseInjectManager(EExecuteComponentSource Source) { return Source >= EExecuteComponentSource::UsesInjectManagerFirst && Source <= EExecuteComponentSource::UsesInjectManagerLast; } +} + +/** + * Execute a UActorComponent on the owning actor as if it was a flow subgraph + */ +UCLASS(NotBlueprintable, meta = (DisplayName = "Execute Component")) +class FLOW_API UFlowNode_ExecuteComponent : public UFlowNode +{ + GENERATED_BODY() + +public: + + UFlowNode_ExecuteComponent(); + + // IFlowCoreExecutableInterface + virtual void InitializeInstance() override; + virtual void DeinitializeInstance() override; + virtual void PreloadContent() override; + virtual void FlushContent() override; + virtual void OnActivate() override; + virtual void Cleanup() override; + virtual void ForceFinishNode() override; + virtual void ExecuteInput(const FName& PinName) override; + // -- + + // UFlowNodeBase + virtual void UpdateNodeConfigText_Implementation() override; + // -- + +#if WITH_EDITOR + // UObject + virtual void PostLoad() override; + virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override; + // -- + + // UFlowNode + virtual FText GetNodeTitle() const override; + virtual EDataValidationResult ValidateNode() override; + + virtual FString GetStatusString() const override; + // -- +#endif // WITH_EDITOR + +protected: + +#if WITH_EDITOR + void RefreshPins(); + const UActorComponent* TryGetExpectedComponent() const; + + void RefreshComponentSource(); +#endif // WITH_EDITOR + + bool TryInjectComponent(); + + UActorComponent* TryResolveComponent(); + TSubclassOf TryGetExpectedActorOwnerClass() const; + +protected: + + // Executable Component (by name) on the expected Flow owning Actor + // (the component must implement the IFlowExecutableComponentInterface) + UPROPERTY(EditAnywhere, Category = "Flow Executable Component", meta = (DisplayName = "Component to Execute", MustImplement = "FlowCoreExecutableInterface,FlowExternalExecutableInterface", EditConditionHides, EditCondition = "ComponentSource == EExecuteComponentSource::BindToExisting || ComponentSource == EExecuteComponentSource::Undetermined")) + FFlowActorOwnerComponentRef ComponentRef; + + // Component (template) to inject on the spawned actor, may be configured inline + UPROPERTY(EditAnywhere, Instanced, Category = Configuration, DisplayName = "Inject & Execute Component (from Template)", meta = (MustImplement = "FlowCoreExecutableInterface,FlowExternalExecutableInterface", EditConditionHides, EditCondition = "ComponentSource == EExecuteComponentSource::InjectFromTemplate || ComponentSource == EExecuteComponentSource::Undetermined")) + TObjectPtr ComponentTemplate = nullptr; + + // Component (class) to inject on the spawned actor + UPROPERTY(EditAnywhere, Category = Configuration, DisplayName = "Inject & Execute Component (by Class)", meta = (MustImplement = "FlowCoreExecutableInterface,FlowExternalExecutableInterface", EditConditionHides, EditCondition = "ComponentSource == EExecuteComponentSource::InjectFromClass || ComponentSource == EExecuteComponentSource::Undetermined")) + TSubclassOf ComponentClass = nullptr; + + // Manager object to inject and remove components from the Flow owning Actor + UPROPERTY(Transient) + TObjectPtr InjectComponentsManager = nullptr; + + // Look for the component (by class) on the Actor and re-use it (rather than injecting) + // if the component already exists. + UPROPERTY(EditAnywhere, Category = Configuration, DisplayName = "Re-use existing component if found", meta = (EditConditionHides, EditCondition = "ComponentSource == EExecuteComponentSource::InjectFromClass")) + bool bReuseExistingComponent = true; + + // Allow injecting the component, if it cannot be found on the Actor + UPROPERTY(EditAnywhere, Category = Configuration, DisplayName = "Allow injecting component", meta = (EditConditionHides, EditCondition = "ComponentSource == EExecuteComponentSource::InjectFromClass && bReuseExistingComponent")) + bool bAllowInjectComponent = true; + + // Inject component(s) onto the owning Actor + UPROPERTY() + EExecuteComponentSource ComponentSource = EExecuteComponentSource::Undetermined; +}; diff --git a/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h b/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h index d7ef98831..5a6626ac5 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h @@ -85,8 +85,10 @@ class FLOW_API UFlowNode_PlayLevelSequence : public UFlowNode public: #if WITH_EDITOR + // IFlowContextPinSupplierInterface virtual bool SupportsContextPins() const override { return true; } - virtual TArray GetContextOutputs() override; + virtual TArray GetContextOutputs() const override; + // -- virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; #endif diff --git a/Source/Flow/Public/Types/FlowActorOwnerComponentRef.h b/Source/Flow/Public/Types/FlowActorOwnerComponentRef.h new file mode 100644 index 000000000..dd29bf4bf --- /dev/null +++ b/Source/Flow/Public/Types/FlowActorOwnerComponentRef.h @@ -0,0 +1,48 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "UObject/ObjectPtr.h" + +#include "FlowActorOwnerComponentRef.generated.h" + +// Forward Declarations +class UActorComponent; + +// Similar to FAnimNodeFunctionRef, providing a FName-based Component binding +// that is resolved at runtime +USTRUCT(BlueprintType) +struct FFlowActorOwnerComponentRef +{ + GENERATED_BODY() + +public: + + // Tries to find the component by name on the given actor + UActorComponent* TryResolveComponent(const AActor& InActor, bool bWarnIfFailed = true); + + // In some cases, the component can be resolved directly + void SetResolvedComponentDirect(UActorComponent& Component); + + // Returns a the resolved component + // (assumes TryResolveComponent() was called previously) + UActorComponent* GetResolvedComponent() const { return ResolvedComponent; } + + // Accessors + bool IsConfigured() const { return !ComponentName.IsNone(); } + bool IsResolved() const; + + static UActorComponent* TryResolveComponentByName(const AActor& InActor, const FName& InComponentName); + +public: + + // The name of the component + UPROPERTY(VisibleAnywhere, Category = "Flow Actor Owner Component") + FName ComponentName = NAME_None; + +protected: + + // Cached resolved component (resolved at runtime by calling TryResolveComponent) + UPROPERTY(Transient) + TObjectPtr ResolvedComponent = nullptr; +}; diff --git a/Source/Flow/Public/Types/FlowInjectComponentsHelper.h b/Source/Flow/Public/Types/FlowInjectComponentsHelper.h new file mode 100644 index 000000000..fdfac3de4 --- /dev/null +++ b/Source/Flow/Public/Types/FlowInjectComponentsHelper.h @@ -0,0 +1,39 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "FlowInjectComponentsHelper.generated.h" + +class AActor; +class UActorComponent; + +// Configuration helper struct for injecting components onto actors +USTRUCT() +struct FFlowInjectComponentsHelper +{ + GENERATED_BODY() + +public: + + FLOW_API TArray CreateComponentInstancesForActor(AActor& Actor); + + // Static functions to create a component for injection: + static FLOW_API UActorComponent* TryCreateComponentInstanceForActorFromTemplate(AActor& Actor, UActorComponent& ComponentTemplate); + static FLOW_API UActorComponent* TryCreateComponentInstanceForActorFromClass(AActor& Actor, TSubclassOf ComponentClass, const FName& InstanceBaseName); + + // After creating using one of the above two functions, inject into the actor: + static FLOW_API void InjectCreatedComponent(AActor& Actor, UActorComponent& ComponentInstance); + + // Remove & Destroy the injected component: + static FLOW_API void DestroyInjectedComponent(AActor& Actor, UActorComponent& ComponentInstance); + +public: + + // Component (template) to inject on the spawned actor + UPROPERTY(EditAnywhere, Instanced, Category = Configuration) + TArray> ComponentTemplates; + + // Component (template) to inject on the spawned actor + UPROPERTY(EditAnywhere, Category = Configuration) + TArray> ComponentClasses; +}; diff --git a/Source/Flow/Public/Types/FlowInjectComponentsManager.h b/Source/Flow/Public/Types/FlowInjectComponentsManager.h new file mode 100644 index 000000000..215306555 --- /dev/null +++ b/Source/Flow/Public/Types/FlowInjectComponentsManager.h @@ -0,0 +1,68 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "UObject/Object.h" + +#include "FlowInjectComponentsManager.generated.h" + +class AActor; +class UActorComponent; +class UFlowNodeBase; + +// Container for injected component instances +USTRUCT() +struct FLOW_API FFlowComponentInstances +{ + GENERATED_BODY() + +public: + + UPROPERTY(Transient) + TArray> Components; +}; + +// Inject components onto actors and will remove them when they are destroyed (or this is shutdown) +UCLASS(MinimalAPI) +class UFlowInjectComponentsManager : public UObject +{ + GENERATED_BODY() + +public: + + FLOW_API void InitializeRuntime(); + FLOW_API void ShutdownRuntime(); + + FLOW_API FORCEINLINE void InjectComponentOnActor(AActor& Actor, UActorComponent& ComponentInstance) { AddAndRegisterComponent(Actor, ComponentInstance); } + FLOW_API void InjectComponentsOnActor(AActor& Actor, const TArray& ComponentInstances); + + FLOW_API void RemoveAllInjectedComponentsAndStopMonitoringActor(AActor& Actor); + +protected: + + FLOW_API void AddAndRegisterComponent(AActor& Actor, UActorComponent& ComponentInstance); + FLOW_API void RemoveAndUnregisterComponent(AActor& Actor, UActorComponent& ComponentInstance); + + FLOW_API void RegisterOnDestroyedDelegate(AActor& Actor); + FLOW_API void UnregisterOnDestroyedDelegate(AActor& Actor); + + FLOW_API void RemoveInjectedComponents(); + + UFUNCTION() + FLOW_API void OnActorDestroyed(AActor* DestroyedActor); + +public: + + DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FFlowBeforeOnActorRemoved, AActor*, SpawnedActor); + + UPROPERTY(BlueprintAssignable) + FFlowBeforeOnActorRemoved BeforeActorRemovedDelegate; + + // Remove the Injected Components from the Actors when Deinitialized + UPROPERTY() + bool bRemoveInjectedComponentsWhenDeinitializing = true; + + // Map of spawned components (if we are cleaning up) + UPROPERTY(Transient) + TMap ActorToComponentsMap; +}; diff --git a/Source/Flow/Public/FlowOwnerFunctionParams.h b/Source/Flow/Public/Types/FlowOwnerFunctionParams.h similarity index 96% rename from Source/Flow/Public/FlowOwnerFunctionParams.h rename to Source/Flow/Public/Types/FlowOwnerFunctionParams.h index 1995c8c66..a9b31984b 100644 --- a/Source/Flow/Public/FlowOwnerFunctionParams.h +++ b/Source/Flow/Public/Types/FlowOwnerFunctionParams.h @@ -2,18 +2,18 @@ #pragma once -#include "CoreMinimal.h" - #include "FlowOwnerFunctionParams.generated.h" +// Forward Declarations class UFlowNode_CallOwnerFunction; UCLASS(BlueprintType, Blueprintable, EditInlineNew) class FLOW_API UFlowOwnerFunctionParams : public UObject { GENERATED_BODY() - + public: + UFlowOwnerFunctionParams(); void PreExecute(UFlowNode_CallOwnerFunction& InSourceNode, const FName& InputPinName); @@ -34,6 +34,7 @@ class FLOW_API UFlowOwnerFunctionParams : public UObject TArray GatherOutputNames() const { return BP_GetOutputNames(); } protected: + // Called prior to the owner executing the function described by this object. // Can be overridden to prepare the stateful data before execution. UFUNCTION(BlueprintImplementableEvent, Category = "FlowOwnerFunction", DisplayName = "PreExecute") @@ -55,9 +56,9 @@ class FLOW_API UFlowOwnerFunctionParams : public UObject TArray BP_GetOutputNames() const; protected: + // CallOwnerObjectFunction node that is executing this set of function params. // Valid only if called between PreExecute() and PostExecute(), inclusive - UPROPERTY(Transient, BlueprintReadOnly, Category = "FlowOwnerFunction") UFlowNode_CallOwnerFunction* SourceNode = nullptr; // This is the Name from the Input Pin that caused this node to Execute. diff --git a/Source/Flow/Public/FlowOwnerFunctionRef.h b/Source/Flow/Public/Types/FlowOwnerFunctionRef.h similarity index 97% rename from Source/Flow/Public/FlowOwnerFunctionRef.h rename to Source/Flow/Public/Types/FlowOwnerFunctionRef.h index e3f5c5e80..a3581d67b 100644 --- a/Source/Flow/Public/FlowOwnerFunctionRef.h +++ b/Source/Flow/Public/Types/FlowOwnerFunctionRef.h @@ -2,10 +2,12 @@ #pragma once +#include "CoreMinimal.h" #include "Templates/SubclassOf.h" #include "FlowOwnerFunctionRef.generated.h" +// Forward Declarations class UFlowOwnerFunctionParams; class IFlowOwnerInterface; diff --git a/Source/FlowEditor/FlowEditor.Build.cs b/Source/FlowEditor/FlowEditor.Build.cs index 00d4faf7d..4dff32d7c 100644 --- a/Source/FlowEditor/FlowEditor.Build.cs +++ b/Source/FlowEditor/FlowEditor.Build.cs @@ -12,7 +12,8 @@ public FlowEditor(ReadOnlyTargetRules target) : base(target) { "EditorSubsystem", "Flow", - "MessageLog" + "MessageLog", + "AIModule", // For BlueprintNodeHelpers::DescribeProperty (could be copy/pasted out to remove editor-only dependency) }); PrivateDependencyModuleNames.AddRange(new[] diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index c4171dce0..b3bb8a317 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -4,7 +4,6 @@ #include "FlowEditorCommands.h" #include "FlowEditorLogChannels.h" -#include "FlowMessageLog.h" #include "Asset/FlowAssetEditorContext.h" #include "Asset/FlowAssetToolbar.h" @@ -188,6 +187,31 @@ void FFlowAssetEditor::PostRegenerateMenusAndToolbars() SetMenuOverlay(MenuOverlayBox); } +void FFlowAssetEditor::SaveAsset_Execute() +{ + DoPresaveAssetUpdate(); + + FAssetEditorToolkit::SaveAsset_Execute(); +} +void FFlowAssetEditor::SaveAssetAs_Execute() +{ + DoPresaveAssetUpdate(); + + FAssetEditorToolkit::SaveAssetAs_Execute(); +} + +void FFlowAssetEditor::DoPresaveAssetUpdate() +{ + if (IsValid(FlowAsset)) + { + UFlowGraph* FlowGraph = Cast(FlowAsset->GetGraph()); + if (IsValid(FlowGraph)) + { + FlowGraph->OnSave(); + } + } +} + bool FFlowAssetEditor::IsTabFocused(const FTabId& TabId) const { if (const TSharedPtr CurrentGraphTab = GetToolkitHost()->GetTabManager()->FindExistingLiveTab(TabId)) @@ -278,6 +302,13 @@ void FFlowAssetEditor::InitFlowAssetEditor(const EToolkitMode::Type Mode, const { FlowAsset = CastChecked(ObjectToEdit); + UFlowGraph* FlowGraph = Cast(FlowAsset->GetGraph()); + if (IsValid(FlowGraph)) + { + // Call the OnLoaded event for the flowgraph that is being edited + FlowGraph->OnLoaded(); + } + // Support undo/redo FlowAsset->SetFlags(RF_Transactional); GEditor->RegisterForUndo(this); @@ -532,12 +563,16 @@ FName FFlowAssetEditor::GetUISelectionState() const return CurrentUISelection; } +void FFlowAssetEditor::OnSelectedNodesChanged(const TSet& Nodes) +{ +} + #if ENABLE_JUMP_TO_INNER_OBJECT void FFlowAssetEditor::JumpToInnerObject(UObject* InnerObject) { - if (const UFlowNode* FlowNode = Cast(InnerObject)) + if (const UFlowNodeBase* FlowNodeBase = Cast(InnerObject)) { - GraphEditor->JumpToNode(FlowNode->GetGraphNode(), true); + GraphEditor->JumpToNode(FlowNodeBase->GetGraphNode(), true); } else if (const UEdGraphNode* GraphNode = Cast(InnerObject)) { diff --git a/Source/FlowEditor/Private/Asset/FlowAssetIndexer.cpp b/Source/FlowEditor/Private/Asset/FlowAssetIndexer.cpp index 8c22f6ded..0d1a3e0f1 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetIndexer.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetIndexer.cpp @@ -3,7 +3,7 @@ #include "Asset/FlowAssetIndexer.h" #include "FlowAsset.h" -#include "Nodes/FlowNode.h" +#include "Nodes/FlowNodeBase.h" #include "Graph/Nodes/FlowGraphNode_Reroute.h" @@ -112,11 +112,11 @@ void FFlowAssetIndexer::IndexGraph(const UFlowAsset* InFlowAsset, FSearchSeriali // Indexing Flow Node if (const UFlowGraphNode* FlowGraphNode = Cast(Node)) { - if (const UFlowNode* FlowNode = FlowGraphNode->GetFlowNode()) + if (const UFlowNodeBase* FlowNodeBase = FlowGraphNode->GetFlowNodeBase()) { - const FString NodeFriendlyName = FString::Printf(TEXT("%s: %s"), *FlowNode->GetClass()->GetName(), *FlowNode->GetNodeDescription()); - Serializer.BeginIndexingObject(FlowNode, NodeFriendlyName); - FIndexerUtilities::IterateIndexableProperties(FlowNode, [&Serializer](const FProperty* Property, const FString& Value) + const FString NodeFriendlyName = FString::Printf(TEXT("%s: %s"), *FlowNodeBase->GetClass()->GetName(), *FlowNodeBase->GetNodeDescription()); + Serializer.BeginIndexingObject(FlowNodeBase, NodeFriendlyName); + FIndexerUtilities::IterateIndexableProperties(FlowNodeBase, [&Serializer](const FProperty* Property, const FString& Value) { Serializer.IndexProperty(Property, Value); }); diff --git a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp index 37d30775f..e36b8f1b8 100644 --- a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp +++ b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp @@ -166,7 +166,8 @@ void UFlowImportUtils::ImportBlueprintGraph(UBlueprint* Blueprint, UFlowAsset* F UFlowGraphNode* StartGraphNode = FFlowGraphSchemaAction_NewNode::CreateNode(FlowGraph, nullptr, UFlowNode_Start::StaticClass(), FVector2D::ZeroVector); FlowGraph->GetSchema()->SetNodeMetaData(StartGraphNode, FNodeMetadata::DefaultGraphNode); StartGraphNode->NodeGuid = StartNode->NodeGuid; - StartGraphNode->GetFlowNode()->SetGuid(StartNode->NodeGuid); + UFlowNode* StartFlowNode = Cast(StartGraphNode->GetFlowNodeBase()); + StartFlowNode->SetGuid(StartNode->NodeGuid); TargetNodes.Add(StartGraphNode->NodeGuid, StartGraphNode); // execute graph import @@ -232,7 +233,7 @@ void UFlowImportUtils::ImportBlueprintFunction(const UFlowAsset* FlowAsset, cons TMap InputPins; GetValidInputPins(NodeImport.SourceGraphNode, InputPins); - UClass* FlowNodeClass = FlowGraphNode->GetFlowNode()->GetClass(); + UClass* FlowNodeClass = FlowGraphNode->GetFlowNodeBase()->GetClass(); for (TFieldIterator PropIt(FlowNodeClass, EFieldIteratorFlags::IncludeSuper); PropIt && (PropIt->PropertyFlags & CPF_Edit); ++PropIt) { const FProperty* Param = *PropIt; @@ -244,8 +245,8 @@ void UFlowImportUtils::ImportBlueprintFunction(const UFlowAsset* FlowAsset, cons if (MatchingInputPin->LinkedTo.Num() == 0) // nothing connected to pin, so user can set value directly on this pin { FString const PinValue = MatchingInputPin->GetDefaultAsString(); - uint8* Offset = Param->ContainerPtrToValuePtr(FlowGraphNode->GetFlowNode()); - Param->ImportText_Direct(*PinValue, Offset, FlowGraphNode->GetFlowNode(), PPF_Copy, GLog); + uint8* Offset = Param->ContainerPtrToValuePtr(FlowGraphNode->GetFlowNodeBase()); + Param->ImportText_Direct(*PinValue, Offset, FlowGraphNode->GetFlowNodeBase(), PPF_Copy, GLog); } } else // try to find matching Pin in connected pure nodes @@ -272,8 +273,8 @@ void UFlowImportUtils::ImportBlueprintFunction(const UFlowAsset* FlowAsset, cons if (PureInputPin->LinkedTo.Num() == 0) // nothing connected to pin, so user can set value directly on this pin { FString const PinValue = PureInputPin->GetDefaultAsString(); - uint8* Offset = Param->ContainerPtrToValuePtr(FlowGraphNode->GetFlowNode()); - Param->ImportText_Direct(*PinValue, Offset, FlowGraphNode->GetFlowNode(), PPF_Copy, GLog); + uint8* Offset = Param->ContainerPtrToValuePtr(FlowGraphNode->GetFlowNodeBase()); + Param->ImportText_Direct(*PinValue, Offset, FlowGraphNode->GetFlowNodeBase(), PPF_Copy, GLog); bPinFound = true; } @@ -296,7 +297,7 @@ void UFlowImportUtils::ImportBlueprintFunction(const UFlowAsset* FlowAsset, cons } // Flow Nodes with Context Pins needs to update related data and call OnReconstructionRequested.ExecuteIfBound() in order to fully construct a graph node - FlowGraphNode->GetFlowNode()->PostImport(); + FlowGraphNode->GetFlowNodeBase()->PostImport(); // connect new node to all already recreated nodes for (const TPair& Connection : NodeImport.Incoming) diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowActorOwnerComponentFilters.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowActorOwnerComponentFilters.cpp new file mode 100644 index 000000000..38b6ce96d --- /dev/null +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowActorOwnerComponentFilters.cpp @@ -0,0 +1,186 @@ + // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "FlowActorOwnerComponentFilters.h" + +#include "Components/ActorComponent.h" +#include "GameFramework/Actor.h" +#include "UObject/UObjectIterator.h" + +#if WITH_EDITOR +#include "EditorClassUtils.h" +#endif // WITH_EDITOR + +#if WITH_EDITOR + +const FName FFlowActorOwnerComponentFilters::NAME_AllowedClasses = "AllowedClasses"; +const FName FFlowActorOwnerComponentFilters::NAME_DisallowedClasses = "DisallowedClasses"; +const FName FFlowActorOwnerComponentFilters::NAME_MustImplement = "MustImplement"; + +void FFlowActorOwnerComponentFilters::BuildFiltersFromMetadata(const FProperty& ComponentNameProperty) +{ + if (bHasBuiltFilters) + { + return; + } + + BuildClassFilters(ComponentNameProperty); + BuildInterfaceFilters(ComponentNameProperty); + + bHasBuiltFilters = true; +} + +void FFlowActorOwnerComponentFilters::BuildClassFilters(const FProperty& ComponentNameProperty) +{ + // NOTE (gtaylor) Adapted from FComponentReferenceCustomization::BuildClassFilters() + + auto AddToClassFilters = [this](const UClass* Class, TArray& ComponentList) + { + if (Class->IsChildOf(UActorComponent::StaticClass())) + { + ComponentList.Add(Class); + } + }; + + auto ParseClassFilters = [this, AddToClassFilters](const FString& MetaDataString, TArray& ComponentList) + { + if (!MetaDataString.IsEmpty()) + { + TArray ClassFilterNames; + MetaDataString.ParseIntoArrayWS(ClassFilterNames, TEXT(","), true); + + for (const FString& ClassName : ClassFilterNames) + { + UClass* Class = FindFirstObject(*ClassName, EFindFirstObjectOptions::EnsureIfAmbiguous); + if (!Class) + { + Class = LoadObject(nullptr, *ClassName); + } + + if (Class) + { + // If the class is an interface, expand it to be all classes in memory that implement the class. + if (Class->HasAnyClassFlags(CLASS_Interface)) + { + for (TObjectIterator ClassIt; ClassIt; ++ClassIt) + { + UClass* const ClassWithInterface = (*ClassIt); + if (ClassWithInterface->ImplementsInterface(Class)) + { + AddToClassFilters(ClassWithInterface, ComponentList); + } + } + } + else + { + AddToClassFilters(Class, ComponentList); + } + } + } + } + }; + + // Account for the allowed classes specified in the property metadata + const FString& AllowedClassesFilterString = ComponentNameProperty.GetMetaData(NAME_AllowedClasses); + ParseClassFilters(AllowedClassesFilterString, AllowedComponentClassFilters); + + // Account for disallowed classes specified in the property metadata + const FString& DisallowedClassesFilterString = ComponentNameProperty.GetMetaData(NAME_DisallowedClasses); + ParseClassFilters(DisallowedClassesFilterString, DisallowedComponentClassFilters); +} + +void FFlowActorOwnerComponentFilters::BuildInterfaceFilters(const FProperty& ComponentNameProperty) +{ + auto ParseInterfaceFilters = [this](const FString& MetaDataString, TArray& RequiredInterfaces) + { + if (!MetaDataString.IsEmpty()) + { + TArray InterfaceFilterNames; + MetaDataString.ParseIntoArrayWS(InterfaceFilterNames, TEXT(","), true); + + for (const FString& InterfaceName : InterfaceFilterNames) + { + if (const UClass* RequiredInterface = FEditorClassUtils::GetClassFromString(InterfaceName)) + { + RequiredInterfaces.Add(RequiredInterface); + } + } + } + }; + + // MustImplement interface(s) + const FString& MustImplementInterfacesFilterString = ComponentNameProperty.GetMetaData(NAME_MustImplement); + ParseInterfaceFilters(MustImplementInterfacesFilterString, RequiredInterfaceFilters); +} + +bool FFlowActorOwnerComponentFilters::IsFilteredComponent(const UActorComponent& Component) const +{ + check(bHasBuiltFilters); + + // For Now(tm) at least, hard coding excluding Transient components + // (could make this configurable, but that doesn't make any sense for FRGIPeerComponentReference) + constexpr bool bAllowTransient = false; + if constexpr (!bAllowTransient) + { + const EObjectFlags Flags = Component.GetFlags(); + const bool bIsTransient = (Flags & RF_Transient) != 0; + if (bIsTransient) + { + // The component is allowed to be transient if the owning actor is also marked as transient. + // This happens with level instance actors placed in a level. + if (const AActor* CompOwnerActor = Component.GetOwner()) + { + const EObjectFlags OuterFlags = CompOwnerActor->GetFlags(); + const bool bIsOuterTransient = (OuterFlags & RF_Transient) != 0; + if(!bIsOuterTransient) + { + return false; + } + } + } + } + + // Check for required interface(s) + for (const UClass* RequiredInterface : RequiredInterfaceFilters) + { + if (IsValid(RequiredInterface) && !Component.GetClass()->ImplementsInterface(RequiredInterface)) + { + return false; + } + } + + // NOTE (gtaylor) Adapted from FComponentReferenceCustomization::IsFilteredObject + + bool bAllowedToSetBasedOnFilter = true; + + const UClass* ObjectClass = Component.GetClass(); + if (AllowedComponentClassFilters.Num() > 0) + { + bAllowedToSetBasedOnFilter = false; + for (const UClass* AllowedClass : AllowedComponentClassFilters) + { + const bool bAllowedClassIsInterface = AllowedClass->HasAnyClassFlags(CLASS_Interface); + if (ObjectClass->IsChildOf(AllowedClass) || (bAllowedClassIsInterface && ObjectClass->ImplementsInterface(AllowedClass))) + { + bAllowedToSetBasedOnFilter = true; + break; + } + } + } + + if (DisallowedComponentClassFilters.Num() > 0 && bAllowedToSetBasedOnFilter) + { + for (const UClass* DisallowedClass : DisallowedComponentClassFilters) + { + const bool bDisallowedClassIsInterface = DisallowedClass->HasAnyClassFlags(CLASS_Interface); + if (ObjectClass->IsChildOf(DisallowedClass) || (bDisallowedClassIsInterface && ObjectClass->ImplementsInterface(DisallowedClass))) + { + bAllowedToSetBasedOnFilter = false; + break; + } + } + } + + return bAllowedToSetBasedOnFilter; +} + +#endif // WITH_EDITOR diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowActorOwnerComponentFilters.h b/Source/FlowEditor/Private/DetailCustomizations/FlowActorOwnerComponentFilters.h new file mode 100644 index 000000000..a7d4f6a08 --- /dev/null +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowActorOwnerComponentFilters.h @@ -0,0 +1,57 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "UObject/ObjectPtr.h" + +#include "FlowActorOwnerComponentFilters.generated.h" + +// Forward Declarations +class UActorComponent; +class IPropertyHandle; + +// Metadata-derived filters to describe qualifying UActorComponents +// for a given FFlowActorOwnerComponentRef +USTRUCT() +struct FFlowActorOwnerComponentFilters +{ + GENERATED_BODY() + +#if WITH_EDITOR +public: + void BuildFiltersFromMetadata(const FProperty& ComponentNameProperty); + + // Returns true if the Component passes the filters (built in BuildFiltersFromMetadata) + bool IsFilteredComponent(const UActorComponent& Component) const; + +protected: + void BuildClassFilters(const FProperty& ComponentNameProperty); + void BuildInterfaceFilters(const FProperty& ComponentNameProperty); + +#endif // WITH_EDITOR + +protected: + +#if WITH_EDITORONLY_DATA + // Classes that can be used with this property + UPROPERTY(Transient) + TArray AllowedComponentClassFilters; + + // Classes that can NOT be used with this property + UPROPERTY(Transient) + TArray DisallowedComponentClassFilters; + + // Must implement (all) interface(s) + UPROPERTY(Transient) + TArray RequiredInterfaceFilters; + + // Has BuildClassFiltersFromMetadata been called? + UPROPERTY(Transient) + bool bHasBuiltFilters = false; + + // Meta-data keys + static const FName NAME_AllowedClasses; + static const FName NAME_DisallowedClasses; + static const FName NAME_MustImplement; +#endif // WITH_EDITORONLY_DATA +}; diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowActorOwnerComponentRefCustomization.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowActorOwnerComponentRefCustomization.cpp new file mode 100644 index 000000000..fa136ba69 --- /dev/null +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowActorOwnerComponentRefCustomization.cpp @@ -0,0 +1,141 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "DetailCustomizations/FlowActorOwnerComponentRefCustomization.h" + +#include "Nodes/FlowNode.h" +#include "FlowAsset.h" +#include "AddOns/FlowNodeAddOn.h" +#include "FlowActorOwnerComponentFilters.h" + +#include "UObject/UnrealType.h" +#include "GameFramework/Actor.h" + +void FFlowActorOwnerComponentRefCustomization::CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) +{ + // Do not include children properties (the header is all we need to show for this struct) +} + +TSharedPtr FFlowActorOwnerComponentRefCustomization::GetCuratedNamePropertyHandle() const +{ + check(StructPropertyHandle->IsValidHandle()); + + TSharedPtr FoundHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowActorOwnerComponentRef, ComponentName)); + check(FoundHandle); + + return FoundHandle; +} + +TArray FFlowActorOwnerComponentRefCustomization::GetCuratedNameOptions() const +{ + TArray Results; + + UClass* ExpectedOwnerClass = TryGetExpectedOwnerClass(); + if (!IsValid(ExpectedOwnerClass) || !ExpectedOwnerClass->IsChildOf()) + { + return Results; + } + + Results = GetFlowActorOwnerComponents(ExpectedOwnerClass); + + return Results; +} + +UClass* FFlowActorOwnerComponentRefCustomization::TryGetExpectedOwnerClass() const +{ + const UFlowNode* NodeOwner = TryGetFlowNodeOuter(); + if (!IsValid(NodeOwner)) + { + return nullptr; + } + + const UFlowAsset* FlowAsset = NodeOwner->GetFlowAsset(); + if (!IsValid(FlowAsset)) + { + return nullptr; + } + + UClass* ExpectedOwnerClass = FlowAsset->GetExpectedOwnerClass(); + return ExpectedOwnerClass; +} + +TArray FFlowActorOwnerComponentRefCustomization::GetFlowActorOwnerComponents(TSubclassOf ExpectedActorOwnerClass) const +{ + TArray AllComponents; + AActor::GetActorClassDefaultComponents(ExpectedActorOwnerClass, AllComponents); + + // Array for components that pass the metadata filter + TArray PassedComponentNames; + PassedComponentNames.Reserve(AllComponents.Num()); + + const FProperty* MetadataProperty = StructPropertyHandle->GetMetaDataProperty(); + if (ensure(MetadataProperty)) + { + // Pull the metadata from the struct property, setting up the AllowedClass filters, etc. + FFlowActorOwnerComponentFilters Filters; + Filters.BuildFiltersFromMetadata(*MetadataProperty); + + for (const UActorComponent* ActorComponent : AllComponents) + { + if (Filters.IsFilteredComponent(*ActorComponent)) + { + FString ComponentCleanedName = ActorComponent->GetFName().ToString(); + + // Some components end with _GEN_VARIABLE, remove that suffix so we can match component names + ComponentCleanedName.RemoveFromEnd(UActorComponent::ComponentTemplateNameSuffix); + + PassedComponentNames.Add(FName(ComponentCleanedName)); + } + } + } + + return PassedComponentNames; +} + +void FFlowActorOwnerComponentRefCustomization::SetCuratedName(const FName& NewComponentName) +{ + TSharedPtr ComponentNameHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowActorOwnerComponentRef, ComponentName)); + + check(ComponentNameHandle); + + ComponentNameHandle->SetPerObjectValue(0, NewComponentName.ToString()); +} + +bool FFlowActorOwnerComponentRefCustomization::TryGetCuratedName(FName& OutName) const +{ + const FFlowActorOwnerComponentRef* ComponentRef = GetFlowActorOwnerComponentRef(); + if (ComponentRef) + { + OutName = ComponentRef->ComponentName; + + return true; + } + else + { + return false; + } +} + +UFlowNode* FFlowActorOwnerComponentRefCustomization::TryGetFlowNodeOuter() const +{ + check(StructPropertyHandle->IsValidHandle()); + + TArray OuterObjects; + StructPropertyHandle->GetOuterObjects(OuterObjects); + + for (UObject* OuterObject : OuterObjects) + { + UFlowNode* FlowNodeOuter = Cast(OuterObject); + if (IsValid(FlowNodeOuter)) + { + return FlowNodeOuter; + } + + UFlowNodeAddOn* FlowNodeAddOnOuter = Cast(OuterObject); + if (IsValid(FlowNodeAddOnOuter)) + { + return FlowNodeAddOnOuter->GetFlowNode(); + } + } + + return nullptr; +} diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowNodeAddOn_Details.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowNodeAddOn_Details.cpp new file mode 100644 index 000000000..673a7ac2f --- /dev/null +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowNodeAddOn_Details.cpp @@ -0,0 +1,14 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "DetailCustomizations/FlowNodeAddOn_Details.h" +#include "PropertyEditing.h" + +void FFlowNodeAddOn_Details::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) +{ + // hide class properties while editing node addon instance placed in the graph + if (DetailLayout.HasClassDefaultObject() == false) + { + DetailLayout.HideCategory(TEXT("FlowNode")); + DetailLayout.HideCategory(TEXT("FlowNodeAddOn")); + } +} diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowOwnerFunctionRefCustomization.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowOwnerFunctionRefCustomization.cpp index c8e1379cd..69cf4798c 100644 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowOwnerFunctionRefCustomization.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowOwnerFunctionRefCustomization.cpp @@ -2,10 +2,13 @@ #include "DetailCustomizations/FlowOwnerFunctionRefCustomization.h" +#include "FlowAsset.h" +#include "Interfaces/FlowOwnerInterface.h" #include "Nodes/FlowNode.h" #include "Nodes/World/FlowNode_CallOwnerFunction.h" #include "UObject/UnrealType.h" +#include "Types/FlowOwnerFunctionParams.h" void FFlowOwnerFunctionRefCustomization::CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) { @@ -46,14 +49,19 @@ TArray FFlowOwnerFunctionRefCustomization::GetCuratedNameOptions() const const UClass* FFlowOwnerFunctionRefCustomization::TryGetExpectedOwnerClass() const { const UFlowNode* NodeOwner = TryGetFlowNodeOuter(); - const UFlowNode_CallOwnerFunction* CallOwnerFunctionNode = Cast(NodeOwner); + if (!IsValid(NodeOwner)) + { + return nullptr; + } - if (IsValid(CallOwnerFunctionNode)) + const UFlowAsset* FlowAsset = NodeOwner->GetFlowAsset(); + if (!IsValid(FlowAsset)) { - return CallOwnerFunctionNode->TryGetExpectedOwnerClass(); + return nullptr; } - return nullptr; + UClass* ExpectedOwnerClass = FlowAsset->GetExpectedOwnerClass(); + return ExpectedOwnerClass; } TArray FFlowOwnerFunctionRefCustomization::GetFlowOwnerFunctionRefs( @@ -118,16 +126,18 @@ void FFlowOwnerFunctionRefCustomization::SetCuratedName(const FName& NewFunction FunctionNameHandle->SetPerObjectValue(0, NewFunctionName.ToString()); } -FName FFlowOwnerFunctionRefCustomization::GetCuratedName() const +bool FFlowOwnerFunctionRefCustomization::TryGetCuratedName(FName& OutName) const { const FFlowOwnerFunctionRef* FlowOwnerFunction = GetFlowOwnerFunctionRef(); if (FlowOwnerFunction) { - return FlowOwnerFunction->FunctionName; + OutName = FlowOwnerFunction->FunctionName; + + return true; } else { - return NAME_None; + return false; } } diff --git a/Source/FlowEditor/Private/FlowEditorModule.cpp b/Source/FlowEditor/Private/FlowEditorModule.cpp index 21d9b3295..8a0dddcef 100644 --- a/Source/FlowEditor/Private/FlowEditorModule.cpp +++ b/Source/FlowEditor/Private/FlowEditorModule.cpp @@ -11,6 +11,7 @@ #include "Utils/SLevelEditorFlow.h" #include "MovieScene/FlowTrackEditor.h" #include "Nodes/AssetTypeActions_FlowNodeBlueprint.h" +#include "Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.h" #include "Pins/SFlowInputPinHandle.h" #include "Pins/SFlowOutputPinHandle.h" @@ -20,10 +21,13 @@ #include "DetailCustomizations/FlowNode_CustomInputDetails.h" #include "DetailCustomizations/FlowNode_CustomOutputDetails.h" #include "DetailCustomizations/FlowNode_PlayLevelSequenceDetails.h" -#include "DetailCustomizations/FlowOwnerFunctionRefCustomization.h" #include "DetailCustomizations/FlowNode_SubGraphDetails.h" +#include "DetailCustomizations/FlowNodeAddOn_Details.h" +#include "DetailCustomizations/FlowOwnerFunctionRefCustomization.h" +#include "DetailCustomizations/FlowActorOwnerComponentRefCustomization.h" #include "FlowAsset.h" +#include "AddOns/FlowNodeAddOn.h" #include "Nodes/Route/FlowNode_CustomInput.h" #include "Nodes/Route/FlowNode_CustomOutput.h" #include "Nodes/Route/FlowNode_SubGraph.h" @@ -132,6 +136,10 @@ void FFlowEditorModule::RegisterAssets() const TSharedRef FlowNodeActions = MakeShareable(new FAssetTypeActions_FlowNodeBlueprint()); RegisteredAssetActions.Add(FlowNodeActions); AssetTools.RegisterAssetTypeActions(FlowNodeActions); + + const TSharedRef FlowNodeAddOnActions = MakeShareable(new FAssetTypeActions_FlowNodeAddOnBlueprint()); + RegisteredAssetActions.Add(FlowNodeAddOnActions); + AssetTools.RegisterAssetTypeActions(FlowNodeAddOnActions); } void FFlowEditorModule::UnregisterAssets() @@ -179,12 +187,14 @@ void FFlowEditorModule::RegisterDetailCustomizations() RegisterCustomClassLayout(UFlowAsset::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowAssetDetails::MakeInstance)); RegisterCustomClassLayout(UFlowNode::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_Details::MakeInstance)); + RegisterCustomClassLayout(UFlowNodeAddOn::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNodeAddOn_Details::MakeInstance)); RegisterCustomClassLayout(UFlowNode_ComponentObserver::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_ComponentObserverDetails::MakeInstance)); RegisterCustomClassLayout(UFlowNode_CustomInput::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_CustomInputDetails::MakeInstance)); RegisterCustomClassLayout(UFlowNode_CustomOutput::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_CustomOutputDetails::MakeInstance)); RegisterCustomClassLayout(UFlowNode_PlayLevelSequence::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_PlayLevelSequenceDetails::MakeInstance)); - RegisterCustomClassLayout(UFlowNode_SubGraph::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_SubGraphDetails::MakeInstance)); + RegisterCustomClassLayout(UFlowNode_SubGraph::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_SubGraphDetails::MakeInstance)); RegisterCustomStructLayout(*FFlowOwnerFunctionRef::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowOwnerFunctionRefCustomization::MakeInstance)); + RegisterCustomStructLayout(*FFlowActorOwnerComponentRef::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowActorOwnerComponentRefCustomization::MakeInstance)); PropertyModule.NotifyCustomizationModuleChanged(); } diff --git a/Source/FlowEditor/Private/Graph/FlowGraph.cpp b/Source/FlowEditor/Private/Graph/FlowGraph.cpp index 465fb9c21..32b0ad5e5 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraph.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraph.cpp @@ -4,11 +4,14 @@ #include "Graph/FlowGraphSchema.h" #include "Graph/FlowGraphSchema_Actions.h" #include "Graph/Nodes/FlowGraphNode.h" - +#include "AddOns/FlowNodeAddOn.h" #include "Nodes/FlowNode.h" +#include "FlowEditorLogChannels.h" #include "Editor.h" #include "Kismet2/BlueprintEditorUtils.h" +#include "EdGraph/EdGraphPin.h" +#include "Logging/LogMacros.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraph) @@ -25,6 +28,8 @@ void FFlowGraphInterface::OnOutputTriggered(UEdGraphNode* GraphNode, const int32 UFlowGraph::UFlowGraph(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { + bLockUpdates = false; + if (!UFlowAsset::GetFlowGraphInterface().IsValid()) { UFlowAsset::SetFlowGraphInterface(MakeShared()); @@ -50,37 +55,60 @@ UEdGraph* UFlowGraph::CreateGraph(UFlowAsset* InFlowAsset, TSubclassOfPlayWorld) + if (!GEditor || GEditor->PlayWorld) { + // don't run fixup in PIE + return; + } + + // Locking updates to the graph while we update it + { + LockUpdates(); + // check if all Graph Nodes have expected, up-to-date type - CastChecked(GetSchema())->GatherNativeNodes(); - for (const TPair& Node : GetFlowAsset()->GetNodes()) + const UFlowGraphSchema* FlowGraphSchema = CastChecked(GetSchema()); + FlowGraphSchema->GatherNodes(); + + const TMap& FlowAssetNodesMap = GetFlowAsset()->GetNodes(); + for (const TPair& Node : FlowAssetNodesMap) { - if (UFlowNode* FlowNode = Node.Value) + UFlowNode* FlowNode = Node.Value; + if (!IsValid(FlowNode)) { - const UClass* ExpectGraphNodeClass = UFlowGraphSchema::GetAssignedGraphNodeClass(FlowNode->GetClass()); - if (FlowNode->GetGraphNode() && FlowNode->GetGraphNode()->GetClass() != ExpectGraphNodeClass) - { - // Create a new Flow Graph Node of proper type - FFlowGraphSchemaAction_NewNode::RecreateNode(this, FlowNode->GetGraphNode(), FlowNode); - } + continue; } - } - // refresh nodes - TArray FlowGraphNodes; - GetNodesOfClass(FlowGraphNodes); - for (UFlowGraphNode* GraphNode : FlowGraphNodes) - { - GraphNode->OnGraphRefresh(); + UFlowGraphNode* const ExistingFlowGraphNode = Cast(FlowNode->GetGraphNode()); + + UFlowGraphNode* RefreshedFlowGraphNode = ExistingFlowGraphNode; + + const TSubclassOf ExpectGraphNodeClass = UFlowGraphSchema::GetAssignedGraphNodeClass(FlowNode->GetClass()); + UClass* ExistingFlowGraphNodeClass = IsValid(ExistingFlowGraphNode) ? ExistingFlowGraphNode->GetClass() : nullptr; + if (ExistingFlowGraphNodeClass != ExpectGraphNodeClass) + { + // Create a new Flow Graph Node of proper type + RefreshedFlowGraphNode = FFlowGraphSchemaAction_NewNode::RecreateNode(this, ExistingFlowGraphNode, FlowNode); + } + + RecursivelyRefreshAddOns(*RefreshedFlowGraphNode); } + + UnlockUpdates(); + } + + // refresh nodes + TArray FlowGraphNodes; + GetNodesOfClass(FlowGraphNodes); + for (UFlowGraphNode* GraphNode : FlowGraphNodes) + { + GraphNode->OnGraphRefresh(); } } void UFlowGraph::NotifyGraphChanged() { GetFlowAsset()->HarvestNodeConnections(); + GetFlowAsset()->MarkPackageDirty(); Super::NotifyGraphChanged(); } @@ -89,3 +117,361 @@ UFlowAsset* UFlowGraph::GetFlowAsset() const { return GetTypedOuter(); } + +void UFlowGraph::RecursivelyRefreshAddOns(UFlowGraphNode& FromFlowGraphNode) +{ + // Refresh AddOns + UFlowNodeBase* FromFlowNodeBase = FromFlowGraphNode.GetFlowNodeBase(); + + const TArray FlowNodeAddOnChildren = FromFlowNodeBase->GetFlowNodeAddOnChildren(); + for (UFlowNodeAddOn* AddOn : FlowNodeAddOnChildren) + { + if (!AddOn) + { + UE_LOG( + LogFlowEditor, + Error, + TEXT("Missing AddOn detected for node %s (parent %s)"), + *FromFlowNodeBase->GetName(), + FromFlowGraphNode.GetParentNode() ? + *FromFlowGraphNode.GetParentNode()->GetName() : + TEXT("")); + + continue; + } + + UFlowGraphNode* const AddOnFlowGraphNode = Cast(AddOn->GetGraphNode()); + + const TSubclassOf ExpectAddOnGraphNodeClass = UFlowGraphSchema::GetAssignedGraphNodeClass(AddOn->GetClass()); + UFlowGraphNode* RefreshedAddOnFlowGraphNode = AddOnFlowGraphNode; + UClass* ExistingAddOnGraphNodeClass = IsValid(AddOnFlowGraphNode) ? AddOnFlowGraphNode->GetClass() : nullptr; + + if (ExistingAddOnGraphNodeClass != ExpectAddOnGraphNodeClass) + { + // Create a new Flow Graph Node of proper type for the AddOn + RefreshedAddOnFlowGraphNode = FFlowSchemaAction_NewSubNode::RecreateNode(this, AddOnFlowGraphNode, &FromFlowGraphNode, AddOn); + } + + // Recurse for the AddOn's AddOn's + RecursivelyRefreshAddOns(*RefreshedAddOnFlowGraphNode); + } +} + +void UFlowGraph::RecursivelySetupAllFlowGraphNodesForEditing(UFlowGraphNode& FromFlowGraphNode) +{ + UFlowNodeBase* FromNodeInstance = FromFlowGraphNode.GetFlowNodeBase(); + if (IsValid(FromNodeInstance)) + { + // Setup all of the flow node (and subnode) instances for editing + FromNodeInstance->SetupForEditing(FromFlowGraphNode); + } + + for (UFlowGraphNode* SubNode : FromFlowGraphNode.SubNodes) + { + // Setup all of the flow subnodes for editing + if (IsValid(SubNode)) + { + SubNode->SetParentNodeForSubNode(&FromFlowGraphNode); + + RecursivelySetupAllFlowGraphNodesForEditing(*SubNode); + } + } +} + +void UFlowGraph::UpdateAsset(int32 UpdateFlags) +{ + if (IsLocked()) + { + return; + } + + // UpdateAsset is called to do any reconciliation from the editor-version of the + // graph to the runtime version of the graph data. + // In our case, it will copy the AddOns from their editor-side UFlowGraphNode containers to + // their runtime UFlowNode and/or UFlowNodeAddOn ::AddOn array entry (via OnUpdateAsset) + for (UEdGraphNode* EdNode : Nodes) + { + UFlowGraphNode* FlowGraphNode = Cast(EdNode); + if (FlowGraphNode) + { + FlowGraphNode->OnUpdateAsset(UpdateFlags); + } + } +} + +void UFlowGraph::OnCreated() +{ + MarkVersion(); +} + +void UFlowGraph::OnLoaded() +{ + check(GEditor); + + // Setup all of the Nodes in the graph for editing + for (UEdGraphNode* EdNode : Nodes) + { + UFlowGraphNode* FlowGraphNode = Cast(EdNode); + if (IsValid(FlowGraphNode)) + { + RecursivelySetupAllFlowGraphNodesForEditing(*FlowGraphNode); + } + } + + UpdateDeprecatedClasses(); + + if (UpdateUnknownNodeClasses()) + { + NotifyGraphChanged(); + } + + RefreshGraph(); +} + +void UFlowGraph::OnSave() +{ + UpdateAsset(); +} + +void UFlowGraph::Initialize() +{ + UpdateVersion(); +} + +void UFlowGraph::UpdateVersion() +{ + if (GraphVersion == 1) + { + return; + } + + MarkVersion(); + Modify(); + + // Insert any Version updating code here +} + +void UFlowGraph::MarkVersion() +{ + GraphVersion = 1; +} + +bool UFlowGraph::UpdateUnknownNodeClasses() +{ + bool bUpdated = false; + for (int32 NodeIdx = 0; NodeIdx < Nodes.Num(); NodeIdx++) + { + UFlowGraphNode* MyNode = Cast(Nodes[NodeIdx]); + if (MyNode) + { + const bool bUpdatedNode = MyNode->RefreshNodeClass(); + bUpdated = bUpdated || bUpdatedNode; + + for (int32 SubNodeIdx = 0; SubNodeIdx < MyNode->SubNodes.Num(); SubNodeIdx++) + { + if (MyNode->SubNodes[SubNodeIdx]) + { + const bool bUpdatedSubNode = MyNode->SubNodes[SubNodeIdx]->RefreshNodeClass(); + bUpdated = bUpdated || bUpdatedSubNode; + } + } + } + } + + return bUpdated; +} + +FString UFlowGraph::GetDeprecationMessage(const UClass* Class) +{ + static FName MetaDeprecated = TEXT("DeprecatedNode"); + static FName MetaDeprecatedMessage = TEXT("DeprecationMessage"); + FString DefDeprecatedMessage("Please remove it!"); + FString DeprecatedPrefix("DEPRECATED"); + FString DeprecatedMessage; + + if (Class && Class->HasAnyClassFlags(CLASS_Native) && Class->HasMetaData(MetaDeprecated)) + { + DeprecatedMessage = DeprecatedPrefix + TEXT(": "); + DeprecatedMessage += Class->HasMetaData(MetaDeprecatedMessage) ? Class->GetMetaData(MetaDeprecatedMessage) : DefDeprecatedMessage; + } + + return DeprecatedMessage; +} + +void UFlowGraph::UpdateFlowGraphNodeErrorMessage(UFlowGraphNode& Node) +{ + // Broke out setting error message in to own function so it can be reused when iterating nodes collection. + if (Node.GetFlowNodeBase()) + { + Node.ErrorMessage = GetDeprecationMessage(Node.GetFlowNodeBase()->GetClass()); + } + else + { + // Null instance. Do we have any meaningful class data? + FString StoredClassName = Node.NodeInstanceClass.GetAssetName(); + StoredClassName.RemoveFromEnd(TEXT("_C")); + + if (!StoredClassName.IsEmpty()) + { + // There is class data here but the instance was not be created. + static const FString IsMissingClassMessage(" class missing. Referenced by "); + Node.ErrorMessage = StoredClassName + IsMissingClassMessage + Node.GetFullName(); + } + } + + if (Node.HasErrors()) + { + UE_LOG(LogFlowEditor, Error, TEXT("%s"), *Node.ErrorMessage); + } +} + +void UFlowGraph::UpdateDeprecatedClasses() +{ + // This function sets error messages and logs errors about nodes. + + for (int32 Idx = 0, IdxNum = Nodes.Num(); Idx < IdxNum; ++Idx) + { + UFlowGraphNode* Node = Cast(Nodes[Idx]); + if (Node != nullptr) + { + UpdateFlowGraphNodeErrorMessage(*Node); + + for (int32 SubIdx = 0, SubIdxNum = Node->SubNodes.Num(); SubIdx < SubIdxNum; ++SubIdx) + { + if (Node->SubNodes[SubIdx] != nullptr) + { + UpdateFlowGraphNodeErrorMessage(*Node->SubNodes[SubIdx]); + } + } + } + } +} + +void UFlowGraph::Serialize(FArchive& Ar) +{ + // Overridden to flags up errors in the behavior tree while cooking. + Super::Serialize(Ar); + + if (Ar.IsSaving() || Ar.IsCooking()) + { + // Logging of errors happens in UpdateDeprecatedClasses + UpdateDeprecatedClasses(); + } +} + +void UFlowGraph::UpdateClassData() +{ + for (int32 Idx = 0; Idx < Nodes.Num(); Idx++) + { + UFlowGraphNode* Node = Cast(Nodes[Idx]); + if (Node) + { + Node->UpdateNodeClassData(); + + for (int32 SubIdx = 0; SubIdx < Node->SubNodes.Num(); SubIdx++) + { + if (UFlowGraphNode* SubNode = Node->SubNodes[SubIdx]) + { + SubNode->UpdateNodeClassData(); + } + } + } + } +} + +void UFlowGraph::CollectAllNodeInstances(TSet& NodeInstances) +{ + for (int32 Idx = 0; Idx < Nodes.Num(); Idx++) + { + UFlowGraphNode* MyNode = Cast(Nodes[Idx]); + if (MyNode) + { + NodeInstances.Add(MyNode->GetFlowNodeBase()); + + for (int32 SubIdx = 0; SubIdx < MyNode->SubNodes.Num(); SubIdx++) + { + if (MyNode->SubNodes[SubIdx]) + { + NodeInstances.Add(MyNode->SubNodes[SubIdx]->GetFlowNodeBase()); + } + } + } + } +} + +bool UFlowGraph::CanRemoveNestedObject(UObject* TestObject) const +{ + return !TestObject->IsA(UEdGraphNode::StaticClass()) && + !TestObject->IsA(UEdGraph::StaticClass()) && + !TestObject->IsA(UEdGraphSchema::StaticClass()); +} + +void UFlowGraph::RemoveOrphanedNodes() +{ + TSet NodeInstances; + CollectAllNodeInstances(NodeInstances); + + NodeInstances.Remove(nullptr); + + // Obtain a list of all nodes actually in the asset and discard unused nodes + TArray AllInners; + const bool bIncludeNestedObjects = false; + GetObjectsWithOuter(GetOuter(), AllInners, bIncludeNestedObjects); + for (auto InnerIt = AllInners.CreateConstIterator(); InnerIt; ++InnerIt) + { + UObject* TestObject = *InnerIt; + if (!NodeInstances.Contains(TestObject) && CanRemoveNestedObject(TestObject)) + { + OnNodeInstanceRemoved(TestObject); + + TestObject->SetFlags(RF_Transient); + TestObject->Rename(NULL, GetTransientPackage(), REN_DontCreateRedirectors | REN_NonTransactional | REN_ForceNoResetLoaders); + } + } +} + +void UFlowGraph::OnNodeInstanceRemoved(UObject* NodeInstance) +{ + // empty in base class +} + +void UFlowGraph::OnNodesPasted(const FString& ImportStr) +{ + // empty in base class +} + +UEdGraphPin* UFlowGraph::FindGraphNodePin(UEdGraphNode* Node, EEdGraphPinDirection Dir) +{ + UEdGraphPin* Pin = nullptr; + for (int32 Idx = 0; Idx < Node->Pins.Num(); Idx++) + { + if (Node->Pins[Idx]->Direction == Dir) + { + Pin = Node->Pins[Idx]; + break; + } + } + + return Pin; +} + +bool UFlowGraph::IsLocked() const +{ + return bLockUpdates; +} + +void UFlowGraph::LockUpdates() +{ + bLockUpdates = true; +} + +void UFlowGraph::UnlockUpdates() +{ + bLockUpdates = false; + UpdateAsset(); +} + +void UFlowGraph::OnSubNodeDropped() +{ + NotifyGraphChanged(); +} diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index 24b7e73bb..467a42acf 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -3,24 +3,21 @@ #include "Graph/FlowGraphEditor.h" #include "FlowEditorCommands.h" +#include "FlowEditorModule.h" #include "Asset/FlowAssetEditor.h" #include "Asset/FlowDebuggerSubsystem.h" #include "Graph/FlowGraphEditorSettings.h" #include "Graph/FlowGraphSchema_Actions.h" #include "Graph/Nodes/FlowGraphNode.h" - #include "Nodes/Route/FlowNode_SubGraph.h" -#include "EdGraphUtilities.h" #include "Framework/Commands/GenericCommands.h" -#include "GraphEditorActions.h" #include "HAL/PlatformApplicationMisc.h" #include "IDetailsView.h" #include "LevelEditor.h" #include "Modules/ModuleManager.h" #include "ScopedTransaction.h" -#include "SNodePanel.h" #include "Widgets/Docking/SDockTab.h" #define LOCTEXT_NAMESPACE "FlowGraphEditor" @@ -340,7 +337,7 @@ void SFlowGraphEditor::OnSelectedNodesChanged(const TSet& Nodes) { if (const UFlowGraphNode* GraphNode = Cast(*SetIt)) { - SelectedObjects.Add(Cast(GraphNode->GetFlowNode())); + SelectedObjects.Add(Cast(GraphNode->GetFlowNodeBase())); } else { @@ -464,15 +461,9 @@ void SFlowGraphEditor::DeleteSelectedNodes() { if (const UFlowGraphNode* FlowGraphNode = Cast(Node)) { - if (FlowGraphNode->GetFlowNode()) + if (UFlowNode* FlowNode = Cast(FlowGraphNode->GetFlowNodeBase())) { - const FGuid NodeGuid = FlowGraphNode->GetFlowNode()->GetGuid(); - - // If the user is pressing shift then try and reconnect the pins - if (FSlateApplication::Get().GetModifierKeys().IsShiftDown()) - { - ReconnectExecPins(FlowGraphNode); - } + const FGuid NodeGuid = FlowNode->GetGuid(); GetCurrentGraph()->GetSchema()->BreakNodeLinks(*Node); Node->DestroyNode(); @@ -561,25 +552,53 @@ bool SFlowGraphEditor::CanCutNodes() const void SFlowGraphEditor::CopySelectedNodes() const { - const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes(); - for (FGraphPanelSelectionSet::TConstIterator SelectedIt(SelectedNodes); SelectedIt; ++SelectedIt) + // Export the selected nodes and place the text on the clipboard + FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes(); + FGraphPanelSelectionSet NewSelectedNodes; + + for (FGraphPanelSelectionSet::TIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter) { - if (UFlowGraphNode* Node = Cast(*SelectedIt)) + if (UFlowGraphNode* FlowGraphNode = Cast(*SelectedIter)) { - Node->PrepareForCopying(); + constexpr int32 RootEdNodeParentIndex = INDEX_NONE; + PrepareFlowGraphNodeForCopy(*FlowGraphNode, RootEdNodeParentIndex, NewSelectedNodes); } } - // Export the selected nodes and place the text on the clipboard FString ExportedText; - FEdGraphUtilities::ExportNodesToText(SelectedNodes, /*out*/ ExportedText); + + FEdGraphUtilities::ExportNodesToText(NewSelectedNodes, ExportedText); FPlatformApplicationMisc::ClipboardCopy(*ExportedText); - for (FGraphPanelSelectionSet::TConstIterator SelectedIt(SelectedNodes); SelectedIt; ++SelectedIt) + for (FGraphPanelSelectionSet::TIterator SelectedIter(NewSelectedNodes); SelectedIter; ++SelectedIter) + { + UFlowGraphNode* FlowGraphNode = Cast(*SelectedIter); + if (FlowGraphNode) + { + FlowGraphNode->PostCopyNode(); + } + } +} + +void SFlowGraphEditor::PrepareFlowGraphNodeForCopy(UFlowGraphNode& FlowGraphNode, int32 ParentEdNodeIndex, FGraphPanelSelectionSet& NewSelectedNodes) const +{ + FlowGraphNode.PrepareForCopying(); + + FlowGraphNode.CopySubNodeParentIndex = ParentEdNodeIndex; + + const int32 ThisFlowGraphNodeIndex = NewSelectedNodes.Num(); + FlowGraphNode.CopySubNodeIndex = ThisFlowGraphNodeIndex; + NewSelectedNodes.Add(&FlowGraphNode); + + // append all subnodes for selection + const TArray& FlowGraphNodeSubNodes = FlowGraphNode.SubNodes; + + for (int32 Idx = 0; Idx < FlowGraphNodeSubNodes.Num(); ++Idx) { - if (UFlowGraphNode* Node = Cast(*SelectedIt)) + UFlowGraphNode* SubNodeCur = FlowGraphNodeSubNodes[Idx]; + if (SubNodeCur) { - Node->PostCopyNode(); + PrepareFlowGraphNodeForCopy(*SubNodeCur, ThisFlowGraphNodeIndex, NewSelectedNodes); } } } @@ -609,15 +628,40 @@ void SFlowGraphEditor::PasteNodes() void SFlowGraphEditor::PasteNodesHere(const FVector2D& Location) { - FlowAssetEditor.Pin()->SetUISelectionState(NAME_None); - // Undo/Redo support const FScopedTransaction Transaction(LOCTEXT("PasteNode", "Paste Node")); - FlowAsset->GetGraph()->Modify(); + UFlowGraph* FlowGraph = CastChecked(FlowAsset->GetGraph()); + FlowGraph->Modify(); FlowAsset->Modify(); + FlowGraph->LockUpdates(); + + UFlowGraphNode* SelectedParent = nullptr; + bool bHasMultipleNodesSelected = false; + + const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes(); + for (FGraphPanelSelectionSet::TConstIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter) + { + UFlowGraphNode* Node = Cast(*SelectedIter); + + if (Node) + { + if (SelectedParent == nullptr) + { + SelectedParent = Node; + } + else + { + bHasMultipleNodesSelected = true; + + break; + } + } + } + // Clear the selection set (newly pasted stuff will be selected) ClearSelectionSet(); + FlowAssetEditor.Pin()->SetUISelectionState(NAME_None); // Grab the text to paste from the clipboard. FString TextToImport; @@ -625,67 +669,138 @@ void SFlowGraphEditor::PasteNodesHere(const FVector2D& Location) // Import the nodes TSet PastedNodes; - FEdGraphUtilities::ImportNodesFromText(FlowAsset->GetGraph(), TextToImport, /*out*/ PastedNodes); + FEdGraphUtilities::ImportNodesFromText(FlowGraph, TextToImport, /*out*/ PastedNodes); //Average position of nodes so we can move them while still maintaining relative distances to each other FVector2D AvgNodePosition(0.0f, 0.0f); + // Number of nodes used to calculate AvgNodePosition + int32 AvgCount = 0; + for (TSet::TIterator It(PastedNodes); It; ++It) { - const UEdGraphNode* Node = *It; - AvgNodePosition.X += Node->NodePosX; - AvgNodePosition.Y += Node->NodePosY; + UEdGraphNode* EdNode = *It; + UFlowGraphNode* FlowGraphNode = Cast(EdNode); + if (EdNode && (FlowGraphNode == nullptr || !FlowGraphNode->IsSubNode())) + { + AvgNodePosition.X += EdNode->NodePosX; + AvgNodePosition.Y += EdNode->NodePosY; + ++AvgCount; + } } - if (PastedNodes.Num() > 0) + if (AvgCount > 0) { - const float InvNumNodes = 1.0f / static_cast(PastedNodes.Num()); + float InvNumNodes = 1.0f / float(AvgCount); AvgNodePosition.X *= InvNumNodes; AvgNodePosition.Y *= InvNumNodes; } + bool bPastedParentNode = false; + + TMap EdNodeCopyIndexMap; for (TSet::TIterator It(PastedNodes); It; ++It) { - UEdGraphNode* Node = *It; + UEdGraphNode* PasteNode = *It; + UFlowGraphNode* PasteFlowGraphNode = Cast(PasteNode); + + EdNodeCopyIndexMap.Add(PasteFlowGraphNode->CopySubNodeIndex, PasteFlowGraphNode); + + if (PasteNode && (PasteFlowGraphNode == nullptr || !PasteFlowGraphNode->IsSubNode())) + { + bPastedParentNode = true; + + // Select the newly pasted stuff + constexpr bool bSelectNodes = true; + SetNodeSelection(PasteNode, bSelectNodes); + + PasteNode->NodePosX = (PasteNode->NodePosX - AvgNodePosition.X) + Location.X; + PasteNode->NodePosY = (PasteNode->NodePosY - AvgNodePosition.Y) + Location.Y; + + PasteNode->SnapToGrid(16); + + // Give new node a different Guid from the old one + PasteNode->CreateNewGuid(); - // Give new node a different Guid from the old one - Node->CreateNewGuid(); + if (UFlowNode* FlowNode = Cast(PasteFlowGraphNode->GetFlowNodeBase())) + { + // Only full FlowNodes are registered with the Asset + // (for now? perhaps we register AddOns in the future?) + FlowAsset->RegisterNode(PasteNode->NodeGuid, FlowNode); + } + } - if (const UFlowGraphNode* FlowGraphNode = Cast(Node)) + if (PasteFlowGraphNode) { - FlowAsset->RegisterNode(Node->NodeGuid, FlowGraphNode->GetFlowNode()); + PasteFlowGraphNode->RemoveAllSubNodes(); } + } - // Select the newly pasted stuff - SetNodeSelection(Node, true); + for (TSet::TIterator It(PastedNodes); It; ++It) + { + UFlowGraphNode* PasteNode = Cast(*It); + if (PasteNode && PasteNode->IsSubNode()) + { + PasteNode->NodePosX = 0; + PasteNode->NodePosY = 0; - Node->NodePosX = (Node->NodePosX - AvgNodePosition.X) + Location.X; - Node->NodePosY = (Node->NodePosY - AvgNodePosition.Y) + Location.Y; + // remove subnode from graph, it will be referenced from parent node + PasteNode->DestroyNode(); - Node->SnapToGrid(SNodePanel::GetSnapGridSize()); + if (PasteNode->CopySubNodeParentIndex == INDEX_NONE) + { + // INDEX_NONE parent index indicates we should set the parent to the SelectedParent + + if (SelectedParent) + { + SelectedParent->AddSubNode(PasteNode, FlowGraph); + } + } + else if (UFlowGraphNode* PastedParentNode = EdNodeCopyIndexMap.FindRef(PasteNode->CopySubNodeParentIndex)) + { + PastedParentNode->AddSubNode(PasteNode, FlowGraph); + } + } } - // Force new pasted FlowNodes to have same connections as graph nodes - FlowAsset->HarvestNodeConnections(); + if (FlowGraph) + { + FlowGraph->UpdateClassData(); + FlowGraph->OnNodesPasted(TextToImport); + FlowGraph->UnlockUpdates(); + } // Update UI NotifyGraphChanged(); - FlowAsset->PostEditChange(); - FlowAsset->MarkPackageDirty(); + UObject* GraphOwner = FlowGraph->GetOuter(); + if (GraphOwner) + { + GraphOwner->PostEditChange(); + GraphOwner->MarkPackageDirty(); + } } bool SFlowGraphEditor::CanPasteNodes() const { - if (CanEdit() && IsTabFocused()) + if (!CanEdit() || !IsTabFocused()) { - FString ClipboardContent; - FPlatformApplicationMisc::ClipboardPaste(ClipboardContent); + return false; + } + + FString ClipboardContent; + FPlatformApplicationMisc::ClipboardPaste(ClipboardContent); + + const bool bIsPastePossible = FEdGraphUtilities::CanImportNodesFromText(FlowAsset->GetGraph(), ClipboardContent); - return FEdGraphUtilities::CanImportNodesFromText(FlowAsset->GetGraph(), ClipboardContent); + if (!bIsPastePossible) + { + return false; } - return false; + // TODO (gtaylor) Need to confirm the nodes are allowed to be pasted on the selected node(s) + + return true; } void SFlowGraphEditor::DuplicateNodes() @@ -701,9 +816,10 @@ bool SFlowGraphEditor::CanDuplicateNodes() const void SFlowGraphEditor::OnNodeDoubleClicked(class UEdGraphNode* Node) const { - UFlowNode* FlowNode = Cast(Node)->GetFlowNode(); + UFlowNodeBase* FlowNodeBase = Cast(Node)->GetFlowNodeBase(); + UFlowNode* FlowNode = Cast(FlowNodeBase); - if (FlowNode) + if (IsValid(FlowNodeBase)) { if (UFlowGraphEditorSettings::Get()->NodeDoubleClickTarget == EFlowNodeDoubleClickTarget::NodeDefinition) { @@ -711,12 +827,20 @@ void SFlowGraphEditor::OnNodeDoubleClicked(class UEdGraphNode* Node) const } else { - const FString AssetPath = FlowNode->GetAssetPath(); + FString AssetPath; + UObject* AssetToEdit = nullptr; + + if (FlowNode) + { + AssetPath = FlowNode->GetAssetPath(); + AssetToEdit = FlowNode->GetAssetToEdit(); + } + if (!AssetPath.IsEmpty()) { GEditor->GetEditorSubsystem()->OpenEditorForAsset(AssetPath); } - else if (UObject* AssetToEdit = FlowNode->GetAssetToEdit()) + else if (AssetToEdit) { GEditor->GetEditorSubsystem()->OpenEditorForAsset(AssetToEdit); @@ -1094,10 +1218,10 @@ void SFlowGraphEditor::FocusViewport() const // Iterator used but should only contain one node for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - const UFlowNode* FlowNode = Cast(SelectedNode)->GetFlowNode(); - if (UFlowNode* NodeInstance = FlowNode->GetInspectedInstance()) + const UFlowNode* FlowNode = Cast(SelectedNode->GetFlowNodeBase()); + if (UFlowNode* InspectedInstance = FlowNode->GetInspectedInstance()) { - if (AActor* ActorToFocus = NodeInstance->GetActorToFocus()) + if (AActor* ActorToFocus = InspectedInstance->GetActorToFocus()) { GEditor->SelectNone(false, false, false); GEditor->SelectActor(ActorToFocus, true, true, true); diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index 60f5097a9..616154d44 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -11,6 +11,8 @@ #include "Graph/Nodes/FlowGraphNode.h" #include "FlowAsset.h" +#include "FlowEditorModule.h" +#include "AddOns/FlowNodeAddOn.h" #include "Nodes/FlowNode.h" #include "Nodes/FlowNodeBlueprint.h" #include "Nodes/Route/FlowNode_CustomInput.h" @@ -21,6 +23,7 @@ #include "EdGraph/EdGraph.h" #include "Editor.h" #include "ScopedTransaction.h" +#include "Nodes/FlowNodeAddOnBlueprint.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraphSchema) @@ -28,8 +31,10 @@ bool UFlowGraphSchema::bInitialGatherPerformed = false; TArray UFlowGraphSchema::NativeFlowNodes; +TArray UFlowGraphSchema::NativeFlowNodeAddOns; TMap UFlowGraphSchema::BlueprintFlowNodes; -TMap UFlowGraphSchema::GraphNodesByFlowNodes; +TMap UFlowGraphSchema::BlueprintFlowNodeAddOns; +TMap, TSubclassOf> UFlowGraphSchema::GraphNodesByFlowNodes; bool UFlowGraphSchema::bBlueprintCompilationPending; @@ -64,7 +69,7 @@ void UFlowGraphSchema::GetPaletteActions(FGraphActionMenuBuilder& ActionMenuBuil void UFlowGraphSchema::GetGraphContextActions(FGraphContextMenuBuilder& ContextMenuBuilder) const { - GetFlowNodeActions(ContextMenuBuilder, GetAssetClassDefaults(ContextMenuBuilder.CurrentGraph), FString()); + GetFlowNodeActions(ContextMenuBuilder, GetEditedAssetOrClassDefault(ContextMenuBuilder.CurrentGraph), FString()); GetCommentAction(ContextMenuBuilder, ContextMenuBuilder.CurrentGraph); if (!ContextMenuBuilder.FromPin && FFlowGraphUtils::GetFlowGraphEditor(ContextMenuBuilder.CurrentGraph)->CanPasteNodes()) @@ -76,7 +81,7 @@ void UFlowGraphSchema::GetGraphContextActions(FGraphContextMenuBuilder& ContextM void UFlowGraphSchema::CreateDefaultNodesForGraph(UEdGraph& Graph) const { - const UFlowAsset* AssetClassDefaults = GetAssetClassDefaults(&Graph); + const UFlowAsset* AssetClassDefaults = GetEditedAssetOrClassDefault(&Graph); static const FVector2D NodeOffsetIncrement = FVector2D(0, 128); FVector2D NodeOffset = FVector2D::ZeroVector; @@ -91,7 +96,7 @@ void UFlowGraphSchema::CreateDefaultNodesForGraph(UEdGraph& Graph) const NodeOffset += NodeOffsetIncrement; const UFlowGraphNode* NewFlowGraphNode = CreateDefaultNode(Graph, AssetClassDefaults, UFlowNode_CustomInput::StaticClass(), NodeOffset, true); - UFlowNode_CustomInput* CustomInputNode = CastChecked(NewFlowGraphNode->GetFlowNode()); + UFlowNode_CustomInput* CustomInputNode = CastChecked(NewFlowGraphNode->GetFlowNodeBase()); CustomInputNode->SetEventName(CustomInputName); } } @@ -152,6 +157,48 @@ const FPinConnectionResponse UFlowGraphSchema::CanCreateConnection(const UEdGrap return FPinConnectionResponse(CONNECT_RESPONSE_MAKE, TEXT("")); } +bool UFlowGraphSchema::IsPIESimulating() +{ + return GEditor->bIsSimulatingInEditor || (GEditor->PlayWorld != nullptr); +} + +const FPinConnectionResponse UFlowGraphSchema::CanMergeNodes(const UEdGraphNode* NodeA, const UEdGraphNode* NodeB) const +{ + if (IsPIESimulating()) + { + return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, TEXT("The Play-in-Editor is simulating")); + } + + // Make sure the nodes are not the same + if (NodeA == NodeB) + { + return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, TEXT("Both are the same node")); + } + + const UFlowGraphNode* FlowGraphNodeA = Cast(NodeA); + const UFlowGraphNode* FlowGraphNodeB = Cast(NodeB); + + const bool bNodeAIsSubnode = FlowGraphNodeA && FlowGraphNodeA->IsSubNode(); + const bool bNodeBIsSubnode = FlowGraphNodeB && FlowGraphNodeB->IsSubNode(); + + FString ReasonString; + if (FlowGraphNodeA && FlowGraphNodeB) + { + if (!FlowGraphNodeB->CanAcceptSubNodeAsChild(*FlowGraphNodeA, &ReasonString)) + { + return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, ReasonString); + } + else + { + return FPinConnectionResponse(CONNECT_RESPONSE_MAKE, ReasonString); + } + } + else + { + return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, TEXT("Incompatible graph node types")); + } +} + bool UFlowGraphSchema::TryCreateConnection(UEdGraphPin* PinA, UEdGraphPin* PinB) const { const bool bModified = UEdGraphSchema::TryCreateConnection(PinA, PinB); @@ -265,7 +312,7 @@ TArray> UFlowGraphSchema::GetFlowNodeCategories() } TSet UnsortedCategories; - for (const UClass* FlowNodeClass : NativeFlowNodes) + for (const TSubclassOf FlowNodeClass : NativeFlowNodes) { if (const UFlowNode* DefaultObject = FlowNodeClass->GetDefaultObject()) { @@ -273,9 +320,25 @@ TArray> UFlowGraphSchema::GetFlowNodeCategories() } } + for (const TSubclassOf FlowNodeAddOnClass : NativeFlowNodeAddOns) + { + if (const UFlowNodeAddOn* DefaultObject = FlowNodeAddOnClass->GetDefaultObject()) + { + UnsortedCategories.Emplace(DefaultObject->GetNodeCategory()); + } + } + for (const TPair& AssetData : BlueprintFlowNodes) { - if (const UBlueprint* Blueprint = GetPlaceableNodeBlueprint(AssetData.Value)) + if (const UBlueprint* Blueprint = GetPlaceableNodeOrAddOnBlueprint(AssetData.Value)) + { + UnsortedCategories.Emplace(Blueprint->BlueprintCategory); + } + } + + for (const TPair& AssetData : BlueprintFlowNodeAddOns) + { + if (const UBlueprint* Blueprint = GetPlaceableNodeOrAddOnBlueprint(AssetData.Value)) { UnsortedCategories.Emplace(Blueprint->BlueprintCategory); } @@ -297,13 +360,13 @@ TArray> UFlowGraphSchema::GetFlowNodeCategories() return Result; } -UClass* UFlowGraphSchema::GetAssignedGraphNodeClass(const UClass* FlowNodeClass) +TSubclassOf UFlowGraphSchema::GetAssignedGraphNodeClass(TSubclassOf FlowNodeClass) { - TArray FoundParentClasses; + TArray> FoundParentClasses; UClass* ReturnClass = nullptr; // Collect all possible parents and their corresponding GraphNodeClasses - for (const TPair& GraphNodeByFlowNode : GraphNodesByFlowNodes) + for (const TPair, TSubclassOf>& GraphNodeByFlowNode : GraphNodesByFlowNodes) { if (FlowNodeClass == GraphNodeByFlowNode.Key) { @@ -348,7 +411,7 @@ UClass* UFlowGraphSchema::GetAssignedGraphNodeClass(const UClass* FlowNodeClass) return IsValid(ReturnClass) ? ReturnClass : UFlowGraphNode::StaticClass(); } -void UFlowGraphSchema::ApplyNodeFilter(const UFlowAsset* EditedFlowAsset, const UClass* FlowNodeClass, TArray& FilteredNodes) +void UFlowGraphSchema::ApplyNodeOrAddOnFilter(const UFlowAsset* EditedFlowAsset, const UClass* FlowNodeClass, TArray& FilteredNodes) { if (FlowNodeClass == nullptr) { @@ -360,50 +423,136 @@ void UFlowGraphSchema::ApplyNodeFilter(const UFlowAsset* EditedFlowAsset, const return; } - if (!EditedFlowAsset->IsNodeClassAllowed(FlowNodeClass)) + if (!EditedFlowAsset->IsNodeOrAddOnClassAllowed(FlowNodeClass)) { return; } - UFlowNode* NodeDefaults = FlowNodeClass->GetDefaultObject(); + UFlowNodeBase* NodeDefaults = FlowNodeClass->GetDefaultObject(); FilteredNodes.Emplace(NodeDefaults); } void UFlowGraphSchema::GetFlowNodeActions(FGraphActionMenuBuilder& ActionMenuBuilder, const UFlowAsset* EditedFlowAsset, const FString& CategoryName) +{ + TArray FilteredNodes = GetFilteredPlaceableNodesOrAddOns(EditedFlowAsset, NativeFlowNodes, BlueprintFlowNodes); + + for (const UFlowNodeBase* FlowNodeBase : FilteredNodes) + { + if ((CategoryName.IsEmpty() || CategoryName.Equals(FlowNodeBase->GetNodeCategory())) && !UFlowGraphSettings::Get()->NodesHiddenFromPalette.Contains(FlowNodeBase->GetClass())) + { + const UFlowNode* FlowNode = CastChecked(FlowNodeBase); + TSharedPtr NewNodeAction(new FFlowGraphSchemaAction_NewNode(FlowNode)); + ActionMenuBuilder.AddAction(NewNodeAction); + } + } +} + +TArray UFlowGraphSchema::GetFilteredPlaceableNodesOrAddOns( + const UFlowAsset* EditedFlowAsset, + const TArray& InNativeNodesOrAddOns, + const TMap& InBlueprintNodesOrAddOns) { if (!bInitialGatherPerformed) { GatherNodes(); } - // Flow Asset type might limit which nodes are placeable - TArray FilteredNodes; + // Flow Asset type might limit which nodes or addons are placeable + TArray FilteredNodes; + + FilteredNodes.Reserve(InNativeNodesOrAddOns.Num() + BlueprintFlowNodes.Num()); + + for (const UClass* FlowNodeClass : InNativeNodesOrAddOns) { - FilteredNodes.Reserve(NativeFlowNodes.Num() + BlueprintFlowNodes.Num()); + ApplyNodeOrAddOnFilter(EditedFlowAsset, FlowNodeClass, FilteredNodes); + } - for (const UClass* FlowNodeClass : NativeFlowNodes) + for (const TPair& AssetData : InBlueprintNodesOrAddOns) + { + if (const UBlueprint* Blueprint = GetPlaceableNodeOrAddOnBlueprint(AssetData.Value)) { - ApplyNodeFilter(EditedFlowAsset, FlowNodeClass, FilteredNodes); + ApplyNodeOrAddOnFilter(EditedFlowAsset, Blueprint->GeneratedClass, FilteredNodes); } + } - for (const TPair& AssetData : BlueprintFlowNodes) + FilteredNodes.Shrink(); + + return FilteredNodes; +} + +void UFlowGraphSchema::GetGraphNodeContextActions(FGraphContextMenuBuilder& ContextMenuBuilder, int32 SubNodeFlags) const +{ + UEdGraph* Graph = const_cast(ContextMenuBuilder.CurrentGraph); + UClass* GraphNodeClass = UFlowGraphNode::StaticClass(); + + const UFlowAsset* EditedFlowAsset = GetEditedAssetOrClassDefault(ContextMenuBuilder.CurrentGraph); + + TArray FilteredNodes = GetFilteredPlaceableNodesOrAddOns(EditedFlowAsset, NativeFlowNodeAddOns, BlueprintFlowNodeAddOns); + + for (UFlowNodeBase* FlowNodeBase : FilteredNodes) + { + UFlowNodeAddOn* FlowNodeAddOnTemplate = CastChecked(FlowNodeBase); + + // Add-Ons are futher filtered by what they are potentially being attached to + // (in addition to the filtering in GetFilteredPlaceableNodesOrAddOns) + const bool bAllowAddOn = IsAddOnAllowedForSelectedObjects(ContextMenuBuilder.SelectedObjects, FlowNodeAddOnTemplate); + if (!bAllowAddOn) { - if (const UBlueprint* Blueprint = GetPlaceableNodeBlueprint(AssetData.Value)) - { - ApplyNodeFilter(EditedFlowAsset, Blueprint->GeneratedClass, FilteredNodes); - } + continue; } - FilteredNodes.Shrink(); + UFlowGraphNode* OpNode = NewObject(Graph, GraphNodeClass); + OpNode->NodeInstanceClass = FlowNodeAddOnTemplate->GetClass(); + + TSharedPtr AddOpAction = + FFlowSchemaAction_NewSubNode::AddNewSubNodeAction( + ContextMenuBuilder, + FText::FromString(FlowNodeBase->GetNodeCategory()), + FlowNodeBase->GetNodeTitle(), + FlowNodeBase->GetNodeToolTip()); + + AddOpAction->ParentNode = Cast(ContextMenuBuilder.SelectedObjects[0]); + AddOpAction->NodeTemplate = OpNode; } +} + +bool UFlowGraphSchema::IsAddOnAllowedForSelectedObjects(const TArray& SelectedObjects, const UFlowNodeAddOn* AddOnTemplate) const +{ + static_assert(static_cast<__underlying_type(EFlowAddOnAcceptResult)>(EFlowAddOnAcceptResult::Max) == 3, "This code may need updating if the enum values change"); - for (const UFlowNode* FlowNode : FilteredNodes) + EFlowAddOnAcceptResult CombinedResult = EFlowAddOnAcceptResult::Undetermined; + + for (const UObject* SelectedObject : SelectedObjects) { - if ((CategoryName.IsEmpty() || CategoryName.Equals(FlowNode->GetNodeCategory())) && !UFlowGraphSettings::Get()->NodesHiddenFromPalette.Contains(FlowNode->GetClass())) + const UFlowGraphNode* FlowGraphNode = Cast(SelectedObject); + if (!IsValid(FlowGraphNode)) { - TSharedPtr NewNodeAction(new FFlowGraphSchemaAction_NewNode(FlowNode)); - ActionMenuBuilder.AddAction(NewNodeAction); + return false; } + + UFlowNodeBase* FlowNodeOuter = Cast(FlowGraphNode->GetFlowNodeBase()); + if (!IsValid(FlowNodeOuter)) + { + continue; + } + + const EFlowAddOnAcceptResult SelectedObjectResult = FlowNodeOuter->CheckAcceptFlowNodeAddOnChild(AddOnTemplate); + + CombinedResult = CombineFlowAddOnAcceptResult(SelectedObjectResult, CombinedResult); + if (CombinedResult == EFlowAddOnAcceptResult::Reject) + { + // Any Rejection rejects the entire operation + return false; + } + } + + if (CombinedResult == EFlowAddOnAcceptResult::TentativeAccept) + { + return true; + } + else + { + return false; } } @@ -420,14 +569,14 @@ void UFlowGraphSchema::GetCommentAction(FGraphActionMenuBuilder& ActionMenuBuild } } -bool UFlowGraphSchema::IsFlowNodePlaceable(const UClass* Class) +bool UFlowGraphSchema::IsFlowNodeOrAddOnPlaceable(const UClass* Class) { if (Class->HasAnyClassFlags(CLASS_Abstract | CLASS_NotPlaceable | CLASS_Deprecated)) { return false; } - if (const UFlowNode* DefaultObject = Class->GetDefaultObject()) + if (const UFlowNodeBase* DefaultObject = Class->GetDefaultObject()) { return !DefaultObject->bNodeDeprecated; } @@ -437,7 +586,7 @@ bool UFlowGraphSchema::IsFlowNodePlaceable(const UClass* Class) void UFlowGraphSchema::OnBlueprintPreCompile(UBlueprint* Blueprint) { - if (Blueprint && Blueprint->GeneratedClass && Blueprint->GeneratedClass->IsChildOf(UFlowNode::StaticClass())) + if (Blueprint && Blueprint->GeneratedClass && Blueprint->GeneratedClass->IsChildOf(UFlowNodeBase::StaticClass())) { bBlueprintCompilationPending = true; } @@ -458,25 +607,23 @@ void UFlowGraphSchema::OnHotReload(EReloadCompleteReason ReloadCompleteReason) GatherNodes(); } -void UFlowGraphSchema::GatherNativeNodes() +void UFlowGraphSchema::GatherNativeNodesOrAddOns( + TSubclassOf FlowNodeBaseClass, + TArray& InOutNodesOrAddOnsArray) { - const bool bHotReloadNativeNodes = UFlowGraphEditorSettings::Get()->bHotReloadNativeNodes; - // collect C++ nodes once per editor session - if (NativeFlowNodes.Num() > 0 && !bHotReloadNativeNodes) + // collect C++ Nodes or AddOns once per editor session + if (InOutNodesOrAddOnsArray.Num() > 0) { return; } - NativeFlowNodes.Reset(); - GraphNodesByFlowNodes.Reset(); - - TArray FlowNodes; - GetDerivedClasses(UFlowNode::StaticClass(), FlowNodes); - for (UClass* Class : FlowNodes) + TArray FlowNodesOrAddOns; + GetDerivedClasses(FlowNodeBaseClass, FlowNodesOrAddOns); + for (UClass* Class : FlowNodesOrAddOns) { - if (Class->ClassGeneratedBy == nullptr && IsFlowNodePlaceable(Class)) + if (Class->ClassGeneratedBy == nullptr && IsFlowNodeOrAddOnPlaceable(Class)) { - NativeFlowNodes.Emplace(Class); + InOutNodesOrAddOnsArray.Emplace(Class); } } @@ -487,7 +634,7 @@ void UFlowGraphSchema::GatherNativeNodes() const UFlowGraphNode* GraphNodeCDO = GraphNodeClass->GetDefaultObject(); for (UClass* AssignedClass : GraphNodeCDO->AssignedNodeClasses) { - if (AssignedClass->IsChildOf(UFlowNode::StaticClass())) + if (AssignedClass->IsChildOf(FlowNodeBaseClass)) { GraphNodesByFlowNodes.Emplace(AssignedClass, GraphNodeClass); } @@ -503,13 +650,22 @@ void UFlowGraphSchema::GatherNodes() return; } + // prevent adding assets while compiling blueprints + // (because adding assets can cause blueprint compiles to be queued as a side-effect (via GetPlaceableNodeOrAddOnBlueprint)) + if (GCompilingBlueprint) + { + return; + } + bInitialGatherPerformed = true; - GatherNativeNodes(); + GatherNativeNodesOrAddOns(UFlowNode::StaticClass(), NativeFlowNodes); + GatherNativeNodesOrAddOns(UFlowNodeAddOn::StaticClass(), NativeFlowNodeAddOns); - // retrieve all blueprint nodes + // retrieve all blueprint nodes & addons FARFilter Filter; Filter.ClassPaths.Add(UFlowNodeBlueprint::StaticClass()->GetClassPathName()); + Filter.ClassPaths.Add(UFlowNodeAddOnBlueprint::StaticClass()->GetClassPathName()); Filter.bRecursiveClasses = true; TArray FoundAssets; @@ -530,26 +686,61 @@ void UFlowGraphSchema::OnAssetAdded(const FAssetData& AssetData) void UFlowGraphSchema::AddAsset(const FAssetData& AssetData, const bool bBatch) { - if (!BlueprintFlowNodes.Contains(AssetData.PackageName)) + const bool bIsAssetAlreadyKnown = + BlueprintFlowNodes.Contains(AssetData.PackageName) || + BlueprintFlowNodeAddOns.Contains(AssetData.PackageName); + + if (bIsAssetAlreadyKnown) { - const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(AssetRegistryConstants::ModuleName); - if (AssetRegistryModule.Get().IsLoadingAssets()) - { - return; - } + return; + } - if (AssetData.GetClass()->IsChildOf(UFlowNodeBlueprint::StaticClass())) - { - BlueprintFlowNodes.Emplace(AssetData.PackageName, AssetData); + const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(AssetRegistryConstants::ModuleName); + if (AssetRegistryModule.Get().IsLoadingAssets()) + { + return; + } + + bool bAddedToMap = false; + if (ShouldAddToBlueprintFlowNodesMap(AssetData, UFlowNodeBlueprint::StaticClass(), UFlowNode::StaticClass())) + { + BlueprintFlowNodes.Emplace(AssetData.PackageName, AssetData); + bAddedToMap = true; + } + else if (ShouldAddToBlueprintFlowNodesMap(AssetData, UFlowNodeAddOnBlueprint::StaticClass(), UFlowNodeAddOn::StaticClass())) + { + BlueprintFlowNodeAddOns.Emplace(AssetData.PackageName, AssetData); + bAddedToMap = true; + } - if (!bBatch) - { - OnNodeListChanged.Broadcast(); - } - } + if (bAddedToMap && !bBatch) + { + OnNodeListChanged.Broadcast(); } } +bool UFlowGraphSchema::ShouldAddToBlueprintFlowNodesMap(const FAssetData& AssetData, TSubclassOf BlueprintClass, TSubclassOf FlowNodeBaseClass) +{ + if (!AssetData.GetClass()->IsChildOf(BlueprintClass)) + { + return false; + } + + const UBlueprint* Blueprint = GetPlaceableNodeOrAddOnBlueprint(AssetData); + if (!IsValid(Blueprint)) + { + return false; + } + + UClass* GeneratedClass = Blueprint->GeneratedClass; + if (!GeneratedClass || !GeneratedClass->IsChildOf(FlowNodeBaseClass)) + { + return false; + } + + return true; +} + void UFlowGraphSchema::OnAssetRemoved(const FAssetData& AssetData) { if (BlueprintFlowNodes.Contains(AssetData.PackageName)) @@ -557,14 +748,21 @@ void UFlowGraphSchema::OnAssetRemoved(const FAssetData& AssetData) BlueprintFlowNodes.Remove(AssetData.PackageName); BlueprintFlowNodes.Shrink(); + OnNodeListChanged.Broadcast(); + } + else if (BlueprintFlowNodeAddOns.Contains(AssetData.PackageName)) + { + BlueprintFlowNodeAddOns.Remove(AssetData.PackageName); + BlueprintFlowNodeAddOns.Shrink(); + OnNodeListChanged.Broadcast(); } } -UBlueprint* UFlowGraphSchema::GetPlaceableNodeBlueprint(const FAssetData& AssetData) +UBlueprint* UFlowGraphSchema::GetPlaceableNodeOrAddOnBlueprint(const FAssetData& AssetData) { UBlueprint* Blueprint = Cast(AssetData.GetAsset()); - if (Blueprint && IsFlowNodePlaceable(Blueprint->GeneratedClass)) + if (Blueprint && IsFlowNodeOrAddOnPlaceable(Blueprint->GeneratedClass)) { return Blueprint; } @@ -572,18 +770,19 @@ UBlueprint* UFlowGraphSchema::GetPlaceableNodeBlueprint(const FAssetData& AssetD return nullptr; } -const UFlowAsset* UFlowGraphSchema::GetAssetClassDefaults(const UEdGraph* Graph) +const UFlowAsset* UFlowGraphSchema::GetEditedAssetOrClassDefault(const UEdGraph* EdGraph) { - const UClass* AssetClass = UFlowAsset::StaticClass(); - - if (Graph) + if (const UFlowGraph* FlowGraph = Cast(EdGraph)) { - if (const UFlowAsset* FlowAsset = Graph->GetTypedOuter()) + UFlowAsset* FlowAsset = FlowGraph->GetFlowAsset(); + + if (FlowAsset) { - AssetClass = FlowAsset->GetClass(); + return FlowGraph->GetFlowAsset(); } } + const UClass* AssetClass = UFlowAsset::StaticClass(); return AssetClass->GetDefaultObject(); } diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp index 7b81daed2..cceb82798 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp @@ -9,6 +9,7 @@ #include "Graph/Nodes/FlowGraphNode.h" #include "FlowAsset.h" +#include "AddOns/FlowNodeAddOn.h" #include "Nodes/FlowNode.h" #include "EdGraph/EdGraph.h" @@ -55,7 +56,8 @@ UFlowGraphNode* FFlowGraphSchemaAction_NewNode::CreateNode(UEdGraph* ParentGraph FlowAsset->Modify(); // create new Flow Graph node - const UClass* GraphNodeClass = UFlowGraphSchema::GetAssignedGraphNodeClass(NodeClass); + TSubclassOf FlowNodeBaseClass = const_cast(NodeClass); + TSubclassOf GraphNodeClass = UFlowGraphSchema::GetAssignedGraphNodeClass(FlowNodeBaseClass); UFlowGraphNode* NewGraphNode = NewObject(ParentGraph, GraphNodeClass, NAME_None, RF_Transactional); // register to the graph @@ -103,7 +105,7 @@ UFlowGraphNode* FFlowGraphSchemaAction_NewNode::RecreateNode(UEdGraph* ParentGra FlowAsset->Modify(); // create new Flow Graph node - const UClass* GraphNodeClass = UFlowGraphSchema::GetAssignedGraphNodeClass(FlowNode->GetClass()); + const TSubclassOf GraphNodeClass = UFlowGraphSchema::GetAssignedGraphNodeClass(FlowNode->GetClass()); UFlowGraphNode* NewGraphNode = NewObject(ParentGraph, GraphNodeClass, NAME_None, RF_Transactional); // register to the graph @@ -171,7 +173,8 @@ UFlowGraphNode* FFlowGraphSchemaAction_NewNode::ImportNode(UEdGraph* ParentGraph FlowAsset->Modify(); // create new Flow Graph node - const UClass* GraphNodeClass = UFlowGraphSchema::GetAssignedGraphNodeClass(NodeClass); + TSubclassOf FlowNodeBaseClass = const_cast(NodeClass); + const TSubclassOf GraphNodeClass = UFlowGraphSchema::GetAssignedGraphNodeClass(FlowNodeBaseClass); UFlowGraphNode* NewGraphNode = NewObject(ParentGraph, GraphNodeClass, NAME_None, RF_Transactional); // register to the graph @@ -197,6 +200,69 @@ UFlowGraphNode* FFlowGraphSchemaAction_NewNode::ImportNode(UEdGraph* ParentGraph return NewGraphNode; } +///////////////////////////////////////////////////// +// New SubNode (AddOn) + +UEdGraphNode* FFlowSchemaAction_NewSubNode::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode) +{ + ParentNode->AddSubNode(NodeTemplate, ParentGraph); + return NULL; +} + +UEdGraphNode* FFlowSchemaAction_NewSubNode::PerformAction(class UEdGraph* ParentGraph, TArray& FromPins, const FVector2D Location, bool bSelectNewNode) +{ + return PerformAction(ParentGraph, NULL, Location, bSelectNewNode); +} + +void FFlowSchemaAction_NewSubNode::AddReferencedObjects(FReferenceCollector& Collector) +{ + FEdGraphSchemaAction::AddReferencedObjects(Collector); + + // These don't get saved to disk, but we want to make sure the objects don't get GC'd while the action array is around + Collector.AddReferencedObject(NodeTemplate); + Collector.AddReferencedObject(ParentNode); +} + +UFlowGraphNode* FFlowSchemaAction_NewSubNode::RecreateNode(UEdGraph* ParentGraph, UEdGraphNode* OldInstance, UFlowGraphNode* ParentFlowGraphNode, UFlowNodeAddOn* FlowNodeAddOn) +{ + check(FlowNodeAddOn); + + UFlowAsset* FlowAsset = CastChecked(ParentGraph)->GetFlowAsset(); + FlowAsset->Modify(); + + // create new Flow Graph node + const TSubclassOf GraphNodeClass = UFlowGraphSchema::GetAssignedGraphNodeClass(FlowNodeAddOn->GetClass()); + UFlowGraphNode* NewGraphNode = NewObject(ParentGraph, GraphNodeClass, NAME_None, RF_Transactional); + + // link editor and runtime nodes together + FlowNodeAddOn->SetGraphNode(NewGraphNode); + NewGraphNode->SetNodeTemplate(FlowNodeAddOn); + + // remove leftover + if (OldInstance) + { + OldInstance->DestroyNode(); + } + + ParentFlowGraphNode->AddSubNode(NewGraphNode, ParentGraph); + + // call notifies + ParentGraph->NotifyGraphChanged(); + NewGraphNode->PostPlacedNewNode(); + + return NewGraphNode; +} + +TSharedPtr FFlowSchemaAction_NewSubNode::AddNewSubNodeAction(FGraphActionListBuilderBase& ContextMenuBuilder, const FText& Category, const FText& MenuDesc, const FText& Tooltip) +{ + TSharedPtr NewAction = TSharedPtr(new FFlowSchemaAction_NewSubNode(Category, MenuDesc, Tooltip, 0)); + ContextMenuBuilder.AddAction(NewAction); + return NewAction; +} + +///////////////////////////////////////////////////// +// Paste + UEdGraphNode* FFlowGraphSchemaAction_Paste::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, const bool bSelectNewNode/* = true*/) { // prevent adding new nodes while playing @@ -209,7 +275,7 @@ UEdGraphNode* FFlowGraphSchemaAction_Paste::PerformAction(class UEdGraph* Parent } ///////////////////////////////////////////////////// -// Comment Node +// New Comment UEdGraphNode* FFlowGraphSchemaAction_NewComment::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, const bool bSelectNewNode/* = true*/) { diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 802c0ba39..a2f8a12d7 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -2,6 +2,7 @@ #include "Graph/Nodes/FlowGraphNode.h" +#include "AddOns/FlowNodeAddOn.h" #include "Asset/FlowDebuggerSubsystem.h" #include "FlowEditorCommands.h" #include "Graph/FlowGraph.h" @@ -9,16 +10,17 @@ #include "Graph/FlowGraphSchema.h" #include "Graph/FlowGraphSettings.h" #include "Graph/Widgets/SFlowGraphNode.h" - +#include "Graph/Widgets/SGraphEditorActionMenuFlow.h" #include "FlowAsset.h" #include "Nodes/FlowNode.h" +#include "BlueprintNodeHelpers.h" #include "Developer/ToolMenus/Public/ToolMenus.h" -#include "EdGraph/EdGraphSchema.h" +#include "DiffResults.h" #include "EdGraphSchema_K2.h" #include "Editor.h" -#include "Editor/EditorEngine.h" #include "Framework/Commands/GenericCommands.h" +#include "GraphDiffControl.h" #include "GraphEditorActions.h" #include "HAL/FileManager.h" #include "Kismet2/KismetEditorUtilities.h" @@ -33,33 +35,36 @@ UFlowGraphNode::UFlowGraphNode(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) - , FlowNode(nullptr) + , NodeInstance(nullptr) , bBlueprintCompilationPending(false) , bNeedsFullReconstruction(false) { OrphanedPinSaveMode = ESaveOrphanPinMode::SaveAll; } -void UFlowGraphNode::SetNodeTemplate(UFlowNode* InFlowNode) +void UFlowGraphNode::SetNodeTemplate(UFlowNodeBase* InFlowNode) { - FlowNode = InFlowNode; + NodeInstance = InFlowNode; } -const UFlowNode* UFlowGraphNode::GetNodeTemplate() const +const UFlowNodeBase* UFlowGraphNode::GetNodeTemplate() const { - return FlowNode; + return NodeInstance; } -UFlowNode* UFlowGraphNode::GetFlowNode() const +UFlowNodeBase* UFlowGraphNode::GetFlowNodeBase() const { - if (FlowNode) + if (NodeInstance) { - if (const UFlowAsset* InspectedInstance = FlowNode->GetFlowAsset()->GetInspectedInstance()) + if (UFlowNode* FlowNode = Cast(NodeInstance)) { - return InspectedInstance->GetNode(FlowNode->GetGuid()); + if (const UFlowAsset* InspectedInstance = FlowNode->GetFlowAsset()->GetInspectedInstance()) + { + return InspectedInstance->GetNode(FlowNode->GetGuid()); + } } - return FlowNode; + return NodeInstance; } return nullptr; @@ -69,9 +74,9 @@ void UFlowGraphNode::PostLoad() { Super::PostLoad(); - if (FlowNode) + if (NodeInstance) { - FlowNode->FixNode(this); // fix already created nodes + NodeInstance->FixNode(this); // fix already created nodes SubscribeToExternalChanges(); } @@ -86,6 +91,7 @@ void UFlowGraphNode::PostDuplicate(bool bDuplicateForPIE) { CreateNewGuid(); + UFlowNode* FlowNode = Cast(NodeInstance); if (FlowNode && FlowNode->GetFlowAsset()) { FlowNode->GetFlowAsset()->RegisterNode(NodeGuid, FlowNode); @@ -99,6 +105,14 @@ void UFlowGraphNode::PostEditImport() PostCopyNode(); SubscribeToExternalChanges(); + + // Reset the owning graph after an edit import + ResetNodeOwner(); + + if (NodeInstance) + { + InitializeInstance(); + } } void UFlowGraphNode::PostPlacedNewNode() @@ -106,49 +120,95 @@ void UFlowGraphNode::PostPlacedNewNode() Super::PostPlacedNewNode(); SubscribeToExternalChanges(); + + // NOTE - NodeInstance can be already spawned by paste operation, don't override it + + if (NodeInstanceClass.IsPending()) + { + NodeInstanceClass.LoadSynchronous(); + } + + UClass* NodeClass = NodeInstanceClass.Get(); + if (NodeClass && (NodeInstance == nullptr)) + { + UEdGraph* MyGraph = GetGraph(); + UObject* GraphOwner = MyGraph ? MyGraph->GetOuter() : nullptr; + if (GraphOwner) + { + NodeInstance = Cast(NewObject(GraphOwner, NodeClass)); + NodeInstance->SetFlags(RF_Transactional); + + InitializeInstance(); + } + } } void UFlowGraphNode::PrepareForCopying() { Super::PrepareForCopying(); - if (FlowNode) + if (NodeInstance) { - // Temporarily take ownership of the FlowNode, so that it is not deleted when cutting - FlowNode->Rename(nullptr, this, REN_DontCreateRedirectors); + // Temporarily take ownership of the node instance, so that it is not deleted when cutting + NodeInstance->Rename(nullptr, this, REN_DontCreateRedirectors | REN_DoNotDirty); } } void UFlowGraphNode::PostCopyNode() { - // Make sure this FlowNode is owned by the FlowAsset it's being pasted into - if (FlowNode) + // Make sure this NodeInstance is owned by the FlowAsset it's being pasted into + if (NodeInstance) { UFlowAsset* FlowAsset = CastChecked(GetGraph())->GetFlowAsset(); - if (FlowNode->GetOuter() != FlowAsset) + if (NodeInstance->GetOuter() != FlowAsset) { - // Ensures FlowNode is owned by the FlowAsset - FlowNode->Rename(nullptr, FlowAsset, REN_DontCreateRedirectors); + // Ensures NodeInstance is owned by the FlowAsset + NodeInstance->Rename(nullptr, FlowAsset, REN_DontCreateRedirectors); } - FlowNode->SetGraphNode(this); + NodeInstance->SetGraphNode(this); } + + // Reset the node's owning graph prior to copying + ResetNodeOwner(); } void UFlowGraphNode::SubscribeToExternalChanges() { - if (FlowNode) + if (NodeInstance) { - FlowNode->OnReconstructionRequested.BindUObject(this, &UFlowGraphNode::OnExternalChange); + NodeInstance->OnReconstructionRequested.BindUObject(this, &UFlowGraphNode::OnExternalChange); + + // blueprint nodes + if (NodeInstance->GetClass()->ClassGeneratedBy && GEditor) + { + GEditor->OnBlueprintPreCompile().AddUObject(this, &UFlowGraphNode::OnBlueprintPreCompile); + GEditor->OnBlueprintCompiled().AddUObject(this, &UFlowGraphNode::OnBlueprintCompiled); + } } } +void UFlowGraphNode::OnBlueprintPreCompile(UBlueprint* Blueprint) +{ + if (Blueprint && Blueprint == NodeInstance->GetClass()->ClassGeneratedBy) + { + bBlueprintCompilationPending = true; + } +} + +void UFlowGraphNode::OnBlueprintCompiled() +{ + if (bBlueprintCompilationPending) + { + OnExternalChange(); + } + + bBlueprintCompilationPending = false; +} + void UFlowGraphNode::OnExternalChange() { - // Do not create transaction here, since this function triggers from modifying UFlowNode's property, which itself already made inside of transaction. - Modify(); - bNeedsFullReconstruction = true; ReconstructNode(); @@ -247,7 +307,7 @@ void UFlowGraphNode::ReconstructNode() OutputPins.Reset(); // Recreate pins - if (SupportsContextPins() && (FlowNode->CanRefreshContextPinsOnLoad() || bNeedsFullReconstruction)) + if (SupportsContextPins() && (NodeInstance->CanRefreshContextPinsOnLoad() || bNeedsFullReconstruction)) { RefreshContextPins(false); } @@ -269,7 +329,7 @@ void UFlowGraphNode::AllocateDefaultPins() { check(Pins.Num() == 0); - if (FlowNode) + if (UFlowNode* FlowNode = Cast(NodeInstance)) { for (const FFlowPin& InputPin : FlowNode->InputPins) { @@ -412,6 +472,16 @@ void UFlowGraphNode::GetNodeContextMenuActions(class UToolMenu* Menu, class UGra } else if (Context->Node) { + { + FToolMenuSection& Section = Menu->AddSection("FlowGraphNodeAddOns", LOCTEXT("NodeAddOnsMenuHeader", "AddOns")); + Section.AddSubMenu( + "AttachAddOn", + LOCTEXT("AttachAddOn", "Attach AddOn..."), + LOCTEXT("AttachAddOnTooltip", "Attaches an AddOn to the Node"), + FNewToolMenuDelegate::CreateUObject(this, &UFlowGraphNode::CreateAttachAddOnSubMenu, (UEdGraph*)Context->Graph) + ); + } + { FToolMenuSection& Section = Menu->AddSection("FlowGraphNodeActions", LOCTEXT("NodeActionsMenuHeader", "Node Actions")); Section.AddMenuEntry(GenericCommands.Delete); @@ -490,14 +560,27 @@ void UFlowGraphNode::GetNodeContextMenuActions(class UToolMenu* Menu, class UGra } } +void UFlowGraphNode::CreateAttachAddOnSubMenu(UToolMenu* Menu, UEdGraph* Graph) const +{ + UFlowGraphNode* MutableThis = const_cast(this); + + TSharedRef Widget = + SNew(SGraphEditorActionMenuFlow) + .GraphObj(Graph) + .GraphNode(MutableThis) + .AutoExpandActionMenu(true); + + Menu->AddMenuEntry("Section", FToolMenuEntry::InitWidget("Widget", Widget, FText(), true)); +} + bool UFlowGraphNode::CanUserDeleteNode() const { - return FlowNode ? FlowNode->bCanDelete : Super::CanUserDeleteNode(); + return NodeInstance ? NodeInstance->bCanDelete : Super::CanUserDeleteNode(); } bool UFlowGraphNode::CanDuplicateNode() const { - return FlowNode ? FlowNode->bCanDuplicate : Super::CanDuplicateNode(); + return NodeInstance ? NodeInstance->bCanDuplicate : Super::CanDuplicateNode(); } TSharedPtr UFlowGraphNode::CreateVisualWidget() @@ -507,29 +590,29 @@ TSharedPtr UFlowGraphNode::CreateVisualWidget() FText UFlowGraphNode::GetNodeTitle(ENodeTitleType::Type TitleType) const { - if (FlowNode) + if (NodeInstance) { if (UFlowGraphEditorSettings::Get()->bShowNodeClass) { FString CleanAssetName; - if (FlowNode->GetClass()->ClassGeneratedBy) + if (NodeInstance->GetClass()->ClassGeneratedBy) { - FlowNode->GetClass()->GetPathName(nullptr, CleanAssetName); + NodeInstance->GetClass()->GetPathName(nullptr, CleanAssetName); const int32 SubStringIdx = CleanAssetName.Find(".", ESearchCase::IgnoreCase, ESearchDir::FromEnd); CleanAssetName.LeftInline(SubStringIdx); } else { - CleanAssetName = FlowNode->GetClass()->GetName(); + CleanAssetName = NodeInstance->GetClass()->GetName(); } FFormatNamedArguments Args; - Args.Add(TEXT("NodeTitle"), FlowNode->GetNodeTitle()); + Args.Add(TEXT("NodeTitle"), NodeInstance->GetNodeTitle()); Args.Add(TEXT("AssetName"), FText::FromString(CleanAssetName)); return FText::Format(INVTEXT("{NodeTitle}\n{AssetName}"), Args); } - return FlowNode->GetNodeTitle(); + return NodeInstance->GetNodeTitle(); } return Super::GetNodeTitle(TitleType); @@ -537,20 +620,20 @@ FText UFlowGraphNode::GetNodeTitle(ENodeTitleType::Type TitleType) const FLinearColor UFlowGraphNode::GetNodeTitleColor() const { - if (FlowNode) + if (NodeInstance) { FLinearColor DynamicColor; - if (FlowNode->GetDynamicTitleColor(DynamicColor)) + if (NodeInstance->GetDynamicTitleColor(DynamicColor)) { return DynamicColor; } UFlowGraphSettings* GraphSettings = UFlowGraphSettings::Get(); - if (const FLinearColor* NodeSpecificColor = GraphSettings->NodeSpecificColors.Find(FlowNode->GetClass())) + if (const FLinearColor* NodeSpecificColor = GraphSettings->NodeSpecificColors.Find(NodeInstance->GetClass())) { return *NodeSpecificColor; } - if (const FLinearColor* StyleColor = GraphSettings->NodeTitleColors.Find(FlowNode->GetNodeStyle())) + if (const FLinearColor* StyleColor = GraphSettings->NodeTitleColors.Find(NodeInstance->GetNodeStyle())) { return *StyleColor; } @@ -567,9 +650,9 @@ FSlateIcon UFlowGraphNode::GetIconAndTint(FLinearColor& OutColor) const FText UFlowGraphNode::GetTooltipText() const { FText Tooltip; - if (FlowNode) + if (NodeInstance) { - Tooltip = FlowNode->GetClass()->GetToolTipText(); + Tooltip = NodeInstance->GetClass()->GetToolTipText(); } if (Tooltip.IsEmpty()) { @@ -580,9 +663,9 @@ FText UFlowGraphNode::GetTooltipText() const FString UFlowGraphNode::GetNodeDescription() const { - if (FlowNode && (GEditor->PlayWorld == nullptr || UFlowGraphEditorSettings::Get()->bShowNodeDescriptionWhilePlaying)) + if (NodeInstance && (GEditor->PlayWorld == nullptr || UFlowGraphEditorSettings::Get()->bShowNodeDescriptionWhilePlaying)) { - return FlowNode->GetNodeDescription(); + return NodeInstance->GetNodeDescription(); } return FString(); @@ -590,16 +673,18 @@ FString UFlowGraphNode::GetNodeDescription() const UFlowNode* UFlowGraphNode::GetInspectedNodeInstance() const { + const UFlowNode* FlowNode = Cast(NodeInstance); return FlowNode ? FlowNode->GetInspectedInstance() : nullptr; } EFlowNodeState UFlowGraphNode::GetActivationState() const { + const UFlowNode* FlowNode = Cast(NodeInstance); if (FlowNode) { - if (const UFlowNode* NodeInstance = FlowNode->GetInspectedInstance()) + if (const UFlowNode* InspectedInstance = FlowNode->GetInspectedInstance()) { - return NodeInstance->GetActivationState(); + return InspectedInstance->GetActivationState(); } } @@ -608,11 +693,12 @@ EFlowNodeState UFlowGraphNode::GetActivationState() const FString UFlowGraphNode::GetStatusString() const { + const UFlowNode* FlowNode = Cast(NodeInstance); if (FlowNode) { - if (const UFlowNode* NodeInstance = FlowNode->GetInspectedInstance()) + if (const UFlowNode* InspectedInstance = FlowNode->GetInspectedInstance()) { - return NodeInstance->GetStatusString(); + return InspectedInstance->GetStatusString(); } } @@ -621,12 +707,13 @@ FString UFlowGraphNode::GetStatusString() const FLinearColor UFlowGraphNode::GetStatusBackgroundColor() const { + const UFlowNode* FlowNode = Cast(NodeInstance); if (FlowNode) { - if (const UFlowNode* NodeInstance = FlowNode->GetInspectedInstance()) + if (const UFlowNode* InspectedInstance = FlowNode->GetInspectedInstance()) { FLinearColor ObtainedColor; - if (NodeInstance->GetStatusBackgroundColor(ObtainedColor)) + if (InspectedInstance->GetStatusBackgroundColor(ObtainedColor)) { return ObtainedColor; } @@ -638,11 +725,12 @@ FLinearColor UFlowGraphNode::GetStatusBackgroundColor() const bool UFlowGraphNode::IsContentPreloaded() const { + const UFlowNode* FlowNode = Cast(NodeInstance); if (FlowNode) { - if (const UFlowNode* NodeInstance = FlowNode->GetInspectedInstance()) + if (const UFlowNode* InspectedInstance = FlowNode->GetInspectedInstance()) { - return NodeInstance->bPreloaded; + return InspectedInstance->bPreloaded; } } @@ -651,23 +739,24 @@ bool UFlowGraphNode::IsContentPreloaded() const bool UFlowGraphNode::CanFocusViewport() const { + UFlowNode* FlowNode = Cast(NodeInstance); return FlowNode ? (GEditor->bIsSimulatingInEditor && FlowNode->GetActorToFocus()) : false; } bool UFlowGraphNode::CanJumpToDefinition() const { - return FlowNode != nullptr; + return NodeInstance != nullptr; } void UFlowGraphNode::JumpToDefinition() const { - if (FlowNode) + if (NodeInstance) { - if (FlowNode->GetClass()->IsNative()) + if (NodeInstance->GetClass()->IsNative()) { - if (FSourceCodeNavigation::CanNavigateToClass(FlowNode->GetClass())) + if (FSourceCodeNavigation::CanNavigateToClass(NodeInstance->GetClass())) { - const bool bSucceeded = FSourceCodeNavigation::NavigateToClass(FlowNode->GetClass()); + const bool bSucceeded = FSourceCodeNavigation::NavigateToClass(NodeInstance->GetClass()); if (bSucceeded) { return; @@ -676,7 +765,7 @@ void UFlowGraphNode::JumpToDefinition() const // Failing that, fall back to the older method which will still get the file open assuming it exists FString NativeParentClassHeaderPath; - const bool bFileFound = FSourceCodeNavigation::FindClassHeaderPath(FlowNode->GetClass(), NativeParentClassHeaderPath) && (IFileManager::Get().FileSize(*NativeParentClassHeaderPath) != INDEX_NONE); + const bool bFileFound = FSourceCodeNavigation::FindClassHeaderPath(NodeInstance->GetClass(), NativeParentClassHeaderPath) && (IFileManager::Get().FileSize(*NativeParentClassHeaderPath) != INDEX_NONE); if (bFileFound) { const FString AbsNativeParentClassHeaderPath = FPaths::ConvertRelativePathToFull(NativeParentClassHeaderPath); @@ -685,11 +774,21 @@ void UFlowGraphNode::JumpToDefinition() const } else { - FKismetEditorUtilities::BringKismetToFocusAttentionOnObject(FlowNode->GetClass()); + FKismetEditorUtilities::BringKismetToFocusAttentionOnObject(NodeInstance->GetClass()); } } } +bool UFlowGraphNode::SupportsCommentBubble() const +{ + if (IsSubNode()) + { + return false; + } + + return Super::SupportsCommentBubble(); +} + void UFlowGraphNode::CreateInputPin(const FFlowPin& FlowPin, const int32 Index /*= INDEX_NONE*/) { if (FlowPin.PinName.IsNone()) @@ -750,36 +849,42 @@ void UFlowGraphNode::RemoveOrphanedPin(UEdGraphPin* Pin) bool UFlowGraphNode::SupportsContextPins() const { - return FlowNode && FlowNode->SupportsContextPins(); + return NodeInstance && NodeInstance->SupportsContextPins(); } bool UFlowGraphNode::CanUserAddInput() const { + UFlowNode* FlowNode = Cast(NodeInstance); return FlowNode && FlowNode->CanUserAddInput() && InputPins.Num() < 256; } bool UFlowGraphNode::CanUserAddOutput() const { + UFlowNode* FlowNode = Cast(NodeInstance); return FlowNode && FlowNode->CanUserAddOutput() && OutputPins.Num() < 256; } bool UFlowGraphNode::CanUserRemoveInput(const UEdGraphPin* Pin) const { + UFlowNode* FlowNode = Cast(NodeInstance); return FlowNode && !FlowNode->GetClass()->GetDefaultObject()->InputPins.Contains(Pin->PinName); } bool UFlowGraphNode::CanUserRemoveOutput(const UEdGraphPin* Pin) const { + UFlowNode* FlowNode = Cast(NodeInstance); return FlowNode && !FlowNode->GetClass()->GetDefaultObject()->OutputPins.Contains(Pin->PinName); } void UFlowGraphNode::AddUserInput() { + UFlowNode* FlowNode = Cast(NodeInstance); AddInstancePin(EGPD_Input, FlowNode->CountNumberedInputs()); } void UFlowGraphNode::AddUserOutput() { + UFlowNode* FlowNode = Cast(NodeInstance); AddInstancePin(EGPD_Output, FlowNode->CountNumberedOutputs()); } @@ -790,6 +895,7 @@ void UFlowGraphNode::AddInstancePin(const EEdGraphPinDirection Direction, const const FFlowPin PinName = FFlowPin(FString::FromInt(NumberedPinsAmount)); + UFlowNode* FlowNode = Cast(NodeInstance); if (Direction == EGPD_Input) { if (FlowNode->InputPins.IsValidIndex(NumberedPinsAmount)) @@ -827,6 +933,7 @@ void UFlowGraphNode::RemoveInstancePin(UEdGraphPin* Pin) PinBreakpoints.Remove(Pin); + UFlowNode* FlowNode = Cast(NodeInstance); if (Pin->Direction == EGPD_Input) { if (InputPins.Contains(Pin)) @@ -856,7 +963,28 @@ void UFlowGraphNode::RemoveInstancePin(UEdGraphPin* Pin) void UFlowGraphNode::RefreshContextPins(const bool bReconstructNode) { - if (SupportsContextPins()) + UFlowNode* FlowNode = Cast(NodeInstance); + if (!IsValid(FlowNode)) + { + return; + } + + const bool bShouldConsiderRefreshingContextPins = SupportsContextPins() || bHasContextPins; + if (!bShouldConsiderRefreshingContextPins) + { + return; + } + + const TArray ContextInputs = FlowNode->GetContextInputs(); + const TArray ContextOutputs = FlowNode->GetContextOutputs(); + + const bool bPrevHasContextPins = bHasContextPins; + bHasContextPins = !ContextInputs.IsEmpty() || !ContextOutputs.IsEmpty(); + + // Skip the rest if the node went from no ContextPins to no ContextPins + const bool bMaintainedNoContextPins = !bPrevHasContextPins && !bHasContextPins; + + if (!bMaintainedNoContextPins) { const FScopedTransaction Transaction(LOCTEXT("RefreshContextPins", "Refresh Context Pins")); Modify(); @@ -865,13 +993,13 @@ void UFlowGraphNode::RefreshContextPins(const bool bReconstructNode) // recreate inputs FlowNode->InputPins = NodeDefaults->InputPins; - FlowNode->AddInputPins(FlowNode->GetContextInputs()); + FlowNode->AddInputPins(ContextInputs); // recreate outputs FlowNode->OutputPins = NodeDefaults->OutputPins; - FlowNode->AddOutputPins(FlowNode->GetContextOutputs()); + FlowNode->AddOutputPins(ContextOutputs); - if (bReconstructNode) + if (bReconstructNode && !bMaintainedNoContextPins) { ReconstructNode(); GetGraph()->NotifyGraphChanged(); @@ -1012,6 +1140,7 @@ void UFlowGraphNode::ForcePinActivation(const FEdGraphPinReference PinReference) void UFlowGraphNode::SetSignalMode(const EFlowSignalMode Mode) { + UFlowNode* FlowNode = Cast(NodeInstance); if (FlowNode) { FlowNode->SignalMode = Mode; @@ -1021,12 +1150,506 @@ void UFlowGraphNode::SetSignalMode(const EFlowSignalMode Mode) EFlowSignalMode UFlowGraphNode::GetSignalMode() const { - return FlowNode ? FlowNode->SignalMode : EFlowSignalMode::Disabled; + if (IsSubNode()) + { + // SubNodes count as enabled for signal mode queries in the editor + return EFlowSignalMode::Enabled; + } + + UFlowNode* FlowNode = Cast(NodeInstance); + if (IsValid(FlowNode)) + { + return FlowNode->SignalMode; + } + else + { + return EFlowSignalMode::Disabled; + } } bool UFlowGraphNode::CanSetSignalMode(const EFlowSignalMode Mode) const { + UFlowNode* FlowNode = Cast(NodeInstance); return FlowNode ? (FlowNode->AllowedSignalModes.Contains(Mode) && FlowNode->SignalMode != Mode) : false; } +void UFlowGraphNode::InitializeInstance() +{ + check(NodeInstance); + + // link editor and runtime nodes together + NodeInstance->SetGraphNode(this); +} + +void UFlowGraphNode::PostEditUndo() +{ + UEdGraphNode::PostEditUndo(); + ResetNodeOwner(); + + if (ParentNode) + { + ParentNode->SubNodes.AddUnique(this); + + ParentNode->RebuildRuntimeAddOnsFromEditorSubNodes(); + } + else + { + RebuildRuntimeAddOnsFromEditorSubNodes(); + } +} + +void UFlowGraphNode::LogError(const FString& MessageToLog, const UFlowNodeBase* FlowNodeBase) +{ + if (UFlowGraph* FlowGraph = GetFlowGraph()) + { + if (UFlowAsset* FlowAsset = FlowGraph->GetFlowAsset()) + { + FlowAsset->LogError(MessageToLog, const_cast(FlowNodeBase)); + } + } +} + +void UFlowGraphNode::ResetNodeOwner() +{ + if (NodeInstance) + { + UEdGraph* MyGraph = GetGraph(); + UObject* GraphOwner = MyGraph ? MyGraph->GetOuter() : nullptr; + + NodeInstance->Rename(NULL, GraphOwner, REN_DontCreateRedirectors | REN_DoNotDirty); + NodeInstance->ClearFlags(RF_Transient); + + for (auto& SubNode : SubNodes) + { + SubNode->ResetNodeOwner(); + } + } +} + +FText UFlowGraphNode::GetDescription() const +{ + FString StoredClassName = NodeInstanceClass.GetAssetName(); + StoredClassName.RemoveFromEnd(TEXT("_C")); + + return FText::Format(LOCTEXT("NodeClassError", "Class {0} not found, make sure it's saved!"), FText::FromString(StoredClassName)); +} + +UEdGraphPin* UFlowGraphNode::GetInputPin(int32 InputIndex) const +{ + check(InputIndex >= 0); + + for (int32 PinIndex = 0, FoundInputs = 0; PinIndex < Pins.Num(); PinIndex++) + { + if (Pins[PinIndex]->Direction == EGPD_Input) + { + if (InputIndex == FoundInputs) + { + return Pins[PinIndex]; + } + else + { + FoundInputs++; + } + } + } + + return nullptr; +} + +UEdGraphPin* UFlowGraphNode::GetOutputPin(int32 InputIndex) const +{ + check(InputIndex >= 0); + + for (int32 PinIndex = 0, FoundInputs = 0; PinIndex < Pins.Num(); PinIndex++) + { + if (Pins[PinIndex]->Direction == EGPD_Output) + { + if (InputIndex == FoundInputs) + { + return Pins[PinIndex]; + } + else + { + FoundInputs++; + } + } + } + + return nullptr; +} + +UFlowGraph* UFlowGraphNode::GetFlowGraph() const +{ + return CastChecked(GetGraph()); +} + +bool UFlowGraphNode::IsSubNode() const +{ + return bIsSubNode || (ParentNode != nullptr); +} + +void UFlowGraphNode::NodeConnectionListChanged() +{ + Super::NodeConnectionListChanged(); + + GetFlowGraph()->UpdateAsset(); +} + +FString UFlowGraphNode::GetPropertyNameAndValueForDiff(const FProperty* Prop, const uint8* PropertyAddr) const +{ + return BlueprintNodeHelpers::DescribeProperty(Prop, PropertyAddr); +} + +void UFlowGraphNode::SetParentNodeForSubNode(UFlowGraphNode* InParentNode) +{ + if (InParentNode) + { + // Once a subnode, always a subnode + bIsSubNode = true; + } + + ParentNode = InParentNode; +} + +void UFlowGraphNode::RebuildRuntimeAddOnsFromEditorSubNodes() +{ + // NOTE (gtaylor) Whenever we change the SubNodes array, we need to mirror the changes + // across to the AddOns array in the runtime instance data + + if (IsValid(NodeInstance)) + { + TArray& NodeInstanceAddOns = NodeInstance->GetFlowNodeAddOnChildrenByEditor(); + NodeInstanceAddOns.Reset(); + NodeInstanceAddOns.Reserve(SubNodes.Num()); + + for (UFlowGraphNode* SubNode : SubNodes) + { + if (!IsValid(SubNode)) + { + LogError(FString::Printf(TEXT("%s: Has unexpectedly null SubNode"), *GetName()), NodeInstance); + + continue; + } + + // Add the runtime AddOn to its runtime UFlowNode or UFlowNodeAddOn container + UFlowNodeAddOn* AddOnSubNodeInstance = Cast(SubNode->NodeInstance); + if (IsValid(AddOnSubNodeInstance)) + { + NodeInstanceAddOns.AddUnique(AddOnSubNodeInstance); + } + else + { + LogError(FString::Printf(TEXT("%s: SubNode is missing an AddOn NodeInstance"), *GetName()), NodeInstance); + } + } + } + + // Update the subnodes as well + for (UFlowGraphNode* SubNode : SubNodes) + { + if (IsValid(SubNode)) + { + SubNode->RebuildRuntimeAddOnsFromEditorSubNodes(); + } + } + + // Reconstruct the context pins for all flow nodes after their AddOns have been processed + if (IsValid(NodeInstance) && NodeInstance->IsA()) + { + constexpr bool bReconstructNode = true; + RefreshContextPins(bReconstructNode); + } +} + +void UFlowGraphNode::FindDiffs(UEdGraphNode* OtherNode, FDiffResults& Results) +{ + Super::FindDiffs(OtherNode, Results); + + UFlowGraphNode* OtherGraphNode = Cast(OtherNode); + if (!IsValid(OtherGraphNode)) + { + return; + } + + if (NodeInstance && OtherGraphNode->NodeInstance) + { + FDiffSingleResult Diff; + Diff.Diff = EDiffType::NODE_PROPERTY; + Diff.Node1 = this; + Diff.Node2 = OtherNode; + Diff.ToolTip = LOCTEXT("DIF_NodeInstancePropertyToolTip", "A property of the node instance has changed"); + Diff.Category = EDiffType::MODIFICATION; + + DiffProperties(NodeInstance->GetClass(), OtherGraphNode->NodeInstance->GetClass(), NodeInstance, OtherGraphNode->NodeInstance, Results, Diff); + } + + DiffSubNodes(LOCTEXT("AddOnDiffDisplayName", "AddOn"), SubNodes, OtherGraphNode->SubNodes, Results); +} + +void UFlowGraphNode::DiffSubNodes( + const FText& NodeTypeDisplayName, + const TArray& LhsSubNodes, + const TArray& RhsSubNodes, + FDiffResults& Results) +{ + TArray NodeMatches; + TSet MatchedRhsNodes; + + FGraphDiffControl::FNodeDiffContext AdditiveDiffContext; + AdditiveDiffContext.NodeTypeDisplayName = NodeTypeDisplayName; + AdditiveDiffContext.bIsRootNode = false; + + // march through the all the nodes in the rhs and look for matches + for (UEdGraphNode* RhsSubNode : RhsSubNodes) + { + FGraphDiffControl::FNodeMatch NodeMatch; + NodeMatch.NewNode = RhsSubNode; + + // Do two passes, exact and soft + for (UEdGraphNode* LhsSubNode : LhsSubNodes) + { + if (FGraphDiffControl::IsNodeMatch(LhsSubNode, RhsSubNode, true, &NodeMatches)) + { + NodeMatch.OldNode = LhsSubNode; + break; + } + } + + if (NodeMatch.NewNode == nullptr) + { + for (UEdGraphNode* LhsSubNode : LhsSubNodes) + { + if (FGraphDiffControl::IsNodeMatch(LhsSubNode, RhsSubNode, false, &NodeMatches)) + { + NodeMatch.OldNode = LhsSubNode; + break; + } + } + } + + // if we found a corresponding node in the lhs graph, track it (so we can prevent future matches with the same nodes) + if (NodeMatch.IsValid()) + { + NodeMatches.Add(NodeMatch); + MatchedRhsNodes.Add(NodeMatch.OldNode); + } + + NodeMatch.Diff(AdditiveDiffContext, Results); + } + + FGraphDiffControl::FNodeDiffContext SubtractiveDiffContext = AdditiveDiffContext; + SubtractiveDiffContext.DiffMode = FGraphDiffControl::EDiffMode::Subtractive; + SubtractiveDiffContext.DiffFlags = FGraphDiffControl::EDiffFlags::NodeExistance; + + // go through the lhs nodes to catch ones that may have been missing from the rhs graph + for (UEdGraphNode* LhsSubNode : LhsSubNodes) + { + // if this node has already been matched, move on + if (!LhsSubNode || MatchedRhsNodes.Find(LhsSubNode)) + { + continue; + } + + // There can't be a matching node in RhsGraph because it would have been found above + FGraphDiffControl::FNodeMatch NodeMatch; + NodeMatch.NewNode = LhsSubNode; + + NodeMatch.Diff(SubtractiveDiffContext, Results); + } +} + +void UFlowGraphNode::AddSubNode(UFlowGraphNode* SubNode, class UEdGraph* ParentGraph) +{ + const FScopedTransaction Transaction(LOCTEXT("AddNode", "Add Node")); + ParentGraph->Modify(); + Modify(); + + SubNode->SetFlags(RF_Transactional); + + // set outer to be the graph so it doesn't go away + SubNode->Rename(nullptr, ParentGraph, REN_NonTransactional); + SubNode->SetParentNodeForSubNode(this); + + SubNode->CreateNewGuid(); + SubNode->PostPlacedNewNode(); + SubNode->AllocateDefaultPins(); + SubNode->AutowireNewNode(nullptr); + + SubNode->NodePosX = 0; + SubNode->NodePosY = 0; + + SubNodes.Add(SubNode); + OnSubNodeAdded(SubNode); + + ParentGraph->NotifyGraphChanged(); + GetFlowGraph()->UpdateAsset(); + + // NOTE - We do not need to RebuildRuntimeAddOnsFromEditorSubNodes here, because UpdateAsset() will do it +} + +void UFlowGraphNode::OnSubNodeAdded(UFlowGraphNode* SubNode) +{ + // Empty in base class +} + +void UFlowGraphNode::RemoveSubNode(UFlowGraphNode* SubNode) +{ + Modify(); + SubNodes.RemoveSingle(SubNode); + + RebuildRuntimeAddOnsFromEditorSubNodes(); + + OnSubNodeRemoved(SubNode); +} + +void UFlowGraphNode::RemoveAllSubNodes() +{ + SubNodes.Reset(); + + RebuildRuntimeAddOnsFromEditorSubNodes(); +} + +void UFlowGraphNode::OnSubNodeRemoved(UFlowGraphNode* SubNode) +{ + // Empty in base class +} + +int32 UFlowGraphNode::FindSubNodeDropIndex(UFlowGraphNode* SubNode) const +{ + const int32 InsertIndex = SubNodes.IndexOfByKey(SubNode); + return InsertIndex; +} + +void UFlowGraphNode::InsertSubNodeAt(UFlowGraphNode* SubNode, int32 DropIndex) +{ + if (DropIndex > -1) + { + SubNodes.Insert(SubNode, DropIndex); + } + else + { + SubNodes.Add(SubNode); + } + + RebuildRuntimeAddOnsFromEditorSubNodes(); +} + +void UFlowGraphNode::DestroyNode() +{ + if (ParentNode) + { + ParentNode->RemoveSubNode(this); + + ParentNode->RebuildRuntimeAddOnsFromEditorSubNodes(); + } + else + { + RebuildRuntimeAddOnsFromEditorSubNodes(); + } + + UEdGraphNode::DestroyNode(); +} + +bool UFlowGraphNode::UsesBlueprint() const +{ + return NodeInstance && NodeInstance->GetClass()->HasAnyClassFlags(CLASS_CompiledFromBlueprint); +} + +bool UFlowGraphNode::RefreshNodeClass() +{ + bool bUpdated = false; + if (NodeInstance == nullptr) + { + if (NodeInstanceClass.IsPending()) + { + NodeInstanceClass.LoadSynchronous(); + } + + if (NodeInstanceClass.IsValid()) + { + PostPlacedNewNode(); + + bUpdated = (NodeInstance != nullptr); + } + } + + return bUpdated; +} + +void UFlowGraphNode::UpdateNodeClassData() +{ + if (NodeInstance) + { + NodeInstanceClass = NodeInstance->GetClass(); + } +} + +bool UFlowGraphNode::HasErrors() const +{ + return ErrorMessage.Len() > 0 || !IsValid(NodeInstance); +} + +bool UFlowGraphNode::IsAncestorNode(const UFlowGraphNode& OtherNode) const +{ + const UFlowGraphNode* CurParentNode = ParentNode; + while (CurParentNode) + { + if (CurParentNode == &OtherNode) + { + return true; + } + + CurParentNode = CurParentNode->ParentNode; + } + + return false; +} + +bool UFlowGraphNode::CanAcceptSubNodeAsChild(const UFlowGraphNode& OtherSubNode, FString* OutReasonString) const +{ + const UFlowNodeBase* OtherFlowNodeSubNode = OtherSubNode.NodeInstance; + + if (!OtherFlowNodeSubNode) + { + if (OutReasonString) + { + *OutReasonString = TEXT("Editor node is missing a runtime AddOn instance"); + } + + return false; + } + + if (IsAncestorNode(OtherSubNode)) + { + if (OutReasonString) + { + *OutReasonString = TEXT("Cannot be a AddOn of one of our own AddOns"); + } + + return false; + } + + check(OtherFlowNodeSubNode); + const UFlowNodeAddOn* OtherAddOnTemplate = Cast(OtherFlowNodeSubNode); + + const UFlowNodeBase* ThisFlowNodeBase = NodeInstance; + const EFlowAddOnAcceptResult AcceptResult = ThisFlowNodeBase->CheckAcceptFlowNodeAddOnChild(OtherAddOnTemplate); + + // Undetermined and Reject both count as Rejection, only TentativeAccept is an 'accept' result + + if (AcceptResult == EFlowAddOnAcceptResult::TentativeAccept) + { + static_assert(static_cast<__underlying_type(EFlowAddOnAcceptResult)>(EFlowAddOnAcceptResult::Max) == 3, "This code may need updating if the enum values change"); + + return true; + } + + if (OutReasonString) + { + *OutReasonString = FString::Printf(TEXT("%s cannot accept AddOn type %s"), *GetClass()->GetName(), *OtherFlowNodeSubNode->GetClass()->GetName()); + } + + return false; +} + #undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Branch.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Branch.cpp new file mode 100644 index 000000000..64b94ae75 --- /dev/null +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Branch.cpp @@ -0,0 +1,18 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Graph/Nodes/FlowGraphNode_Branch.h" +#include "Nodes/Route/FlowNode_Branch.h" + +#include "Textures/SlateIcon.h" + +UFlowGraphNode_Branch::UFlowGraphNode_Branch(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ + AssignedNodeClasses = {UFlowNode_Branch::StaticClass()}; +} + +FSlateIcon UFlowGraphNode_Branch::GetIconAndTint(FLinearColor& OutColor) const +{ + static FSlateIcon Icon("FlowEditorStyle", "GraphEditor.Branch_16x"); + return Icon; +} diff --git a/Source/FlowEditor/Private/Graph/Widgets/DragFlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Widgets/DragFlowGraphNode.cpp new file mode 100644 index 000000000..1e033dfbf --- /dev/null +++ b/Source/FlowEditor/Private/Graph/Widgets/DragFlowGraphNode.cpp @@ -0,0 +1,35 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "DragFlowGraphNode.h" +#include "Framework/Application/SlateApplication.h" +#include "Graph/Nodes/FlowGraphNode.h" + +TSharedRef FDragFlowGraphNode::New(const TSharedRef& InGraphPanel, const TSharedRef& InDraggedNode) +{ + TSharedRef Operation = MakeShareable(new FDragFlowGraphNode); + + Operation->StartTime = FPlatformTime::Seconds(); + Operation->GraphPanel = InGraphPanel; + Operation->DraggedNodes.Add(InDraggedNode); + // adjust the decorator away from the current mouse location a small amount based on cursor size + Operation->DecoratorAdjust = FSlateApplication::Get().GetCursorSize(); + Operation->Construct(); + + return Operation; +} + +TSharedRef FDragFlowGraphNode::New(const TSharedRef& InGraphPanel, const TArray< TSharedRef >& InDraggedNodes) +{ + TSharedRef Operation = MakeShareable(new FDragFlowGraphNode); + Operation->StartTime = FPlatformTime::Seconds(); + Operation->GraphPanel = InGraphPanel; + Operation->DraggedNodes = InDraggedNodes; + Operation->DecoratorAdjust = FSlateApplication::Get().GetCursorSize(); + Operation->Construct(); + return Operation; +} + +UFlowGraphNode* FDragFlowGraphNode::GetDropTargetNode() const +{ + return Cast(GetHoveredNode()); +} diff --git a/Source/FlowEditor/Private/Graph/Widgets/DragFlowGraphNode.h b/Source/FlowEditor/Private/Graph/Widgets/DragFlowGraphNode.h new file mode 100644 index 000000000..c57afc324 --- /dev/null +++ b/Source/FlowEditor/Private/Graph/Widgets/DragFlowGraphNode.h @@ -0,0 +1,26 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Editor/GraphEditor/Private/DragNode.h" +#include "Templates/SharedPointer.h" + +class SGraphPanel; +class UFlowGraphNode; + +// Adapted from FDragAIGraphNode +class FDragFlowGraphNode : public FDragNode +{ +public: + DRAG_DROP_OPERATOR_TYPE(FDragFlowGraphNode, FDragNode) + + static TSharedRef New(const TSharedRef& InGraphPanel, const TSharedRef& InDraggedNode); + static TSharedRef New(const TSharedRef& InGraphPanel, const TArray>& InDraggedNodes); + + UFlowGraphNode* GetDropTargetNode() const; + + double StartTime; + +protected: + typedef FDragNode Super; +}; \ No newline at end of file diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp index 0038cc5f4..9bb5d99d0 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp @@ -1,9 +1,10 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "Graph/Widgets/SFlowGraphNode.h" +#include "DragFlowGraphNode.h" #include "FlowEditorStyle.h" +#include "Graph/FlowGraph.h" #include "Graph/FlowGraphSettings.h" - #include "Nodes/FlowNode.h" #include "EdGraph/EdGraphPin.h" @@ -11,10 +12,14 @@ #include "GraphEditorSettings.h" #include "IDocumentation.h" #include "Input/Reply.h" +#include "Internationalization/BreakIterator.h" #include "Layout/Margin.h" #include "Misc/Attribute.h" +#include "NodeFactory.h" #include "SCommentBubble.h" +#include "ScopedTransaction.h" #include "SGraphNode.h" +#include "SGraphPanel.h" #include "SGraphPin.h" #include "SlateOptMacros.h" #include "SLevelOfDetailBranchNode.h" @@ -42,6 +47,9 @@ void SFlowGraphPinExec::Construct(const FArguments& InArgs, UEdGraphPin* InPin) bUsePinColorForText = true; } +const FLinearColor SFlowGraphNode::UnselectedNodeTint = FLinearColor(1.0f, 1.0f, 1.0f, 0.5f); +const FLinearColor SFlowGraphNode::ConfigBoxColor = FLinearColor(0.04f, 0.04f, 0.04f, 1.0f); + void SFlowGraphNode::Construct(const FArguments& InArgs, UFlowGraphNode* InNode) { GraphNode = InNode; @@ -51,6 +59,8 @@ void SFlowGraphNode::Construct(const FArguments& InArgs, UFlowGraphNode* InNode) SetCursor(EMouseCursor::CardinalCross); UpdateGraphNode(); + + bDragMarkerVisible = false; } void SFlowGraphNode::GetNodeInfoPopups(FNodeInfoContext* Context, TArray& Popups) const @@ -193,6 +203,9 @@ void SFlowGraphNode::UpdateGraphNode() IconBrush = GraphNode->GetIconAndTint(IconColor).GetOptionalIcon(); } + // Compute the SubNode padding indent based on the parentage depth for this node + const FMargin NodePadding = ComputeSubNodeChildIndentPaddingMargin(); + const TSharedRef DefaultTitleAreaWidget = SNew(SOverlay) + SOverlay::Slot() .HAlign(HAlign_Fill) @@ -206,7 +219,7 @@ void SFlowGraphNode::UpdateGraphNode() .BorderImage(FFlowEditorStyle::GetBrush("Flow.Node.Title")) // The extra margin on the right is for making the color spill stretch well past the node title .Padding(FMargin(10, 5, 30, 3)) - .BorderBackgroundColor(this, &SGraphNode::GetNodeTitleColor) + .BorderBackgroundColor(this, &SFlowGraphNode::GetBorderBackgroundColor) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() @@ -303,6 +316,7 @@ void SFlowGraphNode::UpdateGraphNode() SAssignNew(MainVerticalBox, SVerticalBox) + SVerticalBox::Slot() .AutoHeight() + .Padding(FMargin(NodePadding.Left, 0.0f, NodePadding.Right, 0.0f)) [ SNew(SOverlay) .AddMetaData(TagMeta) @@ -356,14 +370,186 @@ void SFlowGraphNode::UpdateGraphNode() CreateAdvancedViewArrow(InnerVerticalBox); } +FSlateColor SFlowGraphNode::GetBorderBackgroundColor() const +{ + return SGraphNode::GetNodeTitleColor(); +} + +FSlateColor SFlowGraphNode::GetConfigBoxBackgroundColor() const +{ + FLinearColor NodeColor = ConfigBoxColor; + + if (FlowGraphNode && !IsFlowGraphNodeSelected(FlowGraphNode)) + { + NodeColor *= UnselectedNodeTint; + } + + return NodeColor; +} + +void SFlowGraphNode::CreateBelowPinControls(TSharedPtr InnerVerticalBox) +{ + static const FMargin ConfigBoxPadding = FMargin(2.0f, 0.0f, 1.0f, 0.0); + + // Add a box to wrap around the Config Text area to make it a more visually distinct part of the node + TSharedPtr BelowPinsBox; + InnerVerticalBox->AddSlot() + .AutoHeight() + .Padding(ConfigBoxPadding) + [ + SNew(SBorder) + .BorderImage(FAppStyle::GetBrush("Graph.StateNode.Body")) + .BorderBackgroundColor(this, &SFlowGraphNode::GetConfigBoxBackgroundColor) + .Visibility(this, &SFlowGraphNode::GetNodeConfigTextVisibility) + [ + SAssignNew(BelowPinsBox, SVerticalBox) + ] + ]; + + CreateConfigText(BelowPinsBox); + + CreateOrRebuildSubNodeBox(InnerVerticalBox); +} + +void SFlowGraphNode::AddSubNodeWidget(TSharedPtr NewSubNodeWidget) +{ + if (OwnerGraphPanelPtr.IsValid()) + { + NewSubNodeWidget->SetOwner(OwnerGraphPanelPtr.Pin().ToSharedRef()); + OwnerGraphPanelPtr.Pin()->AttachGraphEvents(NewSubNodeWidget); + } + NewSubNodeWidget->UpdateGraphNode(); + + AddSubNode(NewSubNodeWidget); +} + +FMargin SFlowGraphNode::ComputeSubNodeChildIndentPaddingMargin() const +{ + if (!IsValid(FlowGraphNode) || !FlowGraphNode->IsSubNode()) + { + return FMargin(); + } + + const UFlowGraphNode* CurrentAncestor = FlowGraphNode->GetParentNode(); + + // Compute the parent depth, so it can be used to determine the indent level for this subnode + int32 ParentDepth = 0; + while (IsValid(CurrentAncestor)) + { + ++ParentDepth; + + CurrentAncestor = CurrentAncestor->GetParentNode(); + } + + constexpr float VerticalDefaultPadding = 2.0f; + constexpr float HorizontalDefaultPadding = 2.0f; + constexpr float IndentedHorizontalPadding = 6.0f; + constexpr float RightPadding = HorizontalDefaultPadding; + float LeftPadding = HorizontalDefaultPadding; + + if (ParentDepth > 0) + { + // Increase the padding by the parent depth for this node + LeftPadding = IndentedHorizontalPadding * ParentDepth; + } + else + { + LeftPadding = 0.0f; + } + + return + FMargin( + LeftPadding, + VerticalDefaultPadding, + RightPadding, + VerticalDefaultPadding); +} + +void SFlowGraphNode::CreateConfigText(TSharedPtr InnerVerticalBox) +{ + static const FMargin ConfigTextPadding = FMargin(2.0f, 0.0f, 0.0f, 3.0f); + + InnerVerticalBox->AddSlot() + .AutoHeight() + .Padding(ConfigTextPadding) + [ + SAssignNew(ConfigTextBlock, STextBlock) + .AutoWrapText(true) + .LineBreakPolicy(FBreakIterator::CreateWordBreakIterator()) + .Text(this, &SFlowGraphNode::GetNodeConfigText) + ]; +} + +FText SFlowGraphNode::GetNodeConfigText() const +{ + if (const UFlowGraphNode* MyNode = CastChecked(GraphNode)) + { + if (UFlowNodeBase* NodeInstance = MyNode->GetFlowNodeBase()) + { + return NodeInstance->GetNodeConfigText(); + } + } + + return FText::GetEmpty(); +} + +EVisibility SFlowGraphNode::GetNodeConfigTextVisibility() const +{ + // Hide in lower LODs + const TSharedPtr OwnerPanel = GetOwnerPanel(); + if (!OwnerPanel.IsValid() || OwnerPanel->GetCurrentLOD() > EGraphRenderingLOD::MediumDetail) + { + if (ConfigTextBlock && !ConfigTextBlock->GetText().IsEmptyOrWhitespace()) + { + return EVisibility::Visible; + } + } + + return EVisibility::Collapsed; +} + +void SFlowGraphNode::CreateOrRebuildSubNodeBox(TSharedPtr InnerVerticalBox) +{ + if (SubNodeBox.IsValid()) + { + SubNodeBox->ClearChildren(); + } + else + { + SAssignNew(SubNodeBox, SVerticalBox); + } + + SubNodes.Reset(); + + if (FlowGraphNode) + { + for (UFlowGraphNode* SubNode : FlowGraphNode->SubNodes) + { + TSharedPtr NewNode = FNodeFactory::CreateNodeWidget(SubNode); + AddSubNodeWidget(NewNode); + } + } + + InnerVerticalBox->AddSlot() + .AutoHeight() + [ + SubNodeBox.ToSharedRef() + ]; +} + +bool SFlowGraphNode::IsFlowGraphNodeSelected(UFlowGraphNode* Node) const +{ + return GetOwnerPanel().IsValid() && GetOwnerPanel()->SelectionManager.SelectedNodes.Contains(Node); +} + void SFlowGraphNode::UpdateErrorInfo() { - if (const UFlowNode* FlowNode = FlowGraphNode->GetFlowNode()) + if (const UFlowNodeBase* FlowNodeBase = FlowGraphNode->GetFlowNodeBase()) { - if (FlowNode->ValidationLog.Messages.Num() > 0) + if (FlowNodeBase->ValidationLog.Messages.Num() > 0) { EMessageSeverity::Type MaxSeverity = EMessageSeverity::Info; - for (const TSharedRef& Message : FlowNode->ValidationLog.Messages) + for (const TSharedRef& Message : FlowNodeBase->ValidationLog.Messages) { if (Message->GetSeverity() < MaxSeverity) { @@ -386,15 +572,16 @@ void SFlowGraphNode::UpdateErrorInfo() ErrorMsg = FString(TEXT("NOTE")); ErrorColor = FAppStyle::GetColor("InfoReporting.BackgroundColor"); break; - default: ; + default: + break; } return; } - if (FlowNode->GetClass()->HasAnyClassFlags(CLASS_Deprecated) || FlowNode->bNodeDeprecated) + if (FlowNodeBase->GetClass()->HasAnyClassFlags(CLASS_Deprecated) || FlowNodeBase->bNodeDeprecated) { - ErrorMsg = FlowNode->ReplacedBy ? FString::Printf(TEXT(" REPLACED BY: %s "), *FlowNode->ReplacedBy->GetName()) : FString(TEXT(" DEPRECATED! ")); + ErrorMsg = FlowNodeBase->ReplacedBy ? FString::Printf(TEXT(" REPLACED BY: %s "), *FlowNodeBase->ReplacedBy->GetName()) : FString(TEXT(" DEPRECATED! ")); ErrorColor = FAppStyle::GetColor("ErrorReporting.WarningBackgroundColor"); return; } @@ -458,6 +645,11 @@ FSlateColor SFlowGraphNode::GetNodeTitleColor() const ReturnTitleColor *= FLinearColor(0.5f, 0.5f, 0.5f, 0.4f); } + if (!IsFlowGraphNodeSelected(FlowGraphNode) && FlowGraphNode->IsSubNode()) + { + ReturnTitleColor *= UnselectedNodeTint; + } + return ReturnTitleColor; } @@ -468,6 +660,11 @@ FSlateColor SFlowGraphNode::GetNodeBodyColor() const { ReturnBodyColor *= FLinearColor(1.0f, 1.0f, 1.0f, 0.5f); } + else if (!IsFlowGraphNodeSelected(FlowGraphNode) && FlowGraphNode->IsSubNode()) + { + ReturnBodyColor *= UnselectedNodeTint; + } + return ReturnBodyColor; } @@ -478,6 +675,11 @@ FSlateColor SFlowGraphNode::GetNodeTitleIconColor() const { ReturnIconColor *= FLinearColor(1.0f, 1.0f, 1.0f, 0.3f); } + else if (!IsFlowGraphNodeSelected(FlowGraphNode) && FlowGraphNode->IsSubNode()) + { + ReturnIconColor *= UnselectedNodeTint; + } + return ReturnIconColor; } @@ -488,11 +690,24 @@ FLinearColor SFlowGraphNode::GetNodeTitleTextColor() const { ReturnTextColor *= FLinearColor(1.0f, 1.0f, 1.0f, 0.3f); } + else if (!IsFlowGraphNodeSelected(FlowGraphNode) && FlowGraphNode->IsSubNode()) + { + ReturnTextColor *= UnselectedNodeTint; + } + return ReturnTextColor; } TSharedPtr SFlowGraphNode::GetEnabledStateWidget() const { + if (FlowGraphNode->IsSubNode()) + { + // SubNodes don't get enabled/disabled on their own, + // they follow the enabled/disabled setting of their owning flow node + + return TSharedPtr(); + } + if (FlowGraphNode->GetSignalMode() != EFlowSignalMode::Enabled && !GraphNode->IsAutomaticallyPlacedGhostNode()) { const bool bPassThrough = FlowGraphNode->GetSignalMode() == EFlowSignalMode::PassThrough; @@ -523,20 +738,20 @@ void SFlowGraphNode::CreateStandardPinWidget(UEdGraphPin* Pin) { const TSharedPtr NewPin = SNew(SFlowGraphPinExec, Pin); - if (!UFlowGraphSettings::Get()->bShowDefaultPinNames && FlowGraphNode->GetFlowNode()) + UFlowNode* FlowNode = Cast(FlowGraphNode->GetFlowNodeBase()); + + if (!UFlowGraphSettings::Get()->bShowDefaultPinNames && IsValid(FlowNode)) { if (Pin->Direction == EGPD_Input) { - // Pin array can have pins with name None, which will not be created. We need to check if array have only one valid pin - if (ValidPinsCount(FlowGraphNode->GetFlowNode()->GetInputPins()) == 1 && Pin->PinName == UFlowNode::DefaultInputPin.PinName) + if (FlowNode->GetInputPins().Num() == 1 && Pin->PinName == UFlowNode::DefaultInputPin.PinName) { NewPin->SetShowLabel(false); } } else { - // Pin array can have pins with name None, which will not be created. We need to check if array have only one valid pin - if (ValidPinsCount(FlowGraphNode->GetFlowNode()->GetOutputPins()) == 1 && Pin->PinName == UFlowNode::DefaultOutputPin.PinName) + if (FlowNode->GetOutputPins().Num() == 1 && Pin->PinName == UFlowNode::DefaultOutputPin.PinName) { NewPin->SetShowLabel(false); } @@ -559,22 +774,22 @@ void SFlowGraphNode::CreateInputSideAddButton(TSharedPtr OutputBox { TSharedPtr AddPinWidget; SAssignNew(AddPinWidget, SHorizontalBox) - +SHorizontalBox::Slot() - .AutoWidth() - . VAlign(VAlign_Center) - . Padding( 0,0,7,0 ) - [ - SNew(SImage) - .Image(FAppStyle::GetBrush(TEXT("Icons.PlusCircle"))) - ] - +SHorizontalBox::Slot() - .AutoWidth() - .HAlign(HAlign_Left) - [ - SNew(STextBlock) - .Text(LOCTEXT("FlowNodeAddPinButton", "Add pin")) - .ColorAndOpacity(FLinearColor::White) - ]; + +SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Center) + .Padding(0, 0, 7, 0) + [ + SNew(SImage) + .Image(FAppStyle::GetBrush(TEXT("Icons.PlusCircle"))) + ] + +SHorizontalBox::Slot() + .AutoWidth() + .HAlign(HAlign_Left) + [ + SNew(STextBlock) + .Text(LOCTEXT("FlowNodeAddPinButton", "Add pin")) + .ColorAndOpacity(FLinearColor::White) + ]; AddPinButton(OutputBox, AddPinWidget.ToSharedRef(), EGPD_Input); } @@ -586,22 +801,22 @@ void SFlowGraphNode::CreateOutputSideAddButton(TSharedPtr OutputBo { TSharedPtr AddPinWidget; SAssignNew(AddPinWidget, SHorizontalBox) - +SHorizontalBox::Slot() - .AutoWidth() - .HAlign(HAlign_Left) - [ - SNew(STextBlock) - .Text(LOCTEXT("FlowNodeAddPinButton", "Add pin")) - .ColorAndOpacity(FLinearColor::White) - ] - +SHorizontalBox::Slot() - .AutoWidth() - .VAlign(VAlign_Center) - .Padding(7,0,0,0) - [ - SNew(SImage) - .Image(FAppStyle::GetBrush(TEXT("Icons.PlusCircle"))) - ]; + +SHorizontalBox::Slot() + .AutoWidth() + .HAlign(HAlign_Left) + [ + SNew(STextBlock) + .Text(LOCTEXT("FlowNodeAddPinButton", "Add pin")) + .ColorAndOpacity(FLinearColor::White) + ] + +SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Center) + .Padding(7, 0, 0, 0) + [ + SNew(SImage) + .Image(FAppStyle::GetBrush(TEXT("Icons.PlusCircle"))) + ]; AddPinButton(OutputBox, AddPinWidget.ToSharedRef(), EGPD_Output); } @@ -622,16 +837,16 @@ void SFlowGraphNode::AddPinButton(TSharedPtr OutputBox, const TSha } const TSharedRef AddPinButton = SNew(SButton) - .ContentPadding(0.0f) - .ButtonStyle(FAppStyle::Get(), "NoBorder") - .OnClicked(this, &SFlowGraphNode::OnAddFlowPin, Direction) - .IsEnabled(this, &SFlowGraphNode::IsNodeEditable) - .ToolTipText(PinTooltipText) - .ToolTip(Tooltip) - .Visibility(this, &SFlowGraphNode::IsAddPinButtonVisible) - [ - ButtonContent - ]; + .ContentPadding(0.0f) + .ButtonStyle(FAppStyle::Get(), "NoBorder") + .OnClicked(this, &SFlowGraphNode::OnAddFlowPin, Direction) + .IsEnabled(this, &SFlowGraphNode::IsNodeEditable) + .ToolTipText(PinTooltipText) + .ToolTip(Tooltip) + .Visibility(this, &SFlowGraphNode::IsAddPinButtonVisible) + [ + ButtonContent + ]; AddPinButton->SetCursor(EMouseCursor::Hand); @@ -657,24 +872,365 @@ FReply SFlowGraphNode::OnAddFlowPin(const EEdGraphPinDirection Direction) case EGPD_Output: FlowGraphNode->AddUserOutput(); break; - default: ; + default: + break; } return FReply::Handled(); } -int32 SFlowGraphNode::ValidPinsCount(const TArray& Pins) +void SFlowGraphNode::AddSubNode(TSharedPtr SubNodeWidget) +{ + SubNodes.Add(SubNodeWidget); + + SubNodeBox->AddSlot().AutoHeight() + [ + SubNodeWidget.ToSharedRef() + ]; +} + +FText SFlowGraphNode::GetTitle() const +{ + return GraphNode ? GraphNode->GetNodeTitle(ENodeTitleType::FullTitle) : FText::GetEmpty(); +} + +FText SFlowGraphNode::GetDescription() const { - int32 Count = 0; - for (const FFlowPin& Pin : Pins) + UFlowGraphNode* MyNode = CastChecked(GraphNode); + return MyNode ? MyNode->GetDescription() : FText::GetEmpty(); +} + +EVisibility SFlowGraphNode::GetDescriptionVisibility() const +{ + // LOD this out once things get too small + TSharedPtr MyOwnerPanel = GetOwnerPanel(); + return (!MyOwnerPanel.IsValid() || MyOwnerPanel->GetCurrentLOD() > EGraphRenderingLOD::LowDetail) ? EVisibility::Visible : EVisibility::Collapsed; +} + +void SFlowGraphNode::AddPin(const TSharedRef& PinToAdd) +{ + PinToAdd->SetOwner(SharedThis(this)); + + const UEdGraphPin* PinObj = PinToAdd->GetPinObj(); + const bool bAdvancedParameter = PinObj && PinObj->bAdvancedView; + if (bAdvancedParameter) { - if (Pin.IsValid()) + PinToAdd->SetVisibility(TAttribute(PinToAdd, &SGraphPin::IsPinVisibleAsAdvanced)); + } + + if (PinToAdd->GetDirection() == EEdGraphPinDirection::EGPD_Input) + { + LeftNodeBox->AddSlot() + .HAlign(HAlign_Fill) + .VAlign(VAlign_Fill) + .FillHeight(1.0f) + [ + PinToAdd + ]; + InputPins.Add(PinToAdd); + } + else // Direction == EEdGraphPinDirection::EGPD_Output + { + RightNodeBox->AddSlot() + .HAlign(HAlign_Fill) + .VAlign(VAlign_Fill) + .FillHeight(1.0f) + [ + PinToAdd + ]; + OutputPins.Add(PinToAdd); + } +} + +FReply SFlowGraphNode::OnMouseMove(const FGeometry& SenderGeometry, const FPointerEvent& MouseEvent) +{ + if (MouseEvent.IsMouseButtonDown(EKeys::LeftMouseButton) && !(GEditor->bIsSimulatingInEditor || GEditor->PlayWorld)) + { + // if we are holding mouse over a subnode + UFlowGraphNode* TestNode = Cast(GraphNode); + if (TestNode && TestNode->IsSubNode()) + { + const TSharedRef& Panel = GetOwnerPanel().ToSharedRef(); + const TSharedRef& Node = SharedThis(this); + return FReply::Handled().BeginDragDrop(FDragFlowGraphNode::New(Panel, Node)); + } + } + + if (!MouseEvent.IsMouseButtonDown(EKeys::LeftMouseButton) && bDragMarkerVisible) + { + SetDragMarker(false); + } + + return FReply::Unhandled(); +} + +TSharedRef SFlowGraphNode::GetNodeUnderMouse(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) +{ + const TSharedPtr SubNode = GetSubNodeUnderCursor(MyGeometry, MouseEvent); + if (SubNode.IsValid()) + { + return SubNode.ToSharedRef(); + } + else + { + return StaticCastSharedRef(AsShared()); + } +} + +FReply SFlowGraphNode::OnMouseButtonDown(const FGeometry& SenderGeometry, const FPointerEvent& MouseEvent) +{ + UFlowGraphNode* TestNode = Cast(GraphNode); + if (TestNode && TestNode->IsSubNode()) + { + GetOwnerPanel()->SelectionManager.ClickedOnNode(GraphNode, MouseEvent); + return FReply::Handled(); + } + + return FReply::Unhandled(); +} + +TSharedPtr SFlowGraphNode::GetSubNodeUnderCursor(const FGeometry& WidgetGeometry, const FPointerEvent& MouseEvent) +{ + // We just need to find the one WidgetToFind among our descendants. + TSet< TSharedRef > SubWidgetsSet; + for (int32 i = 0; i < SubNodes.Num(); i++) + { + SubWidgetsSet.Add(SubNodes[i].ToSharedRef()); + } + + TMap, FArrangedWidget> Result; + FindChildGeometries(WidgetGeometry, SubWidgetsSet, Result); + + TSharedPtr ResultNode; + + if (Result.Num() <= 0) + { + return ResultNode; + } + + FArrangedChildren ArrangedChildren(EVisibility::Visible); + Result.GenerateValueArray(ArrangedChildren.GetInternalArray()); + + const int32 HoveredIndex = SWidget::FindChildUnderMouse(ArrangedChildren, MouseEvent); + if (HoveredIndex == INDEX_NONE) + { + return ResultNode; + } + + ResultNode = StaticCastSharedRef(ArrangedChildren[HoveredIndex].Widget); + + // Recurse if the subnode has subnodes + SFlowGraphNode* ResultFlowGraphNode = static_cast(ResultNode.Get()); + const FGeometry& ChildWidgetGeometry = ArrangedChildren[HoveredIndex].Geometry; + TSharedPtr ResultFlowGraphNodeSubNode = ResultFlowGraphNode->GetSubNodeUnderCursor(ChildWidgetGeometry, MouseEvent); + + if (ResultFlowGraphNodeSubNode) + { + return ResultFlowGraphNodeSubNode; + } + + return ResultNode; +} + +void SFlowGraphNode::SetDragMarker(bool bEnabled) +{ + bDragMarkerVisible = bEnabled; +} + +EVisibility SFlowGraphNode::GetDragOverMarkerVisibility() const +{ + return bDragMarkerVisible ? EVisibility::Visible : EVisibility::Collapsed; +} + +void SFlowGraphNode::OnDragEnter(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) +{ + // Is someone dragging a node? + TSharedPtr DragConnectionOp = DragDropEvent.GetOperationAs(); + if (DragConnectionOp.IsValid()) + { + // Inform the Drag and Drop operation that we are hovering over this node. + TSharedPtr SubNode = GetSubNodeUnderCursor(MyGeometry, DragDropEvent); + DragConnectionOp->SetHoveredNode(SubNode.IsValid() ? SubNode : SharedThis(this)); + + UFlowGraphNode* TestNode = Cast(GraphNode); + if (DragConnectionOp->IsValidOperation() && TestNode && TestNode->IsSubNode()) + { + SetDragMarker(true); + } + } + + SGraphNode::OnDragEnter(MyGeometry, DragDropEvent); +} + +FReply SFlowGraphNode::OnDragOver(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) +{ + // Is someone dragging a node? + TSharedPtr DragConnectionOp = DragDropEvent.GetOperationAs(); + if (DragConnectionOp.IsValid()) + { + // Inform the Drag and Drop operation that we are hovering over this node. + TSharedPtr SubNode = GetSubNodeUnderCursor(MyGeometry, DragDropEvent); + DragConnectionOp->SetHoveredNode(SubNode.IsValid() ? SubNode : SharedThis(this)); + } + return SGraphNode::OnDragOver(MyGeometry, DragDropEvent); +} + +void SFlowGraphNode::OnDragLeave(const FDragDropEvent& DragDropEvent) +{ + TSharedPtr DragConnectionOp = DragDropEvent.GetOperationAs(); + if (DragConnectionOp.IsValid()) + { + // Inform the Drag and Drop operation that we are not hovering any pins + DragConnectionOp->SetHoveredNode(TSharedPtr(NULL)); + } + + SetDragMarker(false); + SGraphNode::OnDragLeave(DragDropEvent); +} + +FReply SFlowGraphNode::OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) +{ + SetDragMarker(false); + + TSharedPtr DragNodeOp = DragDropEvent.GetOperationAs(); + if (DragNodeOp.IsValid()) + { + if (!DragNodeOp->IsValidOperation()) + { + return FReply::Handled(); + } + + const float DragTime = float(FPlatformTime::Seconds() - DragNodeOp->StartTime); + if (DragTime < 0.5f) + { + return FReply::Handled(); + } + + UFlowGraphNode* MyNode = Cast(GraphNode); + if (MyNode == nullptr) + { + return FReply::Unhandled(); + } + + const FScopedTransaction Transaction(NSLOCTEXT("UnrealEd", "GraphEd_DragDropNode", "Drag&Drop Node")); + + UFlowGraphNode* DropTargetNode = DragNodeOp->GetDropTargetNode(); + check(DropTargetNode); + + bool bReorderOperation = true; + const TArray< TSharedRef >& DraggedNodes = DragNodeOp->GetNodes(); + RemoveDraggedSubNodes(DraggedNodes, bReorderOperation); + + bool bShouldDropAsSubNodesOfDropTargetNode = bReorderOperation || ShouldDropDraggedNodesAsSubNodes(DraggedNodes, DropTargetNode); + + // Setup the DropTarget pointers based on the type of drop we've decided to do: + UFlowGraphNode* DropTargetParentNode = MyNode; + + if (bShouldDropAsSubNodesOfDropTargetNode) + { + DropTargetParentNode = DropTargetNode; + DropTargetNode = nullptr; + } + else + { + DropTargetParentNode = DropTargetNode->GetParentNode(); + } + + check(DropTargetParentNode); + + const int32 InsertIndex = DropTargetParentNode->FindSubNodeDropIndex(DropTargetNode); + + for (int32 Idx = 0; Idx < DraggedNodes.Num(); Idx++) + { + UFlowGraphNode* DraggedTestNode = Cast(DraggedNodes[Idx]->GetNodeObj()); + DraggedTestNode->Modify(); + DraggedTestNode->SetParentNodeForSubNode(DropTargetParentNode); + + DropTargetParentNode->Modify(); + DropTargetParentNode->InsertSubNodeAt(DraggedTestNode, InsertIndex); + + DropTargetParentNode->OnSubNodeAdded(DraggedTestNode); + } + + if (bReorderOperation) + { + UpdateGraphNode(); + } + else + { + UFlowGraph* MyGraph = DropTargetParentNode->GetFlowGraph(); + if (DropTargetParentNode) + { + MyGraph->OnSubNodeDropped(); + } + } + } + + return SGraphNode::OnDrop(MyGeometry, DragDropEvent); +} + +bool SFlowGraphNode::ShouldDropDraggedNodesAsSubNodes(const TArray>& DraggedNodes, UFlowGraphNode* DropTargetNode) const +{ + for (int32 Idx = 0; Idx < DraggedNodes.Num(); Idx++) + { + UFlowGraphNode* DraggedNode = Cast(DraggedNodes[Idx]->GetNodeObj()); + if (!DraggedNode) + { + continue; + } + + // Check if all of the dragged nodes can be stopped as a subnode + // (if not ALL, then we cannot drop ANY of them) + const bool bCanDropDraggedNodeAsSubNode = DropTargetNode->CanAcceptSubNodeAsChild(*DraggedNode); + + if (!bCanDropDraggedNodeAsSubNode) { - Count += 1; + return false; } } - return Count; + return true; +} + +void SFlowGraphNode::RemoveDraggedSubNodes(const TArray< TSharedRef >& DraggedNodes, bool& bInOutReorderOperation) +{ + for (int32 Idx = 0; Idx < DraggedNodes.Num(); Idx++) + { + UFlowGraphNode* DraggedNode = Cast(DraggedNodes[Idx]->GetNodeObj()); + if (DraggedNode && DraggedNode->GetParentNode()) + { + if (DraggedNode->GetParentNode() != GraphNode) + { + bInOutReorderOperation = false; + } + + DraggedNode->GetParentNode()->RemoveSubNode(DraggedNode); + } + } +} + +FText SFlowGraphNode::GetPreviewCornerText() const +{ + return FText::GetEmpty(); +} + +const FSlateBrush* SFlowGraphNode::GetNameIcon() const +{ + return FAppStyle::GetBrush(TEXT("Graph.StateNode.Icon")); +} + +void SFlowGraphNode::SetOwner(const TSharedRef& OwnerPanel) +{ + SGraphNode::SetOwner(OwnerPanel); + + for (auto& ChildWidget : SubNodes) + { + if (ChildWidget.IsValid()) + { + ChildWidget->SetOwner(OwnerPanel); + OwnerPanel->AttachGraphEvents(ChildWidget); + } + } } #undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode_SubGraph.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode_SubGraph.cpp index 6ec6a11c3..0c601dc45 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode_SubGraph.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode_SubGraph.cpp @@ -16,7 +16,7 @@ TSharedPtr SFlowGraphNode_SubGraph::GetComplexTooltip() { if (UFlowGraphEditorSettings::Get()->bShowSubGraphPreview && FlowGraphNode) { - if (UFlowNode* FlowNode = FlowGraphNode->GetFlowNode()) + if (UFlowNode* FlowNode = Cast(FlowGraphNode->GetFlowNodeBase())) { const UFlowAsset* AssetToEdit = Cast(FlowNode->GetAssetToEdit()); if (AssetToEdit && AssetToEdit->GetGraph()) diff --git a/Source/FlowEditor/Private/Graph/Widgets/SGraphEditorActionMenuFlow.cpp b/Source/FlowEditor/Private/Graph/Widgets/SGraphEditorActionMenuFlow.cpp new file mode 100644 index 000000000..328c9beb1 --- /dev/null +++ b/Source/FlowEditor/Private/Graph/Widgets/SGraphEditorActionMenuFlow.cpp @@ -0,0 +1,108 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Graph/Widgets/SGraphEditorActionMenuFlow.h" + +#include "Graph/Nodes/FlowGraphNode.h" +#include "Graph/FlowGraphSchema.h" + +#include "EdGraph/EdGraph.h" +#include "Framework/Application/SlateApplication.h" +#include "HAL/Platform.h" +#include "HAL/PlatformCrt.h" +#include "Layout/Margin.h" +#include "Misc/Attribute.h" +#include "SGraphActionMenu.h" +#include "Styling/AppStyle.h" +#include "Templates/Casts.h" +#include "Types/SlateStructs.h" +#include "Widgets/Layout/SBox.h" + +SGraphEditorActionMenuFlow::~SGraphEditorActionMenuFlow() +{ + OnClosedCallback.ExecuteIfBound(); +} + +void SGraphEditorActionMenuFlow::Construct( const FArguments& InArgs ) +{ + this->GraphObj = InArgs._GraphObj; + this->GraphNode = InArgs._GraphNode; + this->DraggedFromPins = InArgs._DraggedFromPins; + this->NewNodePosition = InArgs._NewNodePosition; + this->OnClosedCallback = InArgs._OnClosedCallback; + this->AutoExpandActionMenu = InArgs._AutoExpandActionMenu; + this->SubNodeFlags = InArgs._SubNodeFlags; + + // Build the widget layout + SBorder::Construct( SBorder::FArguments() + .BorderImage( FAppStyle::GetBrush("Menu.Background") ) + .Padding(5.f) + [ + // Achieving fixed width by nesting items within a fixed width box. + SNew(SBox) + .WidthOverride(400.f) + [ + SAssignNew(GraphActionMenu, SGraphActionMenu) + .OnActionSelected(this, &SGraphEditorActionMenuFlow::OnActionSelected) + .OnCollectAllActions(this, &SGraphEditorActionMenuFlow::CollectAllActions) + .AutoExpandActionMenu(AutoExpandActionMenu) + ] + ] + ); +} + +void SGraphEditorActionMenuFlow::CollectAllActions(FGraphActionListBuilderBase& OutAllActions) +{ + // Build up the context object + FGraphContextMenuBuilder ContextMenuBuilder(GraphObj); + if (GraphNode != nullptr) + { + ContextMenuBuilder.SelectedObjects.Add(GraphNode); + } + if (DraggedFromPins.Num() > 0) + { + ContextMenuBuilder.FromPin = DraggedFromPins[0]; + } + + // Determine all possible actions + const UFlowGraphSchema* MySchema = Cast(GraphObj->GetSchema()); + if (MySchema) + { + MySchema->GetGraphNodeContextActions(ContextMenuBuilder, SubNodeFlags); + } + + // Copy the added options back to the main list + //@TODO: Avoid this copy + OutAllActions.Append(ContextMenuBuilder); +} + +TSharedRef SGraphEditorActionMenuFlow::GetFilterTextBox() +{ + return GraphActionMenu->GetFilterTextBox(); +} + +void SGraphEditorActionMenuFlow::OnActionSelected( const TArray< TSharedPtr >& SelectedAction, ESelectInfo::Type InSelectionType ) +{ + if (InSelectionType == ESelectInfo::OnMouseClick || InSelectionType == ESelectInfo::OnKeyPress || SelectedAction.Num() == 0) + { + bool bDoDismissMenus = false; + + if (GraphObj) + { + for ( int32 ActionIndex = 0; ActionIndex < SelectedAction.Num(); ActionIndex++ ) + { + TSharedPtr CurrentAction = SelectedAction[ActionIndex]; + + if ( CurrentAction.IsValid() ) + { + CurrentAction->PerformAction(GraphObj, DraggedFromPins, NewNodePosition); + bDoDismissMenus = true; + } + } + } + + if (bDoDismissMenus) + { + FSlateApplication::Get().DismissAllMenus(); + } + } +} diff --git a/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp b/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp index 277765d34..f80304db0 100644 --- a/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp +++ b/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp @@ -7,6 +7,7 @@ #include "MovieScene/MovieSceneFlowTrack.h" #include "MovieScene/MovieSceneFlowTriggerSection.h" +#include "Components/HorizontalBox.h" #include "Framework/MultiBox/MultiBoxBuilder.h" #include "ISequencerSection.h" #include "LevelSequence.h" diff --git a/Source/FlowEditor/Private/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.cpp b/Source/FlowEditor/Private/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.cpp new file mode 100644 index 000000000..b04b7ce11 --- /dev/null +++ b/Source/FlowEditor/Private/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.cpp @@ -0,0 +1,34 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.h" +#include "Nodes/FlowNodeBlueprintFactory.h" +#include "Nodes/FlowNodeAddOnBlueprint.h" +#include "AddOns/FlowNodeAddOn.h" +#include "Graph/FlowGraphSettings.h" +#include "FlowEditorModule.h" + +#define LOCTEXT_NAMESPACE "AssetTypeActions_FlowNodeAddOnBlueprint" + +FText FAssetTypeActions_FlowNodeAddOnBlueprint::GetName() const +{ + return LOCTEXT("AssetTypeActions_FlowNodeBlueprint", "Flow Node AddOn Blueprint"); +} + +uint32 FAssetTypeActions_FlowNodeAddOnBlueprint::GetCategories() +{ + return UFlowGraphSettings::Get()->bExposeFlowNodeCreation ? FFlowEditorModule::FlowAssetCategory : 0; +} + +UClass* FAssetTypeActions_FlowNodeAddOnBlueprint::GetSupportedClass() const +{ + return UFlowNodeAddOnBlueprint::StaticClass(); +} + +UFactory* FAssetTypeActions_FlowNodeAddOnBlueprint::GetFactoryForBlueprintType(UBlueprint* InBlueprint) const +{ + UFlowNodeAddOnBlueprintFactory* FlowNodeAddOnBlueprintFactory = NewObject(); + FlowNodeAddOnBlueprintFactory->ParentClass = TSubclassOf(*InBlueprint->GeneratedClass); + return FlowNodeAddOnBlueprintFactory; +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/Nodes/AssetTypeActions_FlowNodeBlueprint.cpp b/Source/FlowEditor/Private/Nodes/AssetTypeActions_FlowNodeBlueprint.cpp index a7b1b6afe..a145a98fc 100644 --- a/Source/FlowEditor/Private/Nodes/AssetTypeActions_FlowNodeBlueprint.cpp +++ b/Source/FlowEditor/Private/Nodes/AssetTypeActions_FlowNodeBlueprint.cpp @@ -2,10 +2,11 @@ #include "Nodes/AssetTypeActions_FlowNodeBlueprint.h" #include "Nodes/FlowNodeBlueprintFactory.h" -#include "FlowEditorModule.h" +#include "Nodes/FlowNodeBlueprint.h" +#include "Nodes/FlowNode.h" #include "Graph/FlowGraphSettings.h" +#include "FlowEditorModule.h" -#include "Nodes/FlowNodeBlueprint.h" #define LOCTEXT_NAMESPACE "AssetTypeActions_FlowNodeBlueprint" diff --git a/Source/FlowEditor/Private/Nodes/FlowNodeBlueprintFactory.cpp b/Source/FlowEditor/Private/Nodes/FlowNodeBlueprintFactory.cpp index f23237513..ff62898b8 100644 --- a/Source/FlowEditor/Private/Nodes/FlowNodeBlueprintFactory.cpp +++ b/Source/FlowEditor/Private/Nodes/FlowNodeBlueprintFactory.cpp @@ -4,6 +4,8 @@ #include "Nodes/FlowNode.h" #include "Nodes/FlowNodeBlueprint.h" +#include "Nodes/FlowNodeAddOnBlueprint.h" +#include "AddOns/FlowNodeAddOn.h" #include "BlueprintEditorSettings.h" #include "ClassViewerFilter.h" @@ -13,6 +15,7 @@ #include "Misc/MessageDialog.h" #include "Modules/ModuleManager.h" #include "SlateOptMacros.h" +#include "Templates/SubclassOf.h" #include "Widgets/Input/SButton.h" #include "Widgets/Layout/SBorder.h" #include "Widgets/Layout/SBox.h" @@ -21,10 +24,11 @@ #include "Widgets/SCompoundWidget.h" #include "Widgets/SWindow.h" -#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNodeBlueprintFactory) - #define LOCTEXT_NAMESPACE "FlowNodeBlueprintFactory" +// Forward Declarations +class UFlowNodeBase; + // ------------------------------------------------------------------------------ // Dialog to configure creation properties // ------------------------------------------------------------------------------ @@ -34,13 +38,14 @@ class SFlowNodeBlueprintCreateDialog final : public SCompoundWidget { public: SLATE_BEGIN_ARGS(SFlowNodeBlueprintCreateDialog) {} + SLATE_ARGUMENT(TSubclassOf, ParentClass) SLATE_END_ARGS() /** Constructs this widget with InArgs */ void Construct(const FArguments& InArgs) { bOkClicked = false; - ParentClass = UFlowNode::StaticClass(); + ParentClass = InArgs._ParentClass.Get(); ChildSlot [ @@ -98,7 +103,7 @@ class SFlowNodeBlueprintCreateDialog final : public SCompoundWidget } /** Sets properties for the supplied FlowNodeBlueprintFactory */ - bool ConfigureProperties(const TWeakObjectPtr InFlowNodeBlueprintFactory) + bool ConfigureProperties(const TWeakObjectPtr InFlowNodeBlueprintFactory) { FlowNodeBlueprintFactory = InFlowNodeBlueprintFactory; @@ -152,8 +157,11 @@ class SFlowNodeBlueprintCreateDialog final : public SCompoundWidget const TSharedPtr Filter = MakeShareable(new FFlowNodeBlueprintParentFilter); - // All child child classes of UFlowNode are valid - Filter->AllowedChildrenOfClasses.Add(UFlowNode::StaticClass()); + // All child classes of ParentClass are valid + if (UClass* ParentClassObject = ParentClass.Get()) + { + Filter->AllowedChildrenOfClasses.Add(ParentClassObject); + } Options.ClassFilters = {Filter.ToSharedRef()}; ParentClassContainer->ClearChildren(); @@ -166,7 +174,10 @@ class SFlowNodeBlueprintCreateDialog final : public SCompoundWidget /** Handler for when a parent class is selected */ void OnClassPicked(UClass* ChosenClass) { - ParentClass = ChosenClass; + if (ChosenClass) + { + ParentClass = ChosenClass; + } } /** Handler for when ok is clicked */ @@ -210,7 +221,7 @@ class SFlowNodeBlueprintCreateDialog final : public SCompoundWidget private: /** The factory for which we are setting up properties */ - TWeakObjectPtr FlowNodeBlueprintFactory; + TWeakObjectPtr FlowNodeBlueprintFactory; /** A pointer to the window that is asking the user to select a parent class */ TWeakPtr PickerWindow; @@ -228,56 +239,92 @@ class SFlowNodeBlueprintCreateDialog final : public SCompoundWidget END_SLATE_FUNCTION_BUILD_OPTIMIZATION /*------------------------------------------------------------------------------ - UFlowNodeBlueprintFactory implementation + UFlowNodeBaseBlueprintFactory implementation ------------------------------------------------------------------------------*/ -UFlowNodeBlueprintFactory::UFlowNodeBlueprintFactory(const FObjectInitializer& ObjectInitializer) +UFlowNodeBaseBlueprintFactory::UFlowNodeBaseBlueprintFactory(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { - SupportedClass = UFlowNodeBlueprint::StaticClass(); - ParentClass = UFlowNode::StaticClass(); + SupportedClass = nullptr; + + DefaultParentClass = UFlowNodeBase::StaticClass(); + ParentClass = DefaultParentClass; bCreateNew = true; bEditAfterNew = true; } -bool UFlowNodeBlueprintFactory::ConfigureProperties() +bool UFlowNodeBaseBlueprintFactory::ConfigureProperties() { - const TSharedRef Dialog = SNew(SFlowNodeBlueprintCreateDialog); + const TSharedRef Dialog = + SNew(SFlowNodeBlueprintCreateDialog) + .ParentClass(ParentClass); + return Dialog->ConfigureProperties(this); } -UObject* UFlowNodeBlueprintFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn, FName CallingContext) +UObject* UFlowNodeBaseBlueprintFactory::FactoryCreateNew(UClass* BlueprintClass, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn, FName CallingContext) { - check(Class->IsChildOf(UFlowNodeBlueprint::StaticClass())); + check(BlueprintClass->IsChildOf(SupportedClass)); - if (ParentClass == nullptr || !FKismetEditorUtilities::CanCreateBlueprintOfClass(ParentClass) || !ParentClass->IsChildOf(UFlowNode::StaticClass())) + if (ParentClass == nullptr || !FKismetEditorUtilities::CanCreateBlueprintOfClass(ParentClass) || !ParentClass->IsChildOf(DefaultParentClass)) { - FFormatNamedArguments Args; - Args.Add(TEXT("ClassName"), ParentClass ? FText::FromString(ParentClass->GetName()) : LOCTEXT("Null", "(null)")); - FMessageDialog::Open(EAppMsgType::Ok, FText::Format(LOCTEXT("CannotCreateFlowNodeBlueprint", "Cannot create a Flow Node Blueprint based on the class '{ClassName}'."), Args)); + ShowCannotCreateBlueprintDialog(); + return nullptr; } - UFlowNodeBlueprint* NewBP = CastChecked(FKismetEditorUtilities::CreateBlueprint(ParentClass, InParent, Name, BPTYPE_Normal, UFlowNodeBlueprint::StaticClass(), UBlueprintGeneratedClass::StaticClass(), CallingContext)); + UBlueprint* NewBP = FKismetEditorUtilities::CreateBlueprint(ParentClass, InParent, Name, BPTYPE_Normal, BlueprintClass, UBlueprintGeneratedClass::StaticClass(), CallingContext); if (NewBP && NewBP->UbergraphPages.Num() > 0) { const UBlueprintEditorSettings* Settings = GetMutableDefault(); - if(Settings && Settings->bSpawnDefaultBlueprintNodes) + if (Settings && Settings->bSpawnDefaultBlueprintNodes) { int32 NodePositionY = 0; - FKismetEditorUtilities::AddDefaultEventNode(NewBP, NewBP->UbergraphPages[0], FName(TEXT("K2_ExecuteInput")), UFlowNode::StaticClass(), NodePositionY); - FKismetEditorUtilities::AddDefaultEventNode(NewBP, NewBP->UbergraphPages[0], FName(TEXT("K2_Cleanup")), UFlowNode::StaticClass(), NodePositionY); + FKismetEditorUtilities::AddDefaultEventNode(NewBP, NewBP->UbergraphPages[0], FName(TEXT("K2_ExecuteInput")), DefaultParentClass, NodePositionY); + FKismetEditorUtilities::AddDefaultEventNode(NewBP, NewBP->UbergraphPages[0], FName(TEXT("K2_Cleanup")), DefaultParentClass, NodePositionY); } } - + return NewBP; } -UObject* UFlowNodeBlueprintFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) +UObject* UFlowNodeBaseBlueprintFactory::FactoryCreateNew(UClass* BlueprintClass, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) +{ + return FactoryCreateNew(BlueprintClass, InParent, Name, Flags, Context, Warn, NAME_None); +} + +void UFlowNodeBaseBlueprintFactory::ShowCannotCreateBlueprintDialog() +{ + FFormatNamedArguments Args; + Args.Add(TEXT("DefaultClassName"), DefaultParentClass ? FText::FromString(DefaultParentClass->GetName()) : LOCTEXT("Null", "(null)")); + Args.Add(TEXT("ClassName"), ParentClass ? FText::FromString(ParentClass->GetName()) : LOCTEXT("Null", "(null)")); + FMessageDialog::Open(EAppMsgType::Ok, FText::Format(LOCTEXT("CannotCreateFlowNodeBlueprint", "Cannot create a {DefaultClassName} Blueprint based on the class '{ClassName}'."), Args)); +} + +/*------------------------------------------------------------------------------ + UFlowNodeBlueprintFactory implementation +------------------------------------------------------------------------------*/ + +UFlowNodeBlueprintFactory::UFlowNodeBlueprintFactory(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ + SupportedClass = UFlowNodeBlueprint::StaticClass(); + DefaultParentClass = UFlowNode::StaticClass(); + ParentClass = DefaultParentClass; +} + +/*------------------------------------------------------------------------------ + UFlowNodeAddOnBlueprintFactory implementation +------------------------------------------------------------------------------*/ + +UFlowNodeAddOnBlueprintFactory::UFlowNodeAddOnBlueprintFactory(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) { - return FactoryCreateNew(Class, InParent, Name, Flags, Context, Warn, NAME_None); + SupportedClass = UFlowNodeAddOnBlueprint::StaticClass(); + DefaultParentClass = UFlowNodeAddOn::StaticClass(); + ParentClass = DefaultParentClass; } #undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/Pins/SFlowOutputPinHandle.cpp b/Source/FlowEditor/Private/Pins/SFlowOutputPinHandle.cpp index 1605bb078..24538b20f 100644 --- a/Source/FlowEditor/Private/Pins/SFlowOutputPinHandle.cpp +++ b/Source/FlowEditor/Private/Pins/SFlowOutputPinHandle.cpp @@ -11,7 +11,7 @@ void SFlowOutputPinHandle::RefreshNameList() { PinNames.Empty(); - if (Blueprint && Blueprint->GeneratedClass) + if (Blueprint && Blueprint->GeneratedClass && Blueprint->GeneratedClass->IsChildOf()) { if (UFlowNode* FlowNode = Blueprint->GeneratedClass->GetDefaultObject()) { diff --git a/Source/FlowEditor/Private/UnrealExtensions/IFlowCuratedNamePropertyCustomization.cpp b/Source/FlowEditor/Private/UnrealExtensions/IFlowCuratedNamePropertyCustomization.cpp index e71ecbc2f..5ef3afb86 100644 --- a/Source/FlowEditor/Private/UnrealExtensions/IFlowCuratedNamePropertyCustomization.cpp +++ b/Source/FlowEditor/Private/UnrealExtensions/IFlowCuratedNamePropertyCustomization.cpp @@ -7,8 +7,11 @@ #include "DetailLayoutBuilder.h" #include "DetailWidgetRow.h" +#include "EditorClassUtils.h" #include "IDetailPropertyRow.h" +#include "IDetailChildrenBuilder.h" #include "Internationalization/Text.h" +#include "IPropertyUtilities.h" #include "PropertyHandle.h" #include "Widgets/Input/SComboBox.h" #include "Widgets/Text/STextBlock.h" @@ -33,12 +36,15 @@ void IFlowCuratedNamePropertyCustomization::Initialize() check(!CachedTextSelected.IsValid()); check(CachedTextList.IsEmpty()); - const FName CuratedName = GetCuratedName(); - const bool bChangedValue = TrySetCuratedNameWithSideEffects(CuratedName); + FName CuratedName; + if (TryGetCuratedName(CuratedName)) + { + const bool bChangedValue = TrySetCuratedNameWithSideEffects(CuratedName); - check(bChangedValue); - check(CachedTextSelected.IsValid()); - check(CachedTextList.Num() == 1); + check(bChangedValue); + check(CachedTextSelected.IsValid()); + check(CachedTextList.Num() == 1); + } } void IFlowCuratedNamePropertyCustomization::CreateHeaderRowWidget(FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) @@ -46,6 +52,8 @@ void IFlowCuratedNamePropertyCustomization::CreateHeaderRowWidget(FDetailWidgetR // Do one-time setup first Initialize(); + CachedPropertyUtils = StructCustomizationUtils.GetPropertyUtilities(); + // Replace the default HeaderRow widget with one of our own HeaderRow .NameContent() @@ -70,26 +78,31 @@ void IFlowCuratedNamePropertyCustomization::CreateHeaderRowWidget(FDetailWidgetR ]; // Hook-up the ResetToDefault overrides - { - const FIsResetToDefaultVisible IsResetVisible = - FIsResetToDefaultVisible::CreateSP( - this, - &IFlowCuratedNamePropertyCustomization::CustomIsResetToDefaultVisible); - - const FResetToDefaultHandler ResetHandler = - FResetToDefaultHandler::CreateSP( - this, - &IFlowCuratedNamePropertyCustomization::CustomResetToDefault); - - const FResetToDefaultOverride ResetOverride = FResetToDefaultOverride::Create(IsResetVisible, ResetHandler); - - HeaderRow.OverrideResetToDefault(ResetOverride); - } + FIsResetToDefaultVisible IsResetVisible = + FIsResetToDefaultVisible::CreateSP( + this, + &IFlowCuratedNamePropertyCustomization::CustomIsResetToDefaultVisible); + FResetToDefaultHandler ResetHandler = + FResetToDefaultHandler::CreateSP( + this, + &IFlowCuratedNamePropertyCustomization::CustomResetToDefault); + FResetToDefaultOverride ResetOverride = FResetToDefaultOverride::Create(IsResetVisible, ResetHandler); + + HeaderRow.OverrideResetToDefault(ResetOverride); + + // Replacement IsEnabled Attribute + TAttribute IsEnabledAttribute = TAttribute::CreateSP(this, &IFlowCuratedNamePropertyCustomization::CustomIsEnabled); + HeaderRow.IsEnabled(IsEnabledAttribute); } bool IFlowCuratedNamePropertyCustomization::CustomIsResetToDefaultVisible(TSharedPtr Property) const { - const FName CuratedName = GetCuratedName(); + FName CuratedName; + if (!TryGetCuratedName(CuratedName)) + { + return false; + } + return !CuratedName.IsNone(); } @@ -101,9 +114,25 @@ void IFlowCuratedNamePropertyCustomization::CustomResetToDefault(TSharedPtrIsEditConst()) + { + return false; + } + + if (CachedPropertyUtils && !CachedPropertyUtils->IsPropertyEditingEnabled()) + { + return false; + } + + return true; +} + bool IFlowCuratedNamePropertyCustomization::TrySetCuratedNameWithSideEffects(const FName& NewName) { - const FName ExistingName = GetCuratedName(); + FName ExistingName; + (void) TryGetCuratedName(ExistingName); if (ExistingName != NewName) { @@ -113,7 +142,7 @@ bool IFlowCuratedNamePropertyCustomization::TrySetCuratedNameWithSideEffects(con // Ensure the FText representations are up to date - const TSharedPtr NewText = FindCachedOrCreateText(NewName); + TSharedPtr NewText = FindCachedOrCreateText(NewName); const bool bIsChanged = (NewText != CachedTextSelected); CachedTextSelected = NewText; @@ -129,12 +158,17 @@ bool IFlowCuratedNamePropertyCustomization::TrySetCuratedNameWithSideEffects(con FText IFlowCuratedNamePropertyCustomization::GetCachedText() const { - check(CachedTextSelected.IsValid()); - - return *CachedTextSelected.Get(); + if (CachedTextSelected.IsValid()) + { + return *CachedTextSelected.Get(); + } + else + { + return FText(); + } } -TSharedRef IFlowCuratedNamePropertyCustomization::GenerateTextListWidget(const TSharedPtr InItem) +TSharedRef IFlowCuratedNamePropertyCustomization::GenerateTextListWidget(TSharedPtr InItem) { return SNew(STextBlock) @@ -145,18 +179,24 @@ TSharedRef IFlowCuratedNamePropertyCustomization::GenerateTextListWidge void IFlowCuratedNamePropertyCustomization::OnTextListComboBoxOpening() { - check(CachedTextSelected.IsValid()); + if (!CachedTextSelected.IsValid()) + { + return; + } // Create a dictionary of Names to their shared FTexts // (to preserve the shared FText objects, if they already exist) TMap> MapNameToText; - const FName CurrentName = GetCuratedName(); - MapNameToText.Add(CurrentName, CachedTextSelected); + FName CurrentName; + if (TryGetCuratedName(CurrentName)) + { + MapNameToText.Add(CurrentName, CachedTextSelected); + } for (TSharedPtr& Text : CachedTextList) { - (void)MapNameToText.FindOrAdd(FName(Text.Get()->ToString()), Text); + (void) MapNameToText.FindOrAdd(FName(Text.Get()->ToString()), Text); } TArray CuratedNameOptions = GetCuratedNameOptions(); @@ -180,7 +220,7 @@ void IFlowCuratedNamePropertyCustomization::OnTextListComboBoxOpening() } // Ensure "None" is in the list (if CurrentName is not None) - if (!CurrentName.IsNone()) + if (!CurrentName.IsNone() && (CuratedNameOptions.IsEmpty() || AllowNameNoneIfOtherOptionsExist())) { check(!CachedTextList.Contains(NoneAsText)); @@ -188,7 +228,7 @@ void IFlowCuratedNamePropertyCustomization::OnTextListComboBoxOpening() } } -void IFlowCuratedNamePropertyCustomization::OnTextSelected(const TSharedPtr NewSelection, ESelectInfo::Type SelectInfo) +void IFlowCuratedNamePropertyCustomization::OnTextSelected(TSharedPtr NewSelection, ESelectInfo::Type SelectInfo) { // Called when the combo box has selected a new element @@ -238,14 +278,14 @@ TSharedPtr IFlowCuratedNamePropertyCustomization::FindCachedOrCreateText( return Result; } -void IFlowCuratedNamePropertyCustomization::InsertAtHeadOfCachedTextList(const TSharedPtr Text) +void IFlowCuratedNamePropertyCustomization::InsertAtHeadOfCachedTextList(TSharedPtr Text) { CachedTextList.Remove(Text); CachedTextList.Insert(Text, 0); } -void IFlowCuratedNamePropertyCustomization::AddToCachedTextList(const TSharedPtr Text) +void IFlowCuratedNamePropertyCustomization::AddToCachedTextList(TSharedPtr Text) { CachedTextList.AddUnique(Text); } diff --git a/Source/FlowEditor/Private/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.cpp b/Source/FlowEditor/Private/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.cpp index 57e284066..d54ab8610 100644 --- a/Source/FlowEditor/Private/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.cpp +++ b/Source/FlowEditor/Private/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.cpp @@ -7,8 +7,12 @@ #include "DetailWidgetRow.h" #include "IDetailChildrenBuilder.h" +#include "IDetailPropertyRow.h" #include "Widgets/Text/STextBlock.h" + +// IFlowExtendedPropertyTypeCustomization Implementation + void IFlowExtendedPropertyTypeCustomization::CustomizeHeader(TSharedRef InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) { StructPropertyHandle = InStructPropertyHandle; diff --git a/Source/FlowEditor/Private/Utils/SLevelEditorFlow.cpp b/Source/FlowEditor/Private/Utils/SLevelEditorFlow.cpp index c3662ffce..3fbf2f9a9 100644 --- a/Source/FlowEditor/Private/Utils/SLevelEditorFlow.cpp +++ b/Source/FlowEditor/Private/Utils/SLevelEditorFlow.cpp @@ -11,6 +11,7 @@ #include "Graph/FlowGraphSettings.h" #include "Editor.h" +#include "GameFramework/WorldSettings.h" #include "PropertyCustomizationHelpers.h" #include "Runtime/Launch/Resources/Version.h" diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index 83c6e7cd9..94b7e17cb 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -101,6 +101,8 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit // FAssetEditorToolkit virtual void InitToolMenuContext(FToolMenuContext& MenuContext) override; virtual void PostRegenerateMenusAndToolbars() override; + virtual void SaveAsset_Execute() override; + virtual void SaveAssetAs_Execute() override; // -- bool IsTabFocused(const FTabId& TabId) const; @@ -115,6 +117,8 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit #endif TSharedRef SpawnTab_ValidationLog(const FSpawnTabArgs& Args) const; + void DoPresaveAssetUpdate(); + public: /** Edits the specified FlowAsset object */ void InitFlowAssetEditor(const EToolkitMode::Type Mode, const TSharedPtr& InitToolkitHost, UObject* ObjectToEdit); @@ -150,7 +154,7 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit virtual void ClearSelectionStateFor(const FName SelectionOwner); FName GetUISelectionState() const; - virtual void OnSelectedNodesChanged(const TSet& Nodes) {} + virtual void OnSelectedNodesChanged(const TSet& Nodes); #if ENABLE_JUMP_TO_INNER_OBJECT // FAssetEditorToolkit diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowActorOwnerComponentRefCustomization.h b/Source/FlowEditor/Public/DetailCustomizations/FlowActorOwnerComponentRefCustomization.h new file mode 100644 index 000000000..02cba2ce9 --- /dev/null +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowActorOwnerComponentRefCustomization.h @@ -0,0 +1,45 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "UnrealExtensions/IFlowCuratedNamePropertyCustomization.h" + +#include "Types/FlowActorOwnerComponentRef.h" + +// Forward Declaration +class UFlowAsset; +class UFlowNode; +class UObject; +class UClass; + +// Details customization for FFlowActorOwnerComponentRef +class FFlowActorOwnerComponentRefCustomization : public IFlowCuratedNamePropertyCustomization +{ +private: + typedef IFlowCuratedNamePropertyCustomization Super; + +public: + static TSharedRef MakeInstance() { return MakeShareable(new FFlowActorOwnerComponentRefCustomization()); } + +protected: + + // IPropertyTypeCustomization + virtual void CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; + // -- + + // ICuratedNamePropertyCustomization + virtual TSharedPtr GetCuratedNamePropertyHandle() const override; + virtual void SetCuratedName(const FName& NewName) override; + virtual bool TryGetCuratedName(FName& OutName) const override; + virtual TArray GetCuratedNameOptions() const override; + // -- + + // Accessor to return the actual struct being edited + FORCEINLINE FFlowActorOwnerComponentRef* GetFlowActorOwnerComponentRef() const + { return IFlowExtendedPropertyTypeCustomization::TryGetTypedStructValue(StructPropertyHandle); } + + UClass* TryGetExpectedOwnerClass() const; + UFlowNode* TryGetFlowNodeOuter() const; + + TArray GetFlowActorOwnerComponents(TSubclassOf ExpectedActorOwnerClass) const; +}; diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowNodeAddOn_Details.h b/Source/FlowEditor/Public/DetailCustomizations/FlowNodeAddOn_Details.h new file mode 100644 index 000000000..05544d12b --- /dev/null +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowNodeAddOn_Details.h @@ -0,0 +1,18 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "IDetailCustomization.h" + +class FFlowNodeAddOn_Details final : public IDetailCustomization +{ +public: + static TSharedRef MakeInstance() + { + return MakeShareable(new FFlowNodeAddOn_Details()); + } + + // IDetailCustomization + virtual void CustomizeDetails(IDetailLayoutBuilder& DetailLayout) override; + // -- +}; diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowOwnerFunctionRefCustomization.h b/Source/FlowEditor/Public/DetailCustomizations/FlowOwnerFunctionRefCustomization.h index 1bdc1c979..ccc2f8b77 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowOwnerFunctionRefCustomization.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowOwnerFunctionRefCustomization.h @@ -4,8 +4,10 @@ #include "UnrealExtensions/IFlowCuratedNamePropertyCustomization.h" -#include "FlowOwnerFunctionRef.h" +#include "Types/FlowOwnerFunctionRef.h" + +// Forward Declaration class UFlowAsset; class UFlowNode; class UObject; @@ -13,6 +15,8 @@ class UClass; class UFunction; class UFlowNode_CallOwnerFunction; + +// Details customization for FFlowOwnerFunctionRef class FFlowOwnerFunctionRefCustomization : public IFlowCuratedNamePropertyCustomization { private: @@ -22,22 +26,21 @@ class FFlowOwnerFunctionRefCustomization : public IFlowCuratedNamePropertyCustom static TSharedRef MakeInstance() { return MakeShareable(new FFlowOwnerFunctionRefCustomization()); } protected: + // IPropertyTypeCustomization virtual void CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; - // --- + // -- // ICuratedNamePropertyCustomization virtual TSharedPtr GetCuratedNamePropertyHandle() const override; virtual void SetCuratedName(const FName& NewName) override; - virtual FName GetCuratedName() const override; + virtual bool TryGetCuratedName(FName& OutName) const override; virtual TArray GetCuratedNameOptions() const override; - // --- + // -- // Accessor to return the actual struct being edited FORCEINLINE FFlowOwnerFunctionRef* GetFlowOwnerFunctionRef() const - { - return IFlowExtendedPropertyTypeCustomization::TryGetTypedStructValue(StructPropertyHandle); - } + { return IFlowExtendedPropertyTypeCustomization::TryGetTypedStructValue(StructPropertyHandle); } const UClass* TryGetExpectedOwnerClass() const; UFlowNode* TryGetFlowNodeOuter() const; diff --git a/Source/FlowEditor/Public/Graph/FlowGraph.h b/Source/FlowEditor/Public/Graph/FlowGraph.h index 81251f880..32a47572a 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraph.h +++ b/Source/FlowEditor/Public/Graph/FlowGraph.h @@ -7,6 +7,9 @@ #include "FlowAsset.h" #include "FlowGraph.generated.h" +class SFlowGraphEditor; +class UFlowGraphNode; + class FLOWEDITOR_API FFlowGraphInterface : public IFlowGraphInterface { public: @@ -21,6 +24,7 @@ class FLOWEDITOR_API UFlowGraph : public UEdGraph { GENERATED_UCLASS_BODY() +public: static UEdGraph* CreateGraph(UFlowAsset* InFlowAsset); static UEdGraph* CreateGraph(UFlowAsset* InFlowAsset, TSubclassOf FlowSchema); void RefreshGraph(); @@ -31,4 +35,57 @@ class FLOWEDITOR_API UFlowGraph : public UEdGraph /** Returns the FlowAsset that contains this graph */ UFlowAsset* GetFlowAsset() const; + + //~ Begin UObject Interface. + virtual void Serialize(FArchive& Ar) override; + //~ End UObject Interface. + + virtual void CollectAllNodeInstances(TSet& NodeInstances); + virtual bool CanRemoveNestedObject(UObject* TestObject) const; + virtual void OnNodeInstanceRemoved(UObject* NodeInstance); + + UEdGraphPin* FindGraphNodePin(UEdGraphNode* Node, EEdGraphPinDirection Dir); + +public: + + virtual void OnCreated(); + virtual void OnLoaded(); + virtual void OnSave(); + + virtual void Initialize(); + + virtual void UpdateAsset(int32 UpdateFlags = 0); + virtual void UpdateVersion(); + virtual void MarkVersion(); + + virtual void OnSubNodeDropped(); + virtual void OnNodesPasted(const FString& ImportStr); + + bool UpdateUnknownNodeClasses(); + void UpdateDeprecatedClasses(); + void RemoveOrphanedNodes(); + void UpdateClassData(); + + bool IsLocked() const; + void LockUpdates(); + void UnlockUpdates(); + +protected: + + void RecursivelySetupAllFlowGraphNodesForEditing(UFlowGraphNode& FromFlowGraphNode); + void RecursivelyRefreshAddOns(UFlowGraphNode& FromFlowGraphNode); + + static FString GetDeprecationMessage(const UClass* Class); + static void UpdateFlowGraphNodeErrorMessage(UFlowGraphNode& Node); + +protected: + + /** Graph version number */ + UPROPERTY() + int32 GraphVersion; + + /** if set, graph modifications won't cause updates in internal tree structure + * flag allows freezing update during heavy changes like pasting new nodes + */ + uint32 bLockUpdates : 1; }; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h index 2d3536f4e..45a1e07b4 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h @@ -2,7 +2,10 @@ #pragma once +#include "EdGraphUtilities.h" #include "GraphEditor.h" +#include "GraphEditorActions.h" +#include "SGraphNode.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "FlowGraph.h" @@ -71,6 +74,7 @@ class FLOWEDITOR_API SFlowGraphEditor : public SGraphEditor virtual bool CanDeleteNodes() const; virtual void CopySelectedNodes() const; + void PrepareFlowGraphNodeForCopy(UFlowGraphNode& FlowGraphNode, int32 ParentEdNodeIndex, FGraphPanelSelectionSet& NewSelectedNodes) const; virtual bool CanCopyNodes() const; virtual void CutSelectedNodes(); diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h index cbc82ec41..733cb7e9b 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h @@ -8,6 +8,8 @@ class UFlowAsset; class UFlowNode; +class UFlowNodeAddOn; +class UFlowNodeBase; class UFlowGraphNode; DECLARE_MULTICAST_DELEGATE(FFlowGraphSchemaRefresh); @@ -22,8 +24,10 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema private: static bool bInitialGatherPerformed; static TArray NativeFlowNodes; + static TArray NativeFlowNodeAddOns; static TMap BlueprintFlowNodes; - static TMap GraphNodesByFlowNodes; + static TMap BlueprintFlowNodeAddOns; + static TMap, TSubclassOf> GraphNodesByFlowNodes; static bool bBlueprintCompilationPending; @@ -35,6 +39,7 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema virtual void GetGraphContextActions(FGraphContextMenuBuilder& ContextMenuBuilder) const override; virtual void CreateDefaultNodesForGraph(UEdGraph& Graph) const override; virtual const FPinConnectionResponse CanCreateConnection(const UEdGraphPin* A, const UEdGraphPin* B) const override; + virtual const FPinConnectionResponse CanMergeNodes(const UEdGraphNode* NodeA, const UEdGraphNode* NodeB) const override; virtual bool TryCreateConnection(UEdGraphPin* A, UEdGraphPin* B) const override; virtual bool ShouldHidePinDefaultValue(UEdGraphPin* Pin) const override; virtual FLinearColor GetPinTypeColor(const FEdGraphPinType& PinType) const override; @@ -46,33 +51,56 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema virtual void OnPinConnectionDoubleCicked(UEdGraphPin* PinA, UEdGraphPin* PinB, const FVector2D& GraphPosition) const override; // -- + // FlowGraphSchema + virtual void GetGraphNodeContextActions(FGraphContextMenuBuilder& ContextMenuBuilder, int32 SubNodeFlags) const; + + bool IsAddOnAllowedForSelectedObjects(const TArray& SelectedObjects, const UFlowNodeAddOn* AddOnTemplate) const; + + // -- + static TArray> GetFlowNodeCategories(); - static UClass* GetAssignedGraphNodeClass(const UClass* FlowNodeClass); + static TSubclassOf GetAssignedGraphNodeClass(TSubclassOf FlowNodeClass); + + static bool IsPIESimulating(); protected: static UFlowGraphNode* CreateDefaultNode(UEdGraph& Graph, const UFlowAsset* AssetClassDefaults, const TSubclassOf& NodeClass, const FVector2D& Offset, bool bPlacedAsGhostNode); private: - static void ApplyNodeFilter(const UFlowAsset* AssetClassDefaults, const UClass* FlowNodeClass, TArray& FilteredNodes); + static void ApplyNodeOrAddOnFilter(const UFlowAsset* AssetClassDefaults, const UClass* FlowNodeClass, TArray& FilteredNodes); static void GetFlowNodeActions(FGraphActionMenuBuilder& ActionMenuBuilder, const UFlowAsset* EditedFlowAsset, const FString& CategoryName); + + static TArray GetFilteredPlaceableNodesOrAddOns( + const UFlowAsset* EditedFlowAsset, + const TArray& InNativeNodesOrAddOns, + const TMap& InBlueprintNodesOrAddOns); + static void GetCommentAction(FGraphActionMenuBuilder& ActionMenuBuilder, const UEdGraph* CurrentGraph = nullptr); - static bool IsFlowNodePlaceable(const UClass* Class); + static bool IsFlowNodeOrAddOnPlaceable(const UClass* Class); static void OnBlueprintPreCompile(UBlueprint* Blueprint); static void OnBlueprintCompiled(); static void OnHotReload(EReloadCompleteReason ReloadCompleteReason); - static void GatherNativeNodes(); + static void GatherNativeNodesOrAddOns( + TSubclassOf FlowNodeBaseClass, + TArray& InOutNodesOrAddOnsArray); static void GatherNodes(); static void OnAssetAdded(const FAssetData& AssetData); static void AddAsset(const FAssetData& AssetData, const bool bBatch); + + static bool ShouldAddToBlueprintFlowNodesMap( + const FAssetData& AssetData, + TSubclassOf BlueprintClass, + TSubclassOf FlowNodeBaseClass); + static void OnAssetRemoved(const FAssetData& AssetData); public: static FFlowGraphSchemaRefresh OnNodeListChanged; - static UBlueprint* GetPlaceableNodeBlueprint(const FAssetData& AssetData); + static UBlueprint* GetPlaceableNodeOrAddOnBlueprint(const FAssetData& AssetData); - static const UFlowAsset* GetAssetClassDefaults(const UEdGraph* Graph); + static const UFlowAsset* GetEditedAssetOrClassDefault(const UEdGraph* Graph); }; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h index 3bb8d230d..5d03fe0fa 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h @@ -52,6 +52,45 @@ struct FLOWEDITOR_API FFlowGraphSchemaAction_NewNode : public FEdGraphSchemaActi static UFlowGraphNode* ImportNode(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const UClass* NodeClass, const FGuid& NodeGuid, const FVector2D Location); }; +/** Action to add a subnode to the selected node */ +USTRUCT() +struct FLOWEDITOR_API FFlowSchemaAction_NewSubNode : public FEdGraphSchemaAction +{ + GENERATED_USTRUCT_BODY(); + + /** Template of node we want to create */ + UPROPERTY() + TObjectPtr NodeTemplate; + + /** parent node */ + UPROPERTY() + TObjectPtr ParentNode; + + FFlowSchemaAction_NewSubNode() + : FEdGraphSchemaAction() + , NodeTemplate(nullptr) + , ParentNode(nullptr) + { + } + + FFlowSchemaAction_NewSubNode(FText InNodeCategory, FText InMenuDesc, FText InToolTip, const int32 InGrouping) + : FEdGraphSchemaAction(MoveTemp(InNodeCategory), MoveTemp(InMenuDesc), MoveTemp(InToolTip), InGrouping) + , NodeTemplate(nullptr) + , ParentNode(nullptr) + { + } + + // FEdGraphSchemaAction + virtual UEdGraphNode* PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode = true) override; + virtual UEdGraphNode* PerformAction(class UEdGraph* ParentGraph, TArray& FromPins, const FVector2D Location, bool bSelectNewNode = true) override; + virtual void AddReferencedObjects(FReferenceCollector& Collector) override; + // -- + + static UFlowGraphNode* RecreateNode(UEdGraph* ParentGraph, UEdGraphNode* OldInstance, UFlowGraphNode* ParentFlowGraphNode, UFlowNodeAddOn* FlowNodeAddOn); + + static TSharedPtr AddNewSubNodeAction(FGraphActionListBuilderBase& ContextMenuBuilder, const FText& Category, const FText& MenuDesc, const FText& Tooltip); +}; + /** Action to paste clipboard contents into the graph */ USTRUCT() struct FLOWEDITOR_API FFlowGraphSchemaAction_Paste : public FEdGraphSchemaAction diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h index b80321ad2..33d42fcde 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h @@ -11,6 +11,8 @@ #include "FlowGraphNode.generated.h" class UEdGraphSchema; +class UFlowGraph; +class UFlowNodeBase; class UFlowNode; DECLARE_DELEGATE(FFlowGraphNodeEvent); @@ -27,8 +29,9 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode // Flow node private: + // The FlowNode or FlowNodeAddOn runtime instance that is being edited by this UFlowGraphNode UPROPERTY(Instanced) - UFlowNode* FlowNode; + UFlowNodeBase* NodeInstance; bool bBlueprintCompilationPending; bool bNeedsFullReconstruction; @@ -38,12 +41,12 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode // It would be intuitive to assign a custom Graph Node class in Flow Node class // However, we shouldn't assign class from editor module to runtime module class UPROPERTY() - TArray> AssignedNodeClasses; + TArray> AssignedNodeClasses; - void SetNodeTemplate(UFlowNode* InFlowNode); - const UFlowNode* GetNodeTemplate() const; + void SetNodeTemplate(UFlowNodeBase* InFlowNodeBase); + const UFlowNodeBase* GetNodeTemplate() const; - UFlowNode* GetFlowNode() const; + UFlowNodeBase* GetFlowNodeBase() const; // UObject virtual void PostLoad() override; @@ -60,6 +63,10 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode private: void SubscribeToExternalChanges(); + + void OnBlueprintPreCompile(UBlueprint* Blueprint); + void OnBlueprintCompiled(); + void OnExternalChange(); public: @@ -108,6 +115,12 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode virtual FText GetTooltipText() const override; // -- + void CreateAttachAddOnSubMenu(UToolMenu* Menu, UEdGraph* Graph) const; + + bool CanAcceptSubNodeAsChild(const UFlowGraphNode& OtherSubNode, FString* OutReasonString = nullptr) const; + + bool IsAncestorNode(const UFlowGraphNode& OtherNode) const; + ////////////////////////////////////////////////////////////////////////// // Utils @@ -133,6 +146,7 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode // UEdGraphNode virtual bool CanJumpToDefinition() const override; virtual void JumpToDefinition() const override; + virtual bool SupportsCommentBubble() const override; // -- ////////////////////////////////////////////////////////////////////////// @@ -202,4 +216,111 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode virtual EFlowSignalMode GetSignalMode() const; virtual bool CanSetSignalMode(const EFlowSignalMode Mode) const; + +////////////////////////////////////////////////////////////////////////// +// SubNode Support + + //~ Begin UEdGraphNode Interface + UFlowGraph* GetFlowGraph() const; + virtual void DestroyNode() override; + virtual void NodeConnectionListChanged() override; + virtual void FindDiffs(class UEdGraphNode* OtherNode, struct FDiffResults& Results) override; + virtual FString GetPropertyNameAndValueForDiff(const FProperty* Prop, const uint8* PropertyAddr) const override; + //~ End UEdGraphNode Interface + + void SetParentNodeForSubNode(UFlowGraphNode* InParentNode); + UFlowGraphNode* GetParentNode() const { return ParentNode; } + + void OnUpdateAsset(int32 UpdateFlags) { RebuildRuntimeAddOnsFromEditorSubNodes(); } + void RebuildRuntimeAddOnsFromEditorSubNodes(); + + static void DiffSubNodes( + const FText& NodeTypeDisplayName, + const TArray& LhsSubNodes, + const TArray& RhsSubNodes, + FDiffResults& Results); + + //~ Begin UObject Interface +#if WITH_EDITOR + virtual void PostEditUndo() override; +#endif + // End UObject + + // @return the input pin for this state + virtual UEdGraphPin* GetInputPin(int32 InputIndex = 0) const; + // @return the output pin for this state + virtual UEdGraphPin* GetOutputPin(int32 InputIndex = 0) const; + virtual UEdGraph* GetBoundGraph() const { return NULL; } + + virtual FText GetDescription() const; + + void AddSubNode(UFlowGraphNode* SubNode, class UEdGraph* ParentGraph); + void RemoveSubNode(UFlowGraphNode* SubNode); + virtual void RemoveAllSubNodes(); + virtual void OnSubNodeRemoved(UFlowGraphNode* SubNode); + virtual void OnSubNodeAdded(UFlowGraphNode* SubNode); + + virtual int32 FindSubNodeDropIndex(UFlowGraphNode* SubNode) const; + virtual void InsertSubNodeAt(UFlowGraphNode* SubNode, int32 DropIndex); + + /** check if node is subnode */ + virtual bool IsSubNode() const; + + /** initialize instance object */ + virtual void InitializeInstance(); + + /** reinitialize node instance */ + virtual bool RefreshNodeClass(); + + /** updates ClassData from node instance */ + virtual void UpdateNodeClassData(); + + /** Check if node instance uses blueprint for its implementation */ + bool UsesBlueprint() const; + + /** check if node has any errors, used for assigning colors on graph */ + virtual bool HasErrors() const; + +protected: + + virtual void ResetNodeOwner(); + + void LogError(const FString& MessageToLog, const UFlowNodeBase* FlowNodeBase); + +public: + + /** instance class */ + UPROPERTY() + TSoftClassPtr NodeInstanceClass; + + /** SubNodes that are owned by this UFlowGraphNode */ + UPROPERTY() + TArray> SubNodes; + + /** subnode's parent index assigned during copy operation to connect nodes again on paste */ + UPROPERTY() + int32 CopySubNodeParentIndex = INDEX_NONE; + + /** subnode index assigned during copy operation to connect nodes again on paste */ + UPROPERTY() + int32 CopySubNodeIndex = INDEX_NONE; + + /** if set, this node will be always considered as subnode */ + UPROPERTY() + bool bIsSubNode = false; + + /** if set, this node has context pins from the last RefreshContextPins */ + UPROPERTY() + bool bHasContextPins = false; + + /** error message for node */ + UPROPERTY() + FString ErrorMessage; + +private: + /** parent UFlowGraphNode for this node, + * note, this is not saved, and is restored in when the graph is opened in the editor via + * UFlowGraph::RecursivelySetParentNodeForAllSubNodes */ + UPROPERTY(Transient) + TObjectPtr ParentNode; }; diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_Branch.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_Branch.h new file mode 100644 index 000000000..ff5836bc6 --- /dev/null +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_Branch.h @@ -0,0 +1,16 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Graph/Nodes/FlowGraphNode.h" +#include "FlowGraphNode_Branch.generated.h" + +UCLASS() +class FLOWEDITOR_API UFlowGraphNode_Branch : public UFlowGraphNode +{ + GENERATED_UCLASS_BODY() + + // UEdGraphNode + virtual FSlateIcon GetIconAndTint(FLinearColor& OutColor) const override; + // -- +}; diff --git a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h index a97eade0d..83a8179d5 100644 --- a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h @@ -33,28 +33,60 @@ class FLOWEDITOR_API SFlowGraphNode : public SGraphNode virtual void GetOverlayBrushes(bool bSelected, const FVector2D WidgetSize, TArray& Brushes) const override; // -- + // SGraphNode virtual void GetPinBrush(const bool bLeftSide, const float WidgetWidth, const int32 PinIndex, const FFlowPinTrait& Breakpoint, TArray& Brushes) const; - // SGraphNode + virtual FText GetTitle() const; + virtual FText GetDescription() const; + virtual EVisibility GetDescriptionVisibility() const; + + virtual FText GetPreviewCornerText() const; + virtual const FSlateBrush* GetNameIcon() const; + + virtual FSlateColor GetBorderBackgroundColor() const; + + virtual FSlateColor GetConfigBoxBackgroundColor() const; + + /** adds subnode widget inside current node */ + virtual void AddSubNode(TSharedPtr SubNodeWidget); + // -- + + // SGraphNode Interface + virtual TSharedPtr GetComplexTooltip() override; + virtual void OnDragEnter(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) override; + virtual FReply OnDragOver(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) override; + virtual FReply OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) override; + + virtual void OnDragLeave(const FDragDropEvent& DragDropEvent) override; + virtual FReply OnMouseMove(const FGeometry& SenderGeometry, const FPointerEvent& MouseEvent) override; + virtual TSharedRef GetNodeUnderMouse(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; + virtual void SetOwner(const TSharedRef& OwnerPanel) override; + virtual void AddPin(const TSharedRef& PinToAdd) override; + virtual void UpdateGraphNode() override; virtual void UpdateErrorInfo() override; virtual TSharedRef CreateTitleWidget(TSharedPtr NodeTitle) override; virtual TSharedRef CreateNodeContentArea() override; + virtual void CreateBelowPinControls(TSharedPtr MainBox) override; virtual const FSlateBrush* GetNodeBodyBrush() const override; + virtual void CreateStandardPinWidget(UEdGraphPin* Pin) override; + + virtual void CreateInputSideAddButton(TSharedPtr OutputBox) override; + virtual void CreateOutputSideAddButton(TSharedPtr OutputBox) override; + // -- + + // SWidget + virtual FReply OnMouseButtonDown(const FGeometry& SenderGeometry, const FPointerEvent& MouseEvent) override; + // -- + // purposely overriden non-virtual methods, added PR #9791 to made these methods virtual: https://github.com/EpicGames/UnrealEngine/pull/9791 FSlateColor GetNodeTitleColor() const; FSlateColor GetNodeBodyColor() const; FSlateColor GetNodeTitleIconColor() const; FLinearColor GetNodeTitleTextColor() const; TSharedPtr GetEnabledStateWidget() const; - - virtual void CreateStandardPinWidget(UEdGraphPin* Pin) override; - virtual TSharedPtr GetComplexTooltip() override; - - virtual void CreateInputSideAddButton(TSharedPtr OutputBox) override; - virtual void CreateOutputSideAddButton(TSharedPtr OutputBox) override; // -- // Variant of SGraphNode::AddPinButtonContent @@ -63,9 +95,54 @@ class FLOWEDITOR_API SFlowGraphNode : public SGraphNode // Variant of SGraphNode::OnAddPin virtual FReply OnAddFlowPin(const EEdGraphPinDirection Direction); -private: - static int32 ValidPinsCount(const TArray& Pins); +protected: + + /** adds a sub node widget inside current node */ + void AddSubNodeWidget(TSharedPtr NewSubNodeWidget); + + /** removes dragged subnodes from the current node, + * bInOutReorderOperation reports if this is a simple "reorder" internally within the node or + * if one or more of the removed SubNodes will be removed from the node completely */ + void RemoveDraggedSubNodes(const TArray< TSharedRef >& DraggedNodes, bool& bInOutReorderOperation); + + bool ShouldDropDraggedNodesAsSubNodes(const TArray>& DraggedNodes, UFlowGraphNode* DropTargetNode) const; + + /** gets decorator or service node if one is found under mouse cursor */ + TSharedPtr GetSubNodeUnderCursor(const FGeometry& WidgetGeometry, const FPointerEvent& MouseEvent); + + /** gets drag over marker visibility */ + EVisibility GetDragOverMarkerVisibility() const; + + /** sets drag marker visible or collapsed on this node */ + void SetDragMarker(bool bEnabled); + + FMargin ComputeSubNodeChildIndentPaddingMargin() const; + + void CreateConfigText(TSharedPtr MainBox); + FText GetNodeConfigText() const; + EVisibility GetNodeConfigTextVisibility() const; + + void CreateOrRebuildSubNodeBox(TSharedPtr MainBox); + + bool IsFlowGraphNodeSelected(UFlowGraphNode* Node) const; protected: + // The FlowGraphNode this slate widget is representing UFlowGraphNode* FlowGraphNode = nullptr; + + // SubNodes that are embedded in this widget + TArray> SubNodes; + + // Is the drag marker currently visible? + bool bDragMarkerVisible = false; + + // Box to hold the SubNode widgets + TSharedPtr SubNodeBox; + + // Config Text Block widget + TSharedPtr ConfigTextBlock; + +public: + static const FLinearColor UnselectedNodeTint; + static const FLinearColor ConfigBoxColor; }; diff --git a/Source/FlowEditor/Public/Graph/Widgets/SGraphEditorActionMenuFlow.h b/Source/FlowEditor/Public/Graph/Widgets/SGraphEditorActionMenuFlow.h new file mode 100644 index 000000000..1d2336f8b --- /dev/null +++ b/Source/FlowEditor/Public/Graph/Widgets/SGraphEditorActionMenuFlow.h @@ -0,0 +1,69 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +// Adapted from SGraphEditorActionMenuAI, changing UAIGraphNode to UEdGraphNode, and using UFlowGraphSchema + +#pragma once + +#include "Containers/Array.h" +#include "CoreMinimal.h" +#include "EdGraph/EdGraphSchema.h" +#include "GraphEditor.h" +#include "HAL/PlatformMath.h" +#include "Math/Vector2D.h" +#include "Templates/SharedPointer.h" +#include "Types/SlateEnums.h" +#include "Widgets/DeclarativeSyntaxSupport.h" +#include "Widgets/Layout/SBorder.h" + +class SEditableTextBox; +class SGraphActionMenu; +class UEdGraphNode; +class UEdGraph; +class UEdGraphPin; +struct FEdGraphSchemaAction; +struct FGraphActionListBuilderBase; + +///////////////////////////////////////////////////////////////////////////////////////////////// + +class FLOWEDITOR_API SGraphEditorActionMenuFlow : public SBorder +{ +public: + SLATE_BEGIN_ARGS(SGraphEditorActionMenuFlow) + : _GraphObj( static_cast(nullptr) ) + , _GraphNode(nullptr) + , _NewNodePosition( FVector2D::ZeroVector ) + , _AutoExpandActionMenu( false ) + , _SubNodeFlags(0) + {} + + SLATE_ARGUMENT( UEdGraph*, GraphObj ) + SLATE_ARGUMENT( UEdGraphNode*, GraphNode) + SLATE_ARGUMENT( FVector2D, NewNodePosition ) + SLATE_ARGUMENT( TArray, DraggedFromPins ) + SLATE_ARGUMENT( SGraphEditor::FActionMenuClosed, OnClosedCallback ) + SLATE_ARGUMENT( bool, AutoExpandActionMenu ) + SLATE_ARGUMENT( int32, SubNodeFlags) + SLATE_END_ARGS() + + void Construct( const FArguments& InArgs ); + + ~SGraphEditorActionMenuFlow(); + + TSharedRef GetFilterTextBox(); + +protected: + UEdGraph* GraphObj; + UEdGraphNode* GraphNode; + TArray DraggedFromPins; + FVector2D NewNodePosition; + bool AutoExpandActionMenu; + int32 SubNodeFlags; + + SGraphEditor::FActionMenuClosed OnClosedCallback; + TSharedPtr GraphActionMenu; + + void OnActionSelected( const TArray< TSharedPtr >& SelectedAction, ESelectInfo::Type InSelectionType ); + + /** Callback used to populate all actions list in SGraphActionMenu */ + void CollectAllActions(FGraphActionListBuilderBase& OutAllActions); +}; diff --git a/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.h b/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.h new file mode 100644 index 000000000..08c40a9ec --- /dev/null +++ b/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.h @@ -0,0 +1,21 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "AssetTypeActions/AssetTypeActions_Blueprint.h" + +class FLOWEDITOR_API FAssetTypeActions_FlowNodeAddOnBlueprint : public FAssetTypeActions_Blueprint +{ +public: + virtual FText GetName() const override; + virtual uint32 GetCategories() override; + virtual FColor GetTypeColor() const override { return FColor(255, 196, 128); } + + virtual UClass* GetSupportedClass() const override; + +protected: + // FAssetTypeActions_Blueprint + virtual bool CanCreateNewDerivedBlueprint() const override { return false; } + virtual UFactory* GetFactoryForBlueprintType(UBlueprint* InBlueprint) const override; + // -- +}; diff --git a/Source/FlowEditor/Public/Nodes/FlowNodeBlueprintFactory.h b/Source/FlowEditor/Public/Nodes/FlowNodeBlueprintFactory.h index d3b8a129e..9ce489369 100644 --- a/Source/FlowEditor/Public/Nodes/FlowNodeBlueprintFactory.h +++ b/Source/FlowEditor/Public/Nodes/FlowNodeBlueprintFactory.h @@ -5,18 +5,39 @@ #include "Factories/Factory.h" #include "FlowNodeBlueprintFactory.generated.h" -UCLASS(hidecategories=Object) -class FLOWEDITOR_API UFlowNodeBlueprintFactory : public UFactory +UCLASS(Abstract, hidecategories=Object) +class UFlowNodeBaseBlueprintFactory : public UFactory { GENERATED_UCLASS_BODY() + // The Default parent class of the created blueprint (set by subclasses) + UPROPERTY() + TSubclassOf DefaultParentClass; + // The parent class of the created blueprint UPROPERTY(EditAnywhere, Category = "FlowNodeBlueprintFactory") - TSubclassOf ParentClass; + TSubclassOf ParentClass; // UFactory virtual bool ConfigureProperties() override; virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn, FName CallingContext) override; virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; + + void ShowCannotCreateBlueprintDialog(); // -- }; + +// Specialization of UFlowNodeBaseBlueprintFactory for UFlowNode blueprints +UCLASS(hidecategories = Object) +class FLOWEDITOR_API UFlowNodeBlueprintFactory : public UFlowNodeBaseBlueprintFactory +{ + GENERATED_UCLASS_BODY() +}; + +// Specialization of UFlowNodeBaseBlueprintFactory for UFlowNodeAddOn blueprints +UCLASS(hidecategories = Object) +class FLOWEDITOR_API UFlowNodeAddOnBlueprintFactory : public UFlowNodeBaseBlueprintFactory +{ + GENERATED_UCLASS_BODY() + +}; diff --git a/Source/FlowEditor/Public/UnrealExtensions/IFlowCuratedNamePropertyCustomization.h b/Source/FlowEditor/Public/UnrealExtensions/IFlowCuratedNamePropertyCustomization.h index adbd874af..cabc2636a 100644 --- a/Source/FlowEditor/Public/UnrealExtensions/IFlowCuratedNamePropertyCustomization.h +++ b/Source/FlowEditor/Public/UnrealExtensions/IFlowCuratedNamePropertyCustomization.h @@ -36,18 +36,23 @@ class FLOWEDITOR_API IFlowCuratedNamePropertyCustomization : public IFlowExtende bool CustomIsResetToDefaultVisible(TSharedPtr Property) const; void CustomResetToDefault(TSharedPtr Property); + bool CustomIsEnabled() const; // IFlowCuratedNamePropertyCustomization virtual TSharedPtr GetCuratedNamePropertyHandle() const = 0; virtual void SetCuratedName(const FName& NewName) = 0; - virtual FName GetCuratedName() const = 0; + virtual bool TryGetCuratedName(FName& OutName) const = 0; virtual TArray GetCuratedNameOptions() const = 0; + virtual bool AllowNameNoneIfOtherOptionsExist() const { return true; } // --- public: // Cached property handle for the Curated Name property that is being customized TSharedPtr CachedNameHandle; + // Cached PropertyUtils + TSharedPtr CachedPropertyUtils; + // Cache FTexts for the ComboBox dropdown & current selected TArray> CachedTextList; TSharedPtr CachedTextSelected; diff --git a/Source/FlowEditor/Public/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h b/Source/FlowEditor/Public/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h index 8fa1d9db0..512cab399 100644 --- a/Source/FlowEditor/Public/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h +++ b/Source/FlowEditor/Public/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h @@ -63,7 +63,8 @@ StructT* IFlowExtendedPropertyTypeCustomization::TryGetTypedStructValue(const TS TArray RawData; StructPropertyHandle->AccessRawData(RawData); - if (RawData.Num() > 0) + // (must be exactly one, multi-select is not supported for this feature) + if (RawData.Num() == 1) { return reinterpret_cast(RawData[0]); } From a9ecfa81b2ed8fcd26668c042aaf8f73f5775265 Mon Sep 17 00:00:00 2001 From: FoolsTheoryDev Date: Sun, 16 Jun 2024 16:37:20 +0200 Subject: [PATCH 241/485] cosmetic pass on AddOns refactor - Adding Category to a few functions which fixes build machine compilation in the plugin mode. - Partially restoring class layout to its original shape, taking in account new sections, and improve it a little bit. - Reversing some changes on making symbols editor-only which might break non-editor tools working with Flow Graph. - Static analysis fixes. --- Source/Flow/Private/AddOns/FlowNodeAddOn.cpp | 14 +- Source/Flow/Private/Nodes/FlowNode.cpp | 12 +- Source/Flow/Private/Nodes/FlowNodeBase.cpp | 648 +++++++++--------- Source/Flow/Public/AddOns/FlowNodeAddOn.h | 40 +- Source/Flow/Public/Nodes/FlowNode.h | 2 + Source/Flow/Public/Nodes/FlowNodeBase.h | 286 ++++---- Source/FlowEditor/Private/Graph/FlowGraph.cpp | 2 +- .../Private/Graph/FlowGraphSchema.cpp | 19 +- Source/FlowEditor/Public/Graph/FlowGraph.h | 3 +- .../FlowEditor/Public/Graph/FlowGraphSchema.h | 18 +- 10 files changed, 540 insertions(+), 504 deletions(-) diff --git a/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp b/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp index 6fea958be..b181d1f00 100644 --- a/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp +++ b/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp @@ -29,7 +29,7 @@ void UFlowNodeAddOn::TriggerFirstOutput(const bool bFinish) } } -void UFlowNodeAddOn::TriggerOutput(const FName PinName, const bool bFinish , const EFlowPinActivationType ActivationType) +void UFlowNodeAddOn::TriggerOutput(const FName PinName, const bool bFinish, const EFlowPinActivationType ActivationType) { if (ensure(FlowNode)) { @@ -47,8 +47,7 @@ void UFlowNodeAddOn::Finish() EFlowAddOnAcceptResult UFlowNodeAddOn::AcceptFlowNodeAddOnParent_Implementation(const UFlowNodeBase* ParentTemplate) const { - // Subclasses may override this function to opt-in to parent classes - + // Subclasses may override this function to opt in to parent classes return EFlowAddOnAcceptResult::Undetermined; } @@ -69,14 +68,7 @@ bool UFlowNodeAddOn::IsSupportedInputPinName(const FName& PinName) const return true; } - if (const FFlowPin* FoundFlowPin = FindFlowPinByName(PinName, InputPins)) - { - return true; - } - else - { - return false; - } + return FindFlowPinByName(PinName, InputPins) ? true : false; } void UFlowNodeAddOn::CacheFlowNode() diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index f07455bc8..0784de2ce 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -1,7 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "Nodes/FlowNode.h" -#include "Interfaces/FlowOwnerInterface.h" #include "AddOns/FlowNodeAddOn.h" #include "FlowAsset.h" @@ -82,14 +81,7 @@ bool UFlowNode::IsSupportedInputPinName(const FName& PinName) const return true; } - if (const FFlowPin* FoundInputFlowPin = FindFlowPinByName(PinName, InputPins)) - { - return true; - } - else - { - return false; - } + return FindFlowPinByName(PinName, InputPins) ? true : false; } void UFlowNode::AddInputPins(TArray Pins) @@ -112,7 +104,7 @@ void UFlowNode::AddOutputPins(TArray Pins) bool UFlowNode::RebuildPinArray(const TArray& NewPinNames, TArray& InOutPins, const FFlowPin& DefaultPin) { - bool bIsChanged = false; + bool bIsChanged; TArray NewPins; diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp index 9379b1d23..b431a7b5e 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBase.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -5,7 +5,6 @@ #include "AddOns/FlowNodeAddOn.h" #include "FlowAsset.h" #include "FlowLogChannels.h" -#include "FlowSettings.h" #include "FlowSubsystem.h" #include "FlowTypes.h" @@ -29,117 +28,187 @@ UFlowNodeBase::UFlowNodeBase(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) + , GraphNode(nullptr) +#if WITH_EDITORONLY_DATA + , bCanDelete(true) + , bCanDuplicate(true) + , bNodeDeprecated(false) + , NodeStyle(EFlowNodeStyle::Default) + , NodeColor(FLinearColor::Black) +#endif { } -#if WITH_EDITOR -void UFlowNodeBase::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +UWorld* UFlowNodeBase::GetWorld() const { - Super::PostEditChangeProperty(PropertyChangedEvent); + if (const UFlowAsset* FlowAsset = GetFlowAsset()) + { + if (const UObject* FlowAssetOwner = FlowAsset->GetOwner()) + { + return FlowAssetOwner->GetWorld(); + } + } - if (!PropertyChangedEvent.Property) + if (const UFlowSubsystem* FlowSubsystem = GetFlowSubsystem()) { - return; + return FlowSubsystem->GetWorld(); } - const FName PropertyName = PropertyChangedEvent.GetPropertyName(); + return nullptr; +} - if (PropertyName == GET_MEMBER_NAME_CHECKED(UFlowNode, AddOns)) +void UFlowNodeBase::InitializeInstance() +{ + IFlowCoreExecutableInterface::InitializeInstance(); + + if (!AddOns.IsEmpty()) { - // Potentially need to rebuild the pins from the AddOns of this node - OnReconstructionRequested.ExecuteIfBound(); + TArray SourceAddOns = AddOns; + AddOns.Reset(); + + for (UFlowNodeAddOn* SourceAddOn : SourceAddOns) + { + // Create a new instance of each AddOn + UFlowNodeAddOn* NewAddOnInstance = NewObject(this, SourceAddOn->GetClass(), NAME_None, RF_Transient, SourceAddOn, false, nullptr); + AddOns.Add(NewAddOnInstance); + } + + for (UFlowNodeAddOn* AddOn : AddOns) + { + // Initialize all the AddOn instances after they are all allocated + AddOn->InitializeInstance(); + } } +} - UpdateNodeConfigText(); +void UFlowNodeBase::DeinitializeInstance() +{ + for (UFlowNodeAddOn* AddOn : AddOns) + { + AddOn->DeinitializeInstance(); + } + + IFlowCoreExecutableInterface::DeinitializeInstance(); } -void UFlowNodeBase::FixNode(UEdGraphNode* NewGraphNode) +void UFlowNodeBase::PreloadContent() { - // Fix any node pointers that may be out of date - if (NewGraphNode) + IFlowCoreExecutableInterface::PreloadContent(); + + for (UFlowNodeAddOn* AddOn : AddOns) { - GraphNode = NewGraphNode; + AddOn->PreloadContent(); } } -void UFlowNodeBase::SetGraphNode(UEdGraphNode* NewGraphNode) +void UFlowNodeBase::FlushContent() { - GraphNode = NewGraphNode; + for (UFlowNodeAddOn* AddOn : AddOns) + { + AddOn->FlushContent(); + } - UpdateNodeConfigText(); + IFlowCoreExecutableInterface::FlushContent(); } -void UFlowNodeBase::SetupForEditing(UEdGraphNode& EdGraphNode) +void UFlowNodeBase::OnActivate() { - SetGraphNode(&EdGraphNode); + IFlowCoreExecutableInterface::OnActivate(); - // Refresh the Config text when setting up this FlowNodeBase for editing - UpdateNodeConfigText(); + for (UFlowNodeAddOn* AddOn : AddOns) + { + AddOn->OnActivate(); + } } -FString UFlowNodeBase::GetNodeCategory() const +void UFlowNodeBase::ExecuteInput(const FName& PinName) { - if (GetClass()->ClassGeneratedBy) + // AddOns can introduce input pins to Nodes without the Node being aware of the addition. + // To ensure that Nodes and AddOns only get the input pins signalled that they expect, + // we are filtering the PinName vs. the expected InputPins before carrying on with the ExecuteInput + + if (IsSupportedInputPinName(PinName)) { - const FString& BlueprintCategory = Cast(GetClass()->ClassGeneratedBy)->BlueprintCategory; - if (!BlueprintCategory.IsEmpty()) - { - return BlueprintCategory; - } + IFlowCoreExecutableInterface::ExecuteInput(PinName); } - return Category; + for (UFlowNodeAddOn* AddOn : AddOns) + { + AddOn->ExecuteInput(PinName); + } } -FText UFlowNodeBase::GetNodeTitle() const +void UFlowNodeBase::ForceFinishNode() { - if (GetClass()->ClassGeneratedBy) + for (UFlowNodeAddOn* AddOn : AddOns) { - const FString& BlueprintTitle = Cast(GetClass()->ClassGeneratedBy)->BlueprintDisplayName; - if (!BlueprintTitle.IsEmpty()) - { - return FText::FromString(BlueprintTitle); - } + AddOn->ForceFinishNode(); } - return GetClass()->GetDisplayNameText(); + IFlowCoreExecutableInterface::ForceFinishNode(); } -FText UFlowNodeBase::GetNodeToolTip() const +void UFlowNodeBase::Cleanup() { - if (GetClass()->ClassGeneratedBy) + for (UFlowNodeAddOn* AddOn : AddOns) { - const FString& BlueprintToolTip = Cast(GetClass()->ClassGeneratedBy)->BlueprintDescription; - if (!BlueprintToolTip.IsEmpty()) - { - return FText::FromString(BlueprintToolTip); - } + AddOn->Cleanup(); } - return GetClass()->GetToolTipText(); + IFlowCoreExecutableInterface::Cleanup(); } -FText UFlowNodeBase::GetNodeConfigText() const +const FFlowPin* UFlowNodeBase::FindFlowPinByName(const FName& PinName, const TArray& FlowPins) { - return DevNodeConfigText; + return FlowPins.FindByPredicate([&PinName](const FFlowPin& FlowPin) + { + return FlowPin.PinName == PinName; + }); } -bool UFlowNodeBase::GetDynamicTitleColor(FLinearColor& OutColor) const +#if WITH_EDITOR +TArray UFlowNodeBase::GetContextInputs() const { - if (NodeStyle == EFlowNodeStyle::Custom) + TArray ContextInputs = IFlowContextPinSupplierInterface::GetContextInputs(); + TArray AddOnInputs; + + for (const UFlowNodeAddOn* AddOn : AddOns) { - OutColor = NodeColor; - return true; + AddOnInputs.Append(AddOn->GetContextInputs()); } - return false; + if (!AddOnInputs.IsEmpty()) + { + for (const FFlowPin& FlowPin : AddOnInputs) + { + ContextInputs.AddUnique(FlowPin); + } + } + + return ContextInputs; } -FString UFlowNodeBase::GetNodeDescription() const +TArray UFlowNodeBase::GetContextOutputs() const { - return K2_GetNodeDescription(); + TArray ContextOutputs = IFlowContextPinSupplierInterface::GetContextOutputs(); + TArray AddOnOutputs; + + for (const UFlowNodeAddOn* AddOn : AddOns) + { + AddOnOutputs.Append(AddOn->GetContextOutputs()); + } + + if (!AddOnOutputs.IsEmpty()) + { + for (const FFlowPin& FlowPin : AddOnOutputs) + { + ContextOutputs.AddUnique(FlowPin); + } + } + + return ContextOutputs; } -#endif +#endif // WITH_EDITOR UFlowAsset* UFlowNodeBase::GetFlowAsset() const { @@ -148,6 +217,11 @@ UFlowAsset* UFlowNodeBase::GetFlowAsset() const return FlowNode && FlowNode->GetOuter() ? Cast(FlowNode->GetOuter()) : Cast(GetOuter()); } +const UFlowNode* UFlowNodeBase::GetFlowNodeSelfOrOwner() const +{ + return const_cast(this)->GetFlowNodeSelfOrOwner(); +} + UFlowSubsystem* UFlowNodeBase::GetFlowSubsystem() const { return GetFlowAsset() ? GetFlowAsset()->GetFlowSubsystem() : nullptr; @@ -166,8 +240,7 @@ AActor* UFlowNodeBase::TryGetRootFlowActorOwner() const if (!IsValid(OwningActor)) { - // Check if the if the immediate parent is an UActorComponent - // and return that Component's Owning actor + // Check if the immediate parent is an UActorComponent and return that Component's Owning actor if (const UActorComponent* OwningComponent = Cast(RootFlowOwner)) { OwningActor = OwningComponent->GetOwner(); @@ -223,7 +296,7 @@ IFlowOwnerInterface* UFlowNodeBase::GetFlowOwnerInterface() const return nullptr; } -IFlowOwnerInterface* UFlowNodeBase::TryGetFlowOwnerInterfaceFromRootFlowOwner(UObject& RootFlowOwner, const UClass& ExpectedOwnerClass) const +IFlowOwnerInterface* UFlowNodeBase::TryGetFlowOwnerInterfaceFromRootFlowOwner(UObject& RootFlowOwner, const UClass& ExpectedOwnerClass) { const UClass* RootFlowOwnerClass = RootFlowOwner.GetClass(); if (!IsValid(RootFlowOwnerClass)) @@ -240,7 +313,7 @@ IFlowOwnerInterface* UFlowNodeBase::TryGetFlowOwnerInterfaceFromRootFlowOwner(UO return CastChecked(&RootFlowOwner); } -IFlowOwnerInterface* UFlowNodeBase::TryGetFlowOwnerInterfaceActor(UObject& RootFlowOwner, const UClass& ExpectedOwnerClass) const +IFlowOwnerInterface* UFlowNodeBase::TryGetFlowOwnerInterfaceActor(UObject& RootFlowOwner, const UClass& ExpectedOwnerClass) { // Special case if the immediate owner is a component, also consider the component's owning actor const UActorComponent* FlowComponent = Cast(&RootFlowOwner); @@ -264,384 +337,339 @@ IFlowOwnerInterface* UFlowNodeBase::TryGetFlowOwnerInterfaceActor(UObject& RootF return CastChecked(ActorOwner); } -void UFlowNodeBase::SetNodeConfigText(const FText& NodeConfigText) +EFlowAddOnAcceptResult UFlowNodeBase::AcceptFlowNodeAddOnChild_Implementation(const UFlowNodeAddOn* AddOnTemplate) const { + // Subclasses may override this function to allow AddOn children classes + return EFlowAddOnAcceptResult::Undetermined; +} + #if WITH_EDITOR - if (!NodeConfigText.EqualTo(DevNodeConfigText)) +EFlowAddOnAcceptResult UFlowNodeBase::CheckAcceptFlowNodeAddOnChild(const UFlowNodeAddOn* AddOnTemplate) const +{ + if (!IsValid(AddOnTemplate)) { - DevNodeConfigText = NodeConfigText; - - Modify(); + return EFlowAddOnAcceptResult::Reject; } -#endif // WITH_EDITOR -} -void UFlowNodeBase::UpdateNodeConfigText_Implementation() -{ -} + static_assert(static_cast<__underlying_type(EFlowAddOnAcceptResult)>(EFlowAddOnAcceptResult::Max) == 3, "This code may need updating if the enum values change"); -UWorld* UFlowNodeBase::GetWorld() const -{ - if (UFlowAsset* FlowAsset = GetFlowAsset()) + EFlowAddOnAcceptResult CombinedResult = EFlowAddOnAcceptResult::Undetermined; + + const EFlowAddOnAcceptResult AsChildResult = AcceptFlowNodeAddOnChild(AddOnTemplate); // Potential parents of AddOns are allowed to decide their eligible AddOn children + CombinedResult = CombineFlowAddOnAcceptResult(AsChildResult, CombinedResult); + + if (CombinedResult == EFlowAddOnAcceptResult::Reject) { - if (UObject* FlowAssetOwner = FlowAsset->GetOwner()) - { - return FlowAssetOwner->GetWorld(); - } + return EFlowAddOnAcceptResult::Reject; } - if (UFlowSubsystem* FlowSubsystem = GetFlowSubsystem()) + // FlowNodeAddOns are allowed to opt in to their parent + const EFlowAddOnAcceptResult AsParentResult = AddOnTemplate->AcceptFlowNodeAddOnParent(this); + + if (AsParentResult != EFlowAddOnAcceptResult::Reject && + AddOnTemplate->IsA()) { - return FlowSubsystem->GetWorld(); + const FString Message = FString::Printf(TEXT("%s::AcceptFlowNodeAddOnParent must always Reject for UFlowNode subclasses"), *GetClass()->GetName()); + GetFlowAsset()->GetTemplateAsset()->LogError(Message, const_cast(this)); + + return EFlowAddOnAcceptResult::Reject; } - return nullptr; + CombinedResult = CombineFlowAddOnAcceptResult(AsParentResult, CombinedResult); + + return CombinedResult; } +#endif // WITH_EDITOR -void UFlowNodeBase::LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType) +void UFlowNodeBase::ForEachAddOnConst(const FConstFlowNodeAddOnFunction& Function) const { -#if !UE_BUILD_SHIPPING - if (BuildMessage(Message)) + for (const UFlowNodeAddOn* AddOn : AddOns) { - // OnScreen Message - if (OnScreenMessageType == EFlowOnScreenMessageType::Permanent) + if (IsValid(AddOn)) { - if (GetWorld()) - { - if (UViewportStatsSubsystem* StatsSubsystem = GetWorld()->GetSubsystem()) - { - StatsSubsystem->AddDisplayDelegate([this, Message](FText& OutText, FLinearColor& OutColor) - { - OutText = FText::FromString(Message); - OutColor = FLinearColor::Red; + Function(*AddOn); - return IsValid(this) && GetFlowNodeSelfOrOwner()->GetActivationState() != EFlowNodeState::NeverActivated; - }); - } - } - } - else - { - GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, Message); + AddOn->ForEachAddOnConst(Function); } + } +} - // Output Log - UE_LOG(LogFlow, Error, TEXT("%s"), *Message); +void UFlowNodeBase::ForEachAddOn(const FFlowNodeAddOnFunction& Function) const +{ + for (UFlowNodeAddOn* AddOn : AddOns) + { + if (IsValid(AddOn)) + { + Function(*AddOn); - // Message Log -#if WITH_EDITOR - GetFlowAsset()->GetTemplateAsset()->LogError(Message, this); -#endif + AddOn->ForEachAddOn(Function); + } } -#endif } -void UFlowNodeBase::LogWarning(FString Message) +void UFlowNodeBase::ForEachAddOnForClassConst(const UClass& InterfaceOrClass, const FConstFlowNodeAddOnFunction& Function) const { -#if !UE_BUILD_SHIPPING - if (BuildMessage(Message)) + for (const UFlowNodeAddOn* AddOn : AddOns) { - // Output Log - UE_LOG(LogFlow, Warning, TEXT("%s"), *Message); + if (IsValid(AddOn)) + { + // InterfaceOrClass can either be the AddOn's UClass (or its superclass) + // or an interface (the UClass version) that its UClass implements + if (AddOn->IsA(&InterfaceOrClass) || AddOn->GetClass()->ImplementsInterface(&InterfaceOrClass)) + { + Function(*AddOn); + } - // Message Log -#if WITH_EDITOR - GetFlowAsset()->GetTemplateAsset()->LogWarning(Message, this); -#endif + AddOn->ForEachAddOnForClassConst(InterfaceOrClass, Function); + } } -#endif } -void UFlowNodeBase::LogNote(FString Message) +void UFlowNodeBase::ForEachAddOnForClass(const UClass& InterfaceOrClass, const FFlowNodeAddOnFunction& Function) const { -#if !UE_BUILD_SHIPPING - if (BuildMessage(Message)) + for (UFlowNodeAddOn* AddOn : AddOns) { - // Output Log - UE_LOG(LogFlow, Log, TEXT("%s"), *Message); + if (IsValid(AddOn)) + { + // InterfaceOrClass can either be the AddOn's UClass (or its superclass) + // or an interface (the UClass version) that its UClass implements + if (AddOn->IsA(&InterfaceOrClass) || AddOn->GetClass()->ImplementsInterface(&InterfaceOrClass)) + { + Function(*AddOn); + } - // Message Log -#if WITH_EDITOR - GetFlowAsset()->GetTemplateAsset()->LogNote(Message, this); -#endif + AddOn->ForEachAddOnForClass(InterfaceOrClass, Function); + } } -#endif } -#if !UE_BUILD_SHIPPING -bool UFlowNodeBase::BuildMessage(FString& Message) const +#if WITH_EDITOR +void UFlowNodeBase::SetGraphNode(UEdGraphNode* NewGraphNode) { - if (GetFlowAsset()->GetTemplateAsset()) // this is runtime log which is should be only called on runtime instances of asset - { - const FString TemplatePath = GetFlowAsset()->GetTemplateAsset()->GetPathName(); - Message.Append(TEXT(" --- node ")).Append(GetName()).Append(TEXT(", asset ")).Append(FPaths::GetPath(TemplatePath) / FPaths::GetBaseFilename(TemplatePath)); - - return true; - } + GraphNode = NewGraphNode; - return false; + UpdateNodeConfigText(); } -#endif - -EFlowAddOnAcceptResult UFlowNodeBase::AcceptFlowNodeAddOnChild_Implementation(const UFlowNodeAddOn* AddOnTemplate) const +void UFlowNodeBase::SetupForEditing(UEdGraphNode& EdGraphNode) { - // Subclasses may override this function to allow AddOn children classes - return EFlowAddOnAcceptResult::Undetermined; + SetGraphNode(&EdGraphNode); + + // Refresh the Config text when setting up this FlowNodeBase for editing + UpdateNodeConfigText(); } -#if WITH_EDITOR -EFlowAddOnAcceptResult UFlowNodeBase::CheckAcceptFlowNodeAddOnChild(const UFlowNodeAddOn* AddOnTemplate) const +void UFlowNodeBase::FixNode(UEdGraphNode* NewGraphNode) { - if (!IsValid(AddOnTemplate)) + // Fix any node pointers that may be out of date + if (NewGraphNode) { - return EFlowAddOnAcceptResult::Reject; + GraphNode = NewGraphNode; } +} - static_assert(static_cast<__underlying_type(EFlowAddOnAcceptResult)>(EFlowAddOnAcceptResult::Max) == 3, "This code may need updating if the enum values change"); - - EFlowAddOnAcceptResult CombinedResult = EFlowAddOnAcceptResult::Undetermined; - - // Potential parents of AddOns are allowed to decide their eligible AddOn children - const EFlowAddOnAcceptResult AsChildResult = AcceptFlowNodeAddOnChild(AddOnTemplate); +void UFlowNodeBase::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); - CombinedResult = CombineFlowAddOnAcceptResult(AsChildResult, CombinedResult); - if (CombinedResult == EFlowAddOnAcceptResult::Reject) + if (!PropertyChangedEvent.Property) { - return EFlowAddOnAcceptResult::Reject; + return; } - // FlowNodeAddOns are allowed to opt-in to their parent - const EFlowAddOnAcceptResult AsParentResult = AddOnTemplate->AcceptFlowNodeAddOnParent(this); - - if (AsParentResult != EFlowAddOnAcceptResult::Reject && - AddOnTemplate->IsA()) + const FName PropertyName = PropertyChangedEvent.GetPropertyName(); + if (PropertyName == GET_MEMBER_NAME_CHECKED(UFlowNode, AddOns)) { - const FString Message = FString::Printf(TEXT("%s::AcceptFlowNodeAddOnParent must always Reject for UFlowNode subclasses"), *GetClass()->GetName()); - GetFlowAsset()->GetTemplateAsset()->LogError(Message, const_cast(this)); - - return EFlowAddOnAcceptResult::Reject; + // Potentially need to rebuild the pins from the AddOns of this node + OnReconstructionRequested.ExecuteIfBound(); } - CombinedResult = CombineFlowAddOnAcceptResult(AsParentResult, CombinedResult); - - return CombinedResult; + UpdateNodeConfigText(); } -#endif // WITH_EDITOR -void UFlowNodeBase::InitializeInstance() +FString UFlowNodeBase::GetNodeCategory() const { - IFlowCoreExecutableInterface::InitializeInstance(); - - if (!AddOns.IsEmpty()) + if (GetClass()->ClassGeneratedBy) { - TArray SourceAddOns = AddOns; - AddOns.Reset(); - - for (UFlowNodeAddOn* SourceAddOn : SourceAddOns) - { - // Create a new instance of each AddOn - UFlowNodeAddOn* NewAddOnInstance = NewObject(this, SourceAddOn->GetClass(), NAME_None, RF_Transient, SourceAddOn, false, nullptr); - AddOns.Add(NewAddOnInstance); - } - - for (UFlowNodeAddOn* AddOn : AddOns) + const FString& BlueprintCategory = Cast(GetClass()->ClassGeneratedBy)->BlueprintCategory; + if (!BlueprintCategory.IsEmpty()) { - // Initialize all of the AddOn instances after they are all allocated - AddOn->InitializeInstance(); + return BlueprintCategory; } } -} - -void UFlowNodeBase::DeinitializeInstance() -{ - for (UFlowNodeAddOn* AddOn : AddOns) - { - AddOn->DeinitializeInstance(); - } - IFlowCoreExecutableInterface::DeinitializeInstance(); + return Category; } -void UFlowNodeBase::PreloadContent() +EFlowNodeStyle UFlowNodeBase::GetNodeStyle() const { - IFlowCoreExecutableInterface::PreloadContent(); - - for (UFlowNodeAddOn* AddOn : AddOns) - { - AddOn->PreloadContent(); - } + return NodeStyle; } -void UFlowNodeBase::FlushContent() +bool UFlowNodeBase::GetDynamicTitleColor(FLinearColor& OutColor) const { - for (UFlowNodeAddOn* AddOn : AddOns) + if (NodeStyle == EFlowNodeStyle::Custom) { - AddOn->FlushContent(); + OutColor = NodeColor; + return true; } - IFlowCoreExecutableInterface::FlushContent(); + return false; } -void UFlowNodeBase::OnActivate() +FText UFlowNodeBase::GetNodeTitle() const { - IFlowCoreExecutableInterface::OnActivate(); - - for (UFlowNodeAddOn* AddOn : AddOns) + if (GetClass()->ClassGeneratedBy) { - AddOn->OnActivate(); + const FString& BlueprintTitle = Cast(GetClass()->ClassGeneratedBy)->BlueprintDisplayName; + if (!BlueprintTitle.IsEmpty()) + { + return FText::FromString(BlueprintTitle); + } } + + return GetClass()->GetDisplayNameText(); } -void UFlowNodeBase::Cleanup() +FText UFlowNodeBase::GetNodeToolTip() const { - for (UFlowNodeAddOn* AddOn : AddOns) + if (GetClass()->ClassGeneratedBy) { - AddOn->Cleanup(); + const FString& BlueprintToolTip = Cast(GetClass()->ClassGeneratedBy)->BlueprintDescription; + if (!BlueprintToolTip.IsEmpty()) + { + return FText::FromString(BlueprintToolTip); + } } - IFlowCoreExecutableInterface::Cleanup(); + return GetClass()->GetToolTipText(); } -void UFlowNodeBase::ForceFinishNode() +FText UFlowNodeBase::GetNodeConfigText() const { - for (UFlowNodeAddOn* AddOn : AddOns) - { - AddOn->ForceFinishNode(); - } - - IFlowCoreExecutableInterface::ForceFinishNode(); + return DevNodeConfigText; } +#endif // WITH_EDITOR -void UFlowNodeBase::ExecuteInput(const FName& PinName) +void UFlowNodeBase::SetNodeConfigText(const FText& NodeConfigText) { - // AddOns can introduce input pins to Nodes without the Node being aware of the addition. - // To ensure that Nodes and AddOns only get the input pins signalled that they expect, - // we are filtering the PinName vs. the expected InputPins before carrying on with the ExecuteInput - - if (IsSupportedInputPinName(PinName)) +#if WITH_EDITOR + if (!NodeConfigText.EqualTo(DevNodeConfigText)) { - IFlowCoreExecutableInterface::ExecuteInput(PinName); - } + DevNodeConfigText = NodeConfigText; - for (UFlowNodeAddOn* AddOn : AddOns) - { - AddOn->ExecuteInput(PinName); + Modify(); } +#endif // WITH_EDITOR } -const FFlowPin* UFlowNodeBase::FindFlowPinByName(const FName& PinName, const TArray& FlowPins) +void UFlowNodeBase::UpdateNodeConfigText_Implementation() { - return FlowPins.FindByPredicate([&PinName](const FFlowPin& FlowPin) - { - return FlowPin.PinName == PinName; - }); } -void UFlowNodeBase::ForEachAddOnConst(FConstFlowNodeAddOnFunction Function) const +#if WITH_EDITOR +FString UFlowNodeBase::GetNodeDescription() const { - for (UFlowNodeAddOn* AddOn : AddOns) - { - if (IsValid(AddOn)) - { - Function(*AddOn); - - AddOn->ForEachAddOnConst(Function); - } - } + return K2_GetNodeDescription(); } +#endif // WITH_EDITOR -void UFlowNodeBase::ForEachAddOnForClassConst(const UClass& InterfaceOrClass, FConstFlowNodeAddOnFunction Function) const +void UFlowNodeBase::LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType) { - for (UFlowNodeAddOn* AddOn : AddOns) +#if !UE_BUILD_SHIPPING + if (BuildMessage(Message)) { - if (IsValid(AddOn)) + // OnScreen Message + if (OnScreenMessageType == EFlowOnScreenMessageType::Permanent) { - // InterfaceOrClass can either be the AddOn's UClass (or its superclass) - // or an interface (the UClass version) that its UClass implements - if (AddOn->IsA(&InterfaceOrClass) || - AddOn->GetClass()->ImplementsInterface(&InterfaceOrClass)) + if (GetWorld()) { - Function(*AddOn); - } + if (UViewportStatsSubsystem* StatsSubsystem = GetWorld()->GetSubsystem()) + { + StatsSubsystem->AddDisplayDelegate([this, Message](FText& OutText, FLinearColor& OutColor) + { + OutText = FText::FromString(Message); + OutColor = FLinearColor::Red; - AddOn->ForEachAddOnForClassConst(InterfaceOrClass, Function); + return IsValid(this) && GetFlowNodeSelfOrOwner()->GetActivationState() != EFlowNodeState::NeverActivated; + }); + } + } + } + else + { + GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, Message); } + + // Output Log + UE_LOG(LogFlow, Error, TEXT("%s"), *Message); + + // Message Log +#if WITH_EDITOR + GetFlowAsset()->GetTemplateAsset()->LogError(Message, this); +#endif } +#endif } -void UFlowNodeBase::ForEachAddOn(FFlowNodeAddOnFunction Function) const +void UFlowNodeBase::LogErrorConst(FString Message, const EFlowOnScreenMessageType OnScreenMessageType) const { - for (UFlowNodeAddOn* AddOn : AddOns) - { - if (IsValid(AddOn)) - { - Function(*AddOn); - - AddOn->ForEachAddOn(Function); - } - } + const_cast(this)->LogError(Message, OnScreenMessageType); } -void UFlowNodeBase::ForEachAddOnForClass(const UClass& InterfaceOrClass, FFlowNodeAddOnFunction Function) const +void UFlowNodeBase::LogWarning(FString Message) { - for (UFlowNodeAddOn* AddOn : AddOns) +#if !UE_BUILD_SHIPPING + if (BuildMessage(Message)) { - if (IsValid(AddOn)) - { - // InterfaceOrClass can either be the AddOn's UClass (or its superclass) - // or an interface (the UClass version) that its UClass implements - if (AddOn->IsA(&InterfaceOrClass) || - AddOn->GetClass()->ImplementsInterface(&InterfaceOrClass)) - { - Function(*AddOn); - } + // Output Log + UE_LOG(LogFlow, Warning, TEXT("%s"), *Message); - AddOn->ForEachAddOnForClass(InterfaceOrClass, Function); - } + // Message Log +#if WITH_EDITOR + GetFlowAsset()->GetTemplateAsset()->LogWarning(Message, this); +#endif } +#endif } -#if WITH_EDITOR -TArray UFlowNodeBase::GetContextInputs() const +void UFlowNodeBase::LogWarningConst(FString Message) const { - TArray ContextInputs = IFlowContextPinSupplierInterface::GetContextInputs(); - TArray AddOnInputs; + const_cast(this)->LogWarning(Message); +} - for (UFlowNodeAddOn* AddOn : AddOns) +void UFlowNodeBase::LogNote(FString Message) +{ +#if !UE_BUILD_SHIPPING + if (BuildMessage(Message)) { - AddOnInputs.Append(AddOn->GetContextInputs()); - } + // Output Log + UE_LOG(LogFlow, Log, TEXT("%s"), *Message); - if (!AddOnInputs.IsEmpty()) - { - for (const FFlowPin& FlowPin : AddOnInputs) - { - ContextInputs.AddUnique(FlowPin); - } + // Message Log +#if WITH_EDITOR + GetFlowAsset()->GetTemplateAsset()->LogNote(Message, this); +#endif } - - return ContextInputs; +#endif } -TArray UFlowNodeBase::GetContextOutputs() const +void UFlowNodeBase::LogNoteConst(FString Message) const { - TArray ContextOutputs = IFlowContextPinSupplierInterface::GetContextOutputs(); - TArray AddOnOutputs; + const_cast(this)->LogNote(Message); +} - for (UFlowNodeAddOn* AddOn : AddOns) +#if !UE_BUILD_SHIPPING +bool UFlowNodeBase::BuildMessage(FString& Message) const +{ + if (GetFlowAsset()->GetTemplateAsset()) // this is runtime log which is should be only called on runtime instances of asset { - AddOnOutputs.Append(AddOn->GetContextOutputs()); - } + const FString TemplatePath = GetFlowAsset()->GetTemplateAsset()->GetPathName(); + Message.Append(TEXT(" --- node ")).Append(GetName()).Append(TEXT(", asset ")).Append(FPaths::GetPath(TemplatePath) / FPaths::GetBaseFilename(TemplatePath)); - if (!AddOnOutputs.IsEmpty()) - { - for (const FFlowPin& FlowPin : AddOnOutputs) - { - ContextOutputs.AddUnique(FlowPin); - } + return true; } - return ContextOutputs; + return false; } -#endif // WITH_EDITOR +#endif diff --git a/Source/Flow/Public/AddOns/FlowNodeAddOn.h b/Source/Flow/Public/AddOns/FlowNodeAddOn.h index 5791df1ac..42590e0f6 100644 --- a/Source/Flow/Public/AddOns/FlowNodeAddOn.h +++ b/Source/Flow/Public/AddOns/FlowNodeAddOn.h @@ -8,9 +8,11 @@ #include "FlowNodeAddOn.generated.h" -// Forward Declarations class UFlowNode; +/** + * A Flow Node AddOn allows user to extend given node instance in the graph with additional logic. + */ UCLASS(Abstract, MinimalApi, EditInlineNew, Blueprintable) class UFlowNodeAddOn : public UFlowNodeBase @@ -18,12 +20,26 @@ class UFlowNodeAddOn { GENERATED_BODY() -public: +protected: + // The FlowNode that contains this AddOn + // (accessible only when initialized, runtime only) + UPROPERTY(Transient) + TObjectPtr FlowNode; + + // Input pins to add to the owning flow node + // If defined, ExecuteInput will only be executed for these inputs + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "FlowNodeAddOn") + TArray InputPins; + // Output pins to add to the owning flow node + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "FlowNodeAddOn") + TArray OutputPins; + +public: // UFlowNodeBase - // AddOns may opt-in to be eligible for a given parent - UFUNCTION(BlueprintNativeEvent, BlueprintPure) + // AddOns may opt in to be eligible for a given parent + UFUNCTION(BlueprintNativeEvent, BlueprintPure, Category = "FlowNodeAddOn") FLOW_API EFlowAddOnAcceptResult AcceptFlowNodeAddOnParent(const UFlowNodeBase* ParentTemplate) const; FLOW_API virtual UFlowNode* GetFlowNodeSelfOrOwner() override { return FlowNode; } @@ -56,20 +72,4 @@ class UFlowNodeAddOn protected: void CacheFlowNode(); - -protected: - - // The FlowNode that contains this AddOn - // (accessible only when initialized, runtime only) - UPROPERTY(Transient) - TObjectPtr FlowNode; - - // Input pins to add to the owning flow node - // If defined, ExecuteInput will only be executed for these inputs - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "FlowNodeAddOn") - TArray InputPins; - - // Output pins to add to the owning flow node - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "FlowNodeAddOn") - TArray OutputPins; }; diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index e66fef2c4..da3ce1f31 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -23,6 +23,7 @@ class FLOW_API UFlowNode , public IVisualLoggerDebugSnapshotInterface { GENERATED_UCLASS_BODY() + friend class SFlowGraphNode; friend class UFlowAsset; friend class UFlowGraphNode; @@ -172,6 +173,7 @@ class FLOW_API UFlowNode ////////////////////////////////////////////////////////////////////////// // Debugger + protected: static FString MissingIdentityTag; static FString MissingNotifyTag; diff --git a/Source/Flow/Public/Nodes/FlowNodeBase.h b/Source/Flow/Public/Nodes/FlowNodeBase.h index 07d9c32e8..abb2af44d 100644 --- a/Source/Flow/Public/Nodes/FlowNodeBase.h +++ b/Source/Flow/Public/Nodes/FlowNodeBase.h @@ -29,65 +29,66 @@ typedef TFunction FFlowNodeAddOnFunction; * The base class for UFlowNode and UFlowNodeAddOn, with their shared functionality */ UCLASS(Abstract, HideCategories = Object) -class FLOW_API UFlowNodeBase +class FLOW_API UFlowNodeBase : public UObject - , public IFlowCoreExecutableInterface - , public IFlowContextPinSupplierInterface + , public IFlowCoreExecutableInterface + , public IFlowContextPinSupplierInterface { GENERATED_UCLASS_BODY() + friend class SFlowGraphNode; + friend class UFlowAsset; + friend class UFlowGraphNode; + friend class UFlowGraphSchema; + +////////////////////////////////////////////////////////////////////////// +// Node + +public: // UObject virtual UWorld* GetWorld() const override; -#if WITH_EDITOR - virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; -#endif // -- // IFlowCoreExecutableInterface - virtual void InitializeInstance(); - virtual void DeinitializeInstance(); - virtual void PreloadContent(); - virtual void FlushContent(); - virtual void OnActivate(); - virtual void Cleanup(); - virtual void ForceFinishNode(); - virtual void ExecuteInput(const FName& PinName); - // -- + virtual void InitializeInstance() override; + virtual void DeinitializeInstance() override; - // UFlowNodeBase - virtual bool IsSupportedInputPinName(const FName& PinName) const PURE_VIRTUAL(IsSupportedInputPinName, return true;); + virtual void PreloadContent() override; + virtual void FlushContent() override; -protected: - // FlowNodes and AddOns may determine which AddOns are eligible to be their children - UFUNCTION(BlueprintNativeEvent, BlueprintPure) - EFlowAddOnAcceptResult AcceptFlowNodeAddOnChild(const UFlowNodeAddOn* AddOnTemplate) const; + virtual void OnActivate() override; + virtual void ExecuteInput(const FName& PinName) override; -public: -#if WITH_EDITOR - EFlowAddOnAcceptResult CheckAcceptFlowNodeAddOnChild(const UFlowNodeAddOn* AddOnTemplate) const; - virtual TArray& GetFlowNodeAddOnChildrenByEditor() { return AddOns; } -#endif // WITH_EDITOR - virtual const TArray& GetFlowNodeAddOnChildren() const { return AddOns; } - - virtual UFlowNode* GetFlowNodeSelfOrOwner() PURE_VIRTUAL(GetFlowNodeSelfOrOwner, return nullptr;); - const UFlowNode* GetFlowNodeSelfOrOwner() const { return const_cast(this)->GetFlowNodeSelfOrOwner(); } + virtual void ForceFinishNode() override; + virtual void Cleanup() override; + // -- - // Call a Function for all of this object's AddOns (including all AddOn's AddOns, ie recursively) - void ForEachAddOnConst(FConstFlowNodeAddOnFunction Function) const; - void ForEachAddOn(FFlowNodeAddOnFunction Function) const; +////////////////////////////////////////////////////////////////////////// +// Pins - template - void ForEachAddOnForClassConst(FConstFlowNodeAddOnFunction Function) const { ForEachAddOnForClassConst(*TInterfaceOrClass::StaticClass(), Function); } - void ForEachAddOnForClassConst(const UClass& InterfaceOrClass, FConstFlowNodeAddOnFunction Function) const; +public: + static const FFlowPin* FindFlowPinByName(const FName& PinName, const TArray& FlowPins); + virtual bool IsSupportedInputPinName(const FName& PinName) const PURE_VIRTUAL(IsSupportedInputPinName, return true;); - template - void ForEachAddOnForClass(FFlowNodeAddOnFunction Function) const { ForEachAddOnForClass(*TInterfaceOrClass::StaticClass(), Function); } - void ForEachAddOnForClass(const UClass& InterfaceOrClass, FFlowNodeAddOnFunction Function) const; +#if WITH_EDITOR +public: + // IFlowContextPinSupplierInterface + virtual bool SupportsContextPins() const override { return false; } + virtual TArray GetContextInputs() const override; + virtual TArray GetContextOutputs() const override; // -- +#endif // WITH_EDITOR + +////////////////////////////////////////////////////////////////////////// +// Owners +public: UFUNCTION(BlueprintPure, Category = "FlowNode") UFlowAsset* GetFlowAsset() const; + const UFlowNode* GetFlowNodeSelfOrOwner() const; + virtual UFlowNode* GetFlowNodeSelfOrOwner() PURE_VIRTUAL(GetFlowNodeSelfOrOwner, return nullptr;); + UFUNCTION(BlueprintPure, Category = "FlowNode") UFlowSubsystem* GetFlowSubsystem() const; @@ -105,129 +106,170 @@ class FLOW_API UFlowNodeBase IFlowOwnerInterface* GetFlowOwnerInterface() const; protected: - // Helper functions for GetFlowOwnerInterface() - IFlowOwnerInterface* TryGetFlowOwnerInterfaceFromRootFlowOwner(UObject& RootFlowOwner, const UClass& ExpectedOwnerClass) const; - IFlowOwnerInterface* TryGetFlowOwnerInterfaceActor(UObject& RootFlowOwner, const UClass& ExpectedOwnerClass) const; + static IFlowOwnerInterface* TryGetFlowOwnerInterfaceFromRootFlowOwner(UObject& RootFlowOwner, const UClass& ExpectedOwnerClass); + static IFlowOwnerInterface* TryGetFlowOwnerInterfaceActor(UObject& RootFlowOwner, const UClass& ExpectedOwnerClass); -public: +////////////////////////////////////////////////////////////////////////// +// AddOn support - // Set the editor-only Config Text - // (for displaying config info on the Node in the flow graph, ignored in non-editor builds) - UFUNCTION(BlueprintCallable) - void SetNodeConfigText(const FText& NodeConfigText); +protected: + // Flow Node AddOn attachments + UPROPERTY(BlueprintReadOnly, Instanced, Category = "FlowNode") + TArray AddOns; - // Called whenever a property change event occurs on this flow node object, - // giving the implementor a chance to update their NodeConfigText (via SetNodeConfigText) - UFUNCTION(BlueprintNativeEvent) - void UpdateNodeConfigText(); +protected: + // FlowNodes and AddOns may determine which AddOns are eligible to be their children + UFUNCTION(BlueprintNativeEvent, BlueprintPure, Category = "FlowNode") + EFlowAddOnAcceptResult AcceptFlowNodeAddOnChild(const UFlowNodeAddOn* AddOnTemplate) const; + +public: + virtual const TArray& GetFlowNodeAddOnChildren() const { return AddOns; } #if WITH_EDITOR + virtual TArray& GetFlowNodeAddOnChildrenByEditor() { return AddOns; } + EFlowAddOnAcceptResult CheckAcceptFlowNodeAddOnChild(const UFlowNodeAddOn* AddOnTemplate) const; +#endif // WITH_EDITOR + + // Call a function for all of this object's AddOns (recursively iterating AddOns inside AddOn) + void ForEachAddOnConst(const FConstFlowNodeAddOnFunction& Function) const; + void ForEachAddOn(const FFlowNodeAddOnFunction& Function) const; + + template + void ForEachAddOnForClassConst(const FConstFlowNodeAddOnFunction Function) const + { + ForEachAddOnForClassConst(*TInterfaceOrClass::StaticClass(), Function); + } + + void ForEachAddOnForClassConst(const UClass& InterfaceOrClass, const FConstFlowNodeAddOnFunction& Function) const; + + template + void ForEachAddOnForClass(const FFlowNodeAddOnFunction Function) const + { + ForEachAddOnForClass(*TInterfaceOrClass::StaticClass(), Function); + } + + void ForEachAddOnForClass(const UClass& InterfaceOrClass, const FFlowNodeAddOnFunction& Function) const; + +////////////////////////////////////////////////////////////////////////// +// Editor +// (some editor symbols exposed to enabled creation of non-editor tooling) + + UPROPERTY() + UEdGraphNode* GraphNode; + +#if WITH_EDITORONLY_DATA +protected: + uint8 bCanDelete : 1 ; + uint8 bCanDuplicate : 1; + + UPROPERTY(EditDefaultsOnly, Category = "FlowNode") + bool bNodeDeprecated; + + // If this node is deprecated, it might be replaced by another node + UPROPERTY(EditDefaultsOnly, Category = "FlowNode") + TSubclassOf ReplacedBy; + + FFlowNodeEvent OnReconstructionRequested; + FFlowMessageLog ValidationLog; +#endif // WITH_EDITORONLY_DATA + +public: UEdGraphNode* GetGraphNode() const { return GraphNode; } +#if WITH_EDITOR + void SetGraphNode(UEdGraphNode* NewGraphNode); + + // Set up UFlowNodeBase when being opened for edit in the editor + virtual void SetupForEditing(UEdGraphNode& EdGraphNode); + // Opportunity to update node's data before UFlowGraphNode would call ReconstructNode() virtual void FixNode(UEdGraphNode* NewGraphNode); - void SetGraphNode(UEdGraphNode* NewGraphNode); + // UObject + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; + // -- - // Setup the UFlowNodeBase when being opened for edit in the editor - virtual void SetupForEditing(UEdGraphNode& EdGraphNode); + // used when import graph from another asset + virtual void PostImport() {} +#endif + +#if WITH_EDITORONLY_DATA +protected: + UPROPERTY() + FString Category; + + UPROPERTY(EditDefaultsOnly, Category = "FlowNode") + EFlowNodeStyle NodeStyle; + + // Set Node Style to custom to use your own color for this node + UPROPERTY(EditDefaultsOnly, Category = "FlowNode", meta = (EditCondition = "NodeStyle == EFlowNodeStyle::Custom")) + FLinearColor NodeColor; + // Optional developer-facing text to explain the configuration of this node when viewed in the editor + // may be authored or set procedurally via UpdateNodeConfigText and SetNodeConfigText + UPROPERTY(EditDefaultsOnly, AdvancedDisplay, Category = "FlowNode") + FText DevNodeConfigText; +#endif // WITH_EDITORONLY_DATA + +#if WITH_EDITOR +public: virtual FString GetNodeCategory() const; + EFlowNodeStyle GetNodeStyle() const; + + // This method allows to have different for every node instance, i.e. Red if node represents enemy, Green if node represents a friend + virtual bool GetDynamicTitleColor(FLinearColor& OutColor) const; + virtual FText GetNodeTitle() const; virtual FText GetNodeToolTip() const; virtual FText GetNodeConfigText() const; +#endif - // This method allows to have different for every node instance, i.e. Red if node represents enemy, Green if node represents a friend - virtual bool GetDynamicTitleColor(FLinearColor& OutColor) const; +protected: + // Set the editor-only Config Text + // (for displaying config info on the Node in the flow graph, ignored in non-editor builds) + UFUNCTION(BlueprintCallable, Category = "FlowNode") + void SetNodeConfigText(const FText& NodeConfigText); - EFlowNodeStyle GetNodeStyle() const { return NodeStyle; } + // Called whenever a property change event occurs on this flow node object, + // giving the implementor a chance to update their NodeConfigText (via SetNodeConfigText) + UFUNCTION(BlueprintNativeEvent, Category = "FlowNode") + void UpdateNodeConfigText(); +////////////////////////////////////////////////////////////////////////// +// Debug support + +#if WITH_EDITOR +public: // Short summary of node's content - displayed over node as NodeInfoPopup virtual FString GetNodeDescription() const; +#endif - // used when import graph from another asset - virtual void PostImport() {} - - // IFlowContextPinSupplierInterface - virtual bool SupportsContextPins() const override { return false; } - virtual TArray GetContextInputs() const override; - virtual TArray GetContextOutputs() const override; - // -- - -#endif // WITH_EDITOR - - static const FFlowPin* FindFlowPinByName(const FName& PinName, const TArray& FlowPins); +protected: + // Short summary of node's content - displayed over node as NodeInfoPopup + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Get Node Description")) + FString K2_GetNodeDescription() const; UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) void LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType = EFlowOnScreenMessageType::Permanent); // LogError from constant function (allowing this to be modified only to log the error itself) - FORCEINLINE void LogErrorConst(FString Message, const EFlowOnScreenMessageType OnScreenMessageType = EFlowOnScreenMessageType::Permanent) const - { const_cast(this)->LogError(Message, OnScreenMessageType); } + FORCEINLINE void LogErrorConst(FString Message, const EFlowOnScreenMessageType OnScreenMessageType = EFlowOnScreenMessageType::Permanent) const; UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) void LogWarning(FString Message); // LogWarning from constant function (allowing this to be modified only to log the warning itself) - FORCEINLINE void LogWarningConst(FString Message) const { const_cast(this)->LogWarning(Message); } + FORCEINLINE void LogWarningConst(FString Message) const; UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) void LogNote(FString Message); // LogNote from constant function (allowing this to be modified only to log the note itself) - FORCEINLINE void LogNoteConst(FString Message) const { const_cast(this)->LogNote(Message); } + FORCEINLINE void LogNoteConst(FString Message) const; #if !UE_BUILD_SHIPPING -private: +protected: bool BuildMessage(FString& Message) const; #endif - -protected: - // Short summary of node's content - displayed over node as NodeInfoPopup - UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Get Node Description")) - FString K2_GetNodeDescription() const; - -protected: - // Flow Node AddOn attachments - UPROPERTY(BlueprintReadOnly, Instanced, Category = "Configuration") - TArray AddOns; - -#if WITH_EDITORONLY_DATA - - UPROPERTY() - UEdGraphNode* GraphNode = nullptr; - -public: - UPROPERTY(EditDefaultsOnly, Category = "FlowNode") - EFlowNodeStyle NodeStyle = EFlowNodeStyle::Default; - - // Set Node Style to custom to use your own color for this node - UPROPERTY(EditDefaultsOnly, Category = "FlowNode", meta = (EditCondition = "NodeStyle == EFlowNodeStyle::Custom")) - FLinearColor NodeColor = FLinearColor::Black; - - uint8 bCanDelete : 1 = true; - uint8 bCanDuplicate : 1 = true; - - UPROPERTY() - FString Category; - - // Optional developer-facing text to explain the configuration of this node when viewed in the editor - // may be authored or set procedurally via UpdateNodeConfigText and SetNodeConfigText - UPROPERTY(EditDefaultsOnly, AdvancedDisplay, Category = "Configuration") - FText DevNodeConfigText = FText::GetEmpty(); - - UPROPERTY(EditDefaultsOnly, Category = "FlowNode") - bool bNodeDeprecated = false; - - // If this node is deprecated, it might be replaced by another node - UPROPERTY(EditDefaultsOnly, Category = "FlowNode") - TSubclassOf ReplacedBy; - - FFlowNodeEvent OnReconstructionRequested; - - FFlowMessageLog ValidationLog; - -#endif // WITH_EDITORONLY_DATA }; - diff --git a/Source/FlowEditor/Private/Graph/FlowGraph.cpp b/Source/FlowEditor/Private/Graph/FlowGraph.cpp index 32b0ad5e5..73eb8654b 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraph.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraph.cpp @@ -208,7 +208,7 @@ void UFlowGraph::OnLoaded() { check(GEditor); - // Setup all of the Nodes in the graph for editing + // Setup all the Nodes in the graph for editing for (UEdGraphNode* EdNode : Nodes) { UFlowGraphNode* FlowGraphNode = Cast(EdNode); diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index 616154d44..d400ed474 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -11,7 +11,6 @@ #include "Graph/Nodes/FlowGraphNode.h" #include "FlowAsset.h" -#include "FlowEditorModule.h" #include "AddOns/FlowNodeAddOn.h" #include "Nodes/FlowNode.h" #include "Nodes/FlowNodeBlueprint.h" @@ -178,9 +177,6 @@ const FPinConnectionResponse UFlowGraphSchema::CanMergeNodes(const UEdGraphNode* const UFlowGraphNode* FlowGraphNodeA = Cast(NodeA); const UFlowGraphNode* FlowGraphNodeB = Cast(NodeB); - const bool bNodeAIsSubnode = FlowGraphNodeA && FlowGraphNodeA->IsSubNode(); - const bool bNodeBIsSubnode = FlowGraphNodeB && FlowGraphNodeB->IsSubNode(); - FString ReasonString; if (FlowGraphNodeA && FlowGraphNodeB) { @@ -360,7 +356,7 @@ TArray> UFlowGraphSchema::GetFlowNodeCategories() return Result; } -TSubclassOf UFlowGraphSchema::GetAssignedGraphNodeClass(TSubclassOf FlowNodeClass) +TSubclassOf UFlowGraphSchema::GetAssignedGraphNodeClass(const TSubclassOf& FlowNodeClass) { TArray> FoundParentClasses; UClass* ReturnClass = nullptr; @@ -447,10 +443,7 @@ void UFlowGraphSchema::GetFlowNodeActions(FGraphActionMenuBuilder& ActionMenuBui } } -TArray UFlowGraphSchema::GetFilteredPlaceableNodesOrAddOns( - const UFlowAsset* EditedFlowAsset, - const TArray& InNativeNodesOrAddOns, - const TMap& InBlueprintNodesOrAddOns) +TArray UFlowGraphSchema::GetFilteredPlaceableNodesOrAddOns(const UFlowAsset* EditedFlowAsset, const TArray& InNativeNodesOrAddOns, const TMap& InBlueprintNodesOrAddOns) { if (!bInitialGatherPerformed) { @@ -530,7 +523,7 @@ bool UFlowGraphSchema::IsAddOnAllowedForSelectedObjects(const TArray& return false; } - UFlowNodeBase* FlowNodeOuter = Cast(FlowGraphNode->GetFlowNodeBase()); + const UFlowNodeBase* FlowNodeOuter = Cast(FlowGraphNode->GetFlowNodeBase()); if (!IsValid(FlowNodeOuter)) { continue; @@ -607,9 +600,7 @@ void UFlowGraphSchema::OnHotReload(EReloadCompleteReason ReloadCompleteReason) GatherNodes(); } -void UFlowGraphSchema::GatherNativeNodesOrAddOns( - TSubclassOf FlowNodeBaseClass, - TArray& InOutNodesOrAddOnsArray) +void UFlowGraphSchema::GatherNativeNodesOrAddOns(const TSubclassOf& FlowNodeBaseClass, TArray& InOutNodesOrAddOnsArray) { // collect C++ Nodes or AddOns once per editor session if (InOutNodesOrAddOnsArray.Num() > 0) @@ -719,7 +710,7 @@ void UFlowGraphSchema::AddAsset(const FAssetData& AssetData, const bool bBatch) } } -bool UFlowGraphSchema::ShouldAddToBlueprintFlowNodesMap(const FAssetData& AssetData, TSubclassOf BlueprintClass, TSubclassOf FlowNodeBaseClass) +bool UFlowGraphSchema::ShouldAddToBlueprintFlowNodesMap(const FAssetData& AssetData, const TSubclassOf& BlueprintClass, const TSubclassOf& FlowNodeBaseClass) { if (!AssetData.GetClass()->IsChildOf(BlueprintClass)) { diff --git a/Source/FlowEditor/Public/Graph/FlowGraph.h b/Source/FlowEditor/Public/Graph/FlowGraph.h index 32a47572a..78c857d7d 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraph.h +++ b/Source/FlowEditor/Public/Graph/FlowGraph.h @@ -71,8 +71,7 @@ class FLOWEDITOR_API UFlowGraph : public UEdGraph void UnlockUpdates(); protected: - - void RecursivelySetupAllFlowGraphNodesForEditing(UFlowGraphNode& FromFlowGraphNode); + static void RecursivelySetupAllFlowGraphNodesForEditing(UFlowGraphNode& FromFlowGraphNode); void RecursivelyRefreshAddOns(UFlowGraphNode& FromFlowGraphNode); static FString GetDeprecationMessage(const UClass* Class); diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h index 733cb7e9b..d432a97fe 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h @@ -59,7 +59,7 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema // -- static TArray> GetFlowNodeCategories(); - static TSubclassOf GetAssignedGraphNodeClass(TSubclassOf FlowNodeClass); + static TSubclassOf GetAssignedGraphNodeClass(const TSubclassOf& FlowNodeClass); static bool IsPIESimulating(); @@ -69,11 +69,7 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema private: static void ApplyNodeOrAddOnFilter(const UFlowAsset* AssetClassDefaults, const UClass* FlowNodeClass, TArray& FilteredNodes); static void GetFlowNodeActions(FGraphActionMenuBuilder& ActionMenuBuilder, const UFlowAsset* EditedFlowAsset, const FString& CategoryName); - - static TArray GetFilteredPlaceableNodesOrAddOns( - const UFlowAsset* EditedFlowAsset, - const TArray& InNativeNodesOrAddOns, - const TMap& InBlueprintNodesOrAddOns); + static TArray GetFilteredPlaceableNodesOrAddOns(const UFlowAsset* EditedFlowAsset, const TArray& InNativeNodesOrAddOns, const TMap& InBlueprintNodesOrAddOns); static void GetCommentAction(FGraphActionMenuBuilder& ActionMenuBuilder, const UEdGraph* CurrentGraph = nullptr); @@ -83,18 +79,12 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema static void OnBlueprintCompiled(); static void OnHotReload(EReloadCompleteReason ReloadCompleteReason); - static void GatherNativeNodesOrAddOns( - TSubclassOf FlowNodeBaseClass, - TArray& InOutNodesOrAddOnsArray); + static void GatherNativeNodesOrAddOns(const TSubclassOf& FlowNodeBaseClass, TArray& InOutNodesOrAddOnsArray); static void GatherNodes(); static void OnAssetAdded(const FAssetData& AssetData); static void AddAsset(const FAssetData& AssetData, const bool bBatch); - - static bool ShouldAddToBlueprintFlowNodesMap( - const FAssetData& AssetData, - TSubclassOf BlueprintClass, - TSubclassOf FlowNodeBaseClass); + static bool ShouldAddToBlueprintFlowNodesMap(const FAssetData& AssetData, const TSubclassOf& BlueprintClass, const TSubclassOf& FlowNodeBaseClass); static void OnAssetRemoved(const FAssetData& AssetData); From aa2dced19f25ef2bea4de27de4cdbf174da9f377 Mon Sep 17 00:00:00 2001 From: FoolsTheoryDev Date: Sun, 16 Jun 2024 14:16:38 +0200 Subject: [PATCH 242/485] restored changes wiped out by Flow AddOns merge --- Source/Flow/Private/Nodes/FlowNode.cpp | 6 +-- .../World/FlowNode_CallOwnerFunction.cpp | 44 ++++++------------- Source/Flow/Public/FlowAsset.h | 4 +- .../Nodes/World/FlowNode_CallOwnerFunction.h | 8 ---- Source/FlowEditor/Private/Graph/FlowGraph.cpp | 1 - .../Private/Graph/FlowGraphEditor.cpp | 12 +++-- .../Private/Graph/Nodes/FlowGraphNode.cpp | 28 ++---------- .../Private/Graph/Widgets/SFlowGraphNode.cpp | 20 ++++++++- .../FlowEditor/Public/Graph/FlowGraphEditor.h | 2 +- .../Public/Graph/Nodes/FlowGraphNode.h | 4 -- .../Public/Graph/Widgets/SFlowGraphNode.h | 3 +- 11 files changed, 51 insertions(+), 81 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 0784de2ce..8bd376263 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -53,9 +53,9 @@ void UFlowNode::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEve } const FName PropertyName = PropertyChangedEvent.GetPropertyName(); - - if (PropertyName == GET_MEMBER_NAME_CHECKED(UFlowNode, InputPins) || - PropertyName == GET_MEMBER_NAME_CHECKED(UFlowNode, OutputPins)) + const FName MemberPropertyName = PropertyChangedEvent.GetMemberPropertyName(); + if (PropertyName == GET_MEMBER_NAME_CHECKED(UFlowNode, InputPins) || PropertyName == GET_MEMBER_NAME_CHECKED(UFlowNode, OutputPins) + || MemberPropertyName == GET_MEMBER_NAME_CHECKED(UFlowNode, InputPins) || MemberPropertyName == GET_MEMBER_NAME_CHECKED(UFlowNode, OutputPins)) { // Potentially need to rebuild the pins from the this node OnReconstructionRequested.ExecuteIfBound(); diff --git a/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp b/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp index c7bb566ff..5995f17fc 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp @@ -63,7 +63,7 @@ void UFlowNode_CallOwnerFunction::ExecuteInput(const FName& PinName) Params->PostExecute(); - (void) TryExecuteOutputPin(ResultOutputName); + (void)TryExecuteOutputPin(ResultOutputName); } bool UFlowNode_CallOwnerFunction::TryExecuteOutputPin(const FName& OutputName) @@ -133,7 +133,7 @@ void UFlowNode_CallOwnerFunction::PostEditChangeProperty(struct FPropertyChanged if (MemberPropertyName == GET_MEMBER_NAME_CHECKED(UFlowNode_CallOwnerFunction, Params)) { - OnChangedParamsObject(); + OnReconstructionRequested.ExecuteIfBound(); } const FName PropertyName = PropertyChangedEvent.Property->GetFName(); @@ -141,32 +141,11 @@ void UFlowNode_CallOwnerFunction::PostEditChangeProperty(struct FPropertyChanged { if (TryAllocateParamsInstance()) { - OnChangedParamsObject(); + OnReconstructionRequested.ExecuteIfBound(); } } } -void UFlowNode_CallOwnerFunction::OnChangedParamsObject() -{ - bool bChangedPins = false; - - if (IsValid(Params)) - { - bChangedPins = RebuildPinArray(Params->GetInputNames(), InputPins, DefaultInputPin) || bChangedPins; - bChangedPins = RebuildPinArray(Params->GetOutputNames(), OutputPins, DefaultOutputPin) || bChangedPins; - } - else - { - bChangedPins = RebuildPinArray(TArray(&DefaultInputPin.PinName, 1), InputPins, DefaultInputPin) || bChangedPins; - bChangedPins = RebuildPinArray(TArray(&DefaultOutputPin.PinName, 1), OutputPins, DefaultOutputPin) || bChangedPins; - } - - if (bChangedPins) - { - OnReconstructionRequested.ExecuteIfBound(); - } -} - EDataValidationResult UFlowNode_CallOwnerFunction::ValidateNode() { const bool bHasFunction = FunctionRef.IsConfigured(); @@ -342,11 +321,14 @@ bool UFlowNode_CallOwnerFunction::TryAllocateParamsInstance() } const UClass* ExistingParamsClass = GetExistingParamsClass(); - UClass* RequiredParamsClass = GetRequiredParamsClass(); + const UClass* RequiredParamsClass = GetRequiredParamsClass(); + + if (!IsValid(RequiredParamsClass)) + { + return false; + } - const bool bNeedsAllocateParams = - !IsValid(ExistingParamsClass) || - ExistingParamsClass != RequiredParamsClass; + const bool bNeedsAllocateParams = !IsValid(ExistingParamsClass) || ExistingParamsClass != RequiredParamsClass; if (!bNeedsAllocateParams) { @@ -367,14 +349,14 @@ UClass* UFlowNode_CallOwnerFunction::GetRequiredParamsClass() const const UClass* ExpectedOwnerClass = TryGetExpectedOwnerClass(); if (!IsValid(ExpectedOwnerClass)) { - return UFlowOwnerFunctionParams::StaticClass(); + return nullptr; } const FName FunctionNameAsName = FunctionRef.GetFunctionName(); if (FunctionNameAsName.IsNone()) { - return UFlowOwnerFunctionParams::StaticClass(); + return nullptr; } UClass* RequiredParamsClass = GetParamsClassForFunctionName(*ExpectedOwnerClass, FunctionNameAsName); @@ -422,7 +404,7 @@ FText UFlowNode_CallOwnerFunction::GetNodeTitle() const { const FText FunctionNameText = FText::FromName(FunctionRef.FunctionName); - return FText::Format(LOCTEXT("CallOwnerFunction", "Call {0}"), { FunctionNameText }); + return FText::Format(LOCTEXT("CallOwnerFunction", "Call {0}"), {FunctionNameText}); } else { diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 8382c9c5a..91cb4b902 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -61,7 +61,7 @@ class FLOW_API UFlowAsset : public UObject FGuid AssetGuid; // Set it to False, if this asset is instantiated as Root Flow for owner that doesn't live in the world - // This allow to SaveGame support works properly, if owner of Root Flow would be Game Instance or its subsystem + // This allows to SaveGame support works properly, if owner of Root Flow would be Game Instance or its subsystem UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Flow Asset") bool bWorldBound; @@ -97,7 +97,7 @@ class FLOW_API UFlowAsset : public UObject private: UPROPERTY() - UEdGraph* FlowGraph; + TObjectPtr FlowGraph; static TSharedPtr FlowGraphInterface; #endif diff --git a/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h b/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h index bfab6a21f..547508ecc 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h @@ -43,8 +43,6 @@ class FLOW_API UFlowNode_CallOwnerFunction : public UFlowNode // -- #endif // WITH_EDITOR - UFlowNode_CallOwnerFunction(); - UClass* GetRequiredParamsClass() const; UClass* GetExistingParamsClass() const; @@ -58,11 +56,6 @@ class FLOW_API UFlowNode_CallOwnerFunction : public UFlowNode static UClass* GetParamsClassForFunction(const UFunction& Function); protected: - -#if WITH_EDITOR - void OnChangedParamsObject(); -#endif // WITH_EDITOR - // UFlowNode virtual void ExecuteInput(const FName& PinName) override; // -- @@ -76,7 +69,6 @@ class FLOW_API UFlowNode_CallOwnerFunction : public UFlowNode static bool DoesFunctionHaveNameReturnType(const UFunction& Function); protected: - // Function reference on the expected owner to call // DEPRECATED - Sunsetting this feature from FlowGraph with the next release. Custom FlowNodes are a better mechanism to use UPROPERTY(EditAnywhere, Category = "Call Owner", meta = (DisplayName = "DEPRECATED - Function")) diff --git a/Source/FlowEditor/Private/Graph/FlowGraph.cpp b/Source/FlowEditor/Private/Graph/FlowGraph.cpp index 73eb8654b..77e3ec9ee 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraph.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraph.cpp @@ -108,7 +108,6 @@ void UFlowGraph::RefreshGraph() void UFlowGraph::NotifyGraphChanged() { GetFlowAsset()->HarvestNodeConnections(); - GetFlowAsset()->MarkPackageDirty(); Super::NotifyGraphChanged(); } diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index 467a42acf..92a7a0af7 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -391,7 +391,7 @@ void SFlowGraphEditor::ReconnectExecPins(const UFlowGraphNode* Node) { if (InputPin) { - // more that one connected input pins - do not reconnect anything + // more than one connected input pins - do not reconnect anything return; } @@ -413,7 +413,7 @@ void SFlowGraphEditor::ReconnectExecPins(const UFlowGraphNode* Node) { if (OutputPin) { - // more that one connected output pins - do not reconnect anything + // more than one connected output pins - do not reconnect anything return; } @@ -461,9 +461,15 @@ void SFlowGraphEditor::DeleteSelectedNodes() { if (const UFlowGraphNode* FlowGraphNode = Cast(Node)) { - if (UFlowNode* FlowNode = Cast(FlowGraphNode->GetFlowNodeBase())) + if (const UFlowNode* FlowNode = Cast(FlowGraphNode->GetFlowNodeBase())) { const FGuid NodeGuid = FlowNode->GetGuid(); + + // If the user is pressing shift then try and reconnect the pins + if (FSlateApplication::Get().GetModifierKeys().IsShiftDown()) + { + ReconnectExecPins(FlowGraphNode); + } GetCurrentGraph()->GetSchema()->BreakNodeLinks(*Node); Node->DestroyNode(); diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index a2f8a12d7..4d26d8d05 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -179,36 +179,14 @@ void UFlowGraphNode::SubscribeToExternalChanges() if (NodeInstance) { NodeInstance->OnReconstructionRequested.BindUObject(this, &UFlowGraphNode::OnExternalChange); - - // blueprint nodes - if (NodeInstance->GetClass()->ClassGeneratedBy && GEditor) - { - GEditor->OnBlueprintPreCompile().AddUObject(this, &UFlowGraphNode::OnBlueprintPreCompile); - GEditor->OnBlueprintCompiled().AddUObject(this, &UFlowGraphNode::OnBlueprintCompiled); - } - } -} - -void UFlowGraphNode::OnBlueprintPreCompile(UBlueprint* Blueprint) -{ - if (Blueprint && Blueprint == NodeInstance->GetClass()->ClassGeneratedBy) - { - bBlueprintCompilationPending = true; } } -void UFlowGraphNode::OnBlueprintCompiled() -{ - if (bBlueprintCompilationPending) - { - OnExternalChange(); - } - - bBlueprintCompilationPending = false; -} - void UFlowGraphNode::OnExternalChange() { + // Do not create transaction here, since this function triggers from modifying UFlowNode's property, which itself already made inside of transaction. + Modify(); + bNeedsFullReconstruction = true; ReconstructNode(); diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp index 9bb5d99d0..66389cd32 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp @@ -744,14 +744,16 @@ void SFlowGraphNode::CreateStandardPinWidget(UEdGraphPin* Pin) { if (Pin->Direction == EGPD_Input) { - if (FlowNode->GetInputPins().Num() == 1 && Pin->PinName == UFlowNode::DefaultInputPin.PinName) + // Pin array can have pins with name None, which will not be created. We need to check if array have only one valid pin + if (ValidPinsCount(FlowNode->GetInputPins()) == 1 && Pin->PinName == UFlowNode::DefaultInputPin.PinName) { NewPin->SetShowLabel(false); } } else { - if (FlowNode->GetOutputPins().Num() == 1 && Pin->PinName == UFlowNode::DefaultOutputPin.PinName) + // Pin array can have pins with name None, which will not be created. We need to check if array have only one valid pin + if (ValidPinsCount(FlowNode->GetOutputPins()) == 1 && Pin->PinName == UFlowNode::DefaultOutputPin.PinName) { NewPin->SetShowLabel(false); } @@ -1233,4 +1235,18 @@ void SFlowGraphNode::SetOwner(const TSharedRef& OwnerPanel) } } +int32 SFlowGraphNode::ValidPinsCount(const TArray& Pins) +{ + int32 Count = 0; + for (const FFlowPin& Pin : Pins) + { + if (Pin.IsValid()) + { + Count += 1; + } + } + + return Count; +} + #undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h index 45a1e07b4..fa873ce3b 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h @@ -68,7 +68,7 @@ class FLOWEDITOR_API SFlowGraphEditor : public SGraphEditor protected: virtual bool CanSelectAllNodes() const { return true; } - void ReconnectExecPins(const UFlowGraphNode* Node); + static void ReconnectExecPins(const UFlowGraphNode* Node); virtual void DeleteSelectedNodes(); virtual void DeleteSelectedDuplicableNodes(); virtual bool CanDeleteNodes() const; diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h index 33d42fcde..8df6adcea 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h @@ -63,10 +63,6 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode private: void SubscribeToExternalChanges(); - - void OnBlueprintPreCompile(UBlueprint* Blueprint); - void OnBlueprintCompiled(); - void OnExternalChange(); public: diff --git a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h index 83a8179d5..e362ec3e9 100644 --- a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h @@ -96,7 +96,6 @@ class FLOWEDITOR_API SFlowGraphNode : public SGraphNode virtual FReply OnAddFlowPin(const EEdGraphPinDirection Direction); protected: - /** adds a sub node widget inside current node */ void AddSubNodeWidget(TSharedPtr NewSubNodeWidget); @@ -126,6 +125,8 @@ class FLOWEDITOR_API SFlowGraphNode : public SGraphNode bool IsFlowGraphNodeSelected(UFlowGraphNode* Node) const; + static int32 ValidPinsCount(const TArray& Pins); + protected: // The FlowGraphNode this slate widget is representing UFlowGraphNode* FlowGraphNode = nullptr; From 4f26ac352eab3466f0dbdd576098a3af69a73b96 Mon Sep 17 00:00:00 2001 From: FoolsTheoryDev Date: Sun, 16 Jun 2024 17:21:17 +0200 Subject: [PATCH 243/485] cosmetic changes while investigating UE 5.2 compilation errors After all, decided to drop UE 5.2 support already. --- .../Nodes/World/FlowNode_ExecuteComponent.cpp | 222 +++++++++--------- 1 file changed, 106 insertions(+), 116 deletions(-) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp b/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp index a51076616..6854e4bdd 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp @@ -1,7 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "Nodes/World/FlowNode_ExecuteComponent.h" -#include "Interfaces/FlowOwnerInterface.h" #include "Interfaces/FlowCoreExecutableInterface.h" #include "Interfaces/FlowExternalExecutableInterface.h" #include "Interfaces/FlowContextPinSupplierInterface.h" @@ -30,7 +29,7 @@ void UFlowNode_ExecuteComponent::InitializeInstance() { Super::InitializeInstance(); - (void) TryInjectComponent(); + (void)TryInjectComponent(); if (UActorComponent* ResolvedComp = TryResolveComponent()) { @@ -218,53 +217,53 @@ bool UFlowNode_ExecuteComponent::TryInjectComponent() static_assert(static_cast(EExecuteComponentSource::Max) == 4, TEXT("Update this code if the enum changes")); switch (ComponentSource) { - case EExecuteComponentSource::InjectFromTemplate: - { - if (IsValid(ComponentTemplate)) + case EExecuteComponentSource::InjectFromTemplate: { - if (UActorComponent* ComponentInstance = FFlowInjectComponentsHelper::TryCreateComponentInstanceForActorFromTemplate(*ActorOwner, *ComponentTemplate)) + if (IsValid(ComponentTemplate)) { - ComponentInstances.Add(ComponentInstance); + if (UActorComponent* ComponentInstance = FFlowInjectComponentsHelper::TryCreateComponentInstanceForActorFromTemplate(*ActorOwner, *ComponentTemplate)) + { + ComponentInstances.Add(ComponentInstance); + } } } - } - break; + break; - case EExecuteComponentSource::InjectFromClass: - { - if (IsValid(ComponentClass)) + case EExecuteComponentSource::InjectFromClass: { - if (bReuseExistingComponent) + if (IsValid(ComponentClass)) { - // Look for the component class existing already on the actor, for potential re-use - - UActorComponent* ExistingComponent = ActorOwner->FindComponentByClass(ComponentClass); - if (IsValid(ExistingComponent)) + if (bReuseExistingComponent) { - // Set the ComponentRef directly (for later lookup via TryResolveComponent) - ComponentRef.SetResolvedComponentDirect(*ExistingComponent); + // Look for the component class existing already on the actor, for potential re-use + + UActorComponent* ExistingComponent = ActorOwner->FindComponentByClass(ComponentClass); + if (IsValid(ExistingComponent)) + { + // Set the ComponentRef directly (for later lookup via TryResolveComponent) + ComponentRef.SetResolvedComponentDirect(*ExistingComponent); - return true; + return true; + } + + if (!bAllowInjectComponent) + { + return false; + } } - if (!bAllowInjectComponent) + const FName InstanceBaseName = ComponentClass->GetFName(); + if (UActorComponent* ComponentInstance = FFlowInjectComponentsHelper::TryCreateComponentInstanceForActorFromClass(*ActorOwner, *ComponentClass, InstanceBaseName)) { - return false; + ComponentInstances.Add(ComponentInstance); } } - - const FName InstanceBaseName = ComponentClass->GetFName(); - if (UActorComponent* ComponentInstance = FFlowInjectComponentsHelper::TryCreateComponentInstanceForActorFromClass(*ActorOwner, *ComponentClass, InstanceBaseName)) - { - ComponentInstances.Add(ComponentInstance); - } } - } - break; + break; - default: - checkNoEntry(); - return false; + default: + checkNoEntry(); + return false; } // Create the manager object if we're injecting a component @@ -312,35 +311,29 @@ UActorComponent* UFlowNode_ExecuteComponent::TryResolveComponent() #if WITH_EDITOR const UActorComponent* UFlowNode_ExecuteComponent::TryGetExpectedComponent() const { - TSubclassOf ExpectedOwnerClass = TryGetExpectedActorOwnerClass(); + const TSubclassOf ExpectedOwnerClass = TryGetExpectedActorOwnerClass(); static_assert(static_cast(EExecuteComponentSource::Max) == 4, TEXT("Update this code if the enum changes")); switch (ComponentSource) { - default: - case EExecuteComponentSource::Undetermined: - { + case EExecuteComponentSource::Undetermined: + { + return nullptr; + } + case EExecuteComponentSource::BindToExisting: + { + return AActor::GetActorClassDefaultComponentByName(ExpectedOwnerClass, ComponentRef.ComponentName); + } + case EExecuteComponentSource::InjectFromTemplate: + { + return ComponentTemplate; + } + case EExecuteComponentSource::InjectFromClass: + { + return IsValid(ComponentClass) ? ComponentClass->GetDefaultObject() : nullptr; + } + default: return nullptr; - } - break; - - case EExecuteComponentSource::BindToExisting: - { - return AActor::GetActorClassDefaultComponentByName(ExpectedOwnerClass, ComponentRef.ComponentName); - } - break; - - case EExecuteComponentSource::InjectFromTemplate: - { - return ComponentTemplate; - } - break; - - case EExecuteComponentSource::InjectFromClass: - { - return IsValid(ComponentClass) ? ComponentClass->GetDefaultObject() : nullptr; - } - break; } } @@ -421,7 +414,7 @@ EDataValidationResult UFlowNode_ExecuteComponent::ValidateNode() return EDataValidationResult::Invalid; } - TSubclassOf ExpectedActorOwnerClass = TryGetExpectedActorOwnerClass(); + const TSubclassOf ExpectedActorOwnerClass = TryGetExpectedActorOwnerClass(); if (!IsValid(ExpectedActorOwnerClass)) { ValidationLog.Error(TEXT("Invalid or null Expected Actor Owner Class for this Flow Asset"), this); @@ -481,52 +474,51 @@ TSubclassOf UFlowNode_ExecuteComponent::TryGetExpectedActorOwnerClass() FText UFlowNode_ExecuteComponent::GetNodeTitle() const { - const bool bUseAdaptiveNodeTitles = UFlowSettings::Get()->bUseAdaptiveNodeTitles; - - if (bUseAdaptiveNodeTitles) + if (UFlowSettings::Get()->bUseAdaptiveNodeTitles) { static_assert(static_cast(EExecuteComponentSource::Max) == 4, TEXT("Update this code if the enum changes")); switch (ComponentSource) { - case EExecuteComponentSource::Undetermined: - break; + case EExecuteComponentSource::Undetermined: + break; - case EExecuteComponentSource::BindToExisting: - { - if (!ComponentRef.ComponentName.IsNone()) + case EExecuteComponentSource::BindToExisting: { - const FText ComponentNameText = FText::FromName(ComponentRef.ComponentName); + if (!ComponentRef.ComponentName.IsNone()) + { + const FText ComponentNameText = FText::FromName(ComponentRef.ComponentName); - return FText::Format(LOCTEXT("ExecuteComponent", "Execute {0}"), { ComponentNameText }); + return FText::Format(LOCTEXT("ExecuteComponent", "Execute {0}"), {ComponentNameText}); + } } - } - break; + break; - case EExecuteComponentSource::InjectFromTemplate: - { - if (IsValid(ComponentTemplate)) + case EExecuteComponentSource::InjectFromTemplate: { - FString ComponentNameString = ComponentTemplate->GetName(); - ComponentNameString.RemoveFromEnd(TEXT("_C")); - const FText ComponentNameText = FText::FromString(ComponentNameString); + if (IsValid(ComponentTemplate)) + { + FString ComponentNameString = ComponentTemplate->GetName(); + ComponentNameString.RemoveFromEnd(TEXT("_C")); + const FText ComponentNameText = FText::FromString(ComponentNameString); - return FText::Format(LOCTEXT("ExecuteComponent", "Execute {0}"), { ComponentNameText }); + return FText::Format(LOCTEXT("ExecuteComponent", "Execute {0}"), {ComponentNameText}); + } } - } - break; + break; - case EExecuteComponentSource::InjectFromClass: - { - if (IsValid(ComponentClass)) + case EExecuteComponentSource::InjectFromClass: { - FString ComponentClassString = ComponentClass->GetName(); - ComponentClassString.RemoveFromEnd(TEXT("_C")); - const FText ComponentNameText = FText::FromString(ComponentClassString); + if (IsValid(ComponentClass)) + { + FString ComponentClassString = ComponentClass->GetName(); + ComponentClassString.RemoveFromEnd(TEXT("_C")); + const FText ComponentNameText = FText::FromString(ComponentClassString); - return FText::Format(LOCTEXT("ExecuteComponent", "Execute {0}"), { ComponentNameText }); + return FText::Format(LOCTEXT("ExecuteComponent", "Execute {0}"), {ComponentNameText}); + } } - } - break; + break; + default: ; } } @@ -543,45 +535,43 @@ void UFlowNode_ExecuteComponent::UpdateNodeConfigText_Implementation() const bool bUseAdaptiveNodeTitles = UFlowSettings::Get()->bUseAdaptiveNodeTitles; if (!bUseAdaptiveNodeTitles) { - static_assert(static_cast(EExecuteComponentSource::Max) == 4, TEXT("Update this code if the enum changes")); switch (ComponentSource) { - case EExecuteComponentSource::Undetermined: - break; - - case EExecuteComponentSource::BindToExisting: - { - if (!ComponentRef.ComponentName.IsNone()) + case EExecuteComponentSource::Undetermined: + break; + case EExecuteComponentSource::BindToExisting: { - ComponentNameText = FText::FromName(ComponentRef.ComponentName); + if (!ComponentRef.ComponentName.IsNone()) + { + ComponentNameText = FText::FromName(ComponentRef.ComponentName); + } } - } - break; - - case EExecuteComponentSource::InjectFromTemplate: - { - if (IsValid(ComponentTemplate)) + break; + case EExecuteComponentSource::InjectFromTemplate: { - FString ComponentNameString = ComponentTemplate->GetName(); - ComponentNameString.RemoveFromEnd(TEXT("_C")); + if (IsValid(ComponentTemplate)) + { + FString ComponentNameString = ComponentTemplate->GetName(); + ComponentNameString.RemoveFromEnd(TEXT("_C")); - ComponentNameText = FText::FromString(ComponentNameString); + ComponentNameText = FText::FromString(ComponentNameString); + } } - } - break; - - case EExecuteComponentSource::InjectFromClass: - { - if (IsValid(ComponentClass)) + break; + case EExecuteComponentSource::InjectFromClass: { - FString ComponentClassString = ComponentClass->GetName(); - ComponentClassString.RemoveFromEnd(TEXT("_C")); + if (IsValid(ComponentClass)) + { + FString ComponentClassString = ComponentClass->GetName(); + ComponentClassString.RemoveFromEnd(TEXT("_C")); - ComponentNameText = FText::FromString(ComponentClassString); + ComponentNameText = FText::FromString(ComponentClassString); + } } - } - break; + break; + case EExecuteComponentSource::Max: + break; } } From cf250f00bcdf4c040e0e59e5db29ef74f8557d60 Mon Sep 17 00:00:00 2001 From: MaksymKapelianovych <48297221+MaksymKapelianovych@users.noreply.github.com> Date: Sun, 16 Jun 2024 18:39:54 +0300 Subject: [PATCH 244/485] Added check to prevent crash when deleting two or more node assets (if some of them are in undo history) (#201) --- Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index d400ed474..febf857e4 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -564,7 +564,7 @@ void UFlowGraphSchema::GetCommentAction(FGraphActionMenuBuilder& ActionMenuBuild bool UFlowGraphSchema::IsFlowNodeOrAddOnPlaceable(const UClass* Class) { - if (Class->HasAnyClassFlags(CLASS_Abstract | CLASS_NotPlaceable | CLASS_Deprecated)) + if (Class == nullptr || Class->HasAnyClassFlags(CLASS_Abstract | CLASS_NotPlaceable | CLASS_Deprecated)) { return false; } From 5cc2ac48f567fb5c4deab27053cfea228b291be9 Mon Sep 17 00:00:00 2001 From: NachoAbril Date: Sun, 16 Jun 2024 17:44:25 +0200 Subject: [PATCH 245/485] Change replication to Push Model (#204) --- Source/Flow/Flow.Build.cs | 1 + Source/Flow/Private/FlowComponent.cpp | 43 +++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/Source/Flow/Flow.Build.cs b/Source/Flow/Flow.Build.cs index 5896761f9..74df20a4c 100644 --- a/Source/Flow/Flow.Build.cs +++ b/Source/Flow/Flow.Build.cs @@ -22,6 +22,7 @@ public Flow(ReadOnlyTargetRules target) : base(target) "GameplayTags", "MovieScene", "MovieSceneTracks", + "NetCore", "Slate", "SlateCore" }); diff --git a/Source/Flow/Private/FlowComponent.cpp b/Source/Flow/Private/FlowComponent.cpp index 801a1ace9..fc89b5a5a 100644 --- a/Source/Flow/Private/FlowComponent.cpp +++ b/Source/Flow/Private/FlowComponent.cpp @@ -12,6 +12,7 @@ #include "Engine/ViewportStatsSubsystem.h" #include "Engine/World.h" #include "Net/UnrealNetwork.h" +#include "Net/Core/PushModel/PushModel.h" #include "Serialization/MemoryReader.h" #include "Serialization/MemoryWriter.h" @@ -34,12 +35,24 @@ void UFlowComponent::GetLifetimeReplicatedProps(TArray& OutLi { Super::GetLifetimeReplicatedProps(OutLifetimeProps); +#if WITH_PUSH_MODEL + FDoRepLifetimeParams Params; + Params.bIsPushBased = true; + + DOREPLIFETIME_WITH_PARAMS_FAST(UFlowComponent, AddedIdentityTags, Params); + DOREPLIFETIME_WITH_PARAMS_FAST(UFlowComponent, RemovedIdentityTags, Params); + + DOREPLIFETIME_WITH_PARAMS_FAST(UFlowComponent, RecentlySentNotifyTags, Params); + DOREPLIFETIME_WITH_PARAMS_FAST(UFlowComponent, NotifyTagsFromGraph, Params); + DOREPLIFETIME_WITH_PARAMS_FAST(UFlowComponent, NotifyTagsFromAnotherComponent, Params); +#else DOREPLIFETIME(UFlowComponent, AddedIdentityTags); DOREPLIFETIME(UFlowComponent, RemovedIdentityTags); DOREPLIFETIME(UFlowComponent, RecentlySentNotifyTags); DOREPLIFETIME(UFlowComponent, NotifyTagsFromGraph); DOREPLIFETIME(UFlowComponent, NotifyTagsFromAnotherComponent); +#endif } void UFlowComponent::BeginPlay() @@ -109,6 +122,9 @@ void UFlowComponent::AddIdentityTag(const FGameplayTag Tag, const EFlowNetMode N if (IsNetMode(NM_DedicatedServer) || IsNetMode(NM_ListenServer)) { AddedIdentityTags = FGameplayTagContainer(Tag); +#if WITH_PUSH_MODEL + MARK_PROPERTY_DIRTY_FROM_NAME(UFlowComponent, AddedIdentityTags, this); +#endif } } } @@ -141,6 +157,9 @@ void UFlowComponent::AddIdentityTags(FGameplayTagContainer Tags, const EFlowNetM if (IsNetMode(NM_DedicatedServer) || IsNetMode(NM_ListenServer)) { AddedIdentityTags = ValidatedTags; +#if WITH_PUSH_MODEL + MARK_PROPERTY_DIRTY_FROM_NAME(UFlowComponent, AddedIdentityTags, this); +#endif } } } @@ -164,6 +183,9 @@ void UFlowComponent::RemoveIdentityTag(const FGameplayTag Tag, const EFlowNetMod if (IsNetMode(NM_DedicatedServer) || IsNetMode(NM_ListenServer)) { RemovedIdentityTags = FGameplayTagContainer(Tag); +#if WITH_PUSH_MODEL + MARK_PROPERTY_DIRTY_FROM_NAME(UFlowComponent, RemovedIdentityTags, this); +#endif } } } @@ -196,6 +218,9 @@ void UFlowComponent::RemoveIdentityTags(FGameplayTagContainer Tags, const EFlowN if (IsNetMode(NM_DedicatedServer) || IsNetMode(NM_ListenServer)) { RemovedIdentityTags = ValidatedTags; +#if WITH_PUSH_MODEL + MARK_PROPERTY_DIRTY_FROM_NAME(UFlowComponent, RemovedIdentityTags, this); +#endif } } } @@ -267,6 +292,12 @@ void UFlowComponent::NotifyGraph(const FGameplayTag NotifyTag, const EFlowNetMod // save recently notify, this allow for the retroactive check in nodes // if retroactive check wouldn't be performed, this is only used by the network replication RecentlySentNotifyTags = FGameplayTagContainer(NotifyTag); +#if WITH_PUSH_MODEL + if (IsNetMode(NM_DedicatedServer) || IsNetMode(NM_ListenServer)) + { + MARK_PROPERTY_DIRTY_FROM_NAME(UFlowComponent, RecentlySentNotifyTags, this); + } +#endif OnRep_SentNotifyTags(); } @@ -290,6 +321,12 @@ void UFlowComponent::BulkNotifyGraph(const FGameplayTagContainer NotifyTags, con // save recently notify, this allow for the retroactive check in nodes // if retroactive check wouldn't be performed, this is only used by the network replication RecentlySentNotifyTags = ValidatedTags; +#if WITH_PUSH_MODEL + if (IsNetMode(NM_DedicatedServer) || IsNetMode(NM_ListenServer)) + { + MARK_PROPERTY_DIRTY_FROM_NAME(UFlowComponent, RecentlySentNotifyTags, this); + } +#endif OnRep_SentNotifyTags(); } @@ -327,6 +364,9 @@ void UFlowComponent::NotifyFromGraph(const FGameplayTagContainer& NotifyTags, co if (IsNetMode(NM_DedicatedServer) || IsNetMode(NM_ListenServer)) { NotifyTagsFromGraph = ValidatedTags; +#if WITH_PUSH_MODEL + MARK_PROPERTY_DIRTY_FROM_NAME(UFlowComponent, NotifyTagsFromGraph, this); +#endif } } } @@ -356,6 +396,9 @@ void UFlowComponent::NotifyActor(const FGameplayTag ActorTag, const FGameplayTag { NotifyTagsFromAnotherComponent.Empty(); NotifyTagsFromAnotherComponent.Add(FNotifyTagReplication(ActorTag, NotifyTag)); +#if WITH_PUSH_MODEL + MARK_PROPERTY_DIRTY_FROM_NAME(UFlowComponent, NotifyTagsFromAnotherComponent, this); +#endif } } } From 38a17e0de19d8cf0e89a78053bea62488f27f615 Mon Sep 17 00:00:00 2001 From: Soraphis Date: Sun, 16 Jun 2024 17:47:59 +0200 Subject: [PATCH 246/485] Moves the Start or Load of the RootFlow to a virtual function. (#208) This allows projects are finer controll when implementing saving and loading. --- Source/Flow/Private/FlowComponent.cpp | 23 ++++++++++++++--------- Source/Flow/Public/FlowComponent.h | 3 ++- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/Source/Flow/Private/FlowComponent.cpp b/Source/Flow/Private/FlowComponent.cpp index fc89b5a5a..62bc4de3a 100644 --- a/Source/Flow/Private/FlowComponent.cpp +++ b/Source/Flow/Private/FlowComponent.cpp @@ -74,16 +74,21 @@ void UFlowComponent::RegisterWithFlowSubsystem() FlowSubsystem->RegisterComponent(this); - if (RootFlow) + BeginRootFlow(bComponentLoadedFromSaveGame); + } +} + +void UFlowComponent::BeginRootFlow(bool bComponentLoadedFromSaveGame) +{ + if (RootFlow) + { + if (bComponentLoadedFromSaveGame) { - if (bComponentLoadedFromSaveGame) - { - LoadRootFlow(); - } - else if (bAutoStartRootFlow) - { - StartRootFlow(); - } + LoadRootFlow(); + } + else if (bAutoStartRootFlow) + { + StartRootFlow(); } } } diff --git a/Source/Flow/Public/FlowComponent.h b/Source/Flow/Public/FlowComponent.h index d95fa7abf..0ebba3111 100644 --- a/Source/Flow/Public/FlowComponent.h +++ b/Source/Flow/Public/FlowComponent.h @@ -84,7 +84,8 @@ class FLOW_API UFlowComponent : public UActorComponent, public IFlowOwnerInterfa protected: void RegisterWithFlowSubsystem(); void UnregisterWithFlowSubsystem(); - + virtual void BeginRootFlow(bool bComponentLoadedFromSaveGame); + private: UFUNCTION() void OnRep_AddedIdentityTags(); From be5f8da16b9d2d0f017c90f0a32e2ef5512f1475 Mon Sep 17 00:00:00 2001 From: LindyHopperGT <91915878+LindyHopperGT@users.noreply.github.com> Date: Thu, 20 Jun 2024 08:58:08 -0700 Subject: [PATCH 247/485] Improvements for FlowGraphDiffing (#210) * Flow AddOns Introduced FlowAddOns ~ Major rework of the Flow Editor to support AddOns ~ Also added ExecuteComponent flow node, which allows a component of the owning actor (if it implements a specific interface) to be executed via a proxy node in the Flow Graph. We use this to wrap some legacy systems in flow graph nodes. * Fix Linux compile errors and regressions on UFUNCTIONS * Fix bug with ExecuteInput for AddOns Nodes and AddOns could execute for pins they did not expect, if the AddOns introduced pins that the Node (or other AddOns) were not aware of. Also, removed some deprecation warnings because they were generating compile warnings, which could cause some projects problems with automated builds. I changed the deprecation message to a human-readable message only. * Slight adjustment to IsSupportedInputPinName() early return if the array is empty * AddOns Optimized and changed how Input/Outputs are added from AddOns * Quality of life improvements Revised the ForEachAddOn() signature, since it only ever needs to be used when iterating over all AddOns (recursively), as you can use a normal for loop over AddOns for just the AddOns of this object alone. Also moved some access functions from FlowNode to FlowNodeBase and exposed them to Blueprint, based on a question I saw on the Discord about Get Actor Owner access. * Extracted Component injection code into Flow base also updated ExecuteComponent to use it * Transferring flags from Template component to instance + Transient * Singular InjectComponent signature * Small revisions to Execute Component flownode * Fix uniqueness problem with component lookup * fixed GC-related compilation warning in AddReferencedObjects() * cosmetic pass on AddOns refactor - Adding Category to a few functions which fixes build machine compilation in the plugin mode. - Partially restoring class layout to its original shape, taking in account new sections, and improve it a little bit. - Reversing some changes on making symbols editor-only which might break non-editor tools working with Flow Graph. - Static analysis fixes. * Flow Diff improvements * Added AActor::GetActorClassDefaultComponents() alternative for UE < 5.3 * Folded IFlowNativeExecutableInterface into UFlowNodeBase * Fix compile error --------- Co-authored-by: FoolsTheoryDev --- Source/Flow/Private/AddOns/FlowNodeAddOn.cpp | 9 +- .../FlowExternalExecutableInterface.cpp | 5 +- .../FlowNativeExecutableInterface.cpp | 24 -- Source/Flow/Private/Nodes/FlowNode.cpp | 9 +- Source/Flow/Private/Nodes/FlowNodeBase.cpp | 20 ++ .../World/FlowNode_CallOwnerFunction.cpp | 2 +- .../Nodes/World/FlowNode_ExecuteComponent.cpp | 217 ++++++------- Source/Flow/Public/AddOns/FlowNodeAddOn.h | 15 +- .../FlowExternalExecutableInterface.h | 7 +- .../FlowNativeExecutableInterface.h | 44 --- Source/Flow/Public/Nodes/FlowNode.h | 4 - Source/Flow/Public/Nodes/FlowNodeBase.h | 39 ++- .../Private/Asset/FlowDiffControl.cpp | 291 +++++++++++++++++- .../Private/Asset/FlowObjectDiff.cpp | 87 ++++++ Source/FlowEditor/Private/Asset/SFlowDiff.cpp | 120 +++----- ...lowActorOwnerComponentRefCustomization.cpp | 6 + .../Pre_5_3_GetActorClassDefaultComponents.h | 82 +++++ .../Private/Graph/FlowGraphEditor.cpp | 1 + .../Private/Graph/Nodes/FlowGraphNode.cpp | 2 + .../FlowEditor/Public/Asset/FlowDiffControl.h | 25 +- .../FlowEditor/Public/Asset/FlowObjectDiff.h | 61 ++++ Source/FlowEditor/Public/Asset/SFlowDiff.h | 12 +- 22 files changed, 789 insertions(+), 293 deletions(-) delete mode 100644 Source/Flow/Private/Interfaces/FlowNativeExecutableInterface.cpp delete mode 100644 Source/Flow/Public/Interfaces/FlowNativeExecutableInterface.h create mode 100644 Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp create mode 100644 Source/FlowEditor/Private/DetailCustomizations/Pre_5_3_GetActorClassDefaultComponents.h create mode 100644 Source/FlowEditor/Public/Asset/FlowObjectDiff.h diff --git a/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp b/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp index b181d1f00..eb6e5d5e9 100644 --- a/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp +++ b/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp @@ -68,7 +68,14 @@ bool UFlowNodeAddOn::IsSupportedInputPinName(const FName& PinName) const return true; } - return FindFlowPinByName(PinName, InputPins) ? true : false; + if (const FFlowPin* FoundFlowPin = FindFlowPinByName(PinName, InputPins)) + { + return true; + } + else + { + return false; + } } void UFlowNodeAddOn::CacheFlowNode() diff --git a/Source/Flow/Private/Interfaces/FlowExternalExecutableInterface.cpp b/Source/Flow/Private/Interfaces/FlowExternalExecutableInterface.cpp index 7ed8b1fa9..6697d5405 100644 --- a/Source/Flow/Private/Interfaces/FlowExternalExecutableInterface.cpp +++ b/Source/Flow/Private/Interfaces/FlowExternalExecutableInterface.cpp @@ -1,9 +1,8 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "Interfaces/FlowExternalExecutableInterface.h" -#include "Interfaces/FlowNativeExecutableInterface.h" -void IFlowExternalExecutableInterface::PreActivateExternalFlowExecutable(IFlowNativeExecutableInterface& NativeExecutorProxy) +void IFlowExternalExecutableInterface::PreActivateExternalFlowExecutable(UFlowNodeBase& FlowNodeBase) { - Execute_K2_PreActivateExternalFlowExecutable(Cast(this), TScriptInterface(CastChecked(&NativeExecutorProxy))); + Execute_K2_PreActivateExternalFlowExecutable(Cast(this), &FlowNodeBase); } diff --git a/Source/Flow/Private/Interfaces/FlowNativeExecutableInterface.cpp b/Source/Flow/Private/Interfaces/FlowNativeExecutableInterface.cpp deleted file mode 100644 index e34a75957..000000000 --- a/Source/Flow/Private/Interfaces/FlowNativeExecutableInterface.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#include "Interfaces/FlowNativeExecutableInterface.h" - -void IFlowNativeExecutableInterface::TriggerOutputPin(const FFlowOutputPinHandle Pin, const bool bFinish, const EFlowPinActivationType ActivationType) -{ - TriggerOutput(Pin.PinName, bFinish, ActivationType); -} - -void IFlowNativeExecutableInterface::TriggerOutput(const FString& PinName, const bool bFinish) -{ - TriggerOutput(FName(PinName), bFinish); -} - -void IFlowNativeExecutableInterface::TriggerOutput(const FText& PinName, const bool bFinish) -{ - TriggerOutput(FName(PinName.ToString()), bFinish); -} - -void IFlowNativeExecutableInterface::TriggerOutput(const TCHAR* PinName, const bool bFinish) -{ - TriggerOutput(FName(PinName), bFinish); -} - diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 8bd376263..4386606ef 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -81,7 +81,14 @@ bool UFlowNode::IsSupportedInputPinName(const FName& PinName) const return true; } - return FindFlowPinByName(PinName, InputPins) ? true : false; + if (const FFlowPin* FoundInputFlowPin = FindFlowPinByName(PinName, InputPins)) + { + return true; + } + else + { + return false; + } } void UFlowNode::AddInputPins(TArray Pins) diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp index b431a7b5e..7385d2dce 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBase.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -158,6 +158,26 @@ void UFlowNodeBase::Cleanup() IFlowCoreExecutableInterface::Cleanup(); } +void UFlowNodeBase::TriggerOutputPin(const FFlowOutputPinHandle Pin, const bool bFinish, const EFlowPinActivationType ActivationType) +{ + TriggerOutput(Pin.PinName, bFinish, ActivationType); +} + +void UFlowNodeBase::TriggerOutput(const FString& PinName, const bool bFinish) +{ + TriggerOutput(FName(PinName), bFinish); +} + +void UFlowNodeBase::TriggerOutput(const FText& PinName, const bool bFinish) +{ + TriggerOutput(FName(PinName.ToString()), bFinish); +} + +void UFlowNodeBase::TriggerOutput(const TCHAR* PinName, const bool bFinish) +{ + TriggerOutput(FName(PinName), bFinish); +} + const FFlowPin* UFlowNodeBase::FindFlowPinByName(const FName& PinName, const TArray& FlowPins) { return FlowPins.FindByPredicate([&PinName](const FFlowPin& FlowPin) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp b/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp index 5995f17fc..83eec5d51 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp @@ -63,7 +63,7 @@ void UFlowNode_CallOwnerFunction::ExecuteInput(const FName& PinName) Params->PostExecute(); - (void)TryExecuteOutputPin(ResultOutputName); + (void) TryExecuteOutputPin(ResultOutputName); } bool UFlowNode_CallOwnerFunction::TryExecuteOutputPin(const FName& OutputName) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp b/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp index 6854e4bdd..6bd9794c8 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp @@ -29,7 +29,7 @@ void UFlowNode_ExecuteComponent::InitializeInstance() { Super::InitializeInstance(); - (void)TryInjectComponent(); + (void) TryInjectComponent(); if (UActorComponent* ResolvedComp = TryResolveComponent()) { @@ -111,17 +111,15 @@ void UFlowNode_ExecuteComponent::OnActivate() if (UActorComponent* ResolvedComp = TryResolveComponent()) { - IFlowNativeExecutableInterface* ThisAsNativeExecutorProxy = CastChecked(this); - if (IFlowExternalExecutableInterface* ComponentAsExternalExecutable = Cast(ResolvedComp)) { // By convention, we must call the PreActivateExternalFlowExecutable() before OnActivate // when we (this node) are acting as the proxy for an IFlowExternalExecutableInterface object - ComponentAsExternalExecutable->PreActivateExternalFlowExecutable(*ThisAsNativeExecutorProxy); + ComponentAsExternalExecutable->PreActivateExternalFlowExecutable(*this); } else if (ResolvedComp->Implements()) { - IFlowExternalExecutableInterface::Execute_K2_PreActivateExternalFlowExecutable(ResolvedComp, TScriptInterface(this)); + IFlowExternalExecutableInterface::Execute_K2_PreActivateExternalFlowExecutable(ResolvedComp, this); } else { @@ -217,53 +215,53 @@ bool UFlowNode_ExecuteComponent::TryInjectComponent() static_assert(static_cast(EExecuteComponentSource::Max) == 4, TEXT("Update this code if the enum changes")); switch (ComponentSource) { - case EExecuteComponentSource::InjectFromTemplate: + case EExecuteComponentSource::InjectFromTemplate: + { + if (IsValid(ComponentTemplate)) { - if (IsValid(ComponentTemplate)) + if (UActorComponent* ComponentInstance = FFlowInjectComponentsHelper::TryCreateComponentInstanceForActorFromTemplate(*ActorOwner, *ComponentTemplate)) { - if (UActorComponent* ComponentInstance = FFlowInjectComponentsHelper::TryCreateComponentInstanceForActorFromTemplate(*ActorOwner, *ComponentTemplate)) - { - ComponentInstances.Add(ComponentInstance); - } + ComponentInstances.Add(ComponentInstance); } } - break; + } + break; - case EExecuteComponentSource::InjectFromClass: + case EExecuteComponentSource::InjectFromClass: + { + if (IsValid(ComponentClass)) { - if (IsValid(ComponentClass)) + if (bReuseExistingComponent) { - if (bReuseExistingComponent) - { - // Look for the component class existing already on the actor, for potential re-use + // Look for the component class existing already on the actor, for potential re-use - UActorComponent* ExistingComponent = ActorOwner->FindComponentByClass(ComponentClass); - if (IsValid(ExistingComponent)) - { - // Set the ComponentRef directly (for later lookup via TryResolveComponent) - ComponentRef.SetResolvedComponentDirect(*ExistingComponent); - - return true; - } + UActorComponent* ExistingComponent = ActorOwner->FindComponentByClass(ComponentClass); + if (IsValid(ExistingComponent)) + { + // Set the ComponentRef directly (for later lookup via TryResolveComponent) + ComponentRef.SetResolvedComponentDirect(*ExistingComponent); - if (!bAllowInjectComponent) - { - return false; - } + return true; } - const FName InstanceBaseName = ComponentClass->GetFName(); - if (UActorComponent* ComponentInstance = FFlowInjectComponentsHelper::TryCreateComponentInstanceForActorFromClass(*ActorOwner, *ComponentClass, InstanceBaseName)) + if (!bAllowInjectComponent) { - ComponentInstances.Add(ComponentInstance); + return false; } } + + const FName InstanceBaseName = ComponentClass->GetFName(); + if (UActorComponent* ComponentInstance = FFlowInjectComponentsHelper::TryCreateComponentInstanceForActorFromClass(*ActorOwner, *ComponentClass, InstanceBaseName)) + { + ComponentInstances.Add(ComponentInstance); + } } - break; + } + break; - default: - checkNoEntry(); - return false; + default: + checkNoEntry(); + return false; } // Create the manager object if we're injecting a component @@ -316,24 +314,25 @@ const UActorComponent* UFlowNode_ExecuteComponent::TryGetExpectedComponent() con static_assert(static_cast(EExecuteComponentSource::Max) == 4, TEXT("Update this code if the enum changes")); switch (ComponentSource) { - case EExecuteComponentSource::Undetermined: - { - return nullptr; - } - case EExecuteComponentSource::BindToExisting: - { - return AActor::GetActorClassDefaultComponentByName(ExpectedOwnerClass, ComponentRef.ComponentName); - } - case EExecuteComponentSource::InjectFromTemplate: - { - return ComponentTemplate; - } - case EExecuteComponentSource::InjectFromClass: - { - return IsValid(ComponentClass) ? ComponentClass->GetDefaultObject() : nullptr; - } - default: + case EExecuteComponentSource::Undetermined: + { return nullptr; + } + case EExecuteComponentSource::BindToExisting: + { + return AActor::GetActorClassDefaultComponentByName(ExpectedOwnerClass, ComponentRef.ComponentName); + } + case EExecuteComponentSource::InjectFromTemplate: + { + return ComponentTemplate; + } + case EExecuteComponentSource::InjectFromClass: + { + return IsValid(ComponentClass) ? ComponentClass->GetDefaultObject() : nullptr; + } + + default: + return nullptr; } } @@ -479,46 +478,47 @@ FText UFlowNode_ExecuteComponent::GetNodeTitle() const static_assert(static_cast(EExecuteComponentSource::Max) == 4, TEXT("Update this code if the enum changes")); switch (ComponentSource) { - case EExecuteComponentSource::Undetermined: - break; + case EExecuteComponentSource::Undetermined: + break; - case EExecuteComponentSource::BindToExisting: + case EExecuteComponentSource::BindToExisting: + { + if (!ComponentRef.ComponentName.IsNone()) { - if (!ComponentRef.ComponentName.IsNone()) - { - const FText ComponentNameText = FText::FromName(ComponentRef.ComponentName); + const FText ComponentNameText = FText::FromName(ComponentRef.ComponentName); - return FText::Format(LOCTEXT("ExecuteComponent", "Execute {0}"), {ComponentNameText}); - } + return FText::Format(LOCTEXT("ExecuteComponent", "Execute {0}"), {ComponentNameText}); } - break; + } + break; - case EExecuteComponentSource::InjectFromTemplate: + case EExecuteComponentSource::InjectFromTemplate: + { + if (IsValid(ComponentTemplate)) { - if (IsValid(ComponentTemplate)) - { - FString ComponentNameString = ComponentTemplate->GetName(); - ComponentNameString.RemoveFromEnd(TEXT("_C")); - const FText ComponentNameText = FText::FromString(ComponentNameString); + FString ComponentNameString = ComponentTemplate->GetName(); + ComponentNameString.RemoveFromEnd(TEXT("_C")); + const FText ComponentNameText = FText::FromString(ComponentNameString); - return FText::Format(LOCTEXT("ExecuteComponent", "Execute {0}"), {ComponentNameText}); - } + return FText::Format(LOCTEXT("ExecuteComponent", "Execute {0}"), {ComponentNameText}); } - break; + } + break; - case EExecuteComponentSource::InjectFromClass: + case EExecuteComponentSource::InjectFromClass: + { + if (IsValid(ComponentClass)) { - if (IsValid(ComponentClass)) - { - FString ComponentClassString = ComponentClass->GetName(); - ComponentClassString.RemoveFromEnd(TEXT("_C")); - const FText ComponentNameText = FText::FromString(ComponentClassString); + FString ComponentClassString = ComponentClass->GetName(); + ComponentClassString.RemoveFromEnd(TEXT("_C")); + const FText ComponentNameText = FText::FromString(ComponentClassString); - return FText::Format(LOCTEXT("ExecuteComponent", "Execute {0}"), {ComponentNameText}); - } + return FText::Format(LOCTEXT("ExecuteComponent", "Execute {0}"), {ComponentNameText}); } - break; - default: ; + } + break; + + default: break; } } @@ -538,40 +538,43 @@ void UFlowNode_ExecuteComponent::UpdateNodeConfigText_Implementation() static_assert(static_cast(EExecuteComponentSource::Max) == 4, TEXT("Update this code if the enum changes")); switch (ComponentSource) { - case EExecuteComponentSource::Undetermined: - break; - case EExecuteComponentSource::BindToExisting: + case EExecuteComponentSource::Undetermined: + break; + + case EExecuteComponentSource::BindToExisting: + { + if (!ComponentRef.ComponentName.IsNone()) { - if (!ComponentRef.ComponentName.IsNone()) - { - ComponentNameText = FText::FromName(ComponentRef.ComponentName); - } + ComponentNameText = FText::FromName(ComponentRef.ComponentName); } - break; - case EExecuteComponentSource::InjectFromTemplate: + } + break; + + case EExecuteComponentSource::InjectFromTemplate: + { + if (IsValid(ComponentTemplate)) { - if (IsValid(ComponentTemplate)) - { - FString ComponentNameString = ComponentTemplate->GetName(); - ComponentNameString.RemoveFromEnd(TEXT("_C")); + FString ComponentNameString = ComponentTemplate->GetName(); + ComponentNameString.RemoveFromEnd(TEXT("_C")); - ComponentNameText = FText::FromString(ComponentNameString); - } + ComponentNameText = FText::FromString(ComponentNameString); } - break; - case EExecuteComponentSource::InjectFromClass: + } + break; + + case EExecuteComponentSource::InjectFromClass: + { + if (IsValid(ComponentClass)) { - if (IsValid(ComponentClass)) - { - FString ComponentClassString = ComponentClass->GetName(); - ComponentClassString.RemoveFromEnd(TEXT("_C")); + FString ComponentClassString = ComponentClass->GetName(); + ComponentClassString.RemoveFromEnd(TEXT("_C")); - ComponentNameText = FText::FromString(ComponentClassString); - } + ComponentNameText = FText::FromString(ComponentClassString); } - break; - case EExecuteComponentSource::Max: - break; + } + break; + + default: break; } } diff --git a/Source/Flow/Public/AddOns/FlowNodeAddOn.h b/Source/Flow/Public/AddOns/FlowNodeAddOn.h index 42590e0f6..bf359c11b 100644 --- a/Source/Flow/Public/AddOns/FlowNodeAddOn.h +++ b/Source/Flow/Public/AddOns/FlowNodeAddOn.h @@ -3,7 +3,6 @@ #pragma once #include "Nodes/FlowNodeBase.h" -#include "Interfaces/FlowNativeExecutableInterface.h" #include "Nodes/FlowPin.h" #include "FlowNodeAddOn.generated.h" @@ -14,9 +13,7 @@ class UFlowNode; * A Flow Node AddOn allows user to extend given node instance in the graph with additional logic. */ UCLASS(Abstract, MinimalApi, EditInlineNew, Blueprintable) -class UFlowNodeAddOn - : public UFlowNodeBase - , public IFlowNativeExecutableInterface +class UFlowNodeAddOn : public UFlowNodeBase { GENERATED_BODY() @@ -44,6 +41,10 @@ class UFlowNodeAddOn FLOW_API virtual UFlowNode* GetFlowNodeSelfOrOwner() override { return FlowNode; } FLOW_API virtual bool IsSupportedInputPinName(const FName& PinName) const override; + + FLOW_API virtual void TriggerFirstOutput(const bool bFinish) override; + FLOW_API virtual void TriggerOutput(const FName PinName, const bool bFinish = false, const EFlowPinActivationType ActivationType = EFlowPinActivationType::Default) override; + FLOW_API virtual void Finish() override; // -- // IFlowCoreExecutableInterface @@ -51,12 +52,6 @@ class UFlowNodeAddOn FLOW_API virtual void DeinitializeInstance() override; // -- - // IFlowNativeExecutableInterface - FLOW_API virtual void TriggerFirstOutput(const bool bFinish) override; - FLOW_API virtual void TriggerOutput(const FName PinName, const bool bFinish = false, const EFlowPinActivationType ActivationType = EFlowPinActivationType::Default) override; - FLOW_API virtual void Finish() override; - // -- - // UFlowNodeAddOn UFUNCTION(BlueprintCallable, BlueprintPure, Category = "FlowNodeAddon", DisplayName = "Get Flow Node") FLOW_API UFlowNode* GetFlowNode() const; diff --git a/Source/Flow/Public/Interfaces/FlowExternalExecutableInterface.h b/Source/Flow/Public/Interfaces/FlowExternalExecutableInterface.h index 6579d49f4..4117ef9b2 100644 --- a/Source/Flow/Public/Interfaces/FlowExternalExecutableInterface.h +++ b/Source/Flow/Public/Interfaces/FlowExternalExecutableInterface.h @@ -7,8 +7,7 @@ #include "FlowExternalExecutableInterface.generated.h" -// Forward Declarations -class IFlowNativeExecutableInterface; +class UFlowNodeBase; // Implemented by external objects that can execute within a flow graph // via a FlowNode or FlowNodeAddOn proxy @@ -28,6 +27,6 @@ class FLOW_API IFlowExternalExecutableInterface // external executable object in the flow graph. This is primarily done so that the external element has a // handle to call TriggerOutput() and Finish() when it has completed its work. UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", DisplayName = "PreActivateExternalFlowExecutable") - void K2_PreActivateExternalFlowExecutable(const TScriptInterface& NativeExecutorProxy); - virtual void PreActivateExternalFlowExecutable(IFlowNativeExecutableInterface& NativeExecutorProxy); + void K2_PreActivateExternalFlowExecutable(const UFlowNodeBase* FlowNodeBase); + virtual void PreActivateExternalFlowExecutable(UFlowNodeBase& FlowNodeBase); }; diff --git a/Source/Flow/Public/Interfaces/FlowNativeExecutableInterface.h b/Source/Flow/Public/Interfaces/FlowNativeExecutableInterface.h deleted file mode 100644 index 63e12cee0..000000000 --- a/Source/Flow/Public/Interfaces/FlowNativeExecutableInterface.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#pragma once - -#include "UObject/Interface.h" -#include "Nodes/FlowPin.h" - -#include "FlowNativeExecutableInterface.generated.h" - -// Implemented only by native (C++) objects that can execute within a flow graph -// (eg, UFlowNode and UFlowNodeAddOn subclasses implement this) -UINTERFACE(MinimalAPI, BlueprintType, DisplayName = "Flow Native Executable Interface", meta = (CannotImplementInterfaceInBlueprint)) -class UFlowNativeExecutableInterface : public UInterface -{ - GENERATED_BODY() -}; - -class FLOW_API IFlowNativeExecutableInterface -{ - GENERATED_BODY() - -public: - - // Simply trigger the first Output Pin, convenient to use if node has only one output - UFUNCTION(BlueprintCallable, Category = "FlowNode") - virtual void TriggerFirstOutput(const bool bFinish) = 0; - - // Cause a specific output to be triggered (by PinName) - UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (HidePin = "ActivationType")) - virtual void TriggerOutput(const FName PinName, const bool bFinish = false, const EFlowPinActivationType ActivationType = EFlowPinActivationType::Default) = 0; - - // Cause a specific output to be triggered (by PinHandle) - UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (HidePin = "ActivationType")) - virtual void TriggerOutputPin(const FFlowOutputPinHandle Pin, const bool bFinish = false, const EFlowPinActivationType ActivationType = EFlowPinActivationType::Default); - - // TriggerOutput convenience aliases - void TriggerOutput(const FString& PinName, const bool bFinish = false); - void TriggerOutput(const FText& PinName, const bool bFinish = false); - void TriggerOutput(const TCHAR* PinName, const bool bFinish = false); - - // Finish execution of node, it will call Cleanup - UFUNCTION(BlueprintCallable, Category = "FlowNode") - virtual void Finish() = 0; -}; diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index da3ce1f31..12da3b6d7 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -8,7 +8,6 @@ #include "FlowNodeBase.h" #include "FlowTypes.h" -#include "Interfaces/FlowNativeExecutableInterface.h" #include "Nodes/FlowPin.h" #include "FlowNode.generated.h" @@ -19,7 +18,6 @@ UCLASS(Abstract, Blueprintable, HideCategories = Object) class FLOW_API UFlowNode : public UFlowNodeBase - , public IFlowNativeExecutableInterface , public IVisualLoggerDebugSnapshotInterface { GENERATED_UCLASS_BODY() @@ -209,7 +207,6 @@ class FLOW_API UFlowNode // Trigger execution of input pin void TriggerInput(const FName& PinName, const EFlowPinActivationType ActivationType = EFlowPinActivationType::Default); - // IFlowNativeExecutableInterface protected: void Deactivate(); @@ -217,7 +214,6 @@ class FLOW_API UFlowNode virtual void TriggerOutput(FName PinName, const bool bFinish = false, const EFlowPinActivationType ActivationType = EFlowPinActivationType::Default) override; public: virtual void Finish() override; - // -- private: void ResetRecords(); diff --git a/Source/Flow/Public/Nodes/FlowNodeBase.h b/Source/Flow/Public/Nodes/FlowNodeBase.h index abb2af44d..195ca7af8 100644 --- a/Source/Flow/Public/Nodes/FlowNodeBase.h +++ b/Source/Flow/Public/Nodes/FlowNodeBase.h @@ -28,11 +28,11 @@ typedef TFunction FFlowNodeAddOnFunction; /** * The base class for UFlowNode and UFlowNodeAddOn, with their shared functionality */ -UCLASS(Abstract, HideCategories = Object) +UCLASS(Abstract, BlueprintType, HideCategories = Object) class FLOW_API UFlowNodeBase : public UObject - , public IFlowCoreExecutableInterface - , public IFlowContextPinSupplierInterface + , public IFlowCoreExecutableInterface + , public IFlowContextPinSupplierInterface { GENERATED_UCLASS_BODY() @@ -63,6 +63,27 @@ class FLOW_API UFlowNodeBase virtual void Cleanup() override; // -- + // Finish execution of node, it will call Cleanup + UFUNCTION(BlueprintCallable, Category = "FlowNode") + virtual void Finish() PURE_VIRTUAL(Finish) + + // Simply trigger the first Output Pin, convenient to use if node has only one output + UFUNCTION(BlueprintCallable, Category = "FlowNode") + virtual void TriggerFirstOutput(const bool bFinish) PURE_VIRTUAL(TriggerFirstOutput) + + // Cause a specific output to be triggered (by PinName) + UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (HidePin = "ActivationType")) + virtual void TriggerOutput(const FName PinName, const bool bFinish = false, const EFlowPinActivationType ActivationType = EFlowPinActivationType::Default) PURE_VIRTUAL(TriggerOutput) + + // TriggerOutput convenience aliases + void TriggerOutput(const FString& PinName, const bool bFinish = false); + void TriggerOutput(const FText& PinName, const bool bFinish = false); + void TriggerOutput(const TCHAR* PinName, const bool bFinish = false); + + // Cause a specific output to be triggered (by PinHandle) + UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (HidePin = "ActivationType")) + virtual void TriggerOutputPin(const FFlowOutputPinHandle Pin, const bool bFinish = false, const EFlowPinActivationType ActivationType = EFlowPinActivationType::Default); + ////////////////////////////////////////////////////////////////////////// // Pins @@ -253,20 +274,20 @@ class FLOW_API UFlowNodeBase UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) void LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType = EFlowOnScreenMessageType::Permanent); - // LogError from constant function (allowing this to be modified only to log the error itself) - FORCEINLINE void LogErrorConst(FString Message, const EFlowOnScreenMessageType OnScreenMessageType = EFlowOnScreenMessageType::Permanent) const; + // LogError from constant function (allowing 'this' to be modified only to log the error itself) + void LogErrorConst(FString Message, const EFlowOnScreenMessageType OnScreenMessageType = EFlowOnScreenMessageType::Permanent) const; UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) void LogWarning(FString Message); - // LogWarning from constant function (allowing this to be modified only to log the warning itself) - FORCEINLINE void LogWarningConst(FString Message) const; + // LogWarning from constant function (allowing 'this' to be modified only to log the warning itself) + void LogWarningConst(FString Message) const; UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) void LogNote(FString Message); - // LogNote from constant function (allowing this to be modified only to log the note itself) - FORCEINLINE void LogNoteConst(FString Message) const; + // LogNote from constant function (allowing 'this' to be modified only to log the note itself) + void LogNoteConst(FString Message) const; #if !UE_BUILD_SHIPPING protected: diff --git a/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp b/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp index 71f8a282b..2c16ab486 100644 --- a/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp +++ b/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp @@ -7,10 +7,100 @@ #include "EdGraph/EdGraph.h" #include "GraphDiffControl.h" +#include "Graph/Nodes/FlowGraphNode.h" #include "SBlueprintDiff.h" #define LOCTEXT_NAMESPACE "SFlowDiffControl" +namespace +{ + FString GetDiffKeyFromDiffResult(const FDiffResultItem& Difference) + { + if (Difference.Result.Pin1) + { + return Difference.Result.Pin1->PinId.ToString() + Difference.Result.Pin2->PinId.ToString(); + } + + const UFlowGraphNode* FlowGraphNode = Cast(Difference.Result.Node1); + if (!IsValid(FlowGraphNode)) + { + FlowGraphNode = Cast(Difference.Result.Node2); + } + + if (IsValid(FlowGraphNode)) + { + return FString::FromInt(FlowGraphNode->GetNodeTemplate()->GetUniqueID());//->GetName(); + } + + if (IsValid(Difference.Result.Node1)) + { + return FString::FromInt(Difference.Result.Node1->GetUniqueID()); + } + + if (IsValid(Difference.Result.Node2)) + { + return FString::FromInt(Difference.Result.Node2->GetUniqueID()); + } + + return TEXT("Invalid Diff!"); + } + + FString GetNodeNameFromDiffResult(const FDiffResultItem& Difference) + { + if (Difference.Result.Pin1) + { + return TEXT("Invalid Diff!"); + } + + const UFlowGraphNode* FlowGraphNode = Cast(Difference.Result.Node1); + if (!IsValid(FlowGraphNode)) + { + FlowGraphNode = Cast(Difference.Result.Node2); + } + + if (IsValid(FlowGraphNode)) + { + return FlowGraphNode->GetNodeTemplate()->GetName(); + } + + if (IsValid(Difference.Result.Node1)) + { + return Difference.Result.Node1->GetName(); + } + + if (IsValid(Difference.Result.Node2)) + { + return Difference.Result.Node2->GetName(); + } + + return TEXT("Invalid Diff!"); + } + + void ModifyDiffDisplayName(FDiffSingleResult& InOutResult, const FString& DiffKey) + { + //Only modify the DisplayStrings of node changes, because they are not very descriptive. + //Other generated DisplayStrings seem fine, such as pin connection changes. + if ((IsValid(InOutResult.Node1) || IsValid(InOutResult.Node2)) + && InOutResult.Pin1 == nullptr) + { + FString AdditionalDescription; + switch (InOutResult.Category) + { + case EDiffType::Category::SUBTRACTION: + AdditionalDescription = TEXT("Removed "); + break; + case EDiffType::Category::ADDITION: + AdditionalDescription = TEXT("Added "); + break; + default: + break; + } + + InOutResult.DisplayString = FText::FromString(AdditionalDescription + DiffKey); + } + } +} + ///////////////////////////////////////////////////////////////////////////// /// FFlowAssetDiffControl @@ -79,6 +169,28 @@ FFlowGraphToDiff::~FFlowGraphToDiff() } } +ENodeDiffType FFlowGraphToDiff::GetNodeDiffType(const UEdGraphNode& Node) const +{ + if (IsValid(GraphOld) && Node.GetGraph() == GraphOld) + { + return ENodeDiffType::Old; + } + + if (IsValid(GraphNew) && Node.GetGraph() == GraphNew) + { + return ENodeDiffType::New; + } + + return ENodeDiffType::Invalid; +} + +TSharedPtr FFlowGraphToDiff::GetFlowObjectDiff(const FDiffResultItem& DiffResultItem) +{ + const FString DiffKey = GetDiffKeyFromDiffResult(DiffResultItem); + check(FlowObjectDiffsByNodeName.Contains(DiffKey)); + return *FlowObjectDiffsByNodeName.Find(DiffKey); +} + void FFlowGraphToDiff::GenerateTreeEntries(TArray>& OutTreeEntries, TArray>& OutRealDifferences) { if (!DiffListSource.IsEmpty()) @@ -86,14 +198,118 @@ void FFlowGraphToDiff::GenerateTreeEntries(TArray> Children; + for (const TSharedPtr& Difference : DiffListSource) { - TSharedPtr ChildEntry = MakeShared( - FOnDiffEntryFocused::CreateRaw(DiffWidget, &SFlowDiff::OnDiffListSelectionChanged, Difference), - FGenerateDiffEntryWidget::CreateSP(Difference.ToSharedRef(), &FDiffResultItem::GenerateWidget)); - Children.Push(ChildEntry); - OutRealDifferences.Push(ChildEntry); + FString DiffKey = GetDiffKeyFromDiffResult(*Difference.Get()); + const TSharedPtr* FlowNodeDiff = FlowObjectDiffsByNodeName.Find(DiffKey); + + //generate a FlowObjectDiff if not found + if (FlowNodeDiff == nullptr) + { + TSharedPtr GenerateNodeEntry = GenerateFlowObjectDiff(Difference); + FlowNodeDiff = &FlowObjectDiffsByNodeName.Add(DiffKey, GenerateNodeEntry); + } + + //defer generating certain child tree entries, so they can go at the bottom of the list. + if (Difference->Result.Diff == EDiffType::Type::NODE_MOVED + || Difference->Result.Diff == EDiffType::Type::NODE_COMMENT) + { + (*FlowNodeDiff)->LowPriorityChildDiffResult.Add(Difference); + } + + //Use the highest priority category for the parent diff for the purpose of color. + //Note that a lower category is a higher priority one. + //One issue is that every change (even moves or comments) trigger a "Modification" DiffResult, so we need to + //handle that special case separately. Only allow a "Modification" DiffResult to change colors if there is + //actually a property change, or if a sub node was added/removed. + if (Difference->Result.Category != EDiffType::Category::MODIFICATION + && (*FlowNodeDiff)->DiffResult->Result.Category > Difference->Result.Category) + { + (*FlowNodeDiff)->DiffResult->Result.Diff = Difference->Result.Diff; + (*FlowNodeDiff)->DiffResult->Result.Category = Difference->Result.Category; + } + } + + //loop through the generated FlowObjectDiffs to: + //a) add DiffTreeEntries to their correct place in the tree, + //b) generate property diffs, + //c) set DiffType and Category to change colors where appropriate. + for (const auto& Nodes : FlowObjectDiffsByNodeName) + { + TSharedPtr FlowNodeDiff = Nodes.Value; + OutRealDifferences.Push(FlowNodeDiff->DiffTreeEntry); + + UEdGraphNode* Node1 = FlowNodeDiff->DiffResult->Result.Node1; + //not a node diff + if (!IsValid(Node1)) + { + Children.Push(FlowNodeDiff->DiffTreeEntry); + continue; + } + + FlowNodeDiff->ParentNodeDiff = FindParentNode(Cast(Node1)); + if (FlowNodeDiff->ParentNodeDiff.IsValid()) + { + const TSharedPtr ParentNode = FlowNodeDiff->ParentNodeDiff.Pin(); + ParentNode->DiffTreeEntry->Children.Push(FlowNodeDiff->DiffTreeEntry); + + //if a parent has any sub node changes, make sure the color does not stay as "minor" + if (ParentNode->DiffResult->Result.Category > FlowNodeDiff->DiffResult->Result.Category) + { + ParentNode->DiffResult->Result.Diff = EDiffType::Type::NODE_PROPERTY; + ParentNode->DiffResult->Result.Category = EDiffType::Category::MODIFICATION; + } + } + else + { + Children.Push(FlowNodeDiff->DiffTreeEntry); + } + + //generate property diffs. + TArray PropertyDiffsArray; + FlowNodeDiff->DiffProperties(PropertyDiffsArray); + for (const auto& PropertyDiffEntry : PropertyDiffsArray) + { + check(FlowNodeDiff.IsValid()); + TSharedPtr& FlowObjectPropertyDiff = FlowNodeDiff->PropertyDiffArgList.Add_GetRef(MakeShared(FlowNodeDiff, PropertyDiffEntry)); + + TSharedPtr ChildEntry = MakeShared( + FOnDiffEntryFocused::CreateSP(DiffWidget, &SFlowDiff::OnDiffListSelectionChanged, FlowObjectPropertyDiff), + FGenerateDiffEntryWidget::CreateStatic(&GenerateObjectDiffWidget, FlowObjectPropertyDiff->PropertyDiff, RightRevision)); + + Nodes.Value->DiffTreeEntry->Children.Push(ChildEntry); + OutRealDifferences.Push(ChildEntry); + } + + //if a property changed, allow it to change the color of it's parent tree entry. + if (PropertyDiffsArray.Num() > 0) + { + if (FlowNodeDiff->DiffResult->Result.Category > EDiffType::Category::MODIFICATION) + { + FlowNodeDiff->DiffResult->Result.Diff = EDiffType::Type::NODE_PROPERTY; + FlowNodeDiff->DiffResult->Result.Category = EDiffType::Category::MODIFICATION; + } + } + } + + //generate low priority child tree entries. This for loop should go after generating other tree entries, so that + //these lower priority child trees are at the bottom. + for (const auto& Nodes : FlowObjectDiffsByNodeName) + { + const TSharedPtr FlowNodeDiff = Nodes.Value; + + for (const auto& Difference : Nodes.Value->LowPriorityChildDiffResult) + { + TSharedPtr ChildEntry = MakeShared( + FOnDiffEntryFocused::CreateRaw(DiffWidget, &SFlowDiff::OnDiffListSelectionChanged, FlowNodeDiff->DiffEntryFocusArg), + FGenerateDiffEntryWidget::CreateSP(Difference.ToSharedRef(), &FDiffResultItem::GenerateWidget)); + + FlowNodeDiff->DiffTreeEntry->Children.Push(ChildEntry); + OutRealDifferences.Push(ChildEntry); + } } if (Children.Num() == 0) @@ -106,9 +322,74 @@ void FFlowGraphToDiff::GenerateTreeEntries(TArray(AsShared()), ESelectInfo::Direct), FGenerateDiffEntryWidget::CreateSP(AsShared(), &FFlowGraphToDiff::GenerateCategoryWidget), Children); + OutTreeEntries.Push(Entry); } +TSharedPtr FFlowGraphToDiff::GenerateFlowObjectDiff(const TSharedPtr& Difference) +{ + //copy the first diff found in order to enable jumping to the node in the graph. + TSharedPtr DuplicatedFirstFoundDiffResult = MakeShared(Difference->Result); + + ModifyDiffDisplayName(DuplicatedFirstFoundDiffResult->Result, GetNodeNameFromDiffResult(*Difference.Get())); + + TSharedPtr NewFlowObjectDiff = MakeShared(DuplicatedFirstFoundDiffResult, *this); + + static const FSingleObjectDiffEntry InvalidDiff = FSingleObjectDiffEntry(); //do not specify a property change for the main object diff itself. + NewFlowObjectDiff->DiffEntryFocusArg = MakeShared( + NewFlowObjectDiff, + FSingleObjectDiffEntry(InvalidDiff)); + + NewFlowObjectDiff->DiffTreeEntry = MakeShared( + FOnDiffEntryFocused::CreateRaw(DiffWidget, &SFlowDiff::OnDiffListSelectionChanged, NewFlowObjectDiff->DiffEntryFocusArg), + FGenerateDiffEntryWidget::CreateSP(DuplicatedFirstFoundDiffResult.ToSharedRef(), &FDiffResultItem::GenerateWidget)); + + return NewFlowObjectDiff; +} + +TSharedPtr FFlowGraphToDiff::FindParentNode(UFlowGraphNode* Node) +{ + if (!IsValid(Node)) + { + return nullptr; + } + + const UFlowGraphNode* ParentNode = Node->GetParentNode(); + for (auto& FlowNodeDiff : FlowObjectDiffsByNodeName) + { + //don't allow a pin diff be the parent of anything. + if (FlowNodeDiff.Value->DiffResult->Result.Pin1) + { + continue; + } + //if parent node is set, use that. + if (IsValid(ParentNode)) + { + if (FlowNodeDiff.Value->DiffResult->Result.Node1 == ParentNode + || FlowNodeDiff.Value->DiffResult->Result.Node2 == ParentNode) + { + return FlowNodeDiff.Value; + } + } + //if parent node is not set (not set in node removal changes for some reason), + //try to find the parent in the SubNodes of known node changes. + else + { + const UFlowGraphNode* NodeToCheck = Cast(FlowNodeDiff.Value->DiffResult->Result.Node1); + if (IsValid(NodeToCheck)) + { + const int32 Index = NodeToCheck->SubNodes.Find(Node); + if (Index != INDEX_NONE) + { + return FlowNodeDiff.Value; + } + } + } + } + + return nullptr; +} + FText FFlowGraphToDiff::GetToolTip() const { if (GraphOld && GraphNew) diff --git a/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp b/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp new file mode 100644 index 000000000..6845ae064 --- /dev/null +++ b/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp @@ -0,0 +1,87 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Asset/FlowObjectDiff.h" + +#include "Asset/FlowDiffControl.h" +#include "Nodes/FlowNodeBase.h" +#include "EdGraph/EdGraph.h" + +#include "Graph\Nodes\FlowGraphNode.h" +#include "SBlueprintDiff.h" + +#include "DiffResults.h" + +///////////////////////////////////////////////////////////////////////////// +/// FFlowNodePropertyDiff +FFlowObjectDiffArgs::FFlowObjectDiffArgs(TWeakPtr InFlowNodeDiff, const FSingleObjectDiffEntry& InPropertyDiff) + : FlowNodeDiff(InFlowNodeDiff), + PropertyDiff(InPropertyDiff) +{ +} + +///////////////////////////////////////////////////////////////////////////// +/// FFlowObjectDiff +FFlowObjectDiff::FFlowObjectDiff(TSharedPtr InDiffResult, const FFlowGraphToDiff& GraphToDiff) + : DiffResult(InDiffResult) +{ + //ensure we do not generate details panels for pin changes. + if (InDiffResult->Result.Pin1 == nullptr && InDiffResult->Result.Pin2 == nullptr) + { + InitializeDetailsDiffFromNode(InDiffResult->Result.Node1, InDiffResult->Result.Object1, GraphToDiff); + InitializeDetailsDiffFromNode(InDiffResult->Result.Node2, InDiffResult->Result.Object2, GraphToDiff); + } +} + +void FFlowObjectDiff::InitializeDetailsDiffFromNode(UEdGraphNode* Node, const UObject* Object, const FFlowGraphToDiff& GraphToDiff) +{ + if (!IsValid(Node)) + { + return; + } + + if (!IsValid(Object)) + { + const UFlowGraphNode* FlowGraphNode = Cast(Node); + if (IsValid(FlowGraphNode)) + { + Object = FlowGraphNode->GetNodeTemplate(); + } + } + const ENodeDiffType NodeDiffType = GraphToDiff.GetNodeDiffType(*Node); + + if (NodeDiffType == ENodeDiffType::Old && !OldDetailsView.IsValid()) + { + OldDetailsView = MakeShared(Object, FOnDisplayedPropertiesChanged()); + } + else if (NodeDiffType == ENodeDiffType::New && !NewDetailsView.IsValid()) + { + NewDetailsView = MakeShared(Object, FOnDisplayedPropertiesChanged()); + } +} + +void FFlowObjectDiff::DiffProperties(TArray& OutPropertyDiffsArray) const +{ + if (OldDetailsView.IsValid() && NewDetailsView.IsValid()) + { + static constexpr bool bSortByDisplayOrder = true; + OldDetailsView->DiffAgainst(*NewDetailsView.Get(), OutPropertyDiffsArray, bSortByDisplayOrder); + } +} + +void FFlowObjectDiff::OnSelectDiff(const FSingleObjectDiffEntry& Property) const +{ + if (Property.DiffType == EPropertyDiffType::Type::Invalid) + { + return; + } + + if (OldDetailsView.IsValid()) + { + OldDetailsView->HighlightProperty(Property.Identifier); + } + + if (NewDetailsView.IsValid()) + { + NewDetailsView->HighlightProperty(Property.Identifier); + } +} \ No newline at end of file diff --git a/Source/FlowEditor/Private/Asset/SFlowDiff.cpp b/Source/FlowEditor/Private/Asset/SFlowDiff.cpp index cac426b4e..410a3e228 100644 --- a/Source/FlowEditor/Private/Asset/SFlowDiff.cpp +++ b/Source/FlowEditor/Private/Asset/SFlowDiff.cpp @@ -402,11 +402,19 @@ void SFlowDiff::FocusOnGraphRevisions(const FFlowGraphToDiff* Diff) ResetGraphEditors(); } -void SFlowDiff::OnDiffListSelectionChanged(TSharedPtr TheDiff) +void SFlowDiff::OnDiffListSelectionChanged(TSharedPtr FlowObjectDiffArgs) { - check(!TheDiff->Result.OwningObjectPath.IsEmpty()); - FocusOnGraphRevisions(FindGraphToDiffEntry(TheDiff->Result.OwningObjectPath)); - const FDiffSingleResult Result = TheDiff->Result; + const TSharedPtr FlowObjectDiff = FlowObjectDiffArgs->FlowNodeDiff.Pin(); + if (!ensure(FlowObjectDiff.IsValid())) + { + return; + } + + check(!FlowObjectDiff->DiffResult->Result.OwningObjectPath.IsEmpty()); + FocusOnGraphRevisions(FindGraphToDiffEntry(FlowObjectDiff->DiffResult->Result.OwningObjectPath)); + + const TSharedPtr ParentFlowNodeDiff = FlowObjectDiff->ParentNodeDiff.Pin(); + const FDiffSingleResult& Result = ParentFlowNodeDiff.IsValid() ? ParentFlowNodeDiff->DiffResult->Result : FlowObjectDiff->DiffResult->Result; const auto SafeClearSelection = [](TWeakPtr GraphEditor) { @@ -420,6 +428,16 @@ void SFlowDiff::OnDiffListSelectionChanged(TSharedPtr TheDiff) SafeClearSelection(PanelNew.GraphEditor); SafeClearSelection(PanelOld.GraphEditor); + //Select the details panel to display below the graphs. + //Show an empty details panel if there is no generated details panel. + const TSharedPtr OldDetailsPanel = FlowObjectDiff->OldDetailsView.IsValid() ? + FlowObjectDiff->OldDetailsView->DetailsWidget() : PanelOld.EmptyDetailsView.ToSharedRef(); + const TSharedPtr NewDetailsPanel = FlowObjectDiff->NewDetailsView.IsValid() ? + FlowObjectDiff->NewDetailsView->DetailsWidget() : PanelNew.EmptyDetailsView.ToSharedRef(); + + GraphDiffSplitter->SetBottomLeftContent(OldDetailsPanel.ToSharedRef()); + GraphDiffSplitter->SetBottomRightContent(NewDetailsPanel.ToSharedRef()); + if (Result.Pin1) { GetDiffPanelForNode(*Result.Pin1->GetOwningNode()).FocusDiff(*Result.Pin1); @@ -430,6 +448,8 @@ void SFlowDiff::OnDiffListSelectionChanged(TSharedPtr TheDiff) } else if (Result.Node1) { + FlowObjectDiff->OnSelectDiff(FlowObjectDiffArgs->PropertyDiff); + GetDiffPanelForNode(*Result.Node1).FocusDiff(*Result.Node1); if (Result.Node2) { @@ -506,18 +526,12 @@ void FFlowDiffPanel::GeneratePanel(UEdGraph* Graph, TSharedPtr Container) - { - Container->SetObjects(SelectionSet.Array()); - }; - const auto ContextMenuHandler = [](UEdGraph* CurrentGraph, const UEdGraphNode* InGraphNode, const UEdGraphPin* InGraphPin, FMenuBuilder* MenuBuilder, bool bIsDebugging) { MenuBuilder->AddMenuEntry(FGenericCommands::Get().Copy); return FActionMenuContent(MenuBuilder->MakeWidget()); }; - InEvents.OnSelectionChanged = SGraphEditor::FOnSelectionChanged::CreateStatic(SelectionChangedHandler, DetailsView); InEvents.OnCreateNodeOrPinMenu = SGraphEditor::FOnCreateNodeOrPinMenu::CreateStatic(ContextMenuHandler); } @@ -598,14 +612,13 @@ void FFlowDiffPanel::FocusDiff(const UEdGraphNode& Node) const FFlowDiffPanel& SFlowDiff::GetDiffPanelForNode(const UEdGraphNode& Node) { - const TSharedPtr OldGraphEditorPtr = PanelOld.GraphEditor.Pin(); - if (OldGraphEditorPtr.IsValid() && Node.GetGraph() == OldGraphEditorPtr->GetCurrentGraph()) + const ENodeDiffType NodeDiffType = GraphToDiff->GetNodeDiffType(Node); + + if (NodeDiffType == ENodeDiffType::Old) { return PanelOld; } - - const TSharedPtr NewGraphEditorPtr = PanelNew.GraphEditor.Pin(); - if (NewGraphEditorPtr.IsValid() && Node.GetGraph() == NewGraphEditorPtr->GetCurrentGraph()) + if (NodeDiffType == ENodeDiffType::New) { return PanelNew; } @@ -685,14 +698,13 @@ void SFlowDiff::GenerateDifferencesList() return DetailsView; }; - // TODO: construct DetailsView of PanelOld and PanelNew - PanelOld.DetailsView = CreateInspector(PanelOld.FlowAsset); - PanelNew.DetailsView = CreateInspector(PanelOld.FlowAsset); + PanelOld.EmptyDetailsView = CreateInspector(nullptr); + PanelNew.EmptyDetailsView = CreateInspector(nullptr); // Now that we have done the diffs, create the panel widgets ModePanels.Add(DetailsMode, GenerateDetailsPanel()); ModePanels.Add(GraphMode, GenerateGraphPanel()); - + DifferencesTreeView->RebuildList(); } @@ -701,6 +713,9 @@ SFlowDiff::FDiffControl SFlowDiff::GenerateDetailsPanel() const TSharedPtr NewDiffControl = MakeShared(PanelOld.FlowAsset, PanelNew.FlowAsset, FOnDiffEntryFocused::CreateRaw(this, &SFlowDiff::SetCurrentMode, DetailsMode)); NewDiffControl->GenerateTreeEntries(PrimaryDifferencesList, RealDifferences); + SFlowDiff::FDiffControl Ret; + Ret.DiffControl = NewDiffControl; + #if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 3 const TSharedRef Splitter = SNew(SDetailsSplitter); if (PanelOld.FlowAsset) @@ -721,13 +736,10 @@ SFlowDiff::FDiffControl SFlowDiff::GenerateDetailsPanel() .DifferencesWithRightPanel(NewDiffControl.ToSharedRef(), &FFlowAssetDiffControl::GetDifferencesWithRight, Cast(PanelOld.FlowAsset)) ); } -#endif + Ret.Widget = Splitter; - SFlowDiff::FDiffControl Ret; - Ret.Widget = SNullWidget::NullWidget; - Ret.DiffControl = NewDiffControl; +#elif ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 3 -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 3 Ret.Widget = SNew(SSplitter) .PhysicalSplitterHandleSize(10.0f) + SSplitter::Slot() @@ -750,51 +762,21 @@ SFlowDiff::FDiffControl SFlowDiff::GenerateGraphPanel() // We only have a single permanent graph in Flow Asset GraphToDiff = MakeShared(this, PanelOld.FlowAsset->GetGraph(), PanelNew.FlowAsset->GetGraph(), PanelOld.RevisionInfo, PanelNew.RevisionInfo); GraphToDiff->GenerateTreeEntries(PrimaryDifferencesList, RealDifferences); + + SAssignNew(GraphDiffSplitter,SSplitter2x2) + .TopLeft()[ GenerateGraphWidgetForPanel(PanelOld) ] + .TopRight()[ GenerateGraphWidgetForPanel(PanelNew) ] + + .BottomLeft()[ PanelOld.EmptyDetailsView.ToSharedRef() ] + .BottomRight()[ PanelNew.EmptyDetailsView.ToSharedRef() ]; + + static const FVector2D GraphPercentage = {.5f, .7f}; + static const FVector2D DetailsViewPercentage = {.5f, .3f}; + static FVector2D Percentages[] = {GraphPercentage, DetailsViewPercentage, GraphPercentage, DetailsViewPercentage}; + GraphDiffSplitter->SetSplitterPercentages(MakeArrayView(Percentages, UE_ARRAY_COUNT(Percentages))); FDiffControl Ret; - - Ret.Widget = SNew(SVerticalBox) - + SVerticalBox::Slot() - .FillHeight(1.f) - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .FillWidth(1.f) - [ - //diff window - SNew(SSplitter) - .Orientation(Orient_Vertical) - + SSplitter::Slot() - .Value(.8f) - [ - SAssignNew(DiffGraphSplitter, SSplitter) - .PhysicalSplitterHandleSize(10.0f) - .Orientation(bVerticalSplitGraphMode ? Orient_Horizontal : Orient_Vertical) - + SSplitter::Slot() // Old revision graph slot - [ - GenerateGraphWidgetForPanel(PanelOld) - ] - + SSplitter::Slot() // New revision graph slot - [ - GenerateGraphWidgetForPanel(PanelNew) - ] - ] - + SSplitter::Slot() - .Value(.2f) - [ - SNew(SSplitter) - .PhysicalSplitterHandleSize(10.0f) - + SSplitter::Slot() - [ - PanelOld.DetailsView.ToSharedRef() - ] - + SSplitter::Slot() - [ - PanelNew.DetailsView.ToSharedRef() - ] - ] - ] - ]; + Ret.Widget = GraphDiffSplitter; return Ret; } @@ -847,10 +829,6 @@ void SFlowDiff::SetCurrentMode(FName NewMode) if (FoundControl) { - // Reset inspector view - PanelOld.DetailsView->SetObjects(TArray()); - PanelNew.DetailsView->SetObjects(TArray()); - ModeContents->SetContent(FoundControl->Widget.ToSharedRef()); } else diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowActorOwnerComponentRefCustomization.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowActorOwnerComponentRefCustomization.cpp index fa136ba69..e66771085 100644 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowActorOwnerComponentRefCustomization.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowActorOwnerComponentRefCustomization.cpp @@ -6,6 +6,7 @@ #include "FlowAsset.h" #include "AddOns/FlowNodeAddOn.h" #include "FlowActorOwnerComponentFilters.h" +#include "Pre_5_3_GetActorClassDefaultComponents.h" #include "UObject/UnrealType.h" #include "GameFramework/Actor.h" @@ -61,7 +62,12 @@ UClass* FFlowActorOwnerComponentRefCustomization::TryGetExpectedOwnerClass() con TArray FFlowActorOwnerComponentRefCustomization::GetFlowActorOwnerComponents(TSubclassOf ExpectedActorOwnerClass) const { TArray AllComponents; + +#if USE_PRE_5_3_GET_ACTOR_CLASS_DEFAULT_COMPONENTS + Pre_5_3_GetActorClassDefaultComponents(ExpectedActorOwnerClass, AllComponents); +#else AActor::GetActorClassDefaultComponents(ExpectedActorOwnerClass, AllComponents); +#endif // Array for components that pass the metadata filter TArray PassedComponentNames; diff --git a/Source/FlowEditor/Private/DetailCustomizations/Pre_5_3_GetActorClassDefaultComponents.h b/Source/FlowEditor/Private/DetailCustomizations/Pre_5_3_GetActorClassDefaultComponents.h new file mode 100644 index 000000000..07a135740 --- /dev/null +++ b/Source/FlowEditor/Private/DetailCustomizations/Pre_5_3_GetActorClassDefaultComponents.h @@ -0,0 +1,82 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Runtime/Launch/Resources/Version.h" + +// NOTE (gtaylor) Compatibility patch for pre-5.3 UE to support AActor::GetActorClassDefaultComponents() +#define USE_PRE_5_3_GET_ACTOR_CLASS_DEFAULT_COMPONENTS (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 3) + +#if USE_PRE_5_3_GET_ACTOR_CLASS_DEFAULT_COMPONENTS + +#include "Components/ActorComponent.h" +#include "Engine/BlueprintGeneratedClass.h" +#include "Engine/SCS_Node.h" +#include "Engine/SimpleConstructionScript.h" +#include "GameFramework/Actor.h" + +FORCEINLINE_DEBUGGABLE void Pre_5_3_ForEachComponentOfActorClassDefault(const TSubclassOf& ActorClass, const TSubclassOf& InComponentClass, TFunctionRef InFunc) +{ + // Adapted from UE 5.3 AActor::ForEachComponentOfActorClassDefault() + + if (!ActorClass.Get()) + { + return; + } + + auto FilterFunc = [&](const UActorComponent* TemplateComponent) + { + if (!TemplateComponent) + { + return true; + } + + if (!InComponentClass.Get() || TemplateComponent->IsA(InComponentClass)) + { + return InFunc(TemplateComponent); + } + + return true; + }; + + // Process native components + const AActor* CDO = ActorClass->GetDefaultObject(); + for (const UActorComponent* Component : CDO->GetComponents()) + { + if (!FilterFunc(Component)) + { + return; + } + } + + // Process blueprint components + if (UBlueprintGeneratedClass* ActorBlueprintGeneratedClass = Cast(ActorClass)) + { + UBlueprintGeneratedClass::ForEachGeneratedClassInHierarchy(ActorClass, [&](const UBlueprintGeneratedClass* CurrentBPGC) + { + if (const USimpleConstructionScript* const ConstructionScript = CurrentBPGC->SimpleConstructionScript) + { + // Gets all BP added components + for (const USCS_Node* const Node : ConstructionScript->GetAllNodes()) + { + if (!FilterFunc(Node->GetActualComponentTemplate(ActorBlueprintGeneratedClass))) + { + return false; + } + } + } + return true; + }); + } +} + +template ::IsDerived>::Type> +static void Pre_5_3_GetActorClassDefaultComponents(const TSubclassOf& InActorClass, TArray& OutComponents) +{ + // Adapted from UE 5.3 AActor::GetActorClassDefaultComponents() + + Pre_5_3_ForEachComponentOfActorClassDefault(InActorClass, TComponentClass::StaticClass(), [&](const UActorComponent* TemplateComponent) + { + OutComponents.Add(CastChecked(TemplateComponent)); + return true; + }); +} +#endif diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index 92a7a0af7..be5791331 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -12,6 +12,7 @@ #include "Graph/Nodes/FlowGraphNode.h" #include "Nodes/Route/FlowNode_SubGraph.h" +#include "Framework/Application/SlateApplication.h" #include "Framework/Commands/GenericCommands.h" #include "HAL/PlatformApplicationMisc.h" #include "IDetailsView.h" diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 4d26d8d05..aaba9e1d3 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -1355,6 +1355,8 @@ void UFlowGraphNode::FindDiffs(UEdGraphNode* OtherNode, FDiffResults& Results) Diff.Diff = EDiffType::NODE_PROPERTY; Diff.Node1 = this; Diff.Node2 = OtherNode; + Diff.Object1 = NodeInstance; + Diff.Object2 = OtherGraphNode->NodeInstance; Diff.ToolTip = LOCTEXT("DIF_NodeInstancePropertyToolTip", "A property of the node instance has changed"); Diff.Category = EDiffType::MODIFICATION; diff --git a/Source/FlowEditor/Public/Asset/FlowDiffControl.h b/Source/FlowEditor/Public/Asset/FlowDiffControl.h index 05fecb39e..4f5a3a2b2 100644 --- a/Source/FlowEditor/Public/Asset/FlowDiffControl.h +++ b/Source/FlowEditor/Public/Asset/FlowDiffControl.h @@ -2,17 +2,19 @@ #pragma once +#include "Asset/FlowObjectDiff.h" #include "DiffResults.h" #include "IAssetTypeActions.h" #include "Editor/Kismet/Private/DiffControl.h" #include "Runtime/Launch/Resources/Version.h" -struct FDiffResultItem; +class FBlueprintDifferenceTreeEntry; +class SFlowDiff; class UEdGraph; -struct FEdGraphEditAction; - +class UEdGraphNode; class UFlowAsset; -class SFlowDiff; +struct FDiffResultItem; +struct FEdGraphEditAction; ///////////////////////////////////////////////////////////////////////////// /// FFlowAssetDiffControl @@ -32,7 +34,7 @@ class FLOWEDITOR_API FFlowAssetDiffControl : public FDetailsDiffControl /// FFlowGraphToDiff: engine's FGraphToDiff customized to Flow Graph struct FLOWEDITOR_API FFlowGraphToDiff : public TSharedFromThis, IDiffControl { - FFlowGraphToDiff(class SFlowDiff* DiffWidget, UEdGraph* GraphOld, UEdGraph* GraphNew, const FRevisionInfo& RevisionOld, const FRevisionInfo& RevisionNew); + FFlowGraphToDiff(SFlowDiff* DiffWidget, UEdGraph* GraphOld, UEdGraph* GraphNew, const FRevisionInfo& RevisionOld, const FRevisionInfo& RevisionNew); virtual ~FFlowGraphToDiff() override; /** Add widgets to the differences tree */ @@ -41,6 +43,10 @@ struct FLOWEDITOR_API FFlowGraphToDiff : public TSharedFromThis GetFlowObjectDiff(const FDiffResultItem& DiffResultItem); + /** Source for list view */ TArray> DiffListSource; TSharedPtr> FoundDiffs; @@ -57,7 +63,13 @@ struct FLOWEDITOR_API FFlowGraphToDiff : public TSharedFromThis GenerateFlowObjectDiff(const TSharedPtr& Differences); + + TSharedPtr FindParentNode(class UFlowGraphNode* Node); + + TMap> FlowObjectDiffsByNodeName; + + SFlowDiff* DiffWidget; UEdGraph* GraphOld; UEdGraph* GraphNew; @@ -66,4 +78,5 @@ struct FLOWEDITOR_API FFlowGraphToDiff : public TSharedFromThis InFlowNodeDiff, const FSingleObjectDiffEntry& InPropertyDiff); + + TWeakPtr FlowNodeDiff; + FSingleObjectDiffEntry PropertyDiff; +}; + +///////////////////////////////////////////////////////////////////////////// +/// FFlowObjectDiff: represents diff data for a particular node or pin. +class FLOWEDITOR_API FFlowObjectDiff : public TSharedFromThis +{ +public: + FFlowObjectDiff(TSharedPtr InDiffResult, const FFlowGraphToDiff& GraphToDiff); + + void OnSelectDiff(const FSingleObjectDiffEntry& Property) const; + + void DiffProperties(TArray& OutPropertyDiffsArray) const; + +private: + void InitializeDetailsDiffFromNode(UEdGraphNode* Node, const UObject* Object, const FFlowGraphToDiff& GraphToDiff); + +public: + TSharedPtr DiffTreeEntry; + TSharedPtr DiffResult; + + //parent of this diff. Certain nodes like Add-Ons are displayed inside the DiffTreeEntry for the node they're attached to. + TWeakPtr ParentNodeDiff; + + TSharedPtr OldDetailsView; + TSharedPtr NewDetailsView; + + //arguments used for FOnDiffEntryFocused. + TSharedPtr DiffEntryFocusArg; + TArray> PropertyDiffArgList; + + //a list for deferring creation of DiffTreeEntries that are lower priority. + TArray> LowPriorityChildDiffResult; +}; diff --git a/Source/FlowEditor/Public/Asset/SFlowDiff.h b/Source/FlowEditor/Public/Asset/SFlowDiff.h index b9ac9e6d9..d64093aa8 100644 --- a/Source/FlowEditor/Public/Asset/SFlowDiff.h +++ b/Source/FlowEditor/Public/Asset/SFlowDiff.h @@ -8,6 +8,9 @@ #include "Textures/SlateIcon.h" struct FFlowGraphToDiff; +struct FFlowObjectDiffArgs; +class IDetailsView; +class SSplitter2x2; class UFlowAsset; enum class EAssetEditorCloseReason : uint8; @@ -50,8 +53,9 @@ struct FLOWEDITOR_API FFlowDiffPanel /** The box around the graph editor, used to change the content when new graphs are set */ TSharedPtr GraphEditorBox; - /** The details view associated with the graph editor */ - TSharedPtr DetailsView; + /** using SNullWidget::NullNullWidget can only work for a single widget, since widget instances can only be + * used one at a time. EmptyDetailsView is used for displaying an empty details panel instead. */ + TSharedPtr EmptyDetailsView; /** The graph editor which does the work of displaying the graph */ TWeakPtr GraphEditor; @@ -97,7 +101,7 @@ class FLOWEDITOR_API SFlowDiff : public SCompoundWidget void OnGraphSelectionChanged(const TSharedPtr Item, ESelectInfo::Type SelectionType); /** Called when user clicks on an entry in the listview of differences */ - void OnDiffListSelectionChanged(TSharedPtr TheDiff); + void OnDiffListSelectionChanged(TSharedPtr FlowObjectDiffArgs); /** Helper function for generating an empty widget */ static TSharedRef DefaultEmptyPanel(); @@ -215,5 +219,7 @@ class FLOWEDITOR_API SFlowDiff : public SCompoundWidget /** A pointer to the window holding this */ TWeakPtr WeakParentWindow; + TSharedPtr GraphDiffSplitter = nullptr; + FDelegateHandle AssetEditorCloseDelegate; }; From e143fcd1cd31149eb507da7290c07b95c88a5d41 Mon Sep 17 00:00:00 2001 From: FoolsTheoryDev Date: Thu, 20 Jun 2024 19:09:25 +0200 Subject: [PATCH 248/485] removed UE 5.2 compilation attempt as it would require more work We assume the next Flow release would support last 3 engine versions: UE 5.3, UE 5.4, UE 5.5 --- .../Types/FlowInjectComponentsManager.h | 1 - ...lowActorOwnerComponentRefCustomization.cpp | 9 +- .../Pre_5_3_GetActorClassDefaultComponents.h | 82 ------------------- 3 files changed, 2 insertions(+), 90 deletions(-) delete mode 100644 Source/FlowEditor/Private/DetailCustomizations/Pre_5_3_GetActorClassDefaultComponents.h diff --git a/Source/Flow/Public/Types/FlowInjectComponentsManager.h b/Source/Flow/Public/Types/FlowInjectComponentsManager.h index 215306555..da9d658e5 100644 --- a/Source/Flow/Public/Types/FlowInjectComponentsManager.h +++ b/Source/Flow/Public/Types/FlowInjectComponentsManager.h @@ -6,7 +6,6 @@ #include "FlowInjectComponentsManager.generated.h" -class AActor; class UActorComponent; class UFlowNodeBase; diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowActorOwnerComponentRefCustomization.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowActorOwnerComponentRefCustomization.cpp index e66771085..2a79b67f8 100644 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowActorOwnerComponentRefCustomization.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowActorOwnerComponentRefCustomization.cpp @@ -2,11 +2,10 @@ #include "DetailCustomizations/FlowActorOwnerComponentRefCustomization.h" -#include "Nodes/FlowNode.h" -#include "FlowAsset.h" #include "AddOns/FlowNodeAddOn.h" +#include "FlowAsset.h" #include "FlowActorOwnerComponentFilters.h" -#include "Pre_5_3_GetActorClassDefaultComponents.h" +#include "Nodes/FlowNode.h" #include "UObject/UnrealType.h" #include "GameFramework/Actor.h" @@ -63,11 +62,7 @@ TArray FFlowActorOwnerComponentRefCustomization::GetFlowActorOwnerCompone { TArray AllComponents; -#if USE_PRE_5_3_GET_ACTOR_CLASS_DEFAULT_COMPONENTS - Pre_5_3_GetActorClassDefaultComponents(ExpectedActorOwnerClass, AllComponents); -#else AActor::GetActorClassDefaultComponents(ExpectedActorOwnerClass, AllComponents); -#endif // Array for components that pass the metadata filter TArray PassedComponentNames; diff --git a/Source/FlowEditor/Private/DetailCustomizations/Pre_5_3_GetActorClassDefaultComponents.h b/Source/FlowEditor/Private/DetailCustomizations/Pre_5_3_GetActorClassDefaultComponents.h deleted file mode 100644 index 07a135740..000000000 --- a/Source/FlowEditor/Private/DetailCustomizations/Pre_5_3_GetActorClassDefaultComponents.h +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#include "Runtime/Launch/Resources/Version.h" - -// NOTE (gtaylor) Compatibility patch for pre-5.3 UE to support AActor::GetActorClassDefaultComponents() -#define USE_PRE_5_3_GET_ACTOR_CLASS_DEFAULT_COMPONENTS (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 3) - -#if USE_PRE_5_3_GET_ACTOR_CLASS_DEFAULT_COMPONENTS - -#include "Components/ActorComponent.h" -#include "Engine/BlueprintGeneratedClass.h" -#include "Engine/SCS_Node.h" -#include "Engine/SimpleConstructionScript.h" -#include "GameFramework/Actor.h" - -FORCEINLINE_DEBUGGABLE void Pre_5_3_ForEachComponentOfActorClassDefault(const TSubclassOf& ActorClass, const TSubclassOf& InComponentClass, TFunctionRef InFunc) -{ - // Adapted from UE 5.3 AActor::ForEachComponentOfActorClassDefault() - - if (!ActorClass.Get()) - { - return; - } - - auto FilterFunc = [&](const UActorComponent* TemplateComponent) - { - if (!TemplateComponent) - { - return true; - } - - if (!InComponentClass.Get() || TemplateComponent->IsA(InComponentClass)) - { - return InFunc(TemplateComponent); - } - - return true; - }; - - // Process native components - const AActor* CDO = ActorClass->GetDefaultObject(); - for (const UActorComponent* Component : CDO->GetComponents()) - { - if (!FilterFunc(Component)) - { - return; - } - } - - // Process blueprint components - if (UBlueprintGeneratedClass* ActorBlueprintGeneratedClass = Cast(ActorClass)) - { - UBlueprintGeneratedClass::ForEachGeneratedClassInHierarchy(ActorClass, [&](const UBlueprintGeneratedClass* CurrentBPGC) - { - if (const USimpleConstructionScript* const ConstructionScript = CurrentBPGC->SimpleConstructionScript) - { - // Gets all BP added components - for (const USCS_Node* const Node : ConstructionScript->GetAllNodes()) - { - if (!FilterFunc(Node->GetActualComponentTemplate(ActorBlueprintGeneratedClass))) - { - return false; - } - } - } - return true; - }); - } -} - -template ::IsDerived>::Type> -static void Pre_5_3_GetActorClassDefaultComponents(const TSubclassOf& InActorClass, TArray& OutComponents) -{ - // Adapted from UE 5.3 AActor::GetActorClassDefaultComponents() - - Pre_5_3_ForEachComponentOfActorClassDefault(InActorClass, TComponentClass::StaticClass(), [&](const UActorComponent* TemplateComponent) - { - OutComponents.Add(CastChecked(TemplateComponent)); - return true; - }); -} -#endif From 74ae66457bdf83210f87d2fef50873dba0378745 Mon Sep 17 00:00:00 2001 From: LindyHopperGT <91915878+LindyHopperGT@users.noreply.github.com> Date: Sat, 22 Jun 2024 13:03:29 -0700 Subject: [PATCH 249/485] Fix Modify() for SetConfigText (#211) --- Source/Flow/Private/Nodes/FlowNodeBase.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp index 7385d2dce..18f922dca 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBase.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -576,9 +576,9 @@ void UFlowNodeBase::SetNodeConfigText(const FText& NodeConfigText) #if WITH_EDITOR if (!NodeConfigText.EqualTo(DevNodeConfigText)) { - DevNodeConfigText = NodeConfigText; - Modify(); + + DevNodeConfigText = NodeConfigText; } #endif // WITH_EDITOR } From 50f592dccb9e4da2d6301a8cac70542349c48e80 Mon Sep 17 00:00:00 2001 From: Soraphis Date: Sat, 29 Jun 2024 14:02:29 +0200 Subject: [PATCH 250/485] fix[PlayLevelSequence Node]: Prevent multiple output pins with same name (#212) e.g. adding multiple notifies to a sequence named "Pause" would previously add multiple output pins. --- Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp index d646acaae..f869a6ff3 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp @@ -81,7 +81,7 @@ TArray UFlowNode_PlayLevelSequence::GetContextOutputs() const { for (const FString& EventName : FlowSection->GetAllEntryPoints()) { - if (!EventName.IsEmpty()) + if (!EventName.IsEmpty() && !Pins.Contains(EventName)) { Pins.Emplace(EventName); } From 8b4d32d6e4677acd12a61a034c9653c1abcc1cb4 Mon Sep 17 00:00:00 2001 From: MaksymKapelianovych <48297221+MaksymKapelianovych@users.noreply.github.com> Date: Sat, 29 Jun 2024 15:19:37 +0300 Subject: [PATCH 251/485] Add a bunch of const keywords to allow usage of LogError/LogWarning/LogNote in const functions without const_cast (#207) * Add a bunch of const keywords to allow usage of LogError/LogWarning/LogNote in const functions * Add const to function parameter * Small fixes and clean up after merge * Removed unnecessary const_casts and made UFlowGraphNode::LogError const --- Source/Flow/Private/FlowAsset.cpp | 8 +++---- Source/Flow/Private/FlowMessageLog.cpp | 4 ++-- Source/Flow/Private/Nodes/FlowNodeBase.cpp | 23 ++++--------------- Source/Flow/Public/FlowAsset.h | 10 ++++---- Source/Flow/Public/FlowMessageLog.h | 6 ++--- Source/Flow/Public/Nodes/FlowNodeBase.h | 15 +++--------- .../Private/Asset/FlowDebuggerSubsystem.cpp | 2 +- .../Private/Graph/Nodes/FlowGraphNode.cpp | 4 ++-- .../Public/Asset/FlowDebuggerSubsystem.h | 2 +- .../Public/Graph/Nodes/FlowGraphNode.h | 2 +- 10 files changed, 26 insertions(+), 50 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index d68f4c1ab..4e3805994 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -590,7 +590,7 @@ void UFlowAsset::BroadcastDebuggerRefresh() const RefreshDebuggerEvent.Broadcast(); } -void UFlowAsset::BroadcastRuntimeMessageAdded(const TSharedRef& Message) +void UFlowAsset::BroadcastRuntimeMessageAdded(const TSharedRef& Message) const { RuntimeMessageEvent.Broadcast(this, Message); } @@ -907,7 +907,7 @@ bool UFlowAsset::IsBoundToWorld_Implementation() } #if WITH_EDITOR -void UFlowAsset::LogError(const FString& MessageToLog, UFlowNodeBase* Node) +void UFlowAsset::LogError(const FString& MessageToLog, const UFlowNodeBase* Node) const { // this is runtime log which is should be only called on runtime instances of asset if (TemplateAsset == nullptr) @@ -922,7 +922,7 @@ void UFlowAsset::LogError(const FString& MessageToLog, UFlowNodeBase* Node) } } -void UFlowAsset::LogWarning(const FString& MessageToLog, UFlowNodeBase* Node) +void UFlowAsset::LogWarning(const FString& MessageToLog, const UFlowNodeBase* Node) const { // this is runtime log which is should be only called on runtime instances of asset if (TemplateAsset == nullptr) @@ -937,7 +937,7 @@ void UFlowAsset::LogWarning(const FString& MessageToLog, UFlowNodeBase* Node) } } -void UFlowAsset::LogNote(const FString& MessageToLog, UFlowNodeBase* Node) +void UFlowAsset::LogNote(const FString& MessageToLog, const UFlowNodeBase* Node) const { // this is runtime log which is should be only called on runtime instances of asset if (TemplateAsset == nullptr) diff --git a/Source/Flow/Private/FlowMessageLog.cpp b/Source/Flow/Private/FlowMessageLog.cpp index 48620b2da..39c1d2997 100644 --- a/Source/Flow/Private/FlowMessageLog.cpp +++ b/Source/Flow/Private/FlowMessageLog.cpp @@ -21,7 +21,7 @@ FFlowGraphToken::FFlowGraphToken(const UFlowNodeBase* InFlowNodeBase) CachedText = InFlowNodeBase->GetNodeTitle(); } -FFlowGraphToken::FFlowGraphToken(UEdGraphNode* InGraphNode, const UEdGraphPin* InPin) +FFlowGraphToken::FFlowGraphToken(const UEdGraphNode* InGraphNode, const UEdGraphPin* InPin) : GraphNode(InGraphNode) , GraphPin(InPin) { @@ -61,7 +61,7 @@ TSharedPtr FFlowGraphToken::Create(const UFlowNodeBase* InFlowNod return nullptr; } -TSharedPtr FFlowGraphToken::Create(UEdGraphNode* InGraphNode, FTokenizedMessage& Message) +TSharedPtr FFlowGraphToken::Create(const UEdGraphNode* InGraphNode, FTokenizedMessage& Message) { if (InGraphNode) { diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp index 18f922dca..eec9f13e3 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBase.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -390,7 +390,7 @@ EFlowAddOnAcceptResult UFlowNodeBase::CheckAcceptFlowNodeAddOnChild(const UFlowN AddOnTemplate->IsA()) { const FString Message = FString::Printf(TEXT("%s::AcceptFlowNodeAddOnParent must always Reject for UFlowNode subclasses"), *GetClass()->GetName()); - GetFlowAsset()->GetTemplateAsset()->LogError(Message, const_cast(this)); + GetFlowAsset()->GetTemplateAsset()->LogError(Message, this); return EFlowAddOnAcceptResult::Reject; } @@ -594,7 +594,7 @@ FString UFlowNodeBase::GetNodeDescription() const } #endif // WITH_EDITOR -void UFlowNodeBase::LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType) +void UFlowNodeBase::LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType) const { #if !UE_BUILD_SHIPPING if (BuildMessage(Message)) @@ -632,12 +632,7 @@ void UFlowNodeBase::LogError(FString Message, const EFlowOnScreenMessageType OnS #endif } -void UFlowNodeBase::LogErrorConst(FString Message, const EFlowOnScreenMessageType OnScreenMessageType) const -{ - const_cast(this)->LogError(Message, OnScreenMessageType); -} - -void UFlowNodeBase::LogWarning(FString Message) +void UFlowNodeBase::LogWarning(FString Message) const { #if !UE_BUILD_SHIPPING if (BuildMessage(Message)) @@ -653,12 +648,7 @@ void UFlowNodeBase::LogWarning(FString Message) #endif } -void UFlowNodeBase::LogWarningConst(FString Message) const -{ - const_cast(this)->LogWarning(Message); -} - -void UFlowNodeBase::LogNote(FString Message) +void UFlowNodeBase::LogNote(FString Message) const { #if !UE_BUILD_SHIPPING if (BuildMessage(Message)) @@ -674,11 +664,6 @@ void UFlowNodeBase::LogNote(FString Message) #endif } -void UFlowNodeBase::LogNoteConst(FString Message) const -{ - const_cast(this)->LogNote(Message); -} - #if !UE_BUILD_SHIPPING bool UFlowNodeBase::BuildMessage(FString& Message) const { diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 91cb4b902..3216955eb 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -264,14 +264,14 @@ class FLOW_API UFlowAsset : public UObject FRefreshDebuggerEvent& OnDebuggerRefresh() { return RefreshDebuggerEvent; } FRefreshDebuggerEvent RefreshDebuggerEvent; - DECLARE_EVENT_TwoParams(UFlowAsset, FRuntimeMessageEvent, UFlowAsset*, const TSharedRef&); + DECLARE_EVENT_TwoParams(UFlowAsset, FRuntimeMessageEvent, const UFlowAsset*, const TSharedRef&); FRuntimeMessageEvent& OnRuntimeMessageAdded() { return RuntimeMessageEvent; } FRuntimeMessageEvent RuntimeMessageEvent; private: void BroadcastDebuggerRefresh() const; - void BroadcastRuntimeMessageAdded(const TSharedRef& Message); + void BroadcastRuntimeMessageAdded(const TSharedRef& Message) const; #endif ////////////////////////////////////////////////////////////////////////// @@ -412,8 +412,8 @@ class FLOW_API UFlowAsset : public UObject #if WITH_EDITOR public: - void LogError(const FString& MessageToLog, UFlowNodeBase* Node); - void LogWarning(const FString& MessageToLog, UFlowNodeBase* Node); - void LogNote(const FString& MessageToLog, UFlowNodeBase* Node); + void LogError(const FString& MessageToLog, const UFlowNodeBase* Node) const; + void LogWarning(const FString& MessageToLog, const UFlowNodeBase* Node) const; + void LogNote(const FString& MessageToLog, const UFlowNodeBase* Node) const; #endif }; diff --git a/Source/Flow/Public/FlowMessageLog.h b/Source/Flow/Public/FlowMessageLog.h index d09a5a398..4a5a0542f 100644 --- a/Source/Flow/Public/FlowMessageLog.h +++ b/Source/Flow/Public/FlowMessageLog.h @@ -17,18 +17,18 @@ class UFlowNodeBase; class FLOW_API FFlowGraphToken : public IMessageToken { private: - const TWeakObjectPtr GraphNode; + const TWeakObjectPtr GraphNode; const FEdGraphPinReference GraphPin; explicit FFlowGraphToken(const UFlowAsset* InFlowAsset); explicit FFlowGraphToken(const UFlowNodeBase* InFlowNodeBase); - explicit FFlowGraphToken(UEdGraphNode* InGraphNode, const UEdGraphPin* InPin); + explicit FFlowGraphToken(const UEdGraphNode* InGraphNode, const UEdGraphPin* InPin); public: /** Factory method, tokens can only be constructed as shared refs */ static TSharedPtr Create(const UFlowAsset* InFlowAsset, FTokenizedMessage& Message); static TSharedPtr Create(const UFlowNodeBase* InFlowNodeBase, FTokenizedMessage& Message); - static TSharedPtr Create(UEdGraphNode* InGraphNode, FTokenizedMessage& Message); + static TSharedPtr Create(const UEdGraphNode* InGraphNode, FTokenizedMessage& Message); static TSharedPtr Create(const UEdGraphPin* InPin, FTokenizedMessage& Message); const UEdGraphNode* GetGraphNode() const { return GraphNode.Get(); } diff --git a/Source/Flow/Public/Nodes/FlowNodeBase.h b/Source/Flow/Public/Nodes/FlowNodeBase.h index 195ca7af8..35795ad17 100644 --- a/Source/Flow/Public/Nodes/FlowNodeBase.h +++ b/Source/Flow/Public/Nodes/FlowNodeBase.h @@ -272,22 +272,13 @@ class FLOW_API UFlowNodeBase FString K2_GetNodeDescription() const; UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) - void LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType = EFlowOnScreenMessageType::Permanent); - - // LogError from constant function (allowing 'this' to be modified only to log the error itself) - void LogErrorConst(FString Message, const EFlowOnScreenMessageType OnScreenMessageType = EFlowOnScreenMessageType::Permanent) const; + void LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType = EFlowOnScreenMessageType::Permanent) const; UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) - void LogWarning(FString Message); - - // LogWarning from constant function (allowing 'this' to be modified only to log the warning itself) - void LogWarningConst(FString Message) const; + void LogWarning(FString Message) const; UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) - void LogNote(FString Message); - - // LogNote from constant function (allowing 'this' to be modified only to log the note itself) - void LogNoteConst(FString Message) const; + void LogNote(FString Message) const; #if !UE_BUILD_SHIPPING protected: diff --git a/Source/FlowEditor/Private/Asset/FlowDebuggerSubsystem.cpp b/Source/FlowEditor/Private/Asset/FlowDebuggerSubsystem.cpp index 94715f0ab..ee26f51d1 100644 --- a/Source/FlowEditor/Private/Asset/FlowDebuggerSubsystem.cpp +++ b/Source/FlowEditor/Private/Asset/FlowDebuggerSubsystem.cpp @@ -41,7 +41,7 @@ void UFlowDebuggerSubsystem::OnInstancedTemplateRemoved(UFlowAsset* FlowAsset) c FlowAsset->OnRuntimeMessageAdded().RemoveAll(this); } -void UFlowDebuggerSubsystem::OnRuntimeMessageAdded(UFlowAsset* FlowAsset, const TSharedRef& Message) const +void UFlowDebuggerSubsystem::OnRuntimeMessageAdded(const UFlowAsset* FlowAsset, const TSharedRef& Message) const { const TSharedPtr Log = RuntimeLogs.FindRef(FlowAsset); if (Log.IsValid()) diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index aaba9e1d3..7def38991 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -1176,13 +1176,13 @@ void UFlowGraphNode::PostEditUndo() } } -void UFlowGraphNode::LogError(const FString& MessageToLog, const UFlowNodeBase* FlowNodeBase) +void UFlowGraphNode::LogError(const FString& MessageToLog, const UFlowNodeBase* FlowNodeBase) const { if (UFlowGraph* FlowGraph = GetFlowGraph()) { if (UFlowAsset* FlowAsset = FlowGraph->GetFlowAsset()) { - FlowAsset->LogError(MessageToLog, const_cast(FlowNodeBase)); + FlowAsset->LogError(MessageToLog, FlowNodeBase); } } } diff --git a/Source/FlowEditor/Public/Asset/FlowDebuggerSubsystem.h b/Source/FlowEditor/Public/Asset/FlowDebuggerSubsystem.h index 0200ba010..391bc876f 100644 --- a/Source/FlowEditor/Public/Asset/FlowDebuggerSubsystem.h +++ b/Source/FlowEditor/Public/Asset/FlowDebuggerSubsystem.h @@ -26,7 +26,7 @@ class FLOWEDITOR_API UFlowDebuggerSubsystem : public UEditorSubsystem void OnInstancedTemplateAdded(UFlowAsset* FlowAsset); void OnInstancedTemplateRemoved(UFlowAsset* FlowAsset) const; - void OnRuntimeMessageAdded(UFlowAsset* FlowAsset, const TSharedRef& Message) const; + void OnRuntimeMessageAdded(const UFlowAsset* FlowAsset, const TSharedRef& Message) const; void OnBeginPIE(const bool bIsSimulating); void OnEndPIE(const bool bIsSimulating); diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h index 8df6adcea..5999a6382 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h @@ -281,7 +281,7 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode virtual void ResetNodeOwner(); - void LogError(const FString& MessageToLog, const UFlowNodeBase* FlowNodeBase); + void LogError(const FString& MessageToLog, const UFlowNodeBase* FlowNodeBase) const; public: From 1af1b9215914292d2838bb3274714b2cdcc9a617 Mon Sep 17 00:00:00 2001 From: MaksymKapelianovych <48297221+MaksymKapelianovych@users.noreply.github.com> Date: Wed, 3 Jul 2024 19:53:45 +0300 Subject: [PATCH 252/485] Node title text changes (#193) * Node title text changes: 1. Editing UFlowGraphEditorSettings::bShowNodeClass now immediately updates node titles in graph. 2. Editing UFlowSettings::bUseAdaptiveNodeTitles now immediately updates node titles in graph. 3. UFlowGraphNode::GetTooltipText() now calls UFlowNode::GetNodeToolTip() 4. Refresh graph only once, when node asset is renamed 5. User can now specify node prefixes that will be automatically removed, instead of manually writing custom meta = (DisplayName = ...) * Small optimization for node titles generation: regenerate title only for changing asset. Regenerate titles for every node only after editing NodePrefixesToRemove array or performing GatherNodes() * Adapted code for AddOns and fixed some issues after merge --- Source/Flow/Private/FlowSettings.cpp | 12 ++ Source/Flow/Private/Nodes/FlowNodeBase.cpp | 37 ++++++ Source/Flow/Public/FlowSettings.h | 9 ++ Source/Flow/Public/Nodes/FlowNodeBase.h | 4 + .../Private/Graph/FlowGraphEditorSettings.cpp | 13 ++ .../Private/Graph/FlowGraphSchema.cpp | 111 +++++++++++++++++- .../Private/Graph/FlowGraphSettings.cpp | 59 ++++++++++ .../Private/Graph/FlowGraphUtils.cpp | 19 +++ .../Private/Graph/Nodes/FlowGraphNode.cpp | 2 +- .../Public/Graph/FlowGraphEditorSettings.h | 4 + .../FlowEditor/Public/Graph/FlowGraphSchema.h | 11 ++ .../Public/Graph/FlowGraphSettings.h | 11 ++ .../FlowEditor/Public/Graph/FlowGraphUtils.h | 2 + 13 files changed, 292 insertions(+), 2 deletions(-) diff --git a/Source/Flow/Private/FlowSettings.cpp b/Source/Flow/Private/FlowSettings.cpp index 9463b94be..50bbcbb13 100644 --- a/Source/Flow/Private/FlowSettings.cpp +++ b/Source/Flow/Private/FlowSettings.cpp @@ -16,6 +16,18 @@ UFlowSettings::UFlowSettings(const FObjectInitializer& ObjectInitializer) { } +#if WITH_EDITOR +void UFlowSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + if (PropertyChangedEvent.GetMemberPropertyName() == GET_MEMBER_NAME_CHECKED( UFlowSettings, bUseAdaptiveNodeTitles )) + { + (void)OnAdaptiveNodeTitlesChanged.ExecuteIfBound(); + } +} +#endif + UClass* UFlowSettings::GetDefaultExpectedOwnerClass() const { return CastChecked(TryResolveOrLoadSoftClass(DefaultExpectedOwnerClass), ECastCheckedType::NullAllowed); diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp index eec9f13e3..48c34a175 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBase.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -30,6 +30,7 @@ UFlowNodeBase::UFlowNodeBase(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , GraphNode(nullptr) #if WITH_EDITORONLY_DATA + , bDisplayNodeTitleWithoutPrefix(true) , bCanDelete(true) , bCanDuplicate(true) , bNodeDeprecated(false) @@ -548,6 +549,12 @@ FText UFlowNodeBase::GetNodeTitle() const } } + static const FName NAME_DisplayName(TEXT("DisplayName")); + if (bDisplayNodeTitleWithoutPrefix && !GetClass()->HasMetaData(NAME_DisplayName)) + { + return GetGeneratedDisplayName(); + } + return GetClass()->GetDisplayNameText(); } @@ -562,6 +569,23 @@ FText UFlowNodeBase::GetNodeToolTip() const } } + static const FName NAME_Tooltip(TEXT("Tooltip")); + if (bDisplayNodeTitleWithoutPrefix && !GetClass()->HasMetaData(NAME_Tooltip)) + { + return GetGeneratedDisplayName(); + } + + // GetClass()->GetToolTipText() can return meta = (DisplayName = ... ), but ignore BlueprintDisplayName even if it is BP Node + if (GetClass()->ClassGeneratedBy) + { + const FString& BlueprintTitle = Cast(GetClass()->ClassGeneratedBy)->BlueprintDisplayName; + if (!BlueprintTitle.IsEmpty()) + { + return FText::FromString(BlueprintTitle); + } + } + + return GetClass()->GetToolTipText(); } @@ -569,6 +593,19 @@ FText UFlowNodeBase::GetNodeConfigText() const { return DevNodeConfigText; } + +FText UFlowNodeBase::GetGeneratedDisplayName() const +{ + static const FName NAME_GeneratedDisplayName(TEXT("GeneratedDisplayName")); + + if (GetClass()->ClassGeneratedBy) + { + UClass* Class = Cast(GetClass()->ClassGeneratedBy)->GeneratedClass; + return Class->GetMetaDataText(NAME_GeneratedDisplayName); + } + + return GetClass()->GetMetaDataText(NAME_GeneratedDisplayName); +} #endif // WITH_EDITOR void UFlowNodeBase::SetNodeConfigText(const FText& NodeConfigText) diff --git a/Source/Flow/Public/FlowSettings.h b/Source/Flow/Public/FlowSettings.h index 7760ce6f2..9dbe4d63a 100644 --- a/Source/Flow/Public/FlowSettings.h +++ b/Source/Flow/Public/FlowSettings.h @@ -19,6 +19,10 @@ class FLOW_API UFlowSettings : public UDeveloperSettings static UFlowSettings* Get() { return CastChecked(UFlowSettings::StaticClass()->GetDefaultObject()); } +#if WITH_EDITOR + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; +#endif + // Set if to False, if you don't want to create client-side Flow Graphs // And you don't access to the Flow Component registry on clients UPROPERTY(Config, EditAnywhere, Category = "Networking") @@ -40,6 +44,11 @@ class FLOW_API UFlowSettings : public UDeveloperSettings UPROPERTY(EditAnywhere, config, Category = "Nodes") bool bUseAdaptiveNodeTitles; +#if WITH_EDITOR + DECLARE_DELEGATE(FFlowSettingsEvent); + FFlowSettingsEvent OnAdaptiveNodeTitlesChanged; +#endif + // Default class to use as a FlowAsset's "ExpectedOwnerClass" UPROPERTY(EditAnywhere, Config, Category = "Nodes", meta = (MustImplement = "/Script/Flow.FlowOwnerInterface")) FSoftClassPath DefaultExpectedOwnerClass; diff --git a/Source/Flow/Public/Nodes/FlowNodeBase.h b/Source/Flow/Public/Nodes/FlowNodeBase.h index 35795ad17..bdb98632f 100644 --- a/Source/Flow/Public/Nodes/FlowNodeBase.h +++ b/Source/Flow/Public/Nodes/FlowNodeBase.h @@ -181,6 +181,9 @@ class FLOW_API UFlowNodeBase #if WITH_EDITORONLY_DATA protected: + UPROPERTY(EditDefaultsOnly, Category = "FlowNode") + uint8 bDisplayNodeTitleWithoutPrefix : 1; + uint8 bCanDelete : 1 ; uint8 bCanDuplicate : 1; @@ -244,6 +247,7 @@ class FLOW_API UFlowNodeBase virtual FText GetNodeTitle() const; virtual FText GetNodeToolTip() const; virtual FText GetNodeConfigText() const; + FText GetGeneratedDisplayName() const; #endif protected: diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp index 4bd2477fb..65bcd4428 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp @@ -1,6 +1,7 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "Graph/FlowGraphEditorSettings.h" +#include "Graph/FlowGraphSchema.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraphEditorSettings) @@ -18,3 +19,15 @@ UFlowGraphEditorSettings::UFlowGraphEditorSettings(const FObjectInitializer& Obj , bHighlightOutputWiresOfSelectedNodes(false) { } + +#if WITH_EDITOR +void UFlowGraphEditorSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + if (PropertyChangedEvent.GetMemberPropertyName() == GET_MEMBER_NAME_CHECKED( UFlowGraphEditorSettings, bShowNodeClass )) + { + GetDefault()->ForceVisualizationCacheClear(); + } +} +#endif diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index febf857e4..32e6f8be1 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -11,6 +11,7 @@ #include "Graph/Nodes/FlowGraphNode.h" #include "FlowAsset.h" +#include "FlowSettings.h" #include "AddOns/FlowNodeAddOn.h" #include "Nodes/FlowNode.h" #include "Nodes/FlowNodeBlueprint.h" @@ -36,12 +37,20 @@ TMap UFlowGraphSchema::BlueprintFlowNodeAddOns; TMap, TSubclassOf> UFlowGraphSchema::GraphNodesByFlowNodes; bool UFlowGraphSchema::bBlueprintCompilationPending; +int32 UFlowGraphSchema::CurrentCacheRefreshID = 0; FFlowGraphSchemaRefresh UFlowGraphSchema::OnNodeListChanged; UFlowGraphSchema::UFlowGraphSchema(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { + if (HasAnyFlags(RF_ClassDefaultObject)) + { + GetMutableDefault()->OnAdaptiveNodeTitlesChanged.BindLambda([]() + { + GetDefault()->ForceVisualizationCacheClear(); + }); + } } void UFlowGraphSchema::SubscribeToAssetChanges() @@ -50,6 +59,7 @@ void UFlowGraphSchema::SubscribeToAssetChanges() AssetRegistry.Get().OnFilesLoaded().AddStatic(&UFlowGraphSchema::GatherNodes); AssetRegistry.Get().OnAssetAdded().AddStatic(&UFlowGraphSchema::OnAssetAdded); AssetRegistry.Get().OnAssetRemoved().AddStatic(&UFlowGraphSchema::OnAssetRemoved); + AssetRegistry.Get().OnAssetRenamed().AddStatic(&UFlowGraphSchema::OnAssetRenamed); FCoreUObjectDelegates::ReloadCompleteDelegate.AddStatic(&UFlowGraphSchema::OnHotReload); @@ -300,6 +310,78 @@ void UFlowGraphSchema::OnPinConnectionDoubleCicked(UEdGraphPin* PinA, UEdGraphPi PinB->MakeLinkTo((PinB->Direction == EGPD_Output) ? NewReroute->InputPins[0] : NewReroute->OutputPins[0]); } +bool UFlowGraphSchema::IsCacheVisualizationOutOfDate(int32 InVisualizationCacheID) const +{ + return CurrentCacheRefreshID != InVisualizationCacheID; +} + +int32 UFlowGraphSchema::GetCurrentVisualizationCacheID() const +{ + return CurrentCacheRefreshID; +} + +void UFlowGraphSchema::ForceVisualizationCacheClear() const +{ + ++CurrentCacheRefreshID; +} + +void UFlowGraphSchema::UpdateGeneratedDisplayNames() +{ + for (UClass* FlowNodeClass : NativeFlowNodes) + { + UpdateGeneratedDisplayName(FlowNodeClass, true); + } + + for (UClass* FlowNodeAddOnClass : NativeFlowNodeAddOns) + { + UpdateGeneratedDisplayName(FlowNodeAddOnClass, true); + } + + for (TPair& AssetData : BlueprintFlowNodes) + { + if (UBlueprint* Blueprint = Cast(AssetData.Value.GetAsset())) + { + UClass* NodeClass = Blueprint->GeneratedClass; + UpdateGeneratedDisplayName(NodeClass, true); + } + } + + for (TPair& AssetData : BlueprintFlowNodeAddOns) + { + if (UBlueprint* Blueprint = Cast(AssetData.Value.GetAsset())) + { + UClass* NodeAddOnClass = Blueprint->GeneratedClass; + UpdateGeneratedDisplayName(NodeAddOnClass, true); + } + } + + OnNodeListChanged.Broadcast(); + + // Refresh node titles + GetDefault()->ForceVisualizationCacheClear(); +} + +void UFlowGraphSchema::UpdateGeneratedDisplayName(UClass* NodeClass, bool bBatch) +{ + static const FName NAME_GeneratedDisplayName("GeneratedDisplayName"); + + if (NodeClass->IsChildOf(UFlowNodeBase::StaticClass()) == false) + { + return; + } + + FString NameWithoutPrefix = FFlowGraphUtils::RemovePrefixFromNodeText(NodeClass->GetDisplayNameText()); + NodeClass->SetMetaData(NAME_GeneratedDisplayName, *NameWithoutPrefix); + + if (!bBatch) + { + OnNodeListChanged.Broadcast(); + + // Refresh node titles + GetDefault()->ForceVisualizationCacheClear(); + } +} + TArray> UFlowGraphSchema::GetFlowNodeCategories() { if (!bInitialGatherPerformed) @@ -667,7 +749,7 @@ void UFlowGraphSchema::GatherNodes() AddAsset(AssetData, true); } - OnNodeListChanged.Broadcast(); + UpdateGeneratedDisplayNames(); } void UFlowGraphSchema::OnAssetAdded(const FAssetData& AssetData) @@ -706,6 +788,11 @@ void UFlowGraphSchema::AddAsset(const FAssetData& AssetData, const bool bBatch) if (bAddedToMap && !bBatch) { + if (UBlueprint* Blueprint = Cast(AssetData.GetAsset())) + { + UClass* NodeClass = Blueprint->GeneratedClass; + UpdateGeneratedDisplayName(NodeClass, false); + } OnNodeListChanged.Broadcast(); } } @@ -750,6 +837,28 @@ void UFlowGraphSchema::OnAssetRemoved(const FAssetData& AssetData) } } +void UFlowGraphSchema::OnAssetRenamed(const FAssetData& AssetData, const FString& OldObjectPath) +{ + FString OldPackageName; + FString OldAssetName; + if (OldObjectPath.Split(TEXT("."), &OldPackageName, &OldAssetName)) + { + const FName NAME_OldPackageName{OldPackageName}; + if (BlueprintFlowNodes.Contains(NAME_OldPackageName)) + { + BlueprintFlowNodes.Remove(NAME_OldPackageName); + BlueprintFlowNodes.Shrink(); + } + else if (BlueprintFlowNodeAddOns.Contains(NAME_OldPackageName)) + { + BlueprintFlowNodeAddOns.Remove(NAME_OldPackageName); + BlueprintFlowNodeAddOns.Shrink(); + } + } + + AddAsset(AssetData, false); +} + UBlueprint* UFlowGraphSchema::GetPlaceableNodeOrAddOnBlueprint(const FAssetData& AssetData) { UBlueprint* Blueprint = Cast(AssetData.GetAsset()); diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp index 69587ba58..99c51b619 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp @@ -3,6 +3,10 @@ #include "Graph/FlowGraphSettings.h" #include "FlowAsset.h" +#include "Algo/Unique.h" +#include "Framework/Notifications/NotificationManager.h" +#include "Graph/FlowGraphSchema.h" +#include "Widgets/Notifications/SNotificationList.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraphSettings) @@ -40,6 +44,61 @@ UFlowGraphSettings::UFlowGraphSettings(const FObjectInitializer& ObjectInitializ NodeTitleColors.Emplace(EFlowNodeStyle::Latent, FLinearColor(0.0f, 0.770f, 0.375f, 1.0f)); NodeTitleColors.Emplace(EFlowNodeStyle::Logic, FLinearColor(1.0f, 1.0f, 1.0f, 1.0f)); NodeTitleColors.Emplace(EFlowNodeStyle::SubGraph, FLinearColor(1.0f, 0.128f, 0.0f, 1.0f)); + + NodePrefixesToRemove.Emplace("FN"); + NodePrefixesToRemove.Emplace("FlowNode"); + NodePrefixesToRemove.Emplace("FlowNodeAddOn"); +} + +void UFlowGraphSettings::PostInitProperties() +{ + Super::PostInitProperties(); + + NodePrefixesToRemove.Sort(TGreater{}); +} + +#if WITH_EDITOR + +void UFlowGraphSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + if (PropertyChangedEvent.GetMemberPropertyName() == GET_MEMBER_NAME_CHECKED( UFlowGraphSettings, NodePrefixesToRemove )) + { + // + // We need to sort items in array, because unsorted array can cause only partial prefix removal. + // For example, we have a NodeName = "UFlowNode_Custom" and these elements in the array: + // NodePrefixesToRemove = {"FN", "Flow", "FlowNode"}; + // Note: Prefix "U" is removed by Unreal + // + // First prefix does not match start of NodeName, so nothing changes. + // Second prefix match start of the name and is removed, NodeName becomes "Node_Custom" + // Third prefix does not match start of NodeName, so nothing changes. + // After complete process NodeName == "Node_Custom", but expected result is "Custom" + // + // If NodePrefixesToRemove = {"FN", "FlowNode", "Flow"} instead, everything will be removed as expected. + // + + NodePrefixesToRemove.Sort(TGreater{}); + + const int32 SizeBefore = NodePrefixesToRemove.Num(); + const int32 SizeAfter = Algo::Unique(NodePrefixesToRemove); + + if (SizeBefore > SizeAfter) + { + NodePrefixesToRemove.SetNum(SizeAfter); + + // error notification + FNotificationInfo Info(LOCTEXT("FlowGraphSettings_DuplicatePrefixError", "Added prefix already exists in array.")); + Info.ExpireDuration = 3.0f; + FSlateNotificationManager::Get().AddNotification(Info)->SetCompletionState(SNotificationItem::CS_Fail); + } + else + { + UFlowGraphSchema::UpdateGeneratedDisplayNames(); + } + } } +#endif #undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp b/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp index 468ba353f..d6ed30305 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp @@ -3,6 +3,7 @@ #include "Graph/FlowGraphUtils.h" #include "Asset/FlowAssetEditor.h" #include "Graph/FlowGraph.h" +#include "Graph/FlowGraphSettings.h" #include "FlowAsset.h" @@ -36,3 +37,21 @@ TSharedPtr FFlowGraphUtils::GetFlowGraphEditor(const UEdGraph* return FlowGraphEditor; } + +FString FFlowGraphUtils::RemovePrefixFromNodeText(const FText& Source) +{ + FString SourceString = Source.ToString(); + TArray NodePrefixes = UFlowGraphSettings::Get()->NodePrefixesToRemove; + + for (FString Prefix : NodePrefixes) + { + Prefix = FName::NameToDisplayString(Prefix, false); + if (SourceString.StartsWith(Prefix)) + { + SourceString.MidInline(Prefix.Len(), MAX_int32, false); + SourceString = SourceString.TrimStart(); + } + } + + return SourceString; +} diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 7def38991..80502e847 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -630,7 +630,7 @@ FText UFlowGraphNode::GetTooltipText() const FText Tooltip; if (NodeInstance) { - Tooltip = NodeInstance->GetClass()->GetToolTipText(); + Tooltip = NodeInstance->GetNodeToolTip(); } if (Tooltip.IsEmpty()) { diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h index 6da9fc25d..6e3546702 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h @@ -23,6 +23,10 @@ class FLOWEDITOR_API UFlowGraphEditorSettings : public UDeveloperSettings static UFlowGraphEditorSettings* Get() { return StaticClass()->GetDefaultObject(); } +#if WITH_EDITOR + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; +#endif + // Double-clicking a Flow Node might open relevant asset/code editor UPROPERTY(config, EditAnywhere, Category = "Nodes") EFlowNodeDoubleClickTarget NodeDoubleClickTarget; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h index d432a97fe..3c878e1b1 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h @@ -49,6 +49,9 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema virtual int32 GetNodeSelectionCount(const UEdGraph* Graph) const override; virtual TSharedPtr GetCreateCommentAction() const override; virtual void OnPinConnectionDoubleCicked(UEdGraphPin* PinA, UEdGraphPin* PinB, const FVector2D& GraphPosition) const override; + virtual bool IsCacheVisualizationOutOfDate(int32 InVisualizationCacheID) const override; + virtual int32 GetCurrentVisualizationCacheID() const override; + virtual void ForceVisualizationCacheClear() const override; // -- // FlowGraphSchema @@ -58,6 +61,9 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema // -- + static void UpdateGeneratedDisplayNames(); + static void UpdateGeneratedDisplayName(UClass* NodeClass, bool bBatch = false); + static TArray> GetFlowNodeCategories(); static TSubclassOf GetAssignedGraphNodeClass(const TSubclassOf& FlowNodeClass); @@ -87,10 +93,15 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema static bool ShouldAddToBlueprintFlowNodesMap(const FAssetData& AssetData, const TSubclassOf& BlueprintClass, const TSubclassOf& FlowNodeBaseClass); static void OnAssetRemoved(const FAssetData& AssetData); + static void OnAssetRenamed(const FAssetData& AssetData, const FString& OldObjectPath); public: static FFlowGraphSchemaRefresh OnNodeListChanged; static UBlueprint* GetPlaceableNodeOrAddOnBlueprint(const FAssetData& AssetData); static const UFlowAsset* GetEditedAssetOrClassDefault(const UEdGraph* Graph); + +private: + // ID for checking dirty status of node titles against + static int32 CurrentCacheRefreshID; }; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h index a21116bb6..09d9c1952 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h @@ -17,6 +17,12 @@ class FLOWEDITOR_API UFlowGraphSettings : public UDeveloperSettings GENERATED_UCLASS_BODY() static UFlowGraphSettings* Get() { return StaticClass()->GetDefaultObject(); } + virtual void PostInitProperties() override; + +#if WITH_EDITOR + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; +#endif + /** Show Flow Asset in Flow category of "Create Asset" menu? * Requires restart after making a change. */ UPROPERTY(EditAnywhere, config, Category = "Default UI", meta = (ConfigRestartRequired = true)) @@ -52,6 +58,11 @@ class FLOWEDITOR_API UFlowGraphSettings : public UDeveloperSettings UPROPERTY(EditAnywhere, config, Category = "Nodes") bool bShowDefaultPinNames; + /** List of prefixes to hide on node titles and palette without need to add custom DisplayName. + * If node class has meta = (DisplayName = ... ) or BlueprintDisplayName, those texts will be displayed */ + UPROPERTY(EditAnywhere, config, Category = "Nodes") + TArray NodePrefixesToRemove; + UPROPERTY(EditAnywhere, config, Category = "Nodes") TMap NodeTitleColors; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphUtils.h b/Source/FlowEditor/Public/Graph/FlowGraphUtils.h index 87b8e126c..bb7731dac 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphUtils.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphUtils.h @@ -16,4 +16,6 @@ class FLOWEDITOR_API FFlowGraphUtils static TSharedPtr GetFlowAssetEditor(const UEdGraph* Graph); static TSharedPtr GetFlowGraphEditor(const UEdGraph* Graph); + + static FString RemovePrefixFromNodeText(const FText& Source); }; From 274efcb07627b0e71ebd748b8b238a376b76e796 Mon Sep 17 00:00:00 2001 From: FoolsTheoryDev Date: Wed, 3 Jul 2024 19:13:01 +0200 Subject: [PATCH 253/485] Added OnDetailsRefreshRequested delegate It allows developers to refresh the Asset or Node details panel without the need to add a boilerplate to projects. --- Source/Flow/Public/FlowAsset.h | 10 +++++++--- Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp | 9 +++++++++ Source/FlowEditor/Public/Asset/FlowAssetEditor.h | 1 + 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 3216955eb..28aa4f492 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -69,6 +69,7 @@ class FLOW_API UFlowAsset : public UObject // Graph #if WITH_EDITOR +public: friend class UFlowGraph; // UObject @@ -78,14 +79,17 @@ class FLOW_API UFlowAsset : public UObject virtual void PostLoad() override; // -- +public: + FSimpleDelegate OnDetailsRefreshRequested; + + static FString ValidationError_NodeClassNotAllowed; + static FString ValidationError_NullNodeInstance; + virtual EDataValidationResult ValidateAsset(FFlowMessageLog& MessageLog); // Returns whether the node class is allowed in this flow asset bool IsNodeOrAddOnClassAllowed(const UClass* FlowNodeClass, FText* OutOptionalFailureReason = nullptr) const; - static FString ValidationError_NodeClassNotAllowed; - static FString ValidationError_NullNodeInstance; - protected: bool CanFlowNodeClassBeUsedByFlowAsset(const UClass& FlowNodeClass) const; bool CanFlowAssetUseFlowNodeClass(const UClass& FlowNodeClass) const; diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index b3bb8a317..caf2e554f 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -314,6 +314,7 @@ void FFlowAssetEditor::InitFlowAssetEditor(const EToolkitMode::Type Mode, const GEditor->RegisterForUndo(this); UFlowGraphSchema::SubscribeToAssetChanges(); + FlowAsset->OnDetailsRefreshRequested.BindThreadSafeSP(this, &FFlowAssetEditor::RefreshDetails); BindToolbarCommands(); CreateToolbar(); @@ -435,6 +436,14 @@ void FFlowAssetEditor::RefreshAsset() CastChecked(FlowAsset->GetGraph())->RefreshGraph(); } +void FFlowAssetEditor::RefreshDetails() +{ + if (DetailsView.IsValid()) + { + DetailsView->ForceRefresh(); + } +} + void FFlowAssetEditor::ValidateAsset_Internal() { FFlowMessageLog LogResults; diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index 94b7e17cb..8e6a076ba 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -128,6 +128,7 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit virtual void BindToolbarCommands(); virtual void RefreshAsset(); + virtual void RefreshDetails(); private: void ValidateAsset_Internal(); From b9b515e0aaf54fcb4439db6cd072d8cdc9bf0bcb Mon Sep 17 00:00:00 2001 From: Unbansheee Date: Wed, 10 Jul 2024 02:28:35 +1200 Subject: [PATCH 254/485] Restored SFlowNode pin alignment to pre-FlowAddon settings (#213) --- .../Private/Graph/Widgets/SFlowGraphNode.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp index 66389cd32..d17c6d543 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp @@ -923,9 +923,10 @@ void SFlowGraphNode::AddPin(const TSharedRef& PinToAdd) if (PinToAdd->GetDirection() == EEdGraphPinDirection::EGPD_Input) { LeftNodeBox->AddSlot() - .HAlign(HAlign_Fill) - .VAlign(VAlign_Fill) - .FillHeight(1.0f) + .AutoHeight() + .HAlign(HAlign_Left) + .VAlign(VAlign_Center) + .Padding(Settings->GetInputPinPadding()) [ PinToAdd ]; @@ -934,9 +935,10 @@ void SFlowGraphNode::AddPin(const TSharedRef& PinToAdd) else // Direction == EEdGraphPinDirection::EGPD_Output { RightNodeBox->AddSlot() - .HAlign(HAlign_Fill) - .VAlign(VAlign_Fill) - .FillHeight(1.0f) + .AutoHeight() + .HAlign(HAlign_Right) + .VAlign(VAlign_Center) + .Padding(Settings->GetOutputPinPadding()) [ PinToAdd ]; From b7863852b82299a7be3f8cb7d4395cbf2dc7af1e Mon Sep 17 00:00:00 2001 From: WangXudong <48378407+dzxmxd@users.noreply.github.com> Date: Thu, 15 Aug 2024 21:54:09 +0800 Subject: [PATCH 255/485] Add a search functionality that does not rely on engine modifications. (#206) * feat: Add a search functionality for the FlowGraph plugin that does not rely on engine modifications. * fix: Add missing header files and remove useless logic. * feat: Support double clicking to jump and focus on subgraph nodes. * fix: Fixed the bug that comment nodes in subgraphs cannot be searched. --- .../Private/Asset/FlowAssetEditor.cpp | 27 +- .../Private/Asset/FlowAssetToolbar.cpp | 11 +- Source/FlowEditor/Private/Find/FindInFlow.cpp | 458 ++++++++++++++++++ .../FlowEditor/Public/Asset/FlowAssetEditor.h | 11 +- Source/FlowEditor/Public/Find/FindInFlow.h | 151 ++++++ Source/FlowEditor/Public/FlowEditorDefines.h | 1 + 6 files changed, 637 insertions(+), 22 deletions(-) create mode 100644 Source/FlowEditor/Private/Find/FindInFlow.cpp create mode 100644 Source/FlowEditor/Public/Find/FindInFlow.h diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index caf2e554f..8156dfcbf 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -30,6 +30,8 @@ #if ENABLE_SEARCH_IN_ASSET_EDITOR #include "Source/Private/Widgets/SSearchBrowser.h" +#else +#include "Find/FindInFlow.h" #endif #define LOCTEXT_NAMESPACE "FlowAssetEditor" @@ -127,14 +129,12 @@ void FFlowAssetEditor::RegisterTabSpawners(const TSharedRef& .SetDisplayName(LOCTEXT("RuntimeLog", "Runtime Log")) .SetGroup(WorkspaceMenuCategoryRef) .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.Tabs.CompilerResults")); - -#if ENABLE_SEARCH_IN_ASSET_EDITOR + InTabManager->RegisterTabSpawner(SearchTab, FOnSpawnTab::CreateSP(this, &FFlowAssetEditor::SpawnTab_Search)) .SetDisplayName(LOCTEXT("SearchTab", "Search")) .SetGroup(WorkspaceMenuCategoryRef) .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.Tabs.FindResults")); -#endif - + InTabManager->RegisterTabSpawner(ValidationLogTab, FOnSpawnTab::CreateSP(this, &FFlowAssetEditor::SpawnTab_ValidationLog)) .SetDisplayName(LOCTEXT("ValidationLog", "Validation Log")) .SetGroup(WorkspaceMenuCategoryRef) @@ -149,9 +149,7 @@ void FFlowAssetEditor::UnregisterTabSpawners(const TSharedRef InTabManager->UnregisterTabSpawner(GraphTab); InTabManager->UnregisterTabSpawner(ValidationLogTab); InTabManager->UnregisterTabSpawner(PaletteTab); -#if ENABLE_SEARCH_IN_ASSET_EDITOR InTabManager->UnregisterTabSpawner(SearchTab); -#endif } void FFlowAssetEditor::InitToolMenuContext(FToolMenuContext& MenuContext) @@ -270,7 +268,6 @@ TSharedRef FFlowAssetEditor::SpawnTab_RuntimeLog(const FSpawnTabArgs& ]; } -#if ENABLE_SEARCH_IN_ASSET_EDITOR TSharedRef FFlowAssetEditor::SpawnTab_Search(const FSpawnTabArgs& Args) const { check(Args.GetTabId() == SearchTab); @@ -285,7 +282,6 @@ TSharedRef FFlowAssetEditor::SpawnTab_Search(const FSpawnTabArgs& Args ] ]; } -#endif TSharedRef FFlowAssetEditor::SpawnTab_ValidationLog(const FSpawnTabArgs& Args) const { @@ -408,12 +404,10 @@ void FFlowAssetEditor::BindToolbarCommands() ToolkitCommands->MapAction(ToolbarCommands.ValidateAsset, FExecuteAction::CreateSP(this, &FFlowAssetEditor::ValidateAsset_Internal), FCanExecuteAction()); - -#if ENABLE_SEARCH_IN_ASSET_EDITOR + ToolkitCommands->MapAction(ToolbarCommands.SearchInAsset, FExecuteAction::CreateSP(this, &FFlowAssetEditor::SearchInAsset), FCanExecuteAction()); -#endif ToolkitCommands->MapAction(ToolbarCommands.EditAssetDefaults, FExecuteAction::CreateSP(this, &FFlowAssetEditor::EditAssetDefaults_Clicked), @@ -464,13 +458,11 @@ void FFlowAssetEditor::ValidateAsset(FFlowMessageLog& MessageLog) FlowAsset->ValidateAsset(MessageLog); } -#if ENABLE_SEARCH_IN_ASSET_EDITOR void FFlowAssetEditor::SearchInAsset() { TabManager->TryInvokeTab(SearchTab); SearchBrowser->FocusForUse(); } -#endif void FFlowAssetEditor::EditAssetDefaults_Clicked() const { @@ -516,6 +508,8 @@ void FFlowAssetEditor::CreateWidgets() // Search #if ENABLE_SEARCH_IN_ASSET_EDITOR SearchBrowser = SNew(SSearchBrowser, GetFlowAsset()); +#else + SearchBrowser = SNew(SFindInFlow, SharedThis(this)); #endif // Logs @@ -625,4 +619,11 @@ void FFlowAssetEditor::OnLogTokenClicked(const TSharedRef& Token) } } +void FFlowAssetEditor::JumpToNode(const UEdGraphNode* Node) const +{ + if (GetFlowGraph().IsValid()) + { + GetFlowGraph()->JumpToNode(Node, false); + } +} #undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp index c621c4109..90c86cdc6 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp @@ -224,10 +224,13 @@ void FFlowAssetToolbar::BuildAssetToolbar(UToolMenu* ToolbarMenu) const DiffEntry.StyleNameOverride = "CalloutToolbar"; InSection.AddEntry(DiffEntry); })); - -#if ENABLE_SEARCH_IN_ASSET_EDITOR - Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().SearchInAsset)); -#endif + + Section.AddEntry(FToolMenuEntry::InitToolBarButton( + FFlowToolbarCommands::Get().SearchInAsset, + TAttribute(), + TAttribute(), + FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.Tabs.FindResults") + )); } } diff --git a/Source/FlowEditor/Private/Find/FindInFlow.cpp b/Source/FlowEditor/Private/Find/FindInFlow.cpp new file mode 100644 index 000000000..3687b1af0 --- /dev/null +++ b/Source/FlowEditor/Private/Find/FindInFlow.cpp @@ -0,0 +1,458 @@ +// Copyright Epic Games, Inc. All Rights Reserved. +#include "Find/FindInFlow.h" + +#include "FlowAsset.h" +#include "Asset/FlowAssetEditor.h" +#include "EdGraph/EdGraph.h" +#include "Graph/FlowGraphEditor.h" +#include "EdGraph/EdGraphNode.h" +#include "Framework/Application/SlateApplication.h" +#include "Framework/Views/ITypedTableView.h" +#include "GraphEditor.h" +#include "HAL/PlatformMath.h" +#include "Input/Events.h" +#include "Internationalization/Internationalization.h" +#include "Layout/Children.h" +#include "Layout/WidgetPath.h" +#include "Math/Color.h" +#include "Misc/Attribute.h" +#include "SlotBase.h" +#include "Graph/FlowGraphUtils.h" +#include "Graph/Nodes/FlowGraphNode.h" +#include "Nodes/FlowNode.h" +#include "Nodes/Route/FlowNode_SubGraph.h" +#include "Styling/AppStyle.h" +#include "Styling/SlateColor.h" +#include "Templates/Casts.h" +#include "Types/SlateStructs.h" +#include "UObject/Class.h" +#include "UObject/ObjectPtr.h" +#include "Widgets/Images/SImage.h" +#include "Widgets/Input/SSearchBox.h" +#include "Widgets/Layout/SBorder.h" +#include "Widgets/Layout/SBox.h" +#include "Widgets/SBoxPanel.h" +#include "Widgets/Text/STextBlock.h" +#include "Widgets/Views/STableRow.h" + +class ITableRow; +class SWidget; +struct FSlateBrush; + +#define LOCTEXT_NAMESPACE "FindInFlow" + +////////////////////////////////////////////////////////////////////////// +// FFindInFlowResult + +FFindInFlowResult::FFindInFlowResult(const FString& InValue) + : Value(InValue), GraphNode(nullptr) +{ +} + +FFindInFlowResult::FFindInFlowResult(const FString& InValue, TSharedPtr& InParent, UEdGraphNode* InNode, bool bInIsSubGraphNode) + : Value(InValue), GraphNode(InNode), Parent(InParent), bIsSubGraphNode(bInIsSubGraphNode) +{ +} + +TSharedRef FFindInFlowResult::CreateIcon() const +{ + const FSlateColor IconColor = FSlateColor::UseForeground(); + const FSlateBrush* Brush = FAppStyle::GetBrush(TEXT("GraphEditor.FIB_Event")); + + return SNew(SImage) + .Image(Brush) + .ColorAndOpacity(IconColor); +} + +FReply FFindInFlowResult::OnClick(TWeakPtr FlowAssetEditorPtr, TSharedPtr Root) +{ + if (FlowAssetEditorPtr.IsValid() && GraphNode.IsValid()) + { + if (Parent.IsValid() && !bIsSubGraphNode) + { + FlowAssetEditorPtr.Pin()->JumpToNode(GraphNode.Get()); + } + else + { + FlowAssetEditorPtr.Pin()->JumpToNode(Parent.Pin()->GraphNode.Get()); + } + } + + return FReply::Handled(); +} + +FReply FFindInFlowResult::OnDoubleClick(TSharedPtr Root) +{ + if (!Parent.IsValid() || !bIsSubGraphNode) + { + return FReply::Handled(); + } + const UFlowGraphNode* ParentGraphNode = Cast(Parent.Pin()->GraphNode); + if (!ParentGraphNode || !ParentGraphNode->GetFlowNode()) + { + return FReply::Handled(); + } + + if (UObject* AssetToEdit = ParentGraphNode->GetFlowNode()->GetAssetToEdit()) + { + UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem(); + if (AssetEditorSubsystem->OpenEditorForAsset(AssetToEdit)) + { + if (const TSharedPtr FlowAssetEditor = FFlowGraphUtils::GetFlowAssetEditor(GraphNode->GetGraph())) + { + FlowAssetEditor->JumpToNode(GraphNode.Get()); + } + } + } + + return FReply::Handled(); +} + +FString FFindInFlowResult::GetDescriptionText() const +{ + if (const UFlowGraphNode* FlowGraphNode = Cast(GraphNode.Get())) + { + return FlowGraphNode->GetNodeDescription(); + } + + return FString(); +} + +FString FFindInFlowResult::GetCommentText() const +{ + if (GraphNode.IsValid()) + { + return GraphNode->NodeComment; + } + + return FString(); +} + +FString FFindInFlowResult::GetNodeTypeText() const +{ + if (GraphNode.IsValid()) + { + FString NodeClassName; + const UFlowGraphNode* FlowGraphNode = Cast(GraphNode.Get()); + if (FlowGraphNode && FlowGraphNode->GetFlowNode()) + { + NodeClassName = FlowGraphNode->GetFlowNode()->GetClass()->GetName(); + } + else + { + NodeClassName = GraphNode->GetClass()->GetName(); + } + const int32 Pos = NodeClassName.Find("_"); + if (Pos == INDEX_NONE) + { + return NodeClassName; + } + else + { + return NodeClassName.RightChop(Pos + 1); + } + } + + return FString(); +} + +FText FFindInFlowResult::GetToolTipText() const +{ + FString ToolTipStr = TEXT("Click to focus on nodes."); + if (bIsSubGraphNode) + { + ToolTipStr += TEXT("\nDouble click to focus on subgraph nodes"); + } + return FText::FromString(ToolTipStr); +} + +////////////////////////////////////////////////////////////////////////// +// SFindInFlow + +void SFindInFlow::Construct( const FArguments& InArgs, TSharedPtr InFlowAssetEditor) +{ + FlowAssetEditorPtr = InFlowAssetEditor; + + this->ChildSlot + [ + SNew(SVerticalBox) + +SVerticalBox::Slot() + .AutoHeight() + [ + SNew(SHorizontalBox) + +SHorizontalBox::Slot() + .FillWidth(1) + [ + SAssignNew(SearchTextField, SSearchBox) + .HintText(LOCTEXT("FlowEditorSearchHint", "Enter text to find nodes...")) + .OnTextChanged(this, &SFindInFlow::OnSearchTextChanged) + .OnTextCommitted(this, &SFindInFlow::OnSearchTextCommitted) + ] + +SHorizontalBox::Slot() + .Padding(10,0,5,0) + .AutoWidth() + .VAlign(VAlign_Center) + [ + SNew(STextBlock) + .Text(LOCTEXT("FlowEditorSubGraphSearchText", "Find In SubGraph ")) + ] + +SHorizontalBox::Slot() + .AutoWidth() + [ + SNew(SCheckBox) + .OnCheckStateChanged(this, &SFindInFlow::OnFindInSubGraphStateChanged) + .ToolTipText(LOCTEXT("FlowEditorSubGraphSearchHint", "Checkin means search also in sub graph.")) + ] + ] + +SVerticalBox::Slot() + .FillHeight(1.0f) + .Padding(0.f, 4.f, 0.f, 0.f) + [ + SNew(SBorder) + .BorderImage(FAppStyle::GetBrush("Menu.Background")) + [ + SAssignNew(TreeView, STreeViewType) + .ItemHeight(24) + .TreeItemsSource(&ItemsFound) + .OnGenerateRow(this, &SFindInFlow::OnGenerateRow) + .OnGetChildren(this, &SFindInFlow::OnGetChildren) + .OnSelectionChanged(this, &SFindInFlow::OnTreeSelectionChanged) + .OnMouseButtonDoubleClick(this, &SFindInFlow::OnTreeSelectionDoubleClicked) + .SelectionMode(ESelectionMode::Multi) + ] + ] + ]; +} + +void SFindInFlow::FocusForUse() const +{ + // NOTE: Careful, GeneratePathToWidget can be reentrant in that it can call visibility delegates and such + FWidgetPath FilterTextBoxWidgetPath; + FSlateApplication::Get().GeneratePathToWidgetUnchecked(SearchTextField.ToSharedRef(), FilterTextBoxWidgetPath); + + // Set keyboard focus directly + FSlateApplication::Get().SetKeyboardFocus(FilterTextBoxWidgetPath, EFocusCause::SetDirectly); +} + +void SFindInFlow::OnSearchTextChanged(const FText& Text) +{ + SearchValue = Text.ToString(); + + InitiateSearch(); +} + +void SFindInFlow::OnSearchTextCommitted(const FText& Text, ETextCommit::Type CommitType) +{ + OnSearchTextChanged(Text); +} + +void SFindInFlow::InitiateSearch() +{ + TArray Tokens; + SearchValue.ParseIntoArray(Tokens, TEXT(" "), true); + + for (auto It(ItemsFound.CreateIterator()); It; ++It) + { + TreeView->SetItemExpansion(*It, false); + } + ItemsFound.Empty(); + if (Tokens.Num() > 0) + { + HighlightText = FText::FromString(SearchValue); + MatchTokens(Tokens); + } + + // Insert a fake result to inform user if none found + if (ItemsFound.Num() == 0) + { + ItemsFound.Add(MakeShared(LOCTEXT("FlowEditorSearchNoResults", "No Results found").ToString())); + } + + TreeView->RequestTreeRefresh(); + + for (auto It(ItemsFound.CreateIterator()); It; ++It) + { + TreeView->SetItemExpansion(*It, true); + } +} + +void SFindInFlow::MatchTokens(const TArray& Tokens) +{ + RootSearchResult.Reset(); + + const TWeakPtr FocusedGraphEditor = FlowAssetEditorPtr.Pin()->GetFlowGraph(); + const UEdGraph* Graph = nullptr; + if (FocusedGraphEditor.IsValid()) + { + Graph = FocusedGraphEditor.Pin()->GetCurrentGraph(); + } + + if (Graph == nullptr) + { + return; + } + + RootSearchResult = MakeShared(FString("FlowEditorRoot")); + + for (auto It(Graph->Nodes.CreateConstIterator()); It; ++It) + { + UEdGraphNode* Node = *It; + + const FString NodeName = Node->GetNodeTitle(ENodeTitleType::ListView).ToString(); + FSearchResult NodeResult(new FFindInFlowResult(NodeName, RootSearchResult, Node)); + FString NodeSearchString = NodeName + Node->GetClass()->GetName() + Node->NodeComment; + + if (const UFlowGraphNode* FlowGraphNode = Cast(Node)) + { + FString NodeDescription = FlowGraphNode->GetNodeDescription(); + NodeSearchString += NodeDescription; + + UFlowNode_SubGraph* SubGraphNode = Cast(FlowGraphNode->GetFlowNode()); + if (bFindInSubGraph && SubGraphNode) + { + if (const UFlowAsset* FlowAsset = Cast(SubGraphNode->GetAssetToEdit()); FlowAsset && FlowAsset->GetGraph()) + { + for (auto ChildIt(FlowAsset->GetGraph()->Nodes.CreateConstIterator()); ChildIt; ++ChildIt) + { + MatchTokensInChild(Tokens, *ChildIt, NodeResult); + } + } + } + } + + NodeSearchString = NodeSearchString.Replace(TEXT(" "), TEXT("")); + const bool bNodeMatchesSearch = StringMatchesSearchTokens(Tokens, NodeSearchString); + + if ((NodeResult->Children.Num() > 0) || bNodeMatchesSearch) + { + ItemsFound.Add(NodeResult); + } + } +} + +void SFindInFlow::MatchTokensInChild(const TArray& Tokens, UEdGraphNode* Child, FSearchResult ParentNode) +{ + if (Child == nullptr) + { + return; + } + + const FString ChildName = Child->GetNodeTitle(ENodeTitleType::ListView).ToString(); + FString ChildSearchString = ChildName + Child->GetClass()->GetName() + Child->NodeComment; + if (const UFlowGraphNode* FlowGraphNode = Cast(Child)) + { + FString NodeDescription = FlowGraphNode->GetNodeDescription(); + ChildSearchString += NodeDescription; + } + ChildSearchString = ChildSearchString.Replace(TEXT(" "), TEXT("")); + if (StringMatchesSearchTokens(Tokens, ChildSearchString)) + { + const FSearchResult DecoratorResult(new FFindInFlowResult(ChildName, ParentNode, Child, true)); + ParentNode->Children.Add(DecoratorResult); + } +} + +TSharedRef SFindInFlow::OnGenerateRow( FSearchResult InItem, const TSharedRef& OwnerTable ) +{ + return SNew(STableRow< TSharedPtr >, OwnerTable) + .ToolTip(SNew(SToolTip).Text(InItem->GetToolTipText())) + [ + SNew(SHorizontalBox) + +SHorizontalBox::Slot() + .VAlign(VAlign_Center) + .AutoWidth() + [ + SNew(SBox) + .MinDesiredWidth(300) + [ + SNew(SHorizontalBox) + +SHorizontalBox::Slot() + .AutoWidth() + [ + InItem->CreateIcon() + ] + +SHorizontalBox::Slot() + .VAlign(VAlign_Center) + .AutoWidth() + .Padding(2, 0) + [ + SNew(STextBlock) + .Text(FText::FromString(InItem->Value)) + .HighlightText(HighlightText) + ] + ] + ] + +SHorizontalBox::Slot() + .VAlign(VAlign_Center) + [ + SNew(STextBlock) + .Text(FText::FromString(InItem->GetDescriptionText())) + .HighlightText(HighlightText) + ] + +SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Center) + [ + SNew(STextBlock) + .Text(FText::FromString(InItem->GetNodeTypeText())) + .HighlightText(HighlightText) + ] + +SHorizontalBox::Slot() + .HAlign(HAlign_Right) + .VAlign(VAlign_Center) + [ + SNew(STextBlock) + .Text(FText::FromString(InItem->GetCommentText())) + .ColorAndOpacity(FLinearColor::Yellow) + .HighlightText(HighlightText) + ] + ]; +} + +void SFindInFlow::OnGetChildren(FSearchResult InItem, TArray< FSearchResult >& OutChildren) +{ + OutChildren += InItem->Children; +} + +void SFindInFlow::OnTreeSelectionChanged(FSearchResult Item , ESelectInfo::Type) +{ + if (Item.IsValid()) + { + Item->OnClick(FlowAssetEditorPtr, RootSearchResult); + } +} + +void SFindInFlow::OnTreeSelectionDoubleClicked(FSearchResult Item) +{ + if (Item.IsValid()) + { + Item->OnDoubleClick(RootSearchResult); + } +} + +void SFindInFlow::OnFindInSubGraphStateChanged(ECheckBoxState CheckBoxState) +{ + bFindInSubGraph = CheckBoxState == ECheckBoxState::Checked; + InitiateSearch(); +} + +bool SFindInFlow::StringMatchesSearchTokens(const TArray& Tokens, const FString& ComparisonString) +{ + bool bFoundAllTokens = true; + + //search the entry for each token, it must have all of them to pass + for (auto TokItr(Tokens.CreateConstIterator()); TokItr; ++TokItr) + { + const FString& Token = *TokItr; + if (!ComparisonString.Contains(Token)) + { + bFoundAllTokens = false; + break; + } + } + return bFoundAllTokens; +} + +///////////////////////////////////////////////////// + +#undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index 8e6a076ba..881f6c26e 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -47,6 +47,8 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit #if ENABLE_SEARCH_IN_ASSET_EDITOR TSharedPtr SearchBrowser; +#else + TSharedPtr SearchBrowser; #endif /** Runtime message log, with the log listing that it reflects */ @@ -112,9 +114,7 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit TSharedRef SpawnTab_Graph(const FSpawnTabArgs& Args) const; TSharedRef SpawnTab_Palette(const FSpawnTabArgs& Args) const; TSharedRef SpawnTab_RuntimeLog(const FSpawnTabArgs& Args) const; -#if ENABLE_SEARCH_IN_ASSET_EDITOR TSharedRef SpawnTab_Search(const FSpawnTabArgs& Args) const; -#endif TSharedRef SpawnTab_ValidationLog(const FSpawnTabArgs& Args) const; void DoPresaveAssetUpdate(); @@ -135,10 +135,7 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit protected: virtual void ValidateAsset(FFlowMessageLog& MessageLog); - -#if ENABLE_SEARCH_IN_ASSET_EDITOR virtual void SearchInAsset(); -#endif void EditAssetDefaults_Clicked() const; @@ -165,4 +162,8 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit protected: void OnLogTokenClicked(const TSharedRef& Token) const; + +public: + // Find in flow + void JumpToNode(const UEdGraphNode* Node) const; }; diff --git a/Source/FlowEditor/Public/Find/FindInFlow.h b/Source/FlowEditor/Public/Find/FindInFlow.h new file mode 100644 index 000000000..962cb4b22 --- /dev/null +++ b/Source/FlowEditor/Public/Find/FindInFlow.h @@ -0,0 +1,151 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "Containers/Array.h" +#include "Containers/BitArray.h" +#include "Containers/Set.h" +#include "Containers/SparseArray.h" +#include "Containers/UnrealString.h" +#include "Delegates/Delegate.h" +#include "HAL/PlatformCrt.h" +#include "Input/Reply.h" +#include "Internationalization/Text.h" +#include "Misc/Optional.h" +#include "Templates/SharedPointer.h" +#include "Templates/TypeHash.h" +#include "Templates/UnrealTemplate.h" +#include "Types/SlateEnums.h" +#include "UObject/WeakObjectPtr.h" +#include "UObject/WeakObjectPtrTemplates.h" +#include "Widgets/DeclarativeSyntaxSupport.h" +#include "Widgets/SCompoundWidget.h" +#include "Widgets/Views/STableViewBase.h" +#include "Widgets/Views/STreeView.h" + +class ITableRow; +class SWidget; +class UFlowGraphNode; +class UEdGraphNode; + +/** Item that matched the search results */ +class FFindInFlowResult +{ +public: + /** Create a root (or only text) result */ + FFindInFlowResult(const FString& InValue); + + /** Create a flow node result */ + FFindInFlowResult(const FString& InValue, TSharedPtr& InParent, UEdGraphNode* InNode, bool bInIsSubGraphNode = false); + + /** Called when user clicks on the search item */ + FReply OnClick(TWeakPtr FlowAssetEditor, TSharedPtr Root); + + /** Called when user double clicks on the search item */ + FReply OnDoubleClick(TSharedPtr Root); + + /** Create an icon to represent the result */ + TSharedRef CreateIcon() const; + + /** Gets the description on flow node if any */ + FString GetDescriptionText() const; + + /** Gets the comment on this node if any */ + FString GetCommentText() const; + + /** Gets the node type */ + FString GetNodeTypeText() const; + + /** Gets the node tool tip */ + FText GetToolTipText() const; + + /** Any children listed under this flow node (decorators and services) */ + TArray< TSharedPtr > Children; + + /** The string value for this result */ + FString Value; + + /** The graph node that this search result refers to */ + TWeakObjectPtr GraphNode; + + /** Search result parent */ + TWeakPtr Parent; + + /** Whether this item is a subgraph node */ + bool bIsSubGraphNode = false; +}; + +/** Widget for searching for (Flow nodes) across focused FlowNodes */ +class SFindInFlow : public SCompoundWidget +{ +public: + SLATE_BEGIN_ARGS(SFindInFlow){} + SLATE_END_ARGS() + + void Construct(const FArguments& InArgs, TSharedPtr InFlowAssetEditor); + + /** Focuses this widget's search box */ + void FocusForUse() const; + +private: + typedef TSharedPtr FSearchResult; + typedef STreeView STreeViewType; + + /** Called when user changes the text they are searching for */ + void OnSearchTextChanged(const FText& Text); + + /** Called when user commits text */ + void OnSearchTextCommitted(const FText& Text, ETextCommit::Type CommitType); + + /** Get the children of a row */ + void OnGetChildren(FSearchResult InItem, TArray& OutChildren); + + /** Called when user clicks on a new result */ + void OnTreeSelectionChanged(FSearchResult Item, ESelectInfo::Type SelectInfo); + + /* Called when user double clicks on a new result */ + void OnTreeSelectionDoubleClicked( FSearchResult Item ); + + /** Called when whether find in sub graph changed */ + void OnFindInSubGraphStateChanged(ECheckBoxState CheckBoxState); + + /** Called when a new row is being generated */ + TSharedRef OnGenerateRow(FSearchResult InItem, const TSharedRef& OwnerTable); + + /** Begins the search based on the SearchValue */ + void InitiateSearch(); + + /** Find any results that contain all of the tokens */ + void MatchTokens(const TArray& Tokens); + + /** Find if child contains all of the tokens and add a result accordingly */ + static void MatchTokensInChild(const TArray& Tokens, UEdGraphNode* Child, FSearchResult ParentNode); + + /** Determines if a string matches the search tokens */ + static bool StringMatchesSearchTokens(const TArray& Tokens, const FString& ComparisonString); + +private: + /** Pointer back to the flow editor that owns us */ + TWeakPtr FlowAssetEditorPtr; + + /** The tree view displays the results */ + TSharedPtr TreeView; + + /** The search text box */ + TSharedPtr SearchTextField; + + /** This buffer stores the currently displayed results */ + TArray ItemsFound; + + /** we need to keep a handle on the root result, because it won't show up in the tree */ + FSearchResult RootSearchResult; + + /** The string to highlight in the results */ + FText HighlightText; + + /** The string to search for */ + FString SearchValue; + + /** Using to control whether search in sub graph */ + bool bFindInSubGraph = false; +}; diff --git a/Source/FlowEditor/Public/FlowEditorDefines.h b/Source/FlowEditor/Public/FlowEditorDefines.h index d035e3cad..00fb15e43 100644 --- a/Source/FlowEditor/Public/FlowEditorDefines.h +++ b/Source/FlowEditor/Public/FlowEditorDefines.h @@ -9,6 +9,7 @@ #define ENABLE_JUMP_TO_INNER_OBJECT 0 /** + * When false, use custom SWidget to complete the search logic (cf.SFindInFlow) * Documentation: https://github.com/MothCocoon/FlowGraph/wiki/Asset-Search * Set macro value to 1, if you made these changes to the engine: https://github.com/EpicGames/UnrealEngine/pull/9943 */ From 8d32e76bd6cae9074f326c1c2e9b824ca03abb0e Mon Sep 17 00:00:00 2001 From: MaksymKapelianovych <48297221+MaksymKapelianovych@users.noreply.github.com> Date: Thu, 15 Aug 2024 16:54:54 +0300 Subject: [PATCH 256/485] Change FlowEditor loading phase to "PreDefault" to fix corrupting FlowAssets when DefaultPawnClass for GameMode is set in C++ (as in Epic's templates) (#221) --- Flow.uplugin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.uplugin b/Flow.uplugin index b980e5120..961aaa445 100644 --- a/Flow.uplugin +++ b/Flow.uplugin @@ -22,7 +22,7 @@ { "Name" : "FlowEditor", "Type" : "Editor", - "LoadingPhase" : "Default" + "LoadingPhase" : "PreDefault" } ], "Plugins": [ From 26f70ab1efee984a17ef051fffc5c440f5051afb Mon Sep 17 00:00:00 2001 From: SPontadit Date: Thu, 15 Aug 2024 15:55:13 +0200 Subject: [PATCH 257/485] Replaced the monolithic header include (PropertyEditing.h) by the corresponding files (#216) --- .../Private/DetailCustomizations/FlowAssetDetails.cpp | 2 +- .../DetailCustomizations/FlowNode_CustomEventBaseDetails.cpp | 2 +- .../Private/DetailCustomizations/FlowNode_Details.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowAssetDetails.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowAssetDetails.cpp index 41e6b8861..bcd475264 100644 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowAssetDetails.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowAssetDetails.cpp @@ -5,8 +5,8 @@ #include "Nodes/Route/FlowNode_SubGraph.h" #include "DetailLayoutBuilder.h" +#include "IDetailChildrenBuilder.h" #include "PropertyCustomizationHelpers.h" -#include "PropertyEditing.h" #include "Widgets/Input/SEditableTextBox.h" #define LOCTEXT_NAMESPACE "FlowAssetDetails" diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomEventBaseDetails.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomEventBaseDetails.cpp index b4ffa4bec..77295f743 100644 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomEventBaseDetails.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomEventBaseDetails.cpp @@ -5,8 +5,8 @@ #include "Nodes/Route/FlowNode_CustomEventBase.h" #include "DetailCategoryBuilder.h" +#include "DetailLayoutBuilder.h" #include "DetailWidgetRow.h" -#include "PropertyEditing.h" #include "Widgets/Input/SComboBox.h" #include "Widgets/Text/STextBlock.h" #include "Widgets/SWidget.h" diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowNode_Details.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_Details.cpp index 98074032c..8570fc0f1 100644 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowNode_Details.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_Details.cpp @@ -1,7 +1,7 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "DetailCustomizations/FlowNode_Details.h" -#include "PropertyEditing.h" +#include "DetailLayoutBuilder.h" void FFlowNode_Details::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) { From 2a900dcec61a9edcac497a7758b76b632fde9b6a Mon Sep 17 00:00:00 2001 From: FoolsTheoryDev Date: Thu, 15 Aug 2024 17:05:54 +0200 Subject: [PATCH 258/485] fixed compilation against Flow AddOn refactor, fixed copyright notice --- Source/FlowEditor/Private/Find/FindInFlow.cpp | 47 ++++++++++--------- Source/FlowEditor/Public/Find/FindInFlow.h | 4 +- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/Source/FlowEditor/Private/Find/FindInFlow.cpp b/Source/FlowEditor/Private/Find/FindInFlow.cpp index 3687b1af0..527a025c6 100644 --- a/Source/FlowEditor/Private/Find/FindInFlow.cpp +++ b/Source/FlowEditor/Private/Find/FindInFlow.cpp @@ -1,10 +1,16 @@ -// Copyright Epic Games, Inc. All Rights Reserved. +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + #include "Find/FindInFlow.h" +#include "Asset/FlowAssetEditor.h" +#include "Graph/FlowGraphEditor.h" +#include "Graph/FlowGraphUtils.h" +#include "Graph/Nodes/FlowGraphNode.h" #include "FlowAsset.h" -#include "Asset/FlowAssetEditor.h" +#include "Nodes/FlowNode.h" +#include "Nodes/Route/FlowNode_SubGraph.h" + #include "EdGraph/EdGraph.h" -#include "Graph/FlowGraphEditor.h" #include "EdGraph/EdGraphNode.h" #include "Framework/Application/SlateApplication.h" #include "Framework/Views/ITypedTableView.h" @@ -17,10 +23,6 @@ #include "Math/Color.h" #include "Misc/Attribute.h" #include "SlotBase.h" -#include "Graph/FlowGraphUtils.h" -#include "Graph/Nodes/FlowGraphNode.h" -#include "Nodes/FlowNode.h" -#include "Nodes/Route/FlowNode_SubGraph.h" #include "Styling/AppStyle.h" #include "Styling/SlateColor.h" #include "Templates/Casts.h" @@ -81,26 +83,29 @@ FReply FFindInFlowResult::OnClick(TWeakPtr FlowAssetEdit return FReply::Handled(); } -FReply FFindInFlowResult::OnDoubleClick(TSharedPtr Root) +FReply FFindInFlowResult::OnDoubleClick(TSharedPtr Root) const { if (!Parent.IsValid() || !bIsSubGraphNode) { return FReply::Handled(); } const UFlowGraphNode* ParentGraphNode = Cast(Parent.Pin()->GraphNode); - if (!ParentGraphNode || !ParentGraphNode->GetFlowNode()) + if (!ParentGraphNode || !ParentGraphNode->GetFlowNodeBase()) { return FReply::Handled(); } - - if (UObject* AssetToEdit = ParentGraphNode->GetFlowNode()->GetAssetToEdit()) + + if (UFlowNode* FlowNode = Cast(ParentGraphNode->GetFlowNodeBase())) { - UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem(); - if (AssetEditorSubsystem->OpenEditorForAsset(AssetToEdit)) + if (UObject* AssetToEdit = FlowNode->GetAssetToEdit()) { - if (const TSharedPtr FlowAssetEditor = FFlowGraphUtils::GetFlowAssetEditor(GraphNode->GetGraph())) + UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem(); + if (AssetEditorSubsystem->OpenEditorForAsset(AssetToEdit)) { - FlowAssetEditor->JumpToNode(GraphNode.Get()); + if (const TSharedPtr FlowAssetEditor = FFlowGraphUtils::GetFlowAssetEditor(GraphNode->GetGraph())) + { + FlowAssetEditor->JumpToNode(GraphNode.Get()); + } } } } @@ -134,9 +139,9 @@ FString FFindInFlowResult::GetNodeTypeText() const { FString NodeClassName; const UFlowGraphNode* FlowGraphNode = Cast(GraphNode.Get()); - if (FlowGraphNode && FlowGraphNode->GetFlowNode()) + if (FlowGraphNode && FlowGraphNode->GetFlowNodeBase()) { - NodeClassName = FlowGraphNode->GetFlowNode()->GetClass()->GetName(); + NodeClassName = FlowGraphNode->GetFlowNodeBase()->GetClass()->GetName(); } else { @@ -279,12 +284,12 @@ void SFindInFlow::InitiateSearch() void SFindInFlow::MatchTokens(const TArray& Tokens) { RootSearchResult.Reset(); - - const TWeakPtr FocusedGraphEditor = FlowAssetEditorPtr.Pin()->GetFlowGraph(); + const UEdGraph* Graph = nullptr; + const TSharedPtr FocusedGraphEditor = FlowAssetEditorPtr.Pin()->GetFlowGraph(); if (FocusedGraphEditor.IsValid()) { - Graph = FocusedGraphEditor.Pin()->GetCurrentGraph(); + Graph = FocusedGraphEditor->GetCurrentGraph(); } if (Graph == nullptr) @@ -307,7 +312,7 @@ void SFindInFlow::MatchTokens(const TArray& Tokens) FString NodeDescription = FlowGraphNode->GetNodeDescription(); NodeSearchString += NodeDescription; - UFlowNode_SubGraph* SubGraphNode = Cast(FlowGraphNode->GetFlowNode()); + UFlowNode_SubGraph* SubGraphNode = Cast(FlowGraphNode->GetFlowNodeBase()); if (bFindInSubGraph && SubGraphNode) { if (const UFlowAsset* FlowAsset = Cast(SubGraphNode->GetAssetToEdit()); FlowAsset && FlowAsset->GetGraph()) diff --git a/Source/FlowEditor/Public/Find/FindInFlow.h b/Source/FlowEditor/Public/Find/FindInFlow.h index 962cb4b22..848bcff4d 100644 --- a/Source/FlowEditor/Public/Find/FindInFlow.h +++ b/Source/FlowEditor/Public/Find/FindInFlow.h @@ -1,4 +1,4 @@ -// Copyright Epic Games, Inc. All Rights Reserved. +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #pragma once @@ -42,7 +42,7 @@ class FFindInFlowResult FReply OnClick(TWeakPtr FlowAssetEditor, TSharedPtr Root); /** Called when user double clicks on the search item */ - FReply OnDoubleClick(TSharedPtr Root); + FReply OnDoubleClick(TSharedPtr Root) const; /** Create an icon to represent the result */ TSharedRef CreateIcon() const; From 31a51fba3b7557d928dce90b3935f358bdb7135d Mon Sep 17 00:00:00 2001 From: FoolsTheoryDev Date: Thu, 15 Aug 2024 17:05:30 +0200 Subject: [PATCH 259/485] removing monolithic header, follow-up to recent pull request --- .../Private/DetailCustomizations/FlowNodeAddOn_Details.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowNodeAddOn_Details.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowNodeAddOn_Details.cpp index 673a7ac2f..d57a94587 100644 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowNodeAddOn_Details.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowNodeAddOn_Details.cpp @@ -1,7 +1,7 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "DetailCustomizations/FlowNodeAddOn_Details.h" -#include "PropertyEditing.h" +#include "DetailLayoutBuilder.h" void FFlowNodeAddOn_Details::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) { From b9da31f7a363430de8b48e2206e6e38104ccac07 Mon Sep 17 00:00:00 2001 From: MothDoctor Date: Thu, 15 Aug 2024 20:31:21 +0200 Subject: [PATCH 260/485] static analysis fixes --- .../Private/Graph/FlowGraphEditor.cpp | 17 ++++------------- .../FlowEditor/Public/Graph/FlowGraphEditor.h | 3 --- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index be5791331..9bc264ecd 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -2,18 +2,18 @@ #include "Graph/FlowGraphEditor.h" -#include "FlowEditorCommands.h" -#include "FlowEditorModule.h" - #include "Asset/FlowAssetEditor.h" #include "Asset/FlowDebuggerSubsystem.h" +#include "FlowEditorCommands.h" #include "Graph/FlowGraphEditorSettings.h" #include "Graph/FlowGraphSchema_Actions.h" #include "Graph/Nodes/FlowGraphNode.h" #include "Nodes/Route/FlowNode_SubGraph.h" +#include "EdGraphUtilities.h" #include "Framework/Application/SlateApplication.h" #include "Framework/Commands/GenericCommands.h" +#include "GraphEditorActions.h" #include "HAL/PlatformApplicationMisc.h" #include "IDetailsView.h" #include "LevelEditor.h" @@ -644,14 +644,11 @@ void SFlowGraphEditor::PasteNodesHere(const FVector2D& Location) FlowGraph->LockUpdates(); UFlowGraphNode* SelectedParent = nullptr; - bool bHasMultipleNodesSelected = false; const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes(); for (FGraphPanelSelectionSet::TConstIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter) { - UFlowGraphNode* Node = Cast(*SelectedIter); - - if (Node) + if (UFlowGraphNode* Node = Cast(*SelectedIter)) { if (SelectedParent == nullptr) { @@ -659,8 +656,6 @@ void SFlowGraphEditor::PasteNodesHere(const FVector2D& Location) } else { - bHasMultipleNodesSelected = true; - break; } } @@ -703,8 +698,6 @@ void SFlowGraphEditor::PasteNodesHere(const FVector2D& Location) AvgNodePosition.Y *= InvNumNodes; } - bool bPastedParentNode = false; - TMap EdNodeCopyIndexMap; for (TSet::TIterator It(PastedNodes); It; ++It) { @@ -715,8 +708,6 @@ void SFlowGraphEditor::PasteNodesHere(const FVector2D& Location) if (PasteNode && (PasteFlowGraphNode == nullptr || !PasteFlowGraphNode->IsSubNode())) { - bPastedParentNode = true; - // Select the newly pasted stuff constexpr bool bSelectNodes = true; SetNodeSelection(PasteNode, bSelectNodes); diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h index fa873ce3b..24881c1de 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h @@ -2,10 +2,7 @@ #pragma once -#include "EdGraphUtilities.h" #include "GraphEditor.h" -#include "GraphEditorActions.h" -#include "SGraphNode.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "FlowGraph.h" From 2276710c2ff9eb62bdb2a3b9e2fd3ceb421d5bf5 Mon Sep 17 00:00:00 2001 From: Bigotry0 <43312927+Bigotry0@users.noreply.github.com> Date: Tue, 20 Aug 2024 04:49:43 +0800 Subject: [PATCH 261/485] Fix compile failure due to default C++ standard below 20 (#223) --- Source/FlowEditor/FlowEditor.Build.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/FlowEditor/FlowEditor.Build.cs b/Source/FlowEditor/FlowEditor.Build.cs index 4dff32d7c..13a7e48a4 100644 --- a/Source/FlowEditor/FlowEditor.Build.cs +++ b/Source/FlowEditor/FlowEditor.Build.cs @@ -6,6 +6,11 @@ public class FlowEditor : ModuleRules { public FlowEditor(ReadOnlyTargetRules target) : base(target) { + if(CppStandard is null || CppStandard != CppStandardVersion.Cpp20) + { + CppStandard = CppStandardVersion.Cpp20; + } + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; PublicDependencyModuleNames.AddRange(new[] From ad5e0c223a998edebe530ce22c8d36b1bb88c508 Mon Sep 17 00:00:00 2001 From: Nikolay Slobodin Date: Mon, 19 Aug 2024 23:50:00 +0300 Subject: [PATCH 262/485] Fix compilation for iOS (#224) * Add forward declaration of UFlowGraphSchema, otherwise it wont compile on mac (clang) * Remove wrong backslashes in #include --- Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp | 4 ++-- Source/FlowEditor/Public/Graph/FlowGraph.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp b/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp index 6845ae064..8b80b207f 100644 --- a/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp +++ b/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp @@ -6,7 +6,7 @@ #include "Nodes/FlowNodeBase.h" #include "EdGraph/EdGraph.h" -#include "Graph\Nodes\FlowGraphNode.h" +#include "Graph/Nodes/FlowGraphNode.h" #include "SBlueprintDiff.h" #include "DiffResults.h" @@ -84,4 +84,4 @@ void FFlowObjectDiff::OnSelectDiff(const FSingleObjectDiffEntry& Property) const { NewDetailsView->HighlightProperty(Property.Identifier); } -} \ No newline at end of file +} diff --git a/Source/FlowEditor/Public/Graph/FlowGraph.h b/Source/FlowEditor/Public/Graph/FlowGraph.h index 78c857d7d..3683531ed 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraph.h +++ b/Source/FlowEditor/Public/Graph/FlowGraph.h @@ -9,6 +9,7 @@ class SFlowGraphEditor; class UFlowGraphNode; +class UFlowGraphSchema; class FLOWEDITOR_API FFlowGraphInterface : public IFlowGraphInterface { From 4cef454a71f0e6448aa5ba7111ea6b964139c145 Mon Sep 17 00:00:00 2001 From: SPontadit Date: Sun, 25 Aug 2024 15:50:42 +0200 Subject: [PATCH 263/485] Fix Diff menu not using the FlowAsset of the current editor when several flow asset editors are opened (#225) --- .../Private/Asset/FlowAssetToolbar.cpp | 32 +++++++++++-------- .../Public/Asset/FlowAssetToolbar.h | 3 +- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp index 90c86cdc6..5d275e26c 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp @@ -210,19 +210,23 @@ void FFlowAssetToolbar::BuildAssetToolbar(UToolMenu* ToolbarMenu) const Section.InsertPosition = FToolMenuInsert("FlowAsset", EToolMenuInsertType::After); // Visual Diff: menu to choose asset revision compared with the current one - Section.AddDynamicEntry("SourceControlCommands", FNewToolMenuSectionDelegate::CreateLambda([this](FToolMenuSection& InSection) + Section.AddDynamicEntry("SourceControlCommands", FNewToolMenuSectionDelegate::CreateLambda([](FToolMenuSection& InSection) { - InSection.InsertPosition = FToolMenuInsert(); - FToolMenuEntry DiffEntry = FToolMenuEntry::InitComboButton( - "Diff", - FUIAction(), - FOnGetContent::CreateRaw(this, &FFlowAssetToolbar::MakeDiffMenu), - LOCTEXT("Diff", "Diff"), - LOCTEXT("FlowAssetEditorDiffToolTip", "Diff against previous revisions"), - FSlateIcon(FAppStyle::Get().GetStyleSetName(), "BlueprintDiff.ToolbarIcon") - ); - DiffEntry.StyleNameOverride = "CalloutToolbar"; - InSection.AddEntry(DiffEntry); + const UFlowAssetEditorContext* Context = InSection.FindContext(); + if (Context && Context->FlowAssetEditor.IsValid()) + { + InSection.InsertPosition = FToolMenuInsert(); + FToolMenuEntry DiffEntry = FToolMenuEntry::InitComboButton( + "Diff", + FUIAction(), + FOnGetContent::CreateStatic(&FFlowAssetToolbar::MakeDiffMenu, Context), + LOCTEXT("Diff", "Diff"), + LOCTEXT("FlowAssetEditorDiffToolTip", "Diff against previous revisions"), + FSlateIcon(FAppStyle::Get().GetStyleSetName(), "BlueprintDiff.ToolbarIcon") + ); + DiffEntry.StyleNameOverride = "CalloutToolbar"; + InSection.AddEntry(DiffEntry); + } })); Section.AddEntry(FToolMenuEntry::InitToolBarButton( @@ -280,11 +284,11 @@ static void OnDiffRevisionPicked(FRevisionInfo const& RevisionInfo, const FStrin } // Variant of FBlueprintEditorToolbar::MakeDiffMenu -TSharedRef FFlowAssetToolbar::MakeDiffMenu() const +TSharedRef FFlowAssetToolbar::MakeDiffMenu(const UFlowAssetEditorContext* Context) { if (ISourceControlModule::Get().IsEnabled() && ISourceControlModule::Get().GetProvider().IsAvailable()) { - UFlowAsset* FlowAsset = FlowAssetEditor.Pin()->GetFlowAsset(); + UFlowAsset* FlowAsset = Context ? Context->FlowAssetEditor.Pin()->GetFlowAsset() : nullptr; if (FlowAsset) { FString Filename = SourceControlHelpers::PackageFilename(FlowAsset->GetPathName()); diff --git a/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h b/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h index 09198bcf2..0a867e672 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h @@ -8,6 +8,7 @@ #include "FlowAsset.h" class FFlowAssetEditor; +class UFlowAssetEditorContext; class UToolMenu; ////////////////////////////////////////////////////////////////////////// @@ -87,7 +88,7 @@ class FLOWEDITOR_API FFlowAssetToolbar : public TSharedFromThis MakeDiffMenu() const; + static TSharedRef MakeDiffMenu(const UFlowAssetEditorContext* InContext); void BuildDebuggerToolbar(UToolMenu* ToolbarMenu) const; From a5d63add6e5cbeeb8e18b991907cee5e50bf9f0c Mon Sep 17 00:00:00 2001 From: Dylan Dumesnil Date: Sun, 25 Aug 2024 06:50:54 -0700 Subject: [PATCH 264/485] Fix short type name warnings (#226) ``` Warning LogClass Property StructProperty UFlowNode_ExecuteComponent::ComponentRef defines MetaData key "MustImplement" which contains short type name "FlowCoreExecutableInterface". Suggested pathname: "/Script/Flow.FlowCoreExecutableInterface". Module:Flow File:Public/Nodes/World/FlowNode_ExecuteComponent.h Warning LogClass Property StructProperty UFlowNode_ExecuteComponent::ComponentRef defines MetaData key "MustImplement" which contains short type name "FlowExternalExecutableInterface". Suggested pathname: "/Script/Flow.FlowExternalExecutableInterface". Module:Flow File:Public/Nodes/World/FlowNode_ExecuteComponent.h Warning LogClass Property ObjectProperty UFlowNode_ExecuteComponent::ComponentTemplate defines MetaData key "MustImplement" which contains short type name "FlowCoreExecutableInterface". Suggested pathname: "/Script/Flow.FlowCoreExecutableInterface". Module:Flow File:Public/Nodes/World/FlowNode_ExecuteComponent.h Warning LogClass Property ObjectProperty UFlowNode_ExecuteComponent::ComponentTemplate defines MetaData key "MustImplement" which contains short type name "FlowExternalExecutableInterface". Suggested pathname: "/Script/Flow.FlowExternalExecutableInterface". Module:Flow File:Public/Nodes/World/FlowNode_ExecuteComponent.h Warning LogClass Property ClassProperty UFlowNode_ExecuteComponent::ComponentClass defines MetaData key "MustImplement" which contains short type name "FlowCoreExecutableInterface". Suggested pathname: "/Script/Flow.FlowCoreExecutableInterface". Module:Flow File:Public/Nodes/World/FlowNode_ExecuteComponent.h Warning LogClass Property ClassProperty UFlowNode_ExecuteComponent::ComponentClass defines MetaData key "MustImplement" which contains short type name "FlowExternalExecutableInterface". Suggested pathname: "/Script/Flow.FlowExternalExecutableInterface". Module:Flow File:Public/Nodes/World/FlowNode_ExecuteComponent.h ``` --- Source/Flow/Public/Nodes/World/FlowNode_ExecuteComponent.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Flow/Public/Nodes/World/FlowNode_ExecuteComponent.h b/Source/Flow/Public/Nodes/World/FlowNode_ExecuteComponent.h index a50d20105..7e2f83104 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_ExecuteComponent.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_ExecuteComponent.h @@ -92,15 +92,15 @@ class FLOW_API UFlowNode_ExecuteComponent : public UFlowNode // Executable Component (by name) on the expected Flow owning Actor // (the component must implement the IFlowExecutableComponentInterface) - UPROPERTY(EditAnywhere, Category = "Flow Executable Component", meta = (DisplayName = "Component to Execute", MustImplement = "FlowCoreExecutableInterface,FlowExternalExecutableInterface", EditConditionHides, EditCondition = "ComponentSource == EExecuteComponentSource::BindToExisting || ComponentSource == EExecuteComponentSource::Undetermined")) + UPROPERTY(EditAnywhere, Category = "Flow Executable Component", meta = (DisplayName = "Component to Execute", MustImplement = "/Script/Flow.FlowCoreExecutableInterface,/Script/Flow.FlowExternalExecutableInterface", EditConditionHides, EditCondition = "ComponentSource == EExecuteComponentSource::BindToExisting || ComponentSource == EExecuteComponentSource::Undetermined")) FFlowActorOwnerComponentRef ComponentRef; // Component (template) to inject on the spawned actor, may be configured inline - UPROPERTY(EditAnywhere, Instanced, Category = Configuration, DisplayName = "Inject & Execute Component (from Template)", meta = (MustImplement = "FlowCoreExecutableInterface,FlowExternalExecutableInterface", EditConditionHides, EditCondition = "ComponentSource == EExecuteComponentSource::InjectFromTemplate || ComponentSource == EExecuteComponentSource::Undetermined")) + UPROPERTY(EditAnywhere, Instanced, Category = Configuration, DisplayName = "Inject & Execute Component (from Template)", meta = (MustImplement = "/Script/Flow.FlowCoreExecutableInterface,/Script/Flow.FlowExternalExecutableInterface", EditConditionHides, EditCondition = "ComponentSource == EExecuteComponentSource::InjectFromTemplate || ComponentSource == EExecuteComponentSource::Undetermined")) TObjectPtr ComponentTemplate = nullptr; // Component (class) to inject on the spawned actor - UPROPERTY(EditAnywhere, Category = Configuration, DisplayName = "Inject & Execute Component (by Class)", meta = (MustImplement = "FlowCoreExecutableInterface,FlowExternalExecutableInterface", EditConditionHides, EditCondition = "ComponentSource == EExecuteComponentSource::InjectFromClass || ComponentSource == EExecuteComponentSource::Undetermined")) + UPROPERTY(EditAnywhere, Category = Configuration, DisplayName = "Inject & Execute Component (by Class)", meta = (MustImplement = "/Script/Flow.FlowCoreExecutableInterface,/Script/Flow.FlowExternalExecutableInterface", EditConditionHides, EditCondition = "ComponentSource == EExecuteComponentSource::InjectFromClass || ComponentSource == EExecuteComponentSource::Undetermined")) TSubclassOf ComponentClass = nullptr; // Manager object to inject and remove components from the Flow owning Actor From 2f1e48cfc2dcd2254e2bded5fad17301af939fc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Do=C4=9Fa=20Can=20Yan=C4=B1ko=C4=9Flu?= Date: Tue, 27 Aug 2024 21:20:15 +0300 Subject: [PATCH 265/485] Make it possible to pass InstanceName to CreateRootFlow. Useful for custom game save/load workflows (#228) --- Source/Flow/Private/FlowSubsystem.cpp | 4 ++-- Source/Flow/Public/FlowSubsystem.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index a9ea955f2..5026bd5b7 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -94,7 +94,7 @@ void UFlowSubsystem::StartRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const #endif } -UFlowAsset* UFlowSubsystem::CreateRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const bool bAllowMultipleInstances) +UFlowAsset* UFlowSubsystem::CreateRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const bool bAllowMultipleInstances, FString NewInstanceName) { for (const TPair>& RootInstance : RootInstances) { @@ -111,7 +111,7 @@ UFlowAsset* UFlowSubsystem::CreateRootFlow(UObject* Owner, UFlowAsset* FlowAsset return nullptr; } - UFlowAsset* NewFlow = CreateFlowInstance(Owner, FlowAsset); + UFlowAsset* NewFlow = CreateFlowInstance(Owner, FlowAsset, NewInstanceName); if (NewFlow) { RootInstances.Add(NewFlow, Owner); diff --git a/Source/Flow/Public/FlowSubsystem.h b/Source/Flow/Public/FlowSubsystem.h index 4633eef40..d60a98178 100644 --- a/Source/Flow/Public/FlowSubsystem.h +++ b/Source/Flow/Public/FlowSubsystem.h @@ -75,7 +75,7 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem UFUNCTION(BlueprintCallable, Category = "FlowSubsystem", meta = (DefaultToSelf = "Owner")) virtual void StartRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const bool bAllowMultipleInstances = true); - virtual UFlowAsset* CreateRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const bool bAllowMultipleInstances = true); + virtual UFlowAsset* CreateRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const bool bAllowMultipleInstances = true, FString NewInstanceName = FString()); /* Finish Policy value is read by Flow Node * Nodes have opportunity to terminate themselves differently if Flow Graph has been aborted From c84df086bbcee7f97182a3cab29db699406df334 Mon Sep 17 00:00:00 2001 From: Dimy <43338144+Joschuka@users.noreply.github.com> Date: Tue, 27 Aug 2024 20:27:50 +0200 Subject: [PATCH 266/485] Update FlowAsset.cpp (#222) --- Source/Flow/Private/FlowAsset.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 4e3805994..c23c19cbf 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -778,7 +778,17 @@ void UFlowAsset::FinishNode(UFlowNode* Node) } else { - FinishFlow(EFlowFinishPolicy::Keep); + //If this is a root instance finish root flows else finish locally + TSet RootFlowInstances = GetFlowSubsystem()->GetRootInstancesByOwner(Owner.Get()); + + if (RootFlowInstances.Contains(this)) + { + GetFlowSubsystem()->FinishRootFlow(Owner.Get(), TemplateAsset, EFlowFinishPolicy::Keep); + } + else + { + FinishFlow(EFlowFinishPolicy::Keep); + } } } } From 182b2cbb22526293520299dd17c431e24d28dd95 Mon Sep 17 00:00:00 2001 From: Moth Doctor Date: Tue, 27 Aug 2024 20:56:28 +0200 Subject: [PATCH 267/485] added nullcheck to the PR --- Source/Flow/Private/FlowAsset.cpp | 34 ++++++++++++++++--------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index c23c19cbf..b5eb88750 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -85,7 +85,7 @@ void UFlowAsset::PostLoad() // If we removed or moved a flow node blueprint (and there is no redirector) we might loose the reference to it resulting // in null pointers in the Nodes FGUID->UFlowNode* Map. So here we iterate over all the Nodes and remove all pairs that // are nulled out. - + TSet NodesToRemoveGUID; for (auto& [Guid, Node] : GetNodes()) @@ -112,14 +112,14 @@ EDataValidationResult UFlowAsset::ValidateAsset(FFlowMessageLog& MessageLog) FText FailureReason; if (!IsNodeOrAddOnClassAllowed(Node.Value->GetClass(), &FailureReason)) { - const FString ErrorMsg = - FailureReason.IsEmpty() ? - FString::Format(*ValidationError_NodeClassNotAllowed, {*Node.Value->GetClass()->GetName()}) : - FailureReason.ToString(); + const FString ErrorMsg = + FailureReason.IsEmpty() + ? FString::Format(*ValidationError_NodeClassNotAllowed, {*Node.Value->GetClass()->GetName()}) + : FailureReason.ToString(); MessageLog.Error(*ErrorMsg, Node.Value); } - + Node.Value->ValidationLog.Messages.Empty(); if (Node.Value->ValidateNode() == EDataValidationResult::Invalid) { @@ -510,7 +510,7 @@ TArray UFlowAsset::GetNodesInExecutionOrder(UFlowNode* FirstIterated } } FoundNodes.Shrink(); - + return FoundNodes; } @@ -775,21 +775,23 @@ void UFlowAsset::FinishNode(UFlowNode* Node) if (NodeOwningThisAssetInstance.IsValid()) { NodeOwningThisAssetInstance.Get()->TriggerFirstOutput(true); + return; } else { - //If this is a root instance finish root flows else finish locally - TSet RootFlowInstances = GetFlowSubsystem()->GetRootInstancesByOwner(Owner.Get()); - - if (RootFlowInstances.Contains(this)) + // if this instance is a Root Flow, we need to deregister it from the subsystem first + if (Owner.IsValid()) { - GetFlowSubsystem()->FinishRootFlow(Owner.Get(), TemplateAsset, EFlowFinishPolicy::Keep); - } - else - { - FinishFlow(EFlowFinishPolicy::Keep); + const TSet& RootFlowInstances = GetFlowSubsystem()->GetRootInstancesByOwner(Owner.Get()); + if (RootFlowInstances.Contains(this)) + { + GetFlowSubsystem()->FinishRootFlow(Owner.Get(), TemplateAsset, EFlowFinishPolicy::Keep); + return; + } } } + + FinishFlow(EFlowFinishPolicy::Keep); } } } From c1809717c5fdda8104bf2ba02b5eb96f92cbb12e Mon Sep 17 00:00:00 2001 From: Moth Doctor Date: Tue, 27 Aug 2024 21:01:23 +0200 Subject: [PATCH 268/485] fixed Discord link --- Flow.uplugin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.uplugin b/Flow.uplugin index 961aaa445..09b6def56 100644 --- a/Flow.uplugin +++ b/Flow.uplugin @@ -7,7 +7,7 @@ "CreatedByURL" : "https://github.com/MothCocoon/FlowGraph/graphs/contributors", "DocsURL" : "https://github.com/MothCocoon/FlowGraph/wiki", "MarketplaceURL" : "", - "SupportURL": "https://discord.gg/zMtMQ2vUUa", + "SupportURL": "https://discord.gg/Xmtr6GhbmW", "EnabledByDefault" : true, "CanContainContent" : false, "IsBetaVersion" : false, From cc0ea4153ef21f11d99b560eaafad8323f084985 Mon Sep 17 00:00:00 2001 From: Moth Doctor Date: Sat, 14 Sep 2024 11:46:18 +0200 Subject: [PATCH 269/485] operation on instanced templated now public --- Source/Flow/Public/FlowSubsystem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Flow/Public/FlowSubsystem.h b/Source/Flow/Public/FlowSubsystem.h index d60a98178..fd338cb7f 100644 --- a/Source/Flow/Public/FlowSubsystem.h +++ b/Source/Flow/Public/FlowSubsystem.h @@ -95,10 +95,10 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem UFlowAsset* CreateFlowInstance(const TWeakObjectPtr Owner, TSoftObjectPtr FlowAsset, FString NewInstanceName = FString()); +public: virtual void AddInstancedTemplate(UFlowAsset* Template); virtual void RemoveInstancedTemplate(UFlowAsset* Template); -public: /* Returns all assets instanced by object from another system like World Settings */ UFUNCTION(BlueprintPure, Category = "FlowSubsystem") TMap GetRootInstances() const; From 868bae94a9a2589c8fbef1029c8b3f333d885c47 Mon Sep 17 00:00:00 2001 From: Moth Doctor Date: Sat, 14 Sep 2024 11:46:32 +0200 Subject: [PATCH 270/485] typo fixed --- Source/Flow/Public/FlowComponent.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Flow/Public/FlowComponent.h b/Source/Flow/Public/FlowComponent.h index 0ebba3111..285465875 100644 --- a/Source/Flow/Public/FlowComponent.h +++ b/Source/Flow/Public/FlowComponent.h @@ -118,12 +118,12 @@ class FLOW_API UFlowComponent : public UActorComponent, public IFlowOwnerInterfa const FGameplayTagContainer& GetRecentlySentNotifyTags() const { return RecentlySentNotifyTags; } // Send single notification from the actor to Flow graphs - // If set on server, it always going to be replicated to clients + // If set on server, it's always going to be replicated to clients UFUNCTION(BlueprintCallable, Category = "Flow") void NotifyGraph(const FGameplayTag NotifyTag, const EFlowNetMode NetMode = EFlowNetMode::Authority); // Send multiple notifications at once - from the actor to Flow graphs - // If set on server, it always going to be replicated to clients + // If set on server, it's always going to be replicated to clients UFUNCTION(BlueprintCallable, Category = "Flow") void BulkNotifyGraph(const FGameplayTagContainer NotifyTags, const EFlowNetMode NetMode = EFlowNetMode::Authority); From 3c139e6f3eccbd01ad0f2960d3a33f957e777427 Mon Sep 17 00:00:00 2001 From: Moth Doctor Date: Sat, 14 Sep 2024 11:47:09 +0200 Subject: [PATCH 271/485] replaced class name with ThisClass --- Source/Flow/Private/FlowComponent.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Source/Flow/Private/FlowComponent.cpp b/Source/Flow/Private/FlowComponent.cpp index 62bc4de3a..34ed49761 100644 --- a/Source/Flow/Private/FlowComponent.cpp +++ b/Source/Flow/Private/FlowComponent.cpp @@ -39,19 +39,19 @@ void UFlowComponent::GetLifetimeReplicatedProps(TArray& OutLi FDoRepLifetimeParams Params; Params.bIsPushBased = true; - DOREPLIFETIME_WITH_PARAMS_FAST(UFlowComponent, AddedIdentityTags, Params); - DOREPLIFETIME_WITH_PARAMS_FAST(UFlowComponent, RemovedIdentityTags, Params); + DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, AddedIdentityTags, Params); + DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, RemovedIdentityTags, Params); - DOREPLIFETIME_WITH_PARAMS_FAST(UFlowComponent, RecentlySentNotifyTags, Params); - DOREPLIFETIME_WITH_PARAMS_FAST(UFlowComponent, NotifyTagsFromGraph, Params); - DOREPLIFETIME_WITH_PARAMS_FAST(UFlowComponent, NotifyTagsFromAnotherComponent, Params); + DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, RecentlySentNotifyTags, Params); + DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, NotifyTagsFromGraph, Params); + DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, NotifyTagsFromAnotherComponent, Params); #else - DOREPLIFETIME(UFlowComponent, AddedIdentityTags); - DOREPLIFETIME(UFlowComponent, RemovedIdentityTags); + DOREPLIFETIME(ThisClass, AddedIdentityTags); + DOREPLIFETIME(ThisClass, RemovedIdentityTags); - DOREPLIFETIME(UFlowComponent, RecentlySentNotifyTags); - DOREPLIFETIME(UFlowComponent, NotifyTagsFromGraph); - DOREPLIFETIME(UFlowComponent, NotifyTagsFromAnotherComponent); + DOREPLIFETIME(ThisClass, RecentlySentNotifyTags); + DOREPLIFETIME(ThisClass, NotifyTagsFromGraph); + DOREPLIFETIME(ThisClass, NotifyTagsFromAnotherComponent); #endif } @@ -294,7 +294,7 @@ void UFlowComponent::NotifyGraph(const FGameplayTag NotifyTag, const EFlowNetMod { if (IsFlowNetMode(NetMode) && NotifyTag.IsValid() && HasBegunPlay()) { - // save recently notify, this allow for the retroactive check in nodes + // save recently notify, this allows for the retroactive check in nodes // if retroactive check wouldn't be performed, this is only used by the network replication RecentlySentNotifyTags = FGameplayTagContainer(NotifyTag); #if WITH_PUSH_MODEL @@ -323,7 +323,7 @@ void UFlowComponent::BulkNotifyGraph(const FGameplayTagContainer NotifyTags, con if (ValidatedTags.Num() > 0) { - // save recently notify, this allow for the retroactive check in nodes + // save recently notify, this allows for the retroactive check in nodes // if retroactive check wouldn't be performed, this is only used by the network replication RecentlySentNotifyTags = ValidatedTags; #if WITH_PUSH_MODEL From 297de14b21bf136faf8023ceedea2a66f540a9a6 Mon Sep 17 00:00:00 2001 From: LindyHopperGT <91915878+LindyHopperGT@users.noreply.github.com> Date: Sun, 17 Nov 2024 11:13:50 -0800 Subject: [PATCH 272/485] Flow Data Pins feature (#231) * Flow AddOns Introduced FlowAddOns ~ Major rework of the Flow Editor to support AddOns ~ Also added ExecuteComponent flow node, which allows a component of the owning actor (if it implements a specific interface) to be executed via a proxy node in the Flow Graph. We use this to wrap some legacy systems in flow graph nodes. * Fix Linux compile errors and regressions on UFUNCTIONS * Fix bug with ExecuteInput for AddOns Nodes and AddOns could execute for pins they did not expect, if the AddOns introduced pins that the Node (or other AddOns) were not aware of. Also, removed some deprecation warnings because they were generating compile warnings, which could cause some projects problems with automated builds. I changed the deprecation message to a human-readable message only. * Slight adjustment to IsSupportedInputPinName() early return if the array is empty * AddOns Optimized and changed how Input/Outputs are added from AddOns * Quality of life improvements Revised the ForEachAddOn() signature, since it only ever needs to be used when iterating over all AddOns (recursively), as you can use a normal for loop over AddOns for just the AddOns of this object alone. Also moved some access functions from FlowNode to FlowNodeBase and exposed them to Blueprint, based on a question I saw on the Discord about Get Actor Owner access. * Extracted Component injection code into Flow base also updated ExecuteComponent to use it * Transferring flags from Template component to instance + Transient * Singular InjectComponent signature * Small revisions to Execute Component flownode * Fix uniqueness problem with component lookup * fixed GC-related compilation warning in AddReferencedObjects() * cosmetic pass on AddOns refactor - Adding Category to a few functions which fixes build machine compilation in the plugin mode. - Partially restoring class layout to its original shape, taking in account new sections, and improve it a little bit. - Reversing some changes on making symbols editor-only which might break non-editor tools working with Flow Graph. - Static analysis fixes. * Flow Diff improvements * Added AActor::GetActorClassDefaultComponents() alternative for UE < 5.3 * Folded IFlowNativeExecutableInterface into UFlowNodeBase * Fix compile error * Fix for Undo buffer for SetConfigText * Deleted file * deleted files * Flow Data Pins (initial version) Initial version for Flow Data Pins feature. * Removed Class/Object enum values (until we finish the feature) * Restored commented-out code (and unexpanded the macro) * Reverted last change. need to work on the macros more. * Fixed compilation problem with Enum macro * Fixed details customization for flow data pin input properties * Fixed problem detecting property to pin binding changes. It was only checking the sizes, not doing a deep comparison of the two maps. * Fix problem with data pins incorrectly breaking the wrong pin's connections * Fix erroneous return value where it could find suppliers, but reported that it couldn't * Reworked DataPins to use the authored property name for the lookup map Was using DisplayName, but UE doesn't provide a convenient lookup for the property DisplayName in non-editor builds, making for keeping the property name and the pin name in sync more cumbersome than I'd prefer. Using the Authored name instead allows us to use the GET_MEMBER_PROPERTY_NAME_CHECKED macro to keep the name in sync with the property w/static assert. * Added details customization for named data pin output property * Added InstancedStruct support for Flow Data Pins * Added FlowDataPinPropertyProviderInterface for the AI Flow plugin to use to integrate with data pins. * Status String support for AddOns and ForEachAddOn improvements AddOns can now include line(s) in the StatusString of their node ForEachAddOn functions can break early with 'success' or 'failure' * Added Flow Data Pins support for Rotator, Object, Class * Refined how Data Pins are auto-generated, graph and node updates in the editor * Disallow Reroute nodes for non-exec flow pin connections At least until we develop some data-pin-friendly solution, disabling the reroute node generation for flow data pin wires. * Wire colors match their pin color * Small refinements * Small bugfixes missing header includes calling Super:: Log an Timer support for assets that haven't been resaved with data pins to still source their data correctly. * Code review changes and crash fix crash fix for copy/pasting a flow node with addons. This crash would only happen in this PR (not the current flow mainline version) * Bugfix - Pasting & Drag/Dropping AddOns onto nodes now respects allowed child addon filtering * Updated signature of UFlowNodeAddOn::AcceptFlowNodeAddOnParent() to include additional proposed children This signature now affords the AddOn->Parent question the same information as the Parent->AddOn question gained in the previous change * Fix for BP compile errors for the default array reference for the new AdditionalAddOnsToAssumeAreChildren parameter * Fix crash when pasting nodes with addons into another graph The addons array is not fully-formed when called from some mid-paste functions. * Tag-based Node Display Style conversion hierarchical tag-indexed NodeDisplayStyle. - project extensible node styles - hierarchical node style definitions in INI * UE 5.5 compilation error fixes * Some fixes for Custom NodeDisplayStyle and 5.4 compilation defines --------- Co-authored-by: FoolsTheoryDev --- Flow.uplugin | 6 +- Source/Flow/Flow.Build.cs | 3 +- Source/Flow/Private/AddOns/FlowNodeAddOn.cpp | 27 +- .../AddOns/FlowNodeAddOn_PredicateAND.cpp | 6 +- .../AddOns/FlowNodeAddOn_PredicateNOT.cpp | 8 +- .../AddOns/FlowNodeAddOn_PredicateOR.cpp | 6 +- Source/Flow/Private/FlowAsset.cpp | 621 ++++++++++++++++- Source/Flow/Private/FlowSettings.cpp | 2 +- Source/Flow/Private/FlowSubsystem.cpp | 12 +- Source/Flow/Private/FlowTags.cpp | 21 + .../FlowContextPinSupplierInterface.cpp | 5 + .../DataPins/FlowNode_DefineProperties.cpp | 113 +++ Source/Flow/Private/Nodes/FlowNode.cpp | 448 +++++++++++- Source/Flow/Private/Nodes/FlowNodeBase.cpp | 658 ++++++++++++++++-- Source/Flow/Private/Nodes/FlowPin.cpp | 307 ++++++++ .../Nodes/Operators/FlowNode_LogicalAND.cpp | 2 +- .../Nodes/Operators/FlowNode_LogicalOR.cpp | 2 +- .../Private/Nodes/Route/FlowNode_Branch.cpp | 8 +- .../Private/Nodes/Route/FlowNode_Counter.cpp | 2 +- .../Nodes/Route/FlowNode_CustomEventBase.cpp | 2 +- .../Route/FlowNode_ExecutionMultiGate.cpp | 2 +- .../Route/FlowNode_ExecutionSequence.cpp | 2 +- .../Private/Nodes/Route/FlowNode_Finish.cpp | 2 +- .../Private/Nodes/Route/FlowNode_Start.cpp | 256 ++++++- .../Private/Nodes/Route/FlowNode_SubGraph.cpp | 85 ++- .../Private/Nodes/Route/FlowNode_Timer.cpp | 97 ++- .../Flow/Private/Nodes/Utils/FlowNode_Log.cpp | 50 +- .../World/FlowNode_CallOwnerFunction.cpp | 2 +- .../World/FlowNode_ComponentObserver.cpp | 2 +- .../Nodes/World/FlowNode_ExecuteComponent.cpp | 14 +- .../World/FlowNode_OnNotifyFromActor.cpp | 2 +- .../World/FlowNode_PlayLevelSequence.cpp | 2 +- Source/Flow/Private/Types/FlowClassUtils.cpp | 61 ++ .../Types/FlowDataPinBlueprintLibrary.cpp | 36 + .../Private/Types/FlowDataPinProperties.cpp | 204 ++++++ .../Flow/Private/Types/FlowDataPinResults.cpp | 60 ++ Source/Flow/Public/AddOns/FlowNodeAddOn.h | 21 +- .../AddOns/FlowNodeAddOn_PredicateAND.h | 2 +- .../AddOns/FlowNodeAddOn_PredicateNOT.h | 2 +- .../Public/AddOns/FlowNodeAddOn_PredicateOR.h | 2 +- Source/Flow/Public/FlowAsset.h | 56 +- Source/Flow/Public/FlowSubsystem.h | 4 +- Source/Flow/Public/FlowTags.h | 23 + Source/Flow/Public/FlowTypes.h | 48 +- .../FlowContextPinSupplierInterface.h | 9 +- .../FlowDataPinGeneratorNodeInterface.h | 29 + .../FlowDataPinPropertyProviderInterface.h | 28 + .../FlowDataPinValueSupplierInterface.h | 108 +++ .../FlowNativeExecutableInterface.h | 24 + ...NodeWithExternalDataPinSupplierInterface.h | 34 + .../DataPins/FlowNode_DefineProperties.h | 54 ++ Source/Flow/Public/Nodes/FlowNode.h | 632 ++++++++++++++++- Source/Flow/Public/Nodes/FlowNodeBase.h | 153 +++- Source/Flow/Public/Nodes/FlowPin.h | 190 ++++- .../Flow/Public/Nodes/Route/FlowNode_Branch.h | 2 +- .../Flow/Public/Nodes/Route/FlowNode_Start.h | 47 +- .../Public/Nodes/Route/FlowNode_SubGraph.h | 18 +- .../Flow/Public/Nodes/Route/FlowNode_Timer.h | 15 +- Source/Flow/Public/Nodes/Utils/FlowNode_Log.h | 10 +- .../Nodes/World/FlowNode_ExecuteComponent.h | 6 +- Source/Flow/Public/Types/FlowArray.h | 73 ++ Source/Flow/Public/Types/FlowClassUtils.h | 15 + .../Types/FlowDataPinBlueprintLibrary.h | 224 ++++++ .../Flow/Public/Types/FlowDataPinProperties.h | 637 +++++++++++++++++ Source/Flow/Public/Types/FlowDataPinResults.h | 392 +++++++++++ Source/Flow/Public/Types/FlowEnumUtils.h | 129 ++++ .../Public/Types/FlowGameplayTagMapUtils.h | 190 +++++ Source/Flow/Public/Types/FlowPinEnums.h | 95 +++ Source/FlowEditor/FlowEditor.Build.cs | 4 +- .../Private/Asset/FlowAssetEditor.cpp | 6 +- .../Private/Asset/FlowDiffControl.cpp | 8 +- .../Private/Asset/FlowObjectDiff.cpp | 2 +- .../FlowDataPinPropertyCustomizationBase.cpp | 32 + ...FlowDataPinProperty_ClassCustomization.cpp | 179 +++++ .../FlowDataPinProperty_EnumCustomization.cpp | 109 +++ ...lowDataPinProperty_ObjectCustomization.cpp | 150 ++++ ...amedDataPinOutputPropertyCustomization.cpp | 13 + .../FlowPinCustomization.cpp | 43 ++ Source/FlowEditor/Private/Find/FindInFlow.cpp | 4 + .../FlowEditor/Private/FlowEditorModule.cpp | 66 ++ Source/FlowEditor/Private/Graph/FlowGraph.cpp | 30 +- .../FlowGraphConnectionDrawingPolicy.cpp | 21 +- .../Private/Graph/FlowGraphEditor.cpp | 189 ++++- .../Private/Graph/FlowGraphPinFactory.cpp | 104 +++ .../Private/Graph/FlowGraphSchema.cpp | 554 ++++++++++++++- .../Private/Graph/FlowGraphSettings.cpp | 101 ++- .../Private/Graph/FlowGraphUtils.cpp | 4 + .../Private/Graph/Nodes/FlowGraphNode.cpp | 261 +++++-- .../Private/Graph/Widgets/SFlowGraphNode.cpp | 55 +- .../Private/Utils/SLevelEditorFlow.cpp | 2 +- .../FlowEditor/Public/Asset/FlowObjectDiff.h | 2 + .../FlowDataPinPropertyCustomizationBase.h | 24 + .../FlowDataPinPropertyCustomizations.h | 32 + .../FlowDataPinProperty_ClassCustomization.h | 52 ++ .../FlowDataPinProperty_EnumCustomization.h | 53 ++ .../FlowDataPinProperty_ObjectCustomization.h | 48 ++ ...wNamedDataPinOutputPropertyCustomization.h | 20 + .../FlowPinCustomization.h | 30 + Source/FlowEditor/Public/FlowEditorModule.h | 2 + Source/FlowEditor/Public/Graph/FlowGraph.h | 9 +- .../FlowEditor/Public/Graph/FlowGraphEditor.h | 4 + .../Public/Graph/FlowGraphPinFactory.h | 30 + .../FlowEditor/Public/Graph/FlowGraphSchema.h | 40 +- .../Public/Graph/FlowGraphSettings.h | 52 +- .../Public/Graph/Nodes/FlowGraphNode.h | 25 +- .../Public/Graph/Widgets/SFlowGraphNode.h | 4 - 106 files changed, 8355 insertions(+), 417 deletions(-) create mode 100644 Source/Flow/Private/FlowTags.cpp create mode 100644 Source/Flow/Private/Nodes/DataPins/FlowNode_DefineProperties.cpp create mode 100644 Source/Flow/Private/Types/FlowClassUtils.cpp create mode 100644 Source/Flow/Private/Types/FlowDataPinBlueprintLibrary.cpp create mode 100644 Source/Flow/Private/Types/FlowDataPinProperties.cpp create mode 100644 Source/Flow/Private/Types/FlowDataPinResults.cpp create mode 100644 Source/Flow/Public/FlowTags.h create mode 100644 Source/Flow/Public/Interfaces/FlowDataPinGeneratorNodeInterface.h create mode 100644 Source/Flow/Public/Interfaces/FlowDataPinPropertyProviderInterface.h create mode 100644 Source/Flow/Public/Interfaces/FlowDataPinValueSupplierInterface.h create mode 100644 Source/Flow/Public/Interfaces/FlowNativeExecutableInterface.h create mode 100644 Source/Flow/Public/Interfaces/FlowNodeWithExternalDataPinSupplierInterface.h create mode 100644 Source/Flow/Public/Nodes/DataPins/FlowNode_DefineProperties.h create mode 100644 Source/Flow/Public/Types/FlowArray.h create mode 100644 Source/Flow/Public/Types/FlowClassUtils.h create mode 100644 Source/Flow/Public/Types/FlowDataPinBlueprintLibrary.h create mode 100644 Source/Flow/Public/Types/FlowDataPinProperties.h create mode 100644 Source/Flow/Public/Types/FlowDataPinResults.h create mode 100644 Source/Flow/Public/Types/FlowEnumUtils.h create mode 100644 Source/Flow/Public/Types/FlowGameplayTagMapUtils.h create mode 100644 Source/Flow/Public/Types/FlowPinEnums.h create mode 100644 Source/FlowEditor/Private/DetailCustomizations/FlowDataPinPropertyCustomizationBase.cpp create mode 100644 Source/FlowEditor/Private/DetailCustomizations/FlowDataPinProperty_ClassCustomization.cpp create mode 100644 Source/FlowEditor/Private/DetailCustomizations/FlowDataPinProperty_EnumCustomization.cpp create mode 100644 Source/FlowEditor/Private/DetailCustomizations/FlowDataPinProperty_ObjectCustomization.cpp create mode 100644 Source/FlowEditor/Private/DetailCustomizations/FlowNamedDataPinOutputPropertyCustomization.cpp create mode 100644 Source/FlowEditor/Private/DetailCustomizations/FlowPinCustomization.cpp create mode 100644 Source/FlowEditor/Private/Graph/FlowGraphPinFactory.cpp create mode 100644 Source/FlowEditor/Public/DetailCustomizations/FlowDataPinPropertyCustomizationBase.h create mode 100644 Source/FlowEditor/Public/DetailCustomizations/FlowDataPinPropertyCustomizations.h create mode 100644 Source/FlowEditor/Public/DetailCustomizations/FlowDataPinProperty_ClassCustomization.h create mode 100644 Source/FlowEditor/Public/DetailCustomizations/FlowDataPinProperty_EnumCustomization.h create mode 100644 Source/FlowEditor/Public/DetailCustomizations/FlowDataPinProperty_ObjectCustomization.h create mode 100644 Source/FlowEditor/Public/DetailCustomizations/FlowNamedDataPinOutputPropertyCustomization.h create mode 100644 Source/FlowEditor/Public/DetailCustomizations/FlowPinCustomization.h create mode 100644 Source/FlowEditor/Public/Graph/FlowGraphPinFactory.h diff --git a/Flow.uplugin b/Flow.uplugin index 09b6def56..44cd2a52e 100644 --- a/Flow.uplugin +++ b/Flow.uplugin @@ -1,4 +1,4 @@ -{ +{ "FileVersion" : 3, "Version" : 2.0, "FriendlyName" : "Flow", @@ -33,6 +33,10 @@ { "Name": "EditorScriptingUtilities", "Enabled": true + }, + { + "Name": "StructUtils", + "Enabled": true } ] } \ No newline at end of file diff --git a/Source/Flow/Flow.Build.cs b/Source/Flow/Flow.Build.cs index 74df20a4c..7feac77b2 100644 --- a/Source/Flow/Flow.Build.cs +++ b/Source/Flow/Flow.Build.cs @@ -10,7 +10,8 @@ public Flow(ReadOnlyTargetRules target) : base(target) PublicDependencyModuleNames.AddRange(new[] { - "LevelSequence" + "LevelSequence", + "StructUtils", }); PrivateDependencyModuleNames.AddRange(new[] diff --git a/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp b/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp index eb6e5d5e9..621aa08a8 100644 --- a/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp +++ b/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp @@ -7,6 +7,13 @@ #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNodeAddOn) +UFlowNodeAddOn::UFlowNodeAddOn() +{ +#if WITH_EDITOR + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_AddOn; +#endif +} + void UFlowNodeAddOn::InitializeInstance() { CacheFlowNode(); @@ -45,7 +52,9 @@ void UFlowNodeAddOn::Finish() } } -EFlowAddOnAcceptResult UFlowNodeAddOn::AcceptFlowNodeAddOnParent_Implementation(const UFlowNodeBase* ParentTemplate) const +EFlowAddOnAcceptResult UFlowNodeAddOn::AcceptFlowNodeAddOnParent_Implementation( + const UFlowNodeBase* ParentTemplate, + const TArray& AdditionalAddOnsToAssumeAreChildren) const { // Subclasses may override this function to opt in to parent classes return EFlowAddOnAcceptResult::Undetermined; @@ -94,3 +103,19 @@ void UFlowNodeAddOn::CacheFlowNode() ensureAsRuntimeWarning(FlowNode); } + +#if WITH_EDITOR +TArray UFlowNodeAddOn::GetContextInputs() const +{ + TArray ContextPins = Super::GetContextInputs(); + ContextPins.Append(InputPins); + return ContextPins; +} + +TArray UFlowNodeAddOn::GetContextOutputs() const +{ + TArray ContextPins = Super::GetContextOutputs(); + ContextPins.Append(OutputPins); + return ContextPins; +} +#endif // WITH_EDITOR diff --git a/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateAND.cpp b/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateAND.cpp index e176f73a4..1c5e63ba2 100644 --- a/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateAND.cpp +++ b/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateAND.cpp @@ -8,12 +8,14 @@ UFlowNodeAddOn_PredicateAND::UFlowNodeAddOn_PredicateAND() : Super() { #if WITH_EDITOR - NodeStyle = EFlowNodeStyle::Logic; + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_AddOn_Predicate_Composite; Category = TEXT("Composite"); #endif } -EFlowAddOnAcceptResult UFlowNodeAddOn_PredicateAND::AcceptFlowNodeAddOnChild_Implementation(const UFlowNodeAddOn* AddOnTemplate) const +EFlowAddOnAcceptResult UFlowNodeAddOn_PredicateAND::AcceptFlowNodeAddOnChild_Implementation( + const UFlowNodeAddOn* AddOnTemplate, + const TArray& AdditionalAddOnsToAssumeAreChildren) const { if (IFlowPredicateInterface::ImplementsInterfaceSafe(AddOnTemplate)) { diff --git a/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateNOT.cpp b/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateNOT.cpp index d3c3c5ffb..3cc58c423 100644 --- a/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateNOT.cpp +++ b/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateNOT.cpp @@ -8,14 +8,16 @@ UFlowNodeAddOn_PredicateNOT::UFlowNodeAddOn_PredicateNOT() : Super() { #if WITH_EDITOR - NodeStyle = EFlowNodeStyle::Logic; + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_AddOn_Predicate_Composite; Category = TEXT("Composite"); #endif } -EFlowAddOnAcceptResult UFlowNodeAddOn_PredicateNOT::AcceptFlowNodeAddOnChild_Implementation(const UFlowNodeAddOn* AddOnTemplate) const +EFlowAddOnAcceptResult UFlowNodeAddOn_PredicateNOT::AcceptFlowNodeAddOnChild_Implementation( + const UFlowNodeAddOn* AddOnTemplate, + const TArray& AdditionalAddOnsToAssumeAreChildren) const { - if (AddOns.Num() >= 1) + if (AddOns.Num() >= 1 || !AdditionalAddOnsToAssumeAreChildren.IsEmpty()) { // Must not have more than one child Add-On under any circumstances return EFlowAddOnAcceptResult::Reject; diff --git a/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateOR.cpp b/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateOR.cpp index 9cc596e9e..8ec0a3dc2 100644 --- a/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateOR.cpp +++ b/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateOR.cpp @@ -8,12 +8,14 @@ UFlowNodeAddOn_PredicateOR::UFlowNodeAddOn_PredicateOR() : Super() { #if WITH_EDITOR - NodeStyle = EFlowNodeStyle::Logic; + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_AddOn_Predicate_Composite; Category = TEXT("Composite"); #endif } -EFlowAddOnAcceptResult UFlowNodeAddOn_PredicateOR::AcceptFlowNodeAddOnChild_Implementation(const UFlowNodeAddOn* AddOnTemplate) const +EFlowAddOnAcceptResult UFlowNodeAddOn_PredicateOR::AcceptFlowNodeAddOnChild_Implementation( + const UFlowNodeAddOn* AddOnTemplate, + const TArray& AdditionalAddOnsToAssumeAreChildren) const { if (IFlowPredicateInterface::ImplementsInterfaceSafe(AddOnTemplate)) { diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index b5eb88750..666fa928d 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -7,6 +7,7 @@ #include "FlowSubsystem.h" #include "AddOns/FlowNodeAddOn.h" +#include "Interfaces/FlowDataPinGeneratorNodeInterface.h" #include "Nodes/FlowNodeBase.h" #include "Nodes/Route/FlowNode_CustomInput.h" #include "Nodes/Route/FlowNode_CustomOutput.h" @@ -175,7 +176,8 @@ bool UFlowAsset::CanFlowNodeClassBeUsedByFlowAsset(const UClass& FlowNodeClass) } // UFlowNode class limits which UFlowAsset class can use it - for (const UClass* DeniedAssetClass : NodeDefaults->DeniedAssetClasses) + const TArray>& DeniedAssetClasses = NodeDefaults->DeniedAssetClasses; + for (const UClass* DeniedAssetClass : DeniedAssetClasses) { if (DeniedAssetClass && GetClass()->IsChildOf(DeniedAssetClass)) { @@ -183,10 +185,11 @@ bool UFlowAsset::CanFlowNodeClassBeUsedByFlowAsset(const UClass& FlowNodeClass) } } - if (NodeDefaults->AllowedAssetClasses.Num() > 0) + const TArray>& AllowedAssetClasses = NodeDefaults->AllowedAssetClasses; + if (AllowedAssetClasses.Num() > 0) { bool bAllowedInAsset = false; - for (const UClass* AllowedAssetClass : NodeDefaults->AllowedAssetClasses) + for (const UClass* AllowedAssetClass : AllowedAssetClasses) { if (AllowedAssetClass && GetClass()->IsChildOf(AllowedAssetClass)) { @@ -291,6 +294,7 @@ void UFlowAsset::RegisterNode(const FGuid& NewGuid, UFlowNode* NewNode) Nodes.Emplace(NewGuid, NewNode); HarvestNodeConnections(); + (void) TryUpdateManagedFlowPinsForNode(*NewNode); } void UFlowAsset::UnregisterNode(const FGuid& NodeGuid) @@ -299,6 +303,7 @@ void UFlowAsset::UnregisterNode(const FGuid& NodeGuid) Nodes.Compact(); HarvestNodeConnections(); + MarkPackageDirty(); } @@ -320,13 +325,30 @@ void UFlowAsset::HarvestNodeConnections() for (const TPair& Pair : Nodes) { - UFlowNode* Node = Pair.Value; + UFlowNode* FlowNode = Pair.Value; TMap FoundConnections; + const TArray& GraphNodePins = FlowNode->GetGraphNode()->Pins; - for (const UEdGraphPin* ThisPin : Node->GetGraphNode()->Pins) + for (const UEdGraphPin* ThisPin : GraphNodePins) { - if (ThisPin->Direction == EGPD_Output && ThisPin->LinkedTo.Num() > 0) + const bool bIsExecPin = FFlowPin::IsExecPinCategory(ThisPin->PinType.PinCategory); + const bool bIsDataPin = FFlowPin::IsDataPinCategory(ThisPin->PinType.PinCategory); + const bool bIsOutputPin = (ThisPin->Direction == EGPD_Output); + const bool bIsInputPin = (ThisPin->Direction == EGPD_Input); + const bool bHasAtLeastOneConnection = ThisPin->LinkedTo.Num() > 0; + + if (bIsExecPin && bIsOutputPin && bHasAtLeastOneConnection) + { + // For Exec Pins, harvest the 0th connection (we should have only 1 connection, because of schema rules) + if (const UEdGraphPin* LinkedPin = ThisPin->LinkedTo[0]) + { + const UEdGraphNode* LinkedNode = LinkedPin->GetOwningNode(); + FoundConnections.Add(ThisPin->PinName, FConnectedPin(LinkedNode->NodeGuid, LinkedPin->PinName)); + } + } + else if (bIsDataPin && bIsInputPin && bHasAtLeastOneConnection) { + // For Data Pins, harvest the 0th connection (we should have only 1 connection, because of schema rules) if (const UEdGraphPin* LinkedPin = ThisPin->LinkedTo[0]) { const UEdGraphNode* LinkedNode = LinkedPin->GetOwningNode(); @@ -339,7 +361,9 @@ void UFlowAsset::HarvestNodeConnections() // Optimization: we need check it only until the first node would be marked dirty, as this already marks Flow Asset package dirty if (bGraphDirty == false) { - if (FoundConnections.Num() != Node->Connections.Num()) + const TMap& OldConnections = FlowNode->Connections; + + if (FoundConnections.Num() != OldConnections.Num()) { bGraphDirty = true; } @@ -347,7 +371,7 @@ void UFlowAsset::HarvestNodeConnections() { for (const TPair& FoundConnection : FoundConnections) { - if (const FConnectedPin* OldConnection = Node->Connections.Find(FoundConnection.Key)) + if (const FConnectedPin* OldConnection = OldConnections.Find(FoundConnection.Key)) { if (FoundConnection.Value != *OldConnection) { @@ -366,14 +390,482 @@ void UFlowAsset::HarvestNodeConnections() if (bGraphDirty) { - Node->SetFlags(RF_Transactional); - Node->Modify(); + FlowNode->SetFlags(RF_Transactional); + FlowNode->Modify(); + + FlowNode->SetConnections(FoundConnections); + FlowNode->PostEditChange(); + } + } + + // NOTE (gtaylor) @mothdoctor, do we need to do anything with bGraphDirty here? + // It's scope seems like we wanted to do something at this point. +} + +bool UFlowAsset::TryUpdateManagedFlowPinsForNode(UFlowNode& FlowNode) +{ + const UClass* FlowNodeClass = FlowNode.GetClass(); + if (!IsValid(FlowNodeClass)) + { + return false; + } + + // Setup the working data struct + FFlowHarvestDataPinsWorkingData WorkingData = + FFlowHarvestDataPinsWorkingData( + FlowNode, + FlowNode.GetPinNameToBoundPropertyNameMap(), + FlowNode.GetAutoInputDataPins(), + FlowNode.GetAutoOutputDataPins()); + + // Some nodes can auto-generate some pins directly, + // so let them append their pins into our arrays first. + if (IFlowDataPinGeneratorNodeInterface* AutoGeneratorNode = Cast(&FlowNode)) + { + AutoGeneratorNode->AutoGenerateDataPins( + WorkingData.PinNameToBoundPropertyNameMapNext, + WorkingData.AutoInputDataPinsNext, + WorkingData.AutoOutputDataPinsNext); + } + + // Try to harvest pins to auto-generate and/or bind to for each property in the flow node + for (TFieldIterator PropertyIt(FlowNodeClass); PropertyIt; ++PropertyIt) + { + HarvestFlowPinMetadataForProperty(*PropertyIt, WorkingData); + } + + // Check if the pin name to bound property map changed + WorkingData.bPinNameMapChanged |= WorkingData.DidPinNameToBoundPropertyNameMapChange(); + + // If the auto-generated data pins array changed, it counts as dirty as well + const bool bAutoInputDataPinsChanged = WorkingData.DidAutoInputDataPinsChange(); + const bool bAutoOutputDataPinsChanged = WorkingData.DidAutoOutputDataPinsChange(); + + if (WorkingData.bPinNameMapChanged || bAutoInputDataPinsChanged || bAutoOutputDataPinsChanged) + { + FlowNode.SetFlags(RF_Transactional); + FlowNode.Modify(); + + // Lock-in the data that changed. + if (WorkingData.bPinNameMapChanged) + { + FlowNode.SetPinNameToBoundPropertyNameMap(WorkingData.PinNameToBoundPropertyNameMapNext); + } + + if (bAutoInputDataPinsChanged || bAutoOutputDataPinsChanged) + { + if (bAutoInputDataPinsChanged) + { + FlowNode.SetAutoInputDataPins(WorkingData.AutoInputDataPinsNext); + } + + if (bAutoOutputDataPinsChanged) + { + FlowNode.SetAutoOutputDataPins(WorkingData.AutoOutputDataPinsNext); + } + + if (FlowNode.GraphNode) + { + FlowNode.OnReconstructionRequested.ExecuteIfBound(); + } + } + + FlowNode.PostEditChange(); + + return true; + } + + return false; +} + +void UFlowAsset::HarvestFlowPinMetadataForProperty(const FProperty* Property, FFlowHarvestDataPinsWorkingData& InOutData) +{ + FText PinDisplayName = Property->GetDisplayNameText(); + const FName& PinAuthoredName = Property->GetFName(); + + // Default assumption is the pin is will be a output pin, if no metadata is specified (ie, bIsSourceForOutputPin == false), + // because this is the most common case (the auto-generated input-pin-from-property case is only for defaulting) + TArray* FlowPinArray = &InOutData.AutoOutputDataPinsNext; + + const FString* SourceForOutputFlowPinName = Property->FindMetaData(FFlowPin::MetadataKey_SourceForOutputFlowPin); + const FString* DefaultForInputFlowPinName = Property->FindMetaData(FFlowPin::MetadataKey_DefaultForInputFlowPin); + + if (SourceForOutputFlowPinName && DefaultForInputFlowPinName) + { + LogError( + FString::Printf(TEXT("Error. A property cannot be both a %s and %s"), + *FFlowPin::MetadataKey_SourceForOutputFlowPin.ToString(), + *FFlowPin::MetadataKey_DefaultForInputFlowPin.ToString()), + InOutData.FlowNode); + + return; + } + + if (SourceForOutputFlowPinName) + { + const FString SpecifyOutputPinNameString = *SourceForOutputFlowPinName; + + if (SpecifyOutputPinNameString.Len() > 0) + { + // Replace the default PinDisplayName with the name specified in the Metadata value + PinDisplayName = FText::FromString(SpecifyOutputPinNameString); + } + } + else if (DefaultForInputFlowPinName) + { + const FString SpecifyInputPinNameString = *DefaultForInputFlowPinName; + + if (SpecifyInputPinNameString.Len() > 0) + { + // Replace the default PinDisplayName with the name specified in the Metadata value + PinDisplayName = FText::FromString(SpecifyInputPinNameString); + } + + // If the property is a Default Input for a data pin, then we need to generate the pin in the + // Input Pins array. + FlowPinArray = &InOutData.AutoInputDataPinsNext; + } + + // Check for relevant metadata keys on the property's USTRUCT() + const FStructProperty* StructProperty = CastField(Property); + if (StructProperty && StructProperty->Struct) + { + const UScriptStruct* ScriptStruct = StructProperty->Struct; + + // We also look in the USTRUCT for DefaultForInputFlowPin + DefaultForInputFlowPinName = ScriptStruct->FindMetaData(FFlowPin::MetadataKey_DefaultForInputFlowPin); + if (DefaultForInputFlowPinName) + { + // If the property is a Default Input for a data pin, then we need to generate the pin in the + // Input Pins array. + FlowPinArray = &InOutData.AutoInputDataPinsNext; + } + + if (const FString* AutoPinType = ScriptStruct->FindMetaData(FFlowPin::MetadataKey_FlowPinType)) + { + const bool bIsInputPin = DefaultForInputFlowPinName != nullptr; + + // Auto-generate the pin for this property + if (!TryCreateFlowDataPinFromMetadataValue(*AutoPinType, *InOutData.FlowNode, *Property, PinDisplayName, bIsInputPin, FlowPinArray)) + { + LogError(FString::Printf(TEXT("Error. Unknown value %s for metadata %s"), **AutoPinType, *FFlowPin::MetadataKey_FlowPinType.ToString()), InOutData.FlowNode); + + return; + } + + // Add a binding for the new pin to its property + AddDataPinPropertyBindingToMap( + PinAuthoredName, + Property->GetFName(), + InOutData); + + return; + } + } + + const FString* AutoPinType = Property->FindMetaData(FFlowPin::MetadataKey_FlowPinType); + + if (!SourceForOutputFlowPinName && !DefaultForInputFlowPinName && !AutoPinType) + { + // If we didn't detect any the relevent metadata keys, we can exit early + + return; + } + + if (AutoPinType) + { + // Auto-generate the desired pin for this property + const bool bIsInputPin = DefaultForInputFlowPinName != nullptr; + + if (!TryCreateFlowDataPinFromMetadataValue(*AutoPinType, *InOutData.FlowNode, *Property, PinDisplayName, bIsInputPin, FlowPinArray)) + { + LogError(FString::Printf(TEXT("Unknown value %s for metadata %s"), **AutoPinType, *FFlowPin::MetadataKey_FlowPinType.ToString()), InOutData.FlowNode); + + return; + } + } + else if (SourceForOutputFlowPinName) + { + // Bind to the output data pin to source from the property (but do not auto-generate the pin) + + FFlowPin* FoundFlowPin = InOutData.FlowNode->FindOutputPinByName(PinAuthoredName); + if (!FoundFlowPin) + { + LogError(FString::Printf(TEXT("Could not find bound data pin named %s for property %s"), *PinAuthoredName.ToString(), *Property->GetName()), InOutData.FlowNode); + + return; + } + } + else if (DefaultForInputFlowPinName) + { + // Bind to the input data pin to default its value from the property (but do not auto-generate the pin) + + FFlowPin* FoundFlowPin = InOutData.FlowNode->FindInputPinByName(PinAuthoredName); + if (!FoundFlowPin) + { + LogError(FString::Printf(TEXT("Could not find bound data pin named %s for property %s"), *PinAuthoredName.ToString(), *Property->GetName()), InOutData.FlowNode); + + return; + } + } + + // Add a binding for the data pin to its property + AddDataPinPropertyBindingToMap( + PinAuthoredName, + Property->GetFName(), + InOutData); +} + +void UFlowAsset::AddDataPinPropertyBindingToMap( + const FName& PinAuthoredName, + const FName& PropertyAuthoredName, + FFlowHarvestDataPinsWorkingData& InOutData) +{ + // Add a new entry in the map for this DataPin name to the property it sources from + InOutData.PinNameToBoundPropertyNameMapNext.Add(PinAuthoredName, PropertyAuthoredName); +} + +template +void AddPinForPinType(EFlowPinType PinType, UFlowNode& FlowNode, const FProperty& Property, const FText& PinDisplayName, TArray* InOutDataPinsNext) +{ + const FName& PinAuthoredName = Property.GetFName(); + + // Some of the FlowPinTypes require a SubCategoryObject to fully define the type, so + // we need to find that for the cases that it applies to. + + FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); + + FFlowPin& NewFlowPin = InOutDataPinsNext->Add_GetRef(FFlowPin(PinAuthoredName, PinDisplayName)); + switch (PinType) + { + case EFlowPinType::Enum: + { + UEnum* EnumClass = nullptr; + + if (const FStructProperty* StructProperty = CastField(&Property)) + { + // Check for a wrapper struct to get the enum data from + const UStruct* ScriptStruct = TEnumProperty::StaticStruct(); + if (StructProperty->Struct == ScriptStruct) + { + TEnumProperty ValueStruct; + StructProperty->GetValue_InContainer(&FlowNode, &ValueStruct); + + EnumClass = ValueStruct.EnumClass; + } + } + else if (const FEnumProperty* EnumProperty = CastField(&Property)) + { + // Get the enum data from the FEnumProperty + EnumClass = EnumProperty->GetEnum(); + } + + NewFlowPin.SetPinType(PinType, EnumClass); + } + break; + + case EFlowPinType::Vector: + { + UScriptStruct* ValueStructType = FFlowDataPinProperty::FindScriptStructForFlowDataPinProperty(Property); + NewFlowPin.SetPinType(PinType, ValueStructType); + } + break; + + case EFlowPinType::Rotator: + { + UScriptStruct* ValueStructType = FFlowDataPinProperty::FindScriptStructForFlowDataPinProperty(Property); + NewFlowPin.SetPinType(PinType, ValueStructType); + } + break; + + case EFlowPinType::Transform: + { + UScriptStruct* ValueStructType = FFlowDataPinProperty::FindScriptStructForFlowDataPinProperty(Property); + NewFlowPin.SetPinType(PinType, ValueStructType); + } + break; + + case EFlowPinType::GameplayTag: + { + UScriptStruct* ValueStructType = FFlowDataPinProperty::FindScriptStructForFlowDataPinProperty(Property); + NewFlowPin.SetPinType(PinType, ValueStructType); + } + break; + + case EFlowPinType::GameplayTagContainer: + { + UScriptStruct* ValueStructType = FFlowDataPinProperty::FindScriptStructForFlowDataPinProperty(Property); + NewFlowPin.SetPinType(PinType, ValueStructType); + } + break; + + case EFlowPinType::InstancedStruct: + { + UScriptStruct* ValueStructType = FFlowDataPinProperty::FindScriptStructForFlowDataPinProperty(Property); + NewFlowPin.SetPinType(PinType, ValueStructType); + } + break; + + case EFlowPinType::Object: + { + UClass* Class = nullptr; + if (const FStructProperty* StructProperty = CastField(&Property)) + { + const UStruct* ScriptStruct = TObjectProperty::StaticStruct(); + static const UStruct* SoftObjectPathStruct = TBaseStructure::Get(); + + if (StructProperty->Struct == ScriptStruct) + { + TObjectProperty ValueStruct; + StructProperty->GetValue_InContainer(&FlowNode, &ValueStruct); + + // Get the Object property's base UClass from the FFlowDataPinProperty + Class = ValueStruct.DeriveObjectClass(*StructProperty); + } + else if (StructProperty->Struct == SoftObjectPathStruct) + { + // Get the Object property's base UClass from the struct property's MetaData + Class = FFlowDataPinOutputProperty_Object::TryGetObjectClassFromProperty(*StructProperty); + } + } + else if (const FObjectProperty* ObjectProperty = CastField(&Property)) + { + // Get the Object property's base UClass from the property's MetaData + Class = ObjectProperty->PropertyClass; + } + else if (const FSoftObjectProperty* SoftObjectProperty = CastField(&Property)) + { + // Get the Object property's base UClass from the property's MetaData + Class = SoftObjectProperty->PropertyClass; + } + else if (const FWeakObjectProperty* WeakObjectProperty = CastField(&Property)) + { + // Get the Object property's base UClass from the property's MetaData + Class = WeakObjectProperty->PropertyClass; + } + else if (const FLazyObjectProperty* LazyObjectProperty = CastField(&Property)) + { + // Get the Object property's base UClass from the property's MetaData + Class = LazyObjectProperty->PropertyClass; + } + + NewFlowPin.SetPinType(PinType, Class); + } + break; + + case EFlowPinType::Class: + { + UClass* Class = nullptr; + if (const FStructProperty* StructProperty = CastField(&Property)) + { + const UStruct* ScriptStruct = TClassProperty::StaticStruct(); + static const UStruct* SoftClassPathStruct = TBaseStructure::Get(); + + if (StructProperty->Struct == ScriptStruct) + { + TClassProperty ValueStruct; + StructProperty->GetValue_InContainer(&FlowNode, &ValueStruct); + + // Get the Class property's base UClass from the FFlowDataPinProperty + Class = ValueStruct.DeriveMetaClass(*StructProperty); + } + else if (StructProperty->Struct == SoftClassPathStruct) + { + // Get the Class property's base UClass from the struct property's MetaData + Class = FFlowDataPinOutputProperty_Class::TryGetMetaClassFromProperty(*StructProperty); + } + } + else if (const FClassProperty* ClassProperty = CastField(&Property)) + { + // Get the Class property's base UClass from the property's MetaData + Class = ClassProperty->MetaClass; + } + else if (const FSoftClassProperty* SoftClassProperty = CastField(&Property)) + { + // Get the Class property's base UClass from the property's MetaData + Class = SoftClassProperty->MetaClass; + } + + NewFlowPin.SetPinType(PinType, Class); + } + break; + + default: + { + NewFlowPin.SetPinType(PinType); + } + break; + } +} + +bool UFlowAsset::TryCreateFlowDataPinFromMetadataValue( + const FString& MetadataValue, + UFlowNode& FlowNode, + const FProperty& Property, + const FText& PinDisplayName, + const bool bIsInputPin, + TArray* InOutDataPinsNext) const +{ + check(InOutDataPinsNext); + + const TArray& CachedEnumValueNames = FFlowPin::GetFlowPinTypeEnumValuesWithoutSpaces(); + + const FName MetadataValueAsName = FName(MetadataValue); - Node->SetConnections(FoundConnections); - Node->PostEditChange(); + for (EFlowPinType PinType : TEnumRange()) + { + const int32 PinTypeAsInt = FlowEnum::ToInt(PinType); + check(CachedEnumValueNames.IsValidIndex(PinTypeAsInt)); + const FName& EnumValueAsName = CachedEnumValueNames[PinTypeAsInt]; + + if (MetadataValueAsName == EnumValueAsName) + { + if (bIsInputPin) + { + AddPinForPinType< + FFlowDataPinInputProperty_Enum, + FFlowDataPinInputProperty_Vector, + FFlowDataPinInputProperty_Rotator, + FFlowDataPinInputProperty_Transform, + FFlowDataPinInputProperty_GameplayTag, + FFlowDataPinInputProperty_GameplayTagContainer, + FFlowDataPinInputProperty_InstancedStruct, + FFlowDataPinInputProperty_Object, + FFlowDataPinInputProperty_Class>( + PinType, + FlowNode, + Property, + PinDisplayName, + InOutDataPinsNext); + } + else + { + AddPinForPinType< + FFlowDataPinOutputProperty_Enum, + FFlowDataPinOutputProperty_Vector, + FFlowDataPinInputProperty_Rotator, + FFlowDataPinOutputProperty_Transform, + FFlowDataPinOutputProperty_GameplayTag, + FFlowDataPinOutputProperty_GameplayTagContainer, + FFlowDataPinOutputProperty_InstancedStruct, + FFlowDataPinOutputProperty_Object, + FFlowDataPinOutputProperty_Class>( + PinType, + FlowNode, + Property, + PinDisplayName, + InOutDataPinsNext); + } + + return true; } } + + // Subclasses of UFlowAsset can extend the supported MetadataValues -> FFlowPin mappings + return false; } + #endif UFlowNode* UFlowAsset::GetDefaultEntryNode() const @@ -384,7 +876,7 @@ UFlowNode* UFlowAsset::GetDefaultEntryNode() const { if (UFlowNode_Start* StartNode = Cast(Node.Value)) { - if (StartNode->GetConnectedNodes().Num() > 0) + if (StartNode->GatherConnectedNodes().Num() > 0) { return StartNode; } @@ -510,7 +1002,7 @@ TArray UFlowAsset::GetNodesInExecutionOrder(UFlowNode* FirstIterated } } FoundNodes.Shrink(); - + return FoundNodes; } @@ -656,13 +1148,19 @@ void UFlowAsset::PreStartFlow() #endif } -void UFlowAsset::StartFlow() +void UFlowAsset::StartFlow(IFlowDataPinValueSupplierInterface* DataPinValueSupplier) { PreStartFlow(); if (UFlowNode* ConnectedEntryNode = GetDefaultEntryNode()) { RecordedNodes.Add(ConnectedEntryNode); + + if (IFlowNodeWithExternalDataPinSupplierInterface* ExternalPinSuppliedNode = Cast(ConnectedEntryNode)) + { + ExternalPinSuppliedNode->SetDataPinValueSupplier(DataPinValueSupplier); + } + ConnectedEntryNode->TriggerFirstOutput(true); } } @@ -713,35 +1211,51 @@ TWeakObjectPtr UFlowAsset::GetFlowInstance(UFlowNode_SubGraph* SubGr return ActiveSubGraphs.FindRef(SubGraphNode); } -void UFlowAsset::TriggerCustomInput_FromSubGraph(UFlowNode_SubGraph* Node, const FName& EventName) const +void UFlowAsset::TriggerCustomInput_FromSubGraph(UFlowNode_SubGraph* SubGraphNode, const FName& EventName) const { - const TWeakObjectPtr FlowInstance = ActiveSubGraphs.FindRef(Node); + // NOTE (gtaylor) Custom Input nodes cannot currently add data pins (like Start or DefineProperties nodes can) + // but we may want to allow them to source parameters, so I am providing the subgraph node as the + // IFlowDataPinValueSupplierInterface when triggering the node (even though it's not used at this time). + + const TWeakObjectPtr FlowInstance = ActiveSubGraphs.FindRef(SubGraphNode); if (FlowInstance.IsValid()) { - FlowInstance->TriggerCustomInput(EventName); + FlowInstance->TriggerCustomInput(EventName, SubGraphNode); } } -void UFlowAsset::TriggerCustomInput(const FName& EventName) +void UFlowAsset::TriggerCustomInput(const FName& EventName, IFlowDataPinValueSupplierInterface* DataPinValueSupplier) { - for (UFlowNode_CustomInput* CustomInput : CustomInputNodes) + for (UFlowNode_CustomInput* CustomInputNode : CustomInputNodes) { - if (CustomInput->EventName == EventName) + if (CustomInputNode->EventName == EventName) { - RecordedNodes.Add(CustomInput); - CustomInput->ExecuteInput(EventName); + RecordedNodes.Add(CustomInputNode); + + // NOTE (gtaylor) Custom Input nodes cannot currently add data pins (like Start or DefineProperties nodes can) + // but we may want to allow them to source parameters, so I am providing the subgraph node as the + // IFlowDataPinValueSupplierInterface when triggering the node (even though it's not used at this time). + + if (IFlowNodeWithExternalDataPinSupplierInterface* ExternalPinSuppliedNode = Cast(CustomInputNode)) + { + ExternalPinSuppliedNode->SetDataPinValueSupplier(DataPinValueSupplier); + } + + CustomInputNode->ExecuteInput(EventName); } } } void UFlowAsset::TriggerCustomOutput(const FName& EventName) { - if (NodeOwningThisAssetInstance.IsValid()) // it's a SubGraph + if (NodeOwningThisAssetInstance.IsValid()) { + // it's a SubGraph NodeOwningThisAssetInstance->TriggerOutput(EventName); } - else // it's a Root Flow, so the intention here might be to call event on the Flow Component + else { + // it's a Root Flow, so the intention here might be to call event on the Flow Component if (UFlowComponent* FlowComponent = Cast(GetOwner())) { FlowComponent->OnTriggerRootFlowOutputEventDispatcher(this, EventName); @@ -775,19 +1289,18 @@ void UFlowAsset::FinishNode(UFlowNode* Node) if (NodeOwningThisAssetInstance.IsValid()) { NodeOwningThisAssetInstance.Get()->TriggerFirstOutput(true); + return; } - else + + // if this instance is a Root Flow, we need to deregister it from the subsystem first + if (Owner.IsValid()) { - // if this instance is a Root Flow, we need to deregister it from the subsystem first - if (Owner.IsValid()) + const TSet& RootFlowInstances = GetFlowSubsystem()->GetRootInstancesByOwner(Owner.Get()); + if (RootFlowInstances.Contains(this)) { - const TSet& RootFlowInstances = GetFlowSubsystem()->GetRootInstancesByOwner(Owner.Get()); - if (RootFlowInstances.Contains(this)) - { - GetFlowSubsystem()->FinishRootFlow(Owner.Get(), TemplateAsset, EFlowFinishPolicy::Keep); - return; - } + GetFlowSubsystem()->FinishRootFlow(Owner.Get(), TemplateAsset, EFlowFinishPolicy::Keep); + return; } } @@ -964,3 +1477,43 @@ void UFlowAsset::LogNote(const FString& MessageToLog, const UFlowNodeBase* Node) } } #endif + +#if WITH_EDITOR +bool FFlowHarvestDataPinsWorkingData::DidPinNameToBoundPropertyNameMapChange() const +{ + if (PinNameToBoundPropertyNameMapPrev.Num() != PinNameToBoundPropertyNameMapNext.Num()) + { + return true; + } + + for (const auto& KV : PinNameToBoundPropertyNameMapPrev) + { + const FName& PinNameFromPrev = KV.Key; + const FName& PropertyNameFromPrev = KV.Value; + + const FName* FoundPropertyNameInNext = PinNameToBoundPropertyNameMapNext.Find(PinNameFromPrev); + if (!FoundPropertyNameInNext) + { + return true; + } + + if (*FoundPropertyNameInNext != PropertyNameFromPrev) + { + return true; + } + } + + return false; +} + +bool FFlowHarvestDataPinsWorkingData::DidAutoInputDataPinsChange() const +{ + return !FFlowPin::ArePinArraysMatchingNamesAndTypes(AutoInputDataPinsPrev, AutoInputDataPinsNext); +} + +bool FFlowHarvestDataPinsWorkingData::DidAutoOutputDataPinsChange() const +{ + return !FFlowPin::ArePinArraysMatchingNamesAndTypes(AutoOutputDataPinsPrev, AutoOutputDataPinsNext); +} + +#endif diff --git a/Source/Flow/Private/FlowSettings.cpp b/Source/Flow/Private/FlowSettings.cpp index 50bbcbb13..e098071fd 100644 --- a/Source/Flow/Private/FlowSettings.cpp +++ b/Source/Flow/Private/FlowSettings.cpp @@ -23,7 +23,7 @@ void UFlowSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyChange if (PropertyChangedEvent.GetMemberPropertyName() == GET_MEMBER_NAME_CHECKED( UFlowSettings, bUseAdaptiveNodeTitles )) { - (void)OnAdaptiveNodeTitlesChanged.ExecuteIfBound(); + (void) OnAdaptiveNodeTitlesChanged.ExecuteIfBound(); } } #endif diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index 5026bd5b7..d8318e233 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -82,7 +82,11 @@ void UFlowSubsystem::StartRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const { if (UFlowAsset* NewFlow = CreateRootFlow(Owner, FlowAsset, bAllowMultipleInstances)) { - NewFlow->StartFlow(); + // TODO (gtaylor) In the future, we may want to provide a way to set a data pin value supplier + // for the root flow graph. + constexpr IFlowDataPinValueSupplierInterface* DataPinValueSupplier = nullptr; + + NewFlow->StartFlow(DataPinValueSupplier); } } #if WITH_EDITOR @@ -94,7 +98,7 @@ void UFlowSubsystem::StartRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const #endif } -UFlowAsset* UFlowSubsystem::CreateRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const bool bAllowMultipleInstances, FString NewInstanceName) +UFlowAsset* UFlowSubsystem::CreateRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const bool bAllowMultipleInstances, const FString& NewInstanceName) { for (const TPair>& RootInstance : RootInstances) { @@ -159,7 +163,7 @@ void UFlowSubsystem::FinishAllRootFlows(UObject* Owner, const EFlowFinishPolicy } } -UFlowAsset* UFlowSubsystem::CreateSubFlow(UFlowNode_SubGraph* SubGraphNode, const FString SavedInstanceName, const bool bPreloading /* = false */) +UFlowAsset* UFlowSubsystem::CreateSubFlow(UFlowNode_SubGraph* SubGraphNode, const FString& SavedInstanceName, const bool bPreloading /* = false */) { UFlowAsset* NewInstance = nullptr; @@ -190,7 +194,7 @@ UFlowAsset* UFlowSubsystem::CreateSubFlow(UFlowNode_SubGraph* SubGraphNode, cons // don't activate Start Node if we're loading Sub Graph from SaveGame if (SavedInstanceName.IsEmpty()) { - AssetInstance->StartFlow(); + AssetInstance->StartFlow(SubGraphNode); } } diff --git a/Source/Flow/Private/FlowTags.cpp b/Source/Flow/Private/FlowTags.cpp new file mode 100644 index 000000000..c526cbff4 --- /dev/null +++ b/Source/Flow/Private/FlowTags.cpp @@ -0,0 +1,21 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "FlowTags.h" + +UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle, "Flow.NodeDisplayStyle"); +UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle_Custom, "Flow.NodeDisplayStyle.Custom"); + +UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle_Node, "Flow.NodeDisplayStyle.Node"); +UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle_Node_Condition, "Flow.NodeDisplayStyle.Node.Condition"); +UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle_Node_Deprecated, "Flow.NodeDisplayStyle.Node.Deprecated"); +UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle_Node_Developer, "Flow.NodeDisplayStyle.Node.Developer"); +UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle_Node_InOut, "Flow.NodeDisplayStyle.Node.InOut"); +UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle_Node_Latent, "Flow.NodeDisplayStyle.Node.Latent"); +UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle_Node_Logic, "Flow.NodeDisplayStyle.Node.Logic"); +UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle_Node_SubGraph, "Flow.NodeDisplayStyle.Node.SubGraph"); +UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle_Node_Terminal, "Flow.NodeDisplayStyle.Node.Terminal"); + +UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle_AddOn, "Flow.NodeDisplayStyle.AddOn"); +UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle_AddOn_PerSpawnedActor, "Flow.NodeDisplayStyle.AddOn.PerSpawnedActor"); +UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle_AddOn_Predicate, "Flow.NodeDisplayStyle.AddOn.Predicate"); +UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle_AddOn_Predicate_Composite, "Flow.NodeDisplayStyle.AddOn.Predicate.Composite"); diff --git a/Source/Flow/Private/Interfaces/FlowContextPinSupplierInterface.cpp b/Source/Flow/Private/Interfaces/FlowContextPinSupplierInterface.cpp index 68eacf8d3..61bd406b4 100644 --- a/Source/Flow/Private/Interfaces/FlowContextPinSupplierInterface.cpp +++ b/Source/Flow/Private/Interfaces/FlowContextPinSupplierInterface.cpp @@ -2,6 +2,11 @@ #include "Interfaces/FlowContextPinSupplierInterface.h" +bool IFlowContextPinSupplierInterface::K2_SupportsContextPins_Implementation() const +{ + return false; +} + TArray IFlowContextPinSupplierInterface::K2_GetContextInputs_Implementation() const { return TArray(); diff --git a/Source/Flow/Private/Nodes/DataPins/FlowNode_DefineProperties.cpp b/Source/Flow/Private/Nodes/DataPins/FlowNode_DefineProperties.cpp new file mode 100644 index 000000000..daff72af1 --- /dev/null +++ b/Source/Flow/Private/Nodes/DataPins/FlowNode_DefineProperties.cpp @@ -0,0 +1,113 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Nodes/DataPins/FlowNode_DefineProperties.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_DefineProperties) + +UFlowNode_DefineProperties::UFlowNode_DefineProperties(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ +#if WITH_EDITOR + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_InOut; + Category = TEXT("Data Pins"); +#endif + + InputPins.Empty(); + OutputPins.Empty(); + + AllowedSignalModes = { EFlowSignalMode::Enabled, EFlowSignalMode::Disabled }; +} + +bool UFlowNode_DefineProperties::TryFindPropertyByRemappedPinName( + const FName& RemappedPinName, + const FProperty*& OutFoundProperty, + TInstancedStruct& OutFoundInstancedStruct, + EFlowDataPinResolveResult& InOutResult) const +{ + // The start node stores its properties in instanced structs in an array, so look there first + + for (const FFlowNamedDataPinOutputProperty& NamedProperty : OutputProperties) + { + if (NamedProperty.Name == RemappedPinName && NamedProperty.IsValid()) + { + OutFoundInstancedStruct = NamedProperty.DataPinProperty; + + return true; + } + } + + return Super::TryFindPropertyByPinName(RemappedPinName, OutFoundProperty, OutFoundInstancedStruct, InOutResult); +} + +#if WITH_EDITOR + +void UFlowNode_DefineProperties::AutoGenerateDataPins( + TMap& InOutPinNameToBoundPropertyNameMap, + TArray& InOutInputDataPins, + TArray& InOutOutputDataPins) const +{ + for (const FFlowNamedDataPinOutputProperty& DataPinProperty : OutputProperties) + { + if (DataPinProperty.IsValid()) + { + InOutPinNameToBoundPropertyNameMap.Add(DataPinProperty.Name, DataPinProperty.Name); + + InOutOutputDataPins.AddUnique(DataPinProperty.CreateFlowPin()); + } + } +} + +void UFlowNode_DefineProperties::PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChainEvent) +{ + Super::PostEditChangeChainProperty(PropertyChainEvent); + + if (PropertyChainEvent.PropertyChain.Num() == 0) + { + return; + } + + auto& Property = PropertyChainEvent.PropertyChain.GetActiveMemberNode()->GetValue(); + + // The DetailsCustomization for FFlowDataPinProperties isn't being called when using an InstancedStruct + // so we need to call this refresh by hand... + if (PropertyChainEvent.ChangeType == EPropertyChangeType::ValueSet && + Property->GetFName() == GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Enum, EnumName)) + { + for (FFlowNamedDataPinOutputProperty& OutputProperty : OutputProperties) + { + if (!OutputProperty.IsValid()) + { + continue; + } + + const FFlowDataPinProperty& FlowDataPinProperty = OutputProperty.DataPinProperty.Get(); + + if (FlowDataPinProperty.GetFlowPinType() == EFlowPinType::Enum) + { + FFlowDataPinOutputProperty_Enum& EnumProperty = OutputProperty.DataPinProperty.GetMutable(); + EnumProperty.OnEnumNameChanged(); + } + + // We may need to manually call any PostEdit linked property updates here for future EFlowPinType values + FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); + } + } + + constexpr EPropertyChangeType::Type RelevantChangeTypesForReconstructionMask = + EPropertyChangeType::Unspecified | + EPropertyChangeType::ArrayAdd | + EPropertyChangeType::ArrayRemove | + EPropertyChangeType::ArrayClear | + EPropertyChangeType::ValueSet | + EPropertyChangeType::Redirected | + EPropertyChangeType::ArrayMove; + + const uint32 PropertyChangedTypeFlags = (PropertyChainEvent.ChangeType & RelevantChangeTypesForReconstructionMask); + const bool bIsRelevantChangeTypeForReconstruction = PropertyChangedTypeFlags != 0; + const bool bChangedOutputProperties = Property->GetFName() == GET_MEMBER_NAME_CHECKED(UFlowNode_DefineProperties, OutputProperties); + if (bIsRelevantChangeTypeForReconstruction && bChangedOutputProperties) + { + OnReconstructionRequested.ExecuteIfBound(); + } +} +#endif // WITH_EDITOR diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 4386606ef..6c3fc85e8 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -5,12 +5,15 @@ #include "FlowAsset.h" #include "FlowSettings.h" +#include "Interfaces/FlowNodeWithExternalDataPinSupplierInterface.h" +#include "Types/FlowDataPinProperties.h" #include "Components/ActorComponent.h" #if WITH_EDITOR #include "Editor.h" #endif +#include "Engine/BlueprintGeneratedClass.h" #include "GameFramework/Actor.h" #include "Misc/App.h" #include "Serialization/MemoryReader.h" @@ -33,7 +36,7 @@ UFlowNode::UFlowNode(const FObjectInitializer& ObjectInitializer) { #if WITH_EDITOR Category = TEXT("Uncategorized"); - NodeStyle = EFlowNodeStyle::Default; + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node; NodeColor = FLinearColor::Black; #endif @@ -91,19 +94,19 @@ bool UFlowNode::IsSupportedInputPinName(const FName& PinName) const } } -void UFlowNode::AddInputPins(TArray Pins) +void UFlowNode::AddInputPins(const TArray& Pins) { for (const FFlowPin& Pin : Pins) { - InputPins.Emplace(Pin); + InputPins.AddUnique(Pin); } } -void UFlowNode::AddOutputPins(TArray Pins) +void UFlowNode::AddOutputPins(const TArray& Pins) { for (const FFlowPin& Pin : Pins) { - OutputPins.Emplace(Pin); + OutputPins.AddUnique(Pin); } } @@ -255,6 +258,16 @@ TArray UFlowNode::GetOutputNames() const bool UFlowNode::SupportsContextPins() const { + if (Super::SupportsContextPins()) + { + return true; + } + + if (!GetAutoInputDataPins().IsEmpty() || !GetAutoOutputDataPins().IsEmpty()) + { + return true; + } + for (const UFlowNodeAddOn* AddOn : AddOns) { if (IsValid(AddOn) && AddOn->SupportsContextPins()) @@ -266,6 +279,32 @@ bool UFlowNode::SupportsContextPins() const return false; } +TArray UFlowNode::GetContextInputs() const +{ + TArray ContextOutputs = Super::GetContextInputs(); + + // Add the Auto-Generated DataPins as GetContextInputs + for (const FFlowPin& AutoGeneratedDataPin : GetAutoInputDataPins()) + { + ContextOutputs.AddUnique(AutoGeneratedDataPin); + } + + return ContextOutputs; +} + +TArray UFlowNode::GetContextOutputs() const +{ + TArray ContextOutputs = Super::GetContextOutputs(); + + // Add the Auto-Generated DataPins as ContextOutputs + for (const FFlowPin& AutoGeneratedDataPin : GetAutoOutputDataPins()) + { + ContextOutputs.AddUnique(AutoGeneratedDataPin); + } + + return ContextOutputs; +} + bool UFlowNode::CanUserAddInput() const { return K2_CanUserAddInput(); @@ -331,15 +370,146 @@ void UFlowNode::RemoveUserOutput(const FName& PinName) } } } -#endif -TSet UFlowNode::GetConnectedNodes() const +void UFlowNode::SetPinNameToBoundPropertyNameMap(const TMap& Map) +{ + PinNameToBoundPropertyNameMap = Map; +} + +void UFlowNode::SetAutoInputDataPins(const TArray& AutoInputPins) +{ + AutoInputDataPins = AutoInputPins; +} + +void UFlowNode::SetAutoOutputDataPins(const TArray& AutoOutputPins) +{ + AutoOutputDataPins = AutoOutputPins; +} + +#endif // WITH_EDITOR + +bool UFlowNode::CanSupplyDataPinValues_Implementation() const +{ + if (!PinNameToBoundPropertyNameMap.IsEmpty()) + { + return true; + } + + return false; +} + +bool UFlowNode::TryGetFlowDataPinSupplierDatasForPinName( + const FName& PinName, + TArray& InOutPinValueSupplierDatas) const +{ + const IFlowDataPinValueSupplierInterface* ThisAsPinValueSupplier = Cast(this); + + // This function will build the priority-ordered array of data suppliers for a given PinName. + // It works in two modes: + // - Standard case - Add a connected node as the priority supplier, and this node as the default value supplier + // - Exception case - for External data supplied nodes, we recurse (below) to crawl further and add the supplier + // for the external supplier's node. In practice, this is a node (A) connected to a Start node, which is + // supplied by its outer SubGraph node, which sources its values from the nodes tha are connected to the external inputs + // that the subgraph node added as inputs for its instanced subgraph). The external supplier's value has top priority, + // then it falls to the standard case sources (as above). + + // Potentially add this current node as a default value supplier + // (this will be pushed down the priority queue as higher priority suppliers are found) + if (ThisAsPinValueSupplier && IFlowDataPinValueSupplierInterface::Execute_CanSupplyDataPinValues(this)) + { + FFlowPinValueSupplierData NewPinValueSupplier; + NewPinValueSupplier.PinValueSupplier = ThisAsPinValueSupplier; + NewPinValueSupplier.SupplierPinName = PinName; + + // Put this node as the backup supplier + InOutPinValueSupplierDatas.Insert(NewPinValueSupplier, 0); + } + + // If the pin is connected, try to add the connected node as the priority supplier + FFlowPinValueSupplierData ConnectedPinValueSupplier; + FGuid ConnectedNodeGuid; + + if (FindConnectedNodeForPinFast(PinName, &ConnectedNodeGuid, &ConnectedPinValueSupplier.SupplierPinName)) + { + if (const UFlowAsset* FlowAsset = GetFlowAsset()) + { + const UFlowNode* SupplierFlowNode = FlowAsset->GetNode(ConnectedNodeGuid); + + // If the connected node can supply data pin values, insert it into the top of the priority queue + const IFlowDataPinValueSupplierInterface* SupplierFlowNodeAsInterface = Cast(SupplierFlowNode); + if (SupplierFlowNodeAsInterface && IFlowDataPinValueSupplierInterface::Execute_CanSupplyDataPinValues(SupplierFlowNode)) + { + ConnectedPinValueSupplier.PinValueSupplier = SupplierFlowNodeAsInterface; + + InOutPinValueSupplierDatas.Insert(ConnectedPinValueSupplier, 0); + } + + // Exception case for nodes with external suppliers, recurse here to crawl further + // to the external supplier's connected pin as our most preferred source (see block comment above). + if (const IFlowNodeWithExternalDataPinSupplierInterface* HasExternalPinSupplierInterface = Cast(SupplierFlowNode)) + { + if (const UFlowNode* ExternalDataPinSupplierFlowNode = Cast(HasExternalPinSupplierInterface->GetExternalDataPinSupplier())) + { + return ExternalDataPinSupplierFlowNode->TryGetFlowDataPinSupplierDatasForPinName(ConnectedPinValueSupplier.SupplierPinName, InOutPinValueSupplierDatas); + } + } + } + } + + return !InOutPinValueSupplierDatas.IsEmpty(); +} + +bool UFlowNode::TryFindPropertyByPinName( + const FName& PinName, + const FProperty*& OutFoundProperty, + TInstancedStruct& OutFoundInstancedStruct, + EFlowDataPinResolveResult& InOutResult) const +{ + const FName* RemappedPinName = PinNameToBoundPropertyNameMap.Find(PinName); + if (!RemappedPinName) + { + InOutResult = EFlowDataPinResolveResult::FailedUnknownPin; + + return false; + } + + if (!TryFindPropertyByRemappedPinName(*RemappedPinName, OutFoundProperty, OutFoundInstancedStruct, InOutResult)) + { + return false; + } + + return true; +} + +bool UFlowNode::TryFindPropertyByRemappedPinName( + const FName& RemappedPinName, + const FProperty*& OutFoundProperty, + TInstancedStruct& OutFoundInstancedStruct, + EFlowDataPinResolveResult& InOutResult) const +{ + const UClass* ThisClass = GetClass(); + OutFoundProperty = ThisClass->FindPropertyByName(RemappedPinName); + + if (!OutFoundProperty) + { + LogError(FString::Printf(TEXT("Could not find property %s, but expected to"), *RemappedPinName.ToString()), EFlowOnScreenMessageType::Temporary); + + InOutResult = EFlowDataPinResolveResult::FailedWithError; + + return false; + } + + return true; +} + +TSet UFlowNode::GatherConnectedNodes() const { TSet Result; for (const TPair& Connection : Connections) { Result.Emplace(GetFlowAsset()->GetNode(Connection.Value.NodeGuid)); } + return Result; } @@ -356,21 +526,149 @@ FName UFlowNode::GetPinConnectedToNode(const FGuid& OtherNodeGuid) return NAME_None; } -bool UFlowNode::IsInputConnected(const FName& PinName) const +bool UFlowNode::IsInputConnected(const FName& PinName, bool bErrorIfPinNotFound) const +{ + if (const FFlowPin* FlowPin = FindFlowPinByName(PinName, InputPins)) + { + return IsInputConnected(*FlowPin); + } + + if (bErrorIfPinNotFound) + { + LogError(FString::Printf(TEXT("Unknown pin %s"), *PinName.ToString()), EFlowOnScreenMessageType::Temporary); + } + + return false; +} + +bool UFlowNode::IsOutputConnected(const FName& PinName, bool bErrorIfPinNotFound) const { - if (GetFlowAsset()) + if (const FFlowPin* FlowPin = FindFlowPinByName(PinName, OutputPins)) + { + return IsOutputConnected(*FlowPin); + } + + if (bErrorIfPinNotFound) { - for (const TPair& Pair : GetFlowAsset()->Nodes) + LogError(FString::Printf(TEXT("Unknown pin %s"), *PinName.ToString()), EFlowOnScreenMessageType::Temporary); + } + + return false; +} + +FFlowPin* UFlowNode::FindInputPinByName(const FName& PinName) +{ + if (FFlowPin* FlowPin = FindFlowPinByName(PinName, InputPins)) + { + return FlowPin; + } + + return nullptr; +} + +FFlowPin* UFlowNode::FindOutputPinByName(const FName& PinName) +{ + if (FFlowPin* FlowPin = FindFlowPinByName(PinName, OutputPins)) + { + return FlowPin; + } + + return nullptr; +} + +bool UFlowNode::IsInputConnected(const FFlowPin& FlowPin) const +{ + if (!InputPins.Contains(FlowPin.PinName)) + { + return false; + } + + if (FlowPin.IsDataPin()) + { + return FindConnectedNodeForPinFast(FlowPin.PinName); + } + else + { + // We don't cache the input exec pins for fast lookup in Connections, so use the slow path for them: + + return FindConnectedNodeForPinSlow(FlowPin.PinName); + } +} + +bool UFlowNode::IsOutputConnected(const FFlowPin& FlowPin) const +{ + if (!OutputPins.Contains(FlowPin.PinName)) + { + return false; + } + + if (FlowPin.IsExecPin()) + { + return FindConnectedNodeForPinFast(FlowPin.PinName); + } + else + { + // We don't cache the input data pins for fast lookup in Connections, so use the slow path for them: + + return FindConnectedNodeForPinSlow(FlowPin.PinName); + } +} + +bool UFlowNode::FindConnectedNodeForPinFast(const FName& PinName, FGuid* OutGuid, FName* OutConnectedPinName) const +{ + const FConnectedPin* FoundConnectedPin = Connections.Find(PinName); + if (FoundConnectedPin) + { + if (OutGuid) + { + *OutGuid = FoundConnectedPin->NodeGuid; + } + + if (OutConnectedPinName) { - if (Pair.Value) + *OutConnectedPinName = FoundConnectedPin->PinName; + } + } + + return FoundConnectedPin != nullptr; +} + +bool UFlowNode::FindConnectedNodeForPinSlow(const FName& PinName, FGuid* OutGuid, FName* OutConnectedPinName) const +{ + const UFlowAsset* FlowAsset = GetFlowAsset(); + + if (!IsValid(FlowAsset)) + { + return false; + } + + for (const TPair& Pair : FlowAsset->Nodes) + { + const FGuid& ConnectedFromGuid = Pair.Key; + const UFlowNode* ConnectedFromFlowNode = Pair.Value; + + if (!IsValid(ConnectedFromFlowNode)) + { + continue; + } + + for (const TPair& Connection : Pair.Value->Connections) + { + const FConnectedPin& ConnectedPinStruct = Connection.Value; + + if (ConnectedPinStruct.NodeGuid == NodeGuid && ConnectedPinStruct.PinName == PinName) { - for (const TPair& Connection : Pair.Value->Connections) + if (OutGuid) { - if (Connection.Value.NodeGuid == NodeGuid && Connection.Value.PinName == PinName) - { - return true; - } + *OutGuid = ConnectedFromGuid; } + + if (OutConnectedPinName) + { + *OutConnectedPinName = Connection.Key; + } + + return true; } } } @@ -378,9 +676,82 @@ bool UFlowNode::IsInputConnected(const FName& PinName) const return false; } -bool UFlowNode::IsOutputConnected(const FName& PinName) const +// Must implement TrySupplyDataPinAs... for every EFlowPinType +FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); + +FFlowDataPinResult_Bool UFlowNode::TrySupplyDataPinAsBool_Implementation(const FName& PinName) const +{ + return TrySupplyDataPinAsType(PinName); +} + +FFlowDataPinResult_Int UFlowNode::TrySupplyDataPinAsInt_Implementation(const FName& PinName) const +{ + return TrySupplyDataPinAsNumericType(PinName); +} + +FFlowDataPinResult_Float UFlowNode::TrySupplyDataPinAsFloat_Implementation(const FName& PinName) const +{ + return TrySupplyDataPinAsNumericType(PinName); +} + +FFlowDataPinResult_Name UFlowNode::TrySupplyDataPinAsName_Implementation(const FName& PinName) const +{ + return TrySupplyDataPinAsAnyTextType(PinName); +} + +FFlowDataPinResult_String UFlowNode::TrySupplyDataPinAsString_Implementation(const FName& PinName) const +{ + return TrySupplyDataPinAsAnyTextType(PinName); +} + +FFlowDataPinResult_Text UFlowNode::TrySupplyDataPinAsText_Implementation(const FName& PinName) const +{ + return TrySupplyDataPinAsAnyTextType(PinName); +} + +FFlowDataPinResult_Enum UFlowNode::TrySupplyDataPinAsEnum_Implementation(const FName& PinName) const +{ + return TrySupplyDataPinAsEnumType(PinName); +} + +FFlowDataPinResult_Vector UFlowNode::TrySupplyDataPinAsVector_Implementation(const FName& PinName) const +{ + return TrySupplyDataPinAsStructType(PinName); +} + +FFlowDataPinResult_Rotator UFlowNode::TrySupplyDataPinAsRotator_Implementation(const FName& PinName) const +{ + return TrySupplyDataPinAsStructType(PinName); +} + +FFlowDataPinResult_Transform UFlowNode::TrySupplyDataPinAsTransform_Implementation(const FName& PinName) const +{ + return TrySupplyDataPinAsStructType(PinName); +} + +FFlowDataPinResult_GameplayTag UFlowNode::TrySupplyDataPinAsGameplayTag_Implementation(const FName& PinName) const +{ + return TrySupplyDataPinAsStructType(PinName); +} + +FFlowDataPinResult_GameplayTagContainer UFlowNode::TrySupplyDataPinAsGameplayTagContainer_Implementation(const FName& PinName) const +{ + return TrySupplyDataPinAsStructType(PinName); +} + +FFlowDataPinResult_InstancedStruct UFlowNode::TrySupplyDataPinAsInstancedStruct_Implementation(const FName& PinName) const +{ + return TrySupplyDataPinAsStructType(PinName); +} + +FFlowDataPinResult_Object UFlowNode::TrySupplyDataPinAsObject_Implementation(const FName& PinName) const { - return OutputPins.Contains(PinName) && Connections.Contains(PinName); + return TrySupplyDataPinAsUObjectType(PinName); +} + +FFlowDataPinResult_Class UFlowNode::TrySupplyDataPinAsClass_Implementation(const FName& PinName) const +{ + return TrySupplyDataPinAsUClassType(PinName); } void UFlowNode::RecursiveFindNodesByClass(UFlowNode* Node, const TSubclassOf Class, uint8 Depth, TArray& OutNodes) @@ -399,7 +770,7 @@ void UFlowNode::RecursiveFindNodesByClass(UFlowNode* Node, const TSubclassOfGetConnectedNodes()) + for (UFlowNode* ConnectedNode : Node->GatherConnectedNodes()) { RecursiveFindNodesByClass(ConnectedNode, Class, Depth, OutNodes); } @@ -442,6 +813,8 @@ void UFlowNode::TriggerInput(const FName& PinName, const EFlowPinActivationType // record for debugging TArray& Records = InputRecords.FindOrAdd(PinName); Records.Add(FPinRecord(FApp::GetCurrentTime(), ActivationType)); + + LogVerbose(FString::Printf(TEXT("Triggering input %s."), *PinName.ToString())); #endif // UE_BUILD_SHIPPING #if WITH_EDITOR @@ -462,7 +835,7 @@ void UFlowNode::TriggerInput(const FName& PinName, const EFlowPinActivationType switch (SignalMode) { case EFlowSignalMode::Enabled: - ExecuteInput(PinName); + ExecuteInputForSelfAndAddOns(PinName); break; case EFlowSignalMode::Disabled: if (UFlowSettings::Get()->bLogOnSignalDisabled) @@ -504,12 +877,14 @@ void UFlowNode::TriggerOutput(const FName PinName, const bool bFinish /*= false* TArray& Records = OutputRecords.FindOrAdd(PinName); Records.Add(FPinRecord(FApp::GetCurrentTime(), ActivationType)); + LogVerbose(FString::Printf(TEXT("\n Triggering output: %s. bFinish: %s "), *PinName.ToString(), bFinish ? TEXT("true") : TEXT("false"))); + #if WITH_EDITOR if (GEditor && UFlowAsset::GetFlowGraphInterface().IsValid()) { UFlowAsset::GetFlowGraphInterface()->OnOutputTriggered(GraphNode, OutputPins.IndexOfByKey(PinName)); } -#endif // WITH_EDITOR +#endif } else { @@ -517,6 +892,11 @@ void UFlowNode::TriggerOutput(const FName PinName, const bool bFinish /*= false* } #endif // UE_BUILD_SHIPPING +#if WITH_EDITOR + LogVerbose(FString::Printf(TEXT("\n Description: %s"), *GetNodeDescription())); + LogVerbose(FString::Printf(TEXT("\n Status: %s"), *GetStatusStringForNodeAndAddOns())); +#endif + // call the next node if (OutputPins.Contains(PinName) && Connections.Contains(PinName)) { @@ -641,6 +1021,7 @@ TArray UFlowNode::GetPinRecords(const FName& PinName, const EEdGraph return TArray(); } } + #endif FString UFlowNode::GetIdentityTagDescription(const FGameplayTag& Tag) @@ -679,9 +1060,30 @@ UFlowNode* UFlowNode::GetInspectedInstance() const return nullptr; } -FString UFlowNode::GetStatusString() const +FString UFlowNode::GetStatusStringForNodeAndAddOns() const { - return K2_GetStatusString(); + FString CombinedStatusString = GetStatusString(); + + // Give all of the AddOns a chance to add their status strings as well + (void) ForEachAddOnConst( + [&CombinedStatusString](const UFlowNodeAddOn& AddOn) + { + const FString AddOnStatusString = AddOn.GetStatusString(); + + if (!AddOnStatusString.IsEmpty()) + { + if (!CombinedStatusString.IsEmpty()) + { + CombinedStatusString += TEXT("\n"); + } + + CombinedStatusString += AddOnStatusString; + } + + return EFlowForEachAddOnFunctionReturnValue::Continue; + }); + + return CombinedStatusString; } bool UFlowNode::GetStatusBackgroundColor(FLinearColor& OutColor) const diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp index 48c34a175..fe74c19e2 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBase.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -7,6 +7,7 @@ #include "FlowLogChannels.h" #include "FlowSubsystem.h" #include "FlowTypes.h" +#include "Interfaces/FlowDataPinValueSupplierInterface.h" #include "Components/ActorComponent.h" #if WITH_EDITOR @@ -22,10 +23,11 @@ #include "Misc/Paths.h" #include "Serialization/MemoryReader.h" #include "Serialization/MemoryWriter.h" -#include "Engine/Blueprint.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNodeBase) +using namespace EFlowForEachAddOnFunctionReturnValue_Classifiers; + UFlowNodeBase::UFlowNodeBase(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , GraphNode(nullptr) @@ -34,7 +36,8 @@ UFlowNodeBase::UFlowNodeBase(const FObjectInitializer& ObjectInitializer) , bCanDelete(true) , bCanDuplicate(true) , bNodeDeprecated(false) - , NodeStyle(EFlowNodeStyle::Default) + , NodeDisplayStyle(TAG_Flow_NodeDisplayStyle_Node) + , NodeStyle(EFlowNodeStyle::Invalid) , NodeColor(FLinearColor::Black) #endif { @@ -70,8 +73,15 @@ void UFlowNodeBase::InitializeInstance() for (UFlowNodeAddOn* SourceAddOn : SourceAddOns) { // Create a new instance of each AddOn - UFlowNodeAddOn* NewAddOnInstance = NewObject(this, SourceAddOn->GetClass(), NAME_None, RF_Transient, SourceAddOn, false, nullptr); - AddOns.Add(NewAddOnInstance); + if (IsValid(SourceAddOn)) + { + UFlowNodeAddOn* NewAddOnInstance = NewObject(this, SourceAddOn->GetClass(), NAME_None, RF_Transient, SourceAddOn, false, nullptr); + AddOns.Add(NewAddOnInstance); + } + else + { + LogError(FString::Printf(TEXT("Null AddOn found in node %s"), *GetName()), EFlowOnScreenMessageType::Permanent); + } } for (UFlowNodeAddOn* AddOn : AddOns) @@ -122,7 +132,7 @@ void UFlowNodeBase::OnActivate() } } -void UFlowNodeBase::ExecuteInput(const FName& PinName) +void UFlowNodeBase::ExecuteInputForSelfAndAddOns(const FName& PinName) { // AddOns can introduce input pins to Nodes without the Node being aware of the addition. // To ensure that Nodes and AddOns only get the input pins signalled that they expect, @@ -130,15 +140,20 @@ void UFlowNodeBase::ExecuteInput(const FName& PinName) if (IsSupportedInputPinName(PinName)) { - IFlowCoreExecutableInterface::ExecuteInput(PinName); + ExecuteInput(PinName); } for (UFlowNodeAddOn* AddOn : AddOns) { - AddOn->ExecuteInput(PinName); + AddOn->ExecuteInputForSelfAndAddOns(PinName); } } +void UFlowNodeBase::ExecuteInput(const FName& PinName) +{ + IFlowCoreExecutableInterface::ExecuteInput(PinName); +} + void UFlowNodeBase::ForceFinishNode() { for (UFlowNodeAddOn* AddOn : AddOns) @@ -187,6 +202,14 @@ const FFlowPin* UFlowNodeBase::FindFlowPinByName(const FName& PinName, const TAr }); } +FFlowPin* UFlowNodeBase::FindFlowPinByName(const FName& PinName, TArray& FlowPins) +{ + return FlowPins.FindByPredicate([&PinName](FFlowPin& FlowPin) + { + return FlowPin.PinName == PinName; + }); +} + #if WITH_EDITOR TArray UFlowNodeBase::GetContextInputs() const { @@ -195,7 +218,10 @@ TArray UFlowNodeBase::GetContextInputs() const for (const UFlowNodeAddOn* AddOn : AddOns) { - AddOnInputs.Append(AddOn->GetContextInputs()); + if (IsValid(AddOn)) + { + AddOnInputs.Append(AddOn->GetContextInputs()); + } } if (!AddOnInputs.IsEmpty()) @@ -216,7 +242,10 @@ TArray UFlowNodeBase::GetContextOutputs() const for (const UFlowNodeAddOn* AddOn : AddOns) { - AddOnOutputs.Append(AddOn->GetContextOutputs()); + if (IsValid(AddOn)) + { + AddOnOutputs.Append(AddOn->GetContextOutputs()); + } } if (!AddOnOutputs.IsEmpty()) @@ -229,6 +258,11 @@ TArray UFlowNodeBase::GetContextOutputs() const return ContextOutputs; } + +FString UFlowNodeBase::GetStatusString() const +{ + return K2_GetStatusString(); +} #endif // WITH_EDITOR UFlowAsset* UFlowNodeBase::GetFlowAsset() const @@ -358,25 +392,30 @@ IFlowOwnerInterface* UFlowNodeBase::TryGetFlowOwnerInterfaceActor(UObject& RootF return CastChecked(ActorOwner); } -EFlowAddOnAcceptResult UFlowNodeBase::AcceptFlowNodeAddOnChild_Implementation(const UFlowNodeAddOn* AddOnTemplate) const +EFlowAddOnAcceptResult UFlowNodeBase::AcceptFlowNodeAddOnChild_Implementation( + const UFlowNodeAddOn* AddOnTemplate, + const TArray& AdditionalAddOnsToAssumeAreChildren) const { // Subclasses may override this function to allow AddOn children classes return EFlowAddOnAcceptResult::Undetermined; } #if WITH_EDITOR -EFlowAddOnAcceptResult UFlowNodeBase::CheckAcceptFlowNodeAddOnChild(const UFlowNodeAddOn* AddOnTemplate) const +EFlowAddOnAcceptResult UFlowNodeBase::CheckAcceptFlowNodeAddOnChild( + const UFlowNodeAddOn* AddOnTemplate, + const TArray& AdditionalAddOnsToAssumeAreChildren) const { if (!IsValid(AddOnTemplate)) { return EFlowAddOnAcceptResult::Reject; } - static_assert(static_cast<__underlying_type(EFlowAddOnAcceptResult)>(EFlowAddOnAcceptResult::Max) == 3, "This code may need updating if the enum values change"); + FLOW_ASSERT_ENUM_MAX(EFlowAddOnAcceptResult, 3); EFlowAddOnAcceptResult CombinedResult = EFlowAddOnAcceptResult::Undetermined; - const EFlowAddOnAcceptResult AsChildResult = AcceptFlowNodeAddOnChild(AddOnTemplate); // Potential parents of AddOns are allowed to decide their eligible AddOn children + // Potential parents of AddOns are allowed to decide their eligible AddOn children + const EFlowAddOnAcceptResult AsChildResult = AcceptFlowNodeAddOnChild(AddOnTemplate, AdditionalAddOnsToAssumeAreChildren); CombinedResult = CombineFlowAddOnAcceptResult(AsChildResult, CombinedResult); if (CombinedResult == EFlowAddOnAcceptResult::Reject) @@ -385,7 +424,7 @@ EFlowAddOnAcceptResult UFlowNodeBase::CheckAcceptFlowNodeAddOnChild(const UFlowN } // FlowNodeAddOns are allowed to opt in to their parent - const EFlowAddOnAcceptResult AsParentResult = AddOnTemplate->AcceptFlowNodeAddOnParent(this); + const EFlowAddOnAcceptResult AsParentResult = AddOnTemplate->AcceptFlowNodeAddOnParent(this, AdditionalAddOnsToAssumeAreChildren); if (AsParentResult != EFlowAddOnAcceptResult::Reject && AddOnTemplate->IsA()) @@ -402,69 +441,151 @@ EFlowAddOnAcceptResult UFlowNodeBase::CheckAcceptFlowNodeAddOnChild(const UFlowN } #endif // WITH_EDITOR -void UFlowNodeBase::ForEachAddOnConst(const FConstFlowNodeAddOnFunction& Function) const +EFlowForEachAddOnFunctionReturnValue UFlowNodeBase::ForEachAddOnConst(const FConstFlowNodeAddOnFunction& Function) const { + FLOW_ASSERT_ENUM_MAX(EFlowForEachAddOnFunctionReturnValue, 3); + + EFlowForEachAddOnFunctionReturnValue ReturnValue = EFlowForEachAddOnFunctionReturnValue::Continue; + for (const UFlowNodeAddOn* AddOn : AddOns) { - if (IsValid(AddOn)) + if (!IsValid(AddOn)) { - Function(*AddOn); + continue; + } + + ReturnValue = Function(*AddOn); - AddOn->ForEachAddOnConst(Function); + if (!ShouldContinueForEach(ReturnValue)) + { + break; + } + + ReturnValue = AddOn->ForEachAddOnConst(Function); + + if (!ShouldContinueForEach(ReturnValue)) + { + break; } } + + return ReturnValue; } -void UFlowNodeBase::ForEachAddOn(const FFlowNodeAddOnFunction& Function) const +EFlowForEachAddOnFunctionReturnValue UFlowNodeBase::ForEachAddOn(const FFlowNodeAddOnFunction& Function) const { + FLOW_ASSERT_ENUM_MAX(EFlowForEachAddOnFunctionReturnValue, 3); + + EFlowForEachAddOnFunctionReturnValue ReturnValue = EFlowForEachAddOnFunctionReturnValue::Continue; + for (UFlowNodeAddOn* AddOn : AddOns) { - if (IsValid(AddOn)) + if (!IsValid(AddOn)) + { + continue; + } + + ReturnValue = Function(*AddOn); + + if (!ShouldContinueForEach(ReturnValue)) { - Function(*AddOn); + break; + } + + ReturnValue = AddOn->ForEachAddOn(Function); - AddOn->ForEachAddOn(Function); + if (!ShouldContinueForEach(ReturnValue)) + { + break; } } + + return ReturnValue; } -void UFlowNodeBase::ForEachAddOnForClassConst(const UClass& InterfaceOrClass, const FConstFlowNodeAddOnFunction& Function) const +EFlowForEachAddOnFunctionReturnValue UFlowNodeBase::ForEachAddOnForClassConst(const UClass& InterfaceOrClass, const FConstFlowNodeAddOnFunction& Function) const { + FLOW_ASSERT_ENUM_MAX(EFlowForEachAddOnFunctionReturnValue, 3); + + EFlowForEachAddOnFunctionReturnValue ReturnValue = EFlowForEachAddOnFunctionReturnValue::Continue; + for (const UFlowNodeAddOn* AddOn : AddOns) { - if (IsValid(AddOn)) + if (!IsValid(AddOn)) + { + continue; + } + + // InterfaceOrClass can either be the AddOn's UClass (or its superclass) + // or an interface (the UClass version) that its UClass implements + if (AddOn->IsA(&InterfaceOrClass) || AddOn->GetClass()->ImplementsInterface(&InterfaceOrClass)) { - // InterfaceOrClass can either be the AddOn's UClass (or its superclass) - // or an interface (the UClass version) that its UClass implements - if (AddOn->IsA(&InterfaceOrClass) || AddOn->GetClass()->ImplementsInterface(&InterfaceOrClass)) + ReturnValue = Function(*AddOn); + + if (!ShouldContinueForEach(ReturnValue)) { - Function(*AddOn); + break; } + } + + ReturnValue = AddOn->ForEachAddOnForClassConst(InterfaceOrClass, Function); - AddOn->ForEachAddOnForClassConst(InterfaceOrClass, Function); + if (!ShouldContinueForEach(ReturnValue)) + { + break; } } + + return ReturnValue; } -void UFlowNodeBase::ForEachAddOnForClass(const UClass& InterfaceOrClass, const FFlowNodeAddOnFunction& Function) const +EFlowForEachAddOnFunctionReturnValue UFlowNodeBase::ForEachAddOnForClass(const UClass& InterfaceOrClass, const FFlowNodeAddOnFunction& Function) const { + FLOW_ASSERT_ENUM_MAX(EFlowForEachAddOnFunctionReturnValue, 3); + + EFlowForEachAddOnFunctionReturnValue ReturnValue = EFlowForEachAddOnFunctionReturnValue::Continue; + for (UFlowNodeAddOn* AddOn : AddOns) { - if (IsValid(AddOn)) + if (!IsValid(AddOn)) + { + continue; + } + + // InterfaceOrClass can either be the AddOn's UClass (or its superclass) + // or an interface (the UClass version) that its UClass implements + if (AddOn->IsA(&InterfaceOrClass) || AddOn->GetClass()->ImplementsInterface(&InterfaceOrClass)) { - // InterfaceOrClass can either be the AddOn's UClass (or its superclass) - // or an interface (the UClass version) that its UClass implements - if (AddOn->IsA(&InterfaceOrClass) || AddOn->GetClass()->ImplementsInterface(&InterfaceOrClass)) + ReturnValue = Function(*AddOn); + + if (!ShouldContinueForEach(ReturnValue)) { - Function(*AddOn); + break; } + } + + ReturnValue = AddOn->ForEachAddOnForClass(InterfaceOrClass, Function); - AddOn->ForEachAddOnForClass(InterfaceOrClass, Function); + if (!ShouldContinueForEach(ReturnValue)) + { + break; } } + + return ReturnValue; +} + +void UFlowNodeBase::PostLoad() +{ + Super::PostLoad(); + +#if WITH_EDITOR + EnsureNodeDisplayStyle(); +#endif } #if WITH_EDITOR + void UFlowNodeBase::SetGraphNode(UEdGraphNode* NewGraphNode) { GraphNode = NewGraphNode; @@ -522,14 +643,11 @@ FString UFlowNodeBase::GetNodeCategory() const return Category; } -EFlowNodeStyle UFlowNodeBase::GetNodeStyle() const -{ - return NodeStyle; -} - bool UFlowNodeBase::GetDynamicTitleColor(FLinearColor& OutColor) const { - if (NodeStyle == EFlowNodeStyle::Custom) + // Legacy asset support for NodeStyle == EFlowNodeStyle::Custom + if (NodeDisplayStyle == TAG_Flow_NodeDisplayStyle_Custom || + NodeStyle == EFlowNodeStyle::Custom) { OutColor = NodeColor; return true; @@ -606,7 +724,74 @@ FText UFlowNodeBase::GetGeneratedDisplayName() const return GetClass()->GetMetaDataText(NAME_GeneratedDisplayName); } -#endif // WITH_EDITOR + +void UFlowNodeBase::EnsureNodeDisplayStyle() +{ + // Backward compatibility update to convert NodeStyle to NodeDisplayStyle + FLOW_ASSERT_ENUM_MAX(EFlowNodeStyle, 7); + + const FGameplayTag NodeDisplayStylePrev = NodeDisplayStyle; + + switch (NodeStyle) + { + case EFlowNodeStyle::Condition: + { + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Condition; + } + break; + + case EFlowNodeStyle::Default: + { + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node; + } + break; + + case EFlowNodeStyle::InOut: + { + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_InOut; + } + break; + + case EFlowNodeStyle::Latent: + { + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Latent; + } + break; + + case EFlowNodeStyle::Logic: + { + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Logic; + } + break; + + case EFlowNodeStyle::SubGraph: + { + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_SubGraph; + } + break; + + case EFlowNodeStyle::Custom: + { + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Custom; + } + break; + + default: break; + } + + if (GEditor != nullptr && NodeDisplayStyle != NodeDisplayStylePrev) + { + NodeStyle = EFlowNodeStyle::Invalid; + + Modify(); + } +} + +FString UFlowNodeBase::GetNodeDescription() const +{ + return K2_GetNodeDescription(); +} +#endif void UFlowNodeBase::SetNodeConfigText(const FText& NodeConfigText) { @@ -624,13 +809,6 @@ void UFlowNodeBase::UpdateNodeConfigText_Implementation() { } -#if WITH_EDITOR -FString UFlowNodeBase::GetNodeDescription() const -{ - return K2_GetNodeDescription(); -} -#endif // WITH_EDITOR - void UFlowNodeBase::LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType) const { #if !UE_BUILD_SHIPPING @@ -701,12 +879,24 @@ void UFlowNodeBase::LogNote(FString Message) const #endif } +void UFlowNodeBase::LogVerbose(FString Message) const +{ +#if !UE_BUILD_SHIPPING + if (BuildMessage(Message)) + { + // Output Log + UE_LOG(LogFlow, Verbose, TEXT("%s"), *Message); + } +#endif +} + #if !UE_BUILD_SHIPPING bool UFlowNodeBase::BuildMessage(FString& Message) const { - if (GetFlowAsset()->GetTemplateAsset()) // this is runtime log which is should be only called on runtime instances of asset + UFlowAsset* FlowAsset = GetFlowAsset(); + if (FlowAsset && FlowAsset->GetTemplateAsset()) // this is runtime log which is should be only called on runtime instances of asset { - const FString TemplatePath = GetFlowAsset()->GetTemplateAsset()->GetPathName(); + const FString TemplatePath = FlowAsset->GetTemplateAsset()->GetPathName(); Message.Append(TEXT(" --- node ")).Append(GetName()).Append(TEXT(", asset ")).Append(FPaths::GetPath(TemplatePath) / FPaths::GetBaseFilename(TemplatePath)); return true; @@ -715,3 +905,367 @@ bool UFlowNodeBase::BuildMessage(FString& Message) const return false; } #endif + +EFlowDataPinResolveResult UFlowNodeBase::TryResolveDataPinPrerequisites(const FName& PinName, const UFlowNode*& FlowNode, const FFlowPin*& FlowPin, EFlowPinType PinType) const +{ + FlowNode = GetFlowNodeSelfOrOwner(); + + if (!IsValid(FlowNode)) + { + LogError(FString::Printf(TEXT("Unexpected for %s to not have an associated FlowNode"), *GetName()), EFlowOnScreenMessageType::Temporary); + + return EFlowDataPinResolveResult::FailedWithError; + } + + FlowPin = FindFlowPinByName(PinName, FlowNode->GetInputPins()); + if (!FlowPin) + { + return EFlowDataPinResolveResult::FailedMissingPin; + } + + if (FlowPin->GetPinType() != PinType) + { + return EFlowDataPinResolveResult::FailedMismatchedType; + } + + return EFlowDataPinResolveResult::Success; +} + +// Must implement TryResolveDataPinAs...() for every EFlowPinType +FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); + +template +bool TResolveDataPinWorkingData::TrySetupWorkingData(const FName& PinName, const UFlowNodeBase& FlowNodeBase) +{ + DataPinResult.Result = FlowNodeBase.TryResolveDataPinPrerequisites(PinName, FlowNode, FlowPin, PinType); + if (DataPinResult.Result != EFlowDataPinResolveResult::Success) + { + return false; + } + + if (!FlowNode->TryGetFlowDataPinSupplierDatasForPinName(FlowPin->PinName, PinValueSupplierDatas)) + { + return false; + } + + // If we could not build the PinValueDataSuppliers array, + // then the pin must be disconnected and have no default value available. + DataPinResult.Result = EFlowDataPinResolveResult::FailedUnconnected; + + return true; +} + +FFlowDataPinResult_Bool UFlowNodeBase::TryResolveDataPinAsBool(const FName& PinName) const +{ + TResolveDataPinWorkingData WorkData; + if (!WorkData.TrySetupWorkingData(PinName, *this)) + { + return WorkData.DataPinResult; + } + + for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) + { + WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsBool(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); + + if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) + { + return WorkData.DataPinResult; + } + } + + return WorkData.DataPinResult; +} + +FFlowDataPinResult_Int UFlowNodeBase::TryResolveDataPinAsInt(const FName& PinName) const +{ + TResolveDataPinWorkingData WorkData; + if (!WorkData.TrySetupWorkingData(PinName, *this)) + { + return WorkData.DataPinResult; + } + + for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) + { + WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsInt(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); + + if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) + { + return WorkData.DataPinResult; + } + } + + return WorkData.DataPinResult; +} + +FFlowDataPinResult_Float UFlowNodeBase::TryResolveDataPinAsFloat(const FName& PinName) const +{ + TResolveDataPinWorkingData WorkData; + if (!WorkData.TrySetupWorkingData(PinName, *this)) + { + return WorkData.DataPinResult; + } + + for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) + { + WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsFloat(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); + + if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) + { + return WorkData.DataPinResult; + } + } + + return WorkData.DataPinResult; +} + +FFlowDataPinResult_Name UFlowNodeBase::TryResolveDataPinAsName(const FName& PinName) const +{ + TResolveDataPinWorkingData WorkData; + if (!WorkData.TrySetupWorkingData(PinName, *this)) + { + return WorkData.DataPinResult; + } + + for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) + { + WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsName(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); + + if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) + { + return WorkData.DataPinResult; + } + } + + return WorkData.DataPinResult; +} + +FFlowDataPinResult_String UFlowNodeBase::TryResolveDataPinAsString(const FName& PinName) const +{ + TResolveDataPinWorkingData WorkData; + if (!WorkData.TrySetupWorkingData(PinName, *this)) + { + return WorkData.DataPinResult; + } + + for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) + { + WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsString(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); + + if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) + { + return WorkData.DataPinResult; + } + } + + return WorkData.DataPinResult; +} + +FFlowDataPinResult_Text UFlowNodeBase::TryResolveDataPinAsText(const FName& PinName) const +{ + TResolveDataPinWorkingData WorkData; + if (!WorkData.TrySetupWorkingData(PinName, *this)) + { + return WorkData.DataPinResult; + } + + for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) + { + WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsText(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); + + if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) + { + return WorkData.DataPinResult; + } + } + + return WorkData.DataPinResult; +} + +FFlowDataPinResult_Enum UFlowNodeBase::TryResolveDataPinAsEnum(const FName& PinName) const +{ + TResolveDataPinWorkingData WorkData; + if (!WorkData.TrySetupWorkingData(PinName, *this)) + { + return WorkData.DataPinResult; + } + + for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) + { + WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsEnum(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); + + if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) + { + return WorkData.DataPinResult; + } + } + + return WorkData.DataPinResult; +} + +FFlowDataPinResult_Vector UFlowNodeBase::TryResolveDataPinAsVector(const FName& PinName) const +{ + TResolveDataPinWorkingData WorkData; + if (!WorkData.TrySetupWorkingData(PinName, *this)) + { + return WorkData.DataPinResult; + } + + for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) + { + WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsVector(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); + + if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) + { + return WorkData.DataPinResult; + } + } + + return WorkData.DataPinResult; +} + +FFlowDataPinResult_Rotator UFlowNodeBase::TryResolveDataPinAsRotator(const FName& PinName) const +{ + TResolveDataPinWorkingData WorkData; + if (!WorkData.TrySetupWorkingData(PinName, *this)) + { + return WorkData.DataPinResult; + } + + for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) + { + WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsRotator(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); + + if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) + { + return WorkData.DataPinResult; + } + } + + return WorkData.DataPinResult; +} + +FFlowDataPinResult_Transform UFlowNodeBase::TryResolveDataPinAsTransform(const FName& PinName) const +{ + TResolveDataPinWorkingData WorkData; + if (!WorkData.TrySetupWorkingData(PinName, *this)) + { + return WorkData.DataPinResult; + } + + for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) + { + WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsTransform(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); + + if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) + { + return WorkData.DataPinResult; + } + } + + return WorkData.DataPinResult; +} + +FFlowDataPinResult_GameplayTag UFlowNodeBase::TryResolveDataPinAsGameplayTag(const FName& PinName) const +{ + TResolveDataPinWorkingData WorkData; + if (!WorkData.TrySetupWorkingData(PinName, *this)) + { + return WorkData.DataPinResult; + } + + for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) + { + WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsGameplayTag(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); + + if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) + { + return WorkData.DataPinResult; + } + } + + return WorkData.DataPinResult; +} + +FFlowDataPinResult_GameplayTagContainer UFlowNodeBase::TryResolveDataPinAsGameplayTagContainer(const FName& PinName) const +{ + TResolveDataPinWorkingData WorkData; + if (!WorkData.TrySetupWorkingData(PinName, *this)) + { + return WorkData.DataPinResult; + } + + for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) + { + WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsGameplayTagContainer(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); + + if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) + { + return WorkData.DataPinResult; + } + } + + return WorkData.DataPinResult; +} + +FFlowDataPinResult_InstancedStruct UFlowNodeBase::TryResolveDataPinAsInstancedStruct(const FName& PinName) const +{ + TResolveDataPinWorkingData WorkData; + if (!WorkData.TrySetupWorkingData(PinName, *this)) + { + return WorkData.DataPinResult; + } + + for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) + { + WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsInstancedStruct(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); + + if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) + { + return WorkData.DataPinResult; + } + } + + return WorkData.DataPinResult; +} + +FFlowDataPinResult_Object UFlowNodeBase::TryResolveDataPinAsObject(const FName& PinName) const +{ + TResolveDataPinWorkingData WorkData; + if (!WorkData.TrySetupWorkingData(PinName, *this)) + { + return WorkData.DataPinResult; + } + + for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) + { + WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsObject(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); + + if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) + { + return WorkData.DataPinResult; + } + } + + return WorkData.DataPinResult; +} + +FFlowDataPinResult_Class UFlowNodeBase::TryResolveDataPinAsClass(const FName& PinName) const +{ + TResolveDataPinWorkingData WorkData; + if (!WorkData.TrySetupWorkingData(PinName, *this)) + { + return WorkData.DataPinResult; + } + + for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) + { + WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsClass(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); + + if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) + { + return WorkData.DataPinResult; + } + } + + return WorkData.DataPinResult; +} diff --git a/Source/Flow/Private/Nodes/FlowPin.cpp b/Source/Flow/Private/Nodes/FlowPin.cpp index b6808ece0..92565bf6d 100644 --- a/Source/Flow/Private/Nodes/FlowPin.cpp +++ b/Source/Flow/Private/Nodes/FlowPin.cpp @@ -1,11 +1,17 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "Nodes/FlowPin.h" +#include "FlowLogChannels.h" +#include "GameplayTagContainer.h" +#include "InstancedStruct.h" #include "Misc/DateTime.h" +#include "Misc/MessageDialog.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowPin) +#define LOCTEXT_NAMESPACE "FlowPin" + ////////////////////////////////////////////////////////////////////////// // Pin Record @@ -120,3 +126,304 @@ bool FFlowPinTrait::IsHit() const return bHit; } +////////////////////////////////////////////////////////////////////////// +// Flow Pin + +TArray FFlowPin::FlowPinTypeEnumValuesWithoutSpaces; + +const FName FFlowPin::MetadataKey_SourceForOutputFlowPin = "SourceForOutputFlowPin"; +const FName FFlowPin::MetadataKey_DefaultForInputFlowPin = "DefaultForInputFlowPin"; +const FName FFlowPin::MetadataKey_FlowPinType = "FlowPinType"; + +const TArray& FFlowPin::GetFlowPinTypeEnumValuesWithoutSpaces() +{ + if (FlowPinTypeEnumValuesWithoutSpaces.IsEmpty()) + { + FlowPinTypeEnumValuesWithoutSpaces.Reserve(static_cast(EFlowPinType::Max)); + + // Do a one-time caching of the string-names for this enum, + // since we need to de-spacify it and everything.... + + for (EFlowPinType PinType : TEnumRange()) + { + FString StringValue = UEnum::GetDisplayValueAsText(PinType).ToString(); + StringValue.RemoveSpacesInline(); + + FlowPinTypeEnumValuesWithoutSpaces.Add(FName(StringValue)); + } + } + + return FlowPinTypeEnumValuesWithoutSpaces; +} + +bool FFlowPin::ArePinArraysMatchingNamesAndTypes(const TArray& Left, const TArray& Right) +{ + if (Left.Num() != Right.Num()) + { + return false; + } + + for (int32 Index = 0; Index < Left.Num(); ++Index) + { + const FFlowPin& LeftPin = Left[Index]; + const FFlowPin& RightPin = Right[Index]; + + if (!DoPinsMatchNamesAndTypes(LeftPin, RightPin)) + { + return false; + } + } + + return true; +} + +void FFlowPin::SetPinType(EFlowPinType InFlowPinType, UObject* SubCategoryObject) +{ + if (PinType == InFlowPinType) + { + return; + } + + PinType = InFlowPinType; + + PinSubCategoryObject = SubCategoryObject; + + TrySetStructSubCategoryObjectFromPinType(); +} + +void FFlowPin::TrySetStructSubCategoryObjectFromPinType() +{ + FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); + + // Set the PinSubCategoryObject based on the PinType (if appropriate) + switch (PinType) + { + case EFlowPinType::Vector: + { + PinSubCategoryObject = TBaseStructure::Get(); + } + break; + + case EFlowPinType::Rotator: + { + PinSubCategoryObject = TBaseStructure::Get(); + } + break; + + case EFlowPinType::Transform: + { + PinSubCategoryObject = TBaseStructure::Get(); + } + break; + + case EFlowPinType::GameplayTag: + { + PinSubCategoryObject = TBaseStructure::Get(); + } + break; + + case EFlowPinType::GameplayTagContainer: + { + PinSubCategoryObject = TBaseStructure::Get(); + } + break; + + case EFlowPinType::InstancedStruct: + { + PinSubCategoryObject = TBaseStructure::Get(); + } + break; + + case EFlowPinType::Enum: + { + // Clear the PinSubCategoryObject if it is not an Enum + UObject* PinSubCategoryObjectPtr = PinSubCategoryObject.Get(); + if (PinSubCategoryObjectPtr && !PinSubCategoryObjectPtr->IsA()) + { + PinSubCategoryObject = nullptr; + } + } + break; + + case EFlowPinType::Object: + { + // Clear the PinSubCategoryObject if it is not a Object + UObject* PinSubCategoryObjectPtr = PinSubCategoryObject.Get(); + if (PinSubCategoryObjectPtr && !PinSubCategoryObjectPtr->IsA()) + { + PinSubCategoryObject = nullptr; + } + } + break; + + case EFlowPinType::Class: + { + // Clear the PinSubCategoryObject if it is not a Class + UObject* PinSubCategoryObjectPtr = PinSubCategoryObject.Get(); + if (PinSubCategoryObjectPtr && !PinSubCategoryObjectPtr->IsA()) + { + PinSubCategoryObject = nullptr; + } + } + break; + + default: + { + // Clear the PinSubCategoryObject for all PinTypes that do not use it. + PinSubCategoryObject = nullptr; + } + break; + } +} + +const FName& FFlowPin::GetPinCategoryFromPinType(EFlowPinType FlowPinType) +{ + FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); + + switch (FlowPinType) + { + case EFlowPinType::Exec: + return FFlowPin::PC_Exec; + + case EFlowPinType::Bool: + return FFlowPin::PC_Boolean; + + case EFlowPinType::Int: + return FFlowPin::PC_Int; + + case EFlowPinType::Float: + return FFlowPin::PC_Float; + + case EFlowPinType::Name: + return FFlowPin::PC_Name; + + case EFlowPinType::String: + return FFlowPin::PC_String; + + case EFlowPinType::Text: + return FFlowPin::PC_Text; + + case EFlowPinType::Enum: + return FFlowPin::PC_Enum; + + case EFlowPinType::Vector: + case EFlowPinType::Rotator: + case EFlowPinType::Transform: + case EFlowPinType::GameplayTag: + case EFlowPinType::GameplayTagContainer: + case EFlowPinType::InstancedStruct: + return FFlowPin::PC_Struct; + + case EFlowPinType::Object: + return FFlowPin::PC_Object; + + case EFlowPinType::Class: + return FFlowPin::PC_Class; + + default: + { + static const FName NameNone = NAME_None; + return NameNone; + } + } +} + +#if WITH_EDITOR +void FFlowPin::PostEditChangedPinTypeOrSubCategorySource() +{ + // PinTypes with PinSubCategoryObjects will need to update this function + FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); + + // Must be called from PostEditChangeProperty() by an owning UObject + + switch (PinType) + { + + case EFlowPinType::Class: + { + PinSubCategoryObject = SubCategoryClassFilter; + } + break; + + case EFlowPinType::Object: + { + PinSubCategoryObject = SubCategoryObjectFilter; + } + break; + + case EFlowPinType::Enum: + { + if (!SubCategoryEnumName.IsEmpty()) + { + SubCategoryEnumClass = UClass::TryFindTypeSlow(SubCategoryEnumName, EFindFirstObjectOptions::ExactClass); + + if (SubCategoryEnumClass != nullptr && !FFlowPin::ValidateEnum(*SubCategoryEnumClass)) + { + SubCategoryEnumClass = nullptr; + } + } + + PinSubCategoryObject = SubCategoryEnumClass; + } + break; + + default: + { + TrySetStructSubCategoryObjectFromPinType(); + } + break; + } +} + +FText FFlowPin::BuildHeaderText() const +{ + const FText PinNameToUse = !PinFriendlyName.IsEmpty() ? PinFriendlyName : FText::FromName(PinName); + + if (PinType == EFlowPinType::Exec) + { + return PinNameToUse; + } + else + { + return FText::Format(LOCTEXT("FlowPinNameAndType", "{0} ({1})"), { PinNameToUse, UEnum::GetDisplayValueAsText(PinType) }); + } +} + +bool FFlowPin::ValidateEnum(const UEnum& EnumType) +{ + // This function copied and adapted from UBlackboardKeyType_Enum::ValidateEnum(), + // because it is inaccessible w/o AIModule and private access + + bool bAllValid = true; + + // Do not test the max value (if present) since it is an internal value and users don't have access to it + const int32 NumEnums = EnumType.ContainsExistingMax() ? EnumType.NumEnums() - 1 : EnumType.NumEnums(); + for (int32 i = 0; i < NumEnums; i++) + { + // Enum data type is uint8 (based on UBlackboardKeyType_Enum::ValidateEnum()) + typedef uint8 FDataType; + + const int64 Value = EnumType.GetValueByIndex(i); + if (Value < std::numeric_limits::min() || Value > std::numeric_limits::max()) + { + UE_LOG(LogFlow, Error, TEXT("'%s' value %d is outside the range of supported key values for enum [%d, %d].") + , *EnumType.GenerateFullEnumName(*EnumType.GetDisplayNameTextByIndex(i).ToString()) + , Value, std::numeric_limits::min(), std::numeric_limits::max()); + + bAllValid = false; + } + } + + if (!bAllValid) + { + FMessageDialog::Open(EAppMsgType::Ok, + NSLOCTEXT("FlowPin" + , "Unsupported enumeration" + , "Specified enumeration contains one or more values outside supported value range for enum keys and can not be used for Flow Data Pins. See log for details.")); + } + + return bAllValid; +} +#endif //WITH_EDITOR + +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalAND.cpp b/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalAND.cpp index fa85414c9..3e74094e8 100644 --- a/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalAND.cpp +++ b/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalAND.cpp @@ -9,7 +9,7 @@ UFlowNode_LogicalAND::UFlowNode_LogicalAND(const FObjectInitializer& ObjectIniti { #if WITH_EDITOR Category = TEXT("Operators"); - NodeStyle = EFlowNodeStyle::Logic; + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Logic; #endif SetNumberedInputPins(0, 1); diff --git a/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp b/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp index 772cb2209..17caf54f2 100644 --- a/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp +++ b/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp @@ -12,7 +12,7 @@ UFlowNode_LogicalOR::UFlowNode_LogicalOR(const FObjectInitializer& ObjectInitial { #if WITH_EDITOR Category = TEXT("Operators"); - NodeStyle = EFlowNodeStyle::Logic; + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Logic; #endif SetNumberedInputPins(0, 1); diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Branch.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Branch.cpp index fea562c69..e48023170 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Branch.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Branch.cpp @@ -14,7 +14,7 @@ UFlowNode_Branch::UFlowNode_Branch(const FObjectInitializer& ObjectInitializer) { #if WITH_EDITOR Category = TEXT("Route"); - NodeStyle = EFlowNodeStyle::Logic; + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Logic; #endif InputPins.Empty(); InputPins.Add(FFlowPin(INPIN_Evaluate)); @@ -26,14 +26,16 @@ UFlowNode_Branch::UFlowNode_Branch(const FObjectInitializer& ObjectInitializer) AllowedSignalModes = { EFlowSignalMode::Enabled, EFlowSignalMode::Disabled }; } -EFlowAddOnAcceptResult UFlowNode_Branch::AcceptFlowNodeAddOnChild_Implementation(const UFlowNodeAddOn* AddOnTemplate) const +EFlowAddOnAcceptResult UFlowNode_Branch::AcceptFlowNodeAddOnChild_Implementation( + const UFlowNodeAddOn* AddOnTemplate, + const TArray& AdditionalAddOnsToAssumeAreChildren) const { if (IFlowPredicateInterface::ImplementsInterfaceSafe(AddOnTemplate)) { return EFlowAddOnAcceptResult::TentativeAccept; } - return Super::AcceptFlowNodeAddOnChild_Implementation(AddOnTemplate); + return Super::AcceptFlowNodeAddOnChild_Implementation(AddOnTemplate, AdditionalAddOnsToAssumeAreChildren); } void UFlowNode_Branch::ExecuteInput(const FName& PinName) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Counter.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Counter.cpp index 4456c45ee..9d8978fb8 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Counter.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Counter.cpp @@ -11,7 +11,7 @@ UFlowNode_Counter::UFlowNode_Counter(const FObjectInitializer& ObjectInitializer { #if WITH_EDITOR Category = TEXT("Route"); - NodeStyle = EFlowNodeStyle::Condition; + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Condition; #endif InputPins.Empty(); diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_CustomEventBase.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_CustomEventBase.cpp index 453673523..e9cb3fd10 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_CustomEventBase.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_CustomEventBase.cpp @@ -10,7 +10,7 @@ UFlowNode_CustomEventBase::UFlowNode_CustomEventBase(const FObjectInitializer& O { #if WITH_EDITOR Category = TEXT("Route"); - NodeStyle = EFlowNodeStyle::InOut; + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_InOut; #endif AllowedSignalModes = {EFlowSignalMode::Enabled, EFlowSignalMode::Disabled}; diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp index e413813ed..d6d7f6a57 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp @@ -10,7 +10,7 @@ UFlowNode_ExecutionMultiGate::UFlowNode_ExecutionMultiGate(const FObjectInitiali { #if WITH_EDITOR Category = TEXT("Route"); - NodeStyle = EFlowNodeStyle::Logic; + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Logic; #endif FString ResetPinTooltip = TEXT("Finish work of this node."); diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp index 56eda0348..0b9e5e5c0 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp @@ -10,7 +10,7 @@ UFlowNode_ExecutionSequence::UFlowNode_ExecutionSequence(const FObjectInitialize { #if WITH_EDITOR Category = TEXT("Route"); - NodeStyle = EFlowNodeStyle::Logic; + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Logic; #endif SetNumberedOutputPins(0, 1); diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp index a32dffdd4..0b16ec5b0 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp @@ -9,7 +9,7 @@ UFlowNode_Finish::UFlowNode_Finish(const FObjectInitializer& ObjectInitializer) { #if WITH_EDITOR Category = TEXT("Route"); - NodeStyle = EFlowNodeStyle::InOut; + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_InOut; #endif OutputPins = {}; diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp index cd6fa6b97..8aed45e11 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp @@ -9,15 +9,265 @@ UFlowNode_Start::UFlowNode_Start(const FObjectInitializer& ObjectInitializer) { #if WITH_EDITOR Category = TEXT("Route"); - NodeStyle = EFlowNodeStyle::InOut; + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_InOut; bCanDelete = bCanDuplicate = false; #endif - InputPins = {}; - AllowedSignalModes = {EFlowSignalMode::Enabled, EFlowSignalMode::Disabled}; + OutputPins = { UFlowNode::DefaultOutputPin }; } void UFlowNode_Start::ExecuteInput(const FName& PinName) { TriggerFirstOutput(true); } + +void UFlowNode_Start::SetDataPinValueSupplier(IFlowDataPinValueSupplierInterface* DataPinValueSupplier) +{ + FlowDataPinValueSupplierInterface = Cast(DataPinValueSupplier); +} + +#if WITH_EDITOR + +bool UFlowNode_Start::TryAppendExternalInputPins(TArray& InOutPins) const +{ + // Add pins for all of the Flow DataPin Properties + for (const FFlowNamedDataPinOutputProperty& DataPinProperty : OutputProperties) + { + if (DataPinProperty.IsValid()) + { + InOutPins.AddUnique(DataPinProperty.CreateFlowPin()); + } + } + + return !OutputProperties.IsEmpty(); +} + +#endif // WITH_EDITOR + +// Must implement TrySupplyDataPinAs... for every EFlowPinType +FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); + +FFlowDataPinResult_Bool UFlowNode_Start::TrySupplyDataPinAsBool_Implementation(const FName& PinName) const +{ + if (FlowDataPinValueSupplierInterface) + { + FFlowDataPinResult_Bool SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsBool(FlowDataPinValueSupplierInterface.GetObject(), PinName); + + if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) + { + return SuppliedResult; + } + } + + return Super::TrySupplyDataPinAsBool_Implementation(PinName); +} + +FFlowDataPinResult_Int UFlowNode_Start::TrySupplyDataPinAsInt_Implementation(const FName& PinName) const +{ + if (FlowDataPinValueSupplierInterface) + { + FFlowDataPinResult_Int SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsInt(FlowDataPinValueSupplierInterface.GetObject(), PinName); + + if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) + { + return SuppliedResult; + } + } + + return Super::TrySupplyDataPinAsInt_Implementation(PinName); +} + +FFlowDataPinResult_Float UFlowNode_Start::TrySupplyDataPinAsFloat_Implementation(const FName& PinName) const +{ + if (FlowDataPinValueSupplierInterface) + { + FFlowDataPinResult_Float SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsFloat(FlowDataPinValueSupplierInterface.GetObject(), PinName); + + if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) + { + return SuppliedResult; + } + } + + return Super::TrySupplyDataPinAsFloat_Implementation(PinName); +} + +FFlowDataPinResult_Name UFlowNode_Start::TrySupplyDataPinAsName_Implementation(const FName& PinName) const +{ + if (FlowDataPinValueSupplierInterface) + { + FFlowDataPinResult_Name SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsName(FlowDataPinValueSupplierInterface.GetObject(), PinName); + + if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) + { + return SuppliedResult; + } + } + + return Super::TrySupplyDataPinAsName_Implementation(PinName); +} + +FFlowDataPinResult_String UFlowNode_Start::TrySupplyDataPinAsString_Implementation(const FName& PinName) const +{ + if (FlowDataPinValueSupplierInterface) + { + FFlowDataPinResult_String SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsString(FlowDataPinValueSupplierInterface.GetObject(), PinName); + + if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) + { + return SuppliedResult; + } + } + + return Super::TrySupplyDataPinAsString_Implementation(PinName); +} + +FFlowDataPinResult_Text UFlowNode_Start::TrySupplyDataPinAsText_Implementation(const FName& PinName) const +{ + if (FlowDataPinValueSupplierInterface) + { + FFlowDataPinResult_Text SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsText(FlowDataPinValueSupplierInterface.GetObject(), PinName); + + if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) + { + return SuppliedResult; + } + } + + return Super::TrySupplyDataPinAsText_Implementation(PinName); +} + +FFlowDataPinResult_Enum UFlowNode_Start::TrySupplyDataPinAsEnum_Implementation(const FName& PinName) const +{ + if (FlowDataPinValueSupplierInterface) + { + FFlowDataPinResult_Enum SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsEnum(FlowDataPinValueSupplierInterface.GetObject(), PinName); + + if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) + { + return SuppliedResult; + } + } + + return Super::TrySupplyDataPinAsEnum_Implementation(PinName); +} + +FFlowDataPinResult_Vector UFlowNode_Start::TrySupplyDataPinAsVector_Implementation(const FName& PinName) const +{ + if (FlowDataPinValueSupplierInterface) + { + FFlowDataPinResult_Vector SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsVector(FlowDataPinValueSupplierInterface.GetObject(), PinName); + + if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) + { + return SuppliedResult; + } + } + + return Super::TrySupplyDataPinAsVector_Implementation(PinName); +} + +FFlowDataPinResult_Rotator UFlowNode_Start::TrySupplyDataPinAsRotator_Implementation(const FName& PinName) const +{ + if (FlowDataPinValueSupplierInterface) + { + FFlowDataPinResult_Rotator SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsRotator(FlowDataPinValueSupplierInterface.GetObject(), PinName); + + if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) + { + return SuppliedResult; + } + } + + return Super::TrySupplyDataPinAsRotator_Implementation(PinName); +} + +FFlowDataPinResult_Transform UFlowNode_Start::TrySupplyDataPinAsTransform_Implementation(const FName& PinName) const +{ + if (FlowDataPinValueSupplierInterface) + { + FFlowDataPinResult_Transform SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsTransform(FlowDataPinValueSupplierInterface.GetObject(), PinName); + + if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) + { + return SuppliedResult; + } + } + + return Super::TrySupplyDataPinAsTransform_Implementation(PinName); +} + +FFlowDataPinResult_GameplayTag UFlowNode_Start::TrySupplyDataPinAsGameplayTag_Implementation(const FName& PinName) const +{ + if (FlowDataPinValueSupplierInterface) + { + FFlowDataPinResult_GameplayTag SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsGameplayTag(FlowDataPinValueSupplierInterface.GetObject(), PinName); + + if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) + { + return SuppliedResult; + } + } + + return Super::TrySupplyDataPinAsGameplayTag_Implementation(PinName); +} + +FFlowDataPinResult_GameplayTagContainer UFlowNode_Start::TrySupplyDataPinAsGameplayTagContainer_Implementation(const FName& PinName) const +{ + if (FlowDataPinValueSupplierInterface) + { + FFlowDataPinResult_GameplayTagContainer SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsGameplayTagContainer(FlowDataPinValueSupplierInterface.GetObject(), PinName); + + if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) + { + return SuppliedResult; + } + } + + return Super::TrySupplyDataPinAsGameplayTagContainer_Implementation(PinName); +} + +FFlowDataPinResult_InstancedStruct UFlowNode_Start::TrySupplyDataPinAsInstancedStruct_Implementation(const FName& PinName) const +{ + if (FlowDataPinValueSupplierInterface) + { + FFlowDataPinResult_InstancedStruct SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsInstancedStruct(FlowDataPinValueSupplierInterface.GetObject(), PinName); + + if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) + { + return SuppliedResult; + } + } + + return Super::TrySupplyDataPinAsInstancedStruct_Implementation(PinName); +} + +FFlowDataPinResult_Object UFlowNode_Start::TrySupplyDataPinAsObject_Implementation(const FName& PinName) const +{ + if (FlowDataPinValueSupplierInterface) + { + FFlowDataPinResult_Object SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsObject(FlowDataPinValueSupplierInterface.GetObject(), PinName); + + if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) + { + return SuppliedResult; + } + } + + return Super::TrySupplyDataPinAsObject_Implementation(PinName); +} + +FFlowDataPinResult_Class UFlowNode_Start::TrySupplyDataPinAsClass_Implementation(const FName& PinName) const +{ + if (FlowDataPinValueSupplierInterface) + { + FFlowDataPinResult_Class SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsClass(FlowDataPinValueSupplierInterface.GetObject(), PinName); + + if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) + { + return SuppliedResult; + } + } + + return Super::TrySupplyDataPinAsClass_Implementation(PinName); +} diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp index d5cda58b5..1e936d247 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp @@ -3,10 +3,14 @@ #include "Nodes/Route/FlowNode_SubGraph.h" #include "FlowAsset.h" +#include "FlowSettings.h" #include "FlowSubsystem.h" +#include "Interfaces/FlowNodeWithExternalDataPinSupplierInterface.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_SubGraph) +#define LOCTEXT_NAMESPACE "FlowNode_SubGraph" + FFlowPin UFlowNode_SubGraph::StartPin(TEXT("Start")); FFlowPin UFlowNode_SubGraph::FinishPin(TEXT("Finish")); @@ -16,7 +20,7 @@ UFlowNode_SubGraph::UFlowNode_SubGraph(const FObjectInitializer& ObjectInitializ { #if WITH_EDITOR Category = TEXT("Route"); - NodeStyle = EFlowNodeStyle::SubGraph; + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_SubGraph; AllowedAssignedAssetClasses = {UFlowAsset::StaticClass()}; #endif @@ -99,9 +103,25 @@ void UFlowNode_SubGraph::OnLoad_Implementation() } #if WITH_EDITOR + +FText UFlowNode_SubGraph::GetNodeTitle() const +{ + if (UFlowSettings::Get()->bUseAdaptiveNodeTitles && !Asset.IsNull()) + { + return FText::Format(LOCTEXT("SubGraphTitle", "{0}\n{1}"), { Super::GetNodeTitle(), FText::FromString(Asset.ToSoftObjectPath().GetAssetName()) }); + } + + return Super::GetNodeTitle(); +} + FString UFlowNode_SubGraph::GetNodeDescription() const { - return Asset.IsNull() ? FString() : Asset.ToSoftObjectPath().GetAssetName(); + if (!UFlowSettings::Get()->bUseAdaptiveNodeTitles && !Asset.IsNull()) + { + return Asset.ToSoftObjectPath().GetAssetName(); + } + + return Super::GetNodeDescription();; } UObject* UFlowNode_SubGraph::GetAssetToEdit() @@ -122,40 +142,77 @@ EDataValidationResult UFlowNode_SubGraph::ValidateNode() TArray UFlowNode_SubGraph::GetContextInputs() const { - TArray EventNames; + TArray ContextInputPins = Super::GetContextInputs(); if (!Asset.IsNull()) { - Asset.LoadSynchronous(); + (void) Asset.LoadSynchronous(); + for (const FName& PinName : Asset.Get()->GetCustomInputs()) { if (!PinName.IsNone()) { - EventNames.Emplace(PinName); + ContextInputPins.AddUnique(FFlowPin(PinName)); } } } - return EventNames; + return ContextInputPins; } TArray UFlowNode_SubGraph::GetContextOutputs() const { - TArray Pins; + TArray ContextOutputPins = Super::GetContextOutputs(); if (!Asset.IsNull()) { - Asset.LoadSynchronous(); + (void) Asset.LoadSynchronous(); + for (const FName& PinName : Asset.Get()->GetCustomOutputs()) { if (!PinName.IsNone()) { - Pins.Emplace(PinName); + ContextOutputPins.AddUnique(FFlowPin(PinName)); } } } - return Pins; + return ContextOutputPins; +} + +void UFlowNode_SubGraph::AutoGenerateDataPins( + TMap& InOutPinNameToBoundPropertyNameMap, + TArray& InOutInputDataPins, + TArray& InOutOutputDataPins) const +{ + if (Asset.IsNull()) + { + return; + } + + (void) Asset.LoadSynchronous(); + + for (auto& KV : Asset->Nodes) + { + UFlowNode* FlowNode = KV.Value; + + if (IFlowNodeWithExternalDataPinSupplierInterface* ExternalPinSuppliedNode = Cast(FlowNode)) + { + // If subgraph's current flownode uses an external data supplier (that will be this subgraph node), + // We need to scrape the external input pins from the node and add them to our auto-generated pins list + + TArray ExternalInputPins; + if (ExternalPinSuppliedNode->TryAppendExternalInputPins(ExternalInputPins)) + { + for (const FFlowPin& FlowPin : ExternalInputPins) + { + InOutPinNameToBoundPropertyNameMap.Add(FlowPin.PinName, FlowPin.PinName); + } + + InOutInputDataPins.Append(ExternalInputPins); + } + } + } } void UFlowNode_SubGraph::PostLoad() @@ -189,6 +246,12 @@ void UFlowNode_SubGraph::PostEditChangeProperty(FPropertyChangedEvent& PropertyC } } +bool UFlowNode_SubGraph::CanSupplyDataPinValues_Implementation() const +{ + // SubGraph node cannot supply data-pin values directly (they are created via AutoGenerateDataPins instead) + return false; +} + void UFlowNode_SubGraph::SubscribeToAssetChanges() { if (Asset) @@ -204,3 +267,5 @@ void UFlowNode_SubGraph::SubscribeToAssetChanges() } } #endif + +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp index 87fcfc9d5..06a4c56e5 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp @@ -1,23 +1,29 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "Nodes/Route/FlowNode_Timer.h" +#include "FlowSettings.h" #include "Engine/World.h" #include "TimerManager.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_Timer) +#define LOCTEXT_NAMESPACE "FlowNode_Timer" + +FName UFlowNode_Timer::INPIN_CompletionTime; + UFlowNode_Timer::UFlowNode_Timer(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , CompletionTime(1.0f) , StepTime(0.0f) + , ResolvedCompletionTime(0.0f) , SumOfSteps(0.0f) , RemainingCompletionTime(0.0f) , RemainingStepTime(0.0f) { #if WITH_EDITOR Category = TEXT("Route"); - NodeStyle = EFlowNodeStyle::Latent; + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Latent; #endif InputPins.Add(FFlowPin(TEXT("Skip"))); @@ -27,6 +33,17 @@ UFlowNode_Timer::UFlowNode_Timer(const FObjectInitializer& ObjectInitializer) OutputPins.Add(FFlowPin(TEXT("Completed"))); OutputPins.Add(FFlowPin(TEXT("Step"))); OutputPins.Add(FFlowPin(TEXT("Skipped"))); + + INPIN_CompletionTime = GET_MEMBER_NAME_CHECKED(UFlowNode_Timer, CompletionTime); +} + +void UFlowNode_Timer::InitializeInstance() +{ + Super::InitializeInstance(); + + // Initialize to the configured value, + // but we will overwrite this with the results of ResolveCompletionTime() when the timer is started + ResolvedCompletionTime = CompletionTime; } void UFlowNode_Timer::ExecuteInput(const FName& PinName) @@ -60,9 +77,10 @@ void UFlowNode_Timer::SetTimer() GetWorld()->GetTimerManager().SetTimer(StepTimerHandle, this, &UFlowNode_Timer::OnStep, StepTime, true); } - if (CompletionTime > UE_KINDA_SMALL_NUMBER) + ResolvedCompletionTime = ResolveCompletionTime(); + if (ResolvedCompletionTime > UE_KINDA_SMALL_NUMBER) { - GetWorld()->GetTimerManager().SetTimer(CompletionTimerHandle, this, &UFlowNode_Timer::OnCompletion, CompletionTime, false); + GetWorld()->GetTimerManager().SetTimer(CompletionTimerHandle, this, &UFlowNode_Timer::OnCompletion, ResolvedCompletionTime, false); } else { @@ -86,11 +104,28 @@ void UFlowNode_Timer::Restart() SetTimer(); } +float UFlowNode_Timer::ResolveCompletionTime() const +{ + // Get the CompletionTime from either the default (property) or the data pin (if connected) + FFlowDataPinResult_Float CompletionTimeResult = TryResolveDataPinAsFloat(INPIN_CompletionTime); + + if (CompletionTimeResult.Result == EFlowDataPinResolveResult::FailedMissingPin) + { + // Handle lookup of a UFlowNode_Timer that predated DataPins + CompletionTimeResult.Result = EFlowDataPinResolveResult::Success; + CompletionTimeResult.Value = CompletionTime; + } + + check(CompletionTimeResult.Result == EFlowDataPinResolveResult::Success); + + return static_cast(CompletionTimeResult.Value); +} + void UFlowNode_Timer::OnStep() { SumOfSteps += StepTime; - if (SumOfSteps >= CompletionTime) + if (SumOfSteps >= ResolvedCompletionTime) { TriggerOutput(TEXT("Completed"), true); } @@ -155,33 +190,71 @@ void UFlowNode_Timer::OnLoad_Implementation() } #if WITH_EDITOR -FString UFlowNode_Timer::GetNodeDescription() const + +void UFlowNode_Timer::UpdateNodeConfigText_Implementation() { - if (CompletionTime > UE_KINDA_SMALL_NUMBER) + constexpr bool bErrorIfInputPinNotFound = false; + const bool bIsInputConnected = IsInputConnected(INPIN_CompletionTime); + + if (bIsInputConnected) { + // CompletionTime will be sourced from the data pin + if (StepTime > 0.0f) { - return FString::Printf(TEXT("%.*f, step by %.*f"), 2, CompletionTime, 2, StepTime); + const FString StepTimeString = FString::Printf(TEXT("%.*f"), 2, StepTime); + + SetNodeConfigText(FText::Format(LOCTEXT("TimerConfigPinWithStep", "Step by {1}"), { FText::FromString(StepTimeString) })); + } + else + { + SetNodeConfigText(FText()); } - return FString::Printf(TEXT("%.*f"), 2, CompletionTime); + return; } - return TEXT("Completes in next tick"); + if (CompletionTime > UE_KINDA_SMALL_NUMBER) + { + const FString CompletionTimeString = FString::Printf(TEXT("%.*f"), 2, CompletionTime); + + if (StepTime > 0.0f) + { + const FString StepTimeString = FString::Printf(TEXT("%.*f"), 2, StepTime); + + SetNodeConfigText(FText::Format(LOCTEXT("TimerConfigWithStep", "Time: {0}, step by {1}"), { FText::FromString(CompletionTimeString), FText::FromString(StepTimeString) })); + } + else + { + SetNodeConfigText(FText::Format(LOCTEXT("TimerConfig", "Time: {0}"), { FText::FromString(CompletionTimeString) })); + } + } + else + { + SetNodeConfigText(FText(LOCTEXT("CompletesNextTick", "Completes in next tick"))); + } } FString UFlowNode_Timer::GetStatusString() const { + FString ProgressString; if (StepTime > 0.0f) { - return FString::Printf(TEXT("Progress: %.*f"), 2, SumOfSteps); + ProgressString = FString::Printf(TEXT("%.*f"), 2, SumOfSteps); + } + else if (CompletionTimerHandle.IsValid() && GetWorld()) + { + ProgressString = FString::Printf(TEXT("%.*f"), 2, GetWorld()->GetTimerManager().GetTimerElapsed(CompletionTimerHandle)); } - if (CompletionTimerHandle.IsValid() && GetWorld()) + if (!ProgressString.IsEmpty()) { - return FString::Printf(TEXT("Progress: %.*f"), 2, GetWorld()->GetTimerManager().GetTimerElapsed(CompletionTimerHandle)); + return FText::Format(LOCTEXT("ProgressStatus", "Progress: {0}"), { FText::FromString(ProgressString) }).ToString(); } return FString(); } + #endif + +#undef LOCTEXT_NAMESPACE diff --git a/Source/Flow/Private/Nodes/Utils/FlowNode_Log.cpp b/Source/Flow/Private/Nodes/Utils/FlowNode_Log.cpp index 93edb154b..22e6a259c 100644 --- a/Source/Flow/Private/Nodes/Utils/FlowNode_Log.cpp +++ b/Source/Flow/Private/Nodes/Utils/FlowNode_Log.cpp @@ -2,14 +2,17 @@ #include "Nodes/Utils/FlowNode_Log.h" #include "FlowLogChannels.h" +#include "FlowSettings.h" #include "Engine/Engine.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_Log) +#define LOCTEXT_NAMESPACE "FlowNode_Log" + UFlowNode_Log::UFlowNode_Log(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) - , Message(TEXT("Log!")) + , Message() , Verbosity(EFlowLogVerbosity::Warning) , bPrintToScreen(true) , Duration(5.0f) @@ -17,45 +20,72 @@ UFlowNode_Log::UFlowNode_Log(const FObjectInitializer& ObjectInitializer) { #if WITH_EDITOR Category = TEXT("Utils"); + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Developer; #endif } void UFlowNode_Log::ExecuteInput(const FName& PinName) { + // Get the Message from either the default (Message property) or the data pin (if connected) + FFlowDataPinResult_String MessageResult = TryResolveDataPinAsString(GET_MEMBER_NAME_CHECKED(UFlowNode_Log, Message)); + + if (MessageResult.Result == EFlowDataPinResolveResult::FailedMissingPin) + { + // Handle lookup of a FlowNode_Log that predated DataPins + MessageResult.Result = EFlowDataPinResolveResult::Success; + MessageResult.SetValue(Message); + } + + check(MessageResult.Result == EFlowDataPinResolveResult::Success); + switch (Verbosity) { case EFlowLogVerbosity::Error: - UE_LOG(LogFlow, Error, TEXT("%s"), *Message); + UE_LOG(LogFlow, Error, TEXT("%s"), *MessageResult.Value); break; case EFlowLogVerbosity::Warning: - UE_LOG(LogFlow, Warning, TEXT("%s"), *Message); + UE_LOG(LogFlow, Warning, TEXT("%s"), *MessageResult.Value); break; case EFlowLogVerbosity::Display: - UE_LOG(LogFlow, Display, TEXT("%s"), *Message); + UE_LOG(LogFlow, Display, TEXT("%s"), *MessageResult.Value); break; case EFlowLogVerbosity::Log: - UE_LOG(LogFlow, Log, TEXT("%s"), *Message); + UE_LOG(LogFlow, Log, TEXT("%s"), *MessageResult.Value); break; case EFlowLogVerbosity::Verbose: - UE_LOG(LogFlow, Verbose, TEXT("%s"), *Message); + UE_LOG(LogFlow, Verbose, TEXT("%s"), *MessageResult.Value); break; case EFlowLogVerbosity::VeryVerbose: - UE_LOG(LogFlow, VeryVerbose, TEXT("%s"), *Message); + UE_LOG(LogFlow, VeryVerbose, TEXT("%s"), *MessageResult.Value); break; default: ; } if (bPrintToScreen) { - GEngine->AddOnScreenDebugMessage(-1, Duration, TextColor, Message); + GEngine->AddOnScreenDebugMessage(-1, Duration, TextColor, MessageResult.Value); } TriggerFirstOutput(true); } #if WITH_EDITOR -FString UFlowNode_Log::GetNodeDescription() const + +void UFlowNode_Log::UpdateNodeConfigText_Implementation() { - return Message; + constexpr bool bErrorIfInputPinNotFound = false; + const bool bIsInputConnected = IsInputConnected(GET_MEMBER_NAME_CHECKED(UFlowNode_Log, Message), bErrorIfInputPinNotFound); + + if (bIsInputConnected) + { + SetNodeConfigText(FText()); + } + else + { + SetNodeConfigText(FText::FromString(Message)); + } } + #endif + +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp b/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp index 83eec5d51..8ee25878e 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp @@ -17,7 +17,7 @@ UFlowNode_CallOwnerFunction::UFlowNode_CallOwnerFunction(const FObjectInitialize , Params(nullptr) { #if WITH_EDITOR - NodeStyle = EFlowNodeStyle::Default; + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Deprecated; Category = TEXT("World"); #endif // WITH_EDITOR } diff --git a/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp b/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp index 7cdc68ec2..7a49d877c 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp @@ -12,7 +12,7 @@ UFlowNode_ComponentObserver::UFlowNode_ComponentObserver(const FObjectInitialize , SuccessCount(0) { #if WITH_EDITOR - NodeStyle = EFlowNodeStyle::Condition; + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Condition; Category = TEXT("World"); #endif diff --git a/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp b/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp index 6bd9794c8..59286d4cb 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp @@ -20,7 +20,7 @@ UFlowNode_ExecuteComponent::UFlowNode_ExecuteComponent() : Super() { #if WITH_EDITOR - NodeStyle = EFlowNodeStyle::Default; + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node; Category = TEXT("World"); #endif // WITH_EDITOR } @@ -211,8 +211,9 @@ bool UFlowNode_ExecuteComponent::TryInjectComponent() // Create the component instance TArray ComponentInstances; + + FLOW_ASSERT_ENUM_MAX(EExecuteComponentSource, 4); - static_assert(static_cast(EExecuteComponentSource::Max) == 4, TEXT("Update this code if the enum changes")); switch (ComponentSource) { case EExecuteComponentSource::InjectFromTemplate: @@ -311,7 +312,8 @@ const UActorComponent* UFlowNode_ExecuteComponent::TryGetExpectedComponent() con { const TSubclassOf ExpectedOwnerClass = TryGetExpectedActorOwnerClass(); - static_assert(static_cast(EExecuteComponentSource::Max) == 4, TEXT("Update this code if the enum changes")); + FLOW_ASSERT_ENUM_MAX(EExecuteComponentSource, 4); + switch (ComponentSource) { case EExecuteComponentSource::Undetermined: @@ -475,7 +477,8 @@ FText UFlowNode_ExecuteComponent::GetNodeTitle() const { if (UFlowSettings::Get()->bUseAdaptiveNodeTitles) { - static_assert(static_cast(EExecuteComponentSource::Max) == 4, TEXT("Update this code if the enum changes")); + FLOW_ASSERT_ENUM_MAX(EExecuteComponentSource, 4); + switch (ComponentSource) { case EExecuteComponentSource::Undetermined: @@ -535,7 +538,8 @@ void UFlowNode_ExecuteComponent::UpdateNodeConfigText_Implementation() const bool bUseAdaptiveNodeTitles = UFlowSettings::Get()->bUseAdaptiveNodeTitles; if (!bUseAdaptiveNodeTitles) { - static_assert(static_cast(EExecuteComponentSource::Max) == 4, TEXT("Update this code if the enum changes")); + FLOW_ASSERT_ENUM_MAX(EExecuteComponentSource, 4); + switch (ComponentSource) { case EExecuteComponentSource::Undetermined: diff --git a/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp b/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp index 8c32203a2..9205594f7 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp @@ -11,7 +11,7 @@ UFlowNode_OnNotifyFromActor::UFlowNode_OnNotifyFromActor(const FObjectInitialize { #if WITH_EDITOR Category = TEXT("Notifies"); - NodeStyle = EFlowNodeStyle::Condition; + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Condition; #endif } diff --git a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp index f869a6ff3..823d0a985 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp @@ -38,7 +38,7 @@ UFlowNode_PlayLevelSequence::UFlowNode_PlayLevelSequence(const FObjectInitialize { #if WITH_EDITOR Category = TEXT("World"); - NodeStyle = EFlowNodeStyle::Latent; + NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Latent; #endif InputPins.Empty(); diff --git a/Source/Flow/Private/Types/FlowClassUtils.cpp b/Source/Flow/Private/Types/FlowClassUtils.cpp new file mode 100644 index 000000000..acd0c2b42 --- /dev/null +++ b/Source/Flow/Private/Types/FlowClassUtils.cpp @@ -0,0 +1,61 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Types/FlowClassUtils.h" +#include "UObject/Class.h" +#include "UObject/UObjectIterator.h" + +#if WITH_EDITOR +TArray FlowClassUtils::GetClassesFromMetadataString(const FString& MetadataString) +{ + // Adapted from the inaccessible PropertyCustomizationHelpers::GetClassesFromMetadataString + + if (MetadataString.IsEmpty()) + { + return TArray(); + } + + auto FindClass = [](const FString& InClassName) -> UClass* + { + UClass* Class = UClass::TryFindTypeSlow(InClassName, EFindFirstObjectOptions::EnsureIfAmbiguous); + if (!Class) + { + Class = LoadObject(nullptr, *InClassName); + } + return Class; + }; + + TArray ClassNames; + MetadataString.ParseIntoArrayWS(ClassNames, TEXT(","), true); + + TArray Classes; + Classes.Reserve(ClassNames.Num()); + + for (const FString& ClassName : ClassNames) + { + UClass* Class = FindClass(ClassName); + if (!Class) + { + continue; + } + + // If the class is an interface, expand it to be all classes in memory that implement the class. + if (Class->HasAnyClassFlags(CLASS_Interface)) + { + for (TObjectIterator ClassIt; ClassIt; ++ClassIt) + { + UClass* ClassWithInterface = (*ClassIt); + if (ClassWithInterface->ImplementsInterface(Class)) + { + Classes.Add(ClassWithInterface); + } + } + } + else + { + Classes.Add(Class); + } + } + + return Classes; +} +#endif \ No newline at end of file diff --git a/Source/Flow/Private/Types/FlowDataPinBlueprintLibrary.cpp b/Source/Flow/Private/Types/FlowDataPinBlueprintLibrary.cpp new file mode 100644 index 000000000..13b17024a --- /dev/null +++ b/Source/Flow/Private/Types/FlowDataPinBlueprintLibrary.cpp @@ -0,0 +1,36 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Types/FlowDataPinBlueprintLibrary.h" +#include "FlowLogChannels.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowDataPinBlueprintLibrary) + +uint8 UFlowDataPinBlueprintLibrary::AutoConvert_FlowDataPinPropertyEnumToEnum(const FFlowDataPinOutputProperty_Enum& EnumProperty) +{ + if (IsValid(EnumProperty.EnumClass)) + { + const uint64 EnumValueAsInt = EnumProperty.EnumClass->GetValueByName(EnumProperty.Value); + + // At least For Now(tm) Blueprint Enums want to be uint8's + return static_cast(EnumValueAsInt); + } + + UE_LOG(LogFlow, Error, TEXT("Could not cast enum value %s, because missing enum class"), *EnumProperty.Value.ToString()); + + return static_cast(INDEX_NONE); +} + +uint8 UFlowDataPinBlueprintLibrary::AutoConvert_FlowDataPinResultEnumToEnum(const FFlowDataPinResult_Enum& EnumProperty) +{ + if (IsValid(EnumProperty.EnumClass)) + { + const uint64 EnumValueAsInt = EnumProperty.EnumClass->GetValueByName(EnumProperty.Value); + + // At least For Now(tm) Blueprint Enums want to be uint8's + return static_cast(EnumValueAsInt); + } + + UE_LOG(LogFlow, Error, TEXT("Could not cast enum value %s, because missing enum class"), *EnumProperty.Value.ToString()); + + return static_cast(INDEX_NONE); +} diff --git a/Source/Flow/Private/Types/FlowDataPinProperties.cpp b/Source/Flow/Private/Types/FlowDataPinProperties.cpp new file mode 100644 index 000000000..c640938c7 --- /dev/null +++ b/Source/Flow/Private/Types/FlowDataPinProperties.cpp @@ -0,0 +1,204 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Types/FlowDataPinProperties.h" +#include "Types/FlowClassUtils.h" +#include "FlowLogChannels.h" +#include "UObject/Class.h" +#include "UObject/UObjectIterator.h" +#if WITH_EDITOR +#include "EditorClassUtils.h" +#endif + +#define LOCTEXT_NAMESPACE "FlowDataPinProperties" + +#if WITH_EDITOR + +FFlowPin FFlowDataPinProperty::CreateFlowPin(const FName& PinName, const TInstancedStruct& DataPinProperty) +{ + FFlowPin FlowPin; + + const FFlowDataPinProperty* Property = DataPinProperty.GetPtr(); + if (!Property) + { + return FlowPin; + } + + FlowPin.PinName = PinName; + + const EFlowPinType FlowPinType = Property->GetFlowPinType(); + + FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); + + switch (FlowPinType) + { + case EFlowPinType::Enum: + { + const FFlowDataPinOutputProperty_Enum& EnumDataPinProperty = DataPinProperty.Get(); + UEnum* EnumClass = EnumDataPinProperty.EnumClass; + + FlowPin.SetPinType(FlowPinType, EnumClass); + } + break; + + case EFlowPinType::Class: + { + const FFlowDataPinOutputProperty_Class& ClassDataPinProperty = DataPinProperty.Get(); + + FlowPin.SetPinType(FlowPinType, ClassDataPinProperty.ClassFilter); + } + break; + + case EFlowPinType::Object: + { + const FFlowDataPinOutputProperty_Object& ObjectDataPinProperty = DataPinProperty.Get(); + + FlowPin.SetPinType(FlowPinType, ObjectDataPinProperty.ClassFilter); + } + break; + + default: + { + FlowPin.SetPinType(FlowPinType); + } + break; + } + + return FlowPin; +} + +void FFlowDataPinOutputProperty_Enum::OnEnumNameChanged() +{ + if (!EnumName.IsEmpty()) + { + EnumClass = UClass::TryFindTypeSlow(EnumName, EFindFirstObjectOptions::ExactClass); + + if (EnumClass != nullptr && !FFlowPin::ValidateEnum(*EnumClass)) + { + EnumClass = nullptr; + } + } +} + +FText FFlowNamedDataPinOutputProperty::BuildHeaderText() const +{ + EFlowPinType PinType = EFlowPinType::Invalid; + + if (const FFlowDataPinProperty* DataPinPropertyPtr = DataPinProperty.GetPtr()) + { + PinType = DataPinPropertyPtr->GetFlowPinType(); + } + + return FText::Format(LOCTEXT("FlowNamedDataPinOutputPropertyHeader", "{0} ({1})"), { FText::FromName(Name), UEnum::GetDisplayValueAsText(PinType) }); +} + +UClass* FFlowDataPinOutputProperty_Class::DeriveMetaClass(const FProperty& MetaDataProperty) const +{ + if (UClass* MetaClass = TryGetMetaClassFromProperty(MetaDataProperty)) + { + return MetaClass; + } + + return ClassFilter; +} + +UClass* FFlowDataPinOutputProperty_Class::TryGetMetaClassFromProperty(const FProperty& MetaDataProperty) +{ + const FString& MetaClassName = MetaDataProperty.GetMetaData("MetaClass"); + + if (!MetaClassName.IsEmpty()) + { + if (UClass* FoundClass = FEditorClassUtils::GetClassFromString(MetaClassName)) + { + return FoundClass; + } + else + { + UE_LOG(LogFlow, Error, TEXT("Could not resolve MetaClass named %s for property %s"), *MetaClassName, *MetaDataProperty.GetName()); + } + } + + return nullptr; +} + +UClass* FFlowDataPinOutputProperty_Object::DeriveObjectClass(const FProperty& MetaDataProperty) const +{ + if (UClass* MetaClass = FFlowDataPinOutputProperty_Object::TryGetObjectClassFromProperty(MetaDataProperty)) + { + return MetaClass; + } + + return ClassFilter; +} + +UClass* FFlowDataPinOutputProperty_Object::TryGetObjectClassFromProperty(const FProperty& MetaDataProperty) +{ + if (UClass* MetaClass = FFlowDataPinOutputProperty_Class::TryGetMetaClassFromProperty(MetaDataProperty)) + { + return MetaClass; + } + + // FSoftObjectPath can use the "AllowedClasses" to define what classes are allowed for the object. + // Using the "AllowedClasses" metadata tag, but we only support a single class, due to singular return value for this function. + const FString AllowedClassesString = MetaDataProperty.GetMetaData("AllowedClasses"); + const TArray AllowedClasses = FlowClassUtils::GetClassesFromMetadataString(AllowedClassesString); + + if (AllowedClasses.Num() > 1) + { + UE_LOG(LogFlow, Error, TEXT("Only a single AllowedClasses entry is allowed for flow data pin properties (multiple found: %s) for property %s"), *AllowedClassesString, *MetaDataProperty.GetName()); + + return nullptr; + } + + if (AllowedClasses.IsEmpty()) + { + return nullptr; + } + + if (UClass* AllowedClass = AllowedClasses[0]) + { + return AllowedClass; + } + else + { + UE_LOG(LogFlow, Error, TEXT("Could not resolve AllowedClasses '%s' for property %s"), *AllowedClassesString, *MetaDataProperty.GetName()); + } + + return nullptr; +} +#endif + +FFlowDataPinOutputProperty_Object::FFlowDataPinOutputProperty_Object(UObject* InValue, UClass* InClassFilter) + : Super() +#if WITH_EDITOR + , ClassFilter(InClassFilter) +#endif +{ + SetObjectValue(InValue); +} + +void FFlowDataPinOutputProperty_Object::SetObjectValue(UObject* InValue) +{ + UClass* ObjectClass = IsValid(InValue) ? InValue->GetClass() : nullptr; + if (IsValid(ObjectClass)) + { + const bool bIsInstanced = (ObjectClass->ClassFlags & CLASS_EditInlineNew) != 0; + + if (bIsInstanced) + { + InlineValue = InValue; + ReferenceValue = nullptr; + } + else + { + InlineValue = nullptr; + ReferenceValue = InValue; + } + } + else + { + InlineValue = nullptr; + ReferenceValue = nullptr; + } +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/Flow/Private/Types/FlowDataPinResults.cpp b/Source/Flow/Private/Types/FlowDataPinResults.cpp new file mode 100644 index 000000000..f8e5f440c --- /dev/null +++ b/Source/Flow/Private/Types/FlowDataPinResults.cpp @@ -0,0 +1,60 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Types/FlowDataPinResults.h" +#include "Types/FlowDataPinProperties.h" + +// FFlowDataPinResult_Object + +FFlowDataPinResult_Object::FFlowDataPinResult_Object(UObject* InValue) + : Super(EFlowDataPinResolveResult::Success) +{ + SetValueFromObjectPtr(InValue); +} + +void FFlowDataPinResult_Object::SetValueFromPropertyWrapper(const FFlowDataPinOutputProperty_Object& InPropertyWrapper) +{ + Value = InPropertyWrapper.GetObjectValue(); +} + +// FFlowDataPinResult_Class + +FFlowDataPinResult_Class::FFlowDataPinResult_Class(const FSoftClassPath& InValuePath) + : Super(EFlowDataPinResolveResult::Success) +{ + SetValueFromSoftPath(InValuePath); +} + +FFlowDataPinResult_Class::FFlowDataPinResult_Class(UClass* InValueClass) + : Super(EFlowDataPinResolveResult::Success) +{ + SetValueFromObjectPtr(InValueClass); +} + +void FFlowDataPinResult_Class::SetValueFromPropertyWrapper(const FFlowDataPinOutputProperty_Class& PropertyWrapper) +{ + SetValueFromSoftPath(PropertyWrapper.GetAsSoftClass()); +} + +void FFlowDataPinResult_Class::SetValueFromSoftPath(const FSoftObjectPath& SoftObjectPath) +{ + const FSoftClassPath SoftClassPath(SoftObjectPath.ToString()); + SetValueSoftClassAndClassPtr(SoftClassPath, SoftClassPath.ResolveClass()); +} + +void FFlowDataPinResult_Class::SetValueSoftClassAndClassPtr(const FSoftClassPath& SoftPath, UClass* ObjectPtr) +{ + ValuePath = SoftPath; + ValueClass = ObjectPtr; +} + +FSoftClassPath FFlowDataPinResult_Class::GetAsSoftClass() const +{ + if (ValuePath.IsValid()) + { + return ValuePath; + } + else + { + return FSoftClassPath(ValueClass); + } +} diff --git a/Source/Flow/Public/AddOns/FlowNodeAddOn.h b/Source/Flow/Public/AddOns/FlowNodeAddOn.h index bf359c11b..e06a65882 100644 --- a/Source/Flow/Public/AddOns/FlowNodeAddOn.h +++ b/Source/Flow/Public/AddOns/FlowNodeAddOn.h @@ -28,16 +28,27 @@ class UFlowNodeAddOn : public UFlowNodeBase UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "FlowNodeAddOn") TArray InputPins; +#if WITH_EDITORONLY_DATA // Output pins to add to the owning flow node UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "FlowNodeAddOn") TArray OutputPins; +#endif public: + + FLOW_API UFlowNodeAddOn(); + // UFlowNodeBase // AddOns may opt in to be eligible for a given parent - UFUNCTION(BlueprintNativeEvent, BlueprintPure, Category = "FlowNodeAddOn") - FLOW_API EFlowAddOnAcceptResult AcceptFlowNodeAddOnParent(const UFlowNodeBase* ParentTemplate) const; + // - ParentTemplate - the template of the FlowNode or FlowNodeAddOn that is being considered as a potential parent + // - AdditionalAddOnsToAssumeAreChildren - other AddOns to assume that are already child AddOns for the purposes of this test. + // This list will be populated with the 'other' AddOns in a multi-paste operation in the editor, + // because some paste-targets can only accept a certain mix of addons, so we must know the rest of the set being pasted + // to make the correct decision about whether to allow AddOnTemplate to be added. + // https://forums.unrealengine.com/t/default-parameters-with-tarrays/330225 for details on AutoCreateRefTerm + UFUNCTION(BlueprintNativeEvent, BlueprintPure, Category = "FlowNodeAddOn", meta = (AutoCreateRefTerm = AdditionalAddOnsToAssumeAreChildren)) + FLOW_API EFlowAddOnAcceptResult AcceptFlowNodeAddOnParent(const UFlowNodeBase* ParentTemplate, const TArray& AdditionalAddOnsToAssumeAreChildren) const; FLOW_API virtual UFlowNode* GetFlowNodeSelfOrOwner() override { return FlowNode; } FLOW_API virtual bool IsSupportedInputPinName(const FName& PinName) const override; @@ -59,9 +70,9 @@ class UFlowNodeAddOn : public UFlowNodeBase #if WITH_EDITOR // IFlowContextPinSupplierInterface - FLOW_API virtual bool SupportsContextPins() const override { return !InputPins.IsEmpty() || !OutputPins.IsEmpty(); } - FLOW_API virtual TArray GetContextInputs() const override { return InputPins; } - FLOW_API virtual TArray GetContextOutputs() const override { return OutputPins; } + FLOW_API virtual bool SupportsContextPins() const override { return Super::SupportsContextPins() || (!InputPins.IsEmpty() || !OutputPins.IsEmpty()); } + FLOW_API virtual TArray GetContextInputs() const override; + FLOW_API virtual TArray GetContextOutputs() const override; // -- #endif // WITH_EDITOR diff --git a/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateAND.h b/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateAND.h index 5a8b363c7..381fbb632 100644 --- a/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateAND.h +++ b/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateAND.h @@ -21,7 +21,7 @@ class UFlowNodeAddOn_PredicateAND UFlowNodeAddOn_PredicateAND(); // UFlowNodeBase - virtual EFlowAddOnAcceptResult AcceptFlowNodeAddOnChild_Implementation(const UFlowNodeAddOn* AddOnTemplate) const override; + virtual EFlowAddOnAcceptResult AcceptFlowNodeAddOnChild_Implementation(const UFlowNodeAddOn* AddOnTemplate, const TArray& AdditionalAddOnsToAssumeAreChildren) const override; // -- // IFlowPredicateInterface diff --git a/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateNOT.h b/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateNOT.h index 9ebf63277..99d9e258b 100644 --- a/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateNOT.h +++ b/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateNOT.h @@ -21,7 +21,7 @@ class UFlowNodeAddOn_PredicateNOT UFlowNodeAddOn_PredicateNOT(); // UFlowNodeBase - virtual EFlowAddOnAcceptResult AcceptFlowNodeAddOnChild_Implementation(const UFlowNodeAddOn* AddOnTemplate) const override; + virtual EFlowAddOnAcceptResult AcceptFlowNodeAddOnChild_Implementation(const UFlowNodeAddOn* AddOnTemplate, const TArray& AdditionalAddOnsToAssumeAreChildren) const override; // -- // IFlowPredicateInterface diff --git a/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateOR.h b/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateOR.h index 0d8128656..0df21db22 100644 --- a/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateOR.h +++ b/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateOR.h @@ -21,7 +21,7 @@ class UFlowNodeAddOn_PredicateOR UFlowNodeAddOn_PredicateOR(); // UFlowNodeBase - virtual EFlowAddOnAcceptResult AcceptFlowNodeAddOnChild_Implementation(const UFlowNodeAddOn* AddOnTemplate) const override; + virtual EFlowAddOnAcceptResult AcceptFlowNodeAddOnChild_Implementation(const UFlowNodeAddOn* AddOnTemplate, const TArray& AdditionalAddOnsToAssumeAreChildren) const override; // -- // IFlowPredicateInterface diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 28aa4f492..f6a6e118c 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -39,6 +39,36 @@ DECLARE_DELEGATE(FFlowGraphEvent); #endif +// Working Data struct for the Harvest Data Pins operation +// (passed between functions involved in the harvesting operation to simplify the function signatures) +struct FFlowHarvestDataPinsWorkingData +{ + FFlowHarvestDataPinsWorkingData(UFlowNode& InFlowNode, const TMap& PinNameMapPrev, const TArray& InputPinsPrev, const TArray& OutputPinsPrev) + : FlowNode(&InFlowNode) + , PinNameToBoundPropertyNameMapPrev(PinNameMapPrev) + , AutoInputDataPinsPrev(InputPinsPrev) + , AutoOutputDataPinsPrev(OutputPinsPrev) + { } + +#if WITH_EDITOR + bool DidPinNameToBoundPropertyNameMapChange() const; + bool DidAutoInputDataPinsChange() const; + bool DidAutoOutputDataPinsChange() const; +#endif + + UFlowNode* FlowNode = nullptr; + + const TMap& PinNameToBoundPropertyNameMapPrev; + const TArray& AutoInputDataPinsPrev; + const TArray& AutoOutputDataPinsPrev; + + TMap PinNameToBoundPropertyNameMapNext; + TArray AutoInputDataPinsNext; + TArray AutoOutputDataPinsNext; + + bool bPinNameMapChanged = false; +}; + /** * Single asset containing flow nodes. */ @@ -159,8 +189,28 @@ class FLOW_API UFlowAsset : public UObject // Processes all nodes and creates map of all pin connections void HarvestNodeConnections(); + + // Updates the auto-generated pins and bindings for a given FlowNode, + // returns true if any changes were made. + bool TryUpdateManagedFlowPinsForNode(UFlowNode& FlowNode); + +protected: + void AddDataPinPropertyBindingToMap( + const FName& PinAuthoredName, + const FName& PropertyAuthoredName, + FFlowHarvestDataPinsWorkingData& InOutData); + virtual bool TryCreateFlowDataPinFromMetadataValue( + const FString& MetadataValue, + UFlowNode& FlowNode, + const FProperty& Property, + const FText& PinDisplayName, + const bool bIsInputPin, + TArray* InOutDataPinsNext) const; + + void HarvestFlowPinMetadataForProperty(const FProperty* Property, FFlowHarvestDataPinsWorkingData& InOutData); #endif +public: const TMap& GetNodes() const { return Nodes; } UFlowNode* GetNode(const FGuid& Guid) const { return Nodes.FindRef(Guid); } @@ -206,7 +256,7 @@ class FLOW_API UFlowAsset : public UObject OutNodes.Emplace(NodeOfRequiredType); } - for (UFlowNode* ConnectedNode : Node->GetConnectedNodes()) + for (UFlowNode* ConnectedNode : Node->GatherConnectedNodes()) { if (ConnectedNode && !IteratedNodes.Contains(ConnectedNode)) { @@ -337,12 +387,12 @@ class FLOW_API UFlowAsset : public UObject virtual void PreloadNodes() {} virtual void PreStartFlow(); - virtual void StartFlow(); + virtual void StartFlow(IFlowDataPinValueSupplierInterface* DataPinValueSupplier); virtual void FinishFlow(const EFlowFinishPolicy InFinishPolicy, const bool bRemoveInstance = true); bool HasStartedFlow() const; - void TriggerCustomInput(const FName& EventName); + void TriggerCustomInput(const FName& EventName, IFlowDataPinValueSupplierInterface* DataPinValueSupplier = nullptr); // Get Flow Asset instance created by the given SubGraph node TWeakObjectPtr GetFlowInstance(UFlowNode_SubGraph* SubGraphNode) const; diff --git a/Source/Flow/Public/FlowSubsystem.h b/Source/Flow/Public/FlowSubsystem.h index fd338cb7f..17499ae78 100644 --- a/Source/Flow/Public/FlowSubsystem.h +++ b/Source/Flow/Public/FlowSubsystem.h @@ -75,7 +75,7 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem UFUNCTION(BlueprintCallable, Category = "FlowSubsystem", meta = (DefaultToSelf = "Owner")) virtual void StartRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const bool bAllowMultipleInstances = true); - virtual UFlowAsset* CreateRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const bool bAllowMultipleInstances = true, FString NewInstanceName = FString()); + virtual UFlowAsset* CreateRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const bool bAllowMultipleInstances = true, const FString& NewInstanceName = FString()); /* Finish Policy value is read by Flow Node * Nodes have opportunity to terminate themselves differently if Flow Graph has been aborted @@ -90,7 +90,7 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem virtual void FinishAllRootFlows(UObject* Owner, const EFlowFinishPolicy FinishPolicy); protected: - UFlowAsset* CreateSubFlow(UFlowNode_SubGraph* SubGraphNode, const FString SavedInstanceName = FString(), const bool bPreloading = false); + UFlowAsset* CreateSubFlow(UFlowNode_SubGraph* SubGraphNode, const FString& SavedInstanceName = FString(), const bool bPreloading = false); void RemoveSubFlow(UFlowNode_SubGraph* SubGraphNode, const EFlowFinishPolicy FinishPolicy); UFlowAsset* CreateFlowInstance(const TWeakObjectPtr Owner, TSoftObjectPtr FlowAsset, FString NewInstanceName = FString()); diff --git a/Source/Flow/Public/FlowTags.h b/Source/Flow/Public/FlowTags.h new file mode 100644 index 000000000..3ae5699b9 --- /dev/null +++ b/Source/Flow/Public/FlowTags.h @@ -0,0 +1,23 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "NativeGameplayTags.h" + +FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle); +FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle_Custom); + +FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle_Node); +FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle_Node_Condition); +FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle_Node_Deprecated); +FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle_Node_Developer); +FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle_Node_InOut); +FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle_Node_Latent); +FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle_Node_Logic); +FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle_Node_SubGraph); +FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle_Node_Terminal); + +FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle_AddOn); +FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle_AddOn_PerSpawnedActor); +FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle_AddOn_Predicate); +FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle_AddOn_Predicate_Composite); diff --git a/Source/Flow/Public/FlowTypes.h b/Source/Flow/Public/FlowTypes.h index 8ede68d16..d53b4b79a 100644 --- a/Source/Flow/Public/FlowTypes.h +++ b/Source/Flow/Public/FlowTypes.h @@ -3,20 +3,28 @@ #pragma once #include "GameplayTagContainer.h" +#include "Types/FlowEnumUtils.h" + #include "FlowTypes.generated.h" #if WITH_EDITORONLY_DATA UENUM(BlueprintType) enum class EFlowNodeStyle : uint8 { + // Deprecated EFlowNodeStyle enum (use NodeDisplayStyle tag instead) Condition, Default, InOut UMETA(Hidden), Latent, Logic, SubGraph UMETA(Hidden), - Custom + Custom, + + Max UMETA(Hidden), + Invalid UMETA(Hidden), + Min = 0 UMETA(Hidden), }; +FLOW_ENUM_RANGE_VALUES(EFlowNodeStyle) #endif UENUM(BlueprintType) @@ -25,8 +33,13 @@ enum class EFlowNodeState : uint8 NeverActivated, Active, Completed, - Aborted + Aborted, + + Max UMETA(Hidden), + Invalid UMETA(Hidden), + Min = 0 UMETA(Hidden), }; +FLOW_ENUM_RANGE_VALUES(EFlowNodeState) // Finish Policy value is read by Flow Node // Nodes have opportunity to terminate themselves differently if Flow Graph has been aborted @@ -111,12 +124,39 @@ enum class EFlowAddOnAcceptResult : uint8 Invalid UMETA(Hidden), Min = Undetermined UMETA(Hidden), }; +FLOW_ENUM_RANGE_VALUES(EFlowAddOnAcceptResult); FORCEINLINE_DEBUGGABLE EFlowAddOnAcceptResult CombineFlowAddOnAcceptResult(EFlowAddOnAcceptResult Result0, EFlowAddOnAcceptResult Result1) { - const __underlying_type(EFlowAddOnAcceptResult) Result0AsInt = static_cast<__underlying_type(EFlowAddOnAcceptResult)>(Result0); - const __underlying_type(EFlowAddOnAcceptResult) Result1AsInt = static_cast<__underlying_type(EFlowAddOnAcceptResult)>(Result1); + const FlowEnum::safe_underlying_type::type Result0AsInt = FlowEnum::ToInt(Result0); + const FlowEnum::safe_underlying_type::type Result1AsInt = FlowEnum::ToInt(Result1); // Prioritize the higher numerical value enum value return static_cast(FMath::Max(Result0AsInt, Result1AsInt)); } + +UENUM() +enum class EFlowForEachAddOnFunctionReturnValue : int8 +{ + // Continue iterating the ForEach loop + Continue, + + // Break out of the ForEach loop, with a "Success" result (whatever that means to the TFunction) + BreakWithSuccess, + + // Break out of the ForEach loop, with a "Failure" return (whatever that means to the TFunction) + BreakWithFailure, + + Max UMETA(Hidden), + Invalid = -1 UMETA(Hidden), + Min = 0 UMETA(Hidden), + + ContinueForEachFirst = Continue UMETA(Hidden), + ContinueForEachLast = Continue UMETA(Hidden), +}; +FLOW_ENUM_RANGE_VALUES(EFlowForEachAddOnFunctionReturnValue); + +namespace EFlowForEachAddOnFunctionReturnValue_Classifiers +{ + FORCEINLINE bool ShouldContinueForEach(EFlowForEachAddOnFunctionReturnValue Result) { return FLOW_IS_ENUM_IN_SUBRANGE(Result, EFlowForEachAddOnFunctionReturnValue::ContinueForEach); } +} diff --git a/Source/Flow/Public/Interfaces/FlowContextPinSupplierInterface.h b/Source/Flow/Public/Interfaces/FlowContextPinSupplierInterface.h index 12040d23d..6e14ed37d 100644 --- a/Source/Flow/Public/Interfaces/FlowContextPinSupplierInterface.h +++ b/Source/Flow/Public/Interfaces/FlowContextPinSupplierInterface.h @@ -23,12 +23,17 @@ class FLOW_API IFlowContextPinSupplierInterface public: #if WITH_EDITOR - virtual bool SupportsContextPins() const { return true; } - // Be careful, enabling it might cause loading gigabytes of data as nodes would load all related data (i.e. Level Sequences) virtual bool CanRefreshContextPinsOnLoad() const { return false; } #endif // WITH_EDITOR + UFUNCTION(BlueprintNativeEvent, Category = "FlowNode In-Editor Functions", DisplayName = "SupportsContextPins", meta = (DevelopmentOnly)) + bool K2_SupportsContextPins() const; + virtual bool K2_SupportsContextPins_Implementation() const; +#if WITH_EDITOR + virtual bool SupportsContextPins() const { return Execute_K2_SupportsContextPins(Cast(this)); } +#endif // WITH_EDITOR + UFUNCTION(BlueprintNativeEvent, Category = "FlowNode In-Editor Functions", DisplayName = "GetContextInputs", meta = (DevelopmentOnly)) TArray K2_GetContextInputs() const; virtual TArray K2_GetContextInputs_Implementation() const; diff --git a/Source/Flow/Public/Interfaces/FlowDataPinGeneratorNodeInterface.h b/Source/Flow/Public/Interfaces/FlowDataPinGeneratorNodeInterface.h new file mode 100644 index 000000000..87a9ad214 --- /dev/null +++ b/Source/Flow/Public/Interfaces/FlowDataPinGeneratorNodeInterface.h @@ -0,0 +1,29 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "UObject/Interface.h" +#include "Nodes/FlowPin.h" + +#include "FlowDataPinGeneratorNodeInterface.generated.h" + +// Interface for special UFlowNodes that can auto-generate pins directly +UINTERFACE(MinimalAPI, NotBlueprintable, DisplayName = "Flow Data Pin Generator Node Interface") +class UFlowDataPinGeneratorNodeInterface : public UInterface +{ + GENERATED_BODY() +}; + +class FLOW_API IFlowDataPinGeneratorNodeInterface +{ + GENERATED_BODY() + +public: + +#if WITH_EDITOR + virtual void AutoGenerateDataPins( + TMap& InOutPinNameToBoundPropertyNameMap, + TArray& InOutInputDataPins, + TArray& InOutOutputDataPins) const = 0; +#endif +}; diff --git a/Source/Flow/Public/Interfaces/FlowDataPinPropertyProviderInterface.h b/Source/Flow/Public/Interfaces/FlowDataPinPropertyProviderInterface.h new file mode 100644 index 000000000..24130d709 --- /dev/null +++ b/Source/Flow/Public/Interfaces/FlowDataPinPropertyProviderInterface.h @@ -0,0 +1,28 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "InstancedStruct.h" +#include "UObject/Interface.h" + +#include "FlowDataPinPropertyProviderInterface.generated.h" + +struct FFlowDataPinProperty; + +// Interface to define a FFlowDataPinProperty provider. +// This is used in plumbing data in the AI Flow extension plugin into the Flow Data Pins framework. +UINTERFACE(MinimalAPI, NotBlueprintable) +class UFlowDataPinPropertyProviderInterface : public UInterface +{ + GENERATED_BODY() +}; + +class FLOW_API IFlowDataPinPropertyProviderInterface +{ + GENERATED_BODY() + +public: + + // Provide a FFlowDataPinProperty (instancedStruct) for the creation of data pins and supplying their values. + virtual bool TryProvideFlowDataPinProperty(const bool bIsInputPin, TInstancedStruct& OutFlowDataPinProperty) const = 0; +}; diff --git a/Source/Flow/Public/Interfaces/FlowDataPinValueSupplierInterface.h b/Source/Flow/Public/Interfaces/FlowDataPinValueSupplierInterface.h new file mode 100644 index 000000000..950bd1c5c --- /dev/null +++ b/Source/Flow/Public/Interfaces/FlowDataPinValueSupplierInterface.h @@ -0,0 +1,108 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Types/FlowDataPinResults.h" + +#include "UObject/Interface.h" + +#include "FlowDataPinValueSupplierInterface.generated.h" + +// Interface to define a Flow Data Pin value supplier. This is generally a UFlowNode subclass, +// but we may support external suppliers that are not flow nodes in the future +// (eg, for supplying configuration values for the root graph) +UINTERFACE(MinimalAPI, Blueprintable, DisplayName = "Flow Data Pin Value Supplier Interface") +class UFlowDataPinValueSupplierInterface : public UInterface +{ + GENERATED_BODY() +}; + +class FLOW_API IFlowDataPinValueSupplierInterface +{ + GENERATED_BODY() + +public: + // Can this node actually supply Data Pin values? + // Implementers of this interface will need to use their own logic to answer this question. + UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Can Supply DataPin Values") + bool CanSupplyDataPinValues() const; + virtual bool CanSupplyDataPinValues_Implementation() const { return true; } + + // Must implement TrySupplyDataAs... for every EFlowPinType + FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); + + // Try to supply the value for a data Bool pin + UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As Bool") + FFlowDataPinResult_Bool TrySupplyDataPinAsBool(const FName& PinName) const; + virtual FFlowDataPinResult_Bool TrySupplyDataPinAsBool_Implementation(const FName& PinName) const { return FFlowDataPinResult_Bool(); } + + // Try to supply the value for a data Int pin + UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As Int") + FFlowDataPinResult_Int TrySupplyDataPinAsInt(const FName& PinName) const; + virtual FFlowDataPinResult_Int TrySupplyDataPinAsInt_Implementation(const FName& PinName) const { return FFlowDataPinResult_Int(); } + + // Try to supply the value for a data Float pin + UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As Float") + FFlowDataPinResult_Float TrySupplyDataPinAsFloat(const FName& PinName) const; + virtual FFlowDataPinResult_Float TrySupplyDataPinAsFloat_Implementation(const FName& PinName) const { return FFlowDataPinResult_Float(); } + + // Try to supply the value for a data Name pin + UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As Name") + FFlowDataPinResult_Name TrySupplyDataPinAsName(const FName& PinName) const; + virtual FFlowDataPinResult_Name TrySupplyDataPinAsName_Implementation(const FName& PinName) const { return FFlowDataPinResult_Name(); } + + // Try to supply the value for a data String pin + UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As String") + FFlowDataPinResult_String TrySupplyDataPinAsString(const FName& PinName) const; + virtual FFlowDataPinResult_String TrySupplyDataPinAsString_Implementation(const FName& PinName) const { return FFlowDataPinResult_String(); } + + // Try to supply the value for a data Text pin + UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As Text") + FFlowDataPinResult_Text TrySupplyDataPinAsText(const FName& PinName) const; + virtual FFlowDataPinResult_Text TrySupplyDataPinAsText_Implementation(const FName& PinName) const { return FFlowDataPinResult_Text(); } + + // Try to supply the value for a data Enum pin + UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As Enum") + FFlowDataPinResult_Enum TrySupplyDataPinAsEnum(const FName& PinName) const; + virtual FFlowDataPinResult_Enum TrySupplyDataPinAsEnum_Implementation(const FName& PinName) const { return FFlowDataPinResult_Enum(); } + + // Try to supply the value for a data Vector pin + UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As Vector") + FFlowDataPinResult_Vector TrySupplyDataPinAsVector(const FName& PinName) const; + virtual FFlowDataPinResult_Vector TrySupplyDataPinAsVector_Implementation(const FName& PinName) const { return FFlowDataPinResult_Vector(); } + + // Try to supply the value for a data Rotator pin + UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As Rotator") + FFlowDataPinResult_Rotator TrySupplyDataPinAsRotator(const FName& PinName) const; + virtual FFlowDataPinResult_Rotator TrySupplyDataPinAsRotator_Implementation(const FName& PinName) const { return FFlowDataPinResult_Rotator(); } + + // Try to supply the value for a data Transform pin + UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As Transform") + FFlowDataPinResult_Transform TrySupplyDataPinAsTransform(const FName& PinName) const; + virtual FFlowDataPinResult_Transform TrySupplyDataPinAsTransform_Implementation(const FName& PinName) const { return FFlowDataPinResult_Transform(); } + + // Try to supply the value for a data GameplayTag pin + UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As GameplayTag") + FFlowDataPinResult_GameplayTag TrySupplyDataPinAsGameplayTag(const FName& PinName) const; + virtual FFlowDataPinResult_GameplayTag TrySupplyDataPinAsGameplayTag_Implementation(const FName& PinName) const { return FFlowDataPinResult_GameplayTag(); } + + // Try to supply the value for a data GameplayTagContainer pin + UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As GameplayTagContainer") + FFlowDataPinResult_GameplayTagContainer TrySupplyDataPinAsGameplayTagContainer(const FName& PinName) const; + virtual FFlowDataPinResult_GameplayTagContainer TrySupplyDataPinAsGameplayTagContainer_Implementation(const FName& PinName) const { return FFlowDataPinResult_GameplayTagContainer(); } + + // Try to supply the value for a data InstancedStruct pin + UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As InstancedStruct") + FFlowDataPinResult_InstancedStruct TrySupplyDataPinAsInstancedStruct(const FName& PinName) const; + virtual FFlowDataPinResult_InstancedStruct TrySupplyDataPinAsInstancedStruct_Implementation(const FName& PinName) const { return FFlowDataPinResult_InstancedStruct(); } + + // Try to supply the value for a data Object pin + UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As Object") + FFlowDataPinResult_Object TrySupplyDataPinAsObject(const FName& PinName) const; + virtual FFlowDataPinResult_Object TrySupplyDataPinAsObject_Implementation(const FName& PinName) const { return FFlowDataPinResult_Object(); } + + // Try to supply the value for a data Class pin + UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As Class") + FFlowDataPinResult_Class TrySupplyDataPinAsClass(const FName& PinName) const; + virtual FFlowDataPinResult_Class TrySupplyDataPinAsClass_Implementation(const FName& PinName) const { return FFlowDataPinResult_Class(); } +}; diff --git a/Source/Flow/Public/Interfaces/FlowNativeExecutableInterface.h b/Source/Flow/Public/Interfaces/FlowNativeExecutableInterface.h new file mode 100644 index 000000000..364395847 --- /dev/null +++ b/Source/Flow/Public/Interfaces/FlowNativeExecutableInterface.h @@ -0,0 +1,24 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "UObject/Interface.h" + +#include "FlowNativeExecutableInterface.generated.h" + +UINTERFACE(DisplayName = "[DEPRECATED] Flow Native Executable Interface", meta = (CannotImplementInterfaceInBlueprint, Deprecated)) +class UFlowNativeExecutableInterface : public UInterface +{ + GENERATED_BODY() +}; + +class IFlowNativeExecutableInterface +{ + GENERATED_BODY() + +public: + + // NOTE (gtaylor) All of these functions have been moved into UFlowNodeBase. + // Keeping the empty interface existing for a time until all of the assets are updated, + // to prevent an assert. +}; diff --git a/Source/Flow/Public/Interfaces/FlowNodeWithExternalDataPinSupplierInterface.h b/Source/Flow/Public/Interfaces/FlowNodeWithExternalDataPinSupplierInterface.h new file mode 100644 index 000000000..34247e8ee --- /dev/null +++ b/Source/Flow/Public/Interfaces/FlowNodeWithExternalDataPinSupplierInterface.h @@ -0,0 +1,34 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "UObject/Interface.h" +#include "FlowNodeWithExternalDataPinSupplierInterface.generated.h" + +class IFlowDataPinValueSupplierInterface; +struct FFlowPin; + +// Interface for special flow node types that support an external data pin supplier. +// The primary (only?) implementing node is UFlowNode_Start, which supplies its pin data externally from +// either the SubGraph that instanced the graph that is being started. +UINTERFACE(MinimalAPI, NotBlueprintable, DisplayName = "Flow Node With External Data Pin Value Supplier Interface") +class UFlowNodeWithExternalDataPinSupplierInterface : public UInterface +{ + GENERATED_BODY() +}; + +class FLOW_API IFlowNodeWithExternalDataPinSupplierInterface +{ + GENERATED_BODY() + +public: + + // Set the external DataPinValueSupplier for this node to use. + virtual void SetDataPinValueSupplier(IFlowDataPinValueSupplierInterface* DataPinValueSupplier) = 0; + + // Append the external InputPins for the external supplier to include in its own pins (eg, UFlowNode_Subgraph) + virtual bool TryAppendExternalInputPins(TArray& InOutPins) const { return false; } + + // Get the IFlowDataPinValueSupplierInterface for the external supplier for this node + virtual IFlowDataPinValueSupplierInterface* GetExternalDataPinSupplier() const = 0; +}; diff --git a/Source/Flow/Public/Nodes/DataPins/FlowNode_DefineProperties.h b/Source/Flow/Public/Nodes/DataPins/FlowNode_DefineProperties.h new file mode 100644 index 000000000..1ecaeeae9 --- /dev/null +++ b/Source/Flow/Public/Nodes/DataPins/FlowNode_DefineProperties.h @@ -0,0 +1,54 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Interfaces/FlowDataPinGeneratorNodeInterface.h" +#include "Nodes/FlowNode.h" +#include "Types/FlowDataPinProperties.h" + +#include "FlowNode_DefineProperties.generated.h" + +/** + * FlowNode to define data pin property literals for use connecting to data pin inputs in a flow graph + */ +UCLASS(Blueprintable, meta = (DisplayName = "Define Properties")) +class FLOW_API UFlowNode_DefineProperties + : public UFlowNode + , public IFlowDataPinGeneratorNodeInterface +{ + GENERATED_UCLASS_BODY() + +protected: + + // Instance-defined properties. + // These will auto-generate a matching pin that is bound to its property as its data source. + UPROPERTY(EditAnywhere, Category = "Configuration", DisplayName = Properties) + TArray OutputProperties; + +public: + +#if WITH_EDITOR + // IFlowContextPinSupplierInterface + virtual bool SupportsContextPins() const override { return Super::SupportsContextPins() || !OutputProperties.IsEmpty(); } + // -- + + // UObject + virtual void PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent) override; + // -- + + // IFlowDataPinGeneratorNodeInterface + virtual void AutoGenerateDataPins( + TMap& InOutPinNameToBoundPropertyNameMap, + TArray& InOutInputDataPins, + TArray& InOutOutputDataPins) const override; + // -- +#endif + +protected: + + virtual bool TryFindPropertyByRemappedPinName( + const FName& RemappedPinName, + const FProperty*& OutFoundProperty, + TInstancedStruct& OutFoundInstancedStruct, + EFlowDataPinResolveResult& InOutResult) const override; +}; diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 12da3b6d7..055d5c4dc 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -4,11 +4,14 @@ #include "EdGraph/EdGraphNode.h" #include "GameplayTagContainer.h" +#include "UObject/TextProperty.h" #include "VisualLogger/VisualLoggerDebugSnapshotInterface.h" #include "FlowNodeBase.h" #include "FlowTypes.h" +#include "Interfaces/FlowDataPinValueSupplierInterface.h" #include "Nodes/FlowPin.h" +#include "Types/FlowDataPinProperties.h" #include "FlowNode.generated.h" @@ -18,6 +21,7 @@ UCLASS(Abstract, Blueprintable, HideCategories = Object) class FLOW_API UFlowNode : public UFlowNodeBase + , public IFlowDataPinValueSupplierInterface , public IVisualLoggerDebugSnapshotInterface { GENERATED_UCLASS_BODY() @@ -98,8 +102,8 @@ class FLOW_API UFlowNode UPROPERTY(EditDefaultsOnly, Category = "FlowNode") TArray OutputPins; - void AddInputPins(TArray Pins); - void AddOutputPins(TArray Pins); + void AddInputPins(const TArray& Pins); + void AddOutputPins(const TArray& Pins); #if WITH_EDITOR // Utility function to rebuild a pin array in editor (either InputPins or OutputPins, passed as InOutPins) @@ -115,10 +119,10 @@ class FLOW_API UFlowNode uint8 CountNumberedInputs() const; uint8 CountNumberedOutputs() const; +public: const TArray& GetInputPins() const { return InputPins; } const TArray& GetOutputPins() const { return OutputPins; } -public: UFUNCTION(BlueprintPure, Category = "FlowNode") TArray GetInputNames() const; @@ -128,6 +132,8 @@ class FLOW_API UFlowNode #if WITH_EDITOR // IFlowContextPinSupplierInterface virtual bool SupportsContextPins() const override; + virtual TArray GetContextInputs() const override; + virtual TArray GetContextOutputs() const override; // -- virtual bool CanUserAddInput() const; @@ -135,6 +141,11 @@ class FLOW_API UFlowNode void RemoveUserInput(const FName& PinName); void RemoveUserOutput(const FName& PinName); + + // Functions to determine acceptance for 'wildcard' data pin types (eg., singular, array, set, map) + // TODO (gtaylor) The data pins feature is under construction + bool DoesInputWildcardPinAcceptArray(const UEdGraphPin* Pin) const { return true; } + bool DoesOutputWildcardPinAcceptContainer(const UEdGraphPin* Pin) const { return true; } #endif protected: @@ -148,7 +159,7 @@ class FLOW_API UFlowNode // Connections to other nodes protected: - // Map outputs to the connected node and input pin + // Map input/outputs to the connected node and input pin UPROPERTY() TMap Connections; @@ -157,18 +168,129 @@ class FLOW_API UFlowNode FConnectedPin GetConnection(const FName OutputName) const { return Connections.FindRef(OutputName); } UFUNCTION(BlueprintPure, Category= "FlowNode") - TSet GetConnectedNodes() const; + TSet GatherConnectedNodes() const; FName GetPinConnectedToNode(const FGuid& OtherNodeGuid); UFUNCTION(BlueprintPure, Category= "FlowNode") - bool IsInputConnected(const FName& PinName) const; + bool IsInputConnected(const FName& PinName, bool bErrorIfPinNotFound = true) const; UFUNCTION(BlueprintPure, Category= "FlowNode") - bool IsOutputConnected(const FName& PinName) const; + bool IsOutputConnected(const FName& PinName, bool bErrorIfPinNotFound = true) const; + + bool IsInputConnected(const FFlowPin& FlowPin) const; + bool IsOutputConnected(const FFlowPin& FlowPin) const; + + FFlowPin* FindInputPinByName(const FName& PinName); + FFlowPin* FindOutputPinByName(const FName& PinName); static void RecursiveFindNodesByClass(UFlowNode* Node, const TSubclassOf Class, uint8 Depth, TArray& OutNodes); +protected: + + // Slow and fast lookup functions, based on whether we are proactively caching the connections for quick lookup + // in the Connections array (by PinCategory) + bool FindConnectedNodeForPinFast(const FName& FlowPinName, FGuid* FoundGuid = nullptr, FName* OutConnectedPinName = nullptr) const; + bool FindConnectedNodeForPinSlow(const FName& FlowPinName, FGuid* FoundGuid = nullptr, FName* OutConnectedPinName = nullptr) const; + +////////////////////////////////////////////////////////////////////////// +// Data Pins + +public: + + // Map of DataPin Name to its Bound Property, + // when using metadata tag 'BindOutputFlowDataPin' to bind properties to data pins for automatic supplier support + UPROPERTY(VisibleDefaultsOnly, AdvancedDisplay, Category = "FlowNode", meta = (GetByRef)) + TMap PinNameToBoundPropertyNameMap; + + const TMap& GetPinNameToBoundPropertyNameMap() const { return PinNameToBoundPropertyNameMap; } + +#if WITH_EDITORONLY_DATA + UPROPERTY(VisibleDefaultsOnly, AdvancedDisplay, Category = "FlowNode", meta = (GetByRef)) + TArray AutoInputDataPins; + + UPROPERTY(VisibleDefaultsOnly, AdvancedDisplay, Category = "FlowNode", meta = (GetByRef)) + TArray AutoOutputDataPins; +#endif // WITH_EDITORONLY_DATA + +#if WITH_EDITOR + void SetPinNameToBoundPropertyNameMap(const TMap& Map); + TMap& GetMutablePinNameToBoundPropertyNameMap() { return PinNameToBoundPropertyNameMap; } + + void SetAutoInputDataPins(const TArray& AutoInputPins); + void SetAutoOutputDataPins(const TArray& AutoOutputPins); + const TArray& GetAutoInputDataPins() const { return AutoInputDataPins; } + const TArray& GetAutoOutputDataPins() const { return AutoOutputDataPins; } + + TArray& GetMutableAutoInputDataPins() { return AutoInputDataPins; } + TArray& GetMutableAutoOutputDataPins() { return AutoOutputDataPins; } +#endif // WITH_EDITOR + + // IFlowDataPinValueSupplierInterface + virtual bool CanSupplyDataPinValues_Implementation() const override; + virtual FFlowDataPinResult_Bool TrySupplyDataPinAsBool_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Int TrySupplyDataPinAsInt_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Float TrySupplyDataPinAsFloat_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Name TrySupplyDataPinAsName_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_String TrySupplyDataPinAsString_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Text TrySupplyDataPinAsText_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Enum TrySupplyDataPinAsEnum_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Vector TrySupplyDataPinAsVector_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Rotator TrySupplyDataPinAsRotator_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Transform TrySupplyDataPinAsTransform_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_GameplayTag TrySupplyDataPinAsGameplayTag_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_GameplayTagContainer TrySupplyDataPinAsGameplayTagContainer_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_InstancedStruct TrySupplyDataPinAsInstancedStruct_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Object TrySupplyDataPinAsObject_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Class TrySupplyDataPinAsClass_Implementation(const FName& PinName) const override; + + bool TryGetFlowDataPinSupplierDatasForPinName( + const FName& PinName, + TArray& InOutPinValueSupplierDatas) const; + // -- + +protected: + + // Helper functions for the TrySupplyDataPin...() functions + bool TryFindPropertyByPinName( + const FName& PinName, + const FProperty*& OutFoundProperty, + TInstancedStruct& OutFoundInstancedStruct, + EFlowDataPinResolveResult& InOutResult) const; + virtual bool TryFindPropertyByRemappedPinName( + const FName& RemappedPinName, + const FProperty*& OutFoundProperty, + TInstancedStruct& OutFoundInstancedStruct, + EFlowDataPinResolveResult& InOutResult) const; + + // Functions to supply the pin data value from a variety of supported property types + template + TFlowDataPinResultType TrySupplyDataPinAsType(const FName& PinName) const; + + template + TFlowDataPinResultType TrySupplyDataPinAsNumericType(const FName& PinName) const; + + template + TFlowDataPinResultType TrySupplyDataPinAsAnyTextType(const FName& PinName) const; + + FORCEINLINE_DEBUGGABLE FFlowDataPinResult_Enum TrySupplyDataPinAsEnumType(const FName& PinName) const; + + template + TFlowDataPinResultType TrySupplyDataPinAsStructType(const FName& PinName) const; + + template + TFlowDataPinResultType TrySupplyDataPinAsUObjectTypeCommon(const FName& PinName, const FProperty*& OutFoundProperty) const; + + template + TFlowDataPinResultType TrySupplyDataPinAsUObjectType(const FName& PinName) const; + + template + TFlowDataPinResultType TrySupplyDataPinAsUClassType(const FName& PinName) const; + ////////////////////////////////////////////////////////////////////////// // Debugger @@ -249,7 +371,7 @@ class FLOW_API UFlowNode TArray GetPinRecords(const FName& PinName, const EEdGraphPinDirection PinDirection) const; // Information displayed while node is working - displayed over node as NodeInfoPopup - virtual FString GetStatusString() const; + FString GetStatusStringForNodeAndAddOns() const; virtual bool GetStatusBackgroundColor(FLinearColor& OutColor) const; virtual FString GetAssetPath(); @@ -258,10 +380,6 @@ class FLOW_API UFlowNode #endif protected: - // Information displayed while node is working - displayed over node as NodeInfoPopup - UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Get Status String")) - FString K2_GetStatusString() const; - UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Get Status Background Color")) bool K2_GetStatusBackgroundColor(FLinearColor& OutColor) const; @@ -290,3 +408,493 @@ class FLOW_API UFlowNode UFUNCTION(BlueprintPure, Category = "FlowNode") static FString GetProgressAsString(float Value); }; + +// Templates & inline implementations: + +template +TFlowDataPinResultType UFlowNode::TrySupplyDataPinAsType(const FName& PinName) const +{ + TFlowDataPinResultType SuppliedResult; + + const FProperty* FoundProperty = nullptr; + TInstancedStruct InstancedStruct; + if (!TryFindPropertyByPinName(PinName, FoundProperty, InstancedStruct, SuppliedResult.Result)) + { + return SuppliedResult; + } + + if (const TFlowDataPinProperty* FlowDataPinProp = InstancedStruct.GetPtr()) + { + // In some cases, TryFindPropertyByPinName can find an instanced struct for the wrapper, + // so get the value from it and return straight away + + SuppliedResult.Value = FlowDataPinProp->Value; + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + + return SuppliedResult; + } + + // Check for struct-based wrapper for the property and get the value out of it + if (const FStructProperty* StructProperty = CastField(FoundProperty)) + { + const UScriptStruct* FlowDataPinPropertyStruct = TFlowDataPinProperty::StaticStruct(); + + if (StructProperty->Struct == FlowDataPinPropertyStruct) + { + TFlowDataPinProperty ValueStruct; + StructProperty->GetValue_InContainer(this, &ValueStruct); + + SuppliedResult.Value = ValueStruct.Value; + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + } + + return SuppliedResult; + } + + // Get the value from a UE simple property type + if (const TFieldPropertyType* UnrealProperty = CastField(FoundProperty)) + { + SuppliedResult.Value = UnrealProperty->GetPropertyValue_InContainer(this); + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + + return SuppliedResult; + } + + SuppliedResult.Result = EFlowDataPinResolveResult::FailedMismatchedType; + + return SuppliedResult; +} + +template +TFlowDataPinResultType UFlowNode::TrySupplyDataPinAsNumericType(const FName& PinName) const +{ + TFlowDataPinResultType SuppliedResult; + + const FProperty* FoundProperty = nullptr; + TInstancedStruct InstancedStruct; + if (!TryFindPropertyByPinName(PinName, FoundProperty, InstancedStruct, SuppliedResult.Result)) + { + return SuppliedResult; + } + + if (const FFlowDataPinProperty* FlowDataPinProp = InstancedStruct.GetPtr()) + { + // In some cases, TryFindPropertyByPinName can find an instanced struct for the wrapper, + // so get the value from it and return straight away + + if (const TFlowLargeDataPinProperty* FlowDataPinPropLarge = InstancedStruct.GetPtr()) + { + SuppliedResult.Value = FlowDataPinPropLarge->Value; + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + + return SuppliedResult; + } + else if (const TFlowMediumDataPinProperty* FlowDataPinPropMedium = InstancedStruct.GetPtr()) + { + SuppliedResult.Value = FlowDataPinPropMedium->Value; + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + + return SuppliedResult; + } + } + + // Check for struct-based wrapper for the property and get the value out of it + if (const FStructProperty* StructProperty = CastField(FoundProperty)) + { + const UScriptStruct* FlowLargeDataPinPropertyStruct = TFlowLargeDataPinProperty::StaticStruct(); + const UScriptStruct* FlowMediumDataPinPropertyStruct = TFlowMediumDataPinProperty::StaticStruct(); + + // Supporting both a 64 and 32 bit wrapper for ints/floats, given the ubiquity of int32/float. + if (StructProperty->Struct == FlowLargeDataPinPropertyStruct) + { + TFlowLargeDataPinProperty ValueStruct; + StructProperty->GetValue_InContainer(this, &ValueStruct); + + SuppliedResult.Value = ValueStruct.Value; + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + } + else if (StructProperty->Struct == FlowMediumDataPinPropertyStruct) + { + TFlowMediumDataPinProperty ValueStruct; + StructProperty->GetValue_InContainer(this, &ValueStruct); + + SuppliedResult.Value = ValueStruct.Value; + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + } + + return SuppliedResult; + } + + // Get the value from a UE simple property type + if (const FNumericProperty* NumericProperty = CastField(FoundProperty)) + { + if (const FFloatProperty* FloatProperty = CastField(NumericProperty)) + { + float FloatValue; + FloatProperty->GetValue_InContainer(this, &FloatValue); + SuppliedResult.Value = FloatValue; + } + else if (const FDoubleProperty* DoubleProperty = CastField(NumericProperty)) + { + double DoubleValue; + DoubleProperty->GetValue_InContainer(this, &DoubleValue); + SuppliedResult.Value = DoubleValue; + } + else + { + SuppliedResult.Value = NumericProperty->GetSignedIntPropertyValue_InContainer(this); + } + + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + + return SuppliedResult; + } + + SuppliedResult.Result = EFlowDataPinResolveResult::FailedMismatchedType; + + return SuppliedResult; +} + +template +TFlowDataPinResultType UFlowNode::TrySupplyDataPinAsAnyTextType(const FName& PinName) const +{ + TFlowDataPinResultType SuppliedResult; + + const FProperty* FoundProperty = nullptr; + TInstancedStruct InstancedStruct; + if (!TryFindPropertyByPinName(PinName, FoundProperty, InstancedStruct, SuppliedResult.Result)) + { + return SuppliedResult; + } + + if (const FFlowDataPinProperty* FlowDataPinProp = InstancedStruct.GetPtr()) + { + // In some cases, TryFindPropertyByPinName can find an instanced struct for the wrapper, + // so get the value from it and return straight away + + if (const FFlowDataPinOutputProperty_Name* FlowDataPinPropName = InstancedStruct.GetPtr()) + { + SuppliedResult.SetValue(FlowDataPinPropName->Value); + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + + return SuppliedResult; + } + else if (const FFlowDataPinOutputProperty_String* FlowDataPinPropString = InstancedStruct.GetPtr()) + { + SuppliedResult.SetValue(FlowDataPinPropString->Value); + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + + return SuppliedResult; + } + else if (const FFlowDataPinOutputProperty_Text* FlowDataPinPropText = InstancedStruct.GetPtr()) + { + SuppliedResult.SetValue(FlowDataPinPropText->Value); + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + + return SuppliedResult; + } + } + + // Check for struct-based wrapper for the property and get the value out of it + if (const FStructProperty* StructProperty = CastField(FoundProperty)) + { + const UScriptStruct* FlowDataPinPropertyStruct_Name = FFlowDataPinOutputProperty_Name::StaticStruct(); + const UScriptStruct* FlowDataPinPropertyStruct_String = FFlowDataPinOutputProperty_String::StaticStruct(); + const UScriptStruct* FlowDataPinPropertyStruct_Text = FFlowDataPinOutputProperty_Text::StaticStruct(); + + if (StructProperty->Struct == FlowDataPinPropertyStruct_Name) + { + FFlowDataPinOutputProperty_Name ValueStruct; + StructProperty->GetValue_InContainer(this, &ValueStruct); + + SuppliedResult.SetValue(ValueStruct.Value); + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + } + else if (StructProperty->Struct == FlowDataPinPropertyStruct_String) + { + FFlowDataPinOutputProperty_String ValueStruct; + StructProperty->GetValue_InContainer(this, &ValueStruct); + + SuppliedResult.SetValue(ValueStruct.Value); + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + } + else if (StructProperty->Struct == FlowDataPinPropertyStruct_Text) + { + FFlowDataPinOutputProperty_Text ValueStruct; + StructProperty->GetValue_InContainer(this, &ValueStruct); + + SuppliedResult.SetValue(ValueStruct.Value); + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + } + + return SuppliedResult; + } + + // Get the value from a UE simple property type + if (const FNameProperty* NameProperty = CastField(FoundProperty)) + { + SuppliedResult.SetValue(NameProperty->GetPropertyValue_InContainer(this)); + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + + return SuppliedResult; + } + else if (const FStrProperty* StrProperty = CastField(FoundProperty)) + { + SuppliedResult.SetValue(StrProperty->GetPropertyValue_InContainer(this)); + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + + return SuppliedResult; + } + else if (const FTextProperty* TextProperty = CastField(FoundProperty)) + { + SuppliedResult.SetValue(TextProperty->GetPropertyValue_InContainer(this)); + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + + return SuppliedResult; + } + else + { + SuppliedResult.Result = EFlowDataPinResolveResult::FailedMismatchedType; + + return SuppliedResult; + } +} + +FFlowDataPinResult_Enum UFlowNode::TrySupplyDataPinAsEnumType(const FName& PinName) const +{ + FFlowDataPinResult_Enum SuppliedResult; + + const FProperty* FoundProperty = nullptr; + TInstancedStruct InstancedStruct; + if (!TryFindPropertyByPinName(PinName, FoundProperty, InstancedStruct, SuppliedResult.Result)) + { + return SuppliedResult; + } + + if (const FFlowDataPinOutputProperty_Enum* FlowDataPinProp = InstancedStruct.GetPtr()) + { + // In some cases, TryFindPropertyByPinName can find an instanced struct for the wrapper, + // so get the value from it and return straight away + + SuppliedResult.Value = FlowDataPinProp->Value; + SuppliedResult.EnumClass = FlowDataPinProp->EnumClass; + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + + return SuppliedResult; + } + + // Check for struct-based wrapper for the property and get the value out of it + if (const FStructProperty* StructProperty = CastField(FoundProperty)) + { + const UScriptStruct* FlowDataPinPropertyStruct_Enum = FFlowDataPinOutputProperty_Enum::StaticStruct(); + + if (StructProperty->Struct == FlowDataPinPropertyStruct_Enum) + { + FFlowDataPinOutputProperty_Enum ValueStruct; + StructProperty->GetValue_InContainer(this, &ValueStruct); + + SuppliedResult.Value = ValueStruct.Value; + SuppliedResult.EnumClass = ValueStruct.EnumClass; + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + } + + return SuppliedResult; + } + + // Get the value from a UE enum property type + if (const FEnumProperty* EnumProperty = CastField(FoundProperty)) + { + UEnum* EnumClass = EnumProperty->GetEnum(); + + const FNumericProperty* UnderlyingProperty = EnumProperty->GetUnderlyingProperty(); + const int64 SignedIntValue = UnderlyingProperty->GetSignedIntPropertyValue_InContainer(this); + const FString StringValue = EnumClass->GetAuthoredNameStringByValue(SignedIntValue); + + SuppliedResult.Value = FName(StringValue); + SuppliedResult.EnumClass = EnumClass; + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + + return SuppliedResult; + } + else + { + SuppliedResult.Result = EFlowDataPinResolveResult::FailedMismatchedType; + + return SuppliedResult; + } +} + +template +TFlowDataPinResultType UFlowNode::TrySupplyDataPinAsStructType(const FName& PinName) const +{ + TFlowDataPinResultType SuppliedResult; + + const FProperty* FoundProperty = nullptr; + TInstancedStruct InstancedStruct; + if (!TryFindPropertyByPinName(PinName, FoundProperty, InstancedStruct, SuppliedResult.Result)) + { + return SuppliedResult; + } + + if (const TFlowDataPinProperty* FlowDataPinProp = InstancedStruct.GetPtr()) + { + // In some cases, TryFindPropertyByPinName can find an instanced struct for the wrapper, + // so get the value from it and return straight away + + SuppliedResult.Value = FlowDataPinProp->Value; + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + + return SuppliedResult; + } + + const FStructProperty* StructProperty = CastField(FoundProperty); + if (!StructProperty) + { + SuppliedResult.Result = EFlowDataPinResolveResult::FailedMismatchedType; + + return SuppliedResult; + } + + const UScriptStruct* FlowDataPinPropertyStruct = TFlowDataPinProperty::StaticStruct(); + static const UScriptStruct* TargetPropertyStruct = TBaseStructure::Get(); + + if (StructProperty->Struct == FlowDataPinPropertyStruct) + { + // Check for struct-based wrapper for the property and get the value out of it + + TFlowDataPinProperty ValueStruct; + StructProperty->GetValue_InContainer(this, &ValueStruct); + + SuppliedResult.Value = ValueStruct.Value; + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + + return SuppliedResult; + } + else if (StructProperty->Struct == TargetPropertyStruct) + { + // Get the value from a UE struct (non-wrapper) property type + + TTargetStruct TargetStruct; + StructProperty->GetValue_InContainer(this, &TargetStruct); + + SuppliedResult.Value = TargetStruct; + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + + return SuppliedResult; + } + else + { + SuppliedResult.Result = EFlowDataPinResolveResult::FailedMismatchedType; + + return SuppliedResult; + } +} + +template +TFlowDataPinResultType UFlowNode::TrySupplyDataPinAsUObjectTypeCommon(const FName& PinName, const FProperty*& OutFoundProperty) const +{ + TFlowDataPinResultType SuppliedResult; + + TInstancedStruct InstancedStruct; + if (!TryFindPropertyByPinName(PinName, OutFoundProperty, InstancedStruct, SuppliedResult.Result)) + { + return SuppliedResult; + } + + if (const TFlowDataPinProperty* FlowDataPinProp = InstancedStruct.GetPtr()) + { + // In some cases, TryFindPropertyByPinName can find an instanced struct for the wrapper, + // so get the value from it and return straight away + + SuppliedResult.SetValueFromPropertyWrapper(*FlowDataPinProp); + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + + return SuppliedResult; + } + + // Check for struct-based wrapper for the property and get the value out of it + if (const FStructProperty* StructProperty = CastField(OutFoundProperty)) + { + const UScriptStruct* FlowDataPinPropertyStruct = TFlowDataPinProperty::StaticStruct(); + + if (StructProperty->Struct == FlowDataPinPropertyStruct) + { + TFlowDataPinProperty ValueStruct; + StructProperty->GetValue_InContainer(this, &ValueStruct); + + SuppliedResult.SetValueFromPropertyWrapper(ValueStruct); + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + } + + return SuppliedResult; + } + + // Get the value from one of the UE simple property types + if (const TFieldPropertyObjectType0* UnrealProperty0 = CastField(OutFoundProperty)) + { + // TObjectPtr / UObject* + TUObjectType* Object = Cast(UnrealProperty0->GetPropertyValue_InContainer(this)); + SuppliedResult.SetValueFromObjectPtr(Object); + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + + return SuppliedResult; + } + + if (const TFieldPropertySoftObjectType1* UnrealProperty1 = CastField(OutFoundProperty)) + { + // FSoftObjectPath / TSoftObjectPtr (or their Class variants) + const FSoftObjectPath SoftObjectPath = UnrealProperty1->GetPropertyValue_InContainer(this).ToSoftObjectPath(); + SuppliedResult.SetValueFromSoftPath(SoftObjectPath); + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + + return SuppliedResult; + } + + SuppliedResult.Result = EFlowDataPinResolveResult::FailedMismatchedType; + + return SuppliedResult; +} + +template +TFlowDataPinResultType UFlowNode::TrySupplyDataPinAsUObjectType(const FName& PinName) const +{ + // First execute TrySupplyDataPinAsUObjectTypeCommon to handle all of the shared cases between UObject and UClass properties: + const FProperty* FoundProperty = nullptr; + TFlowDataPinResultType SuppliedResult = + TrySupplyDataPinAsUObjectTypeCommon(PinName, FoundProperty); + + if (SuppliedResult.Result == EFlowDataPinResolveResult::FailedMismatchedType) + { + if (const TFieldPropertyWeakType2* UnrealProperty2 = CastField(FoundProperty)) + { + // TWeakObjectPtr + TUObjectType* Object = Cast(UnrealProperty2->GetPropertyValue_InContainer(this).Get()); + SuppliedResult.SetValueFromObjectPtr(Object); + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + + return SuppliedResult; + } + + if (const TFieldPropertyLazyType3* UnrealProperty3 = CastField(FoundProperty)) + { + // FLazyObjectPtr + TUObjectType* Object = Cast(UnrealProperty3->GetPropertyValue_InContainer(this).Get()); + SuppliedResult.SetValueFromObjectPtr(Object); + SuppliedResult.Result = EFlowDataPinResolveResult::Success; + + return SuppliedResult; + } + } + + return SuppliedResult; +} + +template +TFlowDataPinResultType UFlowNode::TrySupplyDataPinAsUClassType(const FName& PinName) const +{ + const FProperty* FoundProperty = nullptr; + return TrySupplyDataPinAsUObjectTypeCommon(PinName, FoundProperty); +} diff --git a/Source/Flow/Public/Nodes/FlowNodeBase.h b/Source/Flow/Public/Nodes/FlowNodeBase.h index bdb98632f..4670c9e13 100644 --- a/Source/Flow/Public/Nodes/FlowNodeBase.h +++ b/Source/Flow/Public/Nodes/FlowNodeBase.h @@ -7,7 +7,10 @@ #include "Interfaces/FlowCoreExecutableInterface.h" #include "Interfaces/FlowContextPinSupplierInterface.h" #include "FlowMessageLog.h" +#include "FlowTags.h" #include "FlowTypes.h" +#include "Types/FlowDataPinResults.h" +#include "NativeGameplayTags.h" #include "FlowNodeBase.generated.h" @@ -17,13 +20,37 @@ class UFlowNodeAddOn; class UFlowSubsystem; class UEdGraphNode; class IFlowOwnerInterface; +class IFlowDataPinValueSupplierInterface; +struct FFlowPin; #if WITH_EDITOR DECLARE_DELEGATE(FFlowNodeEvent); #endif -typedef TFunction FConstFlowNodeAddOnFunction; -typedef TFunction FFlowNodeAddOnFunction; +typedef TFunction FConstFlowNodeAddOnFunction; +typedef TFunction FFlowNodeAddOnFunction; + +// Supplier + PinName (in that supplier) for a Flow Data Pin value +struct FFlowPinValueSupplierData +{ + FName SupplierPinName; + const IFlowDataPinValueSupplierInterface* PinValueSupplier = nullptr; +}; + +// Helper template to reduce (some) of the boilerplate in TryResolveDataPinAs...() functions +template +struct TResolveDataPinWorkingData +{ + bool TrySetupWorkingData(const FName& PinName, const UFlowNodeBase& FlowNodeBase); + + TFlowDataPinResultType DataPinResult; + const UFlowNode* FlowNode = nullptr; + const FFlowPin* FlowPin = nullptr; + + TArray PinValueSupplierDatas; + + static constexpr bool bCheckDefaultProperties = true; +}; /** * The base class for UFlowNode and UFlowNodeAddOn, with their shared functionality @@ -49,6 +76,9 @@ class FLOW_API UFlowNodeBase virtual UWorld* GetWorld() const override; // -- + // Dispatcher for ExecuteInput to ensure the AddOns get their ExecuteInput calls even if the node/addon + void ExecuteInputForSelfAndAddOns(const FName& PinName); + // IFlowCoreExecutableInterface virtual void InitializeInstance() override; virtual void DeinitializeInstance() override; @@ -89,12 +119,13 @@ class FLOW_API UFlowNodeBase public: static const FFlowPin* FindFlowPinByName(const FName& PinName, const TArray& FlowPins); + static FFlowPin* FindFlowPinByName(const FName& PinName, TArray& FlowPins); virtual bool IsSupportedInputPinName(const FName& PinName) const PURE_VIRTUAL(IsSupportedInputPinName, return true;); #if WITH_EDITOR public: // IFlowContextPinSupplierInterface - virtual bool SupportsContextPins() const override { return false; } + virtual bool SupportsContextPins() const override { return IFlowContextPinSupplierInterface::SupportsContextPins(); } virtual TArray GetContextInputs() const override; virtual TArray GetContextOutputs() const override; // -- @@ -141,36 +172,100 @@ class FLOW_API UFlowNodeBase protected: // FlowNodes and AddOns may determine which AddOns are eligible to be their children - UFUNCTION(BlueprintNativeEvent, BlueprintPure, Category = "FlowNode") - EFlowAddOnAcceptResult AcceptFlowNodeAddOnChild(const UFlowNodeAddOn* AddOnTemplate) const; + // - AddOnTemplate - the template of the FlowNodeAddOn that is being considered to be added as a child + // - AdditionalAddOnsToAssumeAreChildren - other AddOns to assume that are already child AddOns for the purposes of checking is AddOnTemplate is allowed. + // This list will be populated with the 'other' AddOns in a multi-paste operation in the editor, + // because some paste-targets can only accept a certain mix of addons, so we must know the rest of the set being pasted + // to make the correct decision about whether to allow AddOnTemplate to be added. + // https://forums.unrealengine.com/t/default-parameters-with-tarrays/330225 for details on AutoCreateRefTerm + UFUNCTION(BlueprintNativeEvent, BlueprintPure, Category = "FlowNode", meta = (AutoCreateRefTerm = AdditionalAddOnsToAssumeAreChildren)) + EFlowAddOnAcceptResult AcceptFlowNodeAddOnChild(const UFlowNodeAddOn* AddOnTemplate, const TArray& AdditionalAddOnsToAssumeAreChildren) const; public: virtual const TArray& GetFlowNodeAddOnChildren() const { return AddOns; } #if WITH_EDITOR virtual TArray& GetFlowNodeAddOnChildrenByEditor() { return AddOns; } - EFlowAddOnAcceptResult CheckAcceptFlowNodeAddOnChild(const UFlowNodeAddOn* AddOnTemplate) const; + EFlowAddOnAcceptResult CheckAcceptFlowNodeAddOnChild(const UFlowNodeAddOn* AddOnTemplate, const TArray& AdditionalAddOnsToAssumeAreChildren) const; #endif // WITH_EDITOR // Call a function for all of this object's AddOns (recursively iterating AddOns inside AddOn) - void ForEachAddOnConst(const FConstFlowNodeAddOnFunction& Function) const; - void ForEachAddOn(const FFlowNodeAddOnFunction& Function) const; + EFlowForEachAddOnFunctionReturnValue ForEachAddOnConst(const FConstFlowNodeAddOnFunction& Function) const; + EFlowForEachAddOnFunctionReturnValue ForEachAddOn(const FFlowNodeAddOnFunction& Function) const; template - void ForEachAddOnForClassConst(const FConstFlowNodeAddOnFunction Function) const + EFlowForEachAddOnFunctionReturnValue ForEachAddOnForClassConst(const FConstFlowNodeAddOnFunction Function) const { - ForEachAddOnForClassConst(*TInterfaceOrClass::StaticClass(), Function); + return ForEachAddOnForClassConst(*TInterfaceOrClass::StaticClass(), Function); } - void ForEachAddOnForClassConst(const UClass& InterfaceOrClass, const FConstFlowNodeAddOnFunction& Function) const; + EFlowForEachAddOnFunctionReturnValue ForEachAddOnForClassConst(const UClass& InterfaceOrClass, const FConstFlowNodeAddOnFunction& Function) const; template - void ForEachAddOnForClass(const FFlowNodeAddOnFunction Function) const + EFlowForEachAddOnFunctionReturnValue ForEachAddOnForClass(const FFlowNodeAddOnFunction Function) const { - ForEachAddOnForClass(*TInterfaceOrClass::StaticClass(), Function); + return ForEachAddOnForClass(*TInterfaceOrClass::StaticClass(), Function); } - void ForEachAddOnForClass(const UClass& InterfaceOrClass, const FFlowNodeAddOnFunction& Function) const; + EFlowForEachAddOnFunctionReturnValue ForEachAddOnForClass(const UClass& InterfaceOrClass, const FFlowNodeAddOnFunction& Function) const; + +public: + +////////////////////////////////////////////////////////////////////////// +// Data Pins + + // Must implement TryResolveDataAs... for every EFlowPinType + FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); + + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Bool") + FFlowDataPinResult_Bool TryResolveDataPinAsBool(const FName& PinName) const; + + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Int") + FFlowDataPinResult_Int TryResolveDataPinAsInt(const FName& PinName) const; + + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Float") + FFlowDataPinResult_Float TryResolveDataPinAsFloat(const FName& PinName) const; + + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Name") + FFlowDataPinResult_Name TryResolveDataPinAsName(const FName& PinName) const; + + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As String") + FFlowDataPinResult_String TryResolveDataPinAsString(const FName& PinName) const; + + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Text") + FFlowDataPinResult_Text TryResolveDataPinAsText(const FName& PinName) const; + + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Enum") + FFlowDataPinResult_Enum TryResolveDataPinAsEnum(const FName& PinName) const; + + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Vector") + FFlowDataPinResult_Vector TryResolveDataPinAsVector(const FName& PinName) const; + + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Rotator") + FFlowDataPinResult_Rotator TryResolveDataPinAsRotator(const FName& PinName) const; + + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Transform") + FFlowDataPinResult_Transform TryResolveDataPinAsTransform(const FName& PinName) const; + + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As GameplayTag") + FFlowDataPinResult_GameplayTag TryResolveDataPinAsGameplayTag(const FName& PinName) const; + + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As GameplayTagContainer") + FFlowDataPinResult_GameplayTagContainer TryResolveDataPinAsGameplayTagContainer(const FName& PinName) const; + + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As InstancedStruct") + FFlowDataPinResult_InstancedStruct TryResolveDataPinAsInstancedStruct(const FName& PinName) const; + + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Object") + FFlowDataPinResult_Object TryResolveDataPinAsObject(const FName& PinName) const; + + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Class") + FFlowDataPinResult_Class TryResolveDataPinAsClass(const FName& PinName) const; + + // Public only for for TResolveDataPinWorkingData's use + EFlowDataPinResolveResult TryResolveDataPinPrerequisites(const FName& PinName, const UFlowNode*& FlowNode, const FFlowPin*& FlowPin, EFlowPinType PinType) const; + +public: ////////////////////////////////////////////////////////////////////////// // Editor @@ -201,6 +296,8 @@ class FLOW_API UFlowNodeBase public: UEdGraphNode* GetGraphNode() const { return GraphNode; } + virtual void PostLoad() override; + #if WITH_EDITOR void SetGraphNode(UEdGraphNode* NewGraphNode); @@ -216,18 +313,31 @@ class FLOW_API UFlowNodeBase // used when import graph from another asset virtual void PostImport() {} + + // Called by owning FlowNode to add to its Status String. + // (may be multi-line) + virtual FString GetStatusString() const; #endif +protected: + // Information displayed while node is working - displayed over node as NodeInfoPopup + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Get Status String")) + FString K2_GetStatusString() const; + #if WITH_EDITORONLY_DATA protected: UPROPERTY() FString Category; - UPROPERTY(EditDefaultsOnly, Category = "FlowNode") + UPROPERTY(EditDefaultsOnly, Category = "FlowNode", meta = (Categories = "Flow.NodeDisplayStyle")) + FGameplayTag NodeDisplayStyle; + + // Deprecated NodeStyle, replaced by NodeDisplayStyle + UPROPERTY(meta = (DeprecatedProperty, DeprecationMessage = "Use the NodeDisplayStyle instead.")) EFlowNodeStyle NodeStyle; - // Set Node Style to custom to use your own color for this node - UPROPERTY(EditDefaultsOnly, Category = "FlowNode", meta = (EditCondition = "NodeStyle == EFlowNodeStyle::Custom")) + // Set Node Style to custom to use your own color for this node (if using Flow.NodeDisplayStyle.Custom) + UPROPERTY(EditDefaultsOnly, Category = "FlowNode", DisplayName = "Custom Node Color") FLinearColor NodeColor; // Optional developer-facing text to explain the configuration of this node when viewed in the editor @@ -239,7 +349,8 @@ class FLOW_API UFlowNodeBase #if WITH_EDITOR public: virtual FString GetNodeCategory() const; - EFlowNodeStyle GetNodeStyle() const; + + const FGameplayTag& GetNodeDisplayStyle() const { return NodeDisplayStyle; } // This method allows to have different for every node instance, i.e. Red if node represents enemy, Green if node represents a friend virtual bool GetDynamicTitleColor(FLinearColor& OutColor) const; @@ -248,6 +359,9 @@ class FLOW_API UFlowNodeBase virtual FText GetNodeToolTip() const; virtual FText GetNodeConfigText() const; FText GetGeneratedDisplayName() const; + +protected: + void EnsureNodeDisplayStyle(); #endif protected: @@ -284,6 +398,9 @@ class FLOW_API UFlowNodeBase UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) void LogNote(FString Message) const; + UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) + void LogVerbose(FString Message) const; + #if !UE_BUILD_SHIPPING protected: bool BuildMessage(FString& Message) const; diff --git a/Source/Flow/Public/Nodes/FlowPin.h b/Source/Flow/Public/Nodes/FlowPin.h index 80f63a77a..6c4437fdb 100644 --- a/Source/Flow/Public/Nodes/FlowPin.h +++ b/Source/Flow/Public/Nodes/FlowPin.h @@ -2,25 +2,87 @@ #pragma once +#include "Types/FlowPinEnums.h" + +#include "Templates/SubclassOf.h" #include "UObject/ObjectMacros.h" + #include "FlowPin.generated.h" +class UEnum; +class UClass; +class UObject; +class IPropertyHandle; + USTRUCT(BlueprintType) struct FLOW_API FFlowPin { GENERATED_BODY() // A logical name, used during execution of pin - UPROPERTY(EditDefaultsOnly, Category = "FlowPin") + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "FlowPin") FName PinName; // An optional Display Name, you can use it to override PinName without the need to update graph connections - UPROPERTY(EditDefaultsOnly, Category = "FlowPin") + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "FlowPin") FText PinFriendlyName; - UPROPERTY(EditDefaultsOnly, Category = "FlowPin") + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "FlowPin") FString PinToolTip; +protected: + // PinType (implies PinCategory) + UPROPERTY(EditAnywhere, Category = "FlowPin") + EFlowPinType PinType = EFlowPinType::Exec; + + // Sub-category object + // (used to identify the struct or class type for some PinCategories, see IsSubtypeSupportedPinCategory) + UPROPERTY() + TWeakObjectPtr PinSubCategoryObject; + +#if WITH_EDITORONLY_DATA + // Filter for limiting the compatible classes for this data pin. + // This property is editor-only, but it is automatically copied into PinSubCategoryObject if the PinType matches (for runtime use). + UPROPERTY(EditAnywhere, Category = "FlowPin", meta = (EditCondition = "PinType == EFlowPinType::Class", EditConditionHides)) + TSubclassOf SubCategoryClassFilter = UClass::StaticClass(); + + // Filter for limiting the compatible object types for this data pin. + // This property is editor-only, but it is automatically copied into PinSubCategoryObject if the PinType matches (for runtime use). + UPROPERTY(EditAnywhere, Category = "FlowPin", meta = (EditCondition = "PinType == EFlowPinType::Object", EditConditionHides)) + TSubclassOf SubCategoryObjectFilter = UObject::StaticClass(); + + // Configuration option for setting the EnumClass to a Blueprint Enum + // (C++ enums must bind by name using SubCategoryEnumName, due to a limitation with UE's UEnum discovery). + // This property is editor-only, but it is automatically copied into PinSubCategoryObject if the PinType matches (for runtime use). + UPROPERTY(EditAnywhere, Category = "FlowPin", meta = (EditCondition = "PinType == EFlowPinType::Enum", EditConditionHides)) + UEnum* SubCategoryEnumClass = nullptr; + + // name of enum defined in c++ code, will take priority over asset from EnumType property + // (this is a work-around because EnumClass cannot find C++ Enums, + // so you need to type the name of the enum in here, manually) + // See also: FFlowPin::PostEditChangedEnumName() + UPROPERTY(EditAnywhere, Category = "FlowPin", meta = (EditCondition = "PinType == EFlowPinType::Enum", EditConditionHides)) + FString SubCategoryEnumName; +#endif // WITH_EDITORONLY_DATA + +public: + + // PinCategory aliases for (a subset of) those defined in UEdGraphSchema_K2 + static inline FName PC_Exec = TEXT("exec"); + static inline FName PC_Boolean = TEXT("bool"); + static inline FName PC_Byte = TEXT("byte"); + static inline FName PC_Class = TEXT("class"); + static inline FName PC_Int = TEXT("int"); + static inline FName PC_Int64 = TEXT("int64"); + static inline FName PC_Float = TEXT("float"); + static inline FName PC_Double = TEXT("double"); + static inline FName PC_Name = TEXT("name"); + static inline FName PC_Object = TEXT("object"); + static inline FName PC_String = TEXT("string"); + static inline FName PC_Text = TEXT("text"); + static inline FName PC_Struct = TEXT("struct"); + static inline FName PC_Enum = TEXT("enum"); + static inline FName AnyPinName = TEXT("AnyPinName"); FFlowPin() @@ -77,6 +139,25 @@ struct FLOW_API FFlowPin { } + FFlowPin(const FName& InPinName, const FText& InPinFriendlyName) + : PinName(InPinName) + , PinFriendlyName(InPinFriendlyName) + { + } + + FFlowPin(const FName& InPinName, const FText& InPinFriendlyName, EFlowPinType InFlowPinType, UObject* SubCategoryObject = nullptr) + : PinName(InPinName) + , PinFriendlyName(InPinFriendlyName) + { + SetPinType(InFlowPinType, SubCategoryObject); + } + + FFlowPin(const FName& InPinName, EFlowPinType InFlowPinType, UObject* SubCategoryObject = nullptr) + : PinName(InPinName) + { + SetPinType(InFlowPinType, SubCategoryObject); + } + FORCEINLINE bool IsValid() const { return !PinName.IsNone(); @@ -106,6 +187,109 @@ struct FLOW_API FFlowPin { return GetTypeHash(FlowPin.PinName); } + +public: + +#if WITH_EDITOR + // Must be called from PostEditChangeProperty() by an owning UObject + // whenever PinType, + void PostEditChangedPinTypeOrSubCategorySource(); + FText BuildHeaderText() const; + + static bool ValidateEnum(const UEnum& EnumType); +#endif // WITH_EDITOR + + void SetPinType(EFlowPinType InFlowPinType, UObject* SubCategoryObject = nullptr); + EFlowPinType GetPinType() const { return PinType; } + static const FName& GetPinCategoryFromPinType(EFlowPinType FlowPinType); + static const TArray& GetFlowPinTypeEnumValuesWithoutSpaces(); + + const TWeakObjectPtr& GetPinSubCategoryObject() const { return PinSubCategoryObject; } + + static bool ArePinArraysMatchingNamesAndTypes(const TArray& Left, const TArray& Right); + static bool DoPinsMatchNamesAndTypes(const FFlowPin& LeftPin, const FFlowPin& RightPin) + { + return (LeftPin.PinName == RightPin.PinName && LeftPin.PinType == RightPin.PinType && LeftPin.PinSubCategoryObject == RightPin.PinSubCategoryObject); + } + + // FFlowPin instance signatures for "trait" functions + FORCEINLINE bool IsExecPin() const { return PinType == EFlowPinType::Exec; } + FORCEINLINE bool IsDataPin() const { return PinType != EFlowPinType::Exec; } + // -- + + // PinCategory "trait" functions: + FORCEINLINE static bool IsExecPinCategory(const FName& PC) { return PC == PC_Exec; } + FORCEINLINE static bool IsDataPinCategory(const FName& PC) { return PC != PC_Exec; } + FORCEINLINE static bool IsBoolPinCategory(const FName& PC) { return PC == PC_Boolean; } + FORCEINLINE static bool IsIntPinCategory(const FName& PC) { return PC == PC_Byte || PC == PC_Int || PC == PC_Int64; } + FORCEINLINE static bool IsFloatPinCategory(const FName& PC) { return PC == PC_Double || PC == PC_Float; } + FORCEINLINE static bool IsEnumPinCategory(const FName& PC) { return PC == PC_Enum; } + FORCEINLINE static bool IsTextPinCategory(const FName& PC) { return PC == PC_Name || PC == PC_String || PC == PC_Text; } + FORCEINLINE static bool IsObjectPinCategory(const FName& PC) { return PC == PC_Object; } + FORCEINLINE static bool IsClassPinCategory(const FName& PC) { return PC == PC_Class; } + FORCEINLINE static bool IsStructPinCategory(const FName& PC) { return PC == PC_Struct; } + // -- + + // IsConvertable trait functions: + FORCEINLINE static bool IsConvertableToBoolPinCategory(const FName& PC) { return IsBoolPinCategory(PC); } + FORCEINLINE static bool IsConvertableToIntPinCategory(const FName& PC) { return IsIntPinCategory(PC); } + FORCEINLINE static bool IsConvertableToFloatPinCategory(const FName& PC) { return IsFloatPinCategory(PC); } + FORCEINLINE static bool IsConvertableToEnumPinCategory(const FName& PC) { return IsEnumPinCategory(PC); } + FORCEINLINE static bool IsConvertableToTextPinCategory(const FName& PC) { return IsTextPinCategory(PC); } + FORCEINLINE static bool IsConvertableToObjectPinCategory(const FName& PC) { return IsObjectPinCategory(PC); } + FORCEINLINE static bool IsConvertableToClassPinCategory(const FName& PC) { return IsClassPinCategory(PC); } + FORCEINLINE static bool IsConvertableToStructPinCategory(const FName& PC) { return IsStructPinCategory(PC); } + // -- + + // Metadata keys for properties that bind and auto-generate Data Pins: + + // SourceForOutputFlowPin + // May be used on a non-FFlowDataPinProperty within a UFlowNode to bind the + // output data pin to use the property as its source. + // + // If a string value is given, it is interpreted as the Data Pin's name, + // otherwise, the property's DisplayName (or lacking that, its authored name) + // will be assumed to also be the Pin's name. + static const FName MetadataKey_SourceForOutputFlowPin; + + // DefaultForInputFlowPin + // May be used on a non-FFlowDataPinProperty within a UFlowNode to bind the + // input data pin to use the property as its default value. + // + // If the input pin IS NOT connected to another node, then the bound property + // value will be supplied as a default. + // + // If the input pin IS connected to another node, then the connected node's supplied + // value will be used instead of the default from the bound property. + // + // If a string value is given, it is interpreted as the Data Pin's name, + // otherwise, the property's DisplayName (or lacking that, its authored name) + // will be assumed to also be the Pin's name. + static const FName MetadataKey_DefaultForInputFlowPin; + + // FlowPinType + // May be used on either a property (within a UFlowNode) or a USTRUCT declaration for + // a FFlowDataPinProperty subclass. + // + // If used on a property, then it indicates that a data pin of the given type should be auto-generated, + // and bound to the property. May be used in conjunction with SourceForOutputFlowPin or DefaultForInputFlowPin + // (but not both) to determine how the property binding is to be applied (as input default or output supply source) + // + // If used on a FFlowDataPinProperty struct declaration, then it defines the type of pin + // that should be auto-generated when the struct is used as a property in a UFlowNode. + // + // The string value of the metadata should exactly match a value in EFlowPinType + static const FName MetadataKey_FlowPinType; + // -- + +protected: + + void TrySetStructSubCategoryObjectFromPinType(); + +private: + + // Cached EFlowPinType values as FName, de-spaced, so they can be compared with FlowPinType metadata strings + static TArray FlowPinTypeEnumValuesWithoutSpaces; }; USTRUCT() diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_Branch.h b/Source/Flow/Public/Nodes/Route/FlowNode_Branch.h index 04753960f..49adca144 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_Branch.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_Branch.h @@ -15,7 +15,7 @@ class UFlowNode_Branch : public UFlowNode public: // UFlowNodeBase - virtual EFlowAddOnAcceptResult AcceptFlowNodeAddOnChild_Implementation(const UFlowNodeAddOn* AddOnTemplate) const override; + virtual EFlowAddOnAcceptResult AcceptFlowNodeAddOnChild_Implementation(const UFlowNodeAddOn* AddOnTemplate, const TArray& AdditionalAddOnsToAssumeAreChildren) const override; // -- // Event reacting on triggering Input pin diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_Start.h b/Source/Flow/Public/Nodes/Route/FlowNode_Start.h index 803cf42df..c24fd8921 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_Start.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_Start.h @@ -2,19 +2,62 @@ #pragma once -#include "Nodes/FlowNode.h" +#include "Nodes/DataPins/FlowNode_DefineProperties.h" +#include "Interfaces/FlowNodeWithExternalDataPinSupplierInterface.h" + #include "FlowNode_Start.generated.h" /** * Execution of the graph always starts from this node */ UCLASS(NotBlueprintable, NotPlaceable, meta = (DisplayName = "Start")) -class FLOW_API UFlowNode_Start : public UFlowNode +class FLOW_API UFlowNode_Start + : public UFlowNode_DefineProperties + , public IFlowNodeWithExternalDataPinSupplierInterface { GENERATED_UCLASS_BODY() friend class UFlowAsset; protected: + + // External DataPin Value Supplier + // (eg, the UFlowNode_SubGraph that instanced this Start node's flow asset) + UPROPERTY(Transient) + TScriptInterface FlowDataPinValueSupplierInterface; + +public: + + // IFlowCoreExecutableInterface virtual void ExecuteInput(const FName& PinName) override; + // -- + + // IFlowNodeWithExternalDataPinSupplierInterface + virtual void SetDataPinValueSupplier(IFlowDataPinValueSupplierInterface* DataPinValueSupplier) override; + virtual IFlowDataPinValueSupplierInterface* GetExternalDataPinSupplier() const override { return FlowDataPinValueSupplierInterface.GetInterface(); } +#if WITH_EDITOR + virtual bool TryAppendExternalInputPins(TArray& InOutPins) const override; +#endif + // -- + + // Must implement TrySupplyDataAs... for every EFlowPinType + FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); + + // IFlowDataPinValueSupplierInterface + virtual FFlowDataPinResult_Bool TrySupplyDataPinAsBool_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Int TrySupplyDataPinAsInt_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Float TrySupplyDataPinAsFloat_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Name TrySupplyDataPinAsName_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_String TrySupplyDataPinAsString_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Text TrySupplyDataPinAsText_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Enum TrySupplyDataPinAsEnum_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Vector TrySupplyDataPinAsVector_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Rotator TrySupplyDataPinAsRotator_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Transform TrySupplyDataPinAsTransform_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_GameplayTag TrySupplyDataPinAsGameplayTag_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_GameplayTagContainer TrySupplyDataPinAsGameplayTagContainer_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_InstancedStruct TrySupplyDataPinAsInstancedStruct_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Object TrySupplyDataPinAsObject_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Class TrySupplyDataPinAsClass_Implementation(const FName& PinName) const override; + // -- }; diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_SubGraph.h b/Source/Flow/Public/Nodes/Route/FlowNode_SubGraph.h index 9bbd8afe7..f8c26ad3d 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_SubGraph.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_SubGraph.h @@ -3,13 +3,17 @@ #pragma once #include "Nodes/FlowNode.h" +#include "Interfaces/FlowDataPinGeneratorNodeInterface.h" + #include "FlowNode_SubGraph.generated.h" /** * Creates instance of provided Flow Asset and starts its execution */ UCLASS(NotBlueprintable, meta = (DisplayName = "Sub Graph")) -class FLOW_API UFlowNode_SubGraph : public UFlowNode +class FLOW_API UFlowNode_SubGraph + : public UFlowNode + , public IFlowDataPinGeneratorNodeInterface { GENERATED_UCLASS_BODY() @@ -69,6 +73,7 @@ class FLOW_API UFlowNode_SubGraph : public UFlowNode virtual TArray GetContextOutputs() const override; // -- + virtual FText GetNodeTitle() const override; virtual FString GetNodeDescription() const override; virtual UObject* GetAssetToEdit() override; virtual EDataValidationResult ValidateNode() override; @@ -79,6 +84,17 @@ class FLOW_API UFlowNode_SubGraph : public UFlowNode virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; // -- + // IFlowDataPinValueSupplierInterface + virtual bool CanSupplyDataPinValues_Implementation() const override; + // -- + + // IFlowDataPinGeneratorNodeInterface + virtual void AutoGenerateDataPins( + TMap& InOutPinNameToBoundPropertyNameMap, + TArray& InOutInputDataPins, + TArray& InOutOutputDataPins) const override; + // -- + private: void SubscribeToAssetChanges(); #endif diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_Timer.h b/Source/Flow/Public/Nodes/Route/FlowNode_Timer.h index c920c8662..1c0732041 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_Timer.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_Timer.h @@ -16,17 +16,22 @@ class FLOW_API UFlowNode_Timer : public UFlowNode protected: // If the value is closer to 0, Timer will complete in next tick - UPROPERTY(EditAnywhere, Category = "Timer", meta = (ClampMin = 0.0f)) + UPROPERTY(EditAnywhere, Category = "Timer", meta = (ClampMin = 0.0f, DefaultForInputFlowPin, FlowPinType = Float)) float CompletionTime; // this allows to trigger other nodes multiple times before completing the Timer UPROPERTY(EditAnywhere, Category = "Timer", meta = (ClampMin = 0.0f)) float StepTime; + static FName INPIN_CompletionTime; + private: FTimerHandle CompletionTimerHandle; FTimerHandle StepTimerHandle; + UPROPERTY(SaveGame) + float ResolvedCompletionTime; + UPROPERTY(SaveGame) float SumOfSteps; @@ -37,10 +42,13 @@ class FLOW_API UFlowNode_Timer : public UFlowNode float RemainingStepTime; protected: + virtual void InitializeInstance() override; virtual void ExecuteInput(const FName& PinName) override; virtual void SetTimer(); virtual void Restart(); + + float ResolveCompletionTime() const; private: UFUNCTION() @@ -56,7 +64,10 @@ class FLOW_API UFlowNode_Timer : public UFlowNode virtual void OnLoad_Implementation() override; #if WITH_EDITOR - virtual FString GetNodeDescription() const override; +public: + virtual void UpdateNodeConfigText_Implementation() override; + +protected: virtual FString GetStatusString() const override; #endif }; diff --git a/Source/Flow/Public/Nodes/Utils/FlowNode_Log.h b/Source/Flow/Public/Nodes/Utils/FlowNode_Log.h index 89c1fef8f..9f2df3120 100644 --- a/Source/Flow/Public/Nodes/Utils/FlowNode_Log.h +++ b/Source/Flow/Public/Nodes/Utils/FlowNode_Log.h @@ -27,7 +27,9 @@ class FLOW_API UFlowNode_Log : public UFlowNode GENERATED_UCLASS_BODY() private: - UPROPERTY(EditAnywhere, Category = "Flow") + // The message to write to the log + // (if the Message input pin is not connected to another source) + UPROPERTY(EditAnywhere, Category = "Flow", meta = (DefaultForInputFlowPin, FlowPinType = String)) FString Message; UPROPERTY(EditAnywhere, Category = "Flow") @@ -36,10 +38,10 @@ class FLOW_API UFlowNode_Log : public UFlowNode UPROPERTY(EditAnywhere, Category = "Flow") bool bPrintToScreen; - UPROPERTY(EditAnywhere, Category = "Flow", meta = (EditCondition = "bPrintToScreen")) + UPROPERTY(EditAnywhere, Category = "Flow", meta = (EditCondition = "bPrintToScreen", EditConditionHides)) float Duration; - UPROPERTY(EditAnywhere, Category = "Flow", meta = (EditCondition = "bPrintToScreen")) + UPROPERTY(EditAnywhere, Category = "Flow", meta = (EditCondition = "bPrintToScreen", EditConditionHides)) FColor TextColor; protected: @@ -47,6 +49,6 @@ class FLOW_API UFlowNode_Log : public UFlowNode #if WITH_EDITOR public: - virtual FString GetNodeDescription() const override; + virtual void UpdateNodeConfigText_Implementation() override; #endif }; diff --git a/Source/Flow/Public/Nodes/World/FlowNode_ExecuteComponent.h b/Source/Flow/Public/Nodes/World/FlowNode_ExecuteComponent.h index 7e2f83104..6b34ac0c1 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_ExecuteComponent.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_ExecuteComponent.h @@ -6,6 +6,7 @@ #include "Nodes/FlowNode.h" #include "Types/FlowInjectComponentsHelper.h" +#include "Types/FlowEnumUtils.h" #include "FlowNode_ExecuteComponent.generated.h" @@ -23,14 +24,17 @@ enum class EExecuteComponentSource : uint8 InjectFromClass, Max UMETA(Hidden), + Invalid UMETA(Hidden), + Min = 0, UsesInjectManagerFirst = InjectFromTemplate UMETA(Hidden), UsesInjectManagerLast = InjectFromClass UMETA(Hidden), }; +FLOW_ENUM_RANGE_VALUES(EExecuteComponentSource) namespace EExecuteComponentSource_Classifiers { - FORCEINLINE bool DoesComponentSourceUseInjectManager(EExecuteComponentSource Source) { return Source >= EExecuteComponentSource::UsesInjectManagerFirst && Source <= EExecuteComponentSource::UsesInjectManagerLast; } + FORCEINLINE bool DoesComponentSourceUseInjectManager(EExecuteComponentSource Source) { return FLOW_IS_ENUM_IN_SUBRANGE(Source, EExecuteComponentSource::UsesInjectManager); } } /** diff --git a/Source/Flow/Public/Types/FlowArray.h b/Source/Flow/Public/Types/FlowArray.h new file mode 100644 index 000000000..1831efe26 --- /dev/null +++ b/Source/Flow/Public/Types/FlowArray.h @@ -0,0 +1,73 @@ +// Copyright Riot Games, All Rights Reserved. + +#pragma once + +#include "Algo/Unique.h" +#include "Containers/Array.h" +#include "Math/RandomStream.h" + +namespace FlowArray +{ + // Alias for inline-allocated TArray + // (NOTE, UE's TArray will reallocate to heap ("secondary allocation") + // if the fixed capacity is ever exceeded) + + template + using TInlineArray = TArray>; + + template + void ReverseArray(TArray& InOutArray) + { + for (int32 FrontIndex = 0, BackIndex = InOutArray.Num() - 1; FrontIndex < BackIndex; ++FrontIndex, --BackIndex) + { + InOutArray.Swap(FrontIndex, BackIndex); + } + } + + template + void ShuffleArray(TArray& Array, FRandomStream& RandomStream) + { + // Trivial cases + if (Array.Num() <= 2) + { + if (Array.Num() == 2) + { + const bool bShouldSwap = RandomStream.RandRange(0, 1) == 0; + if (bShouldSwap) + { + Array.Swap(0, 1); + } + } + + return; + } + + // Simple shuffle, attempt swaps for each index in the array, once each + for (int32 FromIndex = 0; FromIndex < Array.Num(); ++FromIndex) + { + const int32 IndexOffset = RandomStream.RandRange(1, Array.Num() - 1); + const int32 OtherIndex = (FromIndex + IndexOffset) % Array.Num(); + check(FromIndex != OtherIndex); + + Array.Swap(FromIndex, OtherIndex); + } + } + + template + bool TrySortAndRemoveDuplicatesFromArrayInPlace(TArray& InOutArray) + { + InOutArray.Sort(TGreater{}); + + const int32 SizeBefore = InOutArray.Num(); + const int32 SizeAfter = Algo::Unique(InOutArray); + + if (SizeBefore > SizeAfter) + { + InOutArray.SetNum(SizeAfter); + + return true; + } + + return false; + } +} \ No newline at end of file diff --git a/Source/Flow/Public/Types/FlowClassUtils.h b/Source/Flow/Public/Types/FlowClassUtils.h new file mode 100644 index 000000000..8c878e265 --- /dev/null +++ b/Source/Flow/Public/Types/FlowClassUtils.h @@ -0,0 +1,15 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Containers/Array.h" + +class FString; +class UClass; + +#if WITH_EDITOR +namespace FlowClassUtils +{ + TArray GetClassesFromMetadataString(const FString& MetadataString); +} +#endif \ No newline at end of file diff --git a/Source/Flow/Public/Types/FlowDataPinBlueprintLibrary.h b/Source/Flow/Public/Types/FlowDataPinBlueprintLibrary.h new file mode 100644 index 000000000..250635f10 --- /dev/null +++ b/Source/Flow/Public/Types/FlowDataPinBlueprintLibrary.h @@ -0,0 +1,224 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "FlowDataPinProperties.h" +#include "FlowDataPinResults.h" + +#include "FlowDataPinBlueprintLibrary.generated.h" + +// Auto-cast operators for blueprint to their inner types +UCLASS() +class UFlowDataPinBlueprintLibrary : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + + // Recommend implementing AutoConvert_FlowDataPinProperty... for every EFlowPinType + FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); + + // FFlowDataPinProperty auto-cast functions + + // Convert bool property values to their inner blue values + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Bool", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static bool AutoConvert_FlowDataPinPropertyBoolToBool(const FFlowDataPinOutputProperty_Bool& BoolProperty) { return BoolProperty.Value; } + + // to Int variants for all int types + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Int", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static int32 AutoConvert_FlowDataPinPropertyInt32ToInt32(const FFlowDataPinOutputProperty_Int32& IntProperty) { return IntProperty.Value; } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Int64", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static int64 AutoConvert_FlowDataPinPropertyInt32ToInt64(const FFlowDataPinOutputProperty_Int32& IntProperty) { return static_cast(IntProperty.Value); } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Int", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static int32 AutoConvert_FlowDataPinPropertyInt64ToInt32(const FFlowDataPinOutputProperty_Int64& IntProperty) { /* possible loss of precision */ return static_cast(IntProperty.Value); } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Int64", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static int64 AutoConvert_FlowDataPinPropertyInt64ToInt64(const FFlowDataPinOutputProperty_Int64& IntProperty) { return IntProperty.Value; } + + // to Float variants for all float types + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Float", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static float AutoConvert_FlowDataPinPropertyFloat32ToFloat32(const FFlowDataPinOutputProperty_Float& FloatProperty) { return FloatProperty.Value; } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Double", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static double AutoConvert_FlowDataPinPropertyFloat32ToFloat64(const FFlowDataPinOutputProperty_Float& FloatProperty) { return static_cast(FloatProperty.Value); } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Float", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static float AutoConvert_FlowDataPinPropertyFloat64ToFloat32(const FFlowDataPinOutputProperty_Double& FloatProperty) { /* possible loss of precision */ return static_cast(FloatProperty.Value); } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Double", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static double AutoConvert_FlowDataPinPropertyFloat64ToFloat64(const FFlowDataPinOutputProperty_Double& FloatProperty) { return FloatProperty.Value; } + + // to Name variants for all text-based types + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Name", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FName AutoConvert_FlowDataPinPropertyNameToName(const FFlowDataPinOutputProperty_Name& NameProperty) { return NameProperty.Value; } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Name", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FName AutoConvert_FlowDataPinPropertyStringToName(const FFlowDataPinOutputProperty_String& StringProperty) { return FName(StringProperty.Value); } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Name", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FName AutoConvert_FlowDataPinPropertyTextToName(const FFlowDataPinOutputProperty_Text& TextProperty) { return FName(TextProperty.Value.ToString()); } + + // to String variants for all text-based types + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to String", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FString AutoConvert_FlowDataPinPropertyNameToString(const FFlowDataPinOutputProperty_Name& NameProperty) { return NameProperty.Value.ToString(); } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to String", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FString AutoConvert_FlowDataPinPropertyStringToString(const FFlowDataPinOutputProperty_String& StringProperty) { return StringProperty.Value; } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to String", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FString AutoConvert_FlowDataPinPropertyTextToString(const FFlowDataPinOutputProperty_Text& TextProperty) { return TextProperty.Value.ToString(); } + + // to Text variants for all text-based types + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Text", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FText AutoConvert_FlowDataPinPropertyNameToText(const FFlowDataPinOutputProperty_Name& NameProperty) { return FText::FromName(NameProperty.Value); } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Text", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FText AutoConvert_FlowDataPinPropertyStringToText(const FFlowDataPinOutputProperty_String& StringProperty) { return FText::FromString(StringProperty.Value); } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Text", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FText AutoConvert_FlowDataPinPropertyTextToText(const FFlowDataPinOutputProperty_Text& TextProperty) { return TextProperty.Value; } + + // Convert enum property values to their inner enum values + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Enum", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static uint8 AutoConvert_FlowDataPinPropertyEnumToEnum(const FFlowDataPinOutputProperty_Enum& EnumProperty); + + // Convert vector property values to their inner Vector + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Vector", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FVector AutoConvert_FlowDataPinPropertyVectorToVector(const FFlowDataPinOutputProperty_Vector& VectorProperty) { return VectorProperty.Value; } + + // Convert Rotator property values to their inner Rotator + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Rotator", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FRotator AutoConvert_FlowDataPinPropertyRotatorToRotator(const FFlowDataPinOutputProperty_Rotator& RotatorProperty) { return RotatorProperty.Value; } + + // Convert Transform property values to their inner Transform + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Transform", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FTransform AutoConvert_FlowDataPinPropertyTransformToTransform(const FFlowDataPinOutputProperty_Transform& TransformProperty) { return TransformProperty.Value; } + + // Convert GameplayTag property values to their inner GameplayTag + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to GameplayTag", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FGameplayTag AutoConvert_FlowDataPinPropertyGameplayTagToGameplayTag(const FFlowDataPinOutputProperty_GameplayTag& GameplayTagProperty) { return GameplayTagProperty.Value; } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to GameplayTagContainer", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FGameplayTagContainer AutoConvert_FlowDataPinPropertyGameplayTagToGameplayTagContainer(const FFlowDataPinOutputProperty_GameplayTag& GameplayTagProperty) { return FGameplayTagContainer(GameplayTagProperty.Value); } + + // Convert GameplayTagContainer property values to their inner GameplayTagContainer + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to GameplayTagContainer", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FGameplayTagContainer AutoConvert_FlowDataPinPropertyGameplayTagContainerToGameplayTagContainer(const FFlowDataPinOutputProperty_GameplayTagContainer& GameplayTagContainerProperty) { return GameplayTagContainerProperty.Value; } + + // Convert InstancedStruct property values to their inner InstancedStruct + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to InstancedStruct", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FInstancedStruct AutoConvert_FlowDataPinPropertyInstancedStructToInstancedStruct(const FFlowDataPinOutputProperty_InstancedStruct& InstancedStructProperty) { return InstancedStructProperty.Value; } + + // Convert Object property values to their inner Object + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Object", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static UObject* AutoConvert_FlowDataPinPropertyObjectToObject(const FFlowDataPinOutputProperty_Object& ObjectProperty) { return ObjectProperty.GetObjectValue(); } + + // Convert Class property values to their inner Class + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Class", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static UClass* AutoConvert_FlowDataPinPropertyClassToClass(const FFlowDataPinOutputProperty_Class& ClassProperty) { return ClassProperty.GetResolvedClass(); } + + // Convert Class property values to their FSoftClassPath + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to SoftClass", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FSoftClassPath AutoConvert_FlowDataPinPropertyClassToSoftClass(const FFlowDataPinOutputProperty_Class& ClassProperty) { return ClassProperty.GetAsSoftClass(); } + + // Recommend implementing AutoConvert_FlowDataPinResult... for every EFlowPinType + FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); + + // FFlowDataPinResults auto-cast functions + + // Convert bool property values to their inner blueprint values + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Bool", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static bool AutoConvert_FlowDataPinResultBoolToBool(const FFlowDataPinResult_Bool& BoolProperty) { return BoolProperty.Value; } + + // to Int variants for all int types (that blueprint supports + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Int", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static int32 AutoConvert_FlowDataPinResultInt64ToInt32(const FFlowDataPinResult_Int& IntProperty) { /* possible loss of precision */ return static_cast(IntProperty.Value); } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Int64", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static int64 AutoConvert_FlowDataPinResultInt64ToInt64(const FFlowDataPinResult_Int& IntProperty) { return IntProperty.Value; } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Int", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static uint8 AutoConvert_FlowDataPinResultInt64ToUint8(const FFlowDataPinResult_Int& IntProperty) { /* possible loss of precision */ return static_cast(IntProperty.Value); } + + // to Float variants for all float types + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Float", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static float AutoConvert_FlowDataPinResultFloat64ToFloat32(const FFlowDataPinResult_Float& FloatProperty) { /* possible loss of precision */ return static_cast(FloatProperty.Value); } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Double", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static double AutoConvert_FlowDataPinResultFloat64ToFloat64(const FFlowDataPinResult_Float& FloatProperty) { return FloatProperty.Value; } + + // to Name variants for all text-based types + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Name", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FName AutoConvert_FlowDataPinResultNameToName(const FFlowDataPinResult_Name& NameProperty) { return NameProperty.Value; } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Name", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FName AutoConvert_FlowDataPinResultStringToName(const FFlowDataPinResult_String& StringProperty) { return FName(StringProperty.Value); } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Name", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FName AutoConvert_FlowDataPinResultTextToName(const FFlowDataPinResult_Text& TextProperty) { return FName(TextProperty.Value.ToString()); } + + // to String variants for all text-based types + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to String", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FString AutoConvert_FlowDataPinResultNameToString(const FFlowDataPinResult_Name& NameProperty) { return NameProperty.Value.ToString(); } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to String", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FString AutoConvert_FlowDataPinResultStringToString(const FFlowDataPinResult_String& StringProperty) { return StringProperty.Value; } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to String", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FString AutoConvert_FlowDataPinResultTextToString(const FFlowDataPinResult_Text& TextProperty) { return TextProperty.Value.ToString(); } + + // to Text variants for all text-based types + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Text", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FText AutoConvert_FlowDataPinResultNameToText(const FFlowDataPinResult_Name& NameProperty) { return FText::FromName(NameProperty.Value); } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Text", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FText AutoConvert_FlowDataPinResultStringToText(const FFlowDataPinResult_String& StringProperty) { return FText::FromString(StringProperty.Value); } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Text", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FText AutoConvert_FlowDataPinResultTextToText(const FFlowDataPinResult_Text& TextProperty) { return TextProperty.Value; } + + // Convert enum property values to their inner enum values + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Enum", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static uint8 AutoConvert_FlowDataPinResultEnumToEnum(const FFlowDataPinResult_Enum& EnumProperty); + + // Convert vector property values to their inner Vector + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Vector", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FVector AutoConvert_FlowDataPinResultVectorToVector(const FFlowDataPinResult_Vector& VectorProperty) { return VectorProperty.Value; } + + // Convert Rotator property values to their inner Rotator + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Rotator", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FRotator AutoConvert_FlowDataPinResultRotatorToRotator(const FFlowDataPinResult_Rotator& RotatorProperty) { return RotatorProperty.Value; } + + // Convert Transform property values to their inner Transform + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Transform", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FTransform AutoConvert_FlowDataPinResultTransformToTransform(const FFlowDataPinResult_Transform& TransformProperty) { return TransformProperty.Value; } + + // Convert GameplayTag property values to their inner GameplayTag + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to GameplayTag", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FGameplayTag AutoConvert_FlowDataPinResultGameplayTagToGameplayTag(const FFlowDataPinResult_GameplayTag& GameplayTagProperty) { return GameplayTagProperty.Value; } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to GameplayTagContainer", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FGameplayTagContainer AutoConvert_FlowDataPinResultGameplayTagToGameplayTagContainer(const FFlowDataPinResult_GameplayTag& GameplayTagProperty) { return FGameplayTagContainer(GameplayTagProperty.Value); } + + // Convert GameplayTagContainer property values to their inner GameplayTagContainer + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to GameplayTagContainer", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FGameplayTagContainer AutoConvert_FlowDataPinResultGameplayTagContainerToGameplayTagContainer(const FFlowDataPinResult_GameplayTagContainer& GameplayTagContainerProperty) { return GameplayTagContainerProperty.Value; } + + // Convert InstancedStruct property values to their inner InstancedStruct + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to InstancedStruct", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FInstancedStruct AutoConvert_FlowDataPinResultInstancedStructToInstancedStruct(const FFlowDataPinResult_InstancedStruct& InstancedStructProperty) { return InstancedStructProperty.Value; } + + // Convert Object property values to their inner Object + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Object", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static UObject* AutoConvert_FlowDataPinResultObjectToObject(const FFlowDataPinResult_Object& ObjectProperty) { return ObjectProperty.Value; } + + // Convert Class property values to their inner Class + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Class", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static UClass* AutoConvert_FlowDataPinResultClassToClass(const FFlowDataPinResult_Class& ClassProperty) { return ClassProperty.GetOrResolveClass(); } + + // Convert Class property values to their the FSoftClassPath + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to SoftClass", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FSoftClassPath AutoConvert_FlowDataPinResultClassToSoftClass(const FFlowDataPinResult_Class& ClassProperty) { return ClassProperty.GetAsSoftClass(); } +}; \ No newline at end of file diff --git a/Source/Flow/Public/Types/FlowDataPinProperties.h b/Source/Flow/Public/Types/FlowDataPinProperties.h new file mode 100644 index 000000000..0550b3c66 --- /dev/null +++ b/Source/Flow/Public/Types/FlowDataPinProperties.h @@ -0,0 +1,637 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Nodes/FlowPin.h" + +#include "GameplayTagContainer.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "UObject/Class.h" + +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 5 +#include "StructUtils/InstancedStruct.h" +#else +#include "InstancedStruct.h" +#endif + +#include "FlowDataPinProperties.generated.h" + +class FStructProperty; +class UScriptStruct; + +USTRUCT(BlueprintType, DisplayName = "Base - Flow DataPin Property") +struct FFlowDataPinProperty +{ + GENERATED_BODY() + + FFlowDataPinProperty() { } + + virtual ~FFlowDataPinProperty() { } + + virtual EFlowPinType GetFlowPinType() const { return EFlowPinType::Invalid; } + +#if WITH_EDITOR + FLOW_API static FFlowPin CreateFlowPin(const FName& PinName, const TInstancedStruct& DataPinProperty); + + template + static UScriptStruct* FindScriptStructForFlowDataPinProperty(const FProperty& Property) + { + // Find the ScriptStruct of the wrapped struct in a wrapper (eg, FFlowDataPinOutputProperty_Vector) or the struct itself (eg, FVector) + const FStructProperty* StructProperty = CastField(&Property); + if (!StructProperty) + { + return nullptr; + } + + UScriptStruct* ScriptStruct = TFlowDataPinPropertyType::StaticStruct(); + if (StructProperty->Struct == ScriptStruct) + { + static UScriptStruct* UnrealType = TBaseStructure::Get(); + + return UnrealType; + } + else + { + return StructProperty->Struct; + } + } +#endif +}; + +// Recommend implementing FFlowDataPinProperty... for every EFlowPinType +FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); + +// Wrapper struct for a bool that will generate and link to a Data Pin with its same name +USTRUCT(BlueprintType, DisplayName = "Bool - Output Flow Data Pin Property", meta = (FlowPinType = "Bool")) +struct FFlowDataPinOutputProperty_Bool : public FFlowDataPinProperty +{ + GENERATED_BODY() + +public: + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + bool Value = false; + +public: + + FFlowDataPinOutputProperty_Bool() { } + FFlowDataPinOutputProperty_Bool(bool InValue) : Value(InValue) { } + + virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Bool; } +}; + +// Wrapper struct for a int64 that will generate and link to a Data Pin with its same name +USTRUCT(BlueprintType, DisplayName = "Int64 - Output Flow Data Pin Property", meta = (FlowPinType = "Int")) +struct FFlowDataPinOutputProperty_Int64 : public FFlowDataPinProperty +{ + GENERATED_BODY() + +public: + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + int64 Value = 0; + +public: + + FFlowDataPinOutputProperty_Int64() { } + FFlowDataPinOutputProperty_Int64(int64 InValue) : Value(InValue) { } + + virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Int; } +}; + +// Wrapper struct for a int32 that will generate and link to a Data Pin with its same name +USTRUCT(BlueprintType, DisplayName = "Int - Output Flow Data Pin Property", meta = (FlowPinType = "Int")) +struct FFlowDataPinOutputProperty_Int32 : public FFlowDataPinProperty +{ + GENERATED_BODY() + +public: + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + int32 Value = 0; + +public: + + FFlowDataPinOutputProperty_Int32() { } + FFlowDataPinOutputProperty_Int32(int32 InValue) : Value(InValue) { } + + virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Int; } +}; + +// Wrapper struct for a Double (64bit float) that will generate and link to a Data Pin with its same name +USTRUCT(BlueprintType, DisplayName = "Double (float64) - Output Flow Data Pin Property", meta = (FlowPinType = "Float")) +struct FFlowDataPinOutputProperty_Double : public FFlowDataPinProperty +{ + GENERATED_BODY() + +public: + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + double Value = 0; + +public: + + FFlowDataPinOutputProperty_Double() { } + FFlowDataPinOutputProperty_Double(double InValue) : Value(InValue) { } + + virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Float; } +}; + +// Wrapper struct for a Float (32bit) that will generate and link to a Data Pin with its same name +USTRUCT(BlueprintType, DisplayName = "Float - Output Flow Data Pin Property", meta = (FlowPinType = "Float")) +struct FFlowDataPinOutputProperty_Float : public FFlowDataPinProperty +{ + GENERATED_BODY() + +public: + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + float Value = 0.0f; + +public: + + FFlowDataPinOutputProperty_Float() { } + FFlowDataPinOutputProperty_Float(float InValue) : Value(InValue) { } + + virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Float; } +}; + +// Wrapper struct for a FName that will generate and link to a Data Pin with its same name +USTRUCT(BlueprintType, DisplayName = "Name - Output Flow Data Pin Property", meta = (FlowPinType = "Name")) +struct FFlowDataPinOutputProperty_Name : public FFlowDataPinProperty +{ + GENERATED_BODY() + +public: + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + FName Value = NAME_None; + +public: + + FFlowDataPinOutputProperty_Name() { } + FFlowDataPinOutputProperty_Name(const FName& InValue) : Value(InValue) { } + + virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Name; } +}; + +// Wrapper struct for a FString that will generate and link to a Data Pin with its same name +USTRUCT(BlueprintType, DisplayName = "String - Output Flow Data Pin Property", meta = (FlowPinType = "String")) +struct FFlowDataPinOutputProperty_String : public FFlowDataPinProperty +{ + GENERATED_BODY() + +public: + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + FString Value; + +public: + + FFlowDataPinOutputProperty_String() { } + FFlowDataPinOutputProperty_String(const FString& InValue) : Value(InValue) { } + + virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::String; } +}; + +// Wrapper struct for a FText that will generate and link to a Data Pin with its same name +USTRUCT(BlueprintType, DisplayName = "Text - Output Flow Data Pin Property", meta = (FlowPinType = "Text")) +struct FFlowDataPinOutputProperty_Text : public FFlowDataPinProperty +{ + GENERATED_BODY() + +public: + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + FText Value; + +public: + + FFlowDataPinOutputProperty_Text() { } + FFlowDataPinOutputProperty_Text(const FText& InValue) : Value(InValue) { } + + virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Text; } +}; + +// Wrapper struct for an enum that will generate and link to a Data Pin with its same name +USTRUCT(BlueprintType, DisplayName = "Enum - Output Flow Data Pin Property", meta = (FlowPinType = "Enum")) +struct FFlowDataPinOutputProperty_Enum : public FFlowDataPinProperty +{ + GENERATED_BODY() + +public: + + // The selected enum Value + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + FName Value = NAME_None; + + // Class for this enum + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + UEnum* EnumClass = nullptr; + +#if WITH_EDITORONLY_DATA + // name of enum defined in c++ code, will take priority over asset from EnumType property + // (this is a work-around because EnumClass cannot find C++ Enums, + // so you need to type the name of the enum in here, manually) + // See also: UBlackboardKeyType_Enum::PostEditChangeProperty() + UPROPERTY(EditAnywhere, Category = Blackboard) + FString EnumName; +#endif // WITH_EDITORONLY_DATA + +public: + + FFlowDataPinOutputProperty_Enum() { } + FFlowDataPinOutputProperty_Enum(const FName& InValue, UEnum* InEnumClass) + : Value(InValue) + , EnumClass(InEnumClass) + { } + + virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Enum; } + +#if WITH_EDITOR + FLOW_API void OnEnumNameChanged(); +#endif // WITH_EDITOR +}; + +// Wrapper struct for a FVector that will generate and link to a Data Pin with its same name +USTRUCT(BlueprintType, DisplayName = "Vector - Output Flow Data Pin Property", meta = (FlowPinType = "Vector")) +struct FFlowDataPinOutputProperty_Vector : public FFlowDataPinProperty +{ + GENERATED_BODY() + +public: + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + FVector Value; + +public: + + FFlowDataPinOutputProperty_Vector() {} + FFlowDataPinOutputProperty_Vector(const FVector& InValue) : Value(InValue) { } + + virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Vector; } +}; + +// Wrapper struct for a FRotator that will generate and link to a Data Pin with its same name +USTRUCT(BlueprintType, DisplayName = "Rotator - Output Flow Data Pin Property", meta = (FlowPinType = "Rotator")) +struct FFlowDataPinOutputProperty_Rotator : public FFlowDataPinProperty +{ + GENERATED_BODY() + +public: + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + FRotator Value; + +public: + + FFlowDataPinOutputProperty_Rotator() {} + FFlowDataPinOutputProperty_Rotator(const FRotator& InValue) : Value(InValue) { } + + virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Rotator; } +}; + +// Wrapper struct for a FTransform that will generate and link to a Data Pin with its same name +USTRUCT(BlueprintType, DisplayName = "Transform - Output Flow Data Pin Property", meta = (FlowPinType = "Transform")) +struct FFlowDataPinOutputProperty_Transform : public FFlowDataPinProperty +{ + GENERATED_BODY() + +public: + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + FTransform Value; + +public: + + FFlowDataPinOutputProperty_Transform() {} + FFlowDataPinOutputProperty_Transform(const FTransform& InValue) : Value(InValue) { } + + virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Transform; } +}; + +// Wrapper struct for a FGameplayTag that will generate and link to a Data Pin with its same name +USTRUCT(BlueprintType, DisplayName = "GameplayTag - Output Flow Data Pin Property", meta = (FlowPinType = "GameplayTag")) +struct FFlowDataPinOutputProperty_GameplayTag : public FFlowDataPinProperty +{ + GENERATED_BODY() + +public: + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + FGameplayTag Value; + +public: + + FFlowDataPinOutputProperty_GameplayTag() {} + FFlowDataPinOutputProperty_GameplayTag(const FGameplayTag& InValue) : Value(InValue) { } + + virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::GameplayTag; } +}; + +// Wrapper struct for a FGameplayTagContainer that will generate and link to a Data Pin with its same name +USTRUCT(BlueprintType, DisplayName = "GameplayTagContainer - Output Flow DataPin Property", meta = (FlowPinType = "GameplayTagContainer")) +struct FFlowDataPinOutputProperty_GameplayTagContainer : public FFlowDataPinProperty +{ + GENERATED_BODY() + +public: + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + FGameplayTagContainer Value; + +public: + + FFlowDataPinOutputProperty_GameplayTagContainer() {} + FFlowDataPinOutputProperty_GameplayTagContainer(const FGameplayTagContainer& InValue) : Value(InValue) { } + + virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::GameplayTagContainer; } +}; + +// Wrapper struct for a FInstancedStruct that will generate and link to a Data Pin with its same name +USTRUCT(BlueprintType, DisplayName = "InstancedStruct - Output Flow DataPin Property", meta = (FlowPinType = "InstancedStruct")) +struct FFlowDataPinOutputProperty_InstancedStruct : public FFlowDataPinProperty +{ + GENERATED_BODY() + +public: + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + FInstancedStruct Value; + +public: + + FFlowDataPinOutputProperty_InstancedStruct() {} + FFlowDataPinOutputProperty_InstancedStruct(const FInstancedStruct& InValue) : Value(InValue) { } + + virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::InstancedStruct; } +}; + +// Wrapper struct for a UObject that will generate and link to a Data Pin with its same name +USTRUCT(BlueprintType, DisplayName = "Object - Output Flow DataPin Property", meta = (FlowPinType = "Object")) +struct FFlowDataPinOutputProperty_Object : public FFlowDataPinProperty +{ + GENERATED_BODY() + + friend class FFlowDataPinProperty_ObjectCustomizationBase; + +protected: + + // These pointers are separate so that the default value for the object can be configured + // in the editor according to the type of object that it is (instanced or not). + + // Object reference if the object is a non-instanced UObject type (ie, not EditInlineNew) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins, DisplayName = "Value (reference)", meta = (EditCondition = "InlineValue == nullptr")) + TObjectPtr ReferenceValue = nullptr; + + // Ofject reference if the object is an instanced UObject type (ie, EditInlineNew) + UPROPERTY(EditAnywhere, Instanced, BlueprintReadWrite, Category = DataPins, DisplayName = "Value (inline)", meta = (EditCondition = "ReferenceValue == nullptr")) + TObjectPtr InlineValue = nullptr; + +public: + +#if WITH_EDITORONLY_DATA + UPROPERTY(EditAnywhere, Category = DataPins, meta = (AllowAbstract)) + TObjectPtr ClassFilter = nullptr; +#endif // WITH_EDITORONLY_DATA + +public: + + FFlowDataPinOutputProperty_Object() {} + FLOW_API FFlowDataPinOutputProperty_Object(UObject* InValue, UClass* InClassFilter); + + virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Object; } + + UObject* GetObjectValue() const { return ReferenceValue ? ReferenceValue : InlineValue; } + void SetObjectValue(UObject* InValue); + +#if WITH_EDITOR + UClass* DeriveObjectClass(const FProperty& MetaDataProperty) const; + FLOW_API static UClass* TryGetObjectClassFromProperty(const FProperty& MetaDataProperty); +#endif +}; + +// Wrapper struct for a UClass that will generate and link to a Data Pin with its same name +USTRUCT(BlueprintType, DisplayName = "Class - Output Flow DataPin Property", meta = (FlowPinType = "Class")) +struct FFlowDataPinOutputProperty_Class : public FFlowDataPinProperty +{ + GENERATED_BODY() + + friend class FFlowDataPinProperty_ClassCustomizationBase; + +protected: + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + FSoftClassPath Value; + +public: + +#if WITH_EDITORONLY_DATA + UPROPERTY(EditAnywhere, Category = DataPins, meta = (AllowAbstract)) + TObjectPtr ClassFilter = nullptr; +#endif // WITH_EDITORONLY_DATA + +public: + + FFlowDataPinOutputProperty_Class() {} + FFlowDataPinOutputProperty_Class(const FSoftClassPath& InValue, UClass* InClassFilter) + : Value(InValue) +#if WITH_EDITOR + , ClassFilter(InClassFilter) +#endif + { } + + virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Class; } + +#if WITH_EDITOR + UClass* DeriveMetaClass(const FProperty& MetaDataProperty) const; + FLOW_API static UClass* TryGetMetaClassFromProperty(const FProperty& MetaDataProperty); +#endif + + const FSoftClassPath& GetAsSoftClass() const { return Value; } + UClass* GetResolvedClass() const { return Value.ResolveClass(); } +}; + +// Wrapper for FFlowDataPinProperty that is used for flow nodes that add +// dynamic properties, with associated data pins, on the flow node instance +// (as opposed to C++ or blueprint compile-time). +USTRUCT(BlueprintType, DisplayName = "Flow Named Output DataPin Property") +struct FFlowNamedDataPinOutputProperty +{ + GENERATED_BODY() + +public: + + // Name of this instanced property + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + FName Name; + + // DataPinProperty payload + UPROPERTY(EditAnywhere, Category = DataPins, meta = (ExcludeBaseStruct, NoClear)) + TInstancedStruct DataPinProperty; + +public: + + FFlowNamedDataPinOutputProperty() { } + + bool IsValid() const { return Name != NAME_None && DataPinProperty.GetPtr() != nullptr; } + +#if WITH_EDITOR + FFlowPin CreateFlowPin() const { return FFlowDataPinProperty::CreateFlowPin(Name, DataPinProperty); } + + FLOW_API FText BuildHeaderText() const; +#endif // WITH_EDITOR +}; + +// Wrapper-structs for a blueprint defaulted input pin types +// "Hidden" to keep them out of the TInstancedStruct selection list (but they can still be authored as properties in blueprint) +// "DefaultForInputFlowPin" to change them to an Defaulted-Input property (rather than an output property) + +USTRUCT(BlueprintType, DisplayName = "Bool - Input Flow Data Pin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "Bool")) +struct FFlowDataPinInputProperty_Bool : public FFlowDataPinOutputProperty_Bool +{ + GENERATED_BODY() + + FFlowDataPinInputProperty_Bool(bool InValue = false) : Super(InValue) { } +}; + +USTRUCT(BlueprintType, DisplayName = "Int64 - Input Flow Data Pin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "Int")) +struct FFlowDataPinInputProperty_Int64 : public FFlowDataPinOutputProperty_Int64 +{ + GENERATED_BODY() + + FFlowDataPinInputProperty_Int64(int64 InValue = 0) : Super(InValue) { } +}; + +USTRUCT(BlueprintType, DisplayName = "Int - Input Flow Data Pin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "Int")) +struct FFlowDataPinInputProperty_Int32 : public FFlowDataPinOutputProperty_Int32 +{ + GENERATED_BODY() + + FFlowDataPinInputProperty_Int32(int32 InValue = 0) : Super(InValue) { } +}; + +USTRUCT(BlueprintType, DisplayName = "Double (float64) - Input Flow Data Pin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "Float")) +struct FFlowDataPinInputProperty_Double : public FFlowDataPinOutputProperty_Double +{ + GENERATED_BODY() + + FFlowDataPinInputProperty_Double(double InValue = 0.0) : Super(InValue) { } +}; + +USTRUCT(BlueprintType, DisplayName = "Float - Input Flow Data Pin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "Float")) +struct FFlowDataPinInputProperty_Float : public FFlowDataPinOutputProperty_Float +{ + GENERATED_BODY() + + FFlowDataPinInputProperty_Float(float InValue = 0.0f) : Super(InValue) { } +}; + +USTRUCT(BlueprintType, DisplayName = "Name - Input Flow Data Pin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "Name")) +struct FFlowDataPinInputProperty_Name : public FFlowDataPinOutputProperty_Name +{ + GENERATED_BODY() + + FFlowDataPinInputProperty_Name() : Super() { } + FFlowDataPinInputProperty_Name(const FName& InValue) : Super(InValue) { } +}; + +USTRUCT(BlueprintType, DisplayName = "String - Input Flow Data Pin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "String")) +struct FFlowDataPinInputProperty_String : public FFlowDataPinOutputProperty_String +{ + GENERATED_BODY() + + FFlowDataPinInputProperty_String() : Super() { } + FFlowDataPinInputProperty_String(const FString& InValue) : Super(InValue) { } +}; + +USTRUCT(BlueprintType, DisplayName = "Text - Input Flow Data Pin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "Text")) +struct FFlowDataPinInputProperty_Text : public FFlowDataPinOutputProperty_Text +{ + GENERATED_BODY() + + FFlowDataPinInputProperty_Text() : Super() { } + FFlowDataPinInputProperty_Text(const FText& InValue) : Super(InValue) { } +}; + +USTRUCT(BlueprintType, DisplayName = "Enum - Input Flow Data Pin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "Enum")) +struct FFlowDataPinInputProperty_Enum : public FFlowDataPinOutputProperty_Enum +{ + GENERATED_BODY() + + FFlowDataPinInputProperty_Enum() : Super() { } + FFlowDataPinInputProperty_Enum(const FName& InValue, UEnum* InEnumClass) : Super(InValue, InEnumClass) { } +}; + +USTRUCT(BlueprintType, DisplayName = "Vector - Input Flow Data Pin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "Vector")) +struct FFlowDataPinInputProperty_Vector : public FFlowDataPinOutputProperty_Vector +{ + GENERATED_BODY() + + FFlowDataPinInputProperty_Vector() : Super() { } + FFlowDataPinInputProperty_Vector(const FVector& InValue) : Super(InValue) { } +}; + +USTRUCT(BlueprintType, DisplayName = "Rotator - Input Flow Data Pin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "Rotator")) +struct FFlowDataPinInputProperty_Rotator : public FFlowDataPinOutputProperty_Rotator +{ + GENERATED_BODY() + + FFlowDataPinInputProperty_Rotator() : Super() { } + FFlowDataPinInputProperty_Rotator(const FRotator& InValue) : Super(InValue) { } +}; + +USTRUCT(BlueprintType, DisplayName = "Transform - Input Flow Data Pin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "Transform")) +struct FFlowDataPinInputProperty_Transform : public FFlowDataPinOutputProperty_Transform +{ + GENERATED_BODY() + + FFlowDataPinInputProperty_Transform() : Super() { } + FFlowDataPinInputProperty_Transform(const FTransform& InValue) : Super(InValue) { } +}; + +USTRUCT(BlueprintType, DisplayName = "GameplayTag - Input Flow Data Pin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "GameplayTag")) +struct FFlowDataPinInputProperty_GameplayTag : public FFlowDataPinOutputProperty_GameplayTag +{ + GENERATED_BODY() + + FFlowDataPinInputProperty_GameplayTag() : Super() { } + FFlowDataPinInputProperty_GameplayTag(const FGameplayTag& InValue) : Super(InValue) { } +}; + +USTRUCT(BlueprintType, DisplayName = "GameplayTagContainer - Input Flow DataPin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "GameplayTagContainer")) +struct FFlowDataPinInputProperty_GameplayTagContainer : public FFlowDataPinOutputProperty_GameplayTagContainer +{ + GENERATED_BODY() + + FFlowDataPinInputProperty_GameplayTagContainer() : Super() { } + FFlowDataPinInputProperty_GameplayTagContainer(const FGameplayTagContainer& InValue) : Super(InValue) { } +}; + +USTRUCT(BlueprintType, DisplayName = "InstancedStruct - Input Flow DataPin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "InstancedStruct")) +struct FFlowDataPinInputProperty_InstancedStruct : public FFlowDataPinOutputProperty_InstancedStruct +{ + GENERATED_BODY() + + FFlowDataPinInputProperty_InstancedStruct() : Super() { } + FFlowDataPinInputProperty_InstancedStruct(const FInstancedStruct& InValue) : Super(InValue) { } +}; + +USTRUCT(BlueprintType, DisplayName = "Object - Input Flow DataPin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "Object")) +struct FFlowDataPinInputProperty_Object : public FFlowDataPinOutputProperty_Object +{ + GENERATED_BODY() + + FFlowDataPinInputProperty_Object() : Super() { } + FFlowDataPinInputProperty_Object(UObject* InValue, UClass* InClassFilter) : Super(InValue, InClassFilter) { } +}; + +USTRUCT(BlueprintType, DisplayName = "Class - Input Flow DataPin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "Class")) +struct FFlowDataPinInputProperty_Class : public FFlowDataPinOutputProperty_Class +{ + GENERATED_BODY() + + FFlowDataPinInputProperty_Class() : Super() { } + FFlowDataPinInputProperty_Class(const FSoftClassPath& InValue, UClass* InClassFilter) : Super(InValue, InClassFilter) { } +}; + diff --git a/Source/Flow/Public/Types/FlowDataPinResults.h b/Source/Flow/Public/Types/FlowDataPinResults.h new file mode 100644 index 000000000..ac077fb7a --- /dev/null +++ b/Source/Flow/Public/Types/FlowDataPinResults.h @@ -0,0 +1,392 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Types/FlowPinEnums.h" + +#include "GameplayTagContainer.h" + +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 5 +#include "StructUtils/InstancedStruct.h" +#else +#include "InstancedStruct.h" +#endif + +#include "FlowDataPinResults.generated.h" + +struct FInstancedStruct; +struct FFlowDataPinOutputProperty_Object; +struct FFlowDataPinOutputProperty_Class; + +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result") +struct FFlowDataPinResult +{ + GENERATED_BODY() + +public: + + // Result for the DataPin resolve attempt + UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + EFlowDataPinResolveResult Result = EFlowDataPinResolveResult::FailedUnimplemented; + +public: + FLOW_API FFlowDataPinResult() { } + FLOW_API FFlowDataPinResult(EFlowDataPinResolveResult InResult) : Result(InResult) { } +}; + +// Recommend implementing FFlowDataPinResult... for every EFlowPinType +FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); + +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Bool)") +struct FFlowDataPinResult_Bool : public FFlowDataPinResult +{ + GENERATED_BODY() + +public: + + UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + bool Value = false; + +public: + + FLOW_API FFlowDataPinResult_Bool() { } + FLOW_API FFlowDataPinResult_Bool(bool InValue) + : Super(EFlowDataPinResolveResult::Success) + , Value(InValue) + { } +}; + +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Int)") +struct FFlowDataPinResult_Int : public FFlowDataPinResult +{ + GENERATED_BODY() + +public: + + UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + int64 Value = 0; + +public: + + FLOW_API FFlowDataPinResult_Int() { } + FLOW_API FFlowDataPinResult_Int(int64 InValue) + : Super(EFlowDataPinResolveResult::Success) + , Value(InValue) + { } +}; + +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Float)") +struct FFlowDataPinResult_Float : public FFlowDataPinResult +{ + GENERATED_BODY() + +public: + + UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + double Value = 0; + +public: + + FLOW_API FFlowDataPinResult_Float() { } + FLOW_API FFlowDataPinResult_Float(double InValue) + : Super(EFlowDataPinResolveResult::Success) + , Value(InValue) + { } +}; + +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Name)") +struct FFlowDataPinResult_Name : public FFlowDataPinResult +{ + GENERATED_BODY() + +public: + + UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + FName Value = NAME_None; + +public: + + FLOW_API FFlowDataPinResult_Name() { } + FLOW_API FFlowDataPinResult_Name(const FName& InValue) + : Super(EFlowDataPinResolveResult::Success) + , Value(InValue) + { } + + FLOW_API void SetValue(const FName& FromName) { Value = FromName; } + FLOW_API void SetValue(const FString& FromString) { Value = FName(FromString); } + FLOW_API void SetValue(const FText& FromText) { Value = FName(FromText.ToString()); } +}; + +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (String)") +struct FFlowDataPinResult_String : public FFlowDataPinResult +{ + GENERATED_BODY() + +public: + + UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + FString Value; + +public: + + FLOW_API FFlowDataPinResult_String() { } + FLOW_API FFlowDataPinResult_String(const FString& InValue) + : Super(EFlowDataPinResolveResult::Success) + , Value(InValue) + { } + + FLOW_API void SetValue(const FName& FromName) { Value = FromName.ToString(); } + FLOW_API void SetValue(const FString& FromString) { Value = FromString; } + FLOW_API void SetValue(const FText& FromText) { Value = FromText.ToString(); } +}; + +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Text)") +struct FFlowDataPinResult_Text : public FFlowDataPinResult +{ + GENERATED_BODY() + +public: + + UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + FText Value; + +public: + + FLOW_API FFlowDataPinResult_Text() { } + FLOW_API FFlowDataPinResult_Text(const FText& InValue) + : Super(EFlowDataPinResolveResult::Success) + , Value(InValue) + { } + + FLOW_API void SetValue(const FName& FromName) { Value = FText::FromName(FromName); } + FLOW_API void SetValue(const FString& FromString) { Value = FText::FromString(FromString); } + FLOW_API void SetValue(const FText& FromText) { Value = FromText; } +}; + +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Enum)") +struct FFlowDataPinResult_Enum : public FFlowDataPinResult +{ + GENERATED_BODY() + +public: + + // The selected enum Value + UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + FName Value = NAME_None; + + // Class for this enum + UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + UEnum* EnumClass = nullptr; + +public: + + FLOW_API FFlowDataPinResult_Enum() { } + FLOW_API FFlowDataPinResult_Enum(const FName& InValue, UEnum* InEnumClass) + : Super(EFlowDataPinResolveResult::Success) + , Value(InValue) + , EnumClass(InEnumClass) + { } + + template + static FFlowDataPinResult_Enum BuildResultFromNativeEnumValue(TUnrealNativeEnumType EnumValue) + { + FFlowDataPinResult_Enum Result; + Result.SetFromNativeEnumValue(EnumValue); + + return Result; + } + + template + void SetFromNativeEnumValue(TUnrealNativeEnumType InEnumValue) + { + EnumClass = StaticEnum(); + const FText DisplayValueText = EnumClass->GetDisplayValueAsText(InEnumValue); + const FName EnumValue = FName(DisplayValueText.ToString()); + + Value = EnumValue; + Result = EFlowDataPinResolveResult::Success; + } + + template + TUnrealNativeEnumType GetNativeEnumValue(EGetByNameFlags GetByNameFlags = EGetByNameFlags::None) const + { + if (!IsValid(EnumClass)) + { + return InvalidValue; + } + + int64 ValueAsInt = EnumClass->GetValueByName(Value, GetByNameFlags); + if (ValueAsInt == INDEX_NONE) + { + return InvalidValue; + } + + return static_cast(ValueAsInt); + } +}; + +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Vector)") +struct FFlowDataPinResult_Vector : public FFlowDataPinResult +{ + GENERATED_BODY() + +public: + + UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + FVector Value; + +public: + + FLOW_API FFlowDataPinResult_Vector() { } + FLOW_API FFlowDataPinResult_Vector(const FVector& InValue) + : Super(EFlowDataPinResolveResult::Success) + , Value(InValue) + { } +}; + +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Rotator)") +struct FFlowDataPinResult_Rotator : public FFlowDataPinResult +{ + GENERATED_BODY() + +public: + + UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + FRotator Value; + +public: + + FLOW_API FFlowDataPinResult_Rotator() { } + FLOW_API FFlowDataPinResult_Rotator(const FRotator& InValue) + : Super(EFlowDataPinResolveResult::Success) + , Value(InValue) + { } +}; + +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Transform)") +struct FFlowDataPinResult_Transform : public FFlowDataPinResult +{ + GENERATED_BODY() + +public: + + UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + FTransform Value; + +public: + + FLOW_API FFlowDataPinResult_Transform() { } + FLOW_API FFlowDataPinResult_Transform(const FTransform& InValue) + : Super(EFlowDataPinResolveResult::Success) + , Value(InValue) + { } +}; + +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (GameplayTag)") +struct FFlowDataPinResult_GameplayTag : public FFlowDataPinResult +{ + GENERATED_BODY() + +public: + + UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + FGameplayTag Value; + +public: + + FLOW_API FFlowDataPinResult_GameplayTag() { } + FLOW_API FFlowDataPinResult_GameplayTag(const FGameplayTag& InValue) + : Super(EFlowDataPinResolveResult::Success) + , Value(InValue) + { } +}; + +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (GameplayTagContainer)") +struct FFlowDataPinResult_GameplayTagContainer : public FFlowDataPinResult +{ + GENERATED_BODY() + +public: + + UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + FGameplayTagContainer Value; + +public: + + FLOW_API FFlowDataPinResult_GameplayTagContainer() { } + FLOW_API FFlowDataPinResult_GameplayTagContainer(const FGameplayTagContainer& InValue) + : Super(EFlowDataPinResolveResult::Success) + , Value(InValue) + { } +}; + +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (InstancedStruct)") +struct FFlowDataPinResult_InstancedStruct : public FFlowDataPinResult +{ + GENERATED_BODY() + +public: + + UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + FInstancedStruct Value; + +public: + + FLOW_API FFlowDataPinResult_InstancedStruct() { } + FLOW_API FFlowDataPinResult_InstancedStruct(const FInstancedStruct& InValue) + : Super(EFlowDataPinResolveResult::Success) + , Value(InValue) + { } +}; + +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Object)") +struct FFlowDataPinResult_Object : public FFlowDataPinResult +{ + GENERATED_BODY() + +public: + + UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + TObjectPtr Value; + +public: + + FLOW_API FFlowDataPinResult_Object() { } + FLOW_API FFlowDataPinResult_Object(UObject* InValue); + + FLOW_API void SetValueFromPropertyWrapper(const FFlowDataPinOutputProperty_Object& InPropertyWrapper); + FLOW_API FORCEINLINE void SetValueFromSoftPath(const FSoftObjectPath& SoftPath) { Value = SoftPath.ResolveObject(); } + FLOW_API FORCEINLINE void SetValueFromObjectPtr(UObject* ObjectPtr) { Value = ObjectPtr; } +}; + +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Class)") +struct FFlowDataPinResult_Class : public FFlowDataPinResult +{ + GENERATED_BODY() + +protected: + + // SoftClassPath version of the result + // (both the SoftClassPath and the UClass (if available) will be set for the result) + UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + FSoftClassPath ValuePath; + + // UClass version of the result + // (both the SoftClassPath and the UClass (if available) will be set for the result) + UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + TObjectPtr ValueClass = nullptr; + +public: + + FLOW_API FFlowDataPinResult_Class() { } + FLOW_API FFlowDataPinResult_Class(const FSoftClassPath& InValuePath); + FLOW_API FFlowDataPinResult_Class(UClass* InValueClass); + + FLOW_API void SetValueFromPropertyWrapper(const FFlowDataPinOutputProperty_Class& PropertyWrapper); + FLOW_API void SetValueSoftClassAndClassPtr(const FSoftClassPath& SoftPath, UClass* ObjectPtr); + FLOW_API void SetValueFromSoftPath(const FSoftObjectPath& SoftObjectPath); + FLOW_API FORCEINLINE void SetValueFromObjectPtr(UClass* ClassPtr) { SetValueSoftClassAndClassPtr(FSoftClassPath(ClassPtr), ClassPtr); } + + FLOW_API UClass* GetOrResolveClass() const { return IsValid(ValueClass) ? ValueClass.Get() : ValuePath.ResolveClass(); } + FLOW_API FSoftClassPath GetAsSoftClass() const; +}; diff --git a/Source/Flow/Public/Types/FlowEnumUtils.h b/Source/Flow/Public/Types/FlowEnumUtils.h new file mode 100644 index 000000000..079a6f1a8 --- /dev/null +++ b/Source/Flow/Public/Types/FlowEnumUtils.h @@ -0,0 +1,129 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Misc/EnumRange.h" + +#include + +// Extensions to EnumRange.h + +namespace FlowEnum +{ + template constexpr auto MinOf() { return 0; } + template constexpr auto MaxOf() { return 0; } + + // NOTE (gtaylor) In this context, a "Valid" enum value is one that is within ::Min to ::Max - 1 + // Invalid values (like ::Invalid) should fall outside of this range + template constexpr bool IsValidEnumValue(const TEnum EnumValue) { return false; } + + // NOTE (gtaylor) In this context, a subrange is First..Last (where last is an inclusive bound) + template constexpr bool IsEnumValueInSubrange(const TEnum EnumValue, const TEnum SubrangeFirst, const TEnum SubrangeLast) { return false; } + + // Utility templates for Enums + template ::type> + struct safe_underlying_type { + using type = void; + }; + + template + struct safe_underlying_type { + using type = std::underlying_type_t; + }; + + template + struct safe_underlying_type { + using type = T; + }; + + template + constexpr std::underlying_type_t ToUnderlyingType(const EnumType Value) + { + return static_cast>(Value); + } +} +#define FLOW_ENUM_STATIC_CAST_TO_INT(EnumType) \ + namespace FlowEnum { constexpr auto ToInt(EnumType EnumValue) { return static_cast::type>(EnumValue); } } + +#define FLOW_ENUM_STATIC_CAST_MIN_AND_MAX(EnumType, MinValue, MaxValue) \ + namespace FlowEnum { \ + template<> constexpr auto MinOf() { return static_cast::type>(MinValue); } \ + template<> constexpr auto MaxOf() { return static_cast::type>(MaxValue); } \ + } +#define FLOW_ENUM_RANGE_UTILITY_FUNCTIONS(EnumType) \ + namespace FlowEnum { \ + template<> constexpr bool IsEnumValueInSubrange(const EnumType EnumValue, const EnumType SubrangeFirst, const EnumType SubrangeLast) { return ToInt(EnumValue) >= ToInt(SubrangeFirst) && ToInt(EnumValue) <= ToInt(SubrangeLast); } \ + template<> constexpr bool IsValidEnumValue(const EnumType EnumValue) { return ToInt(EnumValue) >= MinOf() && ToInt(EnumValue) < MaxOf(); } \ + } +#define FLOW_IS_ENUM_IN_SUBRANGE(EnumValue, SubrangeTag) FlowEnum::IsEnumValueInSubrange(EnumValue, SubrangeTag##First, SubrangeTag##Last) + +// Macros to static-assert the max or a particular value of an enum is the expected integral value +#define FLOW_ASSERT_ENUM_MAX(EnumType, IntMaxValue) static_assert(FlowEnum::MaxOf() == IntMaxValue, "Ensure this code is correct after making changes to this enum.") +#define FLOW_ASSERT_ENUM_VALUE(EnumValue, IntValue) static_assert(FlowEnum::ToInt(EnumValue) == IntValue, "Ensure this code is correct after making changes to this enum.") + +/** +* Version of ENUM_RANGE_VALUES for 'C-style' enums +* +* Defines a contiguous enum range from MyEnum_Min to (MyEnum_Max - 1) +* +* Example: +* +* enum EMyEnum +* { +* MyEnum_First, +* MyEnum_Second, +* MyEnum_Third, +* +* MyEnum_Max, +* MyEnum_Invalid = -1, +* MyEnum_Min = 0, +* }; +* +* // Defines iteration over EMyEnum to be: First, Second, Third +* ENUM_RANGE_VALUES_WITH_MIN_AND_MAX(EMyEnum, MyEnum_Min, MyEnum_Max) +*/ +#define FLOW_ENUM_RANGE_VALUES_WITH_MIN_AND_MAX(EnumType, EnumMin, EnumMax) \ + ENUM_RANGE_BY_FIRST_AND_LAST(EnumType, static_cast(EnumMin), static_cast(EnumMax) - 1) \ + FLOW_ENUM_STATIC_CAST_MIN_AND_MAX(EnumType, EnumMin, EnumMax) \ + FLOW_ENUM_STATIC_CAST_TO_INT(EnumType) \ + FLOW_ENUM_RANGE_UTILITY_FUNCTIONS(EnumType) + +/** +* Defines a contiguous enum range from Min to (Max - 1) +* +* Examples: +* +* for unsigned int: +* +* enum class EMyEnum : uint32 +* { +* First, +* Second, +* Third, +* +* Max, +* Invalid, +* Min = 0, +* }; +* +* or with signed int: +* +* enum class EMyEnum : int32 +* { +* First, +* Second, +* Third, +* +* Max, +* Invalid = -1, +* Min = 0, +* }; +* +* // Defines iteration over EMyEnum to be: First, Second, Third +* ENUM_RANGE_VALUES(EMyEnum) +*/ +#define FLOW_ENUM_RANGE_VALUES(EnumType) \ + ENUM_RANGE_BY_FIRST_AND_LAST(EnumType, static_cast(EnumType::Min), static_cast(EnumType::Max) - 1) \ + FLOW_ENUM_STATIC_CAST_MIN_AND_MAX(EnumType, EnumType::Min, EnumType::Max) \ + FLOW_ENUM_STATIC_CAST_TO_INT(EnumType) \ + FLOW_ENUM_RANGE_UTILITY_FUNCTIONS(EnumType) diff --git a/Source/Flow/Public/Types/FlowGameplayTagMapUtils.h b/Source/Flow/Public/Types/FlowGameplayTagMapUtils.h new file mode 100644 index 000000000..445e4d511 --- /dev/null +++ b/Source/Flow/Public/Types/FlowGameplayTagMapUtils.h @@ -0,0 +1,190 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "GameplayTagContainer.h" +#include "GameplayTagsManager.h" + +#include "Types/FlowEnumUtils.h" +#include "Types/FlowArray.h" + +// NOTE (gtaylor) The choice of which EFlowGameplayTagMapExpandPolicy to use will be informed by the map's tolerance +// for memory vs. lookup performance. If speed is not a concern, then fully expanding with AllSubtags can +// make for a single-tech lookup. If memory is more of a concern, then NoExpand will store the minimal information +// in the map keys (potentially requiring multiple parent searches in TryLookupGameplayTagKey). If only the leaf tags +// will be used for lookup, then LeafSubtags expansion policy is a good option. + +UENUM() +enum class EFlowGameplayTagMapExpandPolicy : int8 +{ + AllSubtags, // Apply the payload to all of the tag's child tags + LeafSubtags, // Apply the payload to the tag's leaf child tags + RemoveSubtags, // Remove all of the keys in the result map of the tag's child tags + NoExpand, // Only apply the payload patch to the tag, make no changes to it's child tags + + Max UMETA(Hidden), + Invalid = -1 UMETA(Hidden), + Min = 0 UMETA(Hidden), +}; +FLOW_ENUM_RANGE_VALUES(EFlowGameplayTagMapExpandPolicy); + +namespace FlowMap +{ + // Utility functions for utilizing FGameplayTags as a key in a TMap. + // Expected to be wrapped by the client code to hide some of the details in these function signatures. + + template + void PatchGameplayTagMap( + const TMap& PatchSourceMap, + TMap& InOutPatchedMap) + { + checkf( + &PatchSourceMap != &InOutPatchedMap, + TEXT("We could make this case work, but it would require a temp caching anyway, so letting the caller make the copy if they want to expand in-line")); + + FlowArray::TInlineArray PatchMapKeys; + PatchSourceMap.GenerateKeyArray(PatchMapKeys); + + FLOW_ASSERT_ENUM_MAX(EFlowGameplayTagMapExpandPolicy, 4); + + constexpr bool bProcessSubtags = (ExpandPolicy != EFlowGameplayTagMapExpandPolicy::NoExpand); + + // Only sort the keys if we will be processing the subtags + + if constexpr (bProcessSubtags) + { + PatchMapKeys.StableSort([](const FGameplayTag& Tag0, const FGameplayTag& Tag1) + { + // Sort the keys to apply in order from least specific to most specific + + return Tag0.GetGameplayTagParents().Num() < Tag1.GetGameplayTagParents().Num(); + }); + } + + for (const FGameplayTag& PatchKeyTag : PatchMapKeys) + { + const TPayload& PatchPayload = PatchSourceMap.FindChecked(PatchKeyTag); + + // First, patch the payload in the target map + + InOutPatchedMap.Add(PatchKeyTag, PatchPayload); + + // Now apply the payload to child tag keys in the map + + if constexpr (bProcessSubtags) + { + const UGameplayTagsManager& TagsManager = UGameplayTagsManager::Get(); + const FGameplayTagContainer TagAndChildrenContainer = TagsManager.RequestGameplayTagChildren(PatchKeyTag); + + for (auto ChildTagIt = TagAndChildrenContainer.CreateConstIterator(); ChildTagIt; ++ChildTagIt) + { + const FGameplayTag& ChildTag = *ChildTagIt; + + if constexpr (ExpandPolicy == EFlowGameplayTagMapExpandPolicy::AllSubtags) + { + // Replace all child tag entries (if any) with the patch source tag's payload + InOutPatchedMap.Add(ChildTag, PatchPayload); + } + + if constexpr (ExpandPolicy == EFlowGameplayTagMapExpandPolicy::LeafSubtags) + { + // BB (gtaylor) Is there a lighter-weight way to ask if a tag is a leaf tag? + + const FGameplayTagContainer ChildChildTags = TagsManager.RequestGameplayTagChildren(PatchKeyTag); + const bool bIsChildALeafTag = (ChildChildTags.Num() == 0); + + if (bIsChildALeafTag) + { + // Replace only leaf child tag entries (if any) with the patch source tag's payload + InOutPatchedMap.Add(ChildTag, PatchPayload); + } + } + + if constexpr (ExpandPolicy == EFlowGameplayTagMapExpandPolicy::RemoveSubtags) + { + // Remove all subtag mappings in the map + InOutPatchedMap.Remove(ChildTag); + } + } + } + } + } + + // (const) Lookup function, which works on a gameplaytag-keyed map + // it can crawl up the tag ancestry chain to allow general keys to apply to sub-tags + template + const TPayload* TryLookupGameplayTagKey( + const FGameplayTag& KeyTag, + const TMap& GameplayTagToPayloadMap, + const FGameplayTag& KeyTagBase = FGameplayTag::EmptyTag, + int32 ParentTagSearchDepthMax = 0) + { + check(ParentTagSearchDepthMax >= 0); + check( + KeyTagBase == FGameplayTag::EmptyTag || + KeyTag == KeyTagBase || + KeyTag.MatchesTag(KeyTagBase)); + + const TPayload* FoundPayload = GameplayTagToPayloadMap.Find(KeyTag); + + if (!FoundPayload && + ParentTagSearchDepthMax > 0 && + KeyTag != KeyTagBase) + { + // Recurse to direct parent tag, decrementing the allowed search depth + + const FGameplayTag DirectParentTag = KeyTag.RequestDirectParent(); + const int32 NewParentTagSearchDepthMax = ParentTagSearchDepthMax - 1; + + return TryLookupGameplayTagKey(DirectParentTag, GameplayTagToPayloadMap, KeyTagBase, NewParentTagSearchDepthMax); + } + + return FoundPayload; + } + + // (mutable) Lookup function, which works on a gameplaytag-keyed map + // it can crawl up the tag ancestry chain to allow general keys to apply to sub-tags + template + TPayload* TryLookupGameplayTagKey( + const FGameplayTag& KeyTag, + TMap& GameplayTagToPayloadMap, + const FGameplayTag& KeyTagBase = FGameplayTag::EmptyTag, + int32 ParentTagSearchDepthMax = 0) + { + // Non-const map signature uses the same lookup code as the const version + + return + const_cast( + TryLookupGameplayTagKey( + KeyTag, + *const_cast*>(&GameplayTagToPayloadMap), + KeyTagBase, + ParentTagSearchDepthMax)); + } + + // Extracts the key/value pairs from a gameplaytag-keyed map into a sorted array + template + TArray> BuildSortedGameplayTagMapPairs(const TMap& GameplayTagToPayloadMap) + { + FlowArray::TInlineArray MapKeys; + GameplayTagToPayloadMap.GenerateKeyArray(MapKeys); + + MapKeys.StableSort([](const FGameplayTag& Tag0, const FGameplayTag& Tag1) + { + return Tag0.GetGameplayTagParents().Num() < Tag1.GetGameplayTagParents().Num(); + }); + + TArray> Pairs; + Pairs.Reserve(MapKeys.Num()); + + for (const FGameplayTag& KeyTag : MapKeys) + { + const TPayload& Payload = GameplayTagToPayloadMap.FindChecked(KeyTag); + + Pairs.Emplace(KeyTag, Payload); + } + + return Pairs; + } + +} diff --git a/Source/Flow/Public/Types/FlowPinEnums.h b/Source/Flow/Public/Types/FlowPinEnums.h new file mode 100644 index 000000000..5035013cd --- /dev/null +++ b/Source/Flow/Public/Types/FlowPinEnums.h @@ -0,0 +1,95 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Types/FlowEnumUtils.h" + +#include "FlowPinEnums.generated.h" + +UENUM(BlueprintType) +enum class EFlowPinType : uint8 +{ + // Execution pin + Exec, + + // FBoolProperty + Bool, + + // FByteProperty FInt16Property FIntProperty FInt64Property FUInt16Property FUInt32Property FUInt64Property + Int, + + // FFloatProperty, FDoubleProperty + Float, + + // FNameProperty + Name, + + // FStringProperty + String, + + // FTextProperty + Text, + + // FEnumProperty, FByteProperty + Enum, + + // FVector (FStructProperty) + Vector, + + // FRotator (FStructProperty) + Rotator, + + // FTransform (FStructProperty) + Transform, + + // FGameplayTag (FStructProperty) + GameplayTag, + + // FGameplayTagContainer (FStructProperty) + GameplayTagContainer, + + // FInstancedStruct (FStructProperty) + InstancedStruct, + + // FObjectProperty, FObjectPtrProperty, FWeakObjectProperty, FLazyObjectProperty, FSoftObjectProperty + Object, + + // FClassProperty, FClassPtrProperty, FSoftClassProperty + Class, + + Max UMETA(Hidden), + Invalid UMETA(Hidden), + Min = 0 UMETA(Hidden), +}; +FLOW_ENUM_RANGE_VALUES(EFlowPinType) + +// Result enum for TryResolveDataPinAs...() functions +UENUM(BlueprintType) +enum class EFlowDataPinResolveResult : uint8 +{ + // Pin resolved successfully + Success, + + // The pin is not connected to another pin + FailedUnconnected, + + // The pin name is unknown + FailedUnknownPin, + + // The pin was requested as an unsupported type + FailedMismatchedType, + + // The Flow Node or AddOn did not implement the necessary function to provide this value + FailedUnimplemented, + + // Failed due to missing pin (may just need re-save for the asset) + FailedMissingPin, + + // Failed with an error message (see the error log) + FailedWithError, + + Max UMETA(Hidden), + Invalid UMETA(Hidden), + Min = 0 UMETA(Hidden), +}; +FLOW_ENUM_RANGE_VALUES(EFlowDataPinResolveResult) diff --git a/Source/FlowEditor/FlowEditor.Build.cs b/Source/FlowEditor/FlowEditor.Build.cs index 13a7e48a4..79e348717 100644 --- a/Source/FlowEditor/FlowEditor.Build.cs +++ b/Source/FlowEditor/FlowEditor.Build.cs @@ -6,7 +6,7 @@ public class FlowEditor : ModuleRules { public FlowEditor(ReadOnlyTargetRules target) : base(target) { - if(CppStandard is null || CppStandard != CppStandardVersion.Cpp20) + if (CppStandard is null || CppStandard != CppStandardVersion.Cpp20) { CppStandard = CppStandardVersion.Cpp20; } @@ -38,6 +38,7 @@ public FlowEditor(ReadOnlyTargetRules target) : base(target) "EditorStyle", "Engine", "GraphEditor", + "GameplayTags", "InputCore", "Json", "JsonUtilities", @@ -56,6 +57,7 @@ public FlowEditor(ReadOnlyTargetRules target) : base(target) "Slate", "SlateCore", "SourceControl", + "StructUtils", "ToolMenus", "UnrealEd" }); diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index 8156dfcbf..70fb8f84e 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -455,7 +455,11 @@ void FFlowAssetEditor::ValidateAsset_Internal() void FFlowAssetEditor::ValidateAsset(FFlowMessageLog& MessageLog) { - FlowAsset->ValidateAsset(MessageLog); + UFlowGraph* FlowGraph = Cast(FlowAsset->GetGraph()); + if (FlowGraph) + { + FlowGraph->ValidateAsset(MessageLog); + } } void FFlowAssetEditor::SearchInAsset() diff --git a/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp b/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp index 2c16ab486..0df59b143 100644 --- a/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp +++ b/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp @@ -27,7 +27,7 @@ namespace FlowGraphNode = Cast(Difference.Result.Node2); } - if (IsValid(FlowGraphNode)) + if (IsValid(FlowGraphNode) && IsValid(FlowGraphNode->GetNodeTemplate())) { return FString::FromInt(FlowGraphNode->GetNodeTemplate()->GetUniqueID());//->GetName(); } @@ -58,7 +58,7 @@ namespace FlowGraphNode = Cast(Difference.Result.Node2); } - if (IsValid(FlowGraphNode)) + if (IsValid(FlowGraphNode) && IsValid(FlowGraphNode->GetNodeTemplate())) { return FlowGraphNode->GetNodeTemplate()->GetName(); } @@ -268,9 +268,11 @@ void FFlowGraphToDiff::GenerateTreeEntries(TArrayDiffTreeEntry); } - //generate property diffs. + //find and generate property diffs. TArray PropertyDiffsArray; FlowNodeDiff->DiffProperties(PropertyDiffsArray); + + //generate property diff tree entries. for (const auto& PropertyDiffEntry : PropertyDiffsArray) { check(FlowNodeDiff.IsValid()); diff --git a/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp b/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp index 8b80b207f..54b54b700 100644 --- a/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp +++ b/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp @@ -1,4 +1,4 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "Asset/FlowObjectDiff.h" diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinPropertyCustomizationBase.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinPropertyCustomizationBase.cpp new file mode 100644 index 000000000..e9e998a66 --- /dev/null +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinPropertyCustomizationBase.cpp @@ -0,0 +1,32 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "DetailCustomizations/FlowDataPinPropertyCustomizationBase.h" +#include "DetailWidgetRow.h" + +void FFlowDataPinPropertyCustomizationBase::CustomizeHeader(TSharedRef InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) +{ + uint32 NumChildren = 0; + InStructPropertyHandle->GetNumChildren(NumChildren); + + for (uint32 ChildNum = 0; ChildNum < NumChildren; ++ChildNum) + { + TSharedPtr ChildPtr = InStructPropertyHandle->GetChildHandle(ChildNum); + + HeaderRow.NameContent() + [ + InStructPropertyHandle->CreatePropertyNameWidget() + ]; + HeaderRow.ValueContent() + [ + ChildPtr->CreatePropertyValueWidget() + ]; + + // Use the 0th child's Value Widget to replace the Header row's Value Widget + break; + } +} + +void FFlowDataPinPropertyCustomizationBase::CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) +{ + // Do not display any children +} diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinProperty_ClassCustomization.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinProperty_ClassCustomization.cpp new file mode 100644 index 000000000..13212f888 --- /dev/null +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinProperty_ClassCustomization.cpp @@ -0,0 +1,179 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "DetailCustomizations/FlowDataPinProperty_ClassCustomization.h" +#include "DetailWidgetRow.h" +#include "Types/FlowDataPinProperties.h" +#include "EditorClassUtils.h" +#include "PropertyCustomizationHelpers.h" +#include "IDetailChildrenBuilder.h" + +void FFlowDataPinProperty_ClassCustomizationBase::CustomizeHeader(TSharedRef InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) +{ + StructPropertyHandle = InStructPropertyHandle; + + // Based on SoftClassPtr Customization + + const FString& MustImplementName = StructPropertyHandle->GetMetaData("MustImplement"); + const bool bAllowAbstract = StructPropertyHandle->HasMetaData("AllowAbstract"); + const bool bIsBlueprintBaseOnly = StructPropertyHandle->HasMetaData("IsBlueprintBaseOnly") || StructPropertyHandle->HasMetaData("BlueprintBaseOnly"); + const bool bAllowNone = !(StructPropertyHandle->GetMetaDataProperty()->PropertyFlags & CPF_NoClear); + const bool bShowTreeView = StructPropertyHandle->HasMetaData("ShowTreeView"); + const bool bHideViewOptions = StructPropertyHandle->HasMetaData("HideViewOptions"); + const bool bShowDisplayNames = StructPropertyHandle->HasMetaData("ShowDisplayNames"); + + CachedMetaClassPtr = DeriveBestClassFilter(); + + TrySetClassFilterFromMetaData(); + + const UClass* const RequiredInterface = FEditorClassUtils::GetClassFromString(MustImplementName); + + HeaderRow + .NameContent() + [ + InStructPropertyHandle->CreatePropertyNameWidget() + ] + .ValueContent() + .MinDesiredWidth(250.0f) + .MaxDesiredWidth(0.0f) + [ + // Add a class entry box. Even though this isn't an class entry, we will simulate one + SNew(SClassPropertyEntryBox) + .MetaClass(BuildMetaClass()) + .RequiredInterface(RequiredInterface) + .AllowAbstract(bAllowAbstract) + .IsBlueprintBaseOnly(bIsBlueprintBaseOnly) + .AllowNone(bAllowNone) + .ShowTreeView(bShowTreeView) + .HideViewOptions(bHideViewOptions) + .ShowDisplayNames(bShowDisplayNames) + .SelectedClass(this, &FFlowDataPinProperty_ClassCustomizationBase::OnGetClass) + .OnSetClass(this, &FFlowDataPinProperty_ClassCustomizationBase::OnSetClass) + ]; +} + +void FFlowDataPinProperty_ClassCustomizationBase::CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) +{ + if (TSharedPtr ClassFilterHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Class, ClassFilter))) + { + StructBuilder.AddProperty(ClassFilterHandle.ToSharedRef()); + + ClassFilterHandle->SetOnPropertyValueChanged(FSimpleDelegate::CreateSP(this, &FFlowDataPinProperty_ClassCustomizationBase::OnClassFilterChanged)); + } +} + +void FFlowDataPinProperty_ClassCustomizationBase::OnClassFilterChanged() +{ + // We don't allow changing away from the class filter specified in property metadata. + // So potentially undo the change (would be better to make it non-editable if the metadata was set, but I'm not sure how to do that) + TrySetClassFilterFromMetaData(); + + UClass* MetaClass = DeriveBestClassFilter(); + + TSharedPtr ClassValueHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Class, Value)); + UObject* ClassValueAsObject = nullptr; + ClassValueHandle->GetValue(ClassValueAsObject); + UClass* ClassValue = Cast(ClassValueAsObject); + + if (MetaClass && ClassValue && !ClassValue->IsChildOf(MetaClass)) + { + // Clear the class value if it is not compatible with the new ClassFilter value + const UClass* NullClassPtr = nullptr; + ClassValueHandle->SetValue(NullClassPtr, EPropertyValueSetFlags::DefaultFlags); + } + + CachedMetaClassPtr = MetaClass; + + IFlowExtendedPropertyTypeCustomization::OnAnyChildPropertyChanged(); +} + +UClass* FFlowDataPinProperty_ClassCustomizationBase::DeriveBestClassFilter() const +{ + const FProperty* StructProperty = StructPropertyHandle->GetProperty(); + + if (!StructProperty) + { + return nullptr; + } + + if (UClass* MetaClass = FFlowDataPinOutputProperty_Class::TryGetMetaClassFromProperty(*StructProperty)) + { + return MetaClass; + } + + // Allow the Instance to edit the ClassFilter to override the MetaClass + if (TSharedPtr ClassFilterHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Class, ClassFilter))) + { + UObject* ExistingMetaClass = nullptr; + ClassFilterHandle->GetValue(ExistingMetaClass); + + if (ExistingMetaClass) + { + return Cast(ExistingMetaClass); + } + } + + return nullptr; +} + +void FFlowDataPinProperty_ClassCustomizationBase::TrySetClassFilterFromMetaData() +{ + const FString& MetaClassName = StructPropertyHandle->GetMetaData("MetaClass"); + + if (MetaClassName.IsEmpty()) + { + return; + } + + UClass* MetaClass = FEditorClassUtils::GetClassFromString(MetaClassName); + if (!MetaClass) + { + return; + } + + // If the class filter was set in meta data, force that value to the ClassFilter property + if (TSharedPtr ClassFilterHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Class, ClassFilter))) + { + UObject* ExistingMetaClass = nullptr; + ClassFilterHandle->GetValue(ExistingMetaClass); + + if (ExistingMetaClass != MetaClass) + { + ClassFilterHandle->SetValue(MetaClass, EPropertyValueSetFlags::DefaultFlags); + } + } +} + +const UClass* FFlowDataPinProperty_ClassCustomizationBase::OnGetClass() const +{ + FString ClassName; + + if (TSharedPtr ClassValueHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Class, Value))) + { + ClassValueHandle->GetValueAsFormattedString(ClassName); + } + + // Do we have a valid cached class pointer? + const UClass* Class = CachedClassPtr.Get(); + if (!Class || Class->GetPathName() != ClassName) + { + Class = FEditorClassUtils::GetClassFromString(ClassName); + CachedClassPtr = MakeWeakObjectPtr(const_cast(Class)); + } + return Class; +} + +UClass* FFlowDataPinProperty_ClassCustomizationBase::BuildMetaClass() const +{ + return CachedMetaClassPtr.Get(); +} + +void FFlowDataPinProperty_ClassCustomizationBase::OnSetClass(const UClass* NewClass) +{ + if (TSharedPtr ClassValueHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Class, Value))) + { + if (ClassValueHandle->SetValueFromFormattedString((NewClass) ? NewClass->GetPathName() : "None") == FPropertyAccess::Result::Success) + { + CachedClassPtr = MakeWeakObjectPtr(const_cast(NewClass)); + } + } +} diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinProperty_EnumCustomization.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinProperty_EnumCustomization.cpp new file mode 100644 index 000000000..b1b5027c5 --- /dev/null +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinProperty_EnumCustomization.cpp @@ -0,0 +1,109 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "DetailCustomizations/FlowDataPinProperty_EnumCustomization.h" +#include "Types/FlowDataPinProperties.h" +#include "Nodes/FlowPin.h" + +#include "IDetailChildrenBuilder.h" +#include "UObject/UnrealType.h" + +void FFlowDataPinProperty_EnumCustomizationBase::CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) +{ + if (TSharedPtr EnumClassHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Enum, EnumClass))) + { + StructBuilder.AddProperty(EnumClassHandle.ToSharedRef()); + } + + if (TSharedPtr EnumNameHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Enum, EnumName))) + { + StructBuilder.AddProperty(EnumNameHandle.ToSharedRef()); + + EnumNameHandle->SetOnPropertyValueChanged(FSimpleDelegate::CreateSP(this, &FFlowDataPinProperty_EnumCustomizationBase::OnEnumNameChanged)); + } +} + +TSharedPtr FFlowDataPinProperty_EnumCustomizationBase::GetCuratedNamePropertyHandle() const +{ + check(StructPropertyHandle->IsValidHandle()); + + TSharedPtr FoundHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Enum, Value)); + check(FoundHandle); + + return FoundHandle; +} + +TArray FFlowDataPinProperty_EnumCustomizationBase::GetCuratedNameOptions() const +{ + TArray Results; + + const UEnum* Enum = GetEnumClass(); + + if (IsValid(Enum)) + { + Results = GetEnumValues(*Enum); + } + + return Results; +} + +TArray FFlowDataPinProperty_EnumCustomizationBase::GetEnumValues(const UEnum& Enum) +{ + TArray EnumValues; + + for (int Index = 0; Index < Enum.GetMaxEnumValue(); Index++) + { + if (!Enum.IsValidEnumValue(Index)) + { + continue; + } + + static const TCHAR* MetaDataKey_Hidden = TEXT("Hidden"); + if (!Enum.HasMetaData(MetaDataKey_Hidden, Index)) + { + EnumValues.Add(*Enum.GetDisplayNameTextByIndex(Index).ToString()); + } + } + + return EnumValues; +} + +void FFlowDataPinProperty_EnumCustomizationBase::SetCuratedName(const FName& NewValue) +{ + TSharedPtr ValueHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Enum, Value)); + + check(ValueHandle); + + ValueHandle->SetPerObjectValue(0, NewValue.ToString()); +} + +bool FFlowDataPinProperty_EnumCustomizationBase::TryGetCuratedName(FName& OutName) const +{ + if (const FFlowDataPinOutputProperty_Enum* ConfigurableEnumProperty = GetFlowDataPinEnumProperty()) + { + OutName = ConfigurableEnumProperty->Value; + + return true; + } + else + { + return false; + } +} + +void FFlowDataPinProperty_EnumCustomizationBase::OnEnumNameChanged() +{ + if (FFlowDataPinOutputProperty_Enum* FlowDataPinEnumProperty = GetFlowDataPinEnumProperty()) + { + FlowDataPinEnumProperty->OnEnumNameChanged(); + } +} + +const UEnum* FFlowDataPinProperty_EnumCustomizationBase::GetEnumClass() const +{ + if (const FFlowDataPinOutputProperty_Enum* FlowDataPinEnumProperty = GetFlowDataPinEnumProperty()) + { + return FlowDataPinEnumProperty->EnumClass; + } + + return nullptr; +} diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinProperty_ObjectCustomization.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinProperty_ObjectCustomization.cpp new file mode 100644 index 000000000..a5f6a236a --- /dev/null +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinProperty_ObjectCustomization.cpp @@ -0,0 +1,150 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "DetailCustomizations/FlowDataPinProperty_ObjectCustomization.h" +#include "DetailWidgetRow.h" +#include "Types/FlowDataPinProperties.h" +#include "EditorClassUtils.h" +#include "IDetailChildrenBuilder.h" + +void FFlowDataPinProperty_ObjectCustomizationBase::CustomizeHeader(TSharedRef InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) +{ + StructPropertyHandle = InStructPropertyHandle; + + CachedMetaClassPtr = DeriveBestClassFilter(); + + TrySetClassFilterFromMetaData(); + + // NOTE (gtaylor) Unfortunately, I wasn't able to get the customization filtering the object options using the ClassFilter + // (like FFlowDataPinProperty_ClassCustomizationBase does), because the object selection widget is less customizable (compared to the Class selection widget). + // Longer-term, this property customization could be improved to do this object filtering using the ClassFilter, + // but I don't have time to do that right now. + + HeaderRow + .NameContent() + [ + InStructPropertyHandle->CreatePropertyNameWidget() + ]; + + // This avoids making duplicate reset boxes + StructPropertyHandle->MarkResetToDefaultCustomized(); +} + +void FFlowDataPinProperty_ObjectCustomizationBase::CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) +{ + if (TSharedPtr ClassFilterHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Object, ClassFilter))) + { + StructBuilder.AddProperty(ClassFilterHandle.ToSharedRef()); + + ClassFilterHandle->SetOnPropertyValueChanged(FSimpleDelegate::CreateSP(this, &FFlowDataPinProperty_ObjectCustomizationBase::OnClassFilterChanged)); + } + + if (TSharedPtr ReferenceValueHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Object, ReferenceValue))) + { + StructBuilder.AddProperty(ReferenceValueHandle.ToSharedRef()); + + ReferenceValueHandle->SetOnPropertyValueChanged(FSimpleDelegate::CreateSP(this, &FFlowDataPinProperty_ObjectCustomizationBase::OnObjectValueChanged)); + } + + if (TSharedPtr InlineValueHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Object, InlineValue))) + { + StructBuilder.AddProperty(InlineValueHandle.ToSharedRef()); + + InlineValueHandle->SetOnPropertyValueChanged(FSimpleDelegate::CreateSP(this, &FFlowDataPinProperty_ObjectCustomizationBase::OnObjectValueChanged)); + } +} + +void FFlowDataPinProperty_ObjectCustomizationBase::OnClassFilterChanged() +{ + // We don't allow changing away from the Object filter specified in property metadata. + // So potentially undo the change (would be better to make it non-editable if the metadata was set, but I'm not sure how to do that) + TrySetClassFilterFromMetaData(); + + UClass* MetaClass = DeriveBestClassFilter(); + void* ObjectValuePropertyAsVoid = nullptr; + StructPropertyHandle->GetValueData(ObjectValuePropertyAsVoid); + FFlowDataPinOutputProperty_Object* ObjectValueProperty = static_cast(ObjectValuePropertyAsVoid); + + UObject* ObjectValue = ObjectValueProperty ? ObjectValueProperty->GetObjectValue() : nullptr; + + if (MetaClass && ObjectValue && !ObjectValue->IsA(MetaClass)) + { + TSharedPtr ReferenceObjectValueHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Object, ReferenceValue)); + TSharedPtr InlineObjectValueHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Object, InlineValue)); + + // Clear the Object value if it is not compatible with the new ClassFilter value + const UObject* NullObjectPtr = nullptr; + ReferenceObjectValueHandle->SetValue(NullObjectPtr, EPropertyValueSetFlags::DefaultFlags); + InlineObjectValueHandle->SetValue(NullObjectPtr, EPropertyValueSetFlags::DefaultFlags); + } + + CachedMetaClassPtr = MetaClass; + + IFlowExtendedPropertyTypeCustomization::OnAnyChildPropertyChanged(); +} + +void FFlowDataPinProperty_ObjectCustomizationBase::OnObjectValueChanged() +{ + OnClassFilterChanged(); +} + +UClass* FFlowDataPinProperty_ObjectCustomizationBase::DeriveBestClassFilter() const +{ + const FProperty* StructProperty = StructPropertyHandle->GetProperty(); + + if (!StructProperty) + { + return nullptr; + } + + if (UClass* MetaClass = FFlowDataPinOutputProperty_Class::TryGetMetaClassFromProperty(*StructProperty)) + { + return MetaClass; + } + + // Allow the Instance to edit the ClassFilter to override the MetaClass + if (TSharedPtr ClassFilterHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Object, ClassFilter))) + { + UObject* ExistingMetaClass = nullptr; + ClassFilterHandle->GetValue(ExistingMetaClass); + + if (ExistingMetaClass) + { + return Cast(ExistingMetaClass); + } + } + + return nullptr; +} + +void FFlowDataPinProperty_ObjectCustomizationBase::TrySetClassFilterFromMetaData() +{ + const FString& MetaClassName = StructPropertyHandle->GetMetaData("MetaClass"); + + if (MetaClassName.IsEmpty()) + { + return; + } + + UClass* MetaClass = FEditorClassUtils::GetClassFromString(MetaClassName); + if (!MetaClass) + { + return; + } + + // If the Object filter was set in meta data, force that value to the ClassFilter property + if (TSharedPtr ClassFilterHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Object, ClassFilter))) + { + UObject* ExistingMetaClass = nullptr; + ClassFilterHandle->GetValue(ExistingMetaClass); + + if (ExistingMetaClass != MetaClass) + { + ClassFilterHandle->SetValue(MetaClass, EPropertyValueSetFlags::DefaultFlags); + } + } +} + +UClass* FFlowDataPinProperty_ObjectCustomizationBase::BuildMetaClass() const +{ + return CachedMetaClassPtr.Get(); +} diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowNamedDataPinOutputPropertyCustomization.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowNamedDataPinOutputPropertyCustomization.cpp new file mode 100644 index 000000000..c9ed8c249 --- /dev/null +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowNamedDataPinOutputPropertyCustomization.cpp @@ -0,0 +1,13 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "DetailCustomizations/FlowNamedDataPinOutputPropertyCustomization.h" + +FText FFlowNamedDataPinOutputPropertyCustomization::BuildHeaderText() const +{ + if (const FFlowNamedDataPinOutputProperty* FlowNamedDataPinOutputProperty = IFlowExtendedPropertyTypeCustomization::TryGetTypedStructValue(StructPropertyHandle)) + { + return FlowNamedDataPinOutputProperty->BuildHeaderText(); + } + + return Super::BuildHeaderText(); +} diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowPinCustomization.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowPinCustomization.cpp new file mode 100644 index 000000000..e5dfbfb30 --- /dev/null +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowPinCustomization.cpp @@ -0,0 +1,43 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "DetailCustomizations/FlowPinCustomization.h" +#include "Nodes/FlowPin.h" +#include "IDetailChildrenBuilder.h" +#include "UObject/UnrealType.h" + +void FFlowPinCustomization::CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) +{ + // Add all of the child properties as normal, but also bind a callback when the property value changes + uint32 NumChildren = 0; + verify(InStructPropertyHandle->GetNumChildren(NumChildren) == FPropertyAccess::Success); + for (uint32 ChildIdx = 0; ChildIdx < NumChildren; ++ChildIdx) + { + TSharedPtr ChildProperty = InStructPropertyHandle->GetChildHandle(ChildIdx); + if (ChildProperty.IsValid()) + { + ChildProperty->SetOnPropertyValueChanged(FSimpleDelegate::CreateSP(this, &FFlowPinCustomization::OnChildPropertyValueChanged)); + + ChildBuilder.AddProperty(ChildProperty.ToSharedRef()); + } + } +} + +void FFlowPinCustomization::OnChildPropertyValueChanged() +{ + if (FFlowPin* FlowPin = GetFlowPin()) + { + FlowPin->PostEditChangedPinTypeOrSubCategorySource(); + + IFlowExtendedPropertyTypeCustomization::OnAnyChildPropertyChanged(); + } +} + +FText FFlowPinCustomization::BuildHeaderText() const +{ + if (const FFlowPin* FlowPin = GetFlowPin()) + { + return FlowPin->BuildHeaderText(); + } + + return Super::BuildHeaderText(); +} diff --git a/Source/FlowEditor/Private/Find/FindInFlow.cpp b/Source/FlowEditor/Private/Find/FindInFlow.cpp index 527a025c6..b5c964763 100644 --- a/Source/FlowEditor/Private/Find/FindInFlow.cpp +++ b/Source/FlowEditor/Private/Find/FindInFlow.cpp @@ -30,10 +30,12 @@ #include "UObject/Class.h" #include "UObject/ObjectPtr.h" #include "Widgets/Images/SImage.h" +#include "Widgets/Input/SCheckBox.h" #include "Widgets/Input/SSearchBox.h" #include "Widgets/Layout/SBorder.h" #include "Widgets/Layout/SBox.h" #include "Widgets/SBoxPanel.h" +#include "Widgets/SToolTip.h" #include "Widgets/Text/STextBlock.h" #include "Widgets/Views/STableRow.h" @@ -217,7 +219,9 @@ void SFindInFlow::Construct( const FArguments& InArgs, TSharedPtr("Flow"); + + UFlowGraphSettings& Settings = *UFlowGraphSettings::Get(); + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(TAG_Flow_NodeDisplayStyle, FLinearColor(0.0f, 0.581f, 1.0f, 1.0f))); + + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(TAG_Flow_NodeDisplayStyle_Node_Condition, FLinearColor(1.0f, 0.62f, 0.016f, 1.0f))); + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(TAG_Flow_NodeDisplayStyle_Node_Deprecated, FLinearColor(1.0f, 1.0f, 0.0f, 1.0f))); + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(TAG_Flow_NodeDisplayStyle_Node_Developer, FLinearColor(0.7f, 0.2f, 1.0f, 1.0f))); + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(TAG_Flow_NodeDisplayStyle_Node_InOut, FLinearColor(1.0f, 0.0f, 0.008f, 1.0f))); + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(TAG_Flow_NodeDisplayStyle_Node_Latent, FLinearColor(0.0f, 0.770f, 0.375f, 1.0f))); + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(TAG_Flow_NodeDisplayStyle_Node_Logic, FLinearColor(1.0f, 1.0f, 1.0f, 1.0f))); + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(TAG_Flow_NodeDisplayStyle_Node_SubGraph, FLinearColor(1.0f, 0.128f, 0.0f, 1.0f))); + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(TAG_Flow_NodeDisplayStyle_Node_Terminal, FLinearColor(1.0f, 0.0f, 0.008f, 1.0f))); + + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(TAG_Flow_NodeDisplayStyle_AddOn, FLinearColor(0.0f, 0.581f, 1.0f, 1.0f))); // !!! Update this + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(TAG_Flow_NodeDisplayStyle_AddOn_PerSpawnedActor, FLinearColor(0.3f, 0.3f, 1.0f, 1.0f))); + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(TAG_Flow_NodeDisplayStyle_AddOn_Predicate, FLinearColor(1.0f, 1.0f, 1.0f, 1.0f))); + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(TAG_Flow_NodeDisplayStyle_AddOn_Predicate_Composite, FLinearColor(1.0f, 1.0f, 1.0f, 1.0f))); // !!! Update this +} + void FFlowEditorModule::RegisterAssets() { IAssetTools& AssetTools = FModuleManager::LoadModuleChecked("AssetTools").Get(); @@ -195,6 +232,35 @@ void FFlowEditorModule::RegisterDetailCustomizations() RegisterCustomClassLayout(UFlowNode_SubGraph::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_SubGraphDetails::MakeInstance)); RegisterCustomStructLayout(*FFlowOwnerFunctionRef::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowOwnerFunctionRefCustomization::MakeInstance)); RegisterCustomStructLayout(*FFlowActorOwnerComponentRef::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowActorOwnerComponentRefCustomization::MakeInstance)); + RegisterCustomStructLayout(*FFlowPin::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowPinCustomization::MakeInstance)); + RegisterCustomStructLayout(*FFlowNamedDataPinOutputProperty::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowNamedDataPinOutputPropertyCustomization::MakeInstance)); + + RegisterCustomStructLayout(*FFlowDataPinOutputProperty_Bool::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinOutputProperty_BoolCustomization::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinOutputProperty_Int64::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinOutputProperty_Int64Customization::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinOutputProperty_Int32::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinOutputProperty_Int32Customization::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinOutputProperty_Double::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinOutputProperty_DoubleCustomization::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinOutputProperty_Float::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinOutputProperty_FloatCustomization::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinOutputProperty_Name::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinOutputProperty_NameCustomization::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinOutputProperty_String::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinOutputProperty_StringCustomization::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinOutputProperty_Text::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinOutputProperty_TextCustomization::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinOutputProperty_Enum::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinOutputProperty_EnumCustomization::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinOutputProperty_Class::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinOutputProperty_ClassCustomization::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinOutputProperty_Object::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinOutputProperty_ObjectCustomization::MakeInstance)); + + RegisterCustomStructLayout(*FFlowDataPinInputProperty_Bool::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinInputProperty_BoolCustomization::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinInputProperty_Int64::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinInputProperty_Int64Customization::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinInputProperty_Int32::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinInputProperty_Int32Customization::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinInputProperty_Double::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinInputProperty_DoubleCustomization::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinInputProperty_Float::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinInputProperty_FloatCustomization::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinInputProperty_Name::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinInputProperty_NameCustomization::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinInputProperty_String::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinInputProperty_StringCustomization::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinInputProperty_Text::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinInputProperty_TextCustomization::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinInputProperty_Enum::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinInputProperty_EnumCustomization::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinInputProperty_Class::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinInputProperty_ClassCustomization::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinInputProperty_Object::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinInputProperty_ObjectCustomization::MakeInstance)); + + // Consider implementing details customizations... for every EFlowPinType + FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); PropertyModule.NotifyCustomizationModuleChanged(); } diff --git a/Source/FlowEditor/Private/Graph/FlowGraph.cpp b/Source/FlowEditor/Private/Graph/FlowGraph.cpp index 77e3ec9ee..f67f48733 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraph.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraph.cpp @@ -29,6 +29,7 @@ UFlowGraph::UFlowGraph(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { bLockUpdates = false; + bIsLoadingGraph = false; if (!UFlowAsset::GetFlowGraphInterface().IsValid()) { @@ -107,7 +108,9 @@ void UFlowGraph::RefreshGraph() void UFlowGraph::NotifyGraphChanged() { - GetFlowAsset()->HarvestNodeConnections(); + UFlowAsset* FlowAsset = GetFlowAsset(); + + FlowAsset->HarvestNodeConnections(); Super::NotifyGraphChanged(); } @@ -165,6 +168,9 @@ void UFlowGraph::RecursivelySetupAllFlowGraphNodesForEditing(UFlowGraphNode& Fro FromNodeInstance->SetupForEditing(FromFlowGraphNode); } + // Reconstruct the node when starting up editing + FromFlowGraphNode.ReconstructNode(); + for (UFlowGraphNode* SubNode : FromFlowGraphNode.SubNodes) { // Setup all of the flow subnodes for editing @@ -207,6 +213,8 @@ void UFlowGraph::OnLoaded() { check(GEditor); + bIsLoadingGraph = true; + // Setup all the Nodes in the graph for editing for (UEdGraphNode* EdNode : Nodes) { @@ -225,6 +233,8 @@ void UFlowGraph::OnLoaded() } RefreshGraph(); + + bIsLoadingGraph = false; } void UFlowGraph::OnSave() @@ -324,6 +334,24 @@ void UFlowGraph::UpdateFlowGraphNodeErrorMessage(UFlowGraphNode& Node) } } +void UFlowGraph::ValidateAsset(FFlowMessageLog& MessageLog) +{ + UFlowAsset* FlowAsset = GetFlowAsset(); + if (FlowAsset) + { + FlowAsset->ValidateAsset(MessageLog); + } + + for (int32 Idx = 0, IdxNum = Nodes.Num(); Idx < IdxNum; ++Idx) + { + UFlowGraphNode* Node = Cast(Nodes[Idx]); + if (Node != nullptr) + { + Node->ValidateGraphNode(MessageLog); + } + } +} + void UFlowGraph::UpdateDeprecatedClasses() { // This function sets error messages and logs errors about nodes. diff --git a/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp b/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp index 7afc48d3d..3e187b349 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp @@ -11,6 +11,7 @@ #include "Graph/Nodes/FlowGraphNode.h" #include "FlowAsset.h" +#include "FlowEditorLogChannels.h" #include "Graph/Nodes/FlowGraphNode_Reroute.h" #include "Nodes/FlowNode.h" @@ -64,6 +65,13 @@ void FFlowGraphConnectionDrawingPolicy::BuildPaths() for (const TPair& Record : Node->GetWireRecords()) { + if (!FlowGraphNode->OutputPins.IsValidIndex(Record.Key)) + { + UE_LOG(LogFlowEditor, Error, TEXT("Flow node '%s' has an invalid pin connection. This is probably an flow editor code bug."), *Node->GetName()); + + continue; + } + if (UEdGraphPin* OutputPin = FlowGraphNode->OutputPins[Record.Key]) { // check if Output pin is connected to anything @@ -133,16 +141,16 @@ void FFlowGraphConnectionDrawingPolicy::DetermineWiringStyle(UEdGraphPin* Output { Params.WireColor = FLinearColor::Red; } - else if (Cast(OutputPin->GetOwningNode())->GetSignalMode() == EFlowSignalMode::Disabled) - { - Params.WireColor *= 0.5f; - Params.WireThickness = 0.5f; - } else { Params.WireColor = Schema->GetPinTypeColor(OutputPin->PinType); - if (InputPin) + if (Cast(OutputPin->GetOwningNode())->GetSignalMode() == EFlowSignalMode::Disabled) + { + Params.WireColor *= 0.5f; + Params.WireThickness = 0.5f; + } + else if (InputPin && FFlowPin::IsExecPinCategory(InputPin->PinType.PinCategory)) { // selected paths if (SelectedPaths.Contains(OutputPin) || SelectedPaths.Contains(InputPin)) @@ -202,7 +210,6 @@ void FFlowGraphConnectionDrawingPolicy::DetermineWiringStyle(UEdGraphPin* Output { ApplyHoverDeemphasis(OutputPin, InputPin, /*inout*/ Params.WireThickness, /*inout*/ Params.WireColor); } - } void FFlowGraphConnectionDrawingPolicy::Draw(TMap, FArrangedWidget>& InPinGeometries, FArrangedChildren& ArrangedNodes) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index 9bc264ecd..d611a31ae 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -2,9 +2,11 @@ #include "Graph/FlowGraphEditor.h" +#include "FlowEditorCommands.h" +#include "FlowEditorModule.h" + #include "Asset/FlowAssetEditor.h" #include "Asset/FlowDebuggerSubsystem.h" -#include "FlowEditorCommands.h" #include "Graph/FlowGraphEditorSettings.h" #include "Graph/FlowGraphSchema_Actions.h" #include "Graph/Nodes/FlowGraphNode.h" @@ -643,43 +645,25 @@ void SFlowGraphEditor::PasteNodesHere(const FVector2D& Location) FlowGraph->LockUpdates(); - UFlowGraphNode* SelectedParent = nullptr; + const TArray PasteTargetNodes = DerivePasteTargetNodesFromSelectedNodes(); + checkf(PasteTargetNodes.Num() <= 1, TEXT("This should be enforced in CanPasteNodes()")); - const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes(); - for (FGraphPanelSelectionSet::TConstIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter) - { - if (UFlowGraphNode* Node = Cast(*SelectedIter)) - { - if (SelectedParent == nullptr) - { - SelectedParent = Node; - } - else - { - break; - } - } - } + UFlowGraphNode* PasteTargetNode = !PasteTargetNodes.IsEmpty() ? PasteTargetNodes.Top() : nullptr; + + FString TextToImport; + const TSet NodesToPaste = ImportNodesToPasteFromClipboard(*FlowGraph, TextToImport); // Clear the selection set (newly pasted stuff will be selected) ClearSelectionSet(); FlowAssetEditor.Pin()->SetUISelectionState(NAME_None); - // Grab the text to paste from the clipboard. - FString TextToImport; - FPlatformApplicationMisc::ClipboardPaste(TextToImport); - - // Import the nodes - TSet PastedNodes; - FEdGraphUtilities::ImportNodesFromText(FlowGraph, TextToImport, /*out*/ PastedNodes); - //Average position of nodes so we can move them while still maintaining relative distances to each other FVector2D AvgNodePosition(0.0f, 0.0f); // Number of nodes used to calculate AvgNodePosition int32 AvgCount = 0; - for (TSet::TIterator It(PastedNodes); It; ++It) + for (TSet::TConstIterator It(NodesToPaste); It; ++It) { UEdGraphNode* EdNode = *It; UFlowGraphNode* FlowGraphNode = Cast(EdNode); @@ -698,8 +682,10 @@ void SFlowGraphEditor::PasteNodesHere(const FVector2D& Location) AvgNodePosition.Y *= InvNumNodes; } + bool bPastedParentNode = false; + TMap EdNodeCopyIndexMap; - for (TSet::TIterator It(PastedNodes); It; ++It) + for (TSet::TConstIterator It(NodesToPaste); It; ++It) { UEdGraphNode* PasteNode = *It; UFlowGraphNode* PasteFlowGraphNode = Cast(PasteNode); @@ -708,6 +694,8 @@ void SFlowGraphEditor::PasteNodesHere(const FVector2D& Location) if (PasteNode && (PasteFlowGraphNode == nullptr || !PasteFlowGraphNode->IsSubNode())) { + bPastedParentNode = true; + // Select the newly pasted stuff constexpr bool bSelectNodes = true; SetNodeSelection(PasteNode, bSelectNodes); @@ -734,7 +722,7 @@ void SFlowGraphEditor::PasteNodesHere(const FVector2D& Location) } } - for (TSet::TIterator It(PastedNodes); It; ++It) + for (TSet::TConstIterator It(NodesToPaste); It; ++It) { UFlowGraphNode* PasteNode = Cast(*It); if (PasteNode && PasteNode->IsSubNode()) @@ -747,11 +735,11 @@ void SFlowGraphEditor::PasteNodesHere(const FVector2D& Location) if (PasteNode->CopySubNodeParentIndex == INDEX_NONE) { - // INDEX_NONE parent index indicates we should set the parent to the SelectedParent + // INDEX_NONE parent index indicates we should set the parent to the PasteTargetNode - if (SelectedParent) + if (PasteTargetNode) { - SelectedParent->AddSubNode(PasteNode, FlowGraph); + PasteTargetNode->AddSubNode(PasteNode, FlowGraph); } } else if (UFlowGraphNode* PastedParentNode = EdNodeCopyIndexMap.FindRef(PasteNode->CopySubNodeParentIndex)) @@ -779,6 +767,35 @@ void SFlowGraphEditor::PasteNodesHere(const FVector2D& Location) } } +TSet SFlowGraphEditor::ImportNodesToPasteFromClipboard(UFlowGraph& FlowGraph, FString& OutTextToImport) const +{ + // Grab the text to paste from the clipboard. + FPlatformApplicationMisc::ClipboardPaste(OutTextToImport); + + // Import the nodes + TSet NodesToPaste; + FEdGraphUtilities::ImportNodesFromText(&FlowGraph, OutTextToImport, /*out*/ NodesToPaste); + + return NodesToPaste; +} + +TArray SFlowGraphEditor::DerivePasteTargetNodesFromSelectedNodes() const +{ + TArray PasteTargetNodes; + const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes(); + for (FGraphPanelSelectionSet::TConstIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter) + { + UFlowGraphNode* Node = Cast(*SelectedIter); + + if (IsValid(Node)) + { + PasteTargetNodes.Add(Node); + } + } + + return PasteTargetNodes; +} + bool SFlowGraphEditor::CanPasteNodes() const { if (!CanEdit() || !IsTabFocused()) @@ -789,14 +806,120 @@ bool SFlowGraphEditor::CanPasteNodes() const FString ClipboardContent; FPlatformApplicationMisc::ClipboardPaste(ClipboardContent); - const bool bIsPastePossible = FEdGraphUtilities::CanImportNodesFromText(FlowAsset->GetGraph(), ClipboardContent); + UFlowGraph* FlowGraph = CastChecked(FlowAsset->GetGraph()); + if (!ensure(IsValid(FlowGraph))) + { + // We expect to have a legal FlowGraph pointer at this point + + return false; + } + const bool bIsPastePossible = FEdGraphUtilities::CanImportNodesFromText(FlowGraph, ClipboardContent); if (!bIsPastePossible) { return false; } - // TODO (gtaylor) Need to confirm the nodes are allowed to be pasted on the selected node(s) + // Disallow paste when multiple target nodes are selected. + const TArray PasteTargetNodes = DerivePasteTargetNodesFromSelectedNodes(); + if (PasteTargetNodes.Num() > 1) + { + // NOTE (gtaylor) It's possible we could support multi-paste, but we'd need to rework PasteNodesHere() + // to understand how to paste copies onto each target node. + + return false; + } + + FString TextToImport; + const TSet NodesToPaste = ImportNodesToPasteFromClipboard(*FlowGraph, TextToImport); + + if (NodesToPaste.IsEmpty()) + { + // Must have at least one node to paste + + return false; + } + + ON_SCOPE_EXIT + { + // We need to clean-up the nodes we built to test the paste operation + for (TSet::TConstIterator It(NodesToPaste); It; ++It) + { + UFlowGraphNode* NodeToPaste = Cast(*It); + if (IsValid(NodeToPaste)) + { + NodeToPaste->ClearFlags(RF_Public); + NodeToPaste->SetFlags(RF_Transient); + + const FString NewNameStr = MakeUniqueObjectName(NodeToPaste->GetOuter(), NodeToPaste->GetClass()).ToString(); + + // This will remove the node from its graph + NodeToPaste->DestroyNode(); + + // Rename and garbage the node so that it can't be found by name if the same clipboard is re-pasted + NodeToPaste->Rename(*NewNameStr, nullptr, REN_NonTransactional | REN_DontCreateRedirectors | REN_ForceNoResetLoaders); + NodeToPaste->MarkAsGarbage(); + } + } + }; + + // If pasting onto a selected node, confirm that the paste operation is legal + if (PasteTargetNodes.Num() >= 1) + { + checkf(PasteTargetNodes.Num() == 1, TEXT("This is enforced earlier in this function, just confirming the code stays that way here.")); + + UFlowGraphNode* PasteTargetNode = PasteTargetNodes.Top(); + + if (!CanPasteNodesAsSubNodes(NodesToPaste, *PasteTargetNode)) + { + return false; + } + } + + return true; +} + +bool SFlowGraphEditor::CanPasteNodesAsSubNodes(const TSet& NodesToPaste, const UFlowGraphNode& PasteTargetNode) const +{ + TSet AllRootSubNodesToPaste; + for (TSet::TConstIterator It(NodesToPaste); It; ++It) + { + const UFlowGraphNode* NodeToPaste = Cast(*It); + if (!ensure(IsValid(NodeToPaste))) + { + return false; + } + + if (!NodeToPaste->IsSubNode()) + { + // Only SubNodes can be pasted onto other nodes + + return false; + } + + // Only concerned with the 'root' subnodes + // (we assume the rest of the subnode tree is valid when put into the copy buffer) + if (NodeToPaste->CopySubNodeParentIndex != INDEX_NONE) + { + // a non-INDEX_NONE parent index indicates the subnode is is a non-root subnode in the NodesToPaste set + + continue; + } + + AllRootSubNodesToPaste.Add(NodeToPaste); + } + + for (TSet::TConstIterator It(AllRootSubNodesToPaste); It; ++It) + { + const UFlowGraphNode* NodeToPaste = Cast(*It); + + if (!PasteTargetNode.CanAcceptSubNodeAsChild(*NodeToPaste, AllRootSubNodesToPaste)) + { + // This node cannot accept the SubNode as a child + + return false; + } + } return true; } diff --git a/Source/FlowEditor/Private/Graph/FlowGraphPinFactory.cpp b/Source/FlowEditor/Private/Graph/FlowGraphPinFactory.cpp new file mode 100644 index 000000000..83e891c1e --- /dev/null +++ b/Source/FlowEditor/Private/Graph/FlowGraphPinFactory.cpp @@ -0,0 +1,104 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Graph/FlowGraphPinFactory.h" +#include "Graph/FlowGraphSchema.h" +#include "Graph/FlowGraphSettings.h" +#include "Graph/Nodes/FlowGraphNode.h" +#include "Graph/Widgets/SFlowGraphNode.h" +#include "Nodes/FlowNode.h" +#include "Nodes/FlowPin.h" + +#include "NodeFactory.h" +#include "SGraphPin.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraphPinFactory) + +////////////////////////////////////////////////////////////////////////// +// FFlowGraphPinFactory + +TSharedPtr FFlowGraphPinFactory::CreatePin(UEdGraphPin* InPin) const +{ + if (!InPin->GetSchema()->IsA()) + { + // Limit pin widget creation to FlowGraph schemas + return nullptr; + } + + const UFlowGraphNode* FlowGraphNode = Cast(InPin->GetOwningNode()); + + // Create the widget for a Flow 'Exec'-style pin + if (FlowGraphNode && FFlowPin::IsExecPinCategory(InPin->PinType.PinCategory)) + { + const TSharedPtr NewPinWidget = SNew(SFlowGraphPinExec, InPin); + + const UFlowNode* FlowNode = Cast(FlowGraphNode->GetFlowNodeBase()); + + if (!UFlowGraphSettings::Get()->bShowDefaultPinNames && IsValid(FlowNode)) + { + if (InPin->Direction == EGPD_Input) + { + // Pin array can have pins with name None, which will not be created. We need to check if array have only one valid pin + if (GatherValidPinsCount(FlowNode->GetInputPins()) == 1 && InPin->PinName == UFlowNode::DefaultInputPin.PinName) + { + NewPinWidget->SetShowLabel(false); + } + } + else + { + // Pin array can have pins with name None, which will not be created. We need to check if array have only one valid pin + if (GatherValidPinsCount(FlowNode->GetOutputPins()) == 1 && InPin->PinName == UFlowNode::DefaultOutputPin.PinName) + { + NewPinWidget->SetShowLabel(false); + } + } + } + + return NewPinWidget; + } + + // For data pins, give the K2 (blueprint) node factory an opportunity to create the widget + TSharedPtr K2PinWidget = FNodeFactory::CreateK2PinWidget(InPin); + if (K2PinWidget.IsValid()) + { + return K2PinWidget; + } + + return nullptr; +} + +int32 FFlowGraphPinFactory::GatherValidPinsCount(const TArray& Pins) +{ + int32 Count = 0; + for (const FFlowPin& Pin : Pins) + { + if (Pin.IsValid()) + { + ++Count; + } + } + + return Count; +} + +////////////////////////////////////////////////////////////////////////// +// UFlowK2SchemaSubclassForAccess + +void UFlowK2SchemaSubclassForAccess::AssertPinCategoryNames() +{ + // Assert that the FFlowPin aliases for the UEdGraphSchema_K2 PC_* FNames match exactly + // (since we will leverage some K2 functions that key off these names) + checkf(FFlowPin::PC_Exec == UEdGraphSchema_K2::PC_Exec, TEXT("Flow PC_* aliases should match Epic's K2 PC_* names exactly")); + checkf(FFlowPin::PC_Boolean == UEdGraphSchema_K2::PC_Boolean, TEXT("Flow PC_* aliases should match Epic's K2 PC_* names exactly")); + checkf(FFlowPin::PC_Byte == UEdGraphSchema_K2::PC_Byte, TEXT("Flow PC_* aliases should match Epic's K2 PC_* names exactly")); + checkf(FFlowPin::PC_Class == UEdGraphSchema_K2::PC_Class, TEXT("Flow PC_* aliases should match Epic's K2 PC_* names exactly")); + checkf(FFlowPin::PC_Int == UEdGraphSchema_K2::PC_Int, TEXT("Flow PC_* aliases should match Epic's K2 PC_* names exactly")); + checkf(FFlowPin::PC_Int64 == UEdGraphSchema_K2::PC_Int64, TEXT("Flow PC_* aliases should match Epic's K2 PC_* names exactly")); + checkf(FFlowPin::PC_Float == UEdGraphSchema_K2::PC_Float, TEXT("Flow PC_* aliases should match Epic's K2 PC_* names exactly")); + checkf(FFlowPin::PC_Double == UEdGraphSchema_K2::PC_Double, TEXT("Flow PC_* aliases should match Epic's K2 PC_* names exactly")); + checkf(FFlowPin::PC_Name == UEdGraphSchema_K2::PC_Name, TEXT("Flow PC_* aliases should match Epic's K2 PC_* names exactly")); + checkf(FFlowPin::PC_Object == UEdGraphSchema_K2::PC_Object, TEXT("Flow PC_* aliases should match Epic's K2 PC_* names exactly")); + checkf(FFlowPin::PC_String == UEdGraphSchema_K2::PC_String, TEXT("Flow PC_* aliases should match Epic's K2 PC_* names exactly")); + checkf(FFlowPin::PC_Text == UEdGraphSchema_K2::PC_Text, TEXT("Flow PC_* aliases should match Epic's K2 PC_* names exactly")); + checkf(FFlowPin::PC_Struct == UEdGraphSchema_K2::PC_Struct, TEXT("Flow PC_* aliases should match Epic's K2 PC_* names exactly")); + checkf(FFlowPin::PC_Enum == UEdGraphSchema_K2::PC_Enum, TEXT("Flow PC_* aliases should match Epic's K2 PC_* names exactly")); +} diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index 32e6f8be1..a4e8e96a1 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -11,9 +11,11 @@ #include "Graph/Nodes/FlowGraphNode.h" #include "FlowAsset.h" +#include "FlowEditorLogChannels.h" #include "FlowSettings.h" #include "AddOns/FlowNodeAddOn.h" #include "Nodes/FlowNode.h" +#include "Nodes/FlowNodeAddOnBlueprint.h" #include "Nodes/FlowNodeBlueprint.h" #include "Nodes/Route/FlowNode_CustomInput.h" #include "Nodes/Route/FlowNode_Start.h" @@ -21,9 +23,14 @@ #include "AssetRegistry/AssetRegistryModule.h" #include "EdGraph/EdGraph.h" +#include "EdGraphSchema_K2.h" #include "Editor.h" +#include "Engine/MemberReference.h" +#include "Engine/UserDefinedStruct.h" +#include "Kismet/BlueprintTypeConversions.h" +#include "Kismet2/KismetEditorUtilities.h" +#include "Misc/DefaultValueHelper.h" #include "ScopedTransaction.h" -#include "Nodes/FlowNodeAddOnBlueprint.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraphSchema) @@ -41,6 +48,148 @@ int32 UFlowGraphSchema::CurrentCacheRefreshID = 0; FFlowGraphSchemaRefresh UFlowGraphSchema::OnNodeListChanged; +const UScriptStruct* UFlowGraphSchema::VectorStruct = nullptr; +const UScriptStruct* UFlowGraphSchema::RotatorStruct = nullptr; +const UScriptStruct* UFlowGraphSchema::TransformStruct = nullptr; + +namespace FlowGraphSchema::Private +{ + // Adapted from UE::EdGraphSchemaK2::Private, because it's Private + + template + constexpr bool TAlwaysFalse = false; + + template + UClass* GetAuthoritativeClass(const TProperty& Property) + { + UClass* PropertyClass = nullptr; + if constexpr (std::is_same_v) + { + PropertyClass = Property.PropertyClass; + } + else if constexpr (std::is_same_v) + { + PropertyClass = Property.PropertyClass; + } + else if constexpr (std::is_same_v) + { + PropertyClass = Property.InterfaceClass; + } + else if constexpr (std::is_same_v) + { + PropertyClass = Property.MetaClass; + } + else if constexpr (std::is_same_v) + { + PropertyClass = Property.MetaClass; + } + else + { + static_assert(TAlwaysFalse, "Invalid property used."); + } + + if (PropertyClass && PropertyClass->ClassGeneratedBy) + { + PropertyClass = PropertyClass->GetAuthoritativeClass(); + } + + if (PropertyClass && FKismetEditorUtilities::IsClassABlueprintSkeleton(PropertyClass)) + { + UE_LOG(LogFlowEditor, Warning, TEXT("'%s' is a skeleton class. SubCategoryObject will serialize to a null value."), *PropertyClass->GetFullName()); + } + + return PropertyClass; + } + + static UClass* GetOriginalClassToFixCompatibility(const UClass* InClass) + { + const UBlueprint* BP = InClass ? Cast(InClass->ClassGeneratedBy) : nullptr; + return BP ? BP->OriginalClass : nullptr; + } + + // During compilation, pins are moved around for node expansion and the Blueprints may still inherit from REINST_ classes + // which causes problems for IsChildOf. Because we do not want to modify IsChildOf we must use a separate function + // that can check to see if classes have an AuthoritativeClass that IsChildOf a Target class. + static bool IsAuthoritativeChildOf(const UStruct* InSourceStruct, const UStruct* InTargetStruct) + { + bool bResult = false; + bool bIsNonNativeClass = false; + if (const UClass* TargetAsClass = Cast(InTargetStruct)) + { + InTargetStruct = TargetAsClass->GetAuthoritativeClass(); + } + if (UClass* SourceAsClass = const_cast(Cast(InSourceStruct))) + { + if (SourceAsClass->ClassGeneratedBy) + { + // We have a non-native (Blueprint) class which means it can exist in a semi-compiled state and inherit from a REINST_ class. + bIsNonNativeClass = true; + while (SourceAsClass) + { + if (SourceAsClass->GetAuthoritativeClass() == InTargetStruct) + { + bResult = true; + break; + } + SourceAsClass = SourceAsClass->GetSuperClass(); + } + } + } + + // We have a native (C++) class, do a normal IsChildOf check + if (!bIsNonNativeClass) + { + bResult = InSourceStruct && InSourceStruct->IsChildOf(InTargetStruct); + } + + return bResult; + } + + static bool ExtendedIsChildOf(const UClass* Child, const UClass* Parent) + { + if (Child && Child->IsChildOf(Parent)) + { + return true; + } + + const UClass* OriginalChild = GetOriginalClassToFixCompatibility(Child); + if (OriginalChild && OriginalChild->IsChildOf(Parent)) + { + return true; + } + + const UClass* OriginalParent = GetOriginalClassToFixCompatibility(Parent); + if (OriginalParent && Child && Child->IsChildOf(OriginalParent)) + { + return true; + } + + return false; + } + + static bool ExtendedImplementsInterface(const UClass* Class, const UClass* Interface) + { + if (Class->ImplementsInterface(Interface)) + { + return true; + } + + const UClass* OriginalClass = GetOriginalClassToFixCompatibility(Class); + if (OriginalClass && OriginalClass->ImplementsInterface(Interface)) + { + return true; + } + + const UClass* OriginalInterface = GetOriginalClassToFixCompatibility(Interface); + if (OriginalInterface && Class->ImplementsInterface(OriginalInterface)) + { + return true; + } + + return false; + } +} + UFlowGraphSchema::UFlowGraphSchema(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { @@ -51,6 +200,14 @@ UFlowGraphSchema::UFlowGraphSchema(const FObjectInitializer& ObjectInitializer) GetDefault()->ForceVisualizationCacheClear(); }); } + + // Initialize cached static references to well-known struct types + if (VectorStruct == nullptr) + { + VectorStruct = TBaseStructure::Get(); + RotatorStruct = TBaseStructure::Get(); + TransformStruct = TBaseStructure::Get(); + } } void UFlowGraphSchema::SubscribeToAssetChanges() @@ -110,7 +267,9 @@ void UFlowGraphSchema::CreateDefaultNodesForGraph(UEdGraph& Graph) const } } - CastChecked(&Graph)->GetFlowAsset()->HarvestNodeConnections(); + UFlowAsset* FlowAsset = CastChecked(&Graph)->GetFlowAsset(); + + FlowAsset->HarvestNodeConnections(); } UFlowGraphNode* UFlowGraphSchema::CreateDefaultNode(UEdGraph& Graph, const UFlowAsset* AssetClassDefaults, const TSubclassOf& NodeClass, const FVector2D& Offset, const bool bPlacedAsGhostNode) @@ -126,6 +285,146 @@ UFlowGraphNode* UFlowGraphSchema::CreateDefaultNode(UEdGraph& Graph, const UFlow return NewGraphNode; } +bool UFlowGraphSchema::ArePinsCompatible(const UEdGraphPin* PinA, const UEdGraphPin* PinB, const UClass* CallingContext, bool bIgnoreArray /*= false*/) const +{ + // Adapted from UEdGraphSchema_K2 + if ((PinA->Direction == EGPD_Input) && (PinB->Direction == EGPD_Output)) + { + return ArePinTypesCompatible(PinB->PinType, PinA->PinType, CallingContext, bIgnoreArray); + } + else if ((PinB->Direction == EGPD_Input) && (PinA->Direction == EGPD_Output)) + { + return ArePinTypesCompatible(PinA->PinType, PinB->PinType, CallingContext, bIgnoreArray); + } + else + { + return false; + } +} + +bool UFlowGraphSchema::ArePinCategoriesEffectivelyMatching(const FName& InputPinCategory, const FName& OutputPinCategory, bool bAllowImplicitCasts) +{ + if (InputPinCategory == OutputPinCategory) + { + return true; + } + + if (!bAllowImplicitCasts) + { + return false; + } + + // Must handle pin connectivity for all added EFlowPinTypes + FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); + + // We could extend the compatibility here to accept more implicit conversions (eg, null objects convertible to bools) + // but we'd need to also add support the conversion in the Supply/Resolve side as well. + + if (FFlowPin::IsBoolPinCategory(InputPinCategory) && FFlowPin::IsConvertableToBoolPinCategory(OutputPinCategory)) + { + return true; + } + + if (FFlowPin::IsIntPinCategory(InputPinCategory) && FFlowPin::IsConvertableToIntPinCategory(OutputPinCategory)) + { + return true; + } + + if (FFlowPin::IsFloatPinCategory(InputPinCategory) && FFlowPin::IsConvertableToFloatPinCategory(OutputPinCategory)) + { + return true; + } + + if (FFlowPin::IsEnumPinCategory(InputPinCategory) && FFlowPin::IsConvertableToEnumPinCategory(OutputPinCategory)) + { + return true; + } + + if (FFlowPin::IsTextPinCategory(InputPinCategory) && FFlowPin::IsConvertableToTextPinCategory(OutputPinCategory)) + { + return true; + } + + if (FFlowPin::IsObjectPinCategory(InputPinCategory) && FFlowPin::IsConvertableToObjectPinCategory(OutputPinCategory)) + { + return true; + } + + if (FFlowPin::IsClassPinCategory(InputPinCategory) && FFlowPin::IsConvertableToClassPinCategory(OutputPinCategory)) + { + return true; + } + + if (FFlowPin::IsStructPinCategory(InputPinCategory) && FFlowPin::IsConvertableToStructPinCategory(OutputPinCategory)) + { + return true; + } + + return false; +} + +bool UFlowGraphSchema::ArePinTypesCompatible(const FEdGraphPinType& Output, const FEdGraphPinType& Input, const UClass* CallingContext, bool bIgnoreArray /*= false*/) const +{ + // NOTE - Adapted from UEdGraphSchema_K2::ArePinTypesCompatible() + + using namespace FlowGraphSchema::Private; + using namespace UE::Kismet::BlueprintTypeConversions; + + if (ArePinCategoriesEffectivelyMatching(Input.PinCategory, Output.PinCategory)) + { + const UScriptStruct* OutputStruct = Cast(Output.PinSubCategoryObject.Get()); + const UScriptStruct* InputStruct = Cast(Input.PinSubCategoryObject.Get()); + if (OutputStruct != InputStruct) + { + const bool bAreConvertibleStructs = + FStructConversionTable::Get().GetConversionFunction(OutputStruct, InputStruct).IsSet(); + + if (bAreConvertibleStructs) + { + return true; + } + } + + if ((Output.PinSubCategory == Input.PinSubCategory) + && (Output.PinSubCategoryObject == Input.PinSubCategoryObject) + && (Output.PinSubCategoryMemberReference == Input.PinSubCategoryMemberReference)) + { + // If the sub-category also matches exactly, then the pins are compatible + return true; + } + + if ((Output.PinCategory == FFlowPin::PC_Object) || (Output.PinCategory == FFlowPin::PC_Struct) || (Output.PinCategory == FFlowPin::PC_Class)) + { + // Subcategory mismatch, but the two could be castable + // Only allow a match if the input is a superclass of the output + + UStruct const* OutputObject = (Output.PinSubCategory == UEdGraphSchema_K2::PSC_Self) ? CallingContext : Cast(Output.PinSubCategoryObject.Get()); + UStruct const* InputObject = (Input.PinSubCategory == UEdGraphSchema_K2::PSC_Self) ? CallingContext : Cast(Input.PinSubCategoryObject.Get()); + + if (OutputObject && InputObject) + { + if (Output.PinCategory == FFlowPin::PC_Struct) + { + return OutputObject->IsChildOf(InputObject) && FStructUtils::TheSameLayout(OutputObject, InputObject); + } + + UClass const* OutputClass = Cast(OutputObject); + UClass const* InputClass = Cast(InputObject); + + return + (IsAuthoritativeChildOf(OutputObject, InputObject) || + (OutputClass && InputClass && ExtendedIsChildOf(OutputClass, InputClass))); + } + + return false; + } + + return false; + } + + return false; +} + const FPinConnectionResponse UFlowGraphSchema::CanCreateConnection(const UEdGraphPin* PinA, const UEdGraphPin* PinB) const { const UFlowGraphNode* OwningNodeA = Cast(PinA->GetOwningNodeUnchecked()); @@ -137,7 +436,7 @@ const FPinConnectionResponse UFlowGraphSchema::CanCreateConnection(const UEdGrap } // Make sure the pins are not on the same node - if (PinA->GetOwningNode() == PinB->GetOwningNode()) + if (OwningNodeA == OwningNodeB) { return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, TEXT("Both are on the same node")); } @@ -147,6 +446,18 @@ const FPinConnectionResponse UFlowGraphSchema::CanCreateConnection(const UEdGrap return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, TEXT("Cannot make new connections to orphaned pin")); } + FString NodeResponseMessage; + + // node can disallow the connection + if (OwningNodeA && OwningNodeA->IsConnectionDisallowed(PinA, PinB, NodeResponseMessage)) + { + return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, NodeResponseMessage); + } + if (OwningNodeB && OwningNodeB->IsConnectionDisallowed(PinB, PinA, NodeResponseMessage)) + { + return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, NodeResponseMessage); + } + // Compare the directions const UEdGraphPin* InputPin = nullptr; const UEdGraphPin* OutputPin = nullptr; @@ -156,11 +467,64 @@ const FPinConnectionResponse UFlowGraphSchema::CanCreateConnection(const UEdGrap return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, TEXT("Directions are not compatible")); } - // Break existing connections on outputs only - multiple input connections are acceptable - if (OutputPin->LinkedTo.Num() > 0) + check(InputPin); + check(OutputPin); + + // Use the owning flow node's class as the CallingContext + constexpr bool bIgnoreArray = false; + UClass* CallingContext = nullptr; + if (OwningNodeA) + { + UFlowNodeBase* FlowNodeBase = OwningNodeA->GetFlowNodeBase(); + if (FlowNodeBase) + { + CallingContext = FlowNodeBase->GetClass(); + } + } + + // Compare the pin types + const bool bArePinsCompatible = ArePinsCompatible(OutputPin, InputPin, CallingContext, bIgnoreArray); + if (!bArePinsCompatible) + { + return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, TEXT("Pins are not compatible")); + } + + FPinConnectionResponse ConnectionResponse = DetermineConnectionResponseOfCompatibleTypedPins(PinA, PinB, InputPin, OutputPin); + if (ConnectionResponse.Message.IsEmpty()) + { + ConnectionResponse.Message = FText::FromString(NodeResponseMessage); + } + else if (!NodeResponseMessage.IsEmpty()) + { + ConnectionResponse.Message = FText::Format(LOCTEXT("MultiMsgConnectionResponse", "{0} - {1}"), ConnectionResponse.Message, FText::FromString(NodeResponseMessage)); + } + + return ConnectionResponse; +} + +const FPinConnectionResponse UFlowGraphSchema::DetermineConnectionResponseOfCompatibleTypedPins(const UEdGraphPin* PinA, const UEdGraphPin* PinB, const UEdGraphPin* InputPin, const UEdGraphPin* OutputPin) const +{ + const bool bIsExistingConnection = PinA->LinkedTo.Contains(PinB); + if (bIsExistingConnection) + { + // Don't error for queries about existing connections + return FPinConnectionResponse(CONNECT_RESPONSE_MAKE, TEXT("")); + } + + checkf(!PinB->LinkedTo.Contains(PinA), TEXT("This should be caught with the bIsExistingConnection test above")); + + // Break existing connections on outputs for Exec Pins + if (FFlowPin::IsExecPinCategory(InputPin->PinType.PinCategory) && OutputPin->LinkedTo.Num() > 0) { const ECanCreateConnectionResponse ReplyBreakInputs = (OutputPin == PinA ? CONNECT_RESPONSE_BREAK_OTHERS_A : CONNECT_RESPONSE_BREAK_OTHERS_B); - return FPinConnectionResponse(ReplyBreakInputs, TEXT("Replace existing connections")); + return FPinConnectionResponse(ReplyBreakInputs, TEXT("Replace existing exec connection")); + } + + // Break existing connections on inputs for Data Pins + if (FFlowPin::IsDataPinCategory(InputPin->PinType.PinCategory) && InputPin->LinkedTo.Num() > 0) + { + const ECanCreateConnectionResponse ReplyBreakInputs = (InputPin == PinA ? CONNECT_RESPONSE_BREAK_OTHERS_A : CONNECT_RESPONSE_BREAK_OTHERS_B); + return FPinConnectionResponse(ReplyBreakInputs, TEXT("Replace existing data connection")); } return FPinConnectionResponse(CONNECT_RESPONSE_MAKE, TEXT("")); @@ -190,7 +554,8 @@ const FPinConnectionResponse UFlowGraphSchema::CanMergeNodes(const UEdGraphNode* FString ReasonString; if (FlowGraphNodeA && FlowGraphNodeB) { - if (!FlowGraphNodeB->CanAcceptSubNodeAsChild(*FlowGraphNodeA, &ReasonString)) + TSet OtherGraphNodes; + if (!FlowGraphNodeB->CanAcceptSubNodeAsChild(*FlowGraphNodeA, OtherGraphNodes, &ReasonString)) { return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, ReasonString); } @@ -224,7 +589,77 @@ bool UFlowGraphSchema::ShouldHidePinDefaultValue(UEdGraphPin* Pin) const FLinearColor UFlowGraphSchema::GetPinTypeColor(const FEdGraphPinType& PinType) const { - return FLinearColor::White; + // NOTE - Adapted from UEdGraphSchema_K2::GetPinTypeColor() + // (because we cannot directly inherit from it, but want the same color language) + + const FName& PinCategory = PinType.PinCategory; + const UGraphEditorSettings* Settings = GetDefault(); + + if (FFlowPin::IsExecPinCategory(PinCategory)) + { + return Settings->ExecutionPinTypeColor; + } + else if (PinCategory == FFlowPin::PC_Object) + { + return Settings->ObjectPinTypeColor; + } + else if (PinCategory == FFlowPin::PC_Boolean) + { + return Settings->BooleanPinTypeColor; + } + else if (PinCategory == FFlowPin::PC_Byte) + { + return Settings->BytePinTypeColor; + } + else if (PinCategory == FFlowPin::PC_Int) + { + return Settings->IntPinTypeColor; + } + else if (PinCategory == FFlowPin::PC_Int64) + { + return Settings->Int64PinTypeColor; + } + else if (PinCategory == FFlowPin::PC_Struct) + { + if (PinType.PinSubCategoryObject == VectorStruct) + { + // vector + return Settings->VectorPinTypeColor; + } + else if (PinType.PinSubCategoryObject == RotatorStruct) + { + // rotator + return Settings->RotatorPinTypeColor; + } + else if (PinType.PinSubCategoryObject == TransformStruct) + { + // transform + return Settings->TransformPinTypeColor; + } + else + { + return Settings->StructPinTypeColor; + } + } + else if (PinCategory == FFlowPin::PC_String) + { + return Settings->StringPinTypeColor; + } + else if (PinCategory == FFlowPin::PC_Text) + { + return Settings->TextPinTypeColor; + } + else if (PinCategory == FFlowPin::PC_Name) + { + return Settings->NamePinTypeColor; + } + else if (PinCategory == FFlowPin::PC_Class) + { + return Settings->ClassPinTypeColor; + } + + // Type does not have a defined color! + return Settings->DefaultPinTypeColor; } FText UFlowGraphSchema::GetPinDisplayName(const UEdGraphPin* Pin) const @@ -238,7 +673,9 @@ FText UFlowGraphSchema::GetPinDisplayName(const UEdGraphPin* Pin) const { return FText::GetEmpty(); } - if (GetDefault()->bEnforceFriendlyPinNames) // this option is only difference between this override and UEdGraphSchema::GetPinDisplayName + + // this option is only difference between this override and UEdGraphSchema::GetPinDisplayName + if (GetDefault()->bEnforceFriendlyPinNames) { ResultPinName = FText::FromString(FName::NameToDisplayString(Pin->PinName.ToString(), true)); } @@ -253,19 +690,83 @@ FText UFlowGraphSchema::GetPinDisplayName(const UEdGraphPin* Pin) const bool bShouldUseLocalizedNodeAndPinNames = false; GConfig->GetBool(TEXT("Internationalization"), TEXT("ShouldUseLocalizedNodeAndPinNames"), bShouldUseLocalizedNodeAndPinNames, GEditorSettingsIni); + if (!bShouldUseLocalizedNodeAndPinNames) { ResultPinName = FText::FromString(ResultPinName.BuildSourceString()); } } + return ResultPinName; } +void UFlowGraphSchema::ConstructBasicPinTooltip(const UEdGraphPin& Pin, const FText& PinDescription, FString& TooltipOut) const +{ + if (Pin.bWasTrashed) + { + return; + } + + constexpr bool bGeneratingDocumentation = false; + if (bGeneratingDocumentation) + { + TooltipOut = PinDescription.ToString(); + } + else + { + FFormatNamedArguments Args; + Args.Add(TEXT("PinType"), UEdGraphSchema_K2::TypeToText(Pin.PinType)); + + if (UEdGraphNode* PinNode = Pin.GetOwningNode()) + { + UEdGraphSchema_K2 const* const K2Schema = Cast(PinNode->GetSchema()); + if (ensure(K2Schema != nullptr)) // ensure that this node belongs to this schema + { + Args.Add(TEXT("DisplayName"), GetPinDisplayName(&Pin)); + Args.Add(TEXT("LineFeed1"), FText::FromString(TEXT("\n"))); + } + } + else + { + Args.Add(TEXT("DisplayName"), FText::GetEmpty()); + Args.Add(TEXT("LineFeed1"), FText::GetEmpty()); + } + + + if (!PinDescription.IsEmpty()) + { + Args.Add(TEXT("Description"), PinDescription); + Args.Add(TEXT("LineFeed2"), FText::FromString(TEXT("\n\n"))); + } + else + { + Args.Add(TEXT("Description"), FText::GetEmpty()); + Args.Add(TEXT("LineFeed2"), FText::GetEmpty()); + } + + TooltipOut = FText::Format(LOCTEXT("PinTooltip", "{DisplayName}{LineFeed1}{PinType}{LineFeed2}{Description}"), Args).ToString(); + } +} + +bool UFlowGraphSchema::CanShowDataTooltipForPin(const UEdGraphPin& Pin) const +{ + return !FFlowPin::IsExecPinCategory(Pin.PinType.PinCategory); +} + +bool UFlowGraphSchema::IsTitleBarPin(const UEdGraphPin& Pin) const +{ + return FFlowPin::IsExecPinCategory(Pin.PinType.PinCategory); +} + void UFlowGraphSchema::BreakNodeLinks(UEdGraphNode& TargetNode) const { Super::BreakNodeLinks(TargetNode); - TargetNode.GetGraph()->NotifyGraphChanged(); + UEdGraph* EdGraph = TargetNode.GetGraph(); + if (IsValid(EdGraph)) + { + EdGraph->NotifyGraphChanged(); + } } void UFlowGraphSchema::BreakPinLinks(UEdGraphPin& TargetPin, bool bSendsNodeNotification) const @@ -274,14 +775,27 @@ void UFlowGraphSchema::BreakPinLinks(UEdGraphPin& TargetPin, bool bSendsNodeNoti Super::BreakPinLinks(TargetPin, bSendsNodeNotification); + // Cache the owning node because calling Super::BreakPinLinks might release the pointer + // to the pin owning node as a side effect of notify graph changed events. + UFlowGraphNode* OwningFlowGraphNode = Cast(TargetPin.GetOwningNodeUnchecked()); + + // NOTE (gtaylor) It is possible for OwningFlowGraphNode to be null if the TargetPin has been orphaned. + if (TargetPin.bOrphanedPin) { - // this calls NotifyGraphChanged() - Cast(TargetPin.GetOwningNode())->RemoveOrphanedPin(&TargetPin); + if (OwningFlowGraphNode) + { + // this calls NotifyGraphChanged() + OwningFlowGraphNode->RemoveOrphanedPin(&TargetPin); + } } else if (bSendsNodeNotification) { - TargetPin.GetOwningNode()->GetGraph()->NotifyGraphChanged(); + UEdGraph* EdGraph = (OwningFlowGraphNode) ? OwningFlowGraphNode->GetGraph() : nullptr; + if (IsValid(EdGraph)) + { + EdGraph->NotifyGraphChanged(); + } } } @@ -297,6 +811,13 @@ TSharedPtr UFlowGraphSchema::GetCreateCommentAction() cons void UFlowGraphSchema::OnPinConnectionDoubleCicked(UEdGraphPin* PinA, UEdGraphPin* PinB, const FVector2D& GraphPosition) const { + if (!FFlowPin::IsExecPinCategory(PinA->PinType.PinCategory) || !FFlowPin::IsExecPinCategory(PinB->PinType.PinCategory)) + { + // Disallowing Reroute node creation for non-exec connections (until we have a good solution for it) + + return; + } + const FScopedTransaction Transaction(LOCTEXT("CreateFlowRerouteNodeOnWire", "Create Flow Reroute Node")); const FVector2D NodeSpacerSize(42.0f, 24.0f); @@ -593,7 +1114,10 @@ void UFlowGraphSchema::GetGraphNodeContextActions(FGraphContextMenuBuilder& Cont bool UFlowGraphSchema::IsAddOnAllowedForSelectedObjects(const TArray& SelectedObjects, const UFlowNodeAddOn* AddOnTemplate) const { - static_assert(static_cast<__underlying_type(EFlowAddOnAcceptResult)>(EFlowAddOnAcceptResult::Max) == 3, "This code may need updating if the enum values change"); + FLOW_ASSERT_ENUM_MAX(EFlowAddOnAcceptResult, 3); + + // An empty array of other addons to consider to use with CheckAcceptFlowNodeAddOnChild() below + const TArray OtherAddOns; EFlowAddOnAcceptResult CombinedResult = EFlowAddOnAcceptResult::Undetermined; @@ -611,7 +1135,7 @@ bool UFlowGraphSchema::IsAddOnAllowedForSelectedObjects(const TArray& continue; } - const EFlowAddOnAcceptResult SelectedObjectResult = FlowNodeOuter->CheckAcceptFlowNodeAddOnChild(AddOnTemplate); + const EFlowAddOnAcceptResult SelectedObjectResult = FlowNodeOuter->CheckAcceptFlowNodeAddOnChild(AddOnTemplate, OtherAddOns); CombinedResult = CombineFlowAddOnAcceptResult(SelectedObjectResult, CombinedResult); if (CombinedResult == EFlowAddOnAcceptResult::Reject) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp index 99c51b619..67af3eef1 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp @@ -3,9 +3,12 @@ #include "Graph/FlowGraphSettings.h" #include "FlowAsset.h" +#include "FlowTags.h" +#include "Graph/FlowGraphSchema.h" +#include "Types/FlowGameplayTagMapUtils.h" + #include "Algo/Unique.h" #include "Framework/Notifications/NotificationManager.h" -#include "Graph/FlowGraphSchema.h" #include "Widgets/Notifications/SNotificationList.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraphSettings) @@ -38,13 +41,6 @@ UFlowGraphSettings::UFlowGraphSettings(const FObjectInitializer& ObjectInitializ , SelectedWireColor(FLinearColor(0.984f, 0.482f, 0.010f, 1.0f)) , SelectedWireThickness(1.5f) { - NodeTitleColors.Emplace(EFlowNodeStyle::Condition, FLinearColor(1.0f, 0.62f, 0.016f, 1.0f)); - NodeTitleColors.Emplace(EFlowNodeStyle::Default, FLinearColor(-0.728f, 0.581f, 1.0f, 1.0f)); - NodeTitleColors.Emplace(EFlowNodeStyle::InOut, FLinearColor(1.0f, 0.0f, 0.008f, 1.0f)); - NodeTitleColors.Emplace(EFlowNodeStyle::Latent, FLinearColor(0.0f, 0.770f, 0.375f, 1.0f)); - NodeTitleColors.Emplace(EFlowNodeStyle::Logic, FLinearColor(1.0f, 1.0f, 1.0f, 1.0f)); - NodeTitleColors.Emplace(EFlowNodeStyle::SubGraph, FLinearColor(1.0f, 0.128f, 0.0f, 1.0f)); - NodePrefixesToRemove.Emplace("FN"); NodePrefixesToRemove.Emplace("FlowNode"); NodePrefixesToRemove.Emplace("FlowNodeAddOn"); @@ -79,15 +75,8 @@ void UFlowGraphSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyC // If NodePrefixesToRemove = {"FN", "FlowNode", "Flow"} instead, everything will be removed as expected. // - NodePrefixesToRemove.Sort(TGreater{}); - - const int32 SizeBefore = NodePrefixesToRemove.Num(); - const int32 SizeAfter = Algo::Unique(NodePrefixesToRemove); - - if (SizeBefore > SizeAfter) + if (FlowArray::TrySortAndRemoveDuplicatesFromArrayInPlace(NodePrefixesToRemove)) { - NodePrefixesToRemove.SetNum(SizeAfter); - // error notification FNotificationInfo Info(LOCTEXT("FlowGraphSettings_DuplicatePrefixError", "Added prefix already exists in array.")); Info.ExpireDuration = 3.0f; @@ -98,7 +87,87 @@ void UFlowGraphSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyC UFlowGraphSchema::UpdateGeneratedDisplayNames(); } } + else if (PropertyChangedEvent.GetMemberPropertyName() == GET_MEMBER_NAME_CHECKED(UFlowGraphSettings, NodeDisplayStyles)) + { + if (FlowArray::TrySortAndRemoveDuplicatesFromArrayInPlace(NodeDisplayStyles)) + { + // error notification + FNotificationInfo Info(LOCTEXT("FlowGraphSettings_DuplicateNodeDisplayStyleError", "Added NodeDisplayStyle already exists in array.")); + Info.ExpireDuration = 3.0f; + FSlateNotificationManager::Get().AddNotification(Info)->SetCompletionState(SNotificationItem::CS_Fail); + } + } +} + +const TMap& UFlowGraphSettings::EnsureNodeDisplayStylesMap() +{ + if (NodeDisplayStylesAuthoredTags.Num() != NodeDisplayStyles.Num()) + { + NodeDisplayStylesAuthoredTags.Reset(); + + // Create an expanded GameplayTag map that will allow the settings to be looked up by subtag + TMap UnexpandedMap; + UnexpandedMap.Reserve(NodeDisplayStyles.Num()); + + for (const FFlowNodeDisplayStyleConfig& Config : NodeDisplayStyles) + { + UnexpandedMap.Add(Config.Tag, Config); + + NodeDisplayStylesAuthoredTags.AddTag(Config.Tag); + } + + // Expand the map + NodeDisplayStylesMap.Empty(); + FlowMap::PatchGameplayTagMap(UnexpandedMap, NodeDisplayStylesMap); + } + + return NodeDisplayStylesMap; +} + +bool UFlowGraphSettings::TryAddDefaultNodeDisplayStyle(const FFlowNodeDisplayStyleConfig& StyleConfig) +{ + const int32 FoundIndex = + NodeDisplayStyles.FindLastByPredicate( + [&StyleConfig](const FFlowNodeDisplayStyleConfig& CurConfig) + { + if (CurConfig.Tag == StyleConfig.Tag) + { + return true; + } + + return false; + }); + + if (FoundIndex != INDEX_NONE) + { + // Keep the existing config + + return false; + } + + NodeDisplayStyles.Add(StyleConfig); + + return true; } + +const FLinearColor* UFlowGraphSettings::LookupNodeTitleColorForNode(const UFlowNodeBase& FlowNodeBase) +{ + if (const FLinearColor* NodeSpecificColor = NodeSpecificColors.Find(FlowNodeBase.GetClass())) + { + return NodeSpecificColor; + } + + const FGameplayTag& StyleTag = FlowNodeBase.GetNodeDisplayStyle(); + const TMap& StyleMap = EnsureNodeDisplayStylesMap(); + + if (const FFlowNodeDisplayStyleConfig* Config = FlowMap::TryLookupGameplayTagKey(StyleTag, StyleMap, TAG_Flow_NodeDisplayStyle)) + { + return &Config->TitleColor; + } + + return nullptr; +} + #endif #undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp b/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp index d6ed30305..5a91c24ba 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp @@ -48,7 +48,11 @@ FString FFlowGraphUtils::RemovePrefixFromNodeText(const FText& Source) Prefix = FName::NameToDisplayString(Prefix, false); if (SourceString.StartsWith(Prefix)) { +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 4 + SourceString.MidInline(Prefix.Len(), MAX_int32, EAllowShrinking::No); +#else SourceString.MidInline(Prefix.Len(), MAX_int32, false); +#endif SourceString = SourceString.TrimStart(); } } diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 80502e847..28736bcf2 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -37,6 +37,7 @@ UFlowGraphNode::UFlowGraphNode(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , NodeInstance(nullptr) , bBlueprintCompilationPending(false) + , bIsReconstructingNode(false) , bNeedsFullReconstruction(false) { OrphanedPinSaveMode = ESaveOrphanPinMode::SaveAll; @@ -79,8 +80,6 @@ void UFlowGraphNode::PostLoad() NodeInstance->FixNode(this); // fix already created nodes SubscribeToExternalChanges(); } - - ReconstructNode(); } void UFlowGraphNode::PostDuplicate(bool bDuplicateForPIE) @@ -159,7 +158,7 @@ void UFlowGraphNode::PostCopyNode() // Make sure this NodeInstance is owned by the FlowAsset it's being pasted into if (NodeInstance) { - UFlowAsset* FlowAsset = CastChecked(GetGraph())->GetFlowAsset(); + UFlowAsset* FlowAsset = GetFlowAsset(); if (NodeInstance->GetOuter() != FlowAsset) { @@ -184,6 +183,11 @@ void UFlowGraphNode::SubscribeToExternalChanges() void UFlowGraphNode::OnExternalChange() { + if (bIsReconstructingNode) + { + return; + } + // Do not create transaction here, since this function triggers from modifying UFlowNode's property, which itself already made inside of transaction. Modify(); @@ -276,6 +280,24 @@ void UFlowGraphNode::InsertNewNode(UEdGraphPin* FromPin, UEdGraphPin* NewLinkPin void UFlowGraphNode::ReconstructNode() { + if (const UFlowGraph* FlowGraph = GetFlowGraph()) + { + // If the graph is locked, we shouldn't reconstruct nodes + // (all nodes will all be reconstructed when the graph is unlocked) + + if (FlowGraph->IsLocked()) + { + return; + } + } + + if (bIsReconstructingNode) + { + return; + } + + bIsReconstructingNode = true; + // Store old pins TArray OldPins(Pins); @@ -284,11 +306,21 @@ void UFlowGraphNode::ReconstructNode() InputPins.Reset(); OutputPins.Reset(); - // Recreate pins - if (SupportsContextPins() && (NodeInstance->CanRefreshContextPinsOnLoad() || bNeedsFullReconstruction)) + bool bChangedAutoFlowPins = false; + + // Harvest the auto-generated pins before refreshing context pins + if (UFlowNode* FlowNode = Cast(NodeInstance)) { - RefreshContextPins(false); + if (UFlowAsset* FlowAsset = NodeInstance->GetFlowAsset()) + { + bChangedAutoFlowPins = FlowAsset->TryUpdateManagedFlowPinsForNode(*FlowNode); + } } + + // Recreate pins + constexpr bool bReconstructNode = false; + RefreshContextPins(bReconstructNode); + AllocateDefaultPins(); RewireOldPinsToNewPins(OldPins); @@ -301,6 +333,7 @@ void UFlowGraphNode::ReconstructNode() } bNeedsFullReconstruction = false; + bIsReconstructingNode = false; } void UFlowGraphNode::AllocateDefaultPins() @@ -376,7 +409,11 @@ void UFlowGraphNode::RewireOldPinsToNewPins(TArray& InOldPins) OldPin->bOrphanedPin = true; OldPin->bNotConnectable = true; OrphanedOldPins.Add(OldPin); +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 4 + InOldPins.RemoveAt(OldPinIndex, 1, EAllowShrinking::No); +#else InOldPins.RemoveAt(OldPinIndex, 1, false); +#endif } } @@ -606,12 +643,7 @@ FLinearColor UFlowGraphNode::GetNodeTitleColor() const return DynamicColor; } - UFlowGraphSettings* GraphSettings = UFlowGraphSettings::Get(); - if (const FLinearColor* NodeSpecificColor = GraphSettings->NodeSpecificColors.Find(NodeInstance->GetClass())) - { - return *NodeSpecificColor; - } - if (const FLinearColor* StyleColor = GraphSettings->NodeTitleColors.Find(NodeInstance->GetNodeStyle())) + if (const FLinearColor* StyleColor = UFlowGraphSettings::Get()->LookupNodeTitleColorForNode(*NodeInstance)) { return *StyleColor; } @@ -676,7 +708,7 @@ FString UFlowGraphNode::GetStatusString() const { if (const UFlowNode* InspectedInstance = FlowNode->GetInspectedInstance()) { - return InspectedInstance->GetStatusString(); + return InspectedInstance->GetStatusStringForNodeAndAddOns(); } } @@ -774,7 +806,12 @@ void UFlowGraphNode::CreateInputPin(const FFlowPin& FlowPin, const int32 Index / return; } - const FEdGraphPinType PinType = FEdGraphPinType(UEdGraphSchema_K2::PC_Exec, FName(NAME_None), nullptr, EPinContainerType::None, false, FEdGraphTerminalType()); + const FName PinCategory = GetPinCategoryFromFlowPin(FlowPin); + const FName PinSubCategory = NAME_None; + UObject* PinSubCategoryObject = FlowPin.GetPinSubCategoryObject().Get(); + constexpr bool bIsReference = false; + + const FEdGraphPinType PinType = FEdGraphPinType(PinCategory, PinSubCategory, PinSubCategoryObject, EPinContainerType::None, bIsReference, FEdGraphTerminalType()); UEdGraphPin* NewPin = CreatePin(EGPD_Input, PinType, FlowPin.PinName, Index); check(NewPin); @@ -796,7 +833,12 @@ void UFlowGraphNode::CreateOutputPin(const FFlowPin& FlowPin, const int32 Index return; } - const FEdGraphPinType PinType = FEdGraphPinType(UEdGraphSchema_K2::PC_Exec, FName(NAME_None), nullptr, EPinContainerType::None, false, FEdGraphTerminalType()); + const FName PinCategory = GetPinCategoryFromFlowPin(FlowPin); + const FName PinSubCategory = NAME_None; + UObject* PinSubCategoryObject = FlowPin.GetPinSubCategoryObject().Get(); + constexpr bool bIsReference = false; + + const FEdGraphPinType PinType = FEdGraphPinType(PinCategory, PinSubCategory, PinSubCategoryObject, EPinContainerType::None, bIsReference, FEdGraphTerminalType()); UEdGraphPin* NewPin = CreatePin(EGPD_Output, PinType, FlowPin.PinName, Index); check(NewPin); @@ -947,8 +989,25 @@ void UFlowGraphNode::RefreshContextPins(const bool bReconstructNode) return; } - const bool bShouldConsiderRefreshingContextPins = SupportsContextPins() || bHasContextPins; - if (!bShouldConsiderRefreshingContextPins) + // Update the auto-generated pins before refreshing context pins + bool bChangedAutoFlowPins = false; + if (UFlowAsset* FlowAsset = NodeInstance->GetFlowAsset()) + { + bChangedAutoFlowPins = FlowAsset->TryUpdateManagedFlowPinsForNode(*FlowNode); + } + + bool bIsLoad = false; + if (UFlowGraph* FlowGraph = GetFlowGraph()) + { + bIsLoad = FlowGraph->IsLoadingGraph(); + } + + // Confirm that we should be refreshing context pins + const bool bIsAllowedToRefreshPins = !bIsLoad || NodeInstance->CanRefreshContextPinsOnLoad(); + const bool bShouldConsiderRefreshingContextPins = bIsAllowedToRefreshPins && (SupportsContextPins() || bHasContextPins); + const bool bShouldRefreshContextPins = bShouldConsiderRefreshingContextPins || bChangedAutoFlowPins || bNeedsFullReconstruction; + + if (!bShouldRefreshContextPins) { return; } @@ -962,26 +1021,29 @@ void UFlowGraphNode::RefreshContextPins(const bool bReconstructNode) // Skip the rest if the node went from no ContextPins to no ContextPins const bool bMaintainedNoContextPins = !bPrevHasContextPins && !bHasContextPins; - if (!bMaintainedNoContextPins) + if (bMaintainedNoContextPins || !HavePinsChanged()) { - const FScopedTransaction Transaction(LOCTEXT("RefreshContextPins", "Refresh Context Pins")); - Modify(); + // We dont have contextual pins to account for; or the contextual pins have not changed. We can skip now. + return; + } + + const FScopedTransaction Transaction(LOCTEXT("RefreshContextPins", "Refresh Context Pins")); + Modify(); - const UFlowNode* NodeDefaults = FlowNode->GetClass()->GetDefaultObject(); + const UFlowNode* NodeDefaults = FlowNode->GetClass()->GetDefaultObject(); - // recreate inputs - FlowNode->InputPins = NodeDefaults->InputPins; - FlowNode->AddInputPins(ContextInputs); + // recreate inputs + FlowNode->InputPins = NodeDefaults->InputPins; + FlowNode->AddInputPins(ContextInputs); - // recreate outputs - FlowNode->OutputPins = NodeDefaults->OutputPins; - FlowNode->AddOutputPins(ContextOutputs); + // recreate outputs + FlowNode->OutputPins = NodeDefaults->OutputPins; + FlowNode->AddOutputPins(ContextOutputs); - if (bReconstructNode && !bMaintainedNoContextPins) - { - ReconstructNode(); - GetGraph()->NotifyGraphChanged(); - } + if (bReconstructNode) + { + ReconstructNode(); + GetGraph()->NotifyGraphChanged(); } } @@ -1031,6 +1093,11 @@ void UFlowGraphNode::GetPinHoverText(const UEdGraphPin& Pin, FString& HoverTextO } } +const FName& UFlowGraphNode::GetPinCategoryFromFlowPin(const FFlowPin& FlowPin) const +{ + return FFlowPin::GetPinCategoryFromPinType(FlowPin.GetPinType()); +} + void UFlowGraphNode::OnInputTriggered(const int32 Index) { if (InputPins.IsValidIndex(Index) && PinBreakpoints.Contains(InputPins[Index])) @@ -1176,15 +1243,72 @@ void UFlowGraphNode::PostEditUndo() } } -void UFlowGraphNode::LogError(const FString& MessageToLog, const UFlowNodeBase* FlowNodeBase) const +UFlowAsset* UFlowGraphNode::GetFlowAsset() const { if (UFlowGraph* FlowGraph = GetFlowGraph()) { if (UFlowAsset* FlowAsset = FlowGraph->GetFlowAsset()) { - FlowAsset->LogError(MessageToLog, FlowNodeBase); + return FlowAsset; } } + + return nullptr; +} + +void UFlowGraphNode::LogError(const FString& MessageToLog, const UFlowNodeBase* FlowNodeBase) const +{ + if (UFlowAsset* FlowAsset = GetFlowAsset()) + { + FlowAsset->LogError(MessageToLog, FlowNodeBase); + } +} + +bool UFlowGraphNode::HavePinsChanged() +{ + const UFlowNode* FlowNodeInstance = Cast(NodeInstance); + if (!IsValid(FlowNodeInstance)) + { + // default to having changed because we don't have a way to confirm that the pins have remained intact. + return true; + } + + // Get the pins that are part of the node itself. We use the CDO because it inherently knows about the built-in + // pins for this node. + const UFlowNode* FlowNodeCDO = FlowNodeInstance->GetClass()->GetDefaultObject(); + check(IsValid(FlowNodeCDO)); + + TArray AllFlowPins = FlowNodeCDO->GetInputPins(); + AllFlowPins.Append(FlowNodeCDO->GetOutputPins()); + + // Get the contextual pins for the underlying flow node instance. + AllFlowPins.Append(FlowNodeInstance->GetContextInputs()); + AllFlowPins.Append(FlowNodeInstance->GetContextOutputs()); + + if (Pins.Num() != AllFlowPins.Num()) + { + // There is a different number of EdGraphPins and Flow Node pins; something changed. + return true; + } + + TArray PinNames; + for (const UEdGraphPin* Pin : Pins) + { + PinNames.Add(Pin->PinName); + } + + for (const FFlowPin& Pin : AllFlowPins) + { + if (!PinNames.Contains(Pin.PinName)) + { + // Could not match the pin from the flow node with any of the EdPins array. + // we have a mismatch between the ed graph pins and the flow node, something changed. + return true; + } + } + + // Nothing changed + return false; } void UFlowGraphNode::ResetNodeOwner() @@ -1569,6 +1693,49 @@ bool UFlowGraphNode::HasErrors() const return ErrorMessage.Len() > 0 || !IsValid(NodeInstance); } +void UFlowGraphNode::ValidateGraphNode(FFlowMessageLog& MessageLog) const +{ + // Verify that all input data pin connections are legal + + bool bAppendedErrors = false; + + if (!NodeInstance) + { + // Missing the node instance! + + MessageLog.Error(TEXT("FlowGraphNode is missing its UFlowNode instance!"), nullptr); + + return; + } + + FFlowMessageLog& ValidationLog = NodeInstance->ValidationLog; + + const UFlowGraphSchema* Schema = CastChecked(GetSchema()); + + for (const UEdGraphPin* EdGraphPin : InputPins) + { + if (!FFlowPin::IsDataPinCategory(EdGraphPin->PinType.PinCategory)) + { + continue; + } + + if (!EdGraphPin->HasAnyConnections()) + { + continue; + } + + for (UEdGraphPin* const ConnectedPin : EdGraphPin->LinkedTo) + { + const FPinConnectionResponse Response = Schema->CanCreateConnection(ConnectedPin, EdGraphPin); + + if (!Response.CanSafeConnect()) + { + MessageLog.Error(*FString::Printf(TEXT("Pin %s has invalid connection: %s"), *EdGraphPin->GetName(), *Response.Message.ToString()), NodeInstance); + } + } + } +} + bool UFlowGraphNode::IsAncestorNode(const UFlowGraphNode& OtherNode) const { const UFlowGraphNode* CurParentNode = ParentNode; @@ -1585,9 +1752,9 @@ bool UFlowGraphNode::IsAncestorNode(const UFlowGraphNode& OtherNode) const return false; } -bool UFlowGraphNode::CanAcceptSubNodeAsChild(const UFlowGraphNode& OtherSubNode, FString* OutReasonString) const +bool UFlowGraphNode::CanAcceptSubNodeAsChild(const UFlowGraphNode& SubNodeToConsider, const TSet& AllRootSubNodesToPaste, FString* OutReasonString) const { - const UFlowNodeBase* OtherFlowNodeSubNode = OtherSubNode.NodeInstance; + const UFlowNodeBase* OtherFlowNodeSubNode = SubNodeToConsider.NodeInstance; if (!OtherFlowNodeSubNode) { @@ -1599,7 +1766,7 @@ bool UFlowGraphNode::CanAcceptSubNodeAsChild(const UFlowGraphNode& OtherSubNode, return false; } - if (IsAncestorNode(OtherSubNode)) + if (IsAncestorNode(SubNodeToConsider)) { if (OutReasonString) { @@ -1610,16 +1777,30 @@ bool UFlowGraphNode::CanAcceptSubNodeAsChild(const UFlowGraphNode& OtherSubNode, } check(OtherFlowNodeSubNode); - const UFlowNodeAddOn* OtherAddOnTemplate = Cast(OtherFlowNodeSubNode); + const UFlowNodeAddOn* AddOnToConsider = Cast(OtherFlowNodeSubNode); + + // Build the array of other root AddOns that will also be added as children as an atomic operation (eg, multi-paste) + TArray OtherAddOnsToPaste; + + for (TSet::TConstIterator It(AllRootSubNodesToPaste); It; ++It) + { + const UFlowGraphNode* NodeToPaste = Cast(*It); + UFlowNodeAddOn* AddOnToPaste = Cast(NodeToPaste->NodeInstance); + + if (IsValid(AddOnToPaste) && AddOnToPaste != AddOnToConsider) + { + OtherAddOnsToPaste.Add(AddOnToPaste); + } + } const UFlowNodeBase* ThisFlowNodeBase = NodeInstance; - const EFlowAddOnAcceptResult AcceptResult = ThisFlowNodeBase->CheckAcceptFlowNodeAddOnChild(OtherAddOnTemplate); + const EFlowAddOnAcceptResult AcceptResult = ThisFlowNodeBase->CheckAcceptFlowNodeAddOnChild(AddOnToConsider, OtherAddOnsToPaste); // Undetermined and Reject both count as Rejection, only TentativeAccept is an 'accept' result if (AcceptResult == EFlowAddOnAcceptResult::TentativeAccept) { - static_assert(static_cast<__underlying_type(EFlowAddOnAcceptResult)>(EFlowAddOnAcceptResult::Max) == 3, "This code may need updating if the enum values change"); + FLOW_ASSERT_ENUM_MAX(EFlowAddOnAcceptResult, 3); return true; } diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp index d17c6d543..ffd0d1a03 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp @@ -734,35 +734,6 @@ TSharedPtr SFlowGraphNode::GetEnabledStateWidget() const return TSharedPtr(); } -void SFlowGraphNode::CreateStandardPinWidget(UEdGraphPin* Pin) -{ - const TSharedPtr NewPin = SNew(SFlowGraphPinExec, Pin); - - UFlowNode* FlowNode = Cast(FlowGraphNode->GetFlowNodeBase()); - - if (!UFlowGraphSettings::Get()->bShowDefaultPinNames && IsValid(FlowNode)) - { - if (Pin->Direction == EGPD_Input) - { - // Pin array can have pins with name None, which will not be created. We need to check if array have only one valid pin - if (ValidPinsCount(FlowNode->GetInputPins()) == 1 && Pin->PinName == UFlowNode::DefaultInputPin.PinName) - { - NewPin->SetShowLabel(false); - } - } - else - { - // Pin array can have pins with name None, which will not be created. We need to check if array have only one valid pin - if (ValidPinsCount(FlowNode->GetOutputPins()) == 1 && Pin->PinName == UFlowNode::DefaultOutputPin.PinName) - { - NewPin->SetShowLabel(false); - } - } - } - - this->AddPin(NewPin.ToSharedRef()); -} - END_SLATE_FUNCTION_BUILD_OPTIMIZATION TSharedPtr SFlowGraphNode::GetComplexTooltip() @@ -1175,17 +1146,23 @@ FReply SFlowGraphNode::OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& bool SFlowGraphNode::ShouldDropDraggedNodesAsSubNodes(const TArray>& DraggedNodes, UFlowGraphNode* DropTargetNode) const { + TSet DraggedFlowGraphNodes; for (int32 Idx = 0; Idx < DraggedNodes.Num(); Idx++) { UFlowGraphNode* DraggedNode = Cast(DraggedNodes[Idx]->GetNodeObj()); - if (!DraggedNode) + if (IsValid(DraggedNode)) { - continue; + DraggedFlowGraphNodes.Add(DraggedNode); } + } + + for (TSet::TConstIterator It(DraggedFlowGraphNodes); It; ++It) + { + const UFlowGraphNode* DraggedNode = Cast(*It); // Check if all of the dragged nodes can be stopped as a subnode // (if not ALL, then we cannot drop ANY of them) - const bool bCanDropDraggedNodeAsSubNode = DropTargetNode->CanAcceptSubNodeAsChild(*DraggedNode); + const bool bCanDropDraggedNodeAsSubNode = DropTargetNode->CanAcceptSubNodeAsChild(*DraggedNode, DraggedFlowGraphNodes); if (!bCanDropDraggedNodeAsSubNode) { @@ -1237,18 +1214,4 @@ void SFlowGraphNode::SetOwner(const TSharedRef& OwnerPanel) } } -int32 SFlowGraphNode::ValidPinsCount(const TArray& Pins) -{ - int32 Count = 0; - for (const FFlowPin& Pin : Pins) - { - if (Pin.IsValid()) - { - Count += 1; - } - } - - return Count; -} - #undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/Utils/SLevelEditorFlow.cpp b/Source/FlowEditor/Private/Utils/SLevelEditorFlow.cpp index 3fbf2f9a9..f9dd02438 100644 --- a/Source/FlowEditor/Private/Utils/SLevelEditorFlow.cpp +++ b/Source/FlowEditor/Private/Utils/SLevelEditorFlow.cpp @@ -3,6 +3,7 @@ #include "Utils/SLevelEditorFlow.h" #include "FlowAsset.h" #include "FlowComponent.h" +#include "Runtime/Launch/Resources/Version.h" #if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 3 #include "FlowWorldSettings.h" @@ -13,7 +14,6 @@ #include "Editor.h" #include "GameFramework/WorldSettings.h" #include "PropertyCustomizationHelpers.h" -#include "Runtime/Launch/Resources/Version.h" #define LOCTEXT_NAMESPACE "SLevelEditorFlow" diff --git a/Source/FlowEditor/Public/Asset/FlowObjectDiff.h b/Source/FlowEditor/Public/Asset/FlowObjectDiff.h index 7c2d22c65..8af6a47a3 100644 --- a/Source/FlowEditor/Public/Asset/FlowObjectDiff.h +++ b/Source/FlowEditor/Public/Asset/FlowObjectDiff.h @@ -43,6 +43,8 @@ class FLOWEDITOR_API FFlowObjectDiff : public TSharedFromThis void InitializeDetailsDiffFromNode(UEdGraphNode* Node, const UObject* Object, const FFlowGraphToDiff& GraphToDiff); public: + //the tree entry for this diff object, which can be the parent tree node to other changes such as property changes, + //added/removed add-ons, moves or comments. TSharedPtr DiffTreeEntry; TSharedPtr DiffResult; diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinPropertyCustomizationBase.h b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinPropertyCustomizationBase.h new file mode 100644 index 000000000..f29ee10c4 --- /dev/null +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinPropertyCustomizationBase.h @@ -0,0 +1,24 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h" + +// Base class customization for most FFlow*DataPinProperty structs +class FFlowDataPinPropertyCustomizationBase : public IFlowExtendedPropertyTypeCustomization +{ + typedef IFlowExtendedPropertyTypeCustomization Super; + +protected: + + virtual void CustomizeHeader(TSharedRef InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; + virtual void CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; +}; + +// Template to create customization classes for FFlow*DataPinProperty structs +template +class TFlowDataPinPropertyCustomization : public FFlowDataPinPropertyCustomizationBase +{ +public: + static TSharedRef MakeInstance() { return MakeShareable(new TFlowDataPinPropertyCustomization()); } +}; diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinPropertyCustomizations.h b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinPropertyCustomizations.h new file mode 100644 index 000000000..e696996b5 --- /dev/null +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinPropertyCustomizations.h @@ -0,0 +1,32 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "FlowDataPinPropertyCustomizationBase.h" + +#include "Types/FlowDataPinProperties.h" + +// Consider implementing details customization... for every EFlowPinType +FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); + +typedef TFlowDataPinPropertyCustomization FFlowDataPinOutputProperty_BoolCustomization; +typedef TFlowDataPinPropertyCustomization FFlowDataPinOutputProperty_Int64Customization; +typedef TFlowDataPinPropertyCustomization FFlowDataPinOutputProperty_Int32Customization; +typedef TFlowDataPinPropertyCustomization FFlowDataPinOutputProperty_DoubleCustomization; +typedef TFlowDataPinPropertyCustomization FFlowDataPinOutputProperty_FloatCustomization; +typedef TFlowDataPinPropertyCustomization FFlowDataPinOutputProperty_NameCustomization; +typedef TFlowDataPinPropertyCustomization FFlowDataPinOutputProperty_StringCustomization; +typedef TFlowDataPinPropertyCustomization FFlowDataPinOutputProperty_TextCustomization; + +typedef TFlowDataPinPropertyCustomization FFlowDataPinInputProperty_BoolCustomization; +typedef TFlowDataPinPropertyCustomization FFlowDataPinInputProperty_Int64Customization; +typedef TFlowDataPinPropertyCustomization FFlowDataPinInputProperty_Int32Customization; +typedef TFlowDataPinPropertyCustomization FFlowDataPinInputProperty_DoubleCustomization; +typedef TFlowDataPinPropertyCustomization FFlowDataPinInputProperty_FloatCustomization; +typedef TFlowDataPinPropertyCustomization FFlowDataPinInputProperty_NameCustomization; +typedef TFlowDataPinPropertyCustomization FFlowDataPinInputProperty_StringCustomization; +typedef TFlowDataPinPropertyCustomization FFlowDataPinInputProperty_TextCustomization; + +// NOTE (gtaylor) Enum is defined separately, because it's quite a bit more complex +// NOTE (gtaylor) Class, Object are also defined separately as they are also more complex +// NOTE (gtaylor) BaseStruct types like FVector don't customize well using this technique, so I am leaving their default details handler in-place \ No newline at end of file diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinProperty_ClassCustomization.h b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinProperty_ClassCustomization.h new file mode 100644 index 000000000..eb8a25a90 --- /dev/null +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinProperty_ClassCustomization.h @@ -0,0 +1,52 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h" + +#include "Types/FlowDataPinProperties.h" + +class SClassPropertyEntryBox; + +class FFlowDataPinProperty_ClassCustomizationBase : public IFlowExtendedPropertyTypeCustomization +{ +private: + typedef IFlowExtendedPropertyTypeCustomization Super; + +protected: + + //~Begin IPropertyTypeCustomization + virtual void CustomizeHeader(TSharedRef InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; + virtual void CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; + //~End IPropertyTypeCustomization + + // Accessor to return the actual struct being edited + FORCEINLINE FFlowDataPinOutputProperty_Class* GetFlowDataPinClassProperty() const + { + return IFlowExtendedPropertyTypeCustomization::TryGetTypedStructValue(StructPropertyHandle); + } + + UClass* DeriveBestClassFilter() const; + UClass* BuildMetaClass() const; + + void TrySetClassFilterFromMetaData(); + + void OnClassFilterChanged(); + const UClass* OnGetClass() const; + void OnSetClass(const UClass* NewClass); + + mutable TWeakObjectPtr CachedClassPtr; + mutable TWeakObjectPtr CachedMetaClassPtr; +}; + +// Details customization for FFlowDataPinOutputProperty_Class/FFlowDataPinInputProperty_Class +template +class TFlowDataPinProperty_ClassCustomization : public FFlowDataPinProperty_ClassCustomizationBase +{ +public: + static TSharedRef MakeInstance() { return MakeShareable(new TFlowDataPinProperty_ClassCustomization()); } +}; + +// Details customization for Class FFlowDataPinProperties +typedef TFlowDataPinProperty_ClassCustomization FFlowDataPinOutputProperty_ClassCustomization; +typedef TFlowDataPinProperty_ClassCustomization FFlowDataPinInputProperty_ClassCustomization; diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinProperty_EnumCustomization.h b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinProperty_EnumCustomization.h new file mode 100644 index 000000000..06ffcaa71 --- /dev/null +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinProperty_EnumCustomization.h @@ -0,0 +1,53 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "UnrealExtensions/IFlowCuratedNamePropertyCustomization.h" + +#include "Types/FlowDataPinProperties.h" + +// NOTE (gtaylor) this is nearly identical to AI Flow - FConfigurableEnumPropertyCustomization, can we combine them? + +class FFlowDataPinProperty_EnumCustomizationBase : public IFlowCuratedNamePropertyCustomization +{ +private: + typedef IFlowCuratedNamePropertyCustomization Super; + +protected: + + //~Begin IPropertyTypeCustomization + virtual void CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; + //~End IPropertyTypeCustomization + + //~Begin ICuratedNamePropertyCustomization + virtual TSharedPtr GetCuratedNamePropertyHandle() const override; + virtual void SetCuratedName(const FName& NewName) override; + virtual bool TryGetCuratedName(FName& OutName) const override; + virtual TArray GetCuratedNameOptions() const override; + virtual bool AllowNameNoneIfOtherOptionsExist() const override { return false; } + //~End ICuratedNamePropertyCustomization + + // Accessor to return the actual struct being edited + FORCEINLINE FFlowDataPinOutputProperty_Enum* GetFlowDataPinEnumProperty() const + { + return IFlowExtendedPropertyTypeCustomization::TryGetTypedStructValue(StructPropertyHandle); + } + + void OnEnumNameChanged(); + + const UEnum* GetEnumClass() const; + + static TArray GetEnumValues(const UEnum& Enum); +}; + +// Details customization for FFlowDataPinOutputProperty_Enum +template +class TFlowDataPinProperty_EnumCustomization : public FFlowDataPinProperty_EnumCustomizationBase +{ +public: + static TSharedRef MakeInstance() { return MakeShareable(new TFlowDataPinProperty_EnumCustomization()); } +}; + +// Details customization for enum FFlowDataPinProperties +typedef TFlowDataPinProperty_EnumCustomization FFlowDataPinOutputProperty_EnumCustomization; +typedef TFlowDataPinProperty_EnumCustomization FFlowDataPinInputProperty_EnumCustomization; diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinProperty_ObjectCustomization.h b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinProperty_ObjectCustomization.h new file mode 100644 index 000000000..2a1fffdec --- /dev/null +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinProperty_ObjectCustomization.h @@ -0,0 +1,48 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h" + +#include "Types/FlowDataPinProperties.h" + +class FFlowDataPinProperty_ObjectCustomizationBase : public IFlowExtendedPropertyTypeCustomization +{ +private: + typedef IFlowExtendedPropertyTypeCustomization Super; + +protected: + + //~Begin IPropertyTypeCustomization + virtual void CustomizeHeader(TSharedRef InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; + virtual void CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; + //~End IPropertyTypeCustomization + + // Accessor to return the actual struct being edited + FORCEINLINE FFlowDataPinOutputProperty_Object* GetFlowDataPinObjectProperty() const + { + return IFlowExtendedPropertyTypeCustomization::TryGetTypedStructValue(StructPropertyHandle); + } + + UClass* DeriveBestClassFilter() const; + UClass* BuildMetaClass() const; + + void TrySetClassFilterFromMetaData(); + + void OnClassFilterChanged(); + void OnObjectValueChanged(); + + mutable TWeakObjectPtr CachedMetaClassPtr; +}; + +// Details customization for FFlowDataPinOutputProperty_Object/FFlowDataPinInputProperty_Object +template +class TFlowDataPinProperty_ObjectCustomization : public FFlowDataPinProperty_ObjectCustomizationBase +{ +public: + static TSharedRef MakeInstance() { return MakeShareable(new TFlowDataPinProperty_ObjectCustomization()); } +}; + +// Details customization for Object FFlowDataPinProperties +typedef TFlowDataPinProperty_ObjectCustomization FFlowDataPinOutputProperty_ObjectCustomization; +typedef TFlowDataPinProperty_ObjectCustomization FFlowDataPinInputProperty_ObjectCustomization; diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowNamedDataPinOutputPropertyCustomization.h b/Source/FlowEditor/Public/DetailCustomizations/FlowNamedDataPinOutputPropertyCustomization.h new file mode 100644 index 000000000..f24e3192e --- /dev/null +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowNamedDataPinOutputPropertyCustomization.h @@ -0,0 +1,20 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h" + +#include "Types/FlowDataPinProperties.h" + +// Details customization for FFlowPin +class FFlowNamedDataPinOutputPropertyCustomization : public IFlowExtendedPropertyTypeCustomization +{ + typedef IFlowExtendedPropertyTypeCustomization Super; + +public: + static TSharedRef MakeInstance() { return MakeShareable(new FFlowNamedDataPinOutputPropertyCustomization()); } + +protected: + + virtual FText BuildHeaderText() const override; +}; diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowPinCustomization.h b/Source/FlowEditor/Public/DetailCustomizations/FlowPinCustomization.h new file mode 100644 index 000000000..81fdde281 --- /dev/null +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowPinCustomization.h @@ -0,0 +1,30 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h" + +#include "Types/FlowDataPinProperties.h" + +// Details customization for FFlowPin +class FFlowPinCustomization : public IFlowExtendedPropertyTypeCustomization +{ + typedef IFlowExtendedPropertyTypeCustomization Super; + +public: + static TSharedRef MakeInstance() { return MakeShareable(new FFlowPinCustomization()); } + + virtual void CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; + +protected: + + // Accessor to return the actual struct being edited + FORCEINLINE FFlowPin* GetFlowPin() const + { + return IFlowExtendedPropertyTypeCustomization::TryGetTypedStructValue(StructPropertyHandle); + } + + void OnChildPropertyValueChanged(); + + virtual FText BuildHeaderText() const override; +}; diff --git a/Source/FlowEditor/Public/FlowEditorModule.h b/Source/FlowEditor/Public/FlowEditorModule.h index 55040f8ba..ad3a15f7e 100644 --- a/Source/FlowEditor/Public/FlowEditorModule.h +++ b/Source/FlowEditor/Public/FlowEditorModule.h @@ -30,6 +30,8 @@ class FLOWEDITOR_API FFlowEditorModule : public IModuleInterface virtual void ShutdownModule() override; private: + void TrySetFlowNodeDisplayStyleDefaults() const; + void RegisterAssets(); void UnregisterAssets(); diff --git a/Source/FlowEditor/Public/Graph/FlowGraph.h b/Source/FlowEditor/Public/Graph/FlowGraph.h index 3683531ed..bfbb14311 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraph.h +++ b/Source/FlowEditor/Public/Graph/FlowGraph.h @@ -46,7 +46,9 @@ class FLOWEDITOR_API UFlowGraph : public UEdGraph virtual void OnNodeInstanceRemoved(UObject* NodeInstance); UEdGraphPin* FindGraphNodePin(UEdGraphNode* Node, EEdGraphPinDirection Dir); - + + void ValidateAsset(FFlowMessageLog& MessageLog); + public: virtual void OnCreated(); @@ -71,6 +73,8 @@ class FLOWEDITOR_API UFlowGraph : public UEdGraph void LockUpdates(); void UnlockUpdates(); + bool IsLoadingGraph() const { return bIsLoadingGraph; } + protected: static void RecursivelySetupAllFlowGraphNodesForEditing(UFlowGraphNode& FromFlowGraphNode); void RecursivelyRefreshAddOns(UFlowGraphNode& FromFlowGraphNode); @@ -88,4 +92,7 @@ class FLOWEDITOR_API UFlowGraph : public UEdGraph * flag allows freezing update during heavy changes like pasting new nodes */ uint32 bLockUpdates : 1; + + // is currently loading the flow graph (used to suppress some work during load) + uint32 bIsLoadingGraph : 1; }; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h index 24881c1de..43d36e9a5 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h @@ -79,6 +79,10 @@ class FLOWEDITOR_API SFlowGraphEditor : public SGraphEditor virtual void PasteNodes(); + bool CanPasteNodesAsSubNodes(const TSet& NodesToPaste, const UFlowGraphNode& PasteTargetNode) const; + TSet ImportNodesToPasteFromClipboard(UFlowGraph& FlowGraph, FString& OutTextToImport) const; + TArray DerivePasteTargetNodesFromSelectedNodes() const; + public: virtual void PasteNodesHere(const FVector2D& Location); virtual bool CanPasteNodes() const; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphPinFactory.h b/Source/FlowEditor/Public/Graph/FlowGraphPinFactory.h new file mode 100644 index 000000000..23f748d66 --- /dev/null +++ b/Source/FlowEditor/Public/Graph/FlowGraphPinFactory.h @@ -0,0 +1,30 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "EdGraphSchema_K2.h" +#include "EdGraphUtilities.h" + +#include "FlowGraphPinFactory.generated.h" + +struct FFlowPin; + +class FFlowGraphPinFactory : public FGraphPanelPinFactory +{ +public: + // FGraphPanelPinFactory interface + virtual TSharedPtr CreatePin(class UEdGraphPin* InPin) const override; + // -- + + static int32 GatherValidPinsCount(const TArray& Pins); +}; + +// Thin subclass of UEdGraphSchema_K2 to gain access to PC_* defines so we can assert their values +UCLASS() +class UFlowK2SchemaSubclassForAccess : public UEdGraphSchema_K2 +{ + GENERATED_BODY() + +public: + static void AssertPinCategoryNames(); +}; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h index 3c878e1b1..b513c679b 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h @@ -29,6 +29,11 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema static TMap BlueprintFlowNodeAddOns; static TMap, TSubclassOf> GraphNodesByFlowNodes; + // cached pointers to struct types + static const UScriptStruct* VectorStruct; + static const UScriptStruct* RotatorStruct; + static const UScriptStruct* TransformStruct; + static bool bBlueprintCompilationPending; public: @@ -52,14 +57,45 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema virtual bool IsCacheVisualizationOutOfDate(int32 InVisualizationCacheID) const override; virtual int32 GetCurrentVisualizationCacheID() const override; virtual void ForceVisualizationCacheClear() const override; + virtual bool ArePinsCompatible(const UEdGraphPin* PinA, const UEdGraphPin* PinB, const UClass* CallingContext = nullptr, bool bIgnoreArray = false) const override; + virtual void ConstructBasicPinTooltip(const UEdGraphPin& Pin, const FText& PinDescription, FString& TooltipOut) const override; + virtual bool IsTitleBarPin(const UEdGraphPin& Pin) const override; + virtual bool CanShowDataTooltipForPin(const UEdGraphPin& Pin) const override; // -- // FlowGraphSchema + + /** + * Returns true if the two pin types are schema compatible. Handles outputting a more derived + * type to an input pin expecting a less derived type. + * + * @param Output The output type. + * @param Input The input type. + * @param CallingContext (optional) The calling context (required to properly evaluate pins of type Self) + * @param bIgnoreArray (optional) Whether or not to ignore differences between array and non-array types + * + * @return true if the pin types are compatible. + */ + virtual bool ArePinTypesCompatible(const FEdGraphPinType& Output, const FEdGraphPinType& Input, const UClass* CallingContext = NULL, bool bIgnoreArray = false) const; + + /** + * Returns the connection response for connecting PinA to PinB, which have already been determined to be compatible + * types with a compatible direction. InputPin and OutputPin are PinA and PinB or vis versa, indicating their direction. + * + * @param PinA The pin a. + * @param PinB The pin b. + * @param InputPin Either PinA or PinB, depending on which one is the input. + * @param OutputPin Either PinA or PinB, depending on which one is the output. + * + * @return The message and action to take on trying to make this connection. + */ + virtual const FPinConnectionResponse DetermineConnectionResponseOfCompatibleTypedPins(const UEdGraphPin* PinA, const UEdGraphPin* PinB, const UEdGraphPin* InputPin, const UEdGraphPin* OutputPin) const; + virtual void GetGraphNodeContextActions(FGraphContextMenuBuilder& ContextMenuBuilder, int32 SubNodeFlags) const; bool IsAddOnAllowedForSelectedObjects(const TArray& SelectedObjects, const UFlowNodeAddOn* AddOnTemplate) const; - // -- + // -- static void UpdateGeneratedDisplayNames(); static void UpdateGeneratedDisplayName(UClass* NodeClass, bool bBatch = false); @@ -72,6 +108,8 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema protected: static UFlowGraphNode* CreateDefaultNode(UEdGraph& Graph, const UFlowAsset* AssetClassDefaults, const TSubclassOf& NodeClass, const FVector2D& Offset, bool bPlacedAsGhostNode); + static bool ArePinCategoriesEffectivelyMatching(const FName& InputPinCategory, const FName& OutputPinCategory, bool bAllowImplicitCasts = true); + private: static void ApplyNodeOrAddOnFilter(const UFlowAsset* AssetClassDefaults, const UClass* FlowNodeClass, TArray& FilteredNodes); static void GetFlowNodeActions(FGraphActionMenuBuilder& ActionMenuBuilder, const UFlowAsset* EditedFlowAsset, const FString& CategoryName); diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h index 09d9c1952..944250048 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h @@ -4,10 +4,36 @@ #include "FlowGraphConnectionDrawingPolicy.h" #include "Engine/DeveloperSettings.h" +#include "GameplayTagContainer.h" #include "FlowTypes.h" #include "FlowGraphSettings.generated.h" +class UFlowNodeBase; + +USTRUCT() +struct FFlowNodeDisplayStyleConfig +{ + GENERATED_BODY() + +public: + + FFlowNodeDisplayStyleConfig() {} + FFlowNodeDisplayStyleConfig(const FGameplayTag& InTag, const FLinearColor& InNodeColor) : Tag(InTag), TitleColor(InNodeColor) {} + + FORCEINLINE bool operator ==(const FFlowNodeDisplayStyleConfig& Other) const { return Tag == Other.Tag; } + FORCEINLINE bool operator !=(const FFlowNodeDisplayStyleConfig& Other) const { return Tag != Other.Tag; } + FORCEINLINE bool operator <(const FFlowNodeDisplayStyleConfig& Other) const { return Tag < Other.Tag; } + +public: + + UPROPERTY(Config, EditAnywhere, Category = "Nodes", meta = (Categories = "Flow.NodeDisplayStyle")) + FGameplayTag Tag; + + UPROPERTY(Config, EditAnywhere, Category = "Nodes") + FLinearColor TitleColor; +}; + /** * */ @@ -15,6 +41,7 @@ UCLASS(Config = Editor, defaultconfig, meta = (DisplayName = "Flow Graph")) class FLOWEDITOR_API UFlowGraphSettings : public UDeveloperSettings { GENERATED_UCLASS_BODY() + static UFlowGraphSettings* Get() { return StaticClass()->GetDefaultObject(); } virtual void PostInitProperties() override; @@ -63,7 +90,21 @@ class FLOWEDITOR_API UFlowGraphSettings : public UDeveloperSettings UPROPERTY(EditAnywhere, config, Category = "Nodes") TArray NodePrefixesToRemove; - UPROPERTY(EditAnywhere, config, Category = "Nodes") + // Display Styles for nodes, keyed by gameplay tag + UPROPERTY(EditAnywhere, config, Category = "Nodes", meta = (TitleProperty = "{Tag}}")) + TArray NodeDisplayStyles; + +#if WITH_EDITORONLY_DATA + // Tags in the NodeDisplayStylesMap, used to detect when the map needs updating + UPROPERTY(Transient) + FGameplayTagContainer NodeDisplayStylesAuthoredTags; + + // Cached map of the data in NodeDisplayStyles for gameplaytag-keyed lookup + UPROPERTY(Transient) + TMap NodeDisplayStylesMap; +#endif + + UPROPERTY(EditAnywhere, config, Category = "Nodes", meta = (Deprecated)) TMap NodeTitleColors; UPROPERTY(Config, EditAnywhere, Category = "Nodes") @@ -121,4 +162,13 @@ class FLOWEDITOR_API UFlowGraphSettings : public UDeveloperSettings public: virtual FName GetCategoryName() const override { return FName("Flow Graph"); } virtual FText GetSectionText() const override { return INVTEXT("Graph Settings"); } + +#if WITH_EDITOR + const TMap& EnsureNodeDisplayStylesMap(); + + bool TryAddDefaultNodeDisplayStyle(const FFlowNodeDisplayStyleConfig& StyleConfig); + + const FLinearColor* LookupNodeTitleColorForNode(const UFlowNodeBase& FlowNodeBase); + +#endif }; diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h index 5999a6382..ad91ae2db 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h @@ -14,6 +14,8 @@ class UEdGraphSchema; class UFlowGraph; class UFlowNodeBase; class UFlowNode; +class UFlowAsset; +class FFlowMessageLog; DECLARE_DELEGATE(FFlowGraphNodeEvent); @@ -34,6 +36,7 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode UFlowNodeBase* NodeInstance; bool bBlueprintCompilationPending; + bool bIsReconstructingNode; bool bNeedsFullReconstruction; static bool bFlowAssetsLoaded; @@ -113,7 +116,7 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode void CreateAttachAddOnSubMenu(UToolMenu* Menu, UEdGraph* Graph) const; - bool CanAcceptSubNodeAsChild(const UFlowGraphNode& OtherSubNode, FString* OutReasonString = nullptr) const; + bool CanAcceptSubNodeAsChild(const UFlowGraphNode& OtherSubNode, const TSet& AllRootSubNodesToPaste, FString* OutReasonString = nullptr) const; bool IsAncestorNode(const UFlowGraphNode& OtherNode) const; @@ -127,6 +130,8 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode // Get flow node for the inspected asset instance UFlowNode* GetInspectedNodeInstance() const; + UFlowAsset* GetFlowAsset() const; + // Used for highlighting active nodes of the inspected asset instance EFlowNodeState GetActivationState() const; @@ -145,6 +150,11 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode virtual bool SupportsCommentBubble() const override; // -- + /** check if node has any errors, used for assigning colors on graph */ + virtual bool HasErrors() const; + + void ValidateGraphNode(FFlowMessageLog& MessageLog) const; + ////////////////////////////////////////////////////////////////////////// // Pins @@ -184,6 +194,14 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode virtual void GetPinHoverText(const UEdGraphPin& Pin, FString& HoverTextOut) const override; // -- + // @return true, if pins cannot be connected due to node's inner logic, put message for user in OutReason + virtual bool IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const { return false; } + +protected: + // Gets the PinCategory from the FlowPin + // (accounting for FFlowPin structs that predate the PinCategory field) + const FName& GetPinCategoryFromFlowPin(const FFlowPin& FlowPin) const; + ////////////////////////////////////////////////////////////////////////// // Breakpoints @@ -274,15 +292,14 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode /** Check if node instance uses blueprint for its implementation */ bool UsesBlueprint() const; - /** check if node has any errors, used for assigning colors on graph */ - virtual bool HasErrors() const; - protected: virtual void ResetNodeOwner(); void LogError(const FString& MessageToLog, const UFlowNodeBase* FlowNodeBase) const; + bool HavePinsChanged(); + public: /** instance class */ diff --git a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h index e362ec3e9..143fbfdf1 100644 --- a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h @@ -71,8 +71,6 @@ class FLOWEDITOR_API SFlowGraphNode : public SGraphNode virtual void CreateBelowPinControls(TSharedPtr MainBox) override; virtual const FSlateBrush* GetNodeBodyBrush() const override; - virtual void CreateStandardPinWidget(UEdGraphPin* Pin) override; - virtual void CreateInputSideAddButton(TSharedPtr OutputBox) override; virtual void CreateOutputSideAddButton(TSharedPtr OutputBox) override; // -- @@ -125,8 +123,6 @@ class FLOWEDITOR_API SFlowGraphNode : public SGraphNode bool IsFlowGraphNodeSelected(UFlowGraphNode* Node) const; - static int32 ValidPinsCount(const TArray& Pins); - protected: // The FlowGraphNode this slate widget is representing UFlowGraphNode* FlowGraphNode = nullptr; From c17ae0243ac9ccfc77824d96cc12e92cc7d36697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Do=C4=9Fa=20Can=20Yan=C4=B1ko=C4=9Flu?= Date: Sun, 17 Nov 2024 22:26:39 +0300 Subject: [PATCH 273/485] Add virtual to StartRootFlow & FinishRootFlow (#229) Make it possible to override StartRootFlow / FinishRootFlow. Makes it easier to extend UFlowComponent for project specific needs. --- Source/Flow/Public/FlowComponent.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Flow/Public/FlowComponent.h b/Source/Flow/Public/FlowComponent.h index 285465875..329f551b8 100644 --- a/Source/Flow/Public/FlowComponent.h +++ b/Source/Flow/Public/FlowComponent.h @@ -193,15 +193,15 @@ class FLOW_API UFlowComponent : public UActorComponent, public IFlowOwnerInterfa UPROPERTY(SaveGame) FString SavedAssetInstanceName; - + // This will instantiate Flow Asset assigned on this component. // Created Flow Asset instance will be a "root flow", as additional Flow Assets can be instantiated via Sub Graph node UFUNCTION(BlueprintCallable, Category = "RootFlow") - void StartRootFlow(); + virtual void StartRootFlow(); // This will destroy instantiated Flow Asset - created from asset assigned on this component. UFUNCTION(BlueprintCallable, Category = "RootFlow") - void FinishRootFlow(UFlowAsset* TemplateAsset, const EFlowFinishPolicy FinishPolicy); + virtual void FinishRootFlow(UFlowAsset* TemplateAsset, const EFlowFinishPolicy FinishPolicy); UFUNCTION(BlueprintPure, Category = "FlowSubsystem") TSet GetRootInstances(const UObject* Owner) const; From 754ac3a0169bbef3a1337cecb27cb07d1fcda469 Mon Sep 17 00:00:00 2001 From: SPontadit Date: Sun, 17 Nov 2024 20:27:34 +0100 Subject: [PATCH 274/485] Add a virtual function AdditionalNodeIndexing in FlowGraphNode to index property that are not handled by default (#234) --- Source/FlowEditor/Private/Asset/FlowAssetIndexer.cpp | 1 + Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/Source/FlowEditor/Private/Asset/FlowAssetIndexer.cpp b/Source/FlowEditor/Private/Asset/FlowAssetIndexer.cpp index 0d1a3e0f1..2c7ed92d9 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetIndexer.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetIndexer.cpp @@ -120,6 +120,7 @@ void FFlowAssetIndexer::IndexGraph(const UFlowAsset* InFlowAsset, FSearchSeriali { Serializer.IndexProperty(Property, Value); }); + FlowGraphNode->AdditionalNodeIndexing(Serializer); Serializer.EndIndexingObject(); } } diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h index ad91ae2db..c98c89415 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h @@ -4,6 +4,7 @@ #include "EdGraph/EdGraphNode.h" #include "EdGraph/EdGraphPin.h" +#include "SearchSerializer.h" #include "Templates/SubclassOf.h" #include "FlowTypes.h" @@ -144,6 +145,9 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode bool CanFocusViewport() const; + // Index properties that are not indexed by default + virtual void AdditionalNodeIndexing(FSearchSerializer& Serializer) const {} + // UEdGraphNode virtual bool CanJumpToDefinition() const override; virtual void JumpToDefinition() const override; From 7e284eeafa9636203d7c059ac3144bce70a5ad10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sun, 17 Nov 2024 20:51:09 +0100 Subject: [PATCH 275/485] fixed UE 5.5 compilation, formatting tweaked --- Source/Flow/Private/FlowAsset.cpp | 62 ++++++++++--------- Source/Flow/Private/Nodes/FlowPin.cpp | 22 ++++--- .../Flow/Public/Types/FlowDataPinProperties.h | 6 +- Source/Flow/Public/Types/FlowDataPinResults.h | 6 +- .../FlowGraphConnectionDrawingPolicy.cpp | 2 +- 5 files changed, 53 insertions(+), 45 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 666fa928d..29556d1f9 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -248,24 +248,30 @@ bool UFlowAsset::CanFlowAssetUseFlowNodeClass(const UClass& FlowNodeClass) const return true; } -bool UFlowAsset::CanFlowAssetReferenceFlowNode(const UClass& FlowNodeOrAddOnClass, FText* OutOptionalFailureReason) const +bool UFlowAsset::CanFlowAssetReferenceFlowNode(const UClass& FlowNodeClass, FText* OutOptionalFailureReason) const { - if (!GEditor || !IsValid(&FlowNodeOrAddOnClass)) + if (!GEditor || !IsValid(&FlowNodeClass)) { return false; } - FAssetData FlowNodeAssetData(&FlowNodeOrAddOnClass); - FAssetReferenceFilterContext AssetReferenceFilterContext; + +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 5 AssetReferenceFilterContext.ReferencingAssets.Add(FAssetData(this)); +#else + AssetReferenceFilterContext.AddReferencingAsset(FAssetData(this)); +#endif // Confirm plugin reference restrictions are being respected - TSharedPtr FlowAssetReferenceFilter = GEditor->MakeAssetReferenceFilter(AssetReferenceFilterContext); - if (FlowAssetReferenceFilter.IsValid() && - !FlowAssetReferenceFilter->PassesFilter(FlowNodeAssetData, OutOptionalFailureReason)) + const TSharedPtr FlowAssetReferenceFilter = GEditor->MakeAssetReferenceFilter(AssetReferenceFilterContext); + if (FlowAssetReferenceFilter.IsValid()) { - return false; + const FAssetData FlowNodeAssetData(&FlowNodeClass); + if (!FlowAssetReferenceFilter->PassesFilter(FlowNodeAssetData, OutOptionalFailureReason)) + { + return false; + } } return true; @@ -294,7 +300,7 @@ void UFlowAsset::RegisterNode(const FGuid& NewGuid, UFlowNode* NewNode) Nodes.Emplace(NewGuid, NewNode); HarvestNodeConnections(); - (void) TryUpdateManagedFlowPinsForNode(*NewNode); + (void)TryUpdateManagedFlowPinsForNode(*NewNode); } void UFlowAsset::UnregisterNode(const FGuid& NodeGuid) @@ -426,7 +432,7 @@ bool UFlowAsset::TryUpdateManagedFlowPinsForNode(UFlowNode& FlowNode) WorkingData.PinNameToBoundPropertyNameMapNext, WorkingData.AutoInputDataPinsNext, WorkingData.AutoOutputDataPinsNext); - } + } // Try to harvest pins to auto-generate and/or bind to for each property in the flow node for (TFieldIterator PropertyIt(FlowNodeClass); PropertyIt; ++PropertyIt) @@ -494,8 +500,8 @@ void UFlowAsset::HarvestFlowPinMetadataForProperty(const FProperty* Property, FF { LogError( FString::Printf(TEXT("Error. A property cannot be both a %s and %s"), - *FFlowPin::MetadataKey_SourceForOutputFlowPin.ToString(), - *FFlowPin::MetadataKey_DefaultForInputFlowPin.ToString()), + *FFlowPin::MetadataKey_SourceForOutputFlowPin.ToString(), + *FFlowPin::MetadataKey_DefaultForInputFlowPin.ToString()), InOutData.FlowNode); return; @@ -625,8 +631,8 @@ void UFlowAsset::AddDataPinPropertyBindingToMap( InOutData.PinNameToBoundPropertyNameMapNext.Add(PinAuthoredName, PropertyAuthoredName); } -template +template void AddPinForPinType(EFlowPinType PinType, UFlowNode& FlowNode, const FProperty& Property, const FText& PinDisplayName, TArray* InOutDataPinsNext) { const FName& PinAuthoredName = Property.GetFName(); @@ -743,7 +749,7 @@ void AddPinForPinType(EFlowPinType PinType, UFlowNode& FlowNode, const FProperty { // Get the Object property's base UClass from the property's MetaData Class = WeakObjectProperty->PropertyClass; - } + } else if (const FLazyObjectProperty* LazyObjectProperty = CastField(&Property)) { // Get the Object property's base UClass from the property's MetaData @@ -763,7 +769,7 @@ void AddPinForPinType(EFlowPinType PinType, UFlowNode& FlowNode, const FProperty static const UStruct* SoftClassPathStruct = TBaseStructure::Get(); if (StructProperty->Struct == ScriptStruct) - { + { TClassProperty ValueStruct; StructProperty->GetValue_InContainer(&FlowNode, &ValueStruct); @@ -772,7 +778,7 @@ void AddPinForPinType(EFlowPinType PinType, UFlowNode& FlowNode, const FProperty } else if (StructProperty->Struct == SoftClassPathStruct) { - // Get the Class property's base UClass from the struct property's MetaData + // Get the Class property's base UClass from the struct property's MetaData Class = FFlowDataPinOutputProperty_Class::TryGetMetaClassFromProperty(*StructProperty); } } @@ -833,11 +839,11 @@ bool UFlowAsset::TryCreateFlowDataPinFromMetadataValue( FFlowDataPinInputProperty_InstancedStruct, FFlowDataPinInputProperty_Object, FFlowDataPinInputProperty_Class>( - PinType, - FlowNode, - Property, - PinDisplayName, - InOutDataPinsNext); + PinType, + FlowNode, + Property, + PinDisplayName, + InOutDataPinsNext); } else { @@ -851,11 +857,11 @@ bool UFlowAsset::TryCreateFlowDataPinFromMetadataValue( FFlowDataPinOutputProperty_InstancedStruct, FFlowDataPinOutputProperty_Object, FFlowDataPinOutputProperty_Class>( - PinType, - FlowNode, - Property, - PinDisplayName, - InOutDataPinsNext); + PinType, + FlowNode, + Property, + PinDisplayName, + InOutDataPinsNext); } return true; @@ -1002,7 +1008,7 @@ TArray UFlowAsset::GetNodesInExecutionOrder(UFlowNode* FirstIterated } } FoundNodes.Shrink(); - + return FoundNodes; } diff --git a/Source/Flow/Private/Nodes/FlowPin.cpp b/Source/Flow/Private/Nodes/FlowPin.cpp index 92565bf6d..688ea2b87 100644 --- a/Source/Flow/Private/Nodes/FlowPin.cpp +++ b/Source/Flow/Private/Nodes/FlowPin.cpp @@ -4,10 +4,15 @@ #include "FlowLogChannels.h" #include "GameplayTagContainer.h" -#include "InstancedStruct.h" #include "Misc/DateTime.h" #include "Misc/MessageDialog.h" +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 5 +#include "InstancedStruct.h" +#else +#include "StructUtils/InstancedStruct.h" +#endif + #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowPin) #define LOCTEXT_NAMESPACE "FlowPin" @@ -338,7 +343,6 @@ void FFlowPin::PostEditChangedPinTypeOrSubCategorySource() switch (PinType) { - case EFlowPinType::Class: { PinSubCategoryObject = SubCategoryClassFilter; @@ -385,7 +389,7 @@ FText FFlowPin::BuildHeaderText() const } else { - return FText::Format(LOCTEXT("FlowPinNameAndType", "{0} ({1})"), { PinNameToUse, UEnum::GetDisplayValueAsText(PinType) }); + return FText::Format(LOCTEXT("FlowPinNameAndType", "{0} ({1})"), {PinNameToUse, UEnum::GetDisplayValueAsText(PinType)}); } } @@ -407,8 +411,8 @@ bool FFlowPin::ValidateEnum(const UEnum& EnumType) if (Value < std::numeric_limits::min() || Value > std::numeric_limits::max()) { UE_LOG(LogFlow, Error, TEXT("'%s' value %d is outside the range of supported key values for enum [%d, %d].") - , *EnumType.GenerateFullEnumName(*EnumType.GetDisplayNameTextByIndex(i).ToString()) - , Value, std::numeric_limits::min(), std::numeric_limits::max()); + , *EnumType.GenerateFullEnumName(*EnumType.GetDisplayNameTextByIndex(i).ToString()) + , Value, std::numeric_limits::min(), std::numeric_limits::max()); bAllValid = false; } @@ -416,14 +420,12 @@ bool FFlowPin::ValidateEnum(const UEnum& EnumType) if (!bAllValid) { - FMessageDialog::Open(EAppMsgType::Ok, - NSLOCTEXT("FlowPin" - , "Unsupported enumeration" - , "Specified enumeration contains one or more values outside supported value range for enum keys and can not be used for Flow Data Pins. See log for details.")); + FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("Unsupported enumeration" + , "Specified enumeration contains one or more values outside supported value range for enum keys and can not be used for Flow Data Pins. See log for details.")); } return bAllValid; } #endif //WITH_EDITOR -#undef LOCTEXT_NAMESPACE \ No newline at end of file +#undef LOCTEXT_NAMESPACE diff --git a/Source/Flow/Public/Types/FlowDataPinProperties.h b/Source/Flow/Public/Types/FlowDataPinProperties.h index 0550b3c66..ee2412008 100644 --- a/Source/Flow/Public/Types/FlowDataPinProperties.h +++ b/Source/Flow/Public/Types/FlowDataPinProperties.h @@ -8,10 +8,10 @@ #include "Kismet/BlueprintFunctionLibrary.h" #include "UObject/Class.h" -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 5 -#include "StructUtils/InstancedStruct.h" -#else +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 5 #include "InstancedStruct.h" +#else +#include "StructUtils/InstancedStruct.h" #endif #include "FlowDataPinProperties.generated.h" diff --git a/Source/Flow/Public/Types/FlowDataPinResults.h b/Source/Flow/Public/Types/FlowDataPinResults.h index ac077fb7a..0f9536222 100644 --- a/Source/Flow/Public/Types/FlowDataPinResults.h +++ b/Source/Flow/Public/Types/FlowDataPinResults.h @@ -6,10 +6,10 @@ #include "GameplayTagContainer.h" -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 5 -#include "StructUtils/InstancedStruct.h" -#else +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 5 #include "InstancedStruct.h" +#else +#include "StructUtils/InstancedStruct.h" #endif #include "FlowDataPinResults.generated.h" diff --git a/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp b/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp index 3e187b349..963cee22e 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp @@ -313,7 +313,7 @@ bool FFlowGraphConnectionDrawingPolicy::ShouldChangeTangentForReroute(UFlowGraph FVector2D AverageLeftPin; FVector2D AverageRightPin; - FVector2D CenterPin; + FVector2D CenterPin = FVector2D::ZeroVector; const bool bCenterValid = Reroute->OutputPins.Num() == 0 ? false : FindPinCenter(Reroute->OutputPins[0], /*out*/ CenterPin); const bool bLeftValid = GetAverageConnectedPosition(Reroute, EGPD_Input, /*out*/ AverageLeftPin); const bool bRightValid = GetAverageConnectedPosition(Reroute, EGPD_Output, /*out*/ AverageRightPin); From 59cf80c654dc90d2fc262c81c68a0c383bbe2d0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sun, 17 Nov 2024 20:57:45 +0100 Subject: [PATCH 276/485] compilation fix: BuildPlugin mode --- Source/Flow/Private/Nodes/FlowPin.cpp | 1 + Source/Flow/Public/Types/FlowDataPinProperties.h | 1 + Source/Flow/Public/Types/FlowDataPinResults.h | 1 + 3 files changed, 3 insertions(+) diff --git a/Source/Flow/Private/Nodes/FlowPin.cpp b/Source/Flow/Private/Nodes/FlowPin.cpp index 688ea2b87..8fe457823 100644 --- a/Source/Flow/Private/Nodes/FlowPin.cpp +++ b/Source/Flow/Private/Nodes/FlowPin.cpp @@ -6,6 +6,7 @@ #include "GameplayTagContainer.h" #include "Misc/DateTime.h" #include "Misc/MessageDialog.h" +#include "Runtime/Launch/Resources/Version.h" #if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 5 #include "InstancedStruct.h" diff --git a/Source/Flow/Public/Types/FlowDataPinProperties.h b/Source/Flow/Public/Types/FlowDataPinProperties.h index ee2412008..45d0ac338 100644 --- a/Source/Flow/Public/Types/FlowDataPinProperties.h +++ b/Source/Flow/Public/Types/FlowDataPinProperties.h @@ -6,6 +6,7 @@ #include "GameplayTagContainer.h" #include "Kismet/BlueprintFunctionLibrary.h" +#include "Runtime/Launch/Resources/Version.h" #include "UObject/Class.h" #if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 5 diff --git a/Source/Flow/Public/Types/FlowDataPinResults.h b/Source/Flow/Public/Types/FlowDataPinResults.h index 0f9536222..c0d12cf6f 100644 --- a/Source/Flow/Public/Types/FlowDataPinResults.h +++ b/Source/Flow/Public/Types/FlowDataPinResults.h @@ -5,6 +5,7 @@ #include "Types/FlowPinEnums.h" #include "GameplayTagContainer.h" +#include "Runtime/Launch/Resources/Version.h" #if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 5 #include "InstancedStruct.h" From f0a0487c966a5cf3c781b13a1b80e012759966ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sun, 17 Nov 2024 21:14:18 +0100 Subject: [PATCH 277/485] removed NSLOCTEXT copy-pasted a long time ago --- Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp | 3 +-- Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp index 5d275e26c..c794ad0e5 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp @@ -20,7 +20,6 @@ #include "Widgets/Text/STextBlock.h" #include "AssetToolsModule.h" -#include "IAssetTypeActions.h" #include "ISourceControlModule.h" #include "ISourceControlProvider.h" #include "SourceControlHelpers.h" @@ -274,7 +273,7 @@ static void OnDiffRevisionPicked(FRevisionInfo const& RevisionInfo, const FStrin } else { - FMessageDialog::Open(EAppMsgType::Ok, NSLOCTEXT("SourceControl.HistoryWindow", "UnableToLoadAssets", "Unable to load assets to diff. Content may no longer be supported?")); + FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("UnableToLoadAssets", "Unable to load assets to diff. Content may no longer be supported?")); } } break; diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp index ffd0d1a03..72c11f9fc 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp @@ -1087,7 +1087,7 @@ FReply SFlowGraphNode::OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& return FReply::Unhandled(); } - const FScopedTransaction Transaction(NSLOCTEXT("UnrealEd", "GraphEd_DragDropNode", "Drag&Drop Node")); + const FScopedTransaction Transaction(LOCTEXT("GraphEd_DragDropNode", "Drag&Drop Node")); UFlowGraphNode* DropTargetNode = DragNodeOp->GetDropTargetNode(); check(DropTargetNode); From c19740f930e82c6b1885fd05ed9d6a30deea98b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sun, 17 Nov 2024 21:44:39 +0100 Subject: [PATCH 278/485] removed support for UE 5.1 and UE 5.2 --- .../World/FlowNode_PlayLevelSequence.cpp | 6 --- .../Private/Asset/FlowDiffControl.cpp | 38 ++++--------------- Source/FlowEditor/Private/Asset/SFlowDiff.cpp | 24 +----------- .../Private/MovieScene/FlowSection.cpp | 12 ------ .../Private/MovieScene/FlowTrackEditor.cpp | 8 +--- .../Private/Utils/SLevelEditorFlow.cpp | 6 --- .../FlowEditor/Public/Asset/FlowDiffControl.h | 6 --- 7 files changed, 11 insertions(+), 89 deletions(-) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp index 823d0a985..a1627a59a 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp @@ -14,7 +14,6 @@ #include "LevelSequence.h" #include "LevelSequenceActor.h" -#include "Runtime/Launch/Resources/Version.h" #include "VisualLogger/VisualLogger.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_PlayLevelSequence) @@ -64,14 +63,9 @@ TArray UFlowNode_PlayLevelSequence::GetContextOutputs() const TArray Pins = {}; Sequence.LoadSynchronous(); - if (Sequence && Sequence->GetMovieScene()) { -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 2 - for (const UMovieSceneTrack* Track : Sequence->GetMovieScene()->GetMasterTracks()) -#else for (const UMovieSceneTrack* Track : Sequence->GetMovieScene()->GetTracks()) -#endif { if (Track->GetClass() == UMovieSceneFlowTrack::StaticClass()) { diff --git a/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp b/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp index 0df59b143..c85a47c41 100644 --- a/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp +++ b/Source/FlowEditor/Private/Asset/FlowDiffControl.cpp @@ -18,7 +18,7 @@ namespace { if (Difference.Result.Pin1) { - return Difference.Result.Pin1->PinId.ToString() + Difference.Result.Pin2->PinId.ToString(); + return Difference.Result.Pin1->PinId.ToString() + Difference.Result.Pin2->PinId.ToString(); } const UFlowGraphNode* FlowGraphNode = Cast(Difference.Result.Node1); @@ -29,7 +29,7 @@ namespace if (IsValid(FlowGraphNode) && IsValid(FlowGraphNode->GetNodeTemplate())) { - return FString::FromInt(FlowGraphNode->GetNodeTemplate()->GetUniqueID());//->GetName(); + return FString::FromInt(FlowGraphNode->GetNodeTemplate()->GetUniqueID()); //->GetName(); } if (IsValid(Difference.Result.Node1)) @@ -42,7 +42,7 @@ namespace return FString::FromInt(Difference.Result.Node2->GetUniqueID()); } - return TEXT("Invalid Diff!"); + return TEXT("Invalid Diff!"); } FString GetNodeNameFromDiffResult(const FDiffResultItem& Difference) @@ -105,22 +105,14 @@ namespace /// FFlowAssetDiffControl FFlowAssetDiffControl::FFlowAssetDiffControl(const UFlowAsset* InOldFlowAsset, const UFlowAsset* InNewFlowAsset, FOnDiffEntryFocused InSelectionCallback) -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 2 - : TDetailsDiffControl(InOldFlowAsset, InNewFlowAsset, InSelectionCallback) -#else : FDetailsDiffControl(InOldFlowAsset, InNewFlowAsset, InSelectionCallback, false) -#endif { } // TDetailsDiffControl::GenerateTreeEntries + "NoDifferences" entry + category label void FFlowAssetDiffControl::GenerateTreeEntries(TArray>& OutTreeEntries, TArray>& OutRealDifferences) { -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 2 - TDetailsDiffControl::GenerateTreeEntries(OutTreeEntries, OutRealDifferences); -#else FDetailsDiffControl::GenerateTreeEntries(OutTreeEntries, OutRealDifferences); -#endif const bool bHasDifferences = Children.Num() != 0; if (!bHasDifferences) @@ -279,8 +271,8 @@ void FFlowGraphToDiff::GenerateTreeEntries(TArray& FlowObjectPropertyDiff = FlowNodeDiff->PropertyDiffArgList.Add_GetRef(MakeShared(FlowNodeDiff, PropertyDiffEntry)); TSharedPtr ChildEntry = MakeShared( - FOnDiffEntryFocused::CreateSP(DiffWidget, &SFlowDiff::OnDiffListSelectionChanged, FlowObjectPropertyDiff), - FGenerateDiffEntryWidget::CreateStatic(&GenerateObjectDiffWidget, FlowObjectPropertyDiff->PropertyDiff, RightRevision)); + FOnDiffEntryFocused::CreateSP(DiffWidget, &SFlowDiff::OnDiffListSelectionChanged, FlowObjectPropertyDiff), + FGenerateDiffEntryWidget::CreateStatic(&GenerateObjectDiffWidget, FlowObjectPropertyDiff->PropertyDiff, RightRevision)); Nodes.Value->DiffTreeEntry->Children.Push(ChildEntry); OutRealDifferences.Push(ChildEntry); @@ -306,8 +298,8 @@ void FFlowGraphToDiff::GenerateTreeEntries(TArrayLowPriorityChildDiffResult) { TSharedPtr ChildEntry = MakeShared( - FOnDiffEntryFocused::CreateRaw(DiffWidget, &SFlowDiff::OnDiffListSelectionChanged, FlowNodeDiff->DiffEntryFocusArg), - FGenerateDiffEntryWidget::CreateSP(Difference.ToSharedRef(), &FDiffResultItem::GenerateWidget)); + FOnDiffEntryFocused::CreateRaw(DiffWidget, &SFlowDiff::OnDiffListSelectionChanged, FlowNodeDiff->DiffEntryFocusArg), + FGenerateDiffEntryWidget::CreateSP(Difference.ToSharedRef(), &FDiffResultItem::GenerateWidget)); FlowNodeDiff->DiffTreeEntry->Children.Push(ChildEntry); OutRealDifferences.Push(ChildEntry); @@ -428,9 +420,7 @@ TSharedRef FFlowGraphToDiff::GenerateCategoryWidget() const FLinearColor Color = (GraphOld && GraphNew) ? DiffViewUtils::Identical() : FLinearColor(0.3f, 0.3f, 1.f); - const bool bHasDiffs = DiffListSource.Num() > 0; - - if (bHasDiffs) + if (DiffListSource.Num() > 0) { Color = DiffViewUtils::Differs(); } @@ -452,19 +442,7 @@ void FFlowGraphToDiff::BuildDiffSourceArray() FoundDiffs->Empty(); FGraphDiffControl::DiffGraphs(GraphOld, GraphNew, *FoundDiffs); -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 3 - struct SortDiff - { - bool operator ()(const FDiffSingleResult& A, const FDiffSingleResult& B) const - { - return A.Diff < B.Diff; - } - }; - - Sort(FoundDiffs->GetData(), FoundDiffs->Num(), SortDiff()); -#else Algo::SortBy(*FoundDiffs, &FDiffSingleResult::Diff); -#endif DiffListSource.Empty(); for (const FDiffSingleResult& Diff : *FoundDiffs) diff --git a/Source/FlowEditor/Private/Asset/SFlowDiff.cpp b/Source/FlowEditor/Private/Asset/SFlowDiff.cpp index 410a3e228..3ec288cb0 100644 --- a/Source/FlowEditor/Private/Asset/SFlowDiff.cpp +++ b/Source/FlowEditor/Private/Asset/SFlowDiff.cpp @@ -16,14 +16,11 @@ #include "Internationalization/Text.h" #include "PropertyEditorModule.h" #include "SBlueprintDiff.h" +#include "SDetailsSplitter.h" #include "SlateOptMacros.h" #include "Subsystems/AssetEditorSubsystem.h" #include "Widgets/Layout/SSpacer.h" -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION > 2 -#include "SDetailsSplitter.h" -#endif - #define LOCTEXT_NAMESPACE "SFlowDiff" static const FName DetailsMode = FName(TEXT("DetailsMode")); @@ -713,10 +710,9 @@ SFlowDiff::FDiffControl SFlowDiff::GenerateDetailsPanel() const TSharedPtr NewDiffControl = MakeShared(PanelOld.FlowAsset, PanelNew.FlowAsset, FOnDiffEntryFocused::CreateRaw(this, &SFlowDiff::SetCurrentMode, DetailsMode)); NewDiffControl->GenerateTreeEntries(PrimaryDifferencesList, RealDifferences); - SFlowDiff::FDiffControl Ret; + FDiffControl Ret; Ret.DiffControl = NewDiffControl; -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 3 const TSharedRef Splitter = SNew(SDetailsSplitter); if (PanelOld.FlowAsset) { @@ -738,22 +734,6 @@ SFlowDiff::FDiffControl SFlowDiff::GenerateDetailsPanel() } Ret.Widget = Splitter; -#elif ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 3 - - Ret.Widget = SNew(SSplitter) - .PhysicalSplitterHandleSize(10.0f) - + SSplitter::Slot() - .Value(0.5f) - [ - NewDiffControl->OldDetailsWidget() - ] - + SSplitter::Slot() - .Value(0.5f) - [ - NewDiffControl->NewDetailsWidget() - ]; -#endif - return Ret; } diff --git a/Source/FlowEditor/Private/MovieScene/FlowSection.cpp b/Source/FlowEditor/Private/MovieScene/FlowSection.cpp index 474fffb02..382203fdd 100644 --- a/Source/FlowEditor/Private/MovieScene/FlowSection.cpp +++ b/Source/FlowEditor/Private/MovieScene/FlowSection.cpp @@ -70,11 +70,7 @@ void FFlowSectionBase::PaintEventName(FSequencerSectionPainter& Painter, int32 L FSlateDrawElement::MakeBox( Painter.DrawElements, LayerId + 1, -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 2 - Painter.SectionGeometry.ToPaintGeometry(BoxOffset, BoxSize), -#else Painter.SectionGeometry.ToPaintGeometry(BoxSize, FSlateLayoutTransform(1.0f, TransformPoint(1.0f, UE::Slate::CastToVector2f(BoxOffset)))), -#endif FAppStyle::GetBrush("WhiteBrush"), ESlateDrawEffect::None, FLinearColor::Black.CopyWithNewOpacity(0.5f) @@ -86,11 +82,7 @@ void FFlowSectionBase::PaintEventName(FSequencerSectionPainter& Painter, int32 L FSlateDrawElement::MakeText( Painter.DrawElements, LayerId + 2, -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 2 - Painter.SectionGeometry.ToPaintGeometry(BoxOffset + IconOffset, IconSize), -#else Painter.SectionGeometry.ToPaintGeometry(IconSize, FSlateLayoutTransform(1.0f, TransformPoint(1.0f, UE::Slate::CastToVector2f(BoxOffset + IconOffset)))), -#endif WarningString, FontAwesomeFont, Painter.bParentEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect, @@ -101,11 +93,7 @@ void FFlowSectionBase::PaintEventName(FSequencerSectionPainter& Painter, int32 L FSlateDrawElement::MakeText( Painter.DrawElements, LayerId + 2, -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 2 - Painter.SectionGeometry.ToPaintGeometry(BoxOffset + TextOffset, TextSize), -#else Painter.SectionGeometry.ToPaintGeometry(TextSize, FSlateLayoutTransform(1.0f, TransformPoint(1.0f, UE::Slate::CastToVector2f(BoxOffset + TextOffset)))), -#endif InEventString, SmallLayoutFont, Painter.bParentEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect, diff --git a/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp b/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp index f80304db0..5adc92d7b 100644 --- a/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp +++ b/Source/FlowEditor/Private/MovieScene/FlowTrackEditor.cpp @@ -12,7 +12,6 @@ #include "ISequencerSection.h" #include "LevelSequence.h" #include "MovieSceneSequenceEditor.h" -#include "Runtime/Launch/Resources/Version.h" #include "Sections/MovieSceneEventSection.h" #include "SequencerUtilities.h" @@ -164,14 +163,9 @@ void FFlowTrackEditor::HandleAddFlowTrackMenuEntryExecute(UClass* SectionType) c FocusedMovieScene->Modify(); TArray NewTracks; - -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 2 - UMovieSceneFlowTrack* NewMasterTrack = FocusedMovieScene->AddMasterTrack(); -#else UMovieSceneFlowTrack* NewMasterTrack = FocusedMovieScene->AddTrack(); -#endif - NewTracks.Add(NewMasterTrack); + if (GetSequencer().IsValid()) { GetSequencer()->OnAddTrack(NewMasterTrack, FGuid()); diff --git a/Source/FlowEditor/Private/Utils/SLevelEditorFlow.cpp b/Source/FlowEditor/Private/Utils/SLevelEditorFlow.cpp index f9dd02438..86069f193 100644 --- a/Source/FlowEditor/Private/Utils/SLevelEditorFlow.cpp +++ b/Source/FlowEditor/Private/Utils/SLevelEditorFlow.cpp @@ -3,12 +3,6 @@ #include "Utils/SLevelEditorFlow.h" #include "FlowAsset.h" #include "FlowComponent.h" -#include "Runtime/Launch/Resources/Version.h" - -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 3 -#include "FlowWorldSettings.h" -#endif - #include "Graph/FlowGraphSettings.h" #include "Editor.h" diff --git a/Source/FlowEditor/Public/Asset/FlowDiffControl.h b/Source/FlowEditor/Public/Asset/FlowDiffControl.h index 4f5a3a2b2..3c750564f 100644 --- a/Source/FlowEditor/Public/Asset/FlowDiffControl.h +++ b/Source/FlowEditor/Public/Asset/FlowDiffControl.h @@ -4,9 +4,7 @@ #include "Asset/FlowObjectDiff.h" #include "DiffResults.h" -#include "IAssetTypeActions.h" #include "Editor/Kismet/Private/DiffControl.h" -#include "Runtime/Launch/Resources/Version.h" class FBlueprintDifferenceTreeEntry; class SFlowDiff; @@ -18,11 +16,7 @@ struct FEdGraphEditAction; ///////////////////////////////////////////////////////////////////////////// /// FFlowAssetDiffControl -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 2 -class FLOWEDITOR_API FFlowAssetDiffControl : public TDetailsDiffControl -#else class FLOWEDITOR_API FFlowAssetDiffControl : public FDetailsDiffControl -#endif { public: FFlowAssetDiffControl(const UFlowAsset* InOldFlowAsset, const UFlowAsset* InNewFlowAsset, FOnDiffEntryFocused InSelectionCallback); From b6e1386c2e1ad68256518b9235a21e36fce3645a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sun, 17 Nov 2024 21:48:06 +0100 Subject: [PATCH 279/485] standardized notation of ENGINE_MINOR_VERSION --- .../Private/LevelSequence/FlowLevelSequenceActor.cpp | 6 +++--- .../LevelSequence/FlowLevelSequencePlayer.cpp | 12 ++++++------ Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp | 7 ++++--- .../FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp | 6 +++--- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp b/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp index d3ceaf240..2453af928 100644 --- a/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp +++ b/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp @@ -23,10 +23,10 @@ void AFlowLevelSequenceActor::GetLifetimeReplicatedProps(TArray 3 - GetSequencePlayer()->SetPlaybackSettings(PlaybackSettings); -#else +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 4 SequencePlayer->SetPlaybackSettings(PlaybackSettings); +#else + GetSequencePlayer()->SetPlaybackSettings(PlaybackSettings); #endif } diff --git a/Source/Flow/Private/LevelSequence/FlowLevelSequencePlayer.cpp b/Source/Flow/Private/LevelSequence/FlowLevelSequencePlayer.cpp index 77d95906d..8fd0f0165 100644 --- a/Source/Flow/Private/LevelSequence/FlowLevelSequencePlayer.cpp +++ b/Source/Flow/Private/LevelSequence/FlowLevelSequencePlayer.cpp @@ -42,10 +42,10 @@ UFlowLevelSequencePlayer* UFlowLevelSequencePlayer::CreateFlowLevelSequencePlaye { // apply Transform Origin // https://docs.unrealengine.com/5.0/en-US/creating-level-sequences-with-dynamic-transforms-in-unreal-engine/ -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION > 3 - if (TransformOriginActor->IsValidLowLevel()) -#else +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 4 if (IsValid(TransformOriginActor)) +#else + if (TransformOriginActor->IsValidLowLevel()) #endif { // moving Level Sequence Actor might allow proper distance-based actor replication in networked games @@ -62,10 +62,10 @@ UFlowLevelSequencePlayer* UFlowLevelSequencePlayer::CreateFlowLevelSequencePlaye Actor->CameraSettings = CameraSettings; // apply Transform Origin to spawned actor -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION > 3 - if (TransformOriginActor->IsValidLowLevel()) -#else +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 4 if (IsValid(TransformOriginActor)) +#else + if (TransformOriginActor->IsValidLowLevel()) #endif { if (UDefaultLevelSequenceInstanceData* InstanceData = Cast(Actor->DefaultInstanceData)) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp b/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp index 5a91c24ba..0adc6f730 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp @@ -48,11 +48,12 @@ FString FFlowGraphUtils::RemovePrefixFromNodeText(const FText& Source) Prefix = FName::NameToDisplayString(Prefix, false); if (SourceString.StartsWith(Prefix)) { -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 4 - SourceString.MidInline(Prefix.Len(), MAX_int32, EAllowShrinking::No); -#else +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 4 SourceString.MidInline(Prefix.Len(), MAX_int32, false); +#else + SourceString.MidInline(Prefix.Len(), MAX_int32, EAllowShrinking::No); #endif + SourceString = SourceString.TrimStart(); } } diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 28736bcf2..e9a09862c 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -409,10 +409,10 @@ void UFlowGraphNode::RewireOldPinsToNewPins(TArray& InOldPins) OldPin->bOrphanedPin = true; OldPin->bNotConnectable = true; OrphanedOldPins.Add(OldPin); -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 4 - InOldPins.RemoveAt(OldPinIndex, 1, EAllowShrinking::No); -#else +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 4 InOldPins.RemoveAt(OldPinIndex, 1, false); +#else + InOldPins.RemoveAt(OldPinIndex, 1, EAllowShrinking::No); #endif } } From ce85ed56f5f2fbda5b370e97c898c9fb36a986a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sun, 17 Nov 2024 21:49:26 +0100 Subject: [PATCH 280/485] copyright fix --- Source/Flow/Public/Types/FlowArray.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Flow/Public/Types/FlowArray.h b/Source/Flow/Public/Types/FlowArray.h index 1831efe26..7940a6db8 100644 --- a/Source/Flow/Public/Types/FlowArray.h +++ b/Source/Flow/Public/Types/FlowArray.h @@ -1,4 +1,4 @@ -// Copyright Riot Games, All Rights Reserved. +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #pragma once From 5ef45f328809213024c99e9aba883ace7a104c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sun, 17 Nov 2024 22:35:24 +0100 Subject: [PATCH 281/485] POTENTIALLY BREAKING CHANGE: Added UAssetDefinition_FlowAsset implementing a new way of defining editor-only asset properties like asset category. This is a direct replacement for FAssetTypeActions_FlowAsset. If you had any custom asset class extending `UFlowAsset`, you probably need to convert your custom FAssetTypeActions to UAssetDefinition. This is quite a straightforward process. --- Source/FlowEditor/FlowEditor.Build.cs | 1 + .../Asset/AssetDefinition_FlowAsset.cpp | 77 +++++++++++++++++++ .../Asset/AssetTypeActions_FlowAsset.cpp | 58 -------------- .../FlowEditor/Private/FlowEditorModule.cpp | 6 +- .../Public/Asset/AssetDefinition_FlowAsset.h | 25 ++++++ .../Public/Asset/AssetTypeActions_FlowAsset.h | 19 ----- Source/FlowEditor/Public/FlowEditorModule.h | 5 ++ 7 files changed, 109 insertions(+), 82 deletions(-) create mode 100644 Source/FlowEditor/Private/Asset/AssetDefinition_FlowAsset.cpp delete mode 100644 Source/FlowEditor/Private/Asset/AssetTypeActions_FlowAsset.cpp create mode 100644 Source/FlowEditor/Public/Asset/AssetDefinition_FlowAsset.h delete mode 100644 Source/FlowEditor/Public/Asset/AssetTypeActions_FlowAsset.h diff --git a/Source/FlowEditor/FlowEditor.Build.cs b/Source/FlowEditor/FlowEditor.Build.cs index 79e348717..2596d3cd7 100644 --- a/Source/FlowEditor/FlowEditor.Build.cs +++ b/Source/FlowEditor/FlowEditor.Build.cs @@ -24,6 +24,7 @@ public FlowEditor(ReadOnlyTargetRules target) : base(target) PrivateDependencyModuleNames.AddRange(new[] { "ApplicationCore", + "AssetDefinition", "AssetSearch", "AssetTools", "BlueprintGraph", diff --git a/Source/FlowEditor/Private/Asset/AssetDefinition_FlowAsset.cpp b/Source/FlowEditor/Private/Asset/AssetDefinition_FlowAsset.cpp new file mode 100644 index 000000000..03a513c6d --- /dev/null +++ b/Source/FlowEditor/Private/Asset/AssetDefinition_FlowAsset.cpp @@ -0,0 +1,77 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Asset/AssetDefinition_FlowAsset.h" +#include "Asset/SFlowDiff.h" +#include "FlowEditorModule.h" +#include "Graph/FlowGraphSettings.h" + +#include "FlowAsset.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(AssetDefinition_FlowAsset) + +#define LOCTEXT_NAMESPACE "AssetDefinition_FlowAsset" + +FText UAssetDefinition_FlowAsset::GetAssetDisplayName() const +{ + return LOCTEXT("AssetTypeActions_FlowAsset", "Flow Asset"); +} + +FLinearColor UAssetDefinition_FlowAsset::GetAssetColor() const +{ + return FColor(255, 196, 128); +} + +TSoftClassPtr UAssetDefinition_FlowAsset::GetAssetClass() const +{ + return UFlowAsset::StaticClass(); +} + +TConstArrayView UAssetDefinition_FlowAsset::GetAssetCategories() const +{ + if (UFlowGraphSettings::Get()->bExposeFlowAssetCreation) + { + static const auto Categories = {FFLowAssetCategoryPaths::Flow}; + return Categories; + } + + return {}; +} + +FAssetSupportResponse UAssetDefinition_FlowAsset::CanLocalize(const FAssetData& InAsset) const +{ + return FAssetSupportResponse::Supported(); +} + +EAssetCommandResult UAssetDefinition_FlowAsset::OpenAssets(const FAssetOpenArgs& OpenArgs) const +{ + for (UFlowAsset* FlowAsset : OpenArgs.LoadObjects()) + { + const FFlowEditorModule* FlowModule = &FModuleManager::LoadModuleChecked("FlowEditor"); + FlowModule->CreateFlowAssetEditor(OpenArgs.GetToolkitMode(), OpenArgs.ToolkitHost, FlowAsset); + } + + return EAssetCommandResult::Handled; +} + +EAssetCommandResult UAssetDefinition_FlowAsset::PerformAssetDiff(const FAssetDiffArgs& DiffArgs) const +{ + if (DiffArgs.OldAsset == nullptr && DiffArgs.NewAsset == nullptr) + { + return EAssetCommandResult::Unhandled; + } + + const UFlowAsset* OldFlow = CastChecked(DiffArgs.OldAsset); + const UFlowAsset* NewFlow = CastChecked(DiffArgs.NewAsset); + + // sometimes we're comparing different revisions of one single asset (other + // times we're comparing two completely separate assets altogether) + const bool bIsSingleAsset = (OldFlow->GetName() == NewFlow->GetName()); + + static const FText BasicWindowTitle = LOCTEXT("FlowAssetDiff", "FlowAsset Diff"); + const FText WindowTitle = !bIsSingleAsset ? BasicWindowTitle : FText::Format(LOCTEXT("FlowAsset Diff", "{0} - FlowAsset Diff"), FText::FromString(NewFlow->GetName())); + + SFlowDiff::CreateDiffWindow(WindowTitle, OldFlow, NewFlow, DiffArgs.OldRevision, DiffArgs.NewRevision); + return EAssetCommandResult::Handled; +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/Asset/AssetTypeActions_FlowAsset.cpp b/Source/FlowEditor/Private/Asset/AssetTypeActions_FlowAsset.cpp deleted file mode 100644 index 23d0d0d77..000000000 --- a/Source/FlowEditor/Private/Asset/AssetTypeActions_FlowAsset.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#include "Asset/AssetTypeActions_FlowAsset.h" -#include "Asset/SFlowDiff.h" -#include "FlowEditorModule.h" -#include "Graph/FlowGraphSettings.h" - -#include "FlowAsset.h" - -#include "Toolkits/IToolkit.h" - -#define LOCTEXT_NAMESPACE "AssetTypeActions_FlowAsset" - -FText FAssetTypeActions_FlowAsset::GetName() const -{ - return LOCTEXT("AssetTypeActions_FlowAsset", "Flow Asset"); -} - -uint32 FAssetTypeActions_FlowAsset::GetCategories() -{ - return UFlowGraphSettings::Get()->bExposeFlowAssetCreation ? FFlowEditorModule::FlowAssetCategory : 0; -} - -UClass* FAssetTypeActions_FlowAsset::GetSupportedClass() const -{ - return UFlowAsset::StaticClass(); -} - -void FAssetTypeActions_FlowAsset::OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor) -{ - const EToolkitMode::Type Mode = EditWithinLevelEditor.IsValid() ? EToolkitMode::WorldCentric : EToolkitMode::Standalone; - - for (auto ObjIt = InObjects.CreateConstIterator(); ObjIt; ++ObjIt) - { - if (UFlowAsset* FlowAsset = Cast(*ObjIt)) - { - const FFlowEditorModule* FlowModule = &FModuleManager::LoadModuleChecked("FlowEditor"); - FlowModule->CreateFlowAssetEditor(Mode, EditWithinLevelEditor, FlowAsset); - } - } -} - -void FAssetTypeActions_FlowAsset::PerformAssetDiff(UObject* OldAsset, UObject* NewAsset, const FRevisionInfo& OldRevision, const FRevisionInfo& NewRevision) const -{ - const UFlowAsset* OldFlow = CastChecked(OldAsset); - const UFlowAsset* NewFlow = CastChecked(NewAsset); - - // sometimes we're comparing different revisions of one single asset (other - // times we're comparing two completely separate assets altogether) - const bool bIsSingleAsset = (OldFlow->GetName() == NewFlow->GetName()); - - static const FText BasicWindowTitle = LOCTEXT("FlowAssetDiff", "FlowAsset Diff"); - const FText WindowTitle = !bIsSingleAsset ? BasicWindowTitle : FText::Format(LOCTEXT("FlowAsset Diff", "{0} - FlowAsset Diff"), FText::FromString(NewFlow->GetName())); - - SFlowDiff::CreateDiffWindow(WindowTitle, OldFlow, NewFlow, OldRevision, NewRevision); -} - -#undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/FlowEditorModule.cpp b/Source/FlowEditor/Private/FlowEditorModule.cpp index 7b520f488..fb930822d 100644 --- a/Source/FlowEditor/Private/FlowEditorModule.cpp +++ b/Source/FlowEditor/Private/FlowEditorModule.cpp @@ -3,7 +3,6 @@ #include "FlowEditorModule.h" #include "FlowEditorStyle.h" -#include "Asset/AssetTypeActions_FlowAsset.h" #include "Asset/FlowAssetEditor.h" #include "Asset/FlowAssetIndexer.h" #include "Graph/FlowGraphConnectionDrawingPolicy.h" @@ -56,6 +55,7 @@ static FName AssetSearchModuleName = TEXT("AssetSearch"); #define LOCTEXT_NAMESPACE "FlowEditorModule" EAssetTypeCategories::Type FFlowEditorModule::FlowAssetCategory = static_cast(0); +FAssetCategoryPath FFLowAssetCategoryPaths::Flow(LOCTEXT("Flow", "Flow")); void FFlowEditorModule::StartupModule() { @@ -166,10 +166,6 @@ void FFlowEditorModule::RegisterAssets() } } - const TSharedRef FlowAssetActions = MakeShareable(new FAssetTypeActions_FlowAsset()); - RegisteredAssetActions.Add(FlowAssetActions); - AssetTools.RegisterAssetTypeActions(FlowAssetActions); - const TSharedRef FlowNodeActions = MakeShareable(new FAssetTypeActions_FlowNodeBlueprint()); RegisteredAssetActions.Add(FlowNodeActions); AssetTools.RegisterAssetTypeActions(FlowNodeActions); diff --git a/Source/FlowEditor/Public/Asset/AssetDefinition_FlowAsset.h b/Source/FlowEditor/Public/Asset/AssetDefinition_FlowAsset.h new file mode 100644 index 000000000..e4aeb8c5f --- /dev/null +++ b/Source/FlowEditor/Public/Asset/AssetDefinition_FlowAsset.h @@ -0,0 +1,25 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "AssetDefinition.h" +#include "AssetDefinition_FlowAsset.generated.h" + +/** + * + */ +UCLASS() +class FLOWEDITOR_API UAssetDefinition_FlowAsset : public UAssetDefinition +{ + GENERATED_BODY() + +public: + virtual FText GetAssetDisplayName() const override; + virtual FLinearColor GetAssetColor() const override; + virtual TSoftClassPtr GetAssetClass() const override; + virtual TConstArrayView GetAssetCategories() const override; + virtual FAssetSupportResponse CanLocalize(const FAssetData& InAsset) const override; + + virtual EAssetCommandResult OpenAssets(const FAssetOpenArgs& OpenArgs) const override; + virtual EAssetCommandResult PerformAssetDiff(const FAssetDiffArgs& DiffArgs) const override; +}; diff --git a/Source/FlowEditor/Public/Asset/AssetTypeActions_FlowAsset.h b/Source/FlowEditor/Public/Asset/AssetTypeActions_FlowAsset.h deleted file mode 100644 index 8cbd2f330..000000000 --- a/Source/FlowEditor/Public/Asset/AssetTypeActions_FlowAsset.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#pragma once - -#include "AssetTypeActions_Base.h" -#include "Toolkits/IToolkitHost.h" - -class FLOWEDITOR_API FAssetTypeActions_FlowAsset : public FAssetTypeActions_Base -{ -public: - virtual FText GetName() const override; - virtual uint32 GetCategories() override; - virtual FColor GetTypeColor() const override { return FColor(255, 196, 128); } - - virtual UClass* GetSupportedClass() const override; - virtual void OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor = TSharedPtr()) override; - - virtual void PerformAssetDiff(UObject* OldAsset, UObject* NewAsset, const FRevisionInfo& OldRevision, const FRevisionInfo& NewRevision) const override; -}; diff --git a/Source/FlowEditor/Public/FlowEditorModule.h b/Source/FlowEditor/Public/FlowEditorModule.h index ad3a15f7e..49b578249 100644 --- a/Source/FlowEditor/Public/FlowEditorModule.h +++ b/Source/FlowEditor/Public/FlowEditorModule.h @@ -15,6 +15,11 @@ struct FGraphPanelPinConnectionFactory; class FFlowAssetEditor; class UFlowAsset; +struct FLOWEDITOR_API FFLowAssetCategoryPaths : EAssetCategoryPaths +{ + static FAssetCategoryPath Flow; +}; + class FLOWEDITOR_API FFlowEditorModule : public IModuleInterface { public: From 1141e4081605dfafe3b1eb56f334bfbdb9daed61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Mon, 18 Nov 2024 00:11:54 +0100 Subject: [PATCH 282/485] Renaming new tags now used to define the Display Style of nodes - Wrapped all tags in the FlowNodeStyle namespace which lets us shorten tags from TAG_Flow_NodeDisplayStyle_Node_Condition to FlowNodeStyle::Condition. - Changed the naming of one option: "Node" (which was previously named "Default" as enum option) changed to "Base". - Flow.NodeDisplayStyle tag category is shortened to Flow.NodeStyle as this would intuitively replace the ENodeStyle enum for the perspective of users. --- Source/Flow/Private/AddOns/FlowNodeAddOn.cpp | 2 +- .../AddOns/FlowNodeAddOn_PredicateAND.cpp | 2 +- .../AddOns/FlowNodeAddOn_PredicateNOT.cpp | 2 +- .../AddOns/FlowNodeAddOn_PredicateOR.cpp | 2 +- Source/Flow/Private/FlowTags.cpp | 30 +++---- .../DataPins/FlowNode_DefineProperties.cpp | 2 +- Source/Flow/Private/Nodes/FlowNode.cpp | 2 - Source/Flow/Private/Nodes/FlowNodeBase.cpp | 87 +++++++++---------- .../Nodes/Operators/FlowNode_LogicalAND.cpp | 2 +- .../Nodes/Operators/FlowNode_LogicalOR.cpp | 2 +- .../Private/Nodes/Route/FlowNode_Branch.cpp | 2 +- .../Private/Nodes/Route/FlowNode_Counter.cpp | 2 +- .../Nodes/Route/FlowNode_CustomEventBase.cpp | 2 +- .../Route/FlowNode_ExecutionMultiGate.cpp | 2 +- .../Route/FlowNode_ExecutionSequence.cpp | 2 +- .../Private/Nodes/Route/FlowNode_Finish.cpp | 2 +- .../Private/Nodes/Route/FlowNode_Start.cpp | 2 +- .../Private/Nodes/Route/FlowNode_SubGraph.cpp | 2 +- .../Private/Nodes/Route/FlowNode_Timer.cpp | 2 +- .../Flow/Private/Nodes/Utils/FlowNode_Log.cpp | 2 +- .../World/FlowNode_CallOwnerFunction.cpp | 2 +- .../World/FlowNode_ComponentObserver.cpp | 2 +- .../Nodes/World/FlowNode_ExecuteComponent.cpp | 1 - .../World/FlowNode_OnNotifyFromActor.cpp | 2 +- .../World/FlowNode_PlayLevelSequence.cpp | 2 +- Source/Flow/Public/FlowTags.h | 33 +++---- Source/Flow/Public/Nodes/FlowNodeBase.h | 4 +- .../FlowEditor/Private/FlowEditorModule.cpp | 32 +++---- .../Private/Graph/FlowGraphSettings.cpp | 4 +- .../Public/Graph/FlowGraphSettings.h | 45 ++++++---- 30 files changed, 142 insertions(+), 138 deletions(-) diff --git a/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp b/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp index 621aa08a8..c627bdb18 100644 --- a/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp +++ b/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp @@ -10,7 +10,7 @@ UFlowNodeAddOn::UFlowNodeAddOn() { #if WITH_EDITOR - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_AddOn; + NodeDisplayStyle = FlowNodeStyle::AddOn; #endif } diff --git a/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateAND.cpp b/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateAND.cpp index 1c5e63ba2..5a0f3ec8f 100644 --- a/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateAND.cpp +++ b/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateAND.cpp @@ -8,7 +8,7 @@ UFlowNodeAddOn_PredicateAND::UFlowNodeAddOn_PredicateAND() : Super() { #if WITH_EDITOR - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_AddOn_Predicate_Composite; + NodeDisplayStyle = FlowNodeStyle::AddOn_Predicate_Composite; Category = TEXT("Composite"); #endif } diff --git a/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateNOT.cpp b/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateNOT.cpp index 3cc58c423..74952da78 100644 --- a/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateNOT.cpp +++ b/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateNOT.cpp @@ -8,7 +8,7 @@ UFlowNodeAddOn_PredicateNOT::UFlowNodeAddOn_PredicateNOT() : Super() { #if WITH_EDITOR - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_AddOn_Predicate_Composite; + NodeDisplayStyle = FlowNodeStyle::AddOn_Predicate_Composite; Category = TEXT("Composite"); #endif } diff --git a/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateOR.cpp b/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateOR.cpp index 8ec0a3dc2..4dfa04223 100644 --- a/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateOR.cpp +++ b/Source/Flow/Private/AddOns/FlowNodeAddOn_PredicateOR.cpp @@ -8,7 +8,7 @@ UFlowNodeAddOn_PredicateOR::UFlowNodeAddOn_PredicateOR() : Super() { #if WITH_EDITOR - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_AddOn_Predicate_Composite; + NodeDisplayStyle = FlowNodeStyle::AddOn_Predicate_Composite; Category = TEXT("Composite"); #endif } diff --git a/Source/Flow/Private/FlowTags.cpp b/Source/Flow/Private/FlowTags.cpp index c526cbff4..c3f7cbeda 100644 --- a/Source/Flow/Private/FlowTags.cpp +++ b/Source/Flow/Private/FlowTags.cpp @@ -2,20 +2,20 @@ #include "FlowTags.h" -UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle, "Flow.NodeDisplayStyle"); -UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle_Custom, "Flow.NodeDisplayStyle.Custom"); +UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::CategoryName, "Flow.NodeStyle"); +UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::Custom, "Flow.NodeStyle.Custom"); -UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle_Node, "Flow.NodeDisplayStyle.Node"); -UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle_Node_Condition, "Flow.NodeDisplayStyle.Node.Condition"); -UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle_Node_Deprecated, "Flow.NodeDisplayStyle.Node.Deprecated"); -UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle_Node_Developer, "Flow.NodeDisplayStyle.Node.Developer"); -UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle_Node_InOut, "Flow.NodeDisplayStyle.Node.InOut"); -UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle_Node_Latent, "Flow.NodeDisplayStyle.Node.Latent"); -UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle_Node_Logic, "Flow.NodeDisplayStyle.Node.Logic"); -UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle_Node_SubGraph, "Flow.NodeDisplayStyle.Node.SubGraph"); -UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle_Node_Terminal, "Flow.NodeDisplayStyle.Node.Terminal"); +UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::Base, "Flow.NodeStyle.Base"); +UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::Condition, "Flow.NodeStyle.Node.Condition"); +UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::Deprecated, "Flow.NodeStyle.Node.Deprecated"); +UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::Developer, "Flow.NodeStyle.Node.Developer"); +UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::InOut, "Flow.NodeStyle.Node.InOut"); +UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::Latent, "Flow.NodeStyle.Node.Latent"); +UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::Logic, "Flow.NodeStyle.Node.Logic"); +UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::SubGraph, "Flow.NodeStyle.Node.SubGraph"); +UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::Terminal, "Flow.NodeStyle.Node.Terminal"); -UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle_AddOn, "Flow.NodeDisplayStyle.AddOn"); -UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle_AddOn_PerSpawnedActor, "Flow.NodeDisplayStyle.AddOn.PerSpawnedActor"); -UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle_AddOn_Predicate, "Flow.NodeDisplayStyle.AddOn.Predicate"); -UE_DEFINE_GAMEPLAY_TAG(TAG_Flow_NodeDisplayStyle_AddOn_Predicate_Composite, "Flow.NodeDisplayStyle.AddOn.Predicate.Composite"); +UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::AddOn, "Flow.NodeStyle.AddOn"); +UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::AddOn_PerSpawnedActor, "Flow.NodeStyle.AddOn.PerSpawnedActor"); +UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::AddOn_Predicate, "Flow.NodeStyle.AddOn.Predicate"); +UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::AddOn_Predicate_Composite, "Flow.NodeStyle.AddOn.Predicate.Composite"); diff --git a/Source/Flow/Private/Nodes/DataPins/FlowNode_DefineProperties.cpp b/Source/Flow/Private/Nodes/DataPins/FlowNode_DefineProperties.cpp index daff72af1..5378e1710 100644 --- a/Source/Flow/Private/Nodes/DataPins/FlowNode_DefineProperties.cpp +++ b/Source/Flow/Private/Nodes/DataPins/FlowNode_DefineProperties.cpp @@ -8,7 +8,7 @@ UFlowNode_DefineProperties::UFlowNode_DefineProperties(const FObjectInitializer& : Super(ObjectInitializer) { #if WITH_EDITOR - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_InOut; + NodeDisplayStyle = FlowNodeStyle::InOut; Category = TEXT("Data Pins"); #endif diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 6c3fc85e8..00fa3920c 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -36,8 +36,6 @@ UFlowNode::UFlowNode(const FObjectInitializer& ObjectInitializer) { #if WITH_EDITOR Category = TEXT("Uncategorized"); - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node; - NodeColor = FLinearColor::Black; #endif InputPins = {DefaultInputPin}; diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp index fe74c19e2..58279ec84 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBase.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -36,7 +36,7 @@ UFlowNodeBase::UFlowNodeBase(const FObjectInitializer& ObjectInitializer) , bCanDelete(true) , bCanDuplicate(true) , bNodeDeprecated(false) - , NodeDisplayStyle(TAG_Flow_NodeDisplayStyle_Node) + , NodeDisplayStyle(FlowNodeStyle::Base) , NodeStyle(EFlowNodeStyle::Invalid) , NodeColor(FLinearColor::Black) #endif @@ -646,8 +646,7 @@ FString UFlowNodeBase::GetNodeCategory() const bool UFlowNodeBase::GetDynamicTitleColor(FLinearColor& OutColor) const { // Legacy asset support for NodeStyle == EFlowNodeStyle::Custom - if (NodeDisplayStyle == TAG_Flow_NodeDisplayStyle_Custom || - NodeStyle == EFlowNodeStyle::Custom) + if (NodeDisplayStyle == FlowNodeStyle::Custom || NodeStyle == EFlowNodeStyle::Custom) { OutColor = NodeColor; return true; @@ -727,6 +726,8 @@ FText UFlowNodeBase::GetGeneratedDisplayName() const void UFlowNodeBase::EnsureNodeDisplayStyle() { + // todo: remove in Flow 2.1 + // Backward compatibility update to convert NodeStyle to NodeDisplayStyle FLOW_ASSERT_ENUM_MAX(EFlowNodeStyle, 7); @@ -734,55 +735,47 @@ void UFlowNodeBase::EnsureNodeDisplayStyle() switch (NodeStyle) { - case EFlowNodeStyle::Condition: - { - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Condition; - } - break; - - case EFlowNodeStyle::Default: - { - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node; - } - break; - - case EFlowNodeStyle::InOut: - { - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_InOut; - } - break; - - case EFlowNodeStyle::Latent: - { - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Latent; - } - break; - - case EFlowNodeStyle::Logic: - { - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Logic; - } - break; - - case EFlowNodeStyle::SubGraph: - { - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_SubGraph; - } - break; - - case EFlowNodeStyle::Custom: - { - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Custom; - } - break; - - default: break; + case EFlowNodeStyle::Condition: + { + NodeDisplayStyle = FlowNodeStyle::Condition; + } + break; + case EFlowNodeStyle::Default: + { + NodeDisplayStyle = FlowNodeStyle::Base; + } + break; + case EFlowNodeStyle::InOut: + { + NodeDisplayStyle = FlowNodeStyle::InOut; + } + break; + case EFlowNodeStyle::Latent: + { + NodeDisplayStyle = FlowNodeStyle::Latent; + } + break; + case EFlowNodeStyle::Logic: + { + NodeDisplayStyle = FlowNodeStyle::Logic; + } + break; + case EFlowNodeStyle::SubGraph: + { + NodeDisplayStyle = FlowNodeStyle::SubGraph; + } + break; + case EFlowNodeStyle::Custom: + { + NodeDisplayStyle = FlowNodeStyle::Custom; + } + break; + default: break; } if (GEditor != nullptr && NodeDisplayStyle != NodeDisplayStylePrev) { NodeStyle = EFlowNodeStyle::Invalid; - Modify(); } } diff --git a/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalAND.cpp b/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalAND.cpp index 3e74094e8..e49556897 100644 --- a/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalAND.cpp +++ b/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalAND.cpp @@ -9,7 +9,7 @@ UFlowNode_LogicalAND::UFlowNode_LogicalAND(const FObjectInitializer& ObjectIniti { #if WITH_EDITOR Category = TEXT("Operators"); - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Logic; + NodeDisplayStyle = FlowNodeStyle::Logic; #endif SetNumberedInputPins(0, 1); diff --git a/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp b/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp index 17caf54f2..aee293887 100644 --- a/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp +++ b/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp @@ -12,7 +12,7 @@ UFlowNode_LogicalOR::UFlowNode_LogicalOR(const FObjectInitializer& ObjectInitial { #if WITH_EDITOR Category = TEXT("Operators"); - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Logic; + NodeDisplayStyle = FlowNodeStyle::Logic; #endif SetNumberedInputPins(0, 1); diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Branch.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Branch.cpp index e48023170..2a9c848b1 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Branch.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Branch.cpp @@ -14,7 +14,7 @@ UFlowNode_Branch::UFlowNode_Branch(const FObjectInitializer& ObjectInitializer) { #if WITH_EDITOR Category = TEXT("Route"); - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Logic; + NodeDisplayStyle = FlowNodeStyle::Logic; #endif InputPins.Empty(); InputPins.Add(FFlowPin(INPIN_Evaluate)); diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Counter.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Counter.cpp index 9d8978fb8..f6a4cd701 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Counter.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Counter.cpp @@ -11,7 +11,7 @@ UFlowNode_Counter::UFlowNode_Counter(const FObjectInitializer& ObjectInitializer { #if WITH_EDITOR Category = TEXT("Route"); - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Condition; + NodeDisplayStyle = FlowNodeStyle::Condition; #endif InputPins.Empty(); diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_CustomEventBase.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_CustomEventBase.cpp index e9cb3fd10..0f40d0a23 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_CustomEventBase.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_CustomEventBase.cpp @@ -10,7 +10,7 @@ UFlowNode_CustomEventBase::UFlowNode_CustomEventBase(const FObjectInitializer& O { #if WITH_EDITOR Category = TEXT("Route"); - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_InOut; + NodeDisplayStyle = FlowNodeStyle::InOut; #endif AllowedSignalModes = {EFlowSignalMode::Enabled, EFlowSignalMode::Disabled}; diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp index d6d7f6a57..a7e05289e 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp @@ -10,7 +10,7 @@ UFlowNode_ExecutionMultiGate::UFlowNode_ExecutionMultiGate(const FObjectInitiali { #if WITH_EDITOR Category = TEXT("Route"); - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Logic; + NodeDisplayStyle = FlowNodeStyle::Logic; #endif FString ResetPinTooltip = TEXT("Finish work of this node."); diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp index 0b9e5e5c0..386dbaa81 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp @@ -10,7 +10,7 @@ UFlowNode_ExecutionSequence::UFlowNode_ExecutionSequence(const FObjectInitialize { #if WITH_EDITOR Category = TEXT("Route"); - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Logic; + NodeDisplayStyle = FlowNodeStyle::Logic; #endif SetNumberedOutputPins(0, 1); diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp index 0b16ec5b0..5a9f98ecf 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp @@ -9,7 +9,7 @@ UFlowNode_Finish::UFlowNode_Finish(const FObjectInitializer& ObjectInitializer) { #if WITH_EDITOR Category = TEXT("Route"); - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_InOut; + NodeDisplayStyle = FlowNodeStyle::InOut; #endif OutputPins = {}; diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp index 8aed45e11..264c1326b 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp @@ -9,7 +9,7 @@ UFlowNode_Start::UFlowNode_Start(const FObjectInitializer& ObjectInitializer) { #if WITH_EDITOR Category = TEXT("Route"); - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_InOut; + NodeDisplayStyle = FlowNodeStyle::InOut; bCanDelete = bCanDuplicate = false; #endif diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp index 1e936d247..45311c4da 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp @@ -20,7 +20,7 @@ UFlowNode_SubGraph::UFlowNode_SubGraph(const FObjectInitializer& ObjectInitializ { #if WITH_EDITOR Category = TEXT("Route"); - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_SubGraph; + NodeDisplayStyle = FlowNodeStyle::SubGraph; AllowedAssignedAssetClasses = {UFlowAsset::StaticClass()}; #endif diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp index 06a4c56e5..184862c39 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp @@ -23,7 +23,7 @@ UFlowNode_Timer::UFlowNode_Timer(const FObjectInitializer& ObjectInitializer) { #if WITH_EDITOR Category = TEXT("Route"); - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Latent; + NodeDisplayStyle = FlowNodeStyle::Latent; #endif InputPins.Add(FFlowPin(TEXT("Skip"))); diff --git a/Source/Flow/Private/Nodes/Utils/FlowNode_Log.cpp b/Source/Flow/Private/Nodes/Utils/FlowNode_Log.cpp index 22e6a259c..8e75cab13 100644 --- a/Source/Flow/Private/Nodes/Utils/FlowNode_Log.cpp +++ b/Source/Flow/Private/Nodes/Utils/FlowNode_Log.cpp @@ -20,7 +20,7 @@ UFlowNode_Log::UFlowNode_Log(const FObjectInitializer& ObjectInitializer) { #if WITH_EDITOR Category = TEXT("Utils"); - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Developer; + NodeDisplayStyle = FlowNodeStyle::Developer; #endif } diff --git a/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp b/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp index 8ee25878e..b657328eb 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp @@ -17,7 +17,7 @@ UFlowNode_CallOwnerFunction::UFlowNode_CallOwnerFunction(const FObjectInitialize , Params(nullptr) { #if WITH_EDITOR - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Deprecated; + NodeDisplayStyle = FlowNodeStyle::Deprecated; Category = TEXT("World"); #endif // WITH_EDITOR } diff --git a/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp b/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp index 7a49d877c..6ae5b104d 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp @@ -12,7 +12,7 @@ UFlowNode_ComponentObserver::UFlowNode_ComponentObserver(const FObjectInitialize , SuccessCount(0) { #if WITH_EDITOR - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Condition; + NodeDisplayStyle = FlowNodeStyle::Condition; Category = TEXT("World"); #endif diff --git a/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp b/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp index 59286d4cb..16f27e3a0 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp @@ -20,7 +20,6 @@ UFlowNode_ExecuteComponent::UFlowNode_ExecuteComponent() : Super() { #if WITH_EDITOR - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node; Category = TEXT("World"); #endif // WITH_EDITOR } diff --git a/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp b/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp index 9205594f7..0d03355ce 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp @@ -11,7 +11,7 @@ UFlowNode_OnNotifyFromActor::UFlowNode_OnNotifyFromActor(const FObjectInitialize { #if WITH_EDITOR Category = TEXT("Notifies"); - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Condition; + NodeDisplayStyle = FlowNodeStyle::Condition; #endif } diff --git a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp index a1627a59a..b52769417 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp @@ -37,7 +37,7 @@ UFlowNode_PlayLevelSequence::UFlowNode_PlayLevelSequence(const FObjectInitialize { #if WITH_EDITOR Category = TEXT("World"); - NodeDisplayStyle = TAG_Flow_NodeDisplayStyle_Node_Latent; + NodeDisplayStyle = FlowNodeStyle::Latent; #endif InputPins.Empty(); diff --git a/Source/Flow/Public/FlowTags.h b/Source/Flow/Public/FlowTags.h index 3ae5699b9..cd9ff0ce3 100644 --- a/Source/Flow/Public/FlowTags.h +++ b/Source/Flow/Public/FlowTags.h @@ -4,20 +4,23 @@ #include "NativeGameplayTags.h" -FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle); -FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle_Custom); +namespace FlowNodeStyle +{ + FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(CategoryName); + FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Custom); -FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle_Node); -FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle_Node_Condition); -FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle_Node_Deprecated); -FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle_Node_Developer); -FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle_Node_InOut); -FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle_Node_Latent); -FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle_Node_Logic); -FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle_Node_SubGraph); -FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle_Node_Terminal); + FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Base); + FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Condition); + FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Deprecated); + FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Developer); + FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InOut); + FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Latent); + FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Logic); + FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(SubGraph); + FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Terminal); -FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle_AddOn); -FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle_AddOn_PerSpawnedActor); -FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle_AddOn_Predicate); -FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Flow_NodeDisplayStyle_AddOn_Predicate_Composite); + FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(AddOn); + FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(AddOn_PerSpawnedActor); + FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(AddOn_Predicate); + FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(AddOn_Predicate_Composite); +} diff --git a/Source/Flow/Public/Nodes/FlowNodeBase.h b/Source/Flow/Public/Nodes/FlowNodeBase.h index 4670c9e13..831631cd8 100644 --- a/Source/Flow/Public/Nodes/FlowNodeBase.h +++ b/Source/Flow/Public/Nodes/FlowNodeBase.h @@ -329,14 +329,14 @@ class FLOW_API UFlowNodeBase UPROPERTY() FString Category; - UPROPERTY(EditDefaultsOnly, Category = "FlowNode", meta = (Categories = "Flow.NodeDisplayStyle")) + UPROPERTY(EditDefaultsOnly, Category = "FlowNode", meta = (Categories = "Flow.NodeStyle")) FGameplayTag NodeDisplayStyle; // Deprecated NodeStyle, replaced by NodeDisplayStyle UPROPERTY(meta = (DeprecatedProperty, DeprecationMessage = "Use the NodeDisplayStyle instead.")) EFlowNodeStyle NodeStyle; - // Set Node Style to custom to use your own color for this node (if using Flow.NodeDisplayStyle.Custom) + // Set Node Style to custom to use your own color for this node (if using Flow.NodeStyle.Custom) UPROPERTY(EditDefaultsOnly, Category = "FlowNode", DisplayName = "Custom Node Color") FLinearColor NodeColor; diff --git a/Source/FlowEditor/Private/FlowEditorModule.cpp b/Source/FlowEditor/Private/FlowEditorModule.cpp index fb930822d..fb17a6119 100644 --- a/Source/FlowEditor/Private/FlowEditorModule.cpp +++ b/Source/FlowEditor/Private/FlowEditorModule.cpp @@ -116,25 +116,25 @@ void FFlowEditorModule::ShutdownModule() void FFlowEditorModule::TrySetFlowNodeDisplayStyleDefaults() const { - // Force the flow module to be loaded before we try to access the Settings + // Force the Flow module to be loaded before we try to access the Settings FModuleManager::LoadModuleChecked("Flow"); UFlowGraphSettings& Settings = *UFlowGraphSettings::Get(); - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(TAG_Flow_NodeDisplayStyle, FLinearColor(0.0f, 0.581f, 1.0f, 1.0f))); - - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(TAG_Flow_NodeDisplayStyle_Node_Condition, FLinearColor(1.0f, 0.62f, 0.016f, 1.0f))); - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(TAG_Flow_NodeDisplayStyle_Node_Deprecated, FLinearColor(1.0f, 1.0f, 0.0f, 1.0f))); - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(TAG_Flow_NodeDisplayStyle_Node_Developer, FLinearColor(0.7f, 0.2f, 1.0f, 1.0f))); - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(TAG_Flow_NodeDisplayStyle_Node_InOut, FLinearColor(1.0f, 0.0f, 0.008f, 1.0f))); - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(TAG_Flow_NodeDisplayStyle_Node_Latent, FLinearColor(0.0f, 0.770f, 0.375f, 1.0f))); - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(TAG_Flow_NodeDisplayStyle_Node_Logic, FLinearColor(1.0f, 1.0f, 1.0f, 1.0f))); - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(TAG_Flow_NodeDisplayStyle_Node_SubGraph, FLinearColor(1.0f, 0.128f, 0.0f, 1.0f))); - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(TAG_Flow_NodeDisplayStyle_Node_Terminal, FLinearColor(1.0f, 0.0f, 0.008f, 1.0f))); - - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(TAG_Flow_NodeDisplayStyle_AddOn, FLinearColor(0.0f, 0.581f, 1.0f, 1.0f))); // !!! Update this - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(TAG_Flow_NodeDisplayStyle_AddOn_PerSpawnedActor, FLinearColor(0.3f, 0.3f, 1.0f, 1.0f))); - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(TAG_Flow_NodeDisplayStyle_AddOn_Predicate, FLinearColor(1.0f, 1.0f, 1.0f, 1.0f))); - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(TAG_Flow_NodeDisplayStyle_AddOn_Predicate_Composite, FLinearColor(1.0f, 1.0f, 1.0f, 1.0f))); // !!! Update this + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::Base, FLinearColor(0.0f, 0.581f, 1.0f, 1.0f))); + + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::Condition, FLinearColor(1.0f, 0.62f, 0.016f, 1.0f))); + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::Deprecated, FLinearColor(1.0f, 1.0f, 0.0f, 1.0f))); + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::Developer, FLinearColor(0.7f, 0.2f, 1.0f, 1.0f))); + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::InOut, FLinearColor(1.0f, 0.0f, 0.008f, 1.0f))); + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::Latent, FLinearColor(0.0f, 0.770f, 0.375f, 1.0f))); + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::Logic, FLinearColor(1.0f, 1.0f, 1.0f, 1.0f))); + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::SubGraph, FLinearColor(1.0f, 0.128f, 0.0f, 1.0f))); + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::Terminal, FLinearColor(1.0f, 0.0f, 0.008f, 1.0f))); + + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::AddOn, FLinearColor(0.0f, 0.581f, 1.0f, 1.0f))); // !!! Update this + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::AddOn_PerSpawnedActor, FLinearColor(0.3f, 0.3f, 1.0f, 1.0f))); + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::AddOn_Predicate, FLinearColor(1.0f, 1.0f, 1.0f, 1.0f))); + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::AddOn_Predicate_Composite, FLinearColor(1.0f, 1.0f, 1.0f, 1.0f))); // !!! Update this } void FFlowEditorModule::RegisterAssets() diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp index 67af3eef1..02d054ac1 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp @@ -7,7 +7,6 @@ #include "Graph/FlowGraphSchema.h" #include "Types/FlowGameplayTagMapUtils.h" -#include "Algo/Unique.h" #include "Framework/Notifications/NotificationManager.h" #include "Widgets/Notifications/SNotificationList.h" @@ -112,7 +111,6 @@ const TMap& UFlowGraphSettings::Ensur for (const FFlowNodeDisplayStyleConfig& Config : NodeDisplayStyles) { UnexpandedMap.Add(Config.Tag, Config); - NodeDisplayStylesAuthoredTags.AddTag(Config.Tag); } @@ -160,7 +158,7 @@ const FLinearColor* UFlowGraphSettings::LookupNodeTitleColorForNode(const UFlowN const FGameplayTag& StyleTag = FlowNodeBase.GetNodeDisplayStyle(); const TMap& StyleMap = EnsureNodeDisplayStylesMap(); - if (const FFlowNodeDisplayStyleConfig* Config = FlowMap::TryLookupGameplayTagKey(StyleTag, StyleMap, TAG_Flow_NodeDisplayStyle)) + if (const FFlowNodeDisplayStyleConfig* Config = FlowMap::TryLookupGameplayTagKey(StyleTag, StyleMap, FlowNodeStyle::CategoryName)) { return &Config->TitleColor; } diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h index 944250048..6369a551c 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h @@ -17,17 +17,34 @@ struct FFlowNodeDisplayStyleConfig GENERATED_BODY() public: - - FFlowNodeDisplayStyleConfig() {} - FFlowNodeDisplayStyleConfig(const FGameplayTag& InTag, const FLinearColor& InNodeColor) : Tag(InTag), TitleColor(InNodeColor) {} - - FORCEINLINE bool operator ==(const FFlowNodeDisplayStyleConfig& Other) const { return Tag == Other.Tag; } - FORCEINLINE bool operator !=(const FFlowNodeDisplayStyleConfig& Other) const { return Tag != Other.Tag; } - FORCEINLINE bool operator <(const FFlowNodeDisplayStyleConfig& Other) const { return Tag < Other.Tag; } + FFlowNodeDisplayStyleConfig() + : TitleColor() + { + } + + FFlowNodeDisplayStyleConfig(const FGameplayTag& InTag, const FLinearColor& InNodeColor) + : Tag(InTag) + , TitleColor(InNodeColor) + { + } + + FORCEINLINE bool operator ==(const FFlowNodeDisplayStyleConfig& Other) const + { + return Tag == Other.Tag; + } + + FORCEINLINE bool operator !=(const FFlowNodeDisplayStyleConfig& Other) const + { + return Tag != Other.Tag; + } + + FORCEINLINE bool operator <(const FFlowNodeDisplayStyleConfig& Other) const + { + return Tag < Other.Tag; + } public: - - UPROPERTY(Config, EditAnywhere, Category = "Nodes", meta = (Categories = "Flow.NodeDisplayStyle")) + UPROPERTY(Config, EditAnywhere, Category = "Nodes", meta = (Categories = "Flow.NodeStyle")) FGameplayTag Tag; UPROPERTY(Config, EditAnywhere, Category = "Nodes") @@ -41,7 +58,6 @@ UCLASS(Config = Editor, defaultconfig, meta = (DisplayName = "Flow Graph")) class FLOWEDITOR_API UFlowGraphSettings : public UDeveloperSettings { GENERATED_UCLASS_BODY() - static UFlowGraphSettings* Get() { return StaticClass()->GetDefaultObject(); } virtual void PostInitProperties() override; @@ -49,7 +65,7 @@ class FLOWEDITOR_API UFlowGraphSettings : public UDeveloperSettings #if WITH_EDITOR virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; #endif - + /** Show Flow Asset in Flow category of "Create Asset" menu? * Requires restart after making a change. */ UPROPERTY(EditAnywhere, config, Category = "Default UI", meta = (ConfigRestartRequired = true)) @@ -90,7 +106,7 @@ class FLOWEDITOR_API UFlowGraphSettings : public UDeveloperSettings UPROPERTY(EditAnywhere, config, Category = "Nodes") TArray NodePrefixesToRemove; - // Display Styles for nodes, keyed by gameplay tag + // Display Styles for nodes, keyed by Gameplay Tag UPROPERTY(EditAnywhere, config, Category = "Nodes", meta = (TitleProperty = "{Tag}}")) TArray NodeDisplayStyles; @@ -99,7 +115,7 @@ class FLOWEDITOR_API UFlowGraphSettings : public UDeveloperSettings UPROPERTY(Transient) FGameplayTagContainer NodeDisplayStylesAuthoredTags; - // Cached map of the data in NodeDisplayStyles for gameplaytag-keyed lookup + // Cached map of the data in NodeDisplayStyles for GameplayTag-keyed lookup UPROPERTY(Transient) TMap NodeDisplayStylesMap; #endif @@ -165,10 +181,7 @@ class FLOWEDITOR_API UFlowGraphSettings : public UDeveloperSettings #if WITH_EDITOR const TMap& EnsureNodeDisplayStylesMap(); - bool TryAddDefaultNodeDisplayStyle(const FFlowNodeDisplayStyleConfig& StyleConfig); - const FLinearColor* LookupNodeTitleColorForNode(const UFlowNodeBase& FlowNodeBase); - #endif }; From a517aae50b0092d93791c1c701d65b566044c025 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Mon, 18 Nov 2024 01:34:16 +0100 Subject: [PATCH 283/485] DataPinValueSupplier defaults to nullptr Useful in case of instantiating root graph programmatically from project-specific code --- Source/Flow/Private/FlowSubsystem.cpp | 6 ++---- Source/Flow/Public/FlowAsset.h | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index d8318e233..2e2e4071b 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -82,11 +82,9 @@ void UFlowSubsystem::StartRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const { if (UFlowAsset* NewFlow = CreateRootFlow(Owner, FlowAsset, bAllowMultipleInstances)) { - // TODO (gtaylor) In the future, we may want to provide a way to set a data pin value supplier + // todo: (gtaylor) In the future, we may want to provide a way to set a data pin value supplier // for the root flow graph. - constexpr IFlowDataPinValueSupplierInterface* DataPinValueSupplier = nullptr; - - NewFlow->StartFlow(DataPinValueSupplier); + NewFlow->StartFlow(); } } #if WITH_EDITOR diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index f6a6e118c..e4392f700 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -387,7 +387,7 @@ class FLOW_API UFlowAsset : public UObject virtual void PreloadNodes() {} virtual void PreStartFlow(); - virtual void StartFlow(IFlowDataPinValueSupplierInterface* DataPinValueSupplier); + virtual void StartFlow(IFlowDataPinValueSupplierInterface* DataPinValueSupplier = nullptr); virtual void FinishFlow(const EFlowFinishPolicy InFinishPolicy, const bool bRemoveInstance = true); From c3a6826929a02fcb99cca9e448b5e597e5727891 Mon Sep 17 00:00:00 2001 From: LindyHopperGT <91915878+LindyHopperGT@users.noreply.github.com> Date: Thu, 21 Nov 2024 13:25:43 -0800 Subject: [PATCH 284/485] Post-PR 231 merge revisions PR (#240) * Latest updates post-merge with mainline * Removed stray comment * Bugfix for modifying every flow asset that is opened in the editor. We were unilaterally modifying all flow nodes by reconstructing their nodes when starting editing a flow asset, which is not what we want or need to do. --------- Co-authored-by: FoolsTheoryDev --- Source/Flow/Private/FlowAsset.cpp | 2 +- Source/Flow/Private/FlowTags.cpp | 3 ++- Source/Flow/Private/Nodes/FlowNode.cpp | 1 + Source/Flow/Private/Nodes/FlowNodeBase.cpp | 4 ++-- .../Private/Nodes/World/FlowNode_CallOwnerFunction.cpp | 2 +- Source/Flow/Public/FlowTags.h | 3 ++- Source/FlowEditor/FlowEditor.Build.cs | 2 +- Source/FlowEditor/Private/FlowEditorModule.cpp | 7 +++---- Source/FlowEditor/Private/Graph/FlowGraph.cpp | 8 +++++--- Source/FlowEditor/Public/Graph/FlowGraphSettings.h | 1 + 10 files changed, 19 insertions(+), 14 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 29556d1f9..8096fc310 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -1008,7 +1008,7 @@ TArray UFlowAsset::GetNodesInExecutionOrder(UFlowNode* FirstIterated } } FoundNodes.Shrink(); - + return FoundNodes; } diff --git a/Source/Flow/Private/FlowTags.cpp b/Source/Flow/Private/FlowTags.cpp index c3f7cbeda..9ee1342fd 100644 --- a/Source/Flow/Private/FlowTags.cpp +++ b/Source/Flow/Private/FlowTags.cpp @@ -5,7 +5,8 @@ UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::CategoryName, "Flow.NodeStyle"); UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::Custom, "Flow.NodeStyle.Custom"); -UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::Base, "Flow.NodeStyle.Base"); +UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::Node, "Flow.NodeStyle.Node"); +UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::Default, "Flow.NodeStyle.Node.Default"); UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::Condition, "Flow.NodeStyle.Node.Condition"); UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::Deprecated, "Flow.NodeStyle.Node.Deprecated"); UE_DEFINE_GAMEPLAY_TAG(FlowNodeStyle::Developer, "Flow.NodeStyle.Node.Developer"); diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 00fa3920c..c58c0252d 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -36,6 +36,7 @@ UFlowNode::UFlowNode(const FObjectInitializer& ObjectInitializer) { #if WITH_EDITOR Category = TEXT("Uncategorized"); + NodeDisplayStyle = FlowNodeStyle::Default; #endif InputPins = {DefaultInputPin}; diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp index 58279ec84..fe646b4b0 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBase.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -36,7 +36,7 @@ UFlowNodeBase::UFlowNodeBase(const FObjectInitializer& ObjectInitializer) , bCanDelete(true) , bCanDuplicate(true) , bNodeDeprecated(false) - , NodeDisplayStyle(FlowNodeStyle::Base) + , NodeDisplayStyle(FlowNodeStyle::Node) , NodeStyle(EFlowNodeStyle::Invalid) , NodeColor(FLinearColor::Black) #endif @@ -742,7 +742,7 @@ void UFlowNodeBase::EnsureNodeDisplayStyle() break; case EFlowNodeStyle::Default: { - NodeDisplayStyle = FlowNodeStyle::Base; + NodeDisplayStyle = FlowNodeStyle::Default; } break; case EFlowNodeStyle::InOut: diff --git a/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp b/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp index b657328eb..1670a4876 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp @@ -18,7 +18,7 @@ UFlowNode_CallOwnerFunction::UFlowNode_CallOwnerFunction(const FObjectInitialize { #if WITH_EDITOR NodeDisplayStyle = FlowNodeStyle::Deprecated; - Category = TEXT("World"); + Category = TEXT("Deprecated"); #endif // WITH_EDITOR } diff --git a/Source/Flow/Public/FlowTags.h b/Source/Flow/Public/FlowTags.h index cd9ff0ce3..1a88501d7 100644 --- a/Source/Flow/Public/FlowTags.h +++ b/Source/Flow/Public/FlowTags.h @@ -9,7 +9,8 @@ namespace FlowNodeStyle FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(CategoryName); FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Custom); - FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Base); + FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Node); + FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Default); FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Condition); FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Deprecated); FLOW_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Developer); diff --git a/Source/FlowEditor/FlowEditor.Build.cs b/Source/FlowEditor/FlowEditor.Build.cs index 2596d3cd7..4d851cd0b 100644 --- a/Source/FlowEditor/FlowEditor.Build.cs +++ b/Source/FlowEditor/FlowEditor.Build.cs @@ -15,6 +15,7 @@ public FlowEditor(ReadOnlyTargetRules target) : base(target) PublicDependencyModuleNames.AddRange(new[] { + "AssetSearch", "EditorSubsystem", "Flow", "MessageLog", @@ -25,7 +26,6 @@ public FlowEditor(ReadOnlyTargetRules target) : base(target) { "ApplicationCore", "AssetDefinition", - "AssetSearch", "AssetTools", "BlueprintGraph", "ClassViewer", diff --git a/Source/FlowEditor/Private/FlowEditorModule.cpp b/Source/FlowEditor/Private/FlowEditorModule.cpp index fb17a6119..8f50a55e0 100644 --- a/Source/FlowEditor/Private/FlowEditorModule.cpp +++ b/Source/FlowEditor/Private/FlowEditorModule.cpp @@ -120,8 +120,7 @@ void FFlowEditorModule::TrySetFlowNodeDisplayStyleDefaults() const FModuleManager::LoadModuleChecked("Flow"); UFlowGraphSettings& Settings = *UFlowGraphSettings::Get(); - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::Base, FLinearColor(0.0f, 0.581f, 1.0f, 1.0f))); - + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::Node, FLinearColor(0.0f, 0.581f, 1.0f, 1.0f))); (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::Condition, FLinearColor(1.0f, 0.62f, 0.016f, 1.0f))); (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::Deprecated, FLinearColor(1.0f, 1.0f, 0.0f, 1.0f))); (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::Developer, FLinearColor(0.7f, 0.2f, 1.0f, 1.0f))); @@ -131,10 +130,10 @@ void FFlowEditorModule::TrySetFlowNodeDisplayStyleDefaults() const (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::SubGraph, FLinearColor(1.0f, 0.128f, 0.0f, 1.0f))); (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::Terminal, FLinearColor(1.0f, 0.0f, 0.008f, 1.0f))); - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::AddOn, FLinearColor(0.0f, 0.581f, 1.0f, 1.0f))); // !!! Update this + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::AddOn, FLinearColor(0.0f, 0.581f, 1.0f, 1.0f))); (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::AddOn_PerSpawnedActor, FLinearColor(0.3f, 0.3f, 1.0f, 1.0f))); (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::AddOn_Predicate, FLinearColor(1.0f, 1.0f, 1.0f, 1.0f))); - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::AddOn_Predicate_Composite, FLinearColor(1.0f, 1.0f, 1.0f, 1.0f))); // !!! Update this + (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::AddOn_Predicate_Composite, FLinearColor(1.0f, 1.0f, 1.0f, 1.0f))); } void FFlowEditorModule::RegisterAssets() diff --git a/Source/FlowEditor/Private/Graph/FlowGraph.cpp b/Source/FlowEditor/Private/Graph/FlowGraph.cpp index f67f48733..f2a6dd9e1 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraph.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraph.cpp @@ -167,9 +167,11 @@ void UFlowGraph::RecursivelySetupAllFlowGraphNodesForEditing(UFlowGraphNode& Fro // Setup all of the flow node (and subnode) instances for editing FromNodeInstance->SetupForEditing(FromFlowGraphNode); } - - // Reconstruct the node when starting up editing - FromFlowGraphNode.ReconstructNode(); + else + { + // Reconstruct the node if the NodeInstance is missing + FromFlowGraphNode.ReconstructNode(); + } for (UFlowGraphNode* SubNode : FromFlowGraphNode.SubNodes) { diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h index 6369a551c..2ce8fdc16 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h @@ -58,6 +58,7 @@ UCLASS(Config = Editor, defaultconfig, meta = (DisplayName = "Flow Graph")) class FLOWEDITOR_API UFlowGraphSettings : public UDeveloperSettings { GENERATED_UCLASS_BODY() + static UFlowGraphSettings* Get() { return StaticClass()->GetDefaultObject(); } virtual void PostInitProperties() override; From 12193e10289c5c652b0caef5a7ab2c7515d10c89 Mon Sep 17 00:00:00 2001 From: Benxidosz <61600167+Benxidosz@users.noreply.github.com> Date: Thu, 21 Nov 2024 22:26:25 +0100 Subject: [PATCH 285/485] Initialize Data Pin Struct Values in order to remove errors (#241) (cherry picked from commit cd6575cc577b91b707e0fc9b1106dd7beaee2c4d) Co-authored-by: szabob --- Source/Flow/Public/Types/FlowDataPinProperties.h | 4 ++-- Source/Flow/Public/Types/FlowDataPinResults.h | 4 ++-- Source/FlowEditor/Public/Graph/FlowGraphSettings.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Flow/Public/Types/FlowDataPinProperties.h b/Source/Flow/Public/Types/FlowDataPinProperties.h index 45d0ac338..e425429de 100644 --- a/Source/Flow/Public/Types/FlowDataPinProperties.h +++ b/Source/Flow/Public/Types/FlowDataPinProperties.h @@ -263,7 +263,7 @@ struct FFlowDataPinOutputProperty_Vector : public FFlowDataPinProperty public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) - FVector Value; + FVector Value = FVector::ZeroVector; public: @@ -282,7 +282,7 @@ struct FFlowDataPinOutputProperty_Rotator : public FFlowDataPinProperty public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) - FRotator Value; + FRotator Value = FRotator::ZeroRotator; public: diff --git a/Source/Flow/Public/Types/FlowDataPinResults.h b/Source/Flow/Public/Types/FlowDataPinResults.h index c0d12cf6f..5c57cf334 100644 --- a/Source/Flow/Public/Types/FlowDataPinResults.h +++ b/Source/Flow/Public/Types/FlowDataPinResults.h @@ -234,7 +234,7 @@ struct FFlowDataPinResult_Vector : public FFlowDataPinResult public: UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) - FVector Value; + FVector Value = FVector::ZeroVector; public: @@ -253,7 +253,7 @@ struct FFlowDataPinResult_Rotator : public FFlowDataPinResult public: UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) - FRotator Value; + FRotator Value = FRotator::ZeroRotator; public: diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h index 2ce8fdc16..e23dbf5e0 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h @@ -18,7 +18,7 @@ struct FFlowNodeDisplayStyleConfig public: FFlowNodeDisplayStyleConfig() - : TitleColor() + : TitleColor(FLinearColor::White) { } From b040db4dd8d5ad00eedec556a65318286e001b10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Thu, 21 Nov 2024 22:30:25 +0100 Subject: [PATCH 286/485] empty line --- Source/Flow/Private/FlowAsset.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 8096fc310..29556d1f9 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -1008,7 +1008,7 @@ TArray UFlowAsset::GetNodesInExecutionOrder(UFlowNode* FirstIterated } } FoundNodes.Shrink(); - + return FoundNodes; } From 46d7bad13a189e0d7c5324b6027f7c95eb0fe186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Do=C4=9Fa=20Can=20Yan=C4=B1ko=C4=9Flu?= Date: Sun, 24 Nov 2024 14:08:28 +0300 Subject: [PATCH 287/485] TObjectPtr refactor (#242) --- Source/Flow/Private/FlowAsset.cpp | 16 ++++++++-------- Source/Flow/Private/FlowSubsystem.cpp | 4 ++-- Source/Flow/Public/FlowAsset.h | 16 ++++++++-------- Source/Flow/Public/FlowComponent.h | 2 +- Source/Flow/Public/FlowSubsystem.h | 10 +++++----- Source/Flow/Public/FlowWorldSettings.h | 2 +- .../LevelSequence/FlowLevelSequencePlayer.h | 2 +- .../Flow/Public/MovieScene/MovieSceneFlowTrack.h | 2 +- Source/Flow/Public/Nodes/FlowNodeBase.h | 6 +++--- Source/Flow/Public/Nodes/FlowPin.h | 2 +- .../Nodes/World/FlowNode_CallOwnerFunction.h | 2 +- .../Nodes/World/FlowNode_PlayLevelSequence.h | 4 ++-- Source/Flow/Public/Types/FlowDataPinProperties.h | 2 +- Source/Flow/Public/Types/FlowDataPinResults.h | 2 +- .../Public/Types/FlowInjectComponentsManager.h | 2 +- .../FlowActorOwnerComponentFilters.cpp | 6 +++--- .../FlowActorOwnerComponentFilters.h | 6 +++--- Source/FlowEditor/Public/Asset/FlowImportUtils.h | 2 +- .../Public/Graph/FlowGraphSchema_Actions.h | 2 +- .../Public/Graph/Nodes/FlowGraphNode.h | 2 +- 20 files changed, 46 insertions(+), 46 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 29556d1f9..400bde626 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -106,7 +106,7 @@ void UFlowAsset::PostLoad() EDataValidationResult UFlowAsset::ValidateAsset(FFlowMessageLog& MessageLog) { // validate nodes - for (const TPair& Node : Nodes) + for (const TPair& Node : ObjectPtrDecay(Nodes)) { if (IsValid(Node.Value)) { @@ -329,7 +329,7 @@ void UFlowAsset::HarvestNodeConnections() } } - for (const TPair& Pair : Nodes) + for (const TPair& Pair : ObjectPtrDecay(Nodes)) { UFlowNode* FlowNode = Pair.Value; TMap FoundConnections; @@ -878,7 +878,7 @@ UFlowNode* UFlowAsset::GetDefaultEntryNode() const { UFlowNode* FirstStartNode = nullptr; - for (const TPair& Node : Nodes) + for (const TPair& Node : ObjectPtrDecay(Nodes)) { if (UFlowNode_Start* StartNode = Cast(Node.Value)) { @@ -946,7 +946,7 @@ UFlowNode_CustomInput* UFlowAsset::TryFindCustomInputNodeByEventName(const FName UFlowNode_CustomOutput* UFlowAsset::TryFindCustomOutputNodeByEventName(const FName& EventName) const { - for (const TPair& Node : Nodes) + for (const TPair& Node : ObjectPtrDecay(Nodes)) { if (UFlowNode_CustomOutput* CustomOutput = Cast(Node.Value)) { @@ -966,7 +966,7 @@ TArray UFlowAsset::GatherCustomInputNodeEventNames() const // from the actual flow nodes TArray Results; - for (const TPair& Node : Nodes) + for (const TPair& Node : ObjectPtrDecay(Nodes)) { if (UFlowNode_CustomInput* CustomInput = Cast(Node.Value)) { @@ -983,7 +983,7 @@ TArray UFlowAsset::GatherCustomOutputNodeEventNames() const // from the actual flow nodes TArray Results; - for (const TPair& Node : Nodes) + for (const TPair& Node : ObjectPtrDecay(Nodes)) { if (UFlowNode_CustomOutput* CustomOutput = Cast(Node.Value)) { @@ -1099,7 +1099,7 @@ void UFlowAsset::InitializeInstance(const TWeakObjectPtr InOwner, UFlow Owner = InOwner; TemplateAsset = InTemplateAsset; - for (TPair& Node : Nodes) + for (TPair>& Node : Nodes) { UFlowNode* NewNodeInstance = NewObject(this, Node.Value->GetClass(), NAME_None, RF_Transient, Node.Value, false, nullptr); Node.Value = NewNodeInstance; @@ -1118,7 +1118,7 @@ void UFlowAsset::InitializeInstance(const TWeakObjectPtr InOwner, UFlow void UFlowAsset::DeinitializeInstance() { - for (const TPair& Node : Nodes) + for (const TPair& Node : ObjectPtrDecay(Nodes)) { if (IsValid(Node.Value)) { diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index 2e2e4071b..5d1bfc399 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -126,7 +126,7 @@ void UFlowSubsystem::FinishRootFlow(UObject* Owner, UFlowAsset* TemplateAsset, c { UFlowAsset* InstanceToFinish = nullptr; - for (TPair>& RootInstance : RootInstances) + for (TPair, TWeakObjectPtr>& RootInstance : RootInstances) { if (Owner && Owner == RootInstance.Value.Get() && RootInstance.Key && RootInstance.Key->GetTemplateAsset() == TemplateAsset) { @@ -146,7 +146,7 @@ void UFlowSubsystem::FinishAllRootFlows(UObject* Owner, const EFlowFinishPolicy { TArray InstancesToFinish; - for (TPair>& RootInstance : RootInstances) + for (TPair, TWeakObjectPtr>& RootInstance : RootInstances) { if (Owner && Owner == RootInstance.Value.Get() && RootInstance.Key) { diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index e4392f700..accd9ab11 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -159,7 +159,7 @@ class FLOW_API UFlowAsset : public UObject private: UPROPERTY() - TMap Nodes; + TMap> Nodes; #if WITH_EDITORONLY_DATA protected: @@ -211,7 +211,7 @@ class FLOW_API UFlowAsset : public UObject #endif public: - const TMap& GetNodes() const { return Nodes; } + const TMap& GetNodes() const { return ObjectPtrDecay(Nodes); } UFlowNode* GetNode(const FGuid& Guid) const { return Nodes.FindRef(Guid); } template @@ -290,7 +290,7 @@ class FLOW_API UFlowAsset : public UObject private: // Original object holds references to instances UPROPERTY(Transient) - TArray ActiveInstances; + TArray> ActiveInstances; #if WITH_EDITORONLY_DATA TWeakObjectPtr InspectedInstance; @@ -333,7 +333,7 @@ class FLOW_API UFlowAsset : public UObject protected: UPROPERTY() - UFlowAsset* TemplateAsset; + TObjectPtr TemplateAsset; // Object that spawned Root Flow instance, i.e. World Settings or Player Controller // This pointer is passed to child instances: Flow Asset instances created by the SubGraph nodes @@ -347,18 +347,18 @@ class FLOW_API UFlowAsset : public UObject // Optional entry points to the graph, similar to blueprint Custom Events UPROPERTY() - TSet CustomInputNodes; + TSet> CustomInputNodes; UPROPERTY() - TSet PreloadedNodes; + TSet> PreloadedNodes; // Nodes that have any work left, not marked as Finished yet UPROPERTY() - TArray ActiveNodes; + TArray> ActiveNodes; // All nodes active in the past, done their work UPROPERTY() - TArray RecordedNodes; + TArray> RecordedNodes; EFlowFinishPolicy FinishPolicy; diff --git a/Source/Flow/Public/FlowComponent.h b/Source/Flow/Public/FlowComponent.h index 329f551b8..0a48e6572 100644 --- a/Source/Flow/Public/FlowComponent.h +++ b/Source/Flow/Public/FlowComponent.h @@ -177,7 +177,7 @@ class FLOW_API UFlowComponent : public UActorComponent, public IFlowOwnerInterfa public: // Asset that might instantiated as "Root Flow" UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "RootFlow") - UFlowAsset* RootFlow; + TObjectPtr RootFlow; // If true, component will start Root Flow on Begin Play UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "RootFlow") diff --git a/Source/Flow/Public/FlowSubsystem.h b/Source/Flow/Public/FlowSubsystem.h index 17499ae78..bb763eb59 100644 --- a/Source/Flow/Public/FlowSubsystem.h +++ b/Source/Flow/Public/FlowSubsystem.h @@ -39,15 +39,15 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem private: /* All asset templates with active instances */ UPROPERTY() - TArray InstancedTemplates; + TArray> InstancedTemplates; /* Assets instanced by object from another system, i.e. World Settings or Player Controller */ UPROPERTY() - TMap> RootInstances; + TMap, TWeakObjectPtr> RootInstances; /* Assets instanced by Sub Graph nodes */ UPROPERTY() - TMap InstancedSubFlows; + TMap, TObjectPtr> InstancedSubFlows; #if WITH_EDITOR public: @@ -60,7 +60,7 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem protected: UPROPERTY() - UFlowSaveGame* LoadedSaveGame; + TObjectPtr LoadedSaveGame; public: virtual bool ShouldCreateSubsystem(UObject* Outer) const override; @@ -112,7 +112,7 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem /* Returns assets instanced by Sub Graph nodes */ UFUNCTION(BlueprintPure, Category = "FlowSubsystem") - const TMap& GetInstancedSubFlows() const { return InstancedSubFlows; } + const TMap& GetInstancedSubFlows() const { return ObjectPtrDecay(InstancedSubFlows); } virtual UWorld* GetWorld() const override; diff --git a/Source/Flow/Public/FlowWorldSettings.h b/Source/Flow/Public/FlowWorldSettings.h index b7afd081d..5ebd5e6a6 100644 --- a/Source/Flow/Public/FlowWorldSettings.h +++ b/Source/Flow/Public/FlowWorldSettings.h @@ -17,7 +17,7 @@ class FLOW_API AFlowWorldSettings : public AWorldSettings private: UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category = "Flow", meta = (AllowPrivateAccess = "true")) - UFlowComponent* FlowComponent; + TObjectPtr FlowComponent; public: UFlowComponent* GetFlowComponent() const { return FlowComponent; } diff --git a/Source/Flow/Public/LevelSequence/FlowLevelSequencePlayer.h b/Source/Flow/Public/LevelSequence/FlowLevelSequencePlayer.h index 3e98e11c4..ba4e1f0a6 100644 --- a/Source/Flow/Public/LevelSequence/FlowLevelSequencePlayer.h +++ b/Source/Flow/Public/LevelSequence/FlowLevelSequencePlayer.h @@ -18,7 +18,7 @@ class FLOW_API UFlowLevelSequencePlayer : public ULevelSequencePlayer private: // most likely this is a UFlowNode_PlayLevelSequence or its child UPROPERTY() - UFlowNode* FlowEventReceiver; + TObjectPtr FlowEventReceiver; public: // variant of ULevelSequencePlayer::CreateLevelSequencePlayer diff --git a/Source/Flow/Public/MovieScene/MovieSceneFlowTrack.h b/Source/Flow/Public/MovieScene/MovieSceneFlowTrack.h index 3b7f921ef..0176468ae 100644 --- a/Source/Flow/Public/MovieScene/MovieSceneFlowTrack.h +++ b/Source/Flow/Public/MovieScene/MovieSceneFlowTrack.h @@ -62,5 +62,5 @@ class FLOW_API UMovieSceneFlowTrack private: /** The track's sections. */ UPROPERTY() - TArray Sections; + TArray> Sections; }; diff --git a/Source/Flow/Public/Nodes/FlowNodeBase.h b/Source/Flow/Public/Nodes/FlowNodeBase.h index 831631cd8..a9efca067 100644 --- a/Source/Flow/Public/Nodes/FlowNodeBase.h +++ b/Source/Flow/Public/Nodes/FlowNodeBase.h @@ -168,7 +168,7 @@ class FLOW_API UFlowNodeBase protected: // Flow Node AddOn attachments UPROPERTY(BlueprintReadOnly, Instanced, Category = "FlowNode") - TArray AddOns; + TArray> AddOns; protected: // FlowNodes and AddOns may determine which AddOns are eligible to be their children @@ -185,7 +185,7 @@ class FLOW_API UFlowNodeBase virtual const TArray& GetFlowNodeAddOnChildren() const { return AddOns; } #if WITH_EDITOR - virtual TArray& GetFlowNodeAddOnChildrenByEditor() { return AddOns; } + virtual TArray& GetFlowNodeAddOnChildrenByEditor() { return MutableView(AddOns); } EFlowAddOnAcceptResult CheckAcceptFlowNodeAddOnChild(const UFlowNodeAddOn* AddOnTemplate, const TArray& AdditionalAddOnsToAssumeAreChildren) const; #endif // WITH_EDITOR @@ -272,7 +272,7 @@ class FLOW_API UFlowNodeBase // (some editor symbols exposed to enabled creation of non-editor tooling) UPROPERTY() - UEdGraphNode* GraphNode; + TObjectPtr GraphNode; #if WITH_EDITORONLY_DATA protected: diff --git a/Source/Flow/Public/Nodes/FlowPin.h b/Source/Flow/Public/Nodes/FlowPin.h index 6c4437fdb..567844df3 100644 --- a/Source/Flow/Public/Nodes/FlowPin.h +++ b/Source/Flow/Public/Nodes/FlowPin.h @@ -55,7 +55,7 @@ struct FLOW_API FFlowPin // (C++ enums must bind by name using SubCategoryEnumName, due to a limitation with UE's UEnum discovery). // This property is editor-only, but it is automatically copied into PinSubCategoryObject if the PinType matches (for runtime use). UPROPERTY(EditAnywhere, Category = "FlowPin", meta = (EditCondition = "PinType == EFlowPinType::Enum", EditConditionHides)) - UEnum* SubCategoryEnumClass = nullptr; + TObjectPtr SubCategoryEnumClass = nullptr; // name of enum defined in c++ code, will take priority over asset from EnumType property // (this is a work-around because EnumClass cannot find C++ Enums, diff --git a/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h b/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h index 547508ecc..cf7c514bf 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h @@ -76,5 +76,5 @@ class FLOW_API UFlowNode_CallOwnerFunction : public UFlowNode // Parameter object to pass to the function when called UPROPERTY(EditAnywhere, Category = "Call Owner", Instanced) - UFlowOwnerFunctionParams* Params; + TObjectPtr Params; }; diff --git a/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h b/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h index 5a6626ac5..c878781ff 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h +++ b/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h @@ -64,10 +64,10 @@ class FLOW_API UFlowNode_PlayLevelSequence : public UFlowNode protected: UPROPERTY() - ULevelSequence* LoadedSequence; + TObjectPtr LoadedSequence; UPROPERTY() - UFlowLevelSequencePlayer* SequencePlayer; + TObjectPtr SequencePlayer; // Play Rate set by the user in PlaybackSettings float CachedPlayRate; diff --git a/Source/Flow/Public/Types/FlowDataPinProperties.h b/Source/Flow/Public/Types/FlowDataPinProperties.h index e425429de..cc4ad21b8 100644 --- a/Source/Flow/Public/Types/FlowDataPinProperties.h +++ b/Source/Flow/Public/Types/FlowDataPinProperties.h @@ -228,7 +228,7 @@ struct FFlowDataPinOutputProperty_Enum : public FFlowDataPinProperty // Class for this enum UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) - UEnum* EnumClass = nullptr; + TObjectPtr EnumClass = nullptr; #if WITH_EDITORONLY_DATA // name of enum defined in c++ code, will take priority over asset from EnumType property diff --git a/Source/Flow/Public/Types/FlowDataPinResults.h b/Source/Flow/Public/Types/FlowDataPinResults.h index 5c57cf334..26c114e1f 100644 --- a/Source/Flow/Public/Types/FlowDataPinResults.h +++ b/Source/Flow/Public/Types/FlowDataPinResults.h @@ -177,7 +177,7 @@ struct FFlowDataPinResult_Enum : public FFlowDataPinResult // Class for this enum UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) - UEnum* EnumClass = nullptr; + TObjectPtr EnumClass = nullptr; public: diff --git a/Source/Flow/Public/Types/FlowInjectComponentsManager.h b/Source/Flow/Public/Types/FlowInjectComponentsManager.h index da9d658e5..a70b788ac 100644 --- a/Source/Flow/Public/Types/FlowInjectComponentsManager.h +++ b/Source/Flow/Public/Types/FlowInjectComponentsManager.h @@ -63,5 +63,5 @@ class UFlowInjectComponentsManager : public UObject // Map of spawned components (if we are cleaning up) UPROPERTY(Transient) - TMap ActorToComponentsMap; + TMap, FFlowComponentInstances> ActorToComponentsMap; }; diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowActorOwnerComponentFilters.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowActorOwnerComponentFilters.cpp index 38b6ce96d..9ddb3c1e0 100644 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowActorOwnerComponentFilters.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowActorOwnerComponentFilters.cpp @@ -81,11 +81,11 @@ void FFlowActorOwnerComponentFilters::BuildClassFilters(const FProperty& Compone // Account for the allowed classes specified in the property metadata const FString& AllowedClassesFilterString = ComponentNameProperty.GetMetaData(NAME_AllowedClasses); - ParseClassFilters(AllowedClassesFilterString, AllowedComponentClassFilters); + ParseClassFilters(AllowedClassesFilterString, MutableView(AllowedComponentClassFilters)); // Account for disallowed classes specified in the property metadata const FString& DisallowedClassesFilterString = ComponentNameProperty.GetMetaData(NAME_DisallowedClasses); - ParseClassFilters(DisallowedClassesFilterString, DisallowedComponentClassFilters); + ParseClassFilters(DisallowedClassesFilterString, MutableView(DisallowedComponentClassFilters)); } void FFlowActorOwnerComponentFilters::BuildInterfaceFilters(const FProperty& ComponentNameProperty) @@ -109,7 +109,7 @@ void FFlowActorOwnerComponentFilters::BuildInterfaceFilters(const FProperty& Com // MustImplement interface(s) const FString& MustImplementInterfacesFilterString = ComponentNameProperty.GetMetaData(NAME_MustImplement); - ParseInterfaceFilters(MustImplementInterfacesFilterString, RequiredInterfaceFilters); + ParseInterfaceFilters(MustImplementInterfacesFilterString, MutableView(RequiredInterfaceFilters)); } bool FFlowActorOwnerComponentFilters::IsFilteredComponent(const UActorComponent& Component) const diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowActorOwnerComponentFilters.h b/Source/FlowEditor/Private/DetailCustomizations/FlowActorOwnerComponentFilters.h index a7d4f6a08..12e0891ed 100644 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowActorOwnerComponentFilters.h +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowActorOwnerComponentFilters.h @@ -35,15 +35,15 @@ struct FFlowActorOwnerComponentFilters #if WITH_EDITORONLY_DATA // Classes that can be used with this property UPROPERTY(Transient) - TArray AllowedComponentClassFilters; + TArray> AllowedComponentClassFilters; // Classes that can NOT be used with this property UPROPERTY(Transient) - TArray DisallowedComponentClassFilters; + TArray> DisallowedComponentClassFilters; // Must implement (all) interface(s) UPROPERTY(Transient) - TArray RequiredInterfaceFilters; + TArray> RequiredInterfaceFilters; // Has BuildClassFiltersFromMetadata been called? UPROPERTY(Transient) diff --git a/Source/FlowEditor/Public/Asset/FlowImportUtils.h b/Source/FlowEditor/Public/Asset/FlowImportUtils.h index 183361ff1..f7fd8d206 100644 --- a/Source/FlowEditor/Public/Asset/FlowImportUtils.h +++ b/Source/FlowEditor/Public/Asset/FlowImportUtils.h @@ -15,7 +15,7 @@ struct FLOWEDITOR_API FImportedGraphNode GENERATED_USTRUCT_BODY() UPROPERTY() - UEdGraphNode* SourceGraphNode; + TObjectPtr SourceGraphNode; TMultiMap Incoming; TMultiMap Outgoing; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h index 5d03fe0fa..399ae7cf4 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h @@ -15,7 +15,7 @@ struct FLOWEDITOR_API FFlowGraphSchemaAction_NewNode : public FEdGraphSchemaActi GENERATED_USTRUCT_BODY() UPROPERTY() - class UClass* NodeClass; + TObjectPtr NodeClass; static FName StaticGetTypeId() { diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h index c98c89415..6cfea5f17 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h @@ -34,7 +34,7 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode private: // The FlowNode or FlowNodeAddOn runtime instance that is being edited by this UFlowGraphNode UPROPERTY(Instanced) - UFlowNodeBase* NodeInstance; + TObjectPtr NodeInstance; bool bBlueprintCompilationPending; bool bIsReconstructingNode; From 2a19a1bcb93d8a705360171e2b03367444f8549c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Do=C4=9Fa=20Can=20Yan=C4=B1ko=C4=9Flu?= Date: Sun, 24 Nov 2024 15:11:14 +0300 Subject: [PATCH 288/485] More TObjectPtr fixes (#243) --- Source/Flow/Private/FlowSubsystem.cpp | 8 ++++---- Source/Flow/Private/Nodes/FlowNode.cpp | 2 +- Source/Flow/Private/Nodes/FlowPin.cpp | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index 5d1bfc399..4c242668f 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -98,7 +98,7 @@ void UFlowSubsystem::StartRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const UFlowAsset* UFlowSubsystem::CreateRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const bool bAllowMultipleInstances, const FString& NewInstanceName) { - for (const TPair>& RootInstance : RootInstances) + for (const TPair>& RootInstance : ObjectPtrDecay(RootInstances)) { if (Owner == RootInstance.Value.Get() && FlowAsset == RootInstance.Key->GetTemplateAsset()) { @@ -271,7 +271,7 @@ void UFlowSubsystem::RemoveInstancedTemplate(UFlowAsset* Template) TMap UFlowSubsystem::GetRootInstances() const { TMap Result; - for (const TPair>& RootInstance : RootInstances) + for (const TPair>& RootInstance : ObjectPtrDecay(RootInstances)) { Result.Emplace(RootInstance.Value.Get(), RootInstance.Key); } @@ -281,7 +281,7 @@ TMap UFlowSubsystem::GetRootInstances() const TSet UFlowSubsystem::GetRootInstancesByOwner(const UObject* Owner) const { TSet Result; - for (const TPair>& RootInstance : RootInstances) + for (const TPair>& RootInstance : ObjectPtrDecay(RootInstances)) { if (Owner && RootInstance.Value == Owner) { @@ -334,7 +334,7 @@ void UFlowSubsystem::OnGameSaved(UFlowSaveGame* SaveGame) } // save Flow Graphs - for (const TPair>& RootInstance : RootInstances) + for (const TPair>& RootInstance : ObjectPtrDecay(RootInstances)) { if (RootInstance.Key && RootInstance.Value.IsValid()) { diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index c58c0252d..9a286e1a4 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -641,7 +641,7 @@ bool UFlowNode::FindConnectedNodeForPinSlow(const FName& PinName, FGuid* OutGuid return false; } - for (const TPair& Pair : FlowAsset->Nodes) + for (const TPair& Pair : ObjectPtrDecay(FlowAsset->Nodes)) { const FGuid& ConnectedFromGuid = Pair.Key; const UFlowNode* ConnectedFromFlowNode = Pair.Value; diff --git a/Source/Flow/Private/Nodes/FlowPin.cpp b/Source/Flow/Private/Nodes/FlowPin.cpp index 8fe457823..ab3b8ba28 100644 --- a/Source/Flow/Private/Nodes/FlowPin.cpp +++ b/Source/Flow/Private/Nodes/FlowPin.cpp @@ -411,7 +411,7 @@ bool FFlowPin::ValidateEnum(const UEnum& EnumType) const int64 Value = EnumType.GetValueByIndex(i); if (Value < std::numeric_limits::min() || Value > std::numeric_limits::max()) { - UE_LOG(LogFlow, Error, TEXT("'%s' value %d is outside the range of supported key values for enum [%d, %d].") + UE_LOG(LogFlow, Error, TEXT("'%s' value %lld is outside the range of supported key values for enum [%d, %d].") , *EnumType.GenerateFullEnumName(*EnumType.GetDisplayNameTextByIndex(i).ToString()) , Value, std::numeric_limits::min(), std::numeric_limits::max()); From d1ce64f92b40eb9df85d0bf313119456d7066c6d Mon Sep 17 00:00:00 2001 From: Benxidosz <61600167+Benxidosz@users.noreply.github.com> Date: Thu, 28 Nov 2024 19:46:56 +0100 Subject: [PATCH 289/485] Replace AssetTypeActions for AssetDefinition (POTENTIALLY BREAKING CHANGE) (#245) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added UAssetDefinition_FlowNodeBlueprint & UAssetDefinition_FlowNodeAddOnBlueprint just like Krzysztof Justyński added UAssetDefinition_FlowAsset in commit: "5ef45f328809213024c99e9aba883ace7a104c58". This is a direct replacement for FAssetTypeActions_FlowNodeBlueprint/FAssetTypeActions_FlowNodeAddOnBlueprint. (cherry picked from commit 07f3616eca03c2e57332e58ff318cec62f586b7b) Co-authored-by: szabob --- Flow.uplugin | 4 +++ Source/FlowEditor/FlowEditor.Build.cs | 1 + .../FlowEditor/Private/FlowEditorModule.cpp | 10 -------- ...ssetDefinition_FlowNodeAddOnBlueprint.cpp} | 22 ++++++++++------ ... => AssetDefinition_FlowNodeBlueprint.cpp} | 22 ++++++++++------ .../AssetDefinition_FlowNodeAddOnBlueprint.h | 25 +++++++++++++++++++ .../Nodes/AssetDefinition_FlowNodeBlueprint.h | 25 +++++++++++++++++++ .../AssetTypeActions_FlowNodeAddOnBlueprint.h | 21 ---------------- .../AssetTypeActions_FlowNodeBlueprint.h | 21 ---------------- 9 files changed, 83 insertions(+), 68 deletions(-) rename Source/FlowEditor/Private/Nodes/{AssetTypeActions_FlowNodeAddOnBlueprint.cpp => AssetDefinition_FlowNodeAddOnBlueprint.cpp} (56%) rename Source/FlowEditor/Private/Nodes/{AssetTypeActions_FlowNodeBlueprint.cpp => AssetDefinition_FlowNodeBlueprint.cpp} (55%) create mode 100644 Source/FlowEditor/Public/Nodes/AssetDefinition_FlowNodeAddOnBlueprint.h create mode 100644 Source/FlowEditor/Public/Nodes/AssetDefinition_FlowNodeBlueprint.h delete mode 100644 Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.h delete mode 100644 Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeBlueprint.h diff --git a/Flow.uplugin b/Flow.uplugin index 44cd2a52e..25ee65348 100644 --- a/Flow.uplugin +++ b/Flow.uplugin @@ -30,6 +30,10 @@ "Name": "AssetSearch", "Enabled": true }, + { + "Name": "EngineAssetDefinitions", + "Enabled": true + }, { "Name": "EditorScriptingUtilities", "Enabled": true diff --git a/Source/FlowEditor/FlowEditor.Build.cs b/Source/FlowEditor/FlowEditor.Build.cs index 4d851cd0b..b7a6b3579 100644 --- a/Source/FlowEditor/FlowEditor.Build.cs +++ b/Source/FlowEditor/FlowEditor.Build.cs @@ -20,6 +20,7 @@ public FlowEditor(ReadOnlyTargetRules target) : base(target) "Flow", "MessageLog", "AIModule", // For BlueprintNodeHelpers::DescribeProperty (could be copy/pasted out to remove editor-only dependency) + "EngineAssetDefinitions", // For UAssetDefinition_Blueprint }); PrivateDependencyModuleNames.AddRange(new[] diff --git a/Source/FlowEditor/Private/FlowEditorModule.cpp b/Source/FlowEditor/Private/FlowEditorModule.cpp index 8f50a55e0..20fe598ce 100644 --- a/Source/FlowEditor/Private/FlowEditorModule.cpp +++ b/Source/FlowEditor/Private/FlowEditorModule.cpp @@ -10,8 +10,6 @@ #include "Graph/FlowGraphSettings.h" #include "Utils/SLevelEditorFlow.h" #include "MovieScene/FlowTrackEditor.h" -#include "Nodes/AssetTypeActions_FlowNodeBlueprint.h" -#include "Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.h" #include "Pins/SFlowInputPinHandle.h" #include "Pins/SFlowOutputPinHandle.h" @@ -164,14 +162,6 @@ void FFlowEditorModule::RegisterAssets() FlowAssetCategory = AssetTools.RegisterAdvancedAssetCategory(FName(TEXT("Flow")), AssetCategoryText); } } - - const TSharedRef FlowNodeActions = MakeShareable(new FAssetTypeActions_FlowNodeBlueprint()); - RegisteredAssetActions.Add(FlowNodeActions); - AssetTools.RegisterAssetTypeActions(FlowNodeActions); - - const TSharedRef FlowNodeAddOnActions = MakeShareable(new FAssetTypeActions_FlowNodeAddOnBlueprint()); - RegisteredAssetActions.Add(FlowNodeAddOnActions); - AssetTools.RegisterAssetTypeActions(FlowNodeAddOnActions); } void FFlowEditorModule::UnregisterAssets() diff --git a/Source/FlowEditor/Private/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.cpp b/Source/FlowEditor/Private/Nodes/AssetDefinition_FlowNodeAddOnBlueprint.cpp similarity index 56% rename from Source/FlowEditor/Private/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.cpp rename to Source/FlowEditor/Private/Nodes/AssetDefinition_FlowNodeAddOnBlueprint.cpp index b04b7ce11..016081eea 100644 --- a/Source/FlowEditor/Private/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.cpp +++ b/Source/FlowEditor/Private/Nodes/AssetDefinition_FlowNodeAddOnBlueprint.cpp @@ -1,6 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.h" +#include "Nodes/AssetDefinition_FlowNodeAddOnBlueprint.h" #include "Nodes/FlowNodeBlueprintFactory.h" #include "Nodes/FlowNodeAddOnBlueprint.h" #include "AddOns/FlowNodeAddOn.h" @@ -9,22 +9,28 @@ #define LOCTEXT_NAMESPACE "AssetTypeActions_FlowNodeAddOnBlueprint" -FText FAssetTypeActions_FlowNodeAddOnBlueprint::GetName() const +TSoftClassPtr UAssetDefinition_FlowNodeAddOnBlueprint::GetAssetClass() const { - return LOCTEXT("AssetTypeActions_FlowNodeBlueprint", "Flow Node AddOn Blueprint"); + return UFlowNodeAddOnBlueprint::StaticClass(); } -uint32 FAssetTypeActions_FlowNodeAddOnBlueprint::GetCategories() +FText UAssetDefinition_FlowNodeAddOnBlueprint::GetAssetDisplayName() const { - return UFlowGraphSettings::Get()->bExposeFlowNodeCreation ? FFlowEditorModule::FlowAssetCategory : 0; + return LOCTEXT("AssetTypeActions_FlowNodeBlueprint", "Flow Node AddOn Blueprint"); } -UClass* FAssetTypeActions_FlowNodeAddOnBlueprint::GetSupportedClass() const +TConstArrayView UAssetDefinition_FlowNodeAddOnBlueprint::GetAssetCategories() const { - return UFlowNodeAddOnBlueprint::StaticClass(); + if (UFlowGraphSettings::Get()->bExposeFlowAssetCreation) + { + static const auto Categories = {FFLowAssetCategoryPaths::Flow}; + return Categories; + } + + return {}; } -UFactory* FAssetTypeActions_FlowNodeAddOnBlueprint::GetFactoryForBlueprintType(UBlueprint* InBlueprint) const +UFactory* UAssetDefinition_FlowNodeAddOnBlueprint::GetFactoryForBlueprintType(UBlueprint* InBlueprint) const { UFlowNodeAddOnBlueprintFactory* FlowNodeAddOnBlueprintFactory = NewObject(); FlowNodeAddOnBlueprintFactory->ParentClass = TSubclassOf(*InBlueprint->GeneratedClass); diff --git a/Source/FlowEditor/Private/Nodes/AssetTypeActions_FlowNodeBlueprint.cpp b/Source/FlowEditor/Private/Nodes/AssetDefinition_FlowNodeBlueprint.cpp similarity index 55% rename from Source/FlowEditor/Private/Nodes/AssetTypeActions_FlowNodeBlueprint.cpp rename to Source/FlowEditor/Private/Nodes/AssetDefinition_FlowNodeBlueprint.cpp index a145a98fc..45a784ade 100644 --- a/Source/FlowEditor/Private/Nodes/AssetTypeActions_FlowNodeBlueprint.cpp +++ b/Source/FlowEditor/Private/Nodes/AssetDefinition_FlowNodeBlueprint.cpp @@ -1,6 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "Nodes/AssetTypeActions_FlowNodeBlueprint.h" +#include "Nodes/AssetDefinition_FlowNodeBlueprint.h" #include "Nodes/FlowNodeBlueprintFactory.h" #include "Nodes/FlowNodeBlueprint.h" #include "Nodes/FlowNode.h" @@ -10,22 +10,28 @@ #define LOCTEXT_NAMESPACE "AssetTypeActions_FlowNodeBlueprint" -FText FAssetTypeActions_FlowNodeBlueprint::GetName() const +TSoftClassPtr UAssetDefinition_FlowNodeBlueprint::GetAssetClass() const { - return LOCTEXT("AssetTypeActions_FlowNodeBlueprint", "Flow Node Blueprint"); + return UFlowNodeBlueprint::StaticClass(); } -uint32 FAssetTypeActions_FlowNodeBlueprint::GetCategories() +FText UAssetDefinition_FlowNodeBlueprint::GetAssetDisplayName() const { - return UFlowGraphSettings::Get()->bExposeFlowNodeCreation ? FFlowEditorModule::FlowAssetCategory : 0; + return LOCTEXT("AssetTypeActions_FlowNodeBlueprint", "Flow Node Blueprint"); } -UClass* FAssetTypeActions_FlowNodeBlueprint::GetSupportedClass() const +TConstArrayView UAssetDefinition_FlowNodeBlueprint::GetAssetCategories() const { - return UFlowNodeBlueprint::StaticClass(); + if (UFlowGraphSettings::Get()->bExposeFlowAssetCreation) + { + static const auto Categories = {FFLowAssetCategoryPaths::Flow}; + return Categories; + } + + return {}; } -UFactory* FAssetTypeActions_FlowNodeBlueprint::GetFactoryForBlueprintType(UBlueprint* InBlueprint) const +UFactory* UAssetDefinition_FlowNodeBlueprint::GetFactoryForBlueprintType(UBlueprint* InBlueprint) const { UFlowNodeBlueprintFactory* FlowNodeBlueprintFactory = NewObject(); FlowNodeBlueprintFactory->ParentClass = TSubclassOf(*InBlueprint->GeneratedClass); diff --git a/Source/FlowEditor/Public/Nodes/AssetDefinition_FlowNodeAddOnBlueprint.h b/Source/FlowEditor/Public/Nodes/AssetDefinition_FlowNodeAddOnBlueprint.h new file mode 100644 index 000000000..1eb6e01e8 --- /dev/null +++ b/Source/FlowEditor/Public/Nodes/AssetDefinition_FlowNodeAddOnBlueprint.h @@ -0,0 +1,25 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Script/AssetDefinition_Blueprint.h" +#include "AssetDefinition_FlowNodeAddOnBlueprint.generated.h" + +UCLASS() +class FLOWEDITOR_API UAssetDefinition_FlowNodeAddOnBlueprint : public UAssetDefinition_Blueprint +{ + GENERATED_BODY() +public: + // UAssetDefinition + virtual TSoftClassPtr GetAssetClass() const override; + virtual FLinearColor GetAssetColor() const override { return FColor(255, 196, 128); } + virtual FText GetAssetDisplayName() const override; + virtual TConstArrayView GetAssetCategories() const override; + // -- + +protected: + // FAssetTypeActions_Blueprint + //virtual bool CanCreateNewDerivedBlueprint() const override { return false; } + virtual UFactory* GetFactoryForBlueprintType(UBlueprint* InBlueprint) const override; + // -- +}; diff --git a/Source/FlowEditor/Public/Nodes/AssetDefinition_FlowNodeBlueprint.h b/Source/FlowEditor/Public/Nodes/AssetDefinition_FlowNodeBlueprint.h new file mode 100644 index 000000000..5ea087378 --- /dev/null +++ b/Source/FlowEditor/Public/Nodes/AssetDefinition_FlowNodeBlueprint.h @@ -0,0 +1,25 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Script/AssetDefinition_Blueprint.h" +#include "AssetDefinition_FlowNodeBlueprint.generated.h" + +UCLASS() +class FLOWEDITOR_API UAssetDefinition_FlowNodeBlueprint : public UAssetDefinition_Blueprint +{ + GENERATED_BODY() + +public: + // UAssetDefinition + virtual TSoftClassPtr GetAssetClass() const override; + virtual FLinearColor GetAssetColor() const override { return FColor(255, 196, 128); } + virtual FText GetAssetDisplayName() const override; + virtual TConstArrayView GetAssetCategories() const override; + // -- + +protected: + // UAssetDefinition_Blueprint + virtual UFactory* GetFactoryForBlueprintType(UBlueprint* InBlueprint) const override; + // -- +}; diff --git a/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.h b/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.h deleted file mode 100644 index 08c40a9ec..000000000 --- a/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#pragma once - -#include "AssetTypeActions/AssetTypeActions_Blueprint.h" - -class FLOWEDITOR_API FAssetTypeActions_FlowNodeAddOnBlueprint : public FAssetTypeActions_Blueprint -{ -public: - virtual FText GetName() const override; - virtual uint32 GetCategories() override; - virtual FColor GetTypeColor() const override { return FColor(255, 196, 128); } - - virtual UClass* GetSupportedClass() const override; - -protected: - // FAssetTypeActions_Blueprint - virtual bool CanCreateNewDerivedBlueprint() const override { return false; } - virtual UFactory* GetFactoryForBlueprintType(UBlueprint* InBlueprint) const override; - // -- -}; diff --git a/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeBlueprint.h b/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeBlueprint.h deleted file mode 100644 index e8243cf42..000000000 --- a/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeBlueprint.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#pragma once - -#include "AssetTypeActions/AssetTypeActions_Blueprint.h" - -class FLOWEDITOR_API FAssetTypeActions_FlowNodeBlueprint : public FAssetTypeActions_Blueprint -{ -public: - virtual FText GetName() const override; - virtual uint32 GetCategories() override; - virtual FColor GetTypeColor() const override { return FColor(255, 196, 128); } - - virtual UClass* GetSupportedClass() const override; - -protected: - // FAssetTypeActions_Blueprint - virtual bool CanCreateNewDerivedBlueprint() const override { return false; } - virtual UFactory* GetFactoryForBlueprintType(UBlueprint* InBlueprint) const override; - // -- -}; From acfe1f520ae132232b396fee315b647c44a2fe4e Mon Sep 17 00:00:00 2001 From: LindyHopperGT <91915878+LindyHopperGT@users.noreply.github.com> Date: Thu, 28 Nov 2024 10:47:04 -0800 Subject: [PATCH 290/485] Extending Allow/Deny to include AddOns (#244) * Flow AddOns Introduced FlowAddOns ~ Major rework of the Flow Editor to support AddOns ~ Also added ExecuteComponent flow node, which allows a component of the owning actor (if it implements a specific interface) to be executed via a proxy node in the Flow Graph. We use this to wrap some legacy systems in flow graph nodes. * Fix Linux compile errors and regressions on UFUNCTIONS * Fix bug with ExecuteInput for AddOns Nodes and AddOns could execute for pins they did not expect, if the AddOns introduced pins that the Node (or other AddOns) were not aware of. Also, removed some deprecation warnings because they were generating compile warnings, which could cause some projects problems with automated builds. I changed the deprecation message to a human-readable message only. * Slight adjustment to IsSupportedInputPinName() early return if the array is empty * AddOns Optimized and changed how Input/Outputs are added from AddOns * Quality of life improvements Revised the ForEachAddOn() signature, since it only ever needs to be used when iterating over all AddOns (recursively), as you can use a normal for loop over AddOns for just the AddOns of this object alone. Also moved some access functions from FlowNode to FlowNodeBase and exposed them to Blueprint, based on a question I saw on the Discord about Get Actor Owner access. * Extracted Component injection code into Flow base also updated ExecuteComponent to use it * Transferring flags from Template component to instance + Transient * Singular InjectComponent signature * Small revisions to Execute Component flownode * Fix uniqueness problem with component lookup * fixed GC-related compilation warning in AddReferencedObjects() * cosmetic pass on AddOns refactor - Adding Category to a few functions which fixes build machine compilation in the plugin mode. - Partially restoring class layout to its original shape, taking in account new sections, and improve it a little bit. - Reversing some changes on making symbols editor-only which might break non-editor tools working with Flow Graph. - Static analysis fixes. * Flow Diff improvements * Added AActor::GetActorClassDefaultComponents() alternative for UE < 5.3 * Folded IFlowNativeExecutableInterface into UFlowNodeBase * Fix compile error * Fix for Undo buffer for SetConfigText * Deleted file * deleted files * Flow Data Pins (initial version) Initial version for Flow Data Pins feature. * Removed Class/Object enum values (until we finish the feature) * Restored commented-out code (and unexpanded the macro) * Reverted last change. need to work on the macros more. * Fixed compilation problem with Enum macro * Fixed details customization for flow data pin input properties * Fixed problem detecting property to pin binding changes. It was only checking the sizes, not doing a deep comparison of the two maps. * Fix problem with data pins incorrectly breaking the wrong pin's connections * Fix erroneous return value where it could find suppliers, but reported that it couldn't * Reworked DataPins to use the authored property name for the lookup map Was using DisplayName, but UE doesn't provide a convenient lookup for the property DisplayName in non-editor builds, making for keeping the property name and the pin name in sync more cumbersome than I'd prefer. Using the Authored name instead allows us to use the GET_MEMBER_PROPERTY_NAME_CHECKED macro to keep the name in sync with the property w/static assert. * Added details customization for named data pin output property * Added InstancedStruct support for Flow Data Pins * Added FlowDataPinPropertyProviderInterface for the AI Flow plugin to use to integrate with data pins. * Status String support for AddOns and ForEachAddOn improvements AddOns can now include line(s) in the StatusString of their node ForEachAddOn functions can break early with 'success' or 'failure' * Added Flow Data Pins support for Rotator, Object, Class * Refined how Data Pins are auto-generated, graph and node updates in the editor * Disallow Reroute nodes for non-exec flow pin connections At least until we develop some data-pin-friendly solution, disabling the reroute node generation for flow data pin wires. * Wire colors match their pin color * Small refinements * Small bugfixes missing header includes calling Super:: Log an Timer support for assets that haven't been resaved with data pins to still source their data correctly. * Code review changes and crash fix crash fix for copy/pasting a flow node with addons. This crash would only happen in this PR (not the current flow mainline version) * Bugfix - Pasting & Drag/Dropping AddOns onto nodes now respects allowed child addon filtering * Updated signature of UFlowNodeAddOn::AcceptFlowNodeAddOnParent() to include additional proposed children This signature now affords the AddOn->Parent question the same information as the Parent->AddOn question gained in the previous change * Fix for BP compile errors for the default array reference for the new AdditionalAddOnsToAssumeAreChildren parameter * Fix crash when pasting nodes with addons into another graph The addons array is not fully-formed when called from some mid-paste functions. * Tag-based Node Display Style conversion hierarchical tag-indexed NodeDisplayStyle. - project extensible node styles - hierarchical node style definitions in INI * UE 5.5 compilation error fixes * Some fixes for Custom NodeDisplayStyle and 5.4 compilation defines * Latest updates post-merge with mainline * removed duplicate line * Restored blank line * Removed stray comment * Bugfix for modifying every flow asset that is opened in the editor. We were unilaterally modifying all flow nodes by reconstructing their nodes when starting editing a flow asset, which is not what we want or need to do. * Extending Allow/Deny to include AddOns - also adding capability to opt back-in to being allowed as a subclass of a denied node --------- Co-authored-by: FoolsTheoryDev --- Source/Flow/Private/FlowAsset.cpp | 48 +++++++++++++++++++++---------- Source/Flow/Public/FlowAsset.h | 11 ++++--- 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 400bde626..962babb22 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -34,7 +34,7 @@ UFlowAsset::UFlowAsset(const FObjectInitializer& ObjectInitializer) #if WITH_EDITOR , FlowGraph(nullptr) #endif - , AllowedNodeClasses({UFlowNode::StaticClass()}) + , AllowedNodeClasses({UFlowNodeBase::StaticClass()}) , AllowedInSubgraphNodeClasses({UFlowNode_SubGraph::StaticClass()}) , bStartNodePlacedAsGhostNode(false) , TemplateAsset(nullptr) @@ -208,37 +208,55 @@ bool UFlowAsset::CanFlowNodeClassBeUsedByFlowAsset(const UClass& FlowNodeClass) bool UFlowAsset::CanFlowAssetUseFlowNodeClass(const UClass& FlowNodeClass) const { - const bool bIsFlowNode = FlowNodeClass.IsChildOf(); - if (!bIsFlowNode) + // UFlowAsset class can limit which UFlowNodeBase classes can be used + if (IsFlowNodeClassInDeniedClasses(FlowNodeClass)) { - check(FlowNodeClass.IsChildOf()); - - // AddOns don't have the AllowedAssetClasses/DeniedAssetClasses - // (yet? maybe we move it up to the base?) + return false; + } - return true; + if (!IsFlowNodeClassInAllowedClasses(FlowNodeClass)) + { + return false; } - // UFlowAsset class can limit which UFlowNode classes can be used - for (const UClass* DeniedNodeClass : DeniedNodeClasses) + return true; +} + +bool UFlowAsset::IsFlowNodeClassInDeniedClasses(const UClass& FlowNodeClass) const +{ + for (const TSubclassOf DeniedNodeClass : DeniedNodeClasses) { if (DeniedNodeClass && FlowNodeClass.IsChildOf(DeniedNodeClass)) { - return false; + // Subclasses of a DeniedNodeClass can opt back in to being allowed + if (!IsFlowNodeClassInAllowedClasses(FlowNodeClass, DeniedNodeClass)) + { + return true; + } } } - if (bIsFlowNode && AllowedNodeClasses.Num() > 0) + return false; +} + +bool UFlowAsset::IsFlowNodeClassInAllowedClasses(const UClass& FlowNodeClass, const TSubclassOf RequiredAncestor) const +{ + if (AllowedNodeClasses.Num() > 0) { bool bAllowedInAsset = false; - for (const UClass* AllowedNodeClass : AllowedNodeClasses) + for (const TSubclassOf AllowedNodeClass : AllowedNodeClasses) { - if (AllowedNodeClass && FlowNodeClass.IsChildOf(AllowedNodeClass)) + // If a RequiredAncestor is provided, the AllowedNodeClass must be a subclass of the RequiredAncestor + if (AllowedNodeClass && + FlowNodeClass.IsChildOf(AllowedNodeClass) && + (!RequiredAncestor || AllowedNodeClass->IsChildOf(RequiredAncestor))) { bAllowedInAsset = true; + break; } } + if (!bAllowedInAsset) { return false; @@ -1008,7 +1026,7 @@ TArray UFlowAsset::GetNodesInExecutionOrder(UFlowNode* FirstIterated } } FoundNodes.Shrink(); - + return FoundNodes; } diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index accd9ab11..628300438 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -124,6 +124,9 @@ class FLOW_API UFlowAsset : public UObject bool CanFlowNodeClassBeUsedByFlowAsset(const UClass& FlowNodeClass) const; bool CanFlowAssetUseFlowNodeClass(const UClass& FlowNodeClass) const; bool CanFlowAssetReferenceFlowNode(const UClass& FlowNodeClass, FText* OutOptionalFailureReason = nullptr) const; + + bool IsFlowNodeClassInAllowedClasses(const UClass& FlowNodeClass, const TSubclassOf RequiredAncestor = nullptr) const; + bool IsFlowNodeClassInDeniedClasses(const UClass& FlowNodeClass) const; #endif // IFlowGraphInterface @@ -149,11 +152,11 @@ class FLOW_API UFlowAsset : public UObject // Nodes protected: - TArray> AllowedNodeClasses; - TArray> DeniedNodeClasses; + TArray> AllowedNodeClasses; + TArray> DeniedNodeClasses; - TArray> AllowedInSubgraphNodeClasses; - TArray> DeniedInSubgraphNodeClasses; + TArray> AllowedInSubgraphNodeClasses; + TArray> DeniedInSubgraphNodeClasses; bool bStartNodePlacedAsGhostNode; From 3762a5c275b840d1519c7769eac2b08ebb4ac8e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Thu, 28 Nov 2024 19:50:09 +0100 Subject: [PATCH 291/485] Update FlowEditor.Build.cs --- Source/FlowEditor/FlowEditor.Build.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/FlowEditor/FlowEditor.Build.cs b/Source/FlowEditor/FlowEditor.Build.cs index b7a6b3579..54954ebbd 100644 --- a/Source/FlowEditor/FlowEditor.Build.cs +++ b/Source/FlowEditor/FlowEditor.Build.cs @@ -18,13 +18,12 @@ public FlowEditor(ReadOnlyTargetRules target) : base(target) "AssetSearch", "EditorSubsystem", "Flow", - "MessageLog", - "AIModule", // For BlueprintNodeHelpers::DescribeProperty (could be copy/pasted out to remove editor-only dependency) - "EngineAssetDefinitions", // For UAssetDefinition_Blueprint + "MessageLog" }); PrivateDependencyModuleNames.AddRange(new[] { + "AIModule", // For BlueprintNodeHelpers::DescribeProperty (could be copy/pasted out to remove editor-only dependency) "ApplicationCore", "AssetDefinition", "AssetTools", @@ -39,6 +38,7 @@ public FlowEditor(ReadOnlyTargetRules target) : base(target) "EditorScriptingUtilities", "EditorStyle", "Engine", + "EngineAssetDefinitions", "GraphEditor", "GameplayTags", "InputCore", From 10c469a6196e896386ff642532d75c84556071c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Thu, 28 Nov 2024 20:15:35 +0100 Subject: [PATCH 292/485] remove StructUtils reference from UE 5.5 --- Source/Flow/Flow.Build.cs | 7 +++++-- Source/FlowEditor/FlowEditor.Build.cs | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Source/Flow/Flow.Build.cs b/Source/Flow/Flow.Build.cs index 7feac77b2..f8de81ee0 100644 --- a/Source/Flow/Flow.Build.cs +++ b/Source/Flow/Flow.Build.cs @@ -10,9 +10,12 @@ public Flow(ReadOnlyTargetRules target) : base(target) PublicDependencyModuleNames.AddRange(new[] { - "LevelSequence", - "StructUtils", + "LevelSequence" }); + +#if UE_5_4_OR_EARLIER + PublicDependencyModuleNames.AddRange("StructUtils"); +#endif PrivateDependencyModuleNames.AddRange(new[] { diff --git a/Source/FlowEditor/FlowEditor.Build.cs b/Source/FlowEditor/FlowEditor.Build.cs index 54954ebbd..46389bf57 100644 --- a/Source/FlowEditor/FlowEditor.Build.cs +++ b/Source/FlowEditor/FlowEditor.Build.cs @@ -59,9 +59,12 @@ public FlowEditor(ReadOnlyTargetRules target) : base(target) "Slate", "SlateCore", "SourceControl", - "StructUtils", "ToolMenus", "UnrealEd" }); + +#if UE_5_4_OR_EARLIER + PrivateDependencyModuleNames.AddRange("StructUtils"); +#endif } } \ No newline at end of file From 8bd69a2cfec9c757022fbfca5491c575c98fa95f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Thu, 28 Nov 2024 20:22:30 +0100 Subject: [PATCH 293/485] Revert "remove StructUtils reference from UE 5.5" This reverts commit 10c469a6196e896386ff642532d75c84556071c5. --- Source/Flow/Flow.Build.cs | 7 ++----- Source/FlowEditor/FlowEditor.Build.cs | 5 +---- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/Source/Flow/Flow.Build.cs b/Source/Flow/Flow.Build.cs index f8de81ee0..7feac77b2 100644 --- a/Source/Flow/Flow.Build.cs +++ b/Source/Flow/Flow.Build.cs @@ -10,12 +10,9 @@ public Flow(ReadOnlyTargetRules target) : base(target) PublicDependencyModuleNames.AddRange(new[] { - "LevelSequence" + "LevelSequence", + "StructUtils", }); - -#if UE_5_4_OR_EARLIER - PublicDependencyModuleNames.AddRange("StructUtils"); -#endif PrivateDependencyModuleNames.AddRange(new[] { diff --git a/Source/FlowEditor/FlowEditor.Build.cs b/Source/FlowEditor/FlowEditor.Build.cs index 46389bf57..54954ebbd 100644 --- a/Source/FlowEditor/FlowEditor.Build.cs +++ b/Source/FlowEditor/FlowEditor.Build.cs @@ -59,12 +59,9 @@ public FlowEditor(ReadOnlyTargetRules target) : base(target) "Slate", "SlateCore", "SourceControl", + "StructUtils", "ToolMenus", "UnrealEd" }); - -#if UE_5_4_OR_EARLIER - PrivateDependencyModuleNames.AddRange("StructUtils"); -#endif } } \ No newline at end of file From a15b845df122c94ce87c64c444e911ee679c4501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Wed, 4 Dec 2024 20:03:39 +0100 Subject: [PATCH 294/485] rolled back AssetDefinition_FlowNodeBlueprint this change requires UE 5.5 as previous engine versions are missing export macros in FAssetTypeActions_Blueprint --- Flow.uplugin | 4 --- .../FlowEditor/Private/FlowEditorModule.cpp | 10 ++++++++ ...setTypeActions_FlowNodeAddOnBlueprint.cpp} | 22 ++++++---------- ...=> AssetTypeActions_FlowNodeBlueprint.cpp} | 22 ++++++---------- .../AssetDefinition_FlowNodeAddOnBlueprint.h | 25 ------------------- .../Nodes/AssetDefinition_FlowNodeBlueprint.h | 25 ------------------- .../AssetTypeActions_FlowNodeAddOnBlueprint.h | 21 ++++++++++++++++ .../AssetTypeActions_FlowNodeBlueprint.h | 21 ++++++++++++++++ 8 files changed, 68 insertions(+), 82 deletions(-) rename Source/FlowEditor/Private/Nodes/{AssetDefinition_FlowNodeAddOnBlueprint.cpp => AssetTypeActions_FlowNodeAddOnBlueprint.cpp} (56%) rename Source/FlowEditor/Private/Nodes/{AssetDefinition_FlowNodeBlueprint.cpp => AssetTypeActions_FlowNodeBlueprint.cpp} (55%) delete mode 100644 Source/FlowEditor/Public/Nodes/AssetDefinition_FlowNodeAddOnBlueprint.h delete mode 100644 Source/FlowEditor/Public/Nodes/AssetDefinition_FlowNodeBlueprint.h create mode 100644 Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.h create mode 100644 Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeBlueprint.h diff --git a/Flow.uplugin b/Flow.uplugin index 25ee65348..44cd2a52e 100644 --- a/Flow.uplugin +++ b/Flow.uplugin @@ -30,10 +30,6 @@ "Name": "AssetSearch", "Enabled": true }, - { - "Name": "EngineAssetDefinitions", - "Enabled": true - }, { "Name": "EditorScriptingUtilities", "Enabled": true diff --git a/Source/FlowEditor/Private/FlowEditorModule.cpp b/Source/FlowEditor/Private/FlowEditorModule.cpp index 20fe598ce..8f50a55e0 100644 --- a/Source/FlowEditor/Private/FlowEditorModule.cpp +++ b/Source/FlowEditor/Private/FlowEditorModule.cpp @@ -10,6 +10,8 @@ #include "Graph/FlowGraphSettings.h" #include "Utils/SLevelEditorFlow.h" #include "MovieScene/FlowTrackEditor.h" +#include "Nodes/AssetTypeActions_FlowNodeBlueprint.h" +#include "Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.h" #include "Pins/SFlowInputPinHandle.h" #include "Pins/SFlowOutputPinHandle.h" @@ -162,6 +164,14 @@ void FFlowEditorModule::RegisterAssets() FlowAssetCategory = AssetTools.RegisterAdvancedAssetCategory(FName(TEXT("Flow")), AssetCategoryText); } } + + const TSharedRef FlowNodeActions = MakeShareable(new FAssetTypeActions_FlowNodeBlueprint()); + RegisteredAssetActions.Add(FlowNodeActions); + AssetTools.RegisterAssetTypeActions(FlowNodeActions); + + const TSharedRef FlowNodeAddOnActions = MakeShareable(new FAssetTypeActions_FlowNodeAddOnBlueprint()); + RegisteredAssetActions.Add(FlowNodeAddOnActions); + AssetTools.RegisterAssetTypeActions(FlowNodeAddOnActions); } void FFlowEditorModule::UnregisterAssets() diff --git a/Source/FlowEditor/Private/Nodes/AssetDefinition_FlowNodeAddOnBlueprint.cpp b/Source/FlowEditor/Private/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.cpp similarity index 56% rename from Source/FlowEditor/Private/Nodes/AssetDefinition_FlowNodeAddOnBlueprint.cpp rename to Source/FlowEditor/Private/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.cpp index 016081eea..b04b7ce11 100644 --- a/Source/FlowEditor/Private/Nodes/AssetDefinition_FlowNodeAddOnBlueprint.cpp +++ b/Source/FlowEditor/Private/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.cpp @@ -1,6 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "Nodes/AssetDefinition_FlowNodeAddOnBlueprint.h" +#include "Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.h" #include "Nodes/FlowNodeBlueprintFactory.h" #include "Nodes/FlowNodeAddOnBlueprint.h" #include "AddOns/FlowNodeAddOn.h" @@ -9,28 +9,22 @@ #define LOCTEXT_NAMESPACE "AssetTypeActions_FlowNodeAddOnBlueprint" -TSoftClassPtr UAssetDefinition_FlowNodeAddOnBlueprint::GetAssetClass() const +FText FAssetTypeActions_FlowNodeAddOnBlueprint::GetName() const { - return UFlowNodeAddOnBlueprint::StaticClass(); + return LOCTEXT("AssetTypeActions_FlowNodeBlueprint", "Flow Node AddOn Blueprint"); } -FText UAssetDefinition_FlowNodeAddOnBlueprint::GetAssetDisplayName() const +uint32 FAssetTypeActions_FlowNodeAddOnBlueprint::GetCategories() { - return LOCTEXT("AssetTypeActions_FlowNodeBlueprint", "Flow Node AddOn Blueprint"); + return UFlowGraphSettings::Get()->bExposeFlowNodeCreation ? FFlowEditorModule::FlowAssetCategory : 0; } -TConstArrayView UAssetDefinition_FlowNodeAddOnBlueprint::GetAssetCategories() const +UClass* FAssetTypeActions_FlowNodeAddOnBlueprint::GetSupportedClass() const { - if (UFlowGraphSettings::Get()->bExposeFlowAssetCreation) - { - static const auto Categories = {FFLowAssetCategoryPaths::Flow}; - return Categories; - } - - return {}; + return UFlowNodeAddOnBlueprint::StaticClass(); } -UFactory* UAssetDefinition_FlowNodeAddOnBlueprint::GetFactoryForBlueprintType(UBlueprint* InBlueprint) const +UFactory* FAssetTypeActions_FlowNodeAddOnBlueprint::GetFactoryForBlueprintType(UBlueprint* InBlueprint) const { UFlowNodeAddOnBlueprintFactory* FlowNodeAddOnBlueprintFactory = NewObject(); FlowNodeAddOnBlueprintFactory->ParentClass = TSubclassOf(*InBlueprint->GeneratedClass); diff --git a/Source/FlowEditor/Private/Nodes/AssetDefinition_FlowNodeBlueprint.cpp b/Source/FlowEditor/Private/Nodes/AssetTypeActions_FlowNodeBlueprint.cpp similarity index 55% rename from Source/FlowEditor/Private/Nodes/AssetDefinition_FlowNodeBlueprint.cpp rename to Source/FlowEditor/Private/Nodes/AssetTypeActions_FlowNodeBlueprint.cpp index 45a784ade..a145a98fc 100644 --- a/Source/FlowEditor/Private/Nodes/AssetDefinition_FlowNodeBlueprint.cpp +++ b/Source/FlowEditor/Private/Nodes/AssetTypeActions_FlowNodeBlueprint.cpp @@ -1,6 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "Nodes/AssetDefinition_FlowNodeBlueprint.h" +#include "Nodes/AssetTypeActions_FlowNodeBlueprint.h" #include "Nodes/FlowNodeBlueprintFactory.h" #include "Nodes/FlowNodeBlueprint.h" #include "Nodes/FlowNode.h" @@ -10,28 +10,22 @@ #define LOCTEXT_NAMESPACE "AssetTypeActions_FlowNodeBlueprint" -TSoftClassPtr UAssetDefinition_FlowNodeBlueprint::GetAssetClass() const +FText FAssetTypeActions_FlowNodeBlueprint::GetName() const { - return UFlowNodeBlueprint::StaticClass(); + return LOCTEXT("AssetTypeActions_FlowNodeBlueprint", "Flow Node Blueprint"); } -FText UAssetDefinition_FlowNodeBlueprint::GetAssetDisplayName() const +uint32 FAssetTypeActions_FlowNodeBlueprint::GetCategories() { - return LOCTEXT("AssetTypeActions_FlowNodeBlueprint", "Flow Node Blueprint"); + return UFlowGraphSettings::Get()->bExposeFlowNodeCreation ? FFlowEditorModule::FlowAssetCategory : 0; } -TConstArrayView UAssetDefinition_FlowNodeBlueprint::GetAssetCategories() const +UClass* FAssetTypeActions_FlowNodeBlueprint::GetSupportedClass() const { - if (UFlowGraphSettings::Get()->bExposeFlowAssetCreation) - { - static const auto Categories = {FFLowAssetCategoryPaths::Flow}; - return Categories; - } - - return {}; + return UFlowNodeBlueprint::StaticClass(); } -UFactory* UAssetDefinition_FlowNodeBlueprint::GetFactoryForBlueprintType(UBlueprint* InBlueprint) const +UFactory* FAssetTypeActions_FlowNodeBlueprint::GetFactoryForBlueprintType(UBlueprint* InBlueprint) const { UFlowNodeBlueprintFactory* FlowNodeBlueprintFactory = NewObject(); FlowNodeBlueprintFactory->ParentClass = TSubclassOf(*InBlueprint->GeneratedClass); diff --git a/Source/FlowEditor/Public/Nodes/AssetDefinition_FlowNodeAddOnBlueprint.h b/Source/FlowEditor/Public/Nodes/AssetDefinition_FlowNodeAddOnBlueprint.h deleted file mode 100644 index 1eb6e01e8..000000000 --- a/Source/FlowEditor/Public/Nodes/AssetDefinition_FlowNodeAddOnBlueprint.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#pragma once - -#include "Script/AssetDefinition_Blueprint.h" -#include "AssetDefinition_FlowNodeAddOnBlueprint.generated.h" - -UCLASS() -class FLOWEDITOR_API UAssetDefinition_FlowNodeAddOnBlueprint : public UAssetDefinition_Blueprint -{ - GENERATED_BODY() -public: - // UAssetDefinition - virtual TSoftClassPtr GetAssetClass() const override; - virtual FLinearColor GetAssetColor() const override { return FColor(255, 196, 128); } - virtual FText GetAssetDisplayName() const override; - virtual TConstArrayView GetAssetCategories() const override; - // -- - -protected: - // FAssetTypeActions_Blueprint - //virtual bool CanCreateNewDerivedBlueprint() const override { return false; } - virtual UFactory* GetFactoryForBlueprintType(UBlueprint* InBlueprint) const override; - // -- -}; diff --git a/Source/FlowEditor/Public/Nodes/AssetDefinition_FlowNodeBlueprint.h b/Source/FlowEditor/Public/Nodes/AssetDefinition_FlowNodeBlueprint.h deleted file mode 100644 index 5ea087378..000000000 --- a/Source/FlowEditor/Public/Nodes/AssetDefinition_FlowNodeBlueprint.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#pragma once - -#include "Script/AssetDefinition_Blueprint.h" -#include "AssetDefinition_FlowNodeBlueprint.generated.h" - -UCLASS() -class FLOWEDITOR_API UAssetDefinition_FlowNodeBlueprint : public UAssetDefinition_Blueprint -{ - GENERATED_BODY() - -public: - // UAssetDefinition - virtual TSoftClassPtr GetAssetClass() const override; - virtual FLinearColor GetAssetColor() const override { return FColor(255, 196, 128); } - virtual FText GetAssetDisplayName() const override; - virtual TConstArrayView GetAssetCategories() const override; - // -- - -protected: - // UAssetDefinition_Blueprint - virtual UFactory* GetFactoryForBlueprintType(UBlueprint* InBlueprint) const override; - // -- -}; diff --git a/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.h b/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.h new file mode 100644 index 000000000..08c40a9ec --- /dev/null +++ b/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.h @@ -0,0 +1,21 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "AssetTypeActions/AssetTypeActions_Blueprint.h" + +class FLOWEDITOR_API FAssetTypeActions_FlowNodeAddOnBlueprint : public FAssetTypeActions_Blueprint +{ +public: + virtual FText GetName() const override; + virtual uint32 GetCategories() override; + virtual FColor GetTypeColor() const override { return FColor(255, 196, 128); } + + virtual UClass* GetSupportedClass() const override; + +protected: + // FAssetTypeActions_Blueprint + virtual bool CanCreateNewDerivedBlueprint() const override { return false; } + virtual UFactory* GetFactoryForBlueprintType(UBlueprint* InBlueprint) const override; + // -- +}; diff --git a/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeBlueprint.h b/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeBlueprint.h new file mode 100644 index 000000000..e8243cf42 --- /dev/null +++ b/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeBlueprint.h @@ -0,0 +1,21 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "AssetTypeActions/AssetTypeActions_Blueprint.h" + +class FLOWEDITOR_API FAssetTypeActions_FlowNodeBlueprint : public FAssetTypeActions_Blueprint +{ +public: + virtual FText GetName() const override; + virtual uint32 GetCategories() override; + virtual FColor GetTypeColor() const override { return FColor(255, 196, 128); } + + virtual UClass* GetSupportedClass() const override; + +protected: + // FAssetTypeActions_Blueprint + virtual bool CanCreateNewDerivedBlueprint() const override { return false; } + virtual UFactory* GetFactoryForBlueprintType(UBlueprint* InBlueprint) const override; + // -- +}; From c1cbf3832f03f46f60b2ce0a8087be901701ac6b Mon Sep 17 00:00:00 2001 From: LindyHopperGT <91915878+LindyHopperGT@users.noreply.github.com> Date: Sat, 14 Dec 2024 07:48:37 -0800 Subject: [PATCH 295/485] Fix a typo in the auto data pin generation for Rotators (#247) * Flow AddOns Introduced FlowAddOns ~ Major rework of the Flow Editor to support AddOns ~ Also added ExecuteComponent flow node, which allows a component of the owning actor (if it implements a specific interface) to be executed via a proxy node in the Flow Graph. We use this to wrap some legacy systems in flow graph nodes. * Fix Linux compile errors and regressions on UFUNCTIONS * Fix bug with ExecuteInput for AddOns Nodes and AddOns could execute for pins they did not expect, if the AddOns introduced pins that the Node (or other AddOns) were not aware of. Also, removed some deprecation warnings because they were generating compile warnings, which could cause some projects problems with automated builds. I changed the deprecation message to a human-readable message only. * Slight adjustment to IsSupportedInputPinName() early return if the array is empty * AddOns Optimized and changed how Input/Outputs are added from AddOns * Quality of life improvements Revised the ForEachAddOn() signature, since it only ever needs to be used when iterating over all AddOns (recursively), as you can use a normal for loop over AddOns for just the AddOns of this object alone. Also moved some access functions from FlowNode to FlowNodeBase and exposed them to Blueprint, based on a question I saw on the Discord about Get Actor Owner access. * Extracted Component injection code into Flow base also updated ExecuteComponent to use it * Transferring flags from Template component to instance + Transient * Singular InjectComponent signature * Small revisions to Execute Component flownode * Fix uniqueness problem with component lookup * fixed GC-related compilation warning in AddReferencedObjects() * cosmetic pass on AddOns refactor - Adding Category to a few functions which fixes build machine compilation in the plugin mode. - Partially restoring class layout to its original shape, taking in account new sections, and improve it a little bit. - Reversing some changes on making symbols editor-only which might break non-editor tools working with Flow Graph. - Static analysis fixes. * Flow Diff improvements * Added AActor::GetActorClassDefaultComponents() alternative for UE < 5.3 * Folded IFlowNativeExecutableInterface into UFlowNodeBase * Fix compile error * Fix for Undo buffer for SetConfigText * Deleted file * deleted files * Flow Data Pins (initial version) Initial version for Flow Data Pins feature. * Removed Class/Object enum values (until we finish the feature) * Restored commented-out code (and unexpanded the macro) * Reverted last change. need to work on the macros more. * Fixed compilation problem with Enum macro * Fixed details customization for flow data pin input properties * Fixed problem detecting property to pin binding changes. It was only checking the sizes, not doing a deep comparison of the two maps. * Fix problem with data pins incorrectly breaking the wrong pin's connections * Fix erroneous return value where it could find suppliers, but reported that it couldn't * Reworked DataPins to use the authored property name for the lookup map Was using DisplayName, but UE doesn't provide a convenient lookup for the property DisplayName in non-editor builds, making for keeping the property name and the pin name in sync more cumbersome than I'd prefer. Using the Authored name instead allows us to use the GET_MEMBER_PROPERTY_NAME_CHECKED macro to keep the name in sync with the property w/static assert. * Added details customization for named data pin output property * Added InstancedStruct support for Flow Data Pins * Added FlowDataPinPropertyProviderInterface for the AI Flow plugin to use to integrate with data pins. * Status String support for AddOns and ForEachAddOn improvements AddOns can now include line(s) in the StatusString of their node ForEachAddOn functions can break early with 'success' or 'failure' * Added Flow Data Pins support for Rotator, Object, Class * Refined how Data Pins are auto-generated, graph and node updates in the editor * Disallow Reroute nodes for non-exec flow pin connections At least until we develop some data-pin-friendly solution, disabling the reroute node generation for flow data pin wires. * Wire colors match their pin color * Small refinements * Small bugfixes missing header includes calling Super:: Log an Timer support for assets that haven't been resaved with data pins to still source their data correctly. * Code review changes and crash fix crash fix for copy/pasting a flow node with addons. This crash would only happen in this PR (not the current flow mainline version) * Bugfix - Pasting & Drag/Dropping AddOns onto nodes now respects allowed child addon filtering * Updated signature of UFlowNodeAddOn::AcceptFlowNodeAddOnParent() to include additional proposed children This signature now affords the AddOn->Parent question the same information as the Parent->AddOn question gained in the previous change * Fix for BP compile errors for the default array reference for the new AdditionalAddOnsToAssumeAreChildren parameter * Fix crash when pasting nodes with addons into another graph The addons array is not fully-formed when called from some mid-paste functions. * Tag-based Node Display Style conversion hierarchical tag-indexed NodeDisplayStyle. - project extensible node styles - hierarchical node style definitions in INI * UE 5.5 compilation error fixes * Some fixes for Custom NodeDisplayStyle and 5.4 compilation defines * Latest updates post-merge with mainline * removed duplicate line * Restored blank line * Removed stray comment * Bugfix for modifying every flow asset that is opened in the editor. We were unilaterally modifying all flow nodes by reconstructing their nodes when starting editing a flow asset, which is not what we want or need to do. * Extending Allow/Deny to include AddOns - also adding capability to opt back-in to being allowed as a subclass of a denied node * Fix typo for Rotator auto data pin generation Input was used, when it should be the output variant --------- Co-authored-by: FoolsTheoryDev --- Source/Flow/Private/FlowAsset.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 962babb22..e5a0d96c2 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -868,7 +868,7 @@ bool UFlowAsset::TryCreateFlowDataPinFromMetadataValue( AddPinForPinType< FFlowDataPinOutputProperty_Enum, FFlowDataPinOutputProperty_Vector, - FFlowDataPinInputProperty_Rotator, + FFlowDataPinOutputProperty_Rotator, FFlowDataPinOutputProperty_Transform, FFlowDataPinOutputProperty_GameplayTag, FFlowDataPinOutputProperty_GameplayTagContainer, From 2de712fc3222ef426b953360ba2c0e28c45bfb65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sun, 15 Dec 2024 15:55:02 +0100 Subject: [PATCH 296/485] code readability tweaks --- Source/FlowEditor/Private/Graph/FlowGraph.cpp | 35 +++++----- .../Private/Graph/FlowGraphSchema.cpp | 64 ++++++++----------- .../FlowEditor/Public/Graph/FlowGraphSchema.h | 2 +- 3 files changed, 44 insertions(+), 57 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraph.cpp b/Source/FlowEditor/Private/Graph/FlowGraph.cpp index f2a6dd9e1..481d78bdb 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraph.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraph.cpp @@ -70,28 +70,24 @@ void UFlowGraph::RefreshGraph() const UFlowGraphSchema* FlowGraphSchema = CastChecked(GetSchema()); FlowGraphSchema->GatherNodes(); - const TMap& FlowAssetNodesMap = GetFlowAsset()->GetNodes(); - for (const TPair& Node : FlowAssetNodesMap) + for (const TPair& Node : GetFlowAsset()->GetNodes()) { UFlowNode* FlowNode = Node.Value; - if (!IsValid(FlowNode)) + if (IsValid(FlowNode)) { - continue; - } - - UFlowGraphNode* const ExistingFlowGraphNode = Cast(FlowNode->GetGraphNode()); + UFlowGraphNode* const ExistingFlowGraphNode = Cast(FlowNode->GetGraphNode()); + UFlowGraphNode* RefreshedFlowGraphNode = ExistingFlowGraphNode; - UFlowGraphNode* RefreshedFlowGraphNode = ExistingFlowGraphNode; + const TSubclassOf ExpectGraphNodeClass = UFlowGraphSchema::GetAssignedGraphNodeClass(FlowNode->GetClass()); + const UClass* ExistingFlowGraphNodeClass = IsValid(ExistingFlowGraphNode) ? ExistingFlowGraphNode->GetClass() : nullptr; + if (ExistingFlowGraphNodeClass != ExpectGraphNodeClass) + { + // Create a new Flow Graph Node of proper type + RefreshedFlowGraphNode = FFlowGraphSchemaAction_NewNode::RecreateNode(this, ExistingFlowGraphNode, FlowNode); + } - const TSubclassOf ExpectGraphNodeClass = UFlowGraphSchema::GetAssignedGraphNodeClass(FlowNode->GetClass()); - UClass* ExistingFlowGraphNodeClass = IsValid(ExistingFlowGraphNode) ? ExistingFlowGraphNode->GetClass() : nullptr; - if (ExistingFlowGraphNodeClass != ExpectGraphNodeClass) - { - // Create a new Flow Graph Node of proper type - RefreshedFlowGraphNode = FFlowGraphSchemaAction_NewNode::RecreateNode(this, ExistingFlowGraphNode, FlowNode); + RecursivelyRefreshAddOns(*RefreshedFlowGraphNode); } - - RecursivelyRefreshAddOns(*RefreshedFlowGraphNode); } UnlockUpdates(); @@ -108,9 +104,10 @@ void UFlowGraph::RefreshGraph() void UFlowGraph::NotifyGraphChanged() { - UFlowAsset* FlowAsset = GetFlowAsset(); - - FlowAsset->HarvestNodeConnections(); + if (UFlowAsset* FlowAsset = GetFlowAsset()) + { + FlowAsset->HarvestNodeConnections(); + } Super::NotifyGraphChanged(); } diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index a4e8e96a1..b519a73d6 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -29,7 +29,6 @@ #include "Engine/UserDefinedStruct.h" #include "Kismet/BlueprintTypeConversions.h" #include "Kismet2/KismetEditorUtilities.h" -#include "Misc/DefaultValueHelper.h" #include "ScopedTransaction.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraphSchema) @@ -254,7 +253,7 @@ void UFlowGraphSchema::CreateDefaultNodesForGraph(UEdGraph& Graph) const // Start node CreateDefaultNode(Graph, AssetClassDefaults, UFlowNode_Start::StaticClass(), NodeOffset, AssetClassDefaults->bStartNodePlacedAsGhostNode); - // Add default nodes for all of the CustomInputs + // Add default nodes for all the CustomInputs if (IsValid(AssetClassDefaults)) { for (const FName& CustomInputName : AssetClassDefaults->CustomInputs) @@ -268,7 +267,6 @@ void UFlowGraphSchema::CreateDefaultNodesForGraph(UEdGraph& Graph) const } UFlowAsset* FlowAsset = CastChecked(&Graph)->GetFlowAsset(); - FlowAsset->HarvestNodeConnections(); } @@ -707,45 +705,37 @@ void UFlowGraphSchema::ConstructBasicPinTooltip(const UEdGraphPin& Pin, const FT return; } - constexpr bool bGeneratingDocumentation = false; - if (bGeneratingDocumentation) + FFormatNamedArguments Args; + Args.Add(TEXT("PinType"), UEdGraphSchema_K2::TypeToText(Pin.PinType)); + + if (UEdGraphNode* PinNode = Pin.GetOwningNode()) { - TooltipOut = PinDescription.ToString(); + UEdGraphSchema_K2 const* const K2Schema = Cast(PinNode->GetSchema()); + if (ensure(K2Schema != nullptr)) // ensure that this node belongs to this schema + { + Args.Add(TEXT("DisplayName"), GetPinDisplayName(&Pin)); + Args.Add(TEXT("LineFeed1"), FText::FromString(TEXT("\n"))); + } } else { - FFormatNamedArguments Args; - Args.Add(TEXT("PinType"), UEdGraphSchema_K2::TypeToText(Pin.PinType)); - - if (UEdGraphNode* PinNode = Pin.GetOwningNode()) - { - UEdGraphSchema_K2 const* const K2Schema = Cast(PinNode->GetSchema()); - if (ensure(K2Schema != nullptr)) // ensure that this node belongs to this schema - { - Args.Add(TEXT("DisplayName"), GetPinDisplayName(&Pin)); - Args.Add(TEXT("LineFeed1"), FText::FromString(TEXT("\n"))); - } - } - else - { - Args.Add(TEXT("DisplayName"), FText::GetEmpty()); - Args.Add(TEXT("LineFeed1"), FText::GetEmpty()); - } + Args.Add(TEXT("DisplayName"), FText::GetEmpty()); + Args.Add(TEXT("LineFeed1"), FText::GetEmpty()); + } - if (!PinDescription.IsEmpty()) - { - Args.Add(TEXT("Description"), PinDescription); - Args.Add(TEXT("LineFeed2"), FText::FromString(TEXT("\n\n"))); - } - else - { - Args.Add(TEXT("Description"), FText::GetEmpty()); - Args.Add(TEXT("LineFeed2"), FText::GetEmpty()); - } - - TooltipOut = FText::Format(LOCTEXT("PinTooltip", "{DisplayName}{LineFeed1}{PinType}{LineFeed2}{Description}"), Args).ToString(); + if (!PinDescription.IsEmpty()) + { + Args.Add(TEXT("Description"), PinDescription); + Args.Add(TEXT("LineFeed2"), FText::FromString(TEXT("\n\n"))); + } + else + { + Args.Add(TEXT("Description"), FText::GetEmpty()); + Args.Add(TEXT("LineFeed2"), FText::GetEmpty()); } + + TooltipOut = FText::Format(LOCTEXT("PinTooltip", "{DisplayName}{LineFeed1}{PinType}{LineFeed2}{Description}"), Args).ToString(); } bool UFlowGraphSchema::CanShowDataTooltipForPin(const UEdGraphPin& Pin) const @@ -1112,7 +1102,7 @@ void UFlowGraphSchema::GetGraphNodeContextActions(FGraphContextMenuBuilder& Cont } } -bool UFlowGraphSchema::IsAddOnAllowedForSelectedObjects(const TArray& SelectedObjects, const UFlowNodeAddOn* AddOnTemplate) const +bool UFlowGraphSchema::IsAddOnAllowedForSelectedObjects(const TArray& SelectedObjects, const UFlowNodeAddOn* AddOnTemplate) { FLOW_ASSERT_ENUM_MAX(EFlowAddOnAcceptResult, 3); @@ -1248,7 +1238,7 @@ void UFlowGraphSchema::GatherNodes() } // prevent adding assets while compiling blueprints - // (because adding assets can cause blueprint compiles to be queued as a side-effect (via GetPlaceableNodeOrAddOnBlueprint)) + // (because adding assets can cause blueprint compiles to be queued as a side effect (via GetPlaceableNodeOrAddOnBlueprint)) if (GCompilingBlueprint) { return; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h index b513c679b..c3706f2fe 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h @@ -93,7 +93,7 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema virtual void GetGraphNodeContextActions(FGraphContextMenuBuilder& ContextMenuBuilder, int32 SubNodeFlags) const; - bool IsAddOnAllowedForSelectedObjects(const TArray& SelectedObjects, const UFlowNodeAddOn* AddOnTemplate) const; + static bool IsAddOnAllowedForSelectedObjects(const TArray& SelectedObjects, const UFlowNodeAddOn* AddOnTemplate); // -- From de2e789c7758df56f24df0cfc1b21ead00b0ed4f Mon Sep 17 00:00:00 2001 From: SPontadit Date: Sun, 15 Dec 2024 17:37:39 +0100 Subject: [PATCH 297/485] Call NotifyGraphChanged when ValidatingAsset to refresh nodes visually without making the asset dirty (#235) --- Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index 70fb8f84e..1ef73f9b7 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -451,6 +451,7 @@ void FFlowAssetEditor::ValidateAsset_Internal() ValidationLogListing->AddMessages(LogResults.Messages); } ValidationLogListing->OnDataChanged().Broadcast(); + FlowAsset->GetGraph()->NotifyGraphChanged(); } void FFlowAssetEditor::ValidateAsset(FFlowMessageLog& MessageLog) From 2fe336f994fed02d0059340f494586df63639ee5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sun, 15 Dec 2024 18:54:57 +0100 Subject: [PATCH 298/485] #215 fixed copying Comment node It was indeed broken by Flow AddOn refactor. Applied static analysis fixes while inspecting code. --- .../Private/Graph/FlowGraphEditor.cpp | 80 ++++++++----------- .../FlowEditor/Public/Graph/FlowGraphEditor.h | 6 +- 2 files changed, 38 insertions(+), 48 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index d611a31ae..0abb5bb09 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -2,11 +2,9 @@ #include "Graph/FlowGraphEditor.h" -#include "FlowEditorCommands.h" -#include "FlowEditorModule.h" - #include "Asset/FlowAssetEditor.h" #include "Asset/FlowDebuggerSubsystem.h" +#include "FlowEditorCommands.h" #include "Graph/FlowGraphEditorSettings.h" #include "Graph/FlowGraphSchema_Actions.h" #include "Graph/Nodes/FlowGraphNode.h" @@ -572,6 +570,10 @@ void SFlowGraphEditor::CopySelectedNodes() const constexpr int32 RootEdNodeParentIndex = INDEX_NONE; PrepareFlowGraphNodeForCopy(*FlowGraphNode, RootEdNodeParentIndex, NewSelectedNodes); } + else + { + NewSelectedNodes.Add(*SelectedIter); + } } FString ExportedText; @@ -581,15 +583,14 @@ void SFlowGraphEditor::CopySelectedNodes() const for (FGraphPanelSelectionSet::TIterator SelectedIter(NewSelectedNodes); SelectedIter; ++SelectedIter) { - UFlowGraphNode* FlowGraphNode = Cast(*SelectedIter); - if (FlowGraphNode) + if (UFlowGraphNode* FlowGraphNode = Cast(*SelectedIter)) { FlowGraphNode->PostCopyNode(); } } } -void SFlowGraphEditor::PrepareFlowGraphNodeForCopy(UFlowGraphNode& FlowGraphNode, int32 ParentEdNodeIndex, FGraphPanelSelectionSet& NewSelectedNodes) const +void SFlowGraphEditor::PrepareFlowGraphNodeForCopy(UFlowGraphNode& FlowGraphNode, const int32 ParentEdNodeIndex, FGraphPanelSelectionSet& NewSelectedNodes) { FlowGraphNode.PrepareForCopying(); @@ -600,14 +601,11 @@ void SFlowGraphEditor::PrepareFlowGraphNodeForCopy(UFlowGraphNode& FlowGraphNode NewSelectedNodes.Add(&FlowGraphNode); // append all subnodes for selection - const TArray& FlowGraphNodeSubNodes = FlowGraphNode.SubNodes; - - for (int32 Idx = 0; Idx < FlowGraphNodeSubNodes.Num(); ++Idx) + for (UFlowGraphNode* SubNode : FlowGraphNode.SubNodes) { - UFlowGraphNode* SubNodeCur = FlowGraphNodeSubNodes[Idx]; - if (SubNodeCur) + if (SubNode) { - PrepareFlowGraphNodeForCopy(*SubNodeCur, ThisFlowGraphNodeIndex, NewSelectedNodes); + PrepareFlowGraphNodeForCopy(*SubNode, ThisFlowGraphNodeIndex, NewSelectedNodes); } } } @@ -677,48 +675,47 @@ void SFlowGraphEditor::PasteNodesHere(const FVector2D& Location) if (AvgCount > 0) { - float InvNumNodes = 1.0f / float(AvgCount); + float InvNumNodes = 1.0f / static_cast(AvgCount); AvgNodePosition.X *= InvNumNodes; AvgNodePosition.Y *= InvNumNodes; } - bool bPastedParentNode = false; - TMap EdNodeCopyIndexMap; for (TSet::TConstIterator It(NodesToPaste); It; ++It) { - UEdGraphNode* PasteNode = *It; - UFlowGraphNode* PasteFlowGraphNode = Cast(PasteNode); + UEdGraphNode* PastedNode = *It; - EdNodeCopyIndexMap.Add(PasteFlowGraphNode->CopySubNodeIndex, PasteFlowGraphNode); - - if (PasteNode && (PasteFlowGraphNode == nullptr || !PasteFlowGraphNode->IsSubNode())) + UFlowGraphNode* PastedFlowGraphNode = Cast(PastedNode); + if (PastedFlowGraphNode) { - bPastedParentNode = true; + EdNodeCopyIndexMap.Add(PastedFlowGraphNode->CopySubNodeIndex, PastedFlowGraphNode); + } + if (PastedNode && (PastedFlowGraphNode == nullptr || !PastedFlowGraphNode->IsSubNode())) + { // Select the newly pasted stuff constexpr bool bSelectNodes = true; - SetNodeSelection(PasteNode, bSelectNodes); + SetNodeSelection(PastedNode, bSelectNodes); - PasteNode->NodePosX = (PasteNode->NodePosX - AvgNodePosition.X) + Location.X; - PasteNode->NodePosY = (PasteNode->NodePosY - AvgNodePosition.Y) + Location.Y; + PastedNode->NodePosX = (PastedNode->NodePosX - AvgNodePosition.X) + Location.X; + PastedNode->NodePosY = (PastedNode->NodePosY - AvgNodePosition.Y) + Location.Y; - PasteNode->SnapToGrid(16); + PastedNode->SnapToGrid(16); // Give new node a different Guid from the old one - PasteNode->CreateNewGuid(); + PastedNode->CreateNewGuid(); + } - if (UFlowNode* FlowNode = Cast(PasteFlowGraphNode->GetFlowNodeBase())) + if (PastedFlowGraphNode) + { + if (UFlowNode* FlowNode = Cast(PastedFlowGraphNode->GetFlowNodeBase())) { // Only full FlowNodes are registered with the Asset // (for now? perhaps we register AddOns in the future?) - FlowAsset->RegisterNode(PasteNode->NodeGuid, FlowNode); + FlowAsset->RegisterNode(PastedNode->NodeGuid, FlowNode); } - } - - if (PasteFlowGraphNode) - { - PasteFlowGraphNode->RemoveAllSubNodes(); + + PastedFlowGraphNode->RemoveAllSubNodes(); } } @@ -736,7 +733,6 @@ void SFlowGraphEditor::PasteNodesHere(const FVector2D& Location) if (PasteNode->CopySubNodeParentIndex == INDEX_NONE) { // INDEX_NONE parent index indicates we should set the parent to the PasteTargetNode - if (PasteTargetNode) { PasteTargetNode->AddSubNode(PasteNode, FlowGraph); @@ -759,15 +755,14 @@ void SFlowGraphEditor::PasteNodesHere(const FVector2D& Location) // Update UI NotifyGraphChanged(); - UObject* GraphOwner = FlowGraph->GetOuter(); - if (GraphOwner) + if (UObject* GraphOwner = FlowGraph->GetOuter()) { GraphOwner->PostEditChange(); GraphOwner->MarkPackageDirty(); } } -TSet SFlowGraphEditor::ImportNodesToPasteFromClipboard(UFlowGraph& FlowGraph, FString& OutTextToImport) const +TSet SFlowGraphEditor::ImportNodesToPasteFromClipboard(UFlowGraph& FlowGraph, FString& OutTextToImport) { // Grab the text to paste from the clipboard. FPlatformApplicationMisc::ClipboardPaste(OutTextToImport); @@ -786,7 +781,6 @@ TArray SFlowGraphEditor::DerivePasteTargetNodesFromSelectedNode for (FGraphPanelSelectionSet::TConstIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter) { UFlowGraphNode* Node = Cast(*SelectedIter); - if (IsValid(Node)) { PasteTargetNodes.Add(Node); @@ -810,7 +804,6 @@ bool SFlowGraphEditor::CanPasteNodes() const if (!ensure(IsValid(FlowGraph))) { // We expect to have a legal FlowGraph pointer at this point - return false; } @@ -826,7 +819,6 @@ bool SFlowGraphEditor::CanPasteNodes() const { // NOTE (gtaylor) It's possible we could support multi-paste, but we'd need to rework PasteNodesHere() // to understand how to paste copies onto each target node. - return false; } @@ -836,13 +828,12 @@ bool SFlowGraphEditor::CanPasteNodes() const if (NodesToPaste.IsEmpty()) { // Must have at least one node to paste - return false; } ON_SCOPE_EXIT { - // We need to clean-up the nodes we built to test the paste operation + // We need to clean up the nodes we built to test the paste operation for (TSet::TConstIterator It(NodesToPaste); It; ++It) { UFlowGraphNode* NodeToPaste = Cast(*It); @@ -868,8 +859,7 @@ bool SFlowGraphEditor::CanPasteNodes() const { checkf(PasteTargetNodes.Num() == 1, TEXT("This is enforced earlier in this function, just confirming the code stays that way here.")); - UFlowGraphNode* PasteTargetNode = PasteTargetNodes.Top(); - + const UFlowGraphNode* PasteTargetNode = PasteTargetNodes.Top(); if (!CanPasteNodesAsSubNodes(NodesToPaste, *PasteTargetNode)) { return false; @@ -879,7 +869,7 @@ bool SFlowGraphEditor::CanPasteNodes() const return true; } -bool SFlowGraphEditor::CanPasteNodesAsSubNodes(const TSet& NodesToPaste, const UFlowGraphNode& PasteTargetNode) const +bool SFlowGraphEditor::CanPasteNodesAsSubNodes(const TSet& NodesToPaste, const UFlowGraphNode& PasteTargetNode) { TSet AllRootSubNodesToPaste; for (TSet::TConstIterator It(NodesToPaste); It; ++It) diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h index 43d36e9a5..4f4ecb0e6 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h @@ -71,7 +71,7 @@ class FLOWEDITOR_API SFlowGraphEditor : public SGraphEditor virtual bool CanDeleteNodes() const; virtual void CopySelectedNodes() const; - void PrepareFlowGraphNodeForCopy(UFlowGraphNode& FlowGraphNode, int32 ParentEdNodeIndex, FGraphPanelSelectionSet& NewSelectedNodes) const; + static void PrepareFlowGraphNodeForCopy(UFlowGraphNode& FlowGraphNode, const int32 ParentEdNodeIndex, FGraphPanelSelectionSet& NewSelectedNodes); virtual bool CanCopyNodes() const; virtual void CutSelectedNodes(); @@ -79,8 +79,8 @@ class FLOWEDITOR_API SFlowGraphEditor : public SGraphEditor virtual void PasteNodes(); - bool CanPasteNodesAsSubNodes(const TSet& NodesToPaste, const UFlowGraphNode& PasteTargetNode) const; - TSet ImportNodesToPasteFromClipboard(UFlowGraph& FlowGraph, FString& OutTextToImport) const; + static bool CanPasteNodesAsSubNodes(const TSet& NodesToPaste, const UFlowGraphNode& PasteTargetNode); + static TSet ImportNodesToPasteFromClipboard(UFlowGraph& FlowGraph, FString& OutTextToImport); TArray DerivePasteTargetNodesFromSelectedNodes() const; public: From 6962a097e56024ced28f3df30e27d8f6575b3681 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sun, 15 Dec 2024 18:55:36 +0100 Subject: [PATCH 299/485] Code cleanup Removed some unused local variables. Updated class layout of UFlowGraph, so related methods are grouped together. Ensured that order of methods is the same in .cpp as in header. No functional changes/. --- Source/FlowEditor/Private/Graph/FlowGraph.cpp | 320 +++++++++--------- .../Private/Graph/Nodes/FlowGraphNode.cpp | 25 +- Source/FlowEditor/Public/Graph/FlowGraph.h | 78 +++-- .../Public/Graph/Nodes/FlowGraphNode.h | 2 +- 4 files changed, 201 insertions(+), 224 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraph.cpp b/Source/FlowEditor/Private/Graph/FlowGraph.cpp index 481d78bdb..c9a7b724b 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraph.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraph.cpp @@ -27,6 +27,7 @@ void FFlowGraphInterface::OnOutputTriggered(UEdGraphNode* GraphNode, const int32 UFlowGraph::UFlowGraph(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) + , GraphVersion(0) { bLockUpdates = false; bIsLoadingGraph = false; @@ -102,25 +103,10 @@ void UFlowGraph::RefreshGraph() } } -void UFlowGraph::NotifyGraphChanged() -{ - if (UFlowAsset* FlowAsset = GetFlowAsset()) - { - FlowAsset->HarvestNodeConnections(); - } - - Super::NotifyGraphChanged(); -} - -UFlowAsset* UFlowGraph::GetFlowAsset() const -{ - return GetTypedOuter(); -} - void UFlowGraph::RecursivelyRefreshAddOns(UFlowGraphNode& FromFlowGraphNode) { // Refresh AddOns - UFlowNodeBase* FromFlowNodeBase = FromFlowGraphNode.GetFlowNodeBase(); + const UFlowNodeBase* FromFlowNodeBase = FromFlowGraphNode.GetFlowNodeBase(); const TArray FlowNodeAddOnChildren = FromFlowNodeBase->GetFlowNodeAddOnChildren(); for (UFlowNodeAddOn* AddOn : FlowNodeAddOnChildren) @@ -133,8 +119,8 @@ void UFlowGraph::RecursivelyRefreshAddOns(UFlowGraphNode& FromFlowGraphNode) TEXT("Missing AddOn detected for node %s (parent %s)"), *FromFlowNodeBase->GetName(), FromFlowGraphNode.GetParentNode() ? - *FromFlowGraphNode.GetParentNode()->GetName() : - TEXT("")); + *FromFlowGraphNode.GetParentNode()->GetName() : + TEXT("")); continue; } @@ -143,7 +129,7 @@ void UFlowGraph::RecursivelyRefreshAddOns(UFlowGraphNode& FromFlowGraphNode) const TSubclassOf ExpectAddOnGraphNodeClass = UFlowGraphSchema::GetAssignedGraphNodeClass(AddOn->GetClass()); UFlowGraphNode* RefreshedAddOnFlowGraphNode = AddOnFlowGraphNode; - UClass* ExistingAddOnGraphNodeClass = IsValid(AddOnFlowGraphNode) ? AddOnFlowGraphNode->GetClass() : nullptr; + const UClass* ExistingAddOnGraphNodeClass = IsValid(AddOnFlowGraphNode) ? AddOnFlowGraphNode->GetClass() : nullptr; if (ExistingAddOnGraphNodeClass != ExpectAddOnGraphNodeClass) { @@ -156,50 +142,46 @@ void UFlowGraph::RecursivelyRefreshAddOns(UFlowGraphNode& FromFlowGraphNode) } } -void UFlowGraph::RecursivelySetupAllFlowGraphNodesForEditing(UFlowGraphNode& FromFlowGraphNode) +void UFlowGraph::NotifyGraphChanged() { - UFlowNodeBase* FromNodeInstance = FromFlowGraphNode.GetFlowNodeBase(); - if (IsValid(FromNodeInstance)) + if (UFlowAsset* FlowAsset = GetFlowAsset()) { - // Setup all of the flow node (and subnode) instances for editing - FromNodeInstance->SetupForEditing(FromFlowGraphNode); + FlowAsset->HarvestNodeConnections(); } - else + + Super::NotifyGraphChanged(); +} + +UFlowAsset* UFlowGraph::GetFlowAsset() const +{ + return GetTypedOuter(); +} + +void UFlowGraph::ValidateAsset(FFlowMessageLog& MessageLog) +{ + if (UFlowAsset* FlowAsset = GetFlowAsset()) { - // Reconstruct the node if the NodeInstance is missing - FromFlowGraphNode.ReconstructNode(); + FlowAsset->ValidateAsset(MessageLog); } - for (UFlowGraphNode* SubNode : FromFlowGraphNode.SubNodes) + for (UEdGraphNode* Node : Nodes) { - // Setup all of the flow subnodes for editing - if (IsValid(SubNode)) + if (const UFlowGraphNode* FlowGraphNode = Cast(Node)) { - SubNode->SetParentNodeForSubNode(&FromFlowGraphNode); - - RecursivelySetupAllFlowGraphNodesForEditing(*SubNode); + FlowGraphNode->ValidateGraphNode(MessageLog); } } } -void UFlowGraph::UpdateAsset(int32 UpdateFlags) +void UFlowGraph::Serialize(FArchive& Ar) { - if (IsLocked()) - { - return; - } + // Overridden to flags up errors in the behavior tree while cooking. + Super::Serialize(Ar); - // UpdateAsset is called to do any reconciliation from the editor-version of the - // graph to the runtime version of the graph data. - // In our case, it will copy the AddOns from their editor-side UFlowGraphNode containers to - // their runtime UFlowNode and/or UFlowNodeAddOn ::AddOn array entry (via OnUpdateAsset) - for (UEdGraphNode* EdNode : Nodes) + if (Ar.IsSaving() || Ar.IsCooking()) { - UFlowGraphNode* FlowGraphNode = Cast(EdNode); - if (FlowGraphNode) - { - FlowGraphNode->OnUpdateAsset(UpdateFlags); - } + // Logging of errors happens in UpdateDeprecatedClasses + UpdateDeprecatedClasses(); } } @@ -215,9 +197,9 @@ void UFlowGraph::OnLoaded() bIsLoadingGraph = true; // Setup all the Nodes in the graph for editing - for (UEdGraphNode* EdNode : Nodes) + for (UEdGraphNode* Node : Nodes) { - UFlowGraphNode* FlowGraphNode = Cast(EdNode); + UFlowGraphNode* FlowGraphNode = Cast(Node); if (IsValid(FlowGraphNode)) { RecursivelySetupAllFlowGraphNodesForEditing(*FlowGraphNode); @@ -264,172 +246,135 @@ void UFlowGraph::MarkVersion() GraphVersion = 1; } -bool UFlowGraph::UpdateUnknownNodeClasses() +void UFlowGraph::UpdateClassData() { - bool bUpdated = false; - for (int32 NodeIdx = 0; NodeIdx < Nodes.Num(); NodeIdx++) + for (UEdGraphNode* Node : Nodes) { - UFlowGraphNode* MyNode = Cast(Nodes[NodeIdx]); - if (MyNode) + if (UFlowGraphNode* FlowGraphNode = Cast(Node)) { - const bool bUpdatedNode = MyNode->RefreshNodeClass(); - bUpdated = bUpdated || bUpdatedNode; + FlowGraphNode->UpdateNodeClassData(); - for (int32 SubNodeIdx = 0; SubNodeIdx < MyNode->SubNodes.Num(); SubNodeIdx++) + for (UFlowGraphNode* SubNode : FlowGraphNode->SubNodes) { - if (MyNode->SubNodes[SubNodeIdx]) + if (SubNode) { - const bool bUpdatedSubNode = MyNode->SubNodes[SubNodeIdx]->RefreshNodeClass(); - bUpdated = bUpdated || bUpdatedSubNode; + SubNode->UpdateNodeClassData(); } } } } - - return bUpdated; } -FString UFlowGraph::GetDeprecationMessage(const UClass* Class) +void UFlowGraph::UpdateAsset(const int32 UpdateFlags) { - static FName MetaDeprecated = TEXT("DeprecatedNode"); - static FName MetaDeprecatedMessage = TEXT("DeprecationMessage"); - FString DefDeprecatedMessage("Please remove it!"); - FString DeprecatedPrefix("DEPRECATED"); - FString DeprecatedMessage; - - if (Class && Class->HasAnyClassFlags(CLASS_Native) && Class->HasMetaData(MetaDeprecated)) + if (IsLocked()) { - DeprecatedMessage = DeprecatedPrefix + TEXT(": "); - DeprecatedMessage += Class->HasMetaData(MetaDeprecatedMessage) ? Class->GetMetaData(MetaDeprecatedMessage) : DefDeprecatedMessage; + return; } - return DeprecatedMessage; -} - -void UFlowGraph::UpdateFlowGraphNodeErrorMessage(UFlowGraphNode& Node) -{ - // Broke out setting error message in to own function so it can be reused when iterating nodes collection. - if (Node.GetFlowNodeBase()) - { - Node.ErrorMessage = GetDeprecationMessage(Node.GetFlowNodeBase()->GetClass()); - } - else + // UpdateAsset is called to do any reconciliation from the editor-version of the + // graph to the runtime version of the graph data. + // In our case, it will copy the AddOns from their editor-side UFlowGraphNode containers to + // their runtime UFlowNode and/or UFlowNodeAddOn ::AddOn array entry (via OnUpdateAsset) + for (UEdGraphNode* Node : Nodes) { - // Null instance. Do we have any meaningful class data? - FString StoredClassName = Node.NodeInstanceClass.GetAssetName(); - StoredClassName.RemoveFromEnd(TEXT("_C")); - - if (!StoredClassName.IsEmpty()) + if (UFlowGraphNode* FlowGraphNode = Cast(Node)) { - // There is class data here but the instance was not be created. - static const FString IsMissingClassMessage(" class missing. Referenced by "); - Node.ErrorMessage = StoredClassName + IsMissingClassMessage + Node.GetFullName(); + FlowGraphNode->OnUpdateAsset(UpdateFlags); } } - - if (Node.HasErrors()) - { - UE_LOG(LogFlowEditor, Error, TEXT("%s"), *Node.ErrorMessage); - } } -void UFlowGraph::ValidateAsset(FFlowMessageLog& MessageLog) +bool UFlowGraph::UpdateUnknownNodeClasses() { - UFlowAsset* FlowAsset = GetFlowAsset(); - if (FlowAsset) - { - FlowAsset->ValidateAsset(MessageLog); - } + bool bUpdated = false; - for (int32 Idx = 0, IdxNum = Nodes.Num(); Idx < IdxNum; ++Idx) + for (UEdGraphNode* Node : Nodes) { - UFlowGraphNode* Node = Cast(Nodes[Idx]); - if (Node != nullptr) + if (UFlowGraphNode* FlowGraphNode = Cast(Node)) { - Node->ValidateGraphNode(MessageLog); + const bool bUpdatedNode = FlowGraphNode->RefreshNodeClass(); + bUpdated = bUpdated || bUpdatedNode; + + for (UFlowGraphNode* SubNode : FlowGraphNode->SubNodes) + { + const bool bUpdatedSubNode = SubNode->RefreshNodeClass(); + bUpdated = bUpdated || bUpdatedSubNode; + } } } + + return bUpdated; } void UFlowGraph::UpdateDeprecatedClasses() { // This function sets error messages and logs errors about nodes. - for (int32 Idx = 0, IdxNum = Nodes.Num(); Idx < IdxNum; ++Idx) + for (UEdGraphNode* Node : Nodes) { - UFlowGraphNode* Node = Cast(Nodes[Idx]); - if (Node != nullptr) + if (UFlowGraphNode* FlowGraphNode = Cast(Node)) { - UpdateFlowGraphNodeErrorMessage(*Node); - - for (int32 SubIdx = 0, SubIdxNum = Node->SubNodes.Num(); SubIdx < SubIdxNum; ++SubIdx) + UpdateFlowGraphNodeErrorMessage(*FlowGraphNode); + + for (UFlowGraphNode* SubNode : FlowGraphNode->SubNodes) { - if (Node->SubNodes[SubIdx] != nullptr) + if (SubNode) { - UpdateFlowGraphNodeErrorMessage(*Node->SubNodes[SubIdx]); + UpdateFlowGraphNodeErrorMessage(*SubNode); } } } } } -void UFlowGraph::Serialize(FArchive& Ar) +void UFlowGraph::UpdateFlowGraphNodeErrorMessage(UFlowGraphNode& Node) { - // Overridden to flags up errors in the behavior tree while cooking. - Super::Serialize(Ar); - - if (Ar.IsSaving() || Ar.IsCooking()) + // Broke out setting error message in to own function so it can be reused when iterating nodes collection. + if (Node.GetFlowNodeBase()) { - // Logging of errors happens in UpdateDeprecatedClasses - UpdateDeprecatedClasses(); + Node.ErrorMessage = GetDeprecationMessage(Node.GetFlowNodeBase()->GetClass()); } -} - -void UFlowGraph::UpdateClassData() -{ - for (int32 Idx = 0; Idx < Nodes.Num(); Idx++) + else { - UFlowGraphNode* Node = Cast(Nodes[Idx]); - if (Node) - { - Node->UpdateNodeClassData(); + // Null instance. Do we have any meaningful class data? + FString StoredClassName = Node.NodeInstanceClass.GetAssetName(); + StoredClassName.RemoveFromEnd(TEXT("_C")); - for (int32 SubIdx = 0; SubIdx < Node->SubNodes.Num(); SubIdx++) - { - if (UFlowGraphNode* SubNode = Node->SubNodes[SubIdx]) - { - SubNode->UpdateNodeClassData(); - } - } + if (!StoredClassName.IsEmpty()) + { + // There is class data here but the instance was not be created. + static const FString IsMissingClassMessage(" class missing. Referenced by "); + Node.ErrorMessage = StoredClassName + IsMissingClassMessage + Node.GetFullName(); } } + + if (Node.HasErrors()) + { + UE_LOG(LogFlowEditor, Error, TEXT("%s"), *Node.ErrorMessage); + } } -void UFlowGraph::CollectAllNodeInstances(TSet& NodeInstances) +FString UFlowGraph::GetDeprecationMessage(const UClass* Class) { - for (int32 Idx = 0; Idx < Nodes.Num(); Idx++) - { - UFlowGraphNode* MyNode = Cast(Nodes[Idx]); - if (MyNode) - { - NodeInstances.Add(MyNode->GetFlowNodeBase()); + static FName MetaDeprecated = TEXT("DeprecatedNode"); + static FName MetaDeprecatedMessage = TEXT("DeprecationMessage"); + const FString DefDeprecatedMessage("Please remove it!"); + const FString DeprecatedPrefix("DEPRECATED"); + FString DeprecatedMessage; - for (int32 SubIdx = 0; SubIdx < MyNode->SubNodes.Num(); SubIdx++) - { - if (MyNode->SubNodes[SubIdx]) - { - NodeInstances.Add(MyNode->SubNodes[SubIdx]->GetFlowNodeBase()); - } - } - } + if (Class && Class->HasAnyClassFlags(CLASS_Native) && Class->HasMetaData(MetaDeprecated)) + { + DeprecatedMessage = DeprecatedPrefix + TEXT(": "); + DeprecatedMessage += Class->HasMetaData(MetaDeprecatedMessage) ? Class->GetMetaData(MetaDeprecatedMessage) : DefDeprecatedMessage; } + + return DeprecatedMessage; } -bool UFlowGraph::CanRemoveNestedObject(UObject* TestObject) const +void UFlowGraph::OnSubNodeDropped() { - return !TestObject->IsA(UEdGraphNode::StaticClass()) && - !TestObject->IsA(UEdGraph::StaticClass()) && - !TestObject->IsA(UEdGraphSchema::StaticClass()); + NotifyGraphChanged(); } void UFlowGraph::RemoveOrphanedNodes() @@ -441,7 +386,7 @@ void UFlowGraph::RemoveOrphanedNodes() // Obtain a list of all nodes actually in the asset and discard unused nodes TArray AllInners; - const bool bIncludeNestedObjects = false; + constexpr bool bIncludeNestedObjects = false; GetObjectsWithOuter(GetOuter(), AllInners, bIncludeNestedObjects); for (auto InnerIt = AllInners.CreateConstIterator(); InnerIt; ++InnerIt) { @@ -451,27 +396,43 @@ void UFlowGraph::RemoveOrphanedNodes() OnNodeInstanceRemoved(TestObject); TestObject->SetFlags(RF_Transient); - TestObject->Rename(NULL, GetTransientPackage(), REN_DontCreateRedirectors | REN_NonTransactional | REN_ForceNoResetLoaders); + TestObject->Rename(nullptr, GetTransientPackage(), REN_DontCreateRedirectors | REN_NonTransactional | REN_ForceNoResetLoaders); } } } -void UFlowGraph::OnNodeInstanceRemoved(UObject* NodeInstance) +void UFlowGraph::CollectAllNodeInstances(TSet& NodeInstances) { - // empty in base class + for (UObject* NodeInstance : NodeInstances) + { + if (UFlowGraphNode* FlowGraphNode = Cast(NodeInstance)) + { + NodeInstances.Add(FlowGraphNode->GetFlowNodeBase()); + + for (const UFlowGraphNode* SubNode : FlowGraphNode->SubNodes) + { + if (SubNode) + { + NodeInstances.Add(SubNode->GetFlowNodeBase()); + } + } + } + } } -void UFlowGraph::OnNodesPasted(const FString& ImportStr) +bool UFlowGraph::CanRemoveNestedObject(UObject* TestObject) const { - // empty in base class + return !TestObject->IsA(UEdGraphNode::StaticClass()) && + !TestObject->IsA(UEdGraph::StaticClass()) && + !TestObject->IsA(UEdGraphSchema::StaticClass()); } -UEdGraphPin* UFlowGraph::FindGraphNodePin(UEdGraphNode* Node, EEdGraphPinDirection Dir) +UEdGraphPin* UFlowGraph::FindGraphNodePin(UEdGraphNode* Node, const EEdGraphPinDirection Direction) { UEdGraphPin* Pin = nullptr; for (int32 Idx = 0; Idx < Node->Pins.Num(); Idx++) { - if (Node->Pins[Idx]->Direction == Dir) + if (Node->Pins[Idx]->Direction == Direction) { Pin = Node->Pins[Idx]; break; @@ -497,7 +458,32 @@ void UFlowGraph::UnlockUpdates() UpdateAsset(); } -void UFlowGraph::OnSubNodeDropped() +void UFlowGraph::RecursivelySetupAllFlowGraphNodesForEditing(UFlowGraphNode& FromFlowGraphNode) { - NotifyGraphChanged(); + UFlowNodeBase* FromNodeInstance = FromFlowGraphNode.GetFlowNodeBase(); + if (IsValid(FromNodeInstance)) + { + // Setup all the flow node (and SubNode) instances for editing + FromNodeInstance->SetupForEditing(FromFlowGraphNode); + } + else + { + // Reconstruct the node if the NodeInstance is missing + FromFlowGraphNode.ReconstructNode(); + } + + for (UFlowGraphNode* SubNode : FromFlowGraphNode.SubNodes) + { + // Setup all the flow SubNodes for editing + if (IsValid(SubNode)) + { + SubNode->SetParentNodeForSubNode(&FromFlowGraphNode); + + RecursivelySetupAllFlowGraphNodesForEditing(*SubNode); + } + } } + + + + diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index e9a09862c..f65b838ea 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -306,14 +306,12 @@ void UFlowGraphNode::ReconstructNode() InputPins.Reset(); OutputPins.Reset(); - bool bChangedAutoFlowPins = false; - // Harvest the auto-generated pins before refreshing context pins if (UFlowNode* FlowNode = Cast(NodeInstance)) { if (UFlowAsset* FlowAsset = NodeInstance->GetFlowAsset()) { - bChangedAutoFlowPins = FlowAsset->TryUpdateManagedFlowPinsForNode(*FlowNode); + FlowAsset->TryUpdateManagedFlowPinsForNode(*FlowNode); } } @@ -493,7 +491,7 @@ void UFlowGraphNode::GetNodeContextMenuActions(class UToolMenu* Menu, class UGra "AttachAddOn", LOCTEXT("AttachAddOn", "Attach AddOn..."), LOCTEXT("AttachAddOnTooltip", "Attaches an AddOn to the Node"), - FNewToolMenuDelegate::CreateUObject(this, &UFlowGraphNode::CreateAttachAddOnSubMenu, (UEdGraph*)Context->Graph) + FNewToolMenuDelegate::CreateUObject(this, &UFlowGraphNode::CreateAttachAddOnSubMenu, static_cast(Context->Graph)) ); } @@ -997,7 +995,7 @@ void UFlowGraphNode::RefreshContextPins(const bool bReconstructNode) } bool bIsLoad = false; - if (UFlowGraph* FlowGraph = GetFlowGraph()) + if (const UFlowGraph* FlowGraph = GetFlowGraph()) { bIsLoad = FlowGraph->IsLoadingGraph(); } @@ -1023,7 +1021,7 @@ void UFlowGraphNode::RefreshContextPins(const bool bReconstructNode) if (bMaintainedNoContextPins || !HavePinsChanged()) { - // We dont have contextual pins to account for; or the contextual pins have not changed. We can skip now. + // We don't have contextual pins to account for; or the contextual pins have not changed. We can skip now. return; } @@ -1093,7 +1091,7 @@ void UFlowGraphNode::GetPinHoverText(const UEdGraphPin& Pin, FString& HoverTextO } } -const FName& UFlowGraphNode::GetPinCategoryFromFlowPin(const FFlowPin& FlowPin) const +const FName& UFlowGraphNode::GetPinCategoryFromFlowPin(const FFlowPin& FlowPin) { return FFlowPin::GetPinCategoryFromPinType(FlowPin.GetPinType()); } @@ -1318,7 +1316,7 @@ void UFlowGraphNode::ResetNodeOwner() UEdGraph* MyGraph = GetGraph(); UObject* GraphOwner = MyGraph ? MyGraph->GetOuter() : nullptr; - NodeInstance->Rename(NULL, GraphOwner, REN_DontCreateRedirectors | REN_DoNotDirty); + NodeInstance->Rename(nullptr, GraphOwner, REN_DontCreateRedirectors | REN_DoNotDirty); NodeInstance->ClearFlags(RF_Transient); for (auto& SubNode : SubNodes) @@ -1406,7 +1404,7 @@ void UFlowGraphNode::SetParentNodeForSubNode(UFlowGraphNode* InParentNode) { if (InParentNode) { - // Once a subnode, always a subnode + // Once a SubNode, always a SubNode bIsSubNode = true; } @@ -1446,7 +1444,7 @@ void UFlowGraphNode::RebuildRuntimeAddOnsFromEditorSubNodes() } } - // Update the subnodes as well + // Update the SubNodes as well for (UFlowGraphNode* SubNode : SubNodes) { if (IsValid(SubNode)) @@ -1467,7 +1465,7 @@ void UFlowGraphNode::FindDiffs(UEdGraphNode* OtherNode, FDiffResults& Results) { Super::FindDiffs(OtherNode, Results); - UFlowGraphNode* OtherGraphNode = Cast(OtherNode); + const UFlowGraphNode* OtherGraphNode = Cast(OtherNode); if (!IsValid(OtherGraphNode)) { return; @@ -1697,8 +1695,6 @@ void UFlowGraphNode::ValidateGraphNode(FFlowMessageLog& MessageLog) const { // Verify that all input data pin connections are legal - bool bAppendedErrors = false; - if (!NodeInstance) { // Missing the node instance! @@ -1708,10 +1704,7 @@ void UFlowGraphNode::ValidateGraphNode(FFlowMessageLog& MessageLog) const return; } - FFlowMessageLog& ValidationLog = NodeInstance->ValidationLog; - const UFlowGraphSchema* Schema = CastChecked(GetSchema()); - for (const UEdGraphPin* EdGraphPin : InputPins) { if (!FFlowPin::IsDataPinCategory(EdGraphPin->PinType.PinCategory)) diff --git a/Source/FlowEditor/Public/Graph/FlowGraph.h b/Source/FlowEditor/Public/Graph/FlowGraph.h index bfbb14311..6e1d5c310 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraph.h +++ b/Source/FlowEditor/Public/Graph/FlowGraph.h @@ -25,74 +25,72 @@ class FLOWEDITOR_API UFlowGraph : public UEdGraph { GENERATED_UCLASS_BODY() +protected: + /** Graph version number */ + UPROPERTY() + int32 GraphVersion; + + /** if set, graph modifications won't cause updates in internal tree structure + * flag allows freezing update during heavy changes like pasting new nodes + */ + uint32 bLockUpdates : 1; + + // is currently loading the Flow Graph (used to suppress some work during load) + uint32 bIsLoadingGraph : 1; + public: static UEdGraph* CreateGraph(UFlowAsset* InFlowAsset); static UEdGraph* CreateGraph(UFlowAsset* InFlowAsset, TSubclassOf FlowSchema); void RefreshGraph(); +protected: + void RecursivelyRefreshAddOns(UFlowGraphNode& FromFlowGraphNode); + static void RecursivelySetupAllFlowGraphNodesForEditing(UFlowGraphNode& FromFlowGraphNode); + +public: // UEdGraph virtual void NotifyGraphChanged() override; // -- - /** Returns the FlowAsset that contains this graph */ UFlowAsset* GetFlowAsset() const; - - //~ Begin UObject Interface. - virtual void Serialize(FArchive& Ar) override; - //~ End UObject Interface. - - virtual void CollectAllNodeInstances(TSet& NodeInstances); - virtual bool CanRemoveNestedObject(UObject* TestObject) const; - virtual void OnNodeInstanceRemoved(UObject* NodeInstance); - - UEdGraphPin* FindGraphNodePin(UEdGraphNode* Node, EEdGraphPinDirection Dir); - void ValidateAsset(FFlowMessageLog& MessageLog); + // UObject + virtual void Serialize(FArchive& Ar) override; + // -- + public: - virtual void OnCreated(); virtual void OnLoaded(); virtual void OnSave(); virtual void Initialize(); - - virtual void UpdateAsset(int32 UpdateFlags = 0); virtual void UpdateVersion(); virtual void MarkVersion(); - virtual void OnSubNodeDropped(); - virtual void OnNodesPasted(const FString& ImportStr); - + void UpdateClassData(); + virtual void UpdateAsset(const int32 UpdateFlags = 0); bool UpdateUnknownNodeClasses(); void UpdateDeprecatedClasses(); + +protected: + static void UpdateFlowGraphNodeErrorMessage(UFlowGraphNode& Node); + static FString GetDeprecationMessage(const UClass* Class); + +public: + virtual void OnSubNodeDropped(); + virtual void OnNodesPasted(const FString& ImportStr) {} + void RemoveOrphanedNodes(); - void UpdateClassData(); + virtual void CollectAllNodeInstances(TSet& NodeInstances); + virtual bool CanRemoveNestedObject(UObject* TestObject) const; + virtual void OnNodeInstanceRemoved(UObject* NodeInstance) {} + + static UEdGraphPin* FindGraphNodePin(UEdGraphNode* Node, const EEdGraphPinDirection Direction); bool IsLocked() const; void LockUpdates(); void UnlockUpdates(); bool IsLoadingGraph() const { return bIsLoadingGraph; } - -protected: - static void RecursivelySetupAllFlowGraphNodesForEditing(UFlowGraphNode& FromFlowGraphNode); - void RecursivelyRefreshAddOns(UFlowGraphNode& FromFlowGraphNode); - - static FString GetDeprecationMessage(const UClass* Class); - static void UpdateFlowGraphNodeErrorMessage(UFlowGraphNode& Node); - -protected: - - /** Graph version number */ - UPROPERTY() - int32 GraphVersion; - - /** if set, graph modifications won't cause updates in internal tree structure - * flag allows freezing update during heavy changes like pasting new nodes - */ - uint32 bLockUpdates : 1; - - // is currently loading the flow graph (used to suppress some work during load) - uint32 bIsLoadingGraph : 1; }; diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h index 6cfea5f17..dd598751d 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h @@ -204,7 +204,7 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode protected: // Gets the PinCategory from the FlowPin // (accounting for FFlowPin structs that predate the PinCategory field) - const FName& GetPinCategoryFromFlowPin(const FFlowPin& FlowPin) const; + static const FName& GetPinCategoryFromFlowPin(const FFlowPin& FlowPin); ////////////////////////////////////////////////////////////////////////// // Breakpoints From 19857cdf6eccd12a64a581c115254378839a2fa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sun, 15 Dec 2024 19:20:37 +0100 Subject: [PATCH 300/485] #205 preventing duplication of unique nodes like Start Node --- .../Private/Graph/Nodes/FlowGraphNode.cpp | 37 +++++++++++++++---- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index f65b838ea..7bdd6bc14 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -577,7 +577,7 @@ void UFlowGraphNode::CreateAttachAddOnSubMenu(UToolMenu* Menu, UEdGraph* Graph) { UFlowGraphNode* MutableThis = const_cast(this); - TSharedRef Widget = + const TSharedRef Widget = SNew(SGraphEditorActionMenuFlow) .GraphObj(Graph) .GraphNode(MutableThis) @@ -593,7 +593,28 @@ bool UFlowGraphNode::CanUserDeleteNode() const bool UFlowGraphNode::CanDuplicateNode() const { - return NodeInstance ? NodeInstance->bCanDuplicate : Super::CanDuplicateNode(); + if (NodeInstance) + { + return NodeInstance->bCanDuplicate; + } + + // support code paths calling this method on CDO, where there's no Flow Node Instance + if (AssignedNodeClasses.Num() > 0) + { + // we simply allow action if any Assigned Node Class accepts it, as the action is disallowed in special node likes StartNode + for (const UClass* Class : AssignedNodeClasses) + { + const UFlowNode* NodeDefaults = Class->GetDefaultObject(); + if (NodeDefaults && NodeDefaults->bCanDuplicate) + { + return true; + } + } + + return false; + } + + return true; } TSharedPtr UFlowGraphNode::CreateVisualWidget() @@ -1024,7 +1045,7 @@ void UFlowGraphNode::RefreshContextPins(const bool bReconstructNode) // We don't have contextual pins to account for; or the contextual pins have not changed. We can skip now. return; } - + const FScopedTransaction Transaction(LOCTEXT("RefreshContextPins", "Refresh Context Pins")); Modify(); @@ -1264,7 +1285,7 @@ void UFlowGraphNode::LogError(const FString& MessageToLog, const UFlowNodeBase* bool UFlowGraphNode::HavePinsChanged() { - const UFlowNode* FlowNodeInstance = Cast(NodeInstance); + const UFlowNode* FlowNodeInstance = Cast(NodeInstance); if (!IsValid(FlowNodeInstance)) { // default to having changed because we don't have a way to confirm that the pins have remained intact. @@ -1288,7 +1309,7 @@ bool UFlowGraphNode::HavePinsChanged() // There is a different number of EdGraphPins and Flow Node pins; something changed. return true; } - + TArray PinNames; for (const UEdGraphPin* Pin : Pins) { @@ -1306,7 +1327,7 @@ bool UFlowGraphNode::HavePinsChanged() } // Nothing changed - return false; + return false; } void UFlowGraphNode::ResetNodeOwner() @@ -1720,7 +1741,7 @@ void UFlowGraphNode::ValidateGraphNode(FFlowMessageLog& MessageLog) const for (UEdGraphPin* const ConnectedPin : EdGraphPin->LinkedTo) { const FPinConnectionResponse Response = Schema->CanCreateConnection(ConnectedPin, EdGraphPin); - + if (!Response.CanSafeConnect()) { MessageLog.Error(*FString::Printf(TEXT("Pin %s has invalid connection: %s"), *EdGraphPin->GetName(), *Response.Message.ToString()), NodeInstance); @@ -1765,7 +1786,7 @@ bool UFlowGraphNode::CanAcceptSubNodeAsChild(const UFlowGraphNode& SubNodeToCons { *OutReasonString = TEXT("Cannot be a AddOn of one of our own AddOns"); } - + return false; } From 059f450094f782e89b356f7ae5197e96a3acc452 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sun, 15 Dec 2024 20:45:50 +0100 Subject: [PATCH 301/485] Flow 2.1 --- Flow.uplugin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.uplugin b/Flow.uplugin index 44cd2a52e..a914fff06 100644 --- a/Flow.uplugin +++ b/Flow.uplugin @@ -1,6 +1,6 @@ { "FileVersion" : 3, - "Version" : 2.0, + "Version" : 2.1, "FriendlyName" : "Flow", "Description" : "Design-agnostic node editor for scripting game’s flow.", "Category" : "Gameplay", From dc2240577be4aa90d6c9a7da7032dc15199a5c9c Mon Sep 17 00:00:00 2001 From: Nil Date: Wed, 18 Dec 2024 01:23:50 +0800 Subject: [PATCH 302/485] Check FlowNode is active before TriggerOutput (#248) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If flow node already finished or aborted, we: 1. report an error (in this way, we don’t hide other problems such as not cleanup properly) 2. then return (we do not trigger output if node isn’t active) --- Source/Flow/Private/Nodes/FlowNode.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 9a286e1a4..08b2c87af 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -863,6 +863,13 @@ void UFlowNode::TriggerFirstOutput(const bool bFinish) void UFlowNode::TriggerOutput(const FName PinName, const bool bFinish /*= false*/, const EFlowPinActivationType ActivationType /*= Default*/) { + if (ActivationState == EFlowNodeState::Completed || ActivationState == EFlowNodeState::Aborted) + { + // do not trigger output if node is already finished or aborted + LogError(TEXT("Trying to TriggerOutput after finished or aborted")); + return; + } + // clean up node, if needed if (bFinish) { From 640a787802c7371436cafda13807c088a7427104 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Tue, 17 Dec 2024 18:47:19 +0100 Subject: [PATCH 303/485] #237 Branch node never stops --- .../Private/Nodes/Route/FlowNode_Branch.cpp | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Branch.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Branch.cpp index 2a9c848b1..645b776e9 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Branch.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Branch.cpp @@ -23,12 +23,10 @@ UFlowNode_Branch::UFlowNode_Branch(const FObjectInitializer& ObjectInitializer) OutputPins.Add(FFlowPin(OUTPIN_True)); OutputPins.Add(FFlowPin(OUTPIN_False)); - AllowedSignalModes = { EFlowSignalMode::Enabled, EFlowSignalMode::Disabled }; + AllowedSignalModes = {EFlowSignalMode::Enabled, EFlowSignalMode::Disabled}; } -EFlowAddOnAcceptResult UFlowNode_Branch::AcceptFlowNodeAddOnChild_Implementation( - const UFlowNodeAddOn* AddOnTemplate, - const TArray& AdditionalAddOnsToAssumeAreChildren) const +EFlowAddOnAcceptResult UFlowNode_Branch::AcceptFlowNodeAddOnChild_Implementation(const UFlowNodeAddOn* AddOnTemplate, const TArray& AdditionalAddOnsToAssumeAreChildren) const { if (IFlowPredicateInterface::ImplementsInterfaceSafe(AddOnTemplate)) { @@ -40,14 +38,6 @@ EFlowAddOnAcceptResult UFlowNode_Branch::AcceptFlowNodeAddOnChild_Implementation void UFlowNode_Branch::ExecuteInput(const FName& PinName) { - bool bResult = UFlowNodeAddOn_PredicateAND::EvaluatePredicateAND(AddOns); - - if (bResult) - { - TriggerOutput(OUTPIN_True); - } - else - { - TriggerOutput(OUTPIN_False); - } + const bool bResult = UFlowNodeAddOn_PredicateAND::EvaluatePredicateAND(AddOns); + TriggerOutput(bResult ? OUTPIN_True : OUTPIN_False, true); } From 4c18012e98a504540442de49f4c82508b0f01929 Mon Sep 17 00:00:00 2001 From: Soraphis Date: Tue, 17 Dec 2024 20:12:22 +0100 Subject: [PATCH 304/485] feat: Added BlueprintCallable function to trigger a custom input on a FlowComponent. (#214) this allows more control from the owner of a FlowComponent, incases where sending a notify from actor would be more cumbersome. --- Source/Flow/Private/FlowComponent.cpp | 13 +++++++++++++ Source/Flow/Public/FlowComponent.h | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/Source/Flow/Private/FlowComponent.cpp b/Source/Flow/Private/FlowComponent.cpp index 34ed49761..d6e773811 100644 --- a/Source/Flow/Private/FlowComponent.cpp +++ b/Source/Flow/Private/FlowComponent.cpp @@ -435,6 +435,19 @@ void UFlowComponent::StartRootFlow() } } +void UFlowComponent::TriggerRootFlowCustomInput(const FName& EventName) +{ + if (!RootFlow || !IsFlowNetMode(RootFlowMode)) return; + + if (UFlowSubsystem* FlowSubsystem = GetFlowSubsystem()) + { + if(auto Flow = FlowSubsystem->GetRootFlow(this); IsValid(Flow)) + { + Flow->TriggerCustomInput(EventName); + } + } +} + void UFlowComponent::FinishRootFlow(UFlowAsset* TemplateAsset, const EFlowFinishPolicy FinishPolicy) { if (UFlowSubsystem* FlowSubsystem = GetFlowSubsystem()) diff --git a/Source/Flow/Public/FlowComponent.h b/Source/Flow/Public/FlowComponent.h index 0a48e6572..1e481442f 100644 --- a/Source/Flow/Public/FlowComponent.h +++ b/Source/Flow/Public/FlowComponent.h @@ -199,6 +199,10 @@ class FLOW_API UFlowComponent : public UActorComponent, public IFlowOwnerInterfa UFUNCTION(BlueprintCallable, Category = "RootFlow") virtual void StartRootFlow(); + // This will trigger a specific CustomInput on this components root flow + UFUNCTION(BlueprintCallable, Category = "RootFlow") + void TriggerRootFlowCustomInput(const FName& EventName); + // This will destroy instantiated Flow Asset - created from asset assigned on this component. UFUNCTION(BlueprintCallable, Category = "RootFlow") virtual void FinishRootFlow(UFlowAsset* TemplateAsset, const EFlowFinishPolicy FinishPolicy); From a57b2c2467817d81f236b52055ff7df80d272203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Tue, 17 Dec 2024 20:31:42 +0100 Subject: [PATCH 305/485] consistent naming of functions related to Custom Input/Output consistent --- Source/Flow/Private/FlowAsset.cpp | 2 +- Source/Flow/Private/FlowComponent.cpp | 47 ++++++++++++++++++--------- Source/Flow/Public/FlowComponent.h | 27 ++++++++++----- 3 files changed, 51 insertions(+), 25 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index e5a0d96c2..01fde3733 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -1282,7 +1282,7 @@ void UFlowAsset::TriggerCustomOutput(const FName& EventName) // it's a Root Flow, so the intention here might be to call event on the Flow Component if (UFlowComponent* FlowComponent = Cast(GetOwner())) { - FlowComponent->OnTriggerRootFlowOutputEventDispatcher(this, EventName); + FlowComponent->DispatchRootFlowCustomEvent(this, EventName); } } } diff --git a/Source/Flow/Private/FlowComponent.cpp b/Source/Flow/Private/FlowComponent.cpp index d6e773811..a762291f0 100644 --- a/Source/Flow/Private/FlowComponent.cpp +++ b/Source/Flow/Private/FlowComponent.cpp @@ -435,19 +435,6 @@ void UFlowComponent::StartRootFlow() } } -void UFlowComponent::TriggerRootFlowCustomInput(const FName& EventName) -{ - if (!RootFlow || !IsFlowNetMode(RootFlowMode)) return; - - if (UFlowSubsystem* FlowSubsystem = GetFlowSubsystem()) - { - if(auto Flow = FlowSubsystem->GetRootFlow(this); IsValid(Flow)) - { - Flow->TriggerCustomInput(EventName); - } - } -} - void UFlowComponent::FinishRootFlow(UFlowAsset* TemplateAsset, const EFlowFinishPolicy FinishPolicy) { if (UFlowSubsystem* FlowSubsystem = GetFlowSubsystem()) @@ -482,10 +469,40 @@ UFlowAsset* UFlowComponent::GetRootFlowInstance() const return nullptr; } +void UFlowComponent::TriggerRootFlowCustomInput(const FName& EventName) const +{ + if (RootFlow && IsFlowNetMode(RootFlowMode)) + { + if (const UFlowSubsystem* FlowSubsystem = GetFlowSubsystem()) + { + UFlowAsset* RootFlowInstance = FlowSubsystem->GetRootFlow(this); + if (IsValid(RootFlowInstance)) + { + RootFlowInstance->TriggerCustomInput(EventName); + } + } + } +} + +void UFlowComponent::DispatchRootFlowCustomEvent(UFlowAsset* RootFlowInstance, const FName& EventName) +{ + BP_OnRootFlowCustomEvent(RootFlowInstance, EventName); + OnRootFlowCustomEvent(RootFlowInstance, EventName); +} + +void UFlowComponent::BP_OnTriggerRootFlowOutputEvent(UFlowAsset* RootFlowInstance, const FName& EventName) +{ + BP_OnRootFlowCustomEvent(RootFlowInstance, EventName); +} + +void UFlowComponent::OnTriggerRootFlowOutputEvent(UFlowAsset* RootFlowInstance, const FName& EventName) +{ + OnRootFlowCustomEvent(RootFlowInstance, EventName); +} + void UFlowComponent::OnTriggerRootFlowOutputEventDispatcher(UFlowAsset* RootFlowInstance, const FName& EventName) { - BP_OnTriggerRootFlowOutputEvent(RootFlowInstance, EventName); - OnTriggerRootFlowOutputEvent(RootFlowInstance, EventName); + DispatchRootFlowCustomEvent(RootFlowInstance, EventName); } void UFlowComponent::SaveRootFlow(TArray& SavedFlowInstances) diff --git a/Source/Flow/Public/FlowComponent.h b/Source/Flow/Public/FlowComponent.h index 1e481442f..20de48685 100644 --- a/Source/Flow/Public/FlowComponent.h +++ b/Source/Flow/Public/FlowComponent.h @@ -199,10 +199,6 @@ class FLOW_API UFlowComponent : public UActorComponent, public IFlowOwnerInterfa UFUNCTION(BlueprintCallable, Category = "RootFlow") virtual void StartRootFlow(); - // This will trigger a specific CustomInput on this components root flow - UFUNCTION(BlueprintCallable, Category = "RootFlow") - void TriggerRootFlowCustomInput(const FName& EventName); - // This will destroy instantiated Flow Asset - created from asset assigned on this component. UFUNCTION(BlueprintCallable, Category = "RootFlow") virtual void FinishRootFlow(UFlowAsset* TemplateAsset, const EFlowFinishPolicy FinishPolicy); @@ -214,19 +210,32 @@ class FLOW_API UFlowComponent : public UActorComponent, public IFlowOwnerInterfa UFlowAsset* GetRootFlowInstance() const; ////////////////////////////////////////////////////////////////////////// -// UFlowComponent overrideable events +// Custom Input and Output events public: + // This will trigger a specific CustomInput on this components root flow + UFUNCTION(BlueprintCallable, Category = "RootFlow") + void TriggerRootFlowCustomInput(const FName& EventName) const; + // Called when a Root flow asset triggers a CustomOutput - UFUNCTION(BlueprintImplementableEvent, DisplayName = "OnTriggerRootFlowOutputEvent") - void BP_OnTriggerRootFlowOutputEvent(UFlowAsset* RootFlowInstance, const FName& EventName); + UFUNCTION(BlueprintImplementableEvent, DisplayName = "OnRootFlowCustomEvent") + void BP_OnRootFlowCustomEvent(UFlowAsset* RootFlowInstance, const FName& EventName); - virtual void OnTriggerRootFlowOutputEvent(UFlowAsset* RootFlowInstance, const FName& EventName) {} + virtual void OnRootFlowCustomEvent(UFlowAsset* RootFlowInstance, const FName& EventName) {} // UFlowAsset-only access - void OnTriggerRootFlowOutputEventDispatcher(UFlowAsset* RootFlowInstance, const FName& EventName); + void DispatchRootFlowCustomEvent(UFlowAsset* RootFlowInstance, const FName& EventName); // --- + UE_DEPRECATED(5.5, TEXT("Please use OnRootFlowCustomEvent instead.")); + void BP_OnTriggerRootFlowOutputEvent(UFlowAsset* RootFlowInstance, const FName& EventName); + + UE_DEPRECATED(5.5, TEXT("Please use OnRootFlowCustomEvent instead.")); + virtual void OnTriggerRootFlowOutputEvent(UFlowAsset* RootFlowInstance, const FName& EventName); + + UE_DEPRECATED(5.5, TEXT("Please use OnTriggerRootFlowCustomOutputDispatcher instead.")); + void OnTriggerRootFlowOutputEventDispatcher(UFlowAsset* RootFlowInstance, const FName& EventName); + ////////////////////////////////////////////////////////////////////////// // SaveGame From c17502f4f9fabc8c1c0b71282a71e3e1024bf093 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysiek=20Justy=C5=84ski?= Date: Tue, 17 Dec 2024 20:54:03 +0100 Subject: [PATCH 306/485] Update README.md --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 2f4856bdf..561aa6305 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ ## Concept -Flow plug-in for Unreal Engine is a design-agnostic event node editor. It provides a graph editor tailored for scripting flow of events in virtual worlds. It's based on a decade of experience with designing and implementing narrative in video games. All we need here is simplicity. +Flow plug-in for Unreal Engine is a design-agnostic event node editor. It provides a graph editor tailored for scripting flow of events in virtual worlds. It's based on a decade of experience with designing and implementing narrative layer in video games. All we need here is simplicity. -The aim of publishing it as open-source project is to let people tell great stories and construct immersive worlds easier. That allows us to enrich video game storytelling so we can inspire people and make our world a better place. +The aim of publishing it as an open-source project is to let people tell great stories and construct immersive worlds more easily. That allows us to enrich video game storytelling so we can inspire people and make our world a better place. ![Flow101](https://user-images.githubusercontent.com/5065057/103543817-6d924080-4e9f-11eb-87d9-15ab092c3875.png) -* A single node in this graph is a simple UObject, not a function like in blueprints. This allows you to encapsulate the entire gameplay element (logic with its data) within a single Flow Node. The idea is that your write a repeatable "event script" only once for the entire game! -* Unlike blueprints, Flow Node is async/latent by design. Active node usually subscribe to delegates, so it can react to event by triggering output pin (or whatever you choose to). +* A single node in this graph is a simple UObject, not a function like in blueprints. This allows you to encapsulate the entire gameplay element (logic with its data) within a single Flow Node. The idea is that you write a repeatable "event script" only once for the entire game! +* Unlike blueprints, Flow Node is async/latent by design. Active nodes usually subscribe to delegates, so they can react to events by triggering output pins (or whatever you choose to). * Every node defines its own set of input/output pins. It's dead simple to design the flow of the game - just connect nodes representing features. * Developers creating a Flow Node can call the execution of pins any way they need. API is extremely simple. * Editor supports convenient displaying debug information on nodes and wires while playing a game. You simply provide what kind of message would be displayed over active Flow Nodes - you can't have that with blueprint functions. -* It's up to you to add game-specific functionalities by writing your nodes and editor customizations. It's not like a marketplace providing the very specific implementation of systems. It's a convenient base for building systems tailored to fit your needs. -* Read documentation on the project wiki. I'd recommend starting from reading about [design philosophy](https://github.com/MothCocoon/FlowGraph/wiki). -* It's easy to include plugin in your own project, follow this short [Getting Started](https://github.com/MothCocoon/FlowGraph/wiki/Getting-Started) guide. +* It's up to you to add game-specific functionalities by writing your nodes and editor customizations. It's not like a marketplace providing very specific implementation of systems. It's a convenient base for building systems tailored to fit your needs. +* Please visit the wiki. I'd recommend starting by reading about [design philosophy](https://github.com/MothCocoon/FlowGraph/wiki). Also, check useful pages listed on the sidebar. +* It's easy to include a Flow plugin in your own project, follow this short [Getting Started](https://github.com/MothCocoon/FlowGraph/wiki/Getting-Started) guide. ## In-depth video presentation This 24-minute presentation breaks down the concept of the Flow Graph. Trust me, you want to understand concept properly before diving into implementation. @@ -23,7 +23,7 @@ This 24-minute presentation breaks down the concept of the Flow Graph. Trust me, I got an opportunity to work on something like the Flow Graph at Reikon Games. They shared my enthusiasm for providing the plugin as open source and as such allowed me to publish this work and keep expanding it as a personal project. Kudos, guys! Reikon badly wanted to build a better tool for implementing game flow rather than level blueprints or existing Marketplace plug-ins. I was very much interested in this since the studio was just starting with the production of a new title. And we did exactly that, created a node editor dedicated to scripting game flow. Kudos to Dariusz Murawski - a programmer who spent a few months with me to establish the working system and editor. And who had to endure my never-ending feedback and requests. -I feel it's important to mention that I didn't invent anything new here, with the Flow Graph. It's an old and proven concept. I'm just one of many developers who decided it would be crazy useful to adopt it for Unreal Engine. And this time, also to make it publicly available as an open-source project. -* Such simple graph-based tools for scripting game screenplay are utilized for a long time. Traditionally, RPG games needed such tools as there a lot of stories, quests, dialogues. +I feel it's important to mention that I didn't invent anything new here, with the Flow Graph. It's an old and proven concept. I'm just one of many developers who decided it would be crazy useful to adopt it for Unreal Engine. This time, also to make it publicly available as an open-source project. +* Such simple graph-based tools for scripting game screenplay have been utilized for a long time. Traditionally, RPG games needed such tools as there are a lot of stories, quests, and dialogues. * The best narrative toolset I had the opportunity to work with is what CD Projekt RED built for The Witcher series. Sadly, you can't download the modding toolkit for The Witcher 2 - yeah, it was publically available for some time. Still... you can watch the GDC talk by Piotr Tomsiński on [Cinematic Dialogues in The Witcher 3: Wild Hunt](https://www.youtube.com/watch?v=chf3REzAjgI) - it includes a brief presentation how Quest and Dialogue editors look like. It wouldn't be possible to create such an amazing narrative game without this kind of toolset. I did miss that so much when I moved to the Unreal Engine... * At some point I felt comfortable enough with programming editor tools so I decided to build my own version of such toolset, meant to be published as an open-source project. I am thankful to Reikon bosses they see no issues with me releasing Flow Graph, which is "obviously" similar to our internal tool in many ways. I mean, it's so simple concept of "single node representing a single game feature"... and it's based on the same UE4 node graph API. Some corporations might have an issue with that. From e31c042710bf3bae395741f25a7ae17eb010ed6c Mon Sep 17 00:00:00 2001 From: Ryan DowlingSoka Date: Thu, 19 Dec 2024 01:45:39 -0800 Subject: [PATCH 307/485] Fix compile errors against more strict -Wmissing-declarations (#251) * Remove extra semi-colons triggering -Wmissing-declarations compiler error. `FlowComponent.h(230,71): error: declaration does not declare anything [-Werror,-Wmissing-declarations] UE_DEPRECATED(5.5, TEXT("Please use OnRootFlowCustomEvent instead."));` * Remove TEXT macro from UE_DEPRECATED usage it doesn't need. --- Source/Flow/Public/FlowComponent.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Flow/Public/FlowComponent.h b/Source/Flow/Public/FlowComponent.h index 20de48685..cf2d00b13 100644 --- a/Source/Flow/Public/FlowComponent.h +++ b/Source/Flow/Public/FlowComponent.h @@ -227,13 +227,13 @@ class FLOW_API UFlowComponent : public UActorComponent, public IFlowOwnerInterfa void DispatchRootFlowCustomEvent(UFlowAsset* RootFlowInstance, const FName& EventName); // --- - UE_DEPRECATED(5.5, TEXT("Please use OnRootFlowCustomEvent instead.")); + UE_DEPRECATED(5.5, "Please use OnRootFlowCustomEvent instead.") void BP_OnTriggerRootFlowOutputEvent(UFlowAsset* RootFlowInstance, const FName& EventName); - UE_DEPRECATED(5.5, TEXT("Please use OnRootFlowCustomEvent instead.")); + UE_DEPRECATED(5.5, "Please use OnRootFlowCustomEvent instead.") virtual void OnTriggerRootFlowOutputEvent(UFlowAsset* RootFlowInstance, const FName& EventName); - UE_DEPRECATED(5.5, TEXT("Please use OnTriggerRootFlowCustomOutputDispatcher instead.")); + UE_DEPRECATED(5.5, "Please use OnTriggerRootFlowCustomOutputDispatcher instead.") void OnTriggerRootFlowOutputEventDispatcher(UFlowAsset* RootFlowInstance, const FName& EventName); ////////////////////////////////////////////////////////////////////////// From 8e41a37c04b29406126038ba800caf2a758f107c Mon Sep 17 00:00:00 2001 From: Ryan DowlingSoka Date: Thu, 19 Dec 2024 12:03:21 -0800 Subject: [PATCH 308/485] Enable plugin requirement EngineAssetDefinitions it relies on (#252) ```Warning: Plugin 'Flow' does not list plugin 'EngineAssetDefinitions' as a dependency, but module 'FlowEditor' depends on module 'EngineAssetDefinitions'.``` --- Flow.uplugin | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Flow.uplugin b/Flow.uplugin index a914fff06..8214ef6ea 100644 --- a/Flow.uplugin +++ b/Flow.uplugin @@ -34,9 +34,13 @@ "Name": "EditorScriptingUtilities", "Enabled": true }, + { + "Name": "EngineAssetDefinitions", + "Enabled": true + }, { "Name": "StructUtils", "Enabled": true } ] -} \ No newline at end of file +} From 1533965812f2c4202f4c6495d5af411a9ff5e55b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Thu, 19 Dec 2024 21:58:57 +0100 Subject: [PATCH 309/485] fixed the issue where programmatic graph generation would create Start Node using improper UFlowGraphNode class --- Source/FlowEditor/Private/Graph/FlowGraph.cpp | 19 ++++++++----------- .../Private/Graph/FlowGraphSchema.cpp | 6 +++--- .../Private/Graph/FlowGraphSchema_Actions.cpp | 10 +++++----- Source/FlowEditor/Public/Graph/FlowGraph.h | 3 +-- .../FlowEditor/Public/Graph/FlowGraphSchema.h | 2 +- 5 files changed, 18 insertions(+), 22 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraph.cpp b/Source/FlowEditor/Private/Graph/FlowGraph.cpp index c9a7b724b..4177d8fe9 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraph.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraph.cpp @@ -38,21 +38,18 @@ UFlowGraph::UFlowGraph(const FObjectInitializer& ObjectInitializer) } } -UEdGraph* UFlowGraph::CreateGraph(UFlowAsset* InFlowAsset) +void UFlowGraph::CreateGraph(UFlowAsset* InFlowAsset) { - return CreateGraph(InFlowAsset, UFlowGraphSchema::StaticClass()); -} - -UEdGraph* UFlowGraph::CreateGraph(UFlowAsset* InFlowAsset, TSubclassOf FlowSchema) -{ - check(FlowSchema); - UEdGraph* NewGraph = CastChecked(FBlueprintEditorUtils::CreateNewGraph(InFlowAsset, NAME_None, StaticClass(), FlowSchema)); + UFlowGraph* NewGraph = CastChecked(FBlueprintEditorUtils::CreateNewGraph(InFlowAsset, NAME_None, StaticClass(), UFlowGraphSchema::StaticClass())); NewGraph->bAllowDeletion = false; - InFlowAsset->FlowGraph = NewGraph; - NewGraph->GetSchema()->CreateDefaultNodesForGraph(*NewGraph); + // Ensure we mapped relation between UFlowNode and UFlowGraphNode classes + // Otherwise generating graph wouldn't assign proper UFlowGraphNode class to default nodes generated below + // Issue only occurred if somebody would generate graph programatically without opening Flow Asset editor at least once + UFlowGraphSchema::GatherNodes(); - return NewGraph; + InFlowAsset->FlowGraph = NewGraph; + InFlowAsset->FlowGraph->GetSchema()->CreateDefaultNodesForGraph(*InFlowAsset->FlowGraph); } void UFlowGraph::RefreshGraph() diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index b519a73d6..0ac4291ad 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -251,7 +251,7 @@ void UFlowGraphSchema::CreateDefaultNodesForGraph(UEdGraph& Graph) const FVector2D NodeOffset = FVector2D::ZeroVector; // Start node - CreateDefaultNode(Graph, AssetClassDefaults, UFlowNode_Start::StaticClass(), NodeOffset, AssetClassDefaults->bStartNodePlacedAsGhostNode); + CreateDefaultNode(Graph, UFlowNode_Start::StaticClass(), NodeOffset, AssetClassDefaults->bStartNodePlacedAsGhostNode); // Add default nodes for all the CustomInputs if (IsValid(AssetClassDefaults)) @@ -259,7 +259,7 @@ void UFlowGraphSchema::CreateDefaultNodesForGraph(UEdGraph& Graph) const for (const FName& CustomInputName : AssetClassDefaults->CustomInputs) { NodeOffset += NodeOffsetIncrement; - const UFlowGraphNode* NewFlowGraphNode = CreateDefaultNode(Graph, AssetClassDefaults, UFlowNode_CustomInput::StaticClass(), NodeOffset, true); + const UFlowGraphNode* NewFlowGraphNode = CreateDefaultNode(Graph, UFlowNode_CustomInput::StaticClass(), NodeOffset, true); UFlowNode_CustomInput* CustomInputNode = CastChecked(NewFlowGraphNode->GetFlowNodeBase()); CustomInputNode->SetEventName(CustomInputName); @@ -270,7 +270,7 @@ void UFlowGraphSchema::CreateDefaultNodesForGraph(UEdGraph& Graph) const FlowAsset->HarvestNodeConnections(); } -UFlowGraphNode* UFlowGraphSchema::CreateDefaultNode(UEdGraph& Graph, const UFlowAsset* AssetClassDefaults, const TSubclassOf& NodeClass, const FVector2D& Offset, const bool bPlacedAsGhostNode) +UFlowGraphNode* UFlowGraphSchema::CreateDefaultNode(UEdGraph& Graph, const TSubclassOf& NodeClass, const FVector2D& Offset, const bool bPlacedAsGhostNode) { UFlowGraphNode* NewGraphNode = FFlowGraphSchemaAction_NewNode::CreateNode(&Graph, nullptr, NodeClass, Offset); SetNodeMetaData(NewGraphNode, FNodeMetadata::DefaultGraphNode); diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp index cceb82798..614f397e3 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp @@ -56,8 +56,8 @@ UFlowGraphNode* FFlowGraphSchemaAction_NewNode::CreateNode(UEdGraph* ParentGraph FlowAsset->Modify(); // create new Flow Graph node - TSubclassOf FlowNodeBaseClass = const_cast(NodeClass); - TSubclassOf GraphNodeClass = UFlowGraphSchema::GetAssignedGraphNodeClass(FlowNodeBaseClass); + const TSubclassOf FlowNodeBaseClass = const_cast(NodeClass); + const TSubclassOf GraphNodeClass = UFlowGraphSchema::GetAssignedGraphNodeClass(FlowNodeBaseClass); UFlowGraphNode* NewGraphNode = NewObject(ParentGraph, GraphNodeClass, NAME_None, RF_Transactional); // register to the graph @@ -206,12 +206,12 @@ UFlowGraphNode* FFlowGraphSchemaAction_NewNode::ImportNode(UEdGraph* ParentGraph UEdGraphNode* FFlowSchemaAction_NewSubNode::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode) { ParentNode->AddSubNode(NodeTemplate, ParentGraph); - return NULL; + return nullptr; } UEdGraphNode* FFlowSchemaAction_NewSubNode::PerformAction(class UEdGraph* ParentGraph, TArray& FromPins, const FVector2D Location, bool bSelectNewNode) { - return PerformAction(ParentGraph, NULL, Location, bSelectNewNode); + return PerformAction(ParentGraph, nullptr, Location, bSelectNewNode); } void FFlowSchemaAction_NewSubNode::AddReferencedObjects(FReferenceCollector& Collector) @@ -255,7 +255,7 @@ UFlowGraphNode* FFlowSchemaAction_NewSubNode::RecreateNode(UEdGraph* ParentGraph TSharedPtr FFlowSchemaAction_NewSubNode::AddNewSubNodeAction(FGraphActionListBuilderBase& ContextMenuBuilder, const FText& Category, const FText& MenuDesc, const FText& Tooltip) { - TSharedPtr NewAction = TSharedPtr(new FFlowSchemaAction_NewSubNode(Category, MenuDesc, Tooltip, 0)); + TSharedPtr NewAction = MakeShared(Category, MenuDesc, Tooltip, 0); ContextMenuBuilder.AddAction(NewAction); return NewAction; } diff --git a/Source/FlowEditor/Public/Graph/FlowGraph.h b/Source/FlowEditor/Public/Graph/FlowGraph.h index 6e1d5c310..00461080f 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraph.h +++ b/Source/FlowEditor/Public/Graph/FlowGraph.h @@ -39,8 +39,7 @@ class FLOWEDITOR_API UFlowGraph : public UEdGraph uint32 bIsLoadingGraph : 1; public: - static UEdGraph* CreateGraph(UFlowAsset* InFlowAsset); - static UEdGraph* CreateGraph(UFlowAsset* InFlowAsset, TSubclassOf FlowSchema); + static void CreateGraph(UFlowAsset* InFlowAsset); void RefreshGraph(); protected: diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h index c3706f2fe..a85dd6cd9 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h @@ -106,7 +106,7 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema static bool IsPIESimulating(); protected: - static UFlowGraphNode* CreateDefaultNode(UEdGraph& Graph, const UFlowAsset* AssetClassDefaults, const TSubclassOf& NodeClass, const FVector2D& Offset, bool bPlacedAsGhostNode); + static UFlowGraphNode* CreateDefaultNode(UEdGraph& Graph, const TSubclassOf& NodeClass, const FVector2D& Offset, bool bPlacedAsGhostNode); static bool ArePinCategoriesEffectivelyMatching(const FName& InputPinCategory, const FName& OutputPinCategory, bool bAllowImplicitCasts = true); From c9f5b1a3ed9bd3b71d6fdb12ea5d540c547e10b7 Mon Sep 17 00:00:00 2001 From: Kyle Wilcox Date: Sun, 5 Jan 2025 10:26:26 -0700 Subject: [PATCH 310/485] Graph refresh system cleanup (#255) * Clean up ReconstructNode/RefreshContextPins methods * Graph editor improvements (stop refreshing whole graph unless needed) * Major redesign of graph reconstruction/refreshing code BREAKING CHANGE: RefreshContextPins() has been made protected. Replace any public usage of RefreshContextPins() with ReconstructNode() instead. This commit improves editor performance of the graph editor, and fixes some minor bugs with the graph editor. * Code cleanup * Code cleanup * Code cleanup * Code cleanup, fixed HavePinsChanged() to ignore invalid (NAME_None) pins * Made UFlowGraphNode::ReconstructNode() always rebuild the slate widget Even if there are no updates required to the graph node; this ensures that the graph editor's 'Refresh' button still actually rebuilds the visual graph. * Fix typo * Clean up ReconstructNode/RefreshContextPins methods Graph editor improvements (stop refreshing whole graph unless needed) Major redesign of graph reconstruction/refreshing code BREAKING CHANGE: RefreshContextPins() has been made protected. Replace any public usage of RefreshContextPins() with ReconstructNode() instead. This commit improves editor performance of the graph editor, and fixes some minor bugs with the graph editor. Code cleanup Code cleanup Code cleanup Code cleanup, fixed HavePinsChanged() to ignore invalid (NAME_None) pins Made UFlowGraphNode::ReconstructNode() always rebuild the slate widget Even if there are no updates required to the graph node; this ensures that the graph editor's 'Refresh' button still actually rebuilds the visual graph. Fix typo --- Source/Flow/Private/AddOns/FlowNodeAddOn.cpp | 29 ++- Source/Flow/Private/FlowAsset.cpp | 49 +++-- Source/Flow/Public/AddOns/FlowNodeAddOn.h | 4 + Source/Flow/Public/FlowAsset.h | 4 +- .../Private/Asset/FlowAssetEditor.cpp | 4 - .../FlowEditor/Private/FlowEditorCommands.cpp | 2 +- .../Private/Graph/FlowGraphEditor.cpp | 13 +- .../Private/Graph/FlowGraphSchema.cpp | 45 ++-- .../Private/Graph/FlowGraphSchema_Actions.cpp | 4 +- .../Private/Graph/Nodes/FlowGraphNode.cpp | 203 ++++++++++++------ .../Private/Graph/Widgets/SFlowGraphNode.cpp | 7 + Source/FlowEditor/Public/FlowEditorCommands.h | 2 +- .../FlowEditor/Public/Graph/FlowGraphEditor.h | 4 +- .../FlowEditor/Public/Graph/FlowGraphSchema.h | 2 + .../Public/Graph/Nodes/FlowGraphNode.h | 15 +- .../Public/Graph/Widgets/SFlowGraphNode.h | 2 + 16 files changed, 267 insertions(+), 122 deletions(-) diff --git a/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp b/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp index c627bdb18..996f2745a 100644 --- a/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp +++ b/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp @@ -1,6 +1,8 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "AddOns/FlowNodeAddOn.h" + +#include "FlowLogChannels.h" #include "Nodes/FlowNode.h" #include "Misc/RuntimeErrors.h" @@ -105,17 +107,34 @@ void UFlowNodeAddOn::CacheFlowNode() } #if WITH_EDITOR -TArray UFlowNodeAddOn::GetContextInputs() const +TArray UFlowNodeAddOn::GetPinsForContext(const TArray& Context) const { TArray ContextPins = Super::GetContextInputs(); - ContextPins.Append(InputPins); + + ContextPins.Reserve(ContextPins.Num() + Context.Num()); + + for (const FFlowPin& InputPin : Context) + { + if (InputPin.IsValid()) + { + ContextPins.Add(InputPin); + } + else + { + UE_LOG(LogFlow, Warning, TEXT("Addon %s has invalid pins (name: None), you should clean these up."), *GetName()); + } + } + return ContextPins; } +TArray UFlowNodeAddOn::GetContextInputs() const +{ + return GetPinsForContext(InputPins); +} + TArray UFlowNodeAddOn::GetContextOutputs() const { - TArray ContextPins = Super::GetContextOutputs(); - ContextPins.Append(OutputPins); - return ContextPins; + return GetPinsForContext(OutputPins); } #endif // WITH_EDITOR diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 01fde3733..42af20e06 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -331,30 +331,45 @@ void UFlowAsset::UnregisterNode(const FGuid& NodeGuid) MarkPackageDirty(); } -void UFlowAsset::HarvestNodeConnections() +void UFlowAsset::HarvestNodeConnections(UFlowNode* TargetNode) { - TMap Connections; - bool bGraphDirty = false; + TArray TargetNodes; - // last moment to remove invalid nodes - for (auto NodeIt = Nodes.CreateIterator(); NodeIt; ++NodeIt) + if (IsValid(TargetNode)) { - const TPair& Pair = *NodeIt; - if (Pair.Value == nullptr) + TargetNodes.Reserve(1); + TargetNodes.Add(TargetNode); + } + else + { + TargetNodes.Reserve(Nodes.Num()); + for (const TPair& Pair : ObjectPtrDecay(Nodes)) + { + TargetNodes.Add(Pair.Value); + } + } + + // Remove any invalid nodes + for (auto NodeIt = TargetNodes.CreateIterator(); NodeIt; ++NodeIt) + { + if (*NodeIt == nullptr) { NodeIt.RemoveCurrent(); - bGraphDirty = true; + Modify(); } } - for (const TPair& Pair : ObjectPtrDecay(Nodes)) + bool bAnyNodeDirty = false; + + for (UFlowNode* FlowNode : TargetNodes) { - UFlowNode* FlowNode = Pair.Value; + bool bNodeDirty = false; + TMap FoundConnections; const TArray& GraphNodePins = FlowNode->GetGraphNode()->Pins; for (const UEdGraphPin* ThisPin : GraphNodePins) - { + { const bool bIsExecPin = FFlowPin::IsExecPinCategory(ThisPin->PinType.PinCategory); const bool bIsDataPin = FFlowPin::IsDataPinCategory(ThisPin->PinType.PinCategory); const bool bIsOutputPin = (ThisPin->Direction == EGPD_Output); @@ -383,13 +398,13 @@ void UFlowAsset::HarvestNodeConnections() // This check exists to ensure that we don't mark graph dirty, if none of connections changed // Optimization: we need check it only until the first node would be marked dirty, as this already marks Flow Asset package dirty - if (bGraphDirty == false) + if (bAnyNodeDirty == false) { const TMap& OldConnections = FlowNode->Connections; if (FoundConnections.Num() != OldConnections.Num()) { - bGraphDirty = true; + bNodeDirty = true; } else { @@ -399,20 +414,20 @@ void UFlowAsset::HarvestNodeConnections() { if (FoundConnection.Value != *OldConnection) { - bGraphDirty = true; + bNodeDirty = true; break; } } else { - bGraphDirty = true; + bNodeDirty = true; break; } } } } - if (bGraphDirty) + if (bNodeDirty || bAnyNodeDirty) { FlowNode->SetFlags(RF_Transactional); FlowNode->Modify(); @@ -422,7 +437,7 @@ void UFlowAsset::HarvestNodeConnections() } } - // NOTE (gtaylor) @mothdoctor, do we need to do anything with bGraphDirty here? + // NOTE (gtaylor) @mothdoctor, do we need to do anything with bGraphDirty [renamed by @HomerJohnston to bAnyNodeDirty] here? // It's scope seems like we wanted to do something at this point. } diff --git a/Source/Flow/Public/AddOns/FlowNodeAddOn.h b/Source/Flow/Public/AddOns/FlowNodeAddOn.h index e06a65882..e066a2c4f 100644 --- a/Source/Flow/Public/AddOns/FlowNodeAddOn.h +++ b/Source/Flow/Public/AddOns/FlowNodeAddOn.h @@ -78,4 +78,8 @@ class UFlowNodeAddOn : public UFlowNodeBase protected: void CacheFlowNode(); + +#if WITH_EDITOR + TArray GetPinsForContext(const TArray& Context) const; +#endif }; diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 628300438..b66bcffd9 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -190,8 +190,8 @@ class FLOW_API UFlowAsset : public UObject void RegisterNode(const FGuid& NewGuid, UFlowNode* NewNode); void UnregisterNode(const FGuid& NodeGuid); - // Processes all nodes and creates map of all pin connections - void HarvestNodeConnections(); + // Processes nodes and updates pin connections from the graph to the UFlowNode (processes all nodes in the graph if passed nullptr) + void HarvestNodeConnections(UFlowNode* TargetNode = nullptr); // Updates the auto-generated pins and bindings for a given FlowNode, // returns true if any changes were made. diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index 1ef73f9b7..899814a02 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -77,10 +77,6 @@ void FFlowAssetEditor::HandleUndoTransaction() void FFlowAssetEditor::NotifyPostChange(const FPropertyChangedEvent& PropertyChangedEvent, FProperty* PropertyThatChanged) { - if (PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive) - { - GraphEditor->NotifyGraphChanged(); - } } FName FFlowAssetEditor::GetToolkitFName() const diff --git a/Source/FlowEditor/Private/FlowEditorCommands.cpp b/Source/FlowEditor/Private/FlowEditorCommands.cpp index f57ca6561..8e681edec 100644 --- a/Source/FlowEditor/Private/FlowEditorCommands.cpp +++ b/Source/FlowEditor/Private/FlowEditorCommands.cpp @@ -34,7 +34,7 @@ FFlowGraphCommands::FFlowGraphCommands() void FFlowGraphCommands::RegisterCommands() { - UI_COMMAND(RefreshContextPins, "Refresh context pins", "Refresh pins generated from the context asset", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(ReconstructNode, "Reconstruct node", "Reconstruct this node", EUserInterfaceActionType::Button, FInputChord()); UI_COMMAND(AddInput, "Add Input", "Adds an input to the node", EUserInterfaceActionType::Button, FInputChord()); UI_COMMAND(AddOutput, "Add Output", "Adds an output to the node", EUserInterfaceActionType::Button, FInputChord()); diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index 0abb5bb09..d2bbd96c1 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -11,6 +11,7 @@ #include "Nodes/Route/FlowNode_SubGraph.h" #include "EdGraphUtilities.h" +#include "GraphEditAction.h" #include "Framework/Application/SlateApplication.h" #include "Framework/Commands/GenericCommands.h" #include "GraphEditorActions.h" @@ -104,9 +105,9 @@ void SFlowGraphEditor::BindGraphCommands() FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanDuplicateNodes)); // Pin commands - CommandList->MapAction(FlowGraphCommands.RefreshContextPins, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::RefreshContextPins), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanRefreshContextPins)); + CommandList->MapAction(FlowGraphCommands.ReconstructNode, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::ReconstructNode), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanReconstructNode)); CommandList->MapAction(FlowGraphCommands.AddInput, FExecuteAction::CreateSP(this, &SFlowGraphEditor::AddInput), @@ -985,15 +986,15 @@ void SFlowGraphEditor::OnNodeTitleCommitted(const FText& NewText, ETextCommit::T } } -void SFlowGraphEditor::RefreshContextPins() const +void SFlowGraphEditor::ReconstructNode() const { for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - SelectedNode->RefreshContextPins(true); + SelectedNode->ReconstructNode(); } } -bool SFlowGraphEditor::CanRefreshContextPins() const +bool SFlowGraphEditor::CanReconstructNode() const { if (CanEdit() && GetSelectedFlowNodes().Num() == 1) { diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index 0ac4291ad..8018bf54d 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -570,11 +570,17 @@ const FPinConnectionResponse UFlowGraphSchema::CanMergeNodes(const UEdGraphNode* bool UFlowGraphSchema::TryCreateConnection(UEdGraphPin* PinA, UEdGraphPin* PinB) const { - const bool bModified = UEdGraphSchema::TryCreateConnection(PinA, PinB); - + bool bModified = UEdGraphSchema::TryCreateConnection(PinA, PinB); + if (bModified) { - PinA->GetOwningNode()->GetGraph()->NotifyGraphChanged(); + UFlowGraphNode* FlowGraphNodeA = Cast(PinA->GetOwningNode()); + UFlowGraphNode* FlowGraphNodeB = Cast(PinB->GetOwningNode()); + + UEdGraph* EdGraph = FlowGraphNodeA->GetGraph(); + + EdGraph->NotifyNodeChanged(FlowGraphNodeA); + EdGraph->NotifyNodeChanged(FlowGraphNodeB); } return bModified; @@ -751,40 +757,47 @@ bool UFlowGraphSchema::IsTitleBarPin(const UEdGraphPin& Pin) const void UFlowGraphSchema::BreakNodeLinks(UEdGraphNode& TargetNode) const { Super::BreakNodeLinks(TargetNode); - - UEdGraph* EdGraph = TargetNode.GetGraph(); - if (IsValid(EdGraph)) - { - EdGraph->NotifyGraphChanged(); - } } void UFlowGraphSchema::BreakPinLinks(UEdGraphPin& TargetPin, bool bSendsNodeNotification) const { const FScopedTransaction Transaction(LOCTEXT("GraphEd_BreakPinLinks", "Break Pin Links")); - Super::BreakPinLinks(TargetPin, bSendsNodeNotification); + TArray CachedLinkedTo = TargetPin.LinkedTo; - // Cache the owning node because calling Super::BreakPinLinks might release the pointer - // to the pin owning node as a side effect of notify graph changed events. UFlowGraphNode* OwningFlowGraphNode = Cast(TargetPin.GetOwningNodeUnchecked()); + UEdGraph* EdGraph = (OwningFlowGraphNode) ? OwningFlowGraphNode->GetGraph() : nullptr; - // NOTE (gtaylor) It is possible for OwningFlowGraphNode to be null if the TargetPin has been orphaned. + Super::BreakPinLinks(TargetPin, bSendsNodeNotification); if (TargetPin.bOrphanedPin) { if (OwningFlowGraphNode) { - // this calls NotifyGraphChanged() + // this calls NotifyNodeChanged() OwningFlowGraphNode->RemoveOrphanedPin(&TargetPin); } } else if (bSendsNodeNotification) { - UEdGraph* EdGraph = (OwningFlowGraphNode) ? OwningFlowGraphNode->GetGraph() : nullptr; if (IsValid(EdGraph)) { - EdGraph->NotifyGraphChanged(); + EdGraph->NotifyNodeChanged(OwningFlowGraphNode); + } + } + + for (UEdGraphPin* OtherPin : CachedLinkedTo) + { + UFlowGraphNode* OtherOwningFlowGraphNode = Cast(OtherPin->GetOwningNodeUnchecked()); + + if (OtherPin->bOrphanedPin) + { + // this calls NotifyNodeChanged() + OtherOwningFlowGraphNode->RemoveOrphanedPin(OtherPin); + } + else if (bSendsNodeNotification) + { + EdGraph->NotifyNodeChanged(OtherOwningFlowGraphNode); } } } diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp index 614f397e3..38e05a57c 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp @@ -69,7 +69,7 @@ UFlowGraphNode* FFlowGraphSchemaAction_NewNode::CreateNode(UEdGraph* ParentGraph NewGraphNode->SetNodeTemplate(FlowNode); // create pins and connections - NewGraphNode->AllocateDefaultPins(); + NewGraphNode->ReconstructNode(); NewGraphNode->AutowireNewNode(FromPin); // set position @@ -78,7 +78,7 @@ UFlowGraphNode* FFlowGraphSchemaAction_NewNode::CreateNode(UEdGraph* ParentGraph // call notifies NewGraphNode->PostPlacedNewNode(); - ParentGraph->NotifyGraphChanged(); + ParentGraph->NotifyNodeChanged(NewGraphNode); FlowAsset->PostEditChange(); diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 7bdd6bc14..a68216d27 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -17,8 +17,8 @@ #include "BlueprintNodeHelpers.h" #include "Developer/ToolMenus/Public/ToolMenus.h" #include "DiffResults.h" -#include "EdGraphSchema_K2.h" #include "Editor.h" +#include "FlowLogChannels.h" #include "Framework/Commands/GenericCommands.h" #include "GraphDiffControl.h" #include "GraphEditorActions.h" @@ -28,6 +28,7 @@ #include "SourceCodeNavigation.h" #include "Textures/SlateIcon.h" #include "ToolMenuSection.h" +#include "Editor/Transactor.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraphNode) @@ -80,6 +81,8 @@ void UFlowGraphNode::PostLoad() NodeInstance->FixNode(this); // fix already created nodes SubscribeToExternalChanges(); } + + RebuildPinArraysOnLoad(); } void UFlowGraphNode::PostDuplicate(bool bDuplicateForPIE) @@ -194,12 +197,13 @@ void UFlowGraphNode::OnExternalChange() bNeedsFullReconstruction = true; ReconstructNode(); - GetGraph()->NotifyGraphChanged(); + + GetGraph()->NotifyNodeChanged(this); } void UFlowGraphNode::OnGraphRefresh() { - RefreshContextPins(true); + ReconstructNode(); } bool UFlowGraphNode::CanCreateUnderSpecifiedSchema(const UEdGraphSchema* Schema) const @@ -280,32 +284,17 @@ void UFlowGraphNode::InsertNewNode(UEdGraphPin* FromPin, UEdGraphPin* NewLinkPin void UFlowGraphNode::ReconstructNode() { - if (const UFlowGraph* FlowGraph = GetFlowGraph()) - { - // If the graph is locked, we shouldn't reconstruct nodes - // (all nodes will all be reconstructed when the graph is unlocked) - - if (FlowGraph->IsLocked()) - { - return; - } - } - - if (bIsReconstructingNode) + if (!ShouldReconstructNode()) { + // This ensures the graph editor 'Refresh' button still rebuilds all of the graph widgets even if the FlowGraphNode has nothing to update. + (void)OnReconstructNodeCompleted.ExecuteIfBound(); return; } - + bIsReconstructingNode = true; - - // Store old pins + TArray OldPins(Pins); - - // Reset pin arrays - Pins.Reset(); - InputPins.Reset(); - OutputPins.Reset(); - + // Harvest the auto-generated pins before refreshing context pins if (UFlowNode* FlowNode = Cast(NodeInstance)) { @@ -314,11 +303,12 @@ void UFlowGraphNode::ReconstructNode() FlowAsset->TryUpdateManagedFlowPinsForNode(*FlowNode); } } - - // Recreate pins - constexpr bool bReconstructNode = false; - RefreshContextPins(bReconstructNode); - + + Pins.Reset(); + InputPins.Reset(); + OutputPins.Reset(); + + RefreshContextPins(); AllocateDefaultPins(); RewireOldPinsToNewPins(OldPins); @@ -329,9 +319,11 @@ void UFlowGraphNode::ReconstructNode() OldPin->BreakAllPinLinks(); DestroyPin(OldPin); } - + bNeedsFullReconstruction = false; bIsReconstructingNode = false; + + (void)OnReconstructNodeCompleted.ExecuteIfBound(); } void UFlowGraphNode::AllocateDefaultPins() @@ -422,6 +414,20 @@ void UFlowGraphNode::RewireOldPinsToNewPins(TArray& InOldPins) if (OrphanedPin->ParentPin == nullptr) { Pins.Add(OrphanedPin); + + switch (OrphanedPin->Direction) + { + case EGPD_Input: + { + InputPins.Add(OrphanedPin); + break; + } + case EGPD_Output: + { + OutputPins.Add(OrphanedPin); + break; + } + } } } } @@ -506,7 +512,7 @@ void UFlowGraphNode::GetNodeContextMenuActions(class UToolMenu* Menu, class UGra if (SupportsContextPins()) { - Section.AddMenuEntry(FlowGraphCommands.RefreshContextPins); + Section.AddMenuEntry(FlowGraphCommands.ReconstructNode); } if (CanUserAddInput()) @@ -883,7 +889,8 @@ void UFlowGraphNode::RemoveOrphanedPin(UEdGraphPin* Pin) Pins.Remove(Pin); ReconstructNode(); - GetGraph()->NotifyGraphChanged(); + + GetGraph()->NotifyNodeChanged(this); } bool UFlowGraphNode::SupportsContextPins() const @@ -962,7 +969,7 @@ void UFlowGraphNode::AddInstancePin(const EEdGraphPinDirection Direction, const CreateOutputPin(PinName, FlowNode->InputPins.Num() + NumberedPinsAmount); } - GetGraph()->NotifyGraphChanged(); + GetGraph()->NotifyNodeChanged(this); } void UFlowGraphNode::RemoveInstancePin(UEdGraphPin* Pin) @@ -997,11 +1004,16 @@ void UFlowGraphNode::RemoveInstancePin(UEdGraphPin* Pin) } ReconstructNode(); - GetGraph()->NotifyGraphChanged(); + GetGraph()->NotifyNodeChanged(this); } -void UFlowGraphNode::RefreshContextPins(const bool bReconstructNode) +void UFlowGraphNode::RefreshContextPins() { + if (GIsTransacting) + { + return; + } + UFlowNode* FlowNode = Cast(NodeInstance); if (!IsValid(FlowNode)) { @@ -1040,30 +1052,23 @@ void UFlowGraphNode::RefreshContextPins(const bool bReconstructNode) // Skip the rest if the node went from no ContextPins to no ContextPins const bool bMaintainedNoContextPins = !bPrevHasContextPins && !bHasContextPins; - if (bMaintainedNoContextPins || !HavePinsChanged()) + if (bMaintainedNoContextPins) { // We don't have contextual pins to account for; or the contextual pins have not changed. We can skip now. return; } const FScopedTransaction Transaction(LOCTEXT("RefreshContextPins", "Refresh Context Pins")); + Modify(); const UFlowNode* NodeDefaults = FlowNode->GetClass()->GetDefaultObject(); - // recreate inputs FlowNode->InputPins = NodeDefaults->InputPins; FlowNode->AddInputPins(ContextInputs); - // recreate outputs FlowNode->OutputPins = NodeDefaults->OutputPins; FlowNode->AddOutputPins(ContextOutputs); - - if (bReconstructNode) - { - ReconstructNode(); - GetGraph()->NotifyGraphChanged(); - } } void UFlowGraphNode::GetPinHoverText(const UEdGraphPin& Pin, FString& HoverTextOut) const @@ -1283,7 +1288,7 @@ void UFlowGraphNode::LogError(const FString& MessageToLog, const UFlowNodeBase* } } -bool UFlowGraphNode::HavePinsChanged() +bool UFlowGraphNode::HavePinsChanged() const { const UFlowNode* FlowNodeInstance = Cast(NodeInstance); if (!IsValid(FlowNodeInstance)) @@ -1292,33 +1297,48 @@ bool UFlowGraphNode::HavePinsChanged() return true; } - // Get the pins that are part of the node itself. We use the CDO because it inherently knows about the built-in - // pins for this node. + // Get all pins of the FlowNode itself. We use the CDO because it inherently knows about the built-in pins for this node. const UFlowNode* FlowNodeCDO = FlowNodeInstance->GetClass()->GetDefaultObject(); check(IsValid(FlowNodeCDO)); - TArray AllFlowPins = FlowNodeCDO->GetInputPins(); - AllFlowPins.Append(FlowNodeCDO->GetOutputPins()); + TArray AllFlowNodePins = FlowNodeCDO->GetInputPins(); + AllFlowNodePins.Append(FlowNodeCDO->GetOutputPins()); - // Get the contextual pins for the underlying flow node instance. - AllFlowPins.Append(FlowNodeInstance->GetContextInputs()); - AllFlowPins.Append(FlowNodeInstance->GetContextOutputs()); + AllFlowNodePins.Append(FlowNodeInstance->GetContextInputs()); + AllFlowNodePins.Append(FlowNodeInstance->GetContextOutputs()); - if (Pins.Num() != AllFlowPins.Num()) + // Invalid FlowNode pins need to be stripped from the comparison + for (int i = AllFlowNodePins.Num() - 1; i >= 0; --i) { - // There is a different number of EdGraphPins and Flow Node pins; something changed. - return true; + if (!AllFlowNodePins[i].IsValid()) + { + AllFlowNodePins.RemoveAtSwap(i, EAllowShrinking::No); + } + } + + // Get the current FlowGraphNode pins list - orphaned pins need to be stripped from the current pins. + TArray AllGraphNodePins = Pins; + for (int i = AllGraphNodePins.Num() - 1; i >= 0; --i) + { + if (AllGraphNodePins[i]->bOrphanedPin) + { + AllGraphNodePins.RemoveAtSwap(i, EAllowShrinking::No); + } } - TArray PinNames; - for (const UEdGraphPin* Pin : Pins) + // Compare valid pin counts + if (AllGraphNodePins.Num() != AllFlowNodePins.Num()) { - PinNames.Add(Pin->PinName); + return true; } - for (const FFlowPin& Pin : AllFlowPins) + // Compare valid pin names + for (const FFlowPin& FlowNodePin : AllFlowNodePins) { - if (!PinNames.Contains(Pin.PinName)) + if (!AllGraphNodePins.ContainsByPredicate([&FlowNodePin](UEdGraphPin* GraphNodePin) + { + return GraphNodePin->PinName == FlowNodePin.PinName; + })) { // Could not match the pin from the flow node with any of the EdPins array. // we have a mismatch between the ed graph pins and the flow node, something changed. @@ -1413,7 +1433,11 @@ void UFlowGraphNode::NodeConnectionListChanged() { Super::NodeConnectionListChanged(); - GetFlowGraph()->UpdateAsset(); + UFlowGraph* Graph = Cast(GetGraph()); + + Graph->GetFlowAsset()->HarvestNodeConnections(Cast(GetFlowNodeBase())); + + GetFlowGraph()->NotifyNodeChanged(this); } FString UFlowGraphNode::GetPropertyNameAndValueForDiff(const FProperty* Prop, const uint8* PropertyAddr) const @@ -1477,8 +1501,7 @@ void UFlowGraphNode::RebuildRuntimeAddOnsFromEditorSubNodes() // Reconstruct the context pins for all flow nodes after their AddOns have been processed if (IsValid(NodeInstance) && NodeInstance->IsA()) { - constexpr bool bReconstructNode = true; - RefreshContextPins(bReconstructNode); + ReconstructNode(); } } @@ -1750,6 +1773,36 @@ void UFlowGraphNode::ValidateGraphNode(FFlowMessageLog& MessageLog) const } } +bool UFlowGraphNode::ShouldReconstructNode() const +{ + if (GIsTransacting) + { + return false; + } + + // If the graph is locked, we shouldn't reconstruct nodes + // (all nodes will all be reconstructed when the graph is unlocked) + if (const UFlowGraph* FlowGraph = GetFlowGraph()) + { + if (FlowGraph->IsLocked()) + { + return false; + } + } + + if (bIsReconstructingNode) + { + return false; + } + + if (!bNeedsFullReconstruction && !HavePinsChanged()) + { + return false; + } + + return true; +} + bool UFlowGraphNode::IsAncestorNode(const UFlowGraphNode& OtherNode) const { const UFlowGraphNode* CurParentNode = ParentNode; @@ -1766,6 +1819,30 @@ bool UFlowGraphNode::IsAncestorNode(const UFlowGraphNode& OtherNode) const return false; } +void UFlowGraphNode::RebuildPinArraysOnLoad() +{ + for (UEdGraphPin* Pin : Pins) + { + switch (Pin->Direction) + { + case EGPD_Input: + { + InputPins.Add(Pin); + break; + } + case EGPD_Output: + { + OutputPins.Add(Pin); + break; + } + default: + { + UE_LOG(LogFlow, Error, TEXT("Encountered Pin with invalid direction!")); + } + } + } +} + bool UFlowGraphNode::CanAcceptSubNodeAsChild(const UFlowGraphNode& SubNodeToConsider, const TSet& AllRootSubNodesToPaste, FString* OutReasonString) const { const UFlowNodeBase* OtherFlowNodeSubNode = SubNodeToConsider.NodeInstance; diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp index 72c11f9fc..47bec414c 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp @@ -56,6 +56,7 @@ void SFlowGraphNode::Construct(const FArguments& InArgs, UFlowGraphNode* InNode) FlowGraphNode = InNode; FlowGraphNode->OnSignalModeChanged.BindRaw(this, &SFlowGraphNode::UpdateGraphNode); + FlowGraphNode->OnReconstructNodeCompleted.BindRaw(this, &SFlowGraphNode::UpdateGraphNode); SetCursor(EMouseCursor::CardinalCross); UpdateGraphNode(); @@ -63,6 +64,12 @@ void SFlowGraphNode::Construct(const FArguments& InArgs, UFlowGraphNode* InNode) bDragMarkerVisible = false; } +SFlowGraphNode::~SFlowGraphNode() +{ + FlowGraphNode->OnSignalModeChanged.Unbind(); + FlowGraphNode->OnReconstructNodeCompleted.Unbind(); +} + void SFlowGraphNode::GetNodeInfoPopups(FNodeInfoContext* Context, TArray& Popups) const { const FString& Description = FlowGraphNode->GetNodeDescription(); diff --git a/Source/FlowEditor/Public/FlowEditorCommands.h b/Source/FlowEditor/Public/FlowEditorCommands.h index 3f4e873ab..d83f84d5b 100644 --- a/Source/FlowEditor/Public/FlowEditorCommands.h +++ b/Source/FlowEditor/Public/FlowEditorCommands.h @@ -30,7 +30,7 @@ class FLOWEDITOR_API FFlowGraphCommands : public TCommands FFlowGraphCommands(); /** Context Pins */ - TSharedPtr RefreshContextPins; + TSharedPtr ReconstructNode; /** Pins */ TSharedPtr AddInput; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h index 4f4ecb0e6..2bafbfcc0 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h @@ -94,8 +94,8 @@ class FLOWEDITOR_API SFlowGraphEditor : public SGraphEditor virtual void OnNodeDoubleClicked(class UEdGraphNode* Node) const; virtual void OnNodeTitleCommitted(const FText& NewText, ETextCommit::Type CommitInfo, UEdGraphNode* NodeBeingChanged); - virtual void RefreshContextPins() const; - virtual bool CanRefreshContextPins() const; + virtual void ReconstructNode() const; + virtual bool CanReconstructNode() const; private: void AddInput() const; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h index a85dd6cd9..ed907685a 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h @@ -93,6 +93,8 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema virtual void GetGraphNodeContextActions(FGraphContextMenuBuilder& ContextMenuBuilder, int32 SubNodeFlags) const; + virtual bool ShouldAlwaysPurgeOnModification() const override { return false; } + static bool IsAddOnAllowedForSelectedObjects(const TArray& SelectedObjects, const UFlowNodeAddOn* AddOnTemplate); // -- diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h index dd598751d..ff8cc671c 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h @@ -121,6 +121,9 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode bool IsAncestorNode(const UFlowGraphNode& OtherNode) const; +protected: + void RebuildPinArraysOnLoad(); + ////////////////////////////////////////////////////////////////////////// // Utils @@ -159,6 +162,9 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode void ValidateGraphNode(FFlowMessageLog& MessageLog) const; +protected: + bool ShouldReconstructNode() const; + ////////////////////////////////////////////////////////////////////////// // Pins @@ -191,9 +197,11 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode // Call node and graph updates manually, if using bBatchRemoval void RemoveInstancePin(UEdGraphPin* Pin); +protected: // Create pins from the context asset, i.e. Sequencer events - void RefreshContextPins(const bool bReconstructNode); - + void RefreshContextPins(); + +public: // UEdGraphNode virtual void GetPinHoverText(const UEdGraphPin& Pin, FString& HoverTextOut) const override; // -- @@ -225,6 +233,7 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode public: FFlowGraphNodeEvent OnSignalModeChanged; + FFlowGraphNodeEvent OnReconstructNodeCompleted; // Pin activation forced by user during PIE virtual void ForcePinActivation(const FEdGraphPinReference PinReference) const; @@ -302,7 +311,7 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode void LogError(const FString& MessageToLog, const UFlowNodeBase* FlowNodeBase) const; - bool HavePinsChanged(); + bool HavePinsChanged() const; public: diff --git a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h index 143fbfdf1..e1a2db524 100644 --- a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h @@ -26,6 +26,8 @@ class FLOWEDITOR_API SFlowGraphNode : public SGraphNode void Construct(const FArguments& InArgs, UFlowGraphNode* InNode); + ~SFlowGraphNode(); + protected: // SNodePanel::SNode virtual void GetNodeInfoPopups(FNodeInfoContext* Context, TArray& Popups) const override; From 53170b4a56392d21f032fa04b21ced723f877120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Tue, 7 Jan 2025 17:53:31 +0100 Subject: [PATCH 311/485] New project settings: allows anyone to override Flow Palette category for specific nodes without modifying source code. --- Source/Flow/Private/Nodes/FlowNodeBase.cpp | 3 ++- .../Private/Graph/FlowGraphSchema.cpp | 9 ++++----- .../Private/Graph/FlowGraphSchema_Actions.cpp | 12 ++++++++++++ .../Public/Graph/FlowGraphSchema_Actions.h | 17 ++++++++++------- .../FlowEditor/Public/Graph/FlowGraphSettings.h | 6 +++++- 5 files changed, 33 insertions(+), 14 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp index fe646b4b0..e235d15c5 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBase.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -784,7 +784,8 @@ FString UFlowNodeBase::GetNodeDescription() const { return K2_GetNodeDescription(); } -#endif + +#endif // WITH_EDITOR void UFlowNodeBase::SetNodeConfigText(const FText& NodeConfigText) { diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index 8018bf54d..480a13d6a 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -26,7 +26,6 @@ #include "EdGraphSchema_K2.h" #include "Editor.h" #include "Engine/MemberReference.h" -#include "Engine/UserDefinedStruct.h" #include "Kismet/BlueprintTypeConversions.h" #include "Kismet2/KismetEditorUtilities.h" #include "ScopedTransaction.h" @@ -1038,12 +1037,12 @@ void UFlowGraphSchema::GetFlowNodeActions(FGraphActionMenuBuilder& ActionMenuBui { TArray FilteredNodes = GetFilteredPlaceableNodesOrAddOns(EditedFlowAsset, NativeFlowNodes, BlueprintFlowNodes); - for (const UFlowNodeBase* FlowNodeBase : FilteredNodes) + const UFlowGraphSettings& FlowGraphSettings = *UFlowGraphSettings::Get(); + for (const UFlowNodeBase* FlowNode : FilteredNodes) { - if ((CategoryName.IsEmpty() || CategoryName.Equals(FlowNodeBase->GetNodeCategory())) && !UFlowGraphSettings::Get()->NodesHiddenFromPalette.Contains(FlowNodeBase->GetClass())) + if ((CategoryName.IsEmpty() || CategoryName.Equals(FlowNode->GetNodeCategory())) && !FlowGraphSettings.NodesHiddenFromPalette.Contains(FlowNode->GetClass())) { - const UFlowNode* FlowNode = CastChecked(FlowNodeBase); - TSharedPtr NewNodeAction(new FFlowGraphSchemaAction_NewNode(FlowNode)); + TSharedPtr NewNodeAction(new FFlowGraphSchemaAction_NewNode(FlowNode, FlowGraphSettings)); ActionMenuBuilder.AddAction(NewNodeAction); } } diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp index 38e05a57c..abd8db3d2 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp @@ -4,6 +4,7 @@ #include "Graph/FlowGraph.h" #include "Graph/FlowGraphEditor.h" +#include "Graph/FlowGraphSettings.h" #include "Graph/FlowGraphSchema.h" #include "Graph/FlowGraphUtils.h" #include "Graph/Nodes/FlowGraphNode.h" @@ -200,6 +201,17 @@ UFlowGraphNode* FFlowGraphSchemaAction_NewNode::ImportNode(UEdGraph* ParentGraph return NewGraphNode; } +FText FFlowGraphSchemaAction_NewNode::GetNodeCategory(const UFlowNodeBase* Node, const UFlowGraphSettings& GraphSettings) +{ + const FString* CategoryOverridenByUser = GraphSettings.OverridenNodeCategories.Find(Node->GetClass()); + if (CategoryOverridenByUser && !CategoryOverridenByUser->IsEmpty()) + { + return FText::FromString(*CategoryOverridenByUser); + } + + return FText::FromString(Node->GetNodeCategory()); +} + ///////////////////////////////////////////////////// // New SubNode (AddOn) diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h index 399ae7cf4..f57a715dc 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h @@ -8,6 +8,8 @@ #include "Nodes/FlowNode.h" #include "FlowGraphSchema_Actions.generated.h" +class UFlowGraphSettings; + /** Action to add a node to the graph */ USTRUCT() struct FLOWEDITOR_API FFlowGraphSchemaAction_NewNode : public FEdGraphSchemaAction @@ -26,19 +28,17 @@ struct FLOWEDITOR_API FFlowGraphSchemaAction_NewNode : public FEdGraphSchemaActi virtual FName GetTypeId() const override { return StaticGetTypeId(); } FFlowGraphSchemaAction_NewNode() - : FEdGraphSchemaAction() - , NodeClass(nullptr) + : NodeClass(nullptr) { } - FFlowGraphSchemaAction_NewNode(UClass* Node) - : FEdGraphSchemaAction() - , NodeClass(Node) + explicit FFlowGraphSchemaAction_NewNode(UClass* InNodeClass) + : NodeClass(InNodeClass) { } - FFlowGraphSchemaAction_NewNode(const UFlowNode* Node) - : FEdGraphSchemaAction(FText::FromString(Node->GetNodeCategory()), Node->GetNodeTitle(), Node->GetNodeToolTip(), 0, FText::FromString(Node->GetClass()->GetMetaData("Keywords"))) + explicit FFlowGraphSchemaAction_NewNode(const UFlowNodeBase* Node, const UFlowGraphSettings& GraphSettings) + : FEdGraphSchemaAction(GetNodeCategory(Node, GraphSettings), Node->GetNodeTitle(), Node->GetNodeToolTip(), 0, FText::FromString(Node->GetClass()->GetMetaData("Keywords"))) , NodeClass(Node->GetClass()) { } @@ -50,6 +50,9 @@ struct FLOWEDITOR_API FFlowGraphSchemaAction_NewNode : public FEdGraphSchemaActi static UFlowGraphNode* CreateNode(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const UClass* NodeClass, const FVector2D Location, const bool bSelectNewNode = true); static UFlowGraphNode* RecreateNode(UEdGraph* ParentGraph, UEdGraphNode* OldInstance, UFlowNode* FlowNode); static UFlowGraphNode* ImportNode(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const UClass* NodeClass, const FGuid& NodeGuid, const FVector2D Location); + +private: + static FText GetNodeCategory(const UFlowNodeBase* Node, const UFlowGraphSettings& GraphSettings); }; /** Action to add a subnode to the selected node */ diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h index e23dbf5e0..18a91a7a4 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h @@ -95,9 +95,13 @@ class FLOWEDITOR_API UFlowGraphSettings : public UDeveloperSettings /** Hide specific nodes from the Flow Palette without changing the source code. * Requires restart after making a change. */ - UPROPERTY(EditAnywhere, config, Category = "Nodes") + UPROPERTY(EditAnywhere, config, Category = "Nodes", meta = (ConfigRestartRequired = true)) TArray> NodesHiddenFromPalette; + /** Allows anyone to override Flow Palette category for specific nodes without modifying source code.*/ + UPROPERTY(EditAnywhere, config, Category = "Nodes") + TMap, FString> OverridenNodeCategories; + /** Hide default pin names on simple nodes, reduces UI clutter */ UPROPERTY(EditAnywhere, config, Category = "Nodes") bool bShowDefaultPinNames; From 3a7b1a9cb5b9bf9aa1a39869c4e1559a1bc39fb0 Mon Sep 17 00:00:00 2001 From: LindyHopperGT <91915878+LindyHopperGT@users.noreply.github.com> Date: Sun, 12 Jan 2025 08:53:13 -0800 Subject: [PATCH 312/485] Post-merge updates from Riot version (#256) * Flow AddOns Introduced FlowAddOns ~ Major rework of the Flow Editor to support AddOns ~ Also added ExecuteComponent flow node, which allows a component of the owning actor (if it implements a specific interface) to be executed via a proxy node in the Flow Graph. We use this to wrap some legacy systems in flow graph nodes. * Fix Linux compile errors and regressions on UFUNCTIONS * Fix bug with ExecuteInput for AddOns Nodes and AddOns could execute for pins they did not expect, if the AddOns introduced pins that the Node (or other AddOns) were not aware of. Also, removed some deprecation warnings because they were generating compile warnings, which could cause some projects problems with automated builds. I changed the deprecation message to a human-readable message only. * Slight adjustment to IsSupportedInputPinName() early return if the array is empty * AddOns Optimized and changed how Input/Outputs are added from AddOns * Quality of life improvements Revised the ForEachAddOn() signature, since it only ever needs to be used when iterating over all AddOns (recursively), as you can use a normal for loop over AddOns for just the AddOns of this object alone. Also moved some access functions from FlowNode to FlowNodeBase and exposed them to Blueprint, based on a question I saw on the Discord about Get Actor Owner access. * Extracted Component injection code into Flow base also updated ExecuteComponent to use it * Transferring flags from Template component to instance + Transient * Singular InjectComponent signature * Small revisions to Execute Component flownode * Fix uniqueness problem with component lookup * fixed GC-related compilation warning in AddReferencedObjects() * cosmetic pass on AddOns refactor - Adding Category to a few functions which fixes build machine compilation in the plugin mode. - Partially restoring class layout to its original shape, taking in account new sections, and improve it a little bit. - Reversing some changes on making symbols editor-only which might break non-editor tools working with Flow Graph. - Static analysis fixes. * Flow Diff improvements * Added AActor::GetActorClassDefaultComponents() alternative for UE < 5.3 * Folded IFlowNativeExecutableInterface into UFlowNodeBase * Fix compile error * Fix for Undo buffer for SetConfigText * Deleted file * deleted files * Flow Data Pins (initial version) Initial version for Flow Data Pins feature. * Removed Class/Object enum values (until we finish the feature) * Restored commented-out code (and unexpanded the macro) * Reverted last change. need to work on the macros more. * Fixed compilation problem with Enum macro * Fixed details customization for flow data pin input properties * Fixed problem detecting property to pin binding changes. It was only checking the sizes, not doing a deep comparison of the two maps. * Fix problem with data pins incorrectly breaking the wrong pin's connections * Fix erroneous return value where it could find suppliers, but reported that it couldn't * Reworked DataPins to use the authored property name for the lookup map Was using DisplayName, but UE doesn't provide a convenient lookup for the property DisplayName in non-editor builds, making for keeping the property name and the pin name in sync more cumbersome than I'd prefer. Using the Authored name instead allows us to use the GET_MEMBER_PROPERTY_NAME_CHECKED macro to keep the name in sync with the property w/static assert. * Added details customization for named data pin output property * Added InstancedStruct support for Flow Data Pins * Added FlowDataPinPropertyProviderInterface for the AI Flow plugin to use to integrate with data pins. * Status String support for AddOns and ForEachAddOn improvements AddOns can now include line(s) in the StatusString of their node ForEachAddOn functions can break early with 'success' or 'failure' * Added Flow Data Pins support for Rotator, Object, Class * Refined how Data Pins are auto-generated, graph and node updates in the editor * Disallow Reroute nodes for non-exec flow pin connections At least until we develop some data-pin-friendly solution, disabling the reroute node generation for flow data pin wires. * Wire colors match their pin color * Small refinements * Small bugfixes missing header includes calling Super:: Log an Timer support for assets that haven't been resaved with data pins to still source their data correctly. * Code review changes and crash fix crash fix for copy/pasting a flow node with addons. This crash would only happen in this PR (not the current flow mainline version) * Bugfix - Pasting & Drag/Dropping AddOns onto nodes now respects allowed child addon filtering * Updated signature of UFlowNodeAddOn::AcceptFlowNodeAddOnParent() to include additional proposed children This signature now affords the AddOn->Parent question the same information as the Parent->AddOn question gained in the previous change * Fix for BP compile errors for the default array reference for the new AdditionalAddOnsToAssumeAreChildren parameter * Fix crash when pasting nodes with addons into another graph The addons array is not fully-formed when called from some mid-paste functions. * Tag-based Node Display Style conversion hierarchical tag-indexed NodeDisplayStyle. - project extensible node styles - hierarchical node style definitions in INI * UE 5.5 compilation error fixes * Some fixes for Custom NodeDisplayStyle and 5.4 compilation defines * Latest updates post-merge with mainline * removed duplicate line * Restored blank line * Removed stray comment * Bugfix for modifying every flow asset that is opened in the editor. We were unilaterally modifying all flow nodes by reconstructing their nodes when starting editing a flow asset, which is not what we want or need to do. * Extending Allow/Deny to include AddOns - also adding capability to opt back-in to being allowed as a subclass of a denied node * Fix typo for Rotator auto data pin generation Input was used, when it should be the output variant * Updates from our side recategorization of nodes, and some other stuff * Revisions post-merge with mainline * Restored this from mainline --------- Co-authored-by: FoolsTheoryDev --- Flow.uplugin | 4 +-- Source/Flow/Private/FlowAsset.cpp | 29 ++++++++++++------- Source/Flow/Private/FlowSubsystem.cpp | 2 +- .../DataPins/FlowNode_DefineProperties.cpp | 2 +- .../Nodes/Operators/FlowNode_LogicalAND.cpp | 2 +- .../Nodes/Operators/FlowNode_LogicalOR.cpp | 2 +- .../Private/Nodes/Route/FlowNode_Branch.cpp | 2 +- .../Private/Nodes/Route/FlowNode_Counter.cpp | 2 +- .../Nodes/Route/FlowNode_CustomEventBase.cpp | 2 +- .../Route/FlowNode_ExecutionMultiGate.cpp | 2 +- .../Route/FlowNode_ExecutionSequence.cpp | 2 +- .../Private/Nodes/Route/FlowNode_Finish.cpp | 2 +- .../Private/Nodes/Route/FlowNode_Reroute.cpp | 2 +- .../Private/Nodes/Route/FlowNode_Start.cpp | 2 +- .../Private/Nodes/Route/FlowNode_SubGraph.cpp | 2 +- .../Private/Nodes/Route/FlowNode_Timer.cpp | 2 +- .../Nodes/Utils/FlowNode_Checkpoint.cpp | 4 +-- .../Flow/Private/Nodes/Utils/FlowNode_Log.cpp | 2 +- .../Nodes/World/FlowNode_ExecuteComponent.cpp | 2 +- .../World/FlowNode_OnActorRegistered.cpp | 3 ++ .../World/FlowNode_OnActorUnregistered.cpp | 3 ++ Source/Flow/Public/FlowAsset.h | 7 ++++- .../Flow/Public/Types/FlowDataPinProperties.h | 4 +-- Source/FlowEditor/FlowEditor.Build.cs | 5 ++-- .../Private/Asset/FlowAssetEditor.cpp | 2 ++ Source/FlowEditor/Private/Graph/FlowGraph.cpp | 14 +++++---- .../Private/Graph/Nodes/FlowGraphNode.cpp | 5 ++-- .../Private/Graph/Widgets/SFlowGraphNode.cpp | 5 ++++ Source/FlowEditor/Public/Graph/FlowGraph.h | 1 + .../Public/Graph/Widgets/SFlowGraphNode.h | 2 +- 30 files changed, 74 insertions(+), 46 deletions(-) diff --git a/Flow.uplugin b/Flow.uplugin index 8214ef6ea..bc3200afd 100644 --- a/Flow.uplugin +++ b/Flow.uplugin @@ -1,4 +1,4 @@ -{ +{ "FileVersion" : 3, "Version" : 2.1, "FriendlyName" : "Flow", @@ -43,4 +43,4 @@ "Enabled": true } ] -} +} \ No newline at end of file diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 42af20e06..88ebbba9a 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -1127,10 +1127,12 @@ void UFlowAsset::BroadcastRuntimeMessageAdded(const TSharedRef InOwner, UFlowAsset* InTemplateAsset) +void UFlowAsset::InitializeInstance(const TWeakObjectPtr InOwner, UFlowAsset& InTemplateAsset) { + check(!IsInstanceInitialized()); + Owner = InOwner; - TemplateAsset = InTemplateAsset; + TemplateAsset = &InTemplateAsset; for (TPair>& Node : Nodes) { @@ -1151,21 +1153,23 @@ void UFlowAsset::InitializeInstance(const TWeakObjectPtr InOwner, UFlow void UFlowAsset::DeinitializeInstance() { - for (const TPair& Node : ObjectPtrDecay(Nodes)) + if (IsInstanceInitialized()) { - if (IsValid(Node.Value)) + for (const TPair& Node : ObjectPtrDecay(Nodes)) { - Node.Value->DeinitializeInstance(); + if (IsValid(Node.Value)) + { + Node.Value->DeinitializeInstance(); + } } - } - if (TemplateAsset) - { const int32 ActiveInstancesLeft = TemplateAsset->RemoveInstance(this); if (ActiveInstancesLeft == 0 && GetFlowSubsystem()) { GetFlowSubsystem()->RemoveInstancedTemplate(TemplateAsset); } + + TemplateAsset = nullptr; } } @@ -1174,6 +1178,8 @@ void UFlowAsset::PreStartFlow() ResetNodes(); #if WITH_EDITOR + check(IsInstanceInitialized()); + if (TemplateAsset->ActiveInstances.Num() == 1) { // this instance is the only active one, set it directly as Inspected Instance @@ -1339,6 +1345,7 @@ void UFlowAsset::FinishNode(UFlowNode* Node) if (RootFlowInstances.Contains(this)) { GetFlowSubsystem()->FinishRootFlow(Owner.Get(), TemplateAsset, EFlowFinishPolicy::Keep); + return; } } @@ -1476,7 +1483,7 @@ void UFlowAsset::LogError(const FString& MessageToLog, const UFlowNodeBase* Node // this is runtime log which is should be only called on runtime instances of asset if (TemplateAsset == nullptr) { - UE_LOG(LogFlow, Log, TEXT("Attempted to use Runtime Log on template asset %s"), *MessageToLog); + UE_LOG(LogFlow, Log, TEXT("Attempted to use Runtime Log on null template asset %s"), *MessageToLog); } if (RuntimeLog.Get()) @@ -1491,7 +1498,7 @@ void UFlowAsset::LogWarning(const FString& MessageToLog, const UFlowNodeBase* No // this is runtime log which is should be only called on runtime instances of asset if (TemplateAsset == nullptr) { - UE_LOG(LogFlow, Log, TEXT("Attempted to use Runtime Log on template asset %s"), *MessageToLog); + UE_LOG(LogFlow, Log, TEXT("Attempted to use Runtime Log on null template asset %s"), *MessageToLog); } if (RuntimeLog.Get()) @@ -1506,7 +1513,7 @@ void UFlowAsset::LogNote(const FString& MessageToLog, const UFlowNodeBase* Node) // this is runtime log which is should be only called on runtime instances of asset if (TemplateAsset == nullptr) { - UE_LOG(LogFlow, Log, TEXT("Attempted to use Runtime Log on template asset %s"), *MessageToLog); + UE_LOG(LogFlow, Log, TEXT("Attempted to use Runtime Log on null template asset %s"), *MessageToLog); } if (RuntimeLog.Get()) diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index 4c242668f..04a1da79a 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -238,7 +238,7 @@ UFlowAsset* UFlowSubsystem::CreateFlowInstance(const TWeakObjectPtr Own } UFlowAsset* NewInstance = NewObject(this, LoadedFlowAsset->GetClass(), *NewInstanceName, RF_Transient, LoadedFlowAsset, false, nullptr); - NewInstance->InitializeInstance(Owner, LoadedFlowAsset); + NewInstance->InitializeInstance(Owner, *LoadedFlowAsset); LoadedFlowAsset->AddInstance(NewInstance); diff --git a/Source/Flow/Private/Nodes/DataPins/FlowNode_DefineProperties.cpp b/Source/Flow/Private/Nodes/DataPins/FlowNode_DefineProperties.cpp index 5378e1710..2065d26ba 100644 --- a/Source/Flow/Private/Nodes/DataPins/FlowNode_DefineProperties.cpp +++ b/Source/Flow/Private/Nodes/DataPins/FlowNode_DefineProperties.cpp @@ -9,7 +9,7 @@ UFlowNode_DefineProperties::UFlowNode_DefineProperties(const FObjectInitializer& { #if WITH_EDITOR NodeDisplayStyle = FlowNodeStyle::InOut; - Category = TEXT("Data Pins"); + Category = TEXT("Flow|Graph"); #endif InputPins.Empty(); diff --git a/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalAND.cpp b/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalAND.cpp index e49556897..68ff95cc1 100644 --- a/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalAND.cpp +++ b/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalAND.cpp @@ -8,7 +8,7 @@ UFlowNode_LogicalAND::UFlowNode_LogicalAND(const FObjectInitializer& ObjectIniti : Super(ObjectInitializer) { #if WITH_EDITOR - Category = TEXT("Operators"); + Category = TEXT("Flow|Routing|Logic"); NodeDisplayStyle = FlowNodeStyle::Logic; #endif diff --git a/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp b/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp index aee293887..d331f02c0 100644 --- a/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp +++ b/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp @@ -11,7 +11,7 @@ UFlowNode_LogicalOR::UFlowNode_LogicalOR(const FObjectInitializer& ObjectInitial , ExecutionCount(0) { #if WITH_EDITOR - Category = TEXT("Operators"); + Category = TEXT("Flow|Routing|Logic"); NodeDisplayStyle = FlowNodeStyle::Logic; #endif diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Branch.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Branch.cpp index 645b776e9..78e39ac2a 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Branch.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Branch.cpp @@ -13,7 +13,7 @@ UFlowNode_Branch::UFlowNode_Branch(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { #if WITH_EDITOR - Category = TEXT("Route"); + Category = TEXT("Flow|Routing"); NodeDisplayStyle = FlowNodeStyle::Logic; #endif InputPins.Empty(); diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Counter.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Counter.cpp index f6a4cd701..bbf7e6077 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Counter.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Counter.cpp @@ -10,7 +10,7 @@ UFlowNode_Counter::UFlowNode_Counter(const FObjectInitializer& ObjectInitializer , CurrentSum(0) { #if WITH_EDITOR - Category = TEXT("Route"); + Category = TEXT("Flow|Routing"); NodeDisplayStyle = FlowNodeStyle::Condition; #endif diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_CustomEventBase.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_CustomEventBase.cpp index 0f40d0a23..ddc501d4e 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_CustomEventBase.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_CustomEventBase.cpp @@ -9,7 +9,7 @@ UFlowNode_CustomEventBase::UFlowNode_CustomEventBase(const FObjectInitializer& O : Super(ObjectInitializer) { #if WITH_EDITOR - Category = TEXT("Route"); + Category = TEXT("Flow|Graph"); NodeDisplayStyle = FlowNodeStyle::InOut; #endif diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp index a7e05289e..c50e611db 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp @@ -9,7 +9,7 @@ UFlowNode_ExecutionMultiGate::UFlowNode_ExecutionMultiGate(const FObjectInitiali , StartIndex(INDEX_NONE) { #if WITH_EDITOR - Category = TEXT("Route"); + Category = TEXT("Flow|Routing"); NodeDisplayStyle = FlowNodeStyle::Logic; #endif diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp index 386dbaa81..5056deabe 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp @@ -9,7 +9,7 @@ UFlowNode_ExecutionSequence::UFlowNode_ExecutionSequence(const FObjectInitialize , bSavePinExecutionState(true) { #if WITH_EDITOR - Category = TEXT("Route"); + Category = TEXT("Flow|Routing"); NodeDisplayStyle = FlowNodeStyle::Logic; #endif diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp index 5a9f98ecf..7824a1886 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp @@ -8,7 +8,7 @@ UFlowNode_Finish::UFlowNode_Finish(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { #if WITH_EDITOR - Category = TEXT("Route"); + Category = TEXT("Flow|Graph"); NodeDisplayStyle = FlowNodeStyle::InOut; #endif diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Reroute.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Reroute.cpp index 2c24b9900..6e518b623 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Reroute.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Reroute.cpp @@ -8,7 +8,7 @@ UFlowNode_Reroute::UFlowNode_Reroute(const FObjectInitializer& ObjectInitializer : Super(ObjectInitializer) { #if WITH_EDITOR - Category = TEXT("Route"); + Category = TEXT("Flow|Routing"); #endif AllowedSignalModes = {EFlowSignalMode::Enabled, EFlowSignalMode::Disabled}; diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp index 264c1326b..321848542 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp @@ -8,7 +8,7 @@ UFlowNode_Start::UFlowNode_Start(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { #if WITH_EDITOR - Category = TEXT("Route"); + Category = TEXT("Flow|Graph"); NodeDisplayStyle = FlowNodeStyle::InOut; bCanDelete = bCanDuplicate = false; #endif diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp index 45311c4da..43dd0417e 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp @@ -19,7 +19,7 @@ UFlowNode_SubGraph::UFlowNode_SubGraph(const FObjectInitializer& ObjectInitializ , bCanInstanceIdenticalAsset(false) { #if WITH_EDITOR - Category = TEXT("Route"); + Category = TEXT("Flow|Graph"); NodeDisplayStyle = FlowNodeStyle::SubGraph; AllowedAssignedAssetClasses = {UFlowAsset::StaticClass()}; diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp index 184862c39..64fd2a2a3 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp @@ -22,7 +22,7 @@ UFlowNode_Timer::UFlowNode_Timer(const FObjectInitializer& ObjectInitializer) , RemainingStepTime(0.0f) { #if WITH_EDITOR - Category = TEXT("Route"); + Category = TEXT("Flow|Routing"); NodeDisplayStyle = FlowNodeStyle::Latent; #endif diff --git a/Source/Flow/Private/Nodes/Utils/FlowNode_Checkpoint.cpp b/Source/Flow/Private/Nodes/Utils/FlowNode_Checkpoint.cpp index f56715549..3184abb8a 100644 --- a/Source/Flow/Private/Nodes/Utils/FlowNode_Checkpoint.cpp +++ b/Source/Flow/Private/Nodes/Utils/FlowNode_Checkpoint.cpp @@ -1,4 +1,4 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "Nodes/Utils/FlowNode_Checkpoint.h" #include "FlowSubsystem.h" @@ -11,7 +11,7 @@ UFlowNode_Checkpoint::UFlowNode_Checkpoint(const FObjectInitializer& ObjectIniti : Super(ObjectInitializer) { #if WITH_EDITOR - Category = TEXT("Utils"); + Category = TEXT("Flow|Save"); #endif } diff --git a/Source/Flow/Private/Nodes/Utils/FlowNode_Log.cpp b/Source/Flow/Private/Nodes/Utils/FlowNode_Log.cpp index 8e75cab13..3bbd8ddbd 100644 --- a/Source/Flow/Private/Nodes/Utils/FlowNode_Log.cpp +++ b/Source/Flow/Private/Nodes/Utils/FlowNode_Log.cpp @@ -19,7 +19,7 @@ UFlowNode_Log::UFlowNode_Log(const FObjectInitializer& ObjectInitializer) , TextColor(FColor::Yellow) { #if WITH_EDITOR - Category = TEXT("Utils"); + Category = TEXT("Developer"); NodeDisplayStyle = FlowNodeStyle::Developer; #endif } diff --git a/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp b/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp index 16f27e3a0..717ab9b10 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp @@ -20,7 +20,7 @@ UFlowNode_ExecuteComponent::UFlowNode_ExecuteComponent() : Super() { #if WITH_EDITOR - Category = TEXT("World"); + Category = TEXT("Flow|Owner"); #endif // WITH_EDITOR } diff --git a/Source/Flow/Private/Nodes/World/FlowNode_OnActorRegistered.cpp b/Source/Flow/Private/Nodes/World/FlowNode_OnActorRegistered.cpp index 5ae82eeee..7a1ed67cb 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_OnActorRegistered.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_OnActorRegistered.cpp @@ -7,6 +7,9 @@ UFlowNode_OnActorRegistered::UFlowNode_OnActorRegistered(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { +#if WITH_EDITOR + Category = TEXT("World|Actor"); +#endif } void UFlowNode_OnActorRegistered::ObserveActor(TWeakObjectPtr Actor, TWeakObjectPtr Component) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_OnActorUnregistered.cpp b/Source/Flow/Private/Nodes/World/FlowNode_OnActorUnregistered.cpp index 988d4677b..eac411c5d 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_OnActorUnregistered.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_OnActorUnregistered.cpp @@ -7,6 +7,9 @@ UFlowNode_OnActorUnregistered::UFlowNode_OnActorUnregistered(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { +#if WITH_EDITOR + Category = TEXT("World|Actor"); +#endif } void UFlowNode_OnActorUnregistered::ObserveActor(TWeakObjectPtr Actor, TWeakObjectPtr Component) diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index b66bcffd9..8dce99a67 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -366,8 +366,12 @@ class FLOW_API UFlowAsset : public UObject EFlowFinishPolicy FinishPolicy; public: - virtual void InitializeInstance(const TWeakObjectPtr InOwner, UFlowAsset* InTemplateAsset); + UE_DEPRECATED(5.4, "Use version that takes a UFlowAssetReference instead.") + virtual void InitializeInstance(const TWeakObjectPtr InOwner, UFlowAsset* InTemplateAsset) { InitializeInstance(InOwner, *InTemplateAsset); } + + virtual void InitializeInstance(const TWeakObjectPtr InOwner, UFlowAsset& InTemplateAsset); virtual void DeinitializeInstance(); + bool IsInstanceInitialized() const { return IsValid(TemplateAsset); } UFlowAsset* GetTemplateAsset() const { return TemplateAsset; } @@ -401,6 +405,7 @@ class FLOW_API UFlowAsset : public UObject TWeakObjectPtr GetFlowInstance(UFlowNode_SubGraph* SubGraphNode) const; protected: + void TriggerCustomInput_FromSubGraph(UFlowNode_SubGraph* Node, const FName& EventName) const; void TriggerCustomOutput(const FName& EventName); diff --git a/Source/Flow/Public/Types/FlowDataPinProperties.h b/Source/Flow/Public/Types/FlowDataPinProperties.h index cc4ad21b8..d71277416 100644 --- a/Source/Flow/Public/Types/FlowDataPinProperties.h +++ b/Source/Flow/Public/Types/FlowDataPinProperties.h @@ -399,7 +399,7 @@ struct FFlowDataPinOutputProperty_Object : public FFlowDataPinProperty public: FFlowDataPinOutputProperty_Object() {} - FLOW_API FFlowDataPinOutputProperty_Object(UObject* InValue, UClass* InClassFilter); + FLOW_API FFlowDataPinOutputProperty_Object(UObject* InValue, UClass* InClassFilter = nullptr); virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Object; } @@ -435,7 +435,7 @@ struct FFlowDataPinOutputProperty_Class : public FFlowDataPinProperty public: FFlowDataPinOutputProperty_Class() {} - FFlowDataPinOutputProperty_Class(const FSoftClassPath& InValue, UClass* InClassFilter) + FFlowDataPinOutputProperty_Class(const FSoftClassPath& InValue, UClass* InClassFilter = nullptr) : Value(InValue) #if WITH_EDITOR , ClassFilter(InClassFilter) diff --git a/Source/FlowEditor/FlowEditor.Build.cs b/Source/FlowEditor/FlowEditor.Build.cs index 54954ebbd..4d851cd0b 100644 --- a/Source/FlowEditor/FlowEditor.Build.cs +++ b/Source/FlowEditor/FlowEditor.Build.cs @@ -18,12 +18,12 @@ public FlowEditor(ReadOnlyTargetRules target) : base(target) "AssetSearch", "EditorSubsystem", "Flow", - "MessageLog" + "MessageLog", + "AIModule", // For BlueprintNodeHelpers::DescribeProperty (could be copy/pasted out to remove editor-only dependency) }); PrivateDependencyModuleNames.AddRange(new[] { - "AIModule", // For BlueprintNodeHelpers::DescribeProperty (could be copy/pasted out to remove editor-only dependency) "ApplicationCore", "AssetDefinition", "AssetTools", @@ -38,7 +38,6 @@ public FlowEditor(ReadOnlyTargetRules target) : base(target) "EditorScriptingUtilities", "EditorStyle", "Engine", - "EngineAssetDefinitions", "GraphEditor", "GameplayTags", "InputCore", diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index 899814a02..c825e33be 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -446,7 +446,9 @@ void FFlowAssetEditor::ValidateAsset_Internal() TabManager->TryInvokeTab(ValidationLogTab); ValidationLogListing->AddMessages(LogResults.Messages); } + ValidationLogListing->OnDataChanged().Broadcast(); + FlowAsset->GetGraph()->NotifyGraphChanged(); } diff --git a/Source/FlowEditor/Private/Graph/FlowGraph.cpp b/Source/FlowEditor/Private/Graph/FlowGraph.cpp index 4177d8fe9..826205701 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraph.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraph.cpp @@ -40,7 +40,12 @@ UFlowGraph::UFlowGraph(const FObjectInitializer& ObjectInitializer) void UFlowGraph::CreateGraph(UFlowAsset* InFlowAsset) { - UFlowGraph* NewGraph = CastChecked(FBlueprintEditorUtils::CreateNewGraph(InFlowAsset, NAME_None, StaticClass(), UFlowGraphSchema::StaticClass())); + return CreateGraph(InFlowAsset, UFlowGraphSchema::StaticClass()); +} + +void UFlowGraph::CreateGraph(UFlowAsset* InFlowAsset, TSubclassOf FlowSchema) +{ + UFlowGraph* NewGraph = CastChecked(FBlueprintEditorUtils::CreateNewGraph(InFlowAsset, NAME_None, StaticClass(), FlowSchema)); NewGraph->bAllowDeletion = false; // Ensure we mapped relation between UFlowNode and UFlowGraphNode classes @@ -116,8 +121,8 @@ void UFlowGraph::RecursivelyRefreshAddOns(UFlowGraphNode& FromFlowGraphNode) TEXT("Missing AddOn detected for node %s (parent %s)"), *FromFlowNodeBase->GetName(), FromFlowGraphNode.GetParentNode() ? - *FromFlowGraphNode.GetParentNode()->GetName() : - TEXT("")); + *FromFlowGraphNode.GetParentNode()->GetName() : + TEXT("")); continue; } @@ -481,6 +486,3 @@ void UFlowGraph::RecursivelySetupAllFlowGraphNodesForEditing(UFlowGraphNode& Fro } } - - - diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index a68216d27..8618a79db 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -287,7 +287,8 @@ void UFlowGraphNode::ReconstructNode() if (!ShouldReconstructNode()) { // This ensures the graph editor 'Refresh' button still rebuilds all of the graph widgets even if the FlowGraphNode has nothing to update. - (void)OnReconstructNodeCompleted.ExecuteIfBound(); + (void) OnReconstructNodeCompleted.ExecuteIfBound(); + return; } @@ -323,7 +324,7 @@ void UFlowGraphNode::ReconstructNode() bNeedsFullReconstruction = false; bIsReconstructingNode = false; - (void)OnReconstructNodeCompleted.ExecuteIfBound(); + (void) OnReconstructNodeCompleted.ExecuteIfBound(); } void UFlowGraphNode::AllocateDefaultPins() diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp index 47bec414c..6ca9ca6b7 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp @@ -55,6 +55,8 @@ void SFlowGraphNode::Construct(const FArguments& InArgs, UFlowGraphNode* InNode) GraphNode = InNode; FlowGraphNode = InNode; + + check(FlowGraphNode); FlowGraphNode->OnSignalModeChanged.BindRaw(this, &SFlowGraphNode::UpdateGraphNode); FlowGraphNode->OnReconstructNodeCompleted.BindRaw(this, &SFlowGraphNode::UpdateGraphNode); @@ -66,8 +68,11 @@ void SFlowGraphNode::Construct(const FArguments& InArgs, UFlowGraphNode* InNode) SFlowGraphNode::~SFlowGraphNode() { + check(FlowGraphNode); FlowGraphNode->OnSignalModeChanged.Unbind(); FlowGraphNode->OnReconstructNodeCompleted.Unbind(); + + FlowGraphNode = nullptr; } void SFlowGraphNode::GetNodeInfoPopups(FNodeInfoContext* Context, TArray& Popups) const diff --git a/Source/FlowEditor/Public/Graph/FlowGraph.h b/Source/FlowEditor/Public/Graph/FlowGraph.h index 00461080f..c5478d432 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraph.h +++ b/Source/FlowEditor/Public/Graph/FlowGraph.h @@ -40,6 +40,7 @@ class FLOWEDITOR_API UFlowGraph : public UEdGraph public: static void CreateGraph(UFlowAsset* InFlowAsset); + static void CreateGraph(UFlowAsset* InFlowAsset, TSubclassOf FlowSchema); void RefreshGraph(); protected: diff --git a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h index e1a2db524..17116380f 100644 --- a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h @@ -26,7 +26,7 @@ class FLOWEDITOR_API SFlowGraphNode : public SGraphNode void Construct(const FArguments& InArgs, UFlowGraphNode* InNode); - ~SFlowGraphNode(); + virtual ~SFlowGraphNode(); protected: // SNodePanel::SNode From a0a6bbcf3690cad177abd8c19d91f3141c15e15b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sun, 12 Jan 2025 18:13:37 +0100 Subject: [PATCH 313/485] a new categorization of nodes, more flat than proposed in PR --- .../Flow/Private/Nodes/DataPins/FlowNode_DefineProperties.cpp | 2 +- Source/Flow/Private/Nodes/Operators/FlowNode_LogicalAND.cpp | 2 +- Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp | 2 +- Source/Flow/Private/Nodes/Route/FlowNode_Branch.cpp | 2 +- Source/Flow/Private/Nodes/Route/FlowNode_Counter.cpp | 2 +- Source/Flow/Private/Nodes/Route/FlowNode_CustomEventBase.cpp | 2 +- .../Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp | 2 +- .../Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp | 2 +- Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp | 2 +- Source/Flow/Private/Nodes/Route/FlowNode_Reroute.cpp | 2 +- Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp | 2 +- Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp | 2 +- Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp | 2 +- Source/Flow/Private/Nodes/Utils/FlowNode_Checkpoint.cpp | 2 +- .../Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp | 2 +- Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp | 4 ++-- Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp | 2 +- .../Flow/Private/Nodes/World/FlowNode_OnActorRegistered.cpp | 3 --- .../Flow/Private/Nodes/World/FlowNode_OnActorUnregistered.cpp | 3 --- .../Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp | 1 - .../Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp | 2 +- 21 files changed, 19 insertions(+), 26 deletions(-) diff --git a/Source/Flow/Private/Nodes/DataPins/FlowNode_DefineProperties.cpp b/Source/Flow/Private/Nodes/DataPins/FlowNode_DefineProperties.cpp index 2065d26ba..235815ac9 100644 --- a/Source/Flow/Private/Nodes/DataPins/FlowNode_DefineProperties.cpp +++ b/Source/Flow/Private/Nodes/DataPins/FlowNode_DefineProperties.cpp @@ -9,7 +9,7 @@ UFlowNode_DefineProperties::UFlowNode_DefineProperties(const FObjectInitializer& { #if WITH_EDITOR NodeDisplayStyle = FlowNodeStyle::InOut; - Category = TEXT("Flow|Graph"); + Category = TEXT("Graph"); #endif InputPins.Empty(); diff --git a/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalAND.cpp b/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalAND.cpp index 68ff95cc1..d78f05940 100644 --- a/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalAND.cpp +++ b/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalAND.cpp @@ -8,7 +8,7 @@ UFlowNode_LogicalAND::UFlowNode_LogicalAND(const FObjectInitializer& ObjectIniti : Super(ObjectInitializer) { #if WITH_EDITOR - Category = TEXT("Flow|Routing|Logic"); + Category = TEXT("Route|Logic"); NodeDisplayStyle = FlowNodeStyle::Logic; #endif diff --git a/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp b/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp index d331f02c0..5e90ea60a 100644 --- a/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp +++ b/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp @@ -11,7 +11,7 @@ UFlowNode_LogicalOR::UFlowNode_LogicalOR(const FObjectInitializer& ObjectInitial , ExecutionCount(0) { #if WITH_EDITOR - Category = TEXT("Flow|Routing|Logic"); + Category = TEXT("Route|Logic"); NodeDisplayStyle = FlowNodeStyle::Logic; #endif diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Branch.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Branch.cpp index 78e39ac2a..de5bef971 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Branch.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Branch.cpp @@ -13,7 +13,7 @@ UFlowNode_Branch::UFlowNode_Branch(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { #if WITH_EDITOR - Category = TEXT("Flow|Routing"); + Category = TEXT("Route|Logic"); NodeDisplayStyle = FlowNodeStyle::Logic; #endif InputPins.Empty(); diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Counter.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Counter.cpp index bbf7e6077..f6a4cd701 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Counter.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Counter.cpp @@ -10,7 +10,7 @@ UFlowNode_Counter::UFlowNode_Counter(const FObjectInitializer& ObjectInitializer , CurrentSum(0) { #if WITH_EDITOR - Category = TEXT("Flow|Routing"); + Category = TEXT("Route"); NodeDisplayStyle = FlowNodeStyle::Condition; #endif diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_CustomEventBase.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_CustomEventBase.cpp index ddc501d4e..c45a29b5d 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_CustomEventBase.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_CustomEventBase.cpp @@ -9,7 +9,7 @@ UFlowNode_CustomEventBase::UFlowNode_CustomEventBase(const FObjectInitializer& O : Super(ObjectInitializer) { #if WITH_EDITOR - Category = TEXT("Flow|Graph"); + Category = TEXT("Graph"); NodeDisplayStyle = FlowNodeStyle::InOut; #endif diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp index c50e611db..a7e05289e 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp @@ -9,7 +9,7 @@ UFlowNode_ExecutionMultiGate::UFlowNode_ExecutionMultiGate(const FObjectInitiali , StartIndex(INDEX_NONE) { #if WITH_EDITOR - Category = TEXT("Flow|Routing"); + Category = TEXT("Route"); NodeDisplayStyle = FlowNodeStyle::Logic; #endif diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp index 5056deabe..386dbaa81 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp @@ -9,7 +9,7 @@ UFlowNode_ExecutionSequence::UFlowNode_ExecutionSequence(const FObjectInitialize , bSavePinExecutionState(true) { #if WITH_EDITOR - Category = TEXT("Flow|Routing"); + Category = TEXT("Route"); NodeDisplayStyle = FlowNodeStyle::Logic; #endif diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp index 7824a1886..108eeaa7b 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp @@ -8,7 +8,7 @@ UFlowNode_Finish::UFlowNode_Finish(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { #if WITH_EDITOR - Category = TEXT("Flow|Graph"); + Category = TEXT("Graph"); NodeDisplayStyle = FlowNodeStyle::InOut; #endif diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Reroute.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Reroute.cpp index 6e518b623..2c24b9900 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Reroute.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Reroute.cpp @@ -8,7 +8,7 @@ UFlowNode_Reroute::UFlowNode_Reroute(const FObjectInitializer& ObjectInitializer : Super(ObjectInitializer) { #if WITH_EDITOR - Category = TEXT("Flow|Routing"); + Category = TEXT("Route"); #endif AllowedSignalModes = {EFlowSignalMode::Enabled, EFlowSignalMode::Disabled}; diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp index 321848542..e15fe2e29 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp @@ -8,7 +8,7 @@ UFlowNode_Start::UFlowNode_Start(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { #if WITH_EDITOR - Category = TEXT("Flow|Graph"); + Category = TEXT("Graph"); NodeDisplayStyle = FlowNodeStyle::InOut; bCanDelete = bCanDuplicate = false; #endif diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp index 43dd0417e..403cc1184 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp @@ -19,7 +19,7 @@ UFlowNode_SubGraph::UFlowNode_SubGraph(const FObjectInitializer& ObjectInitializ , bCanInstanceIdenticalAsset(false) { #if WITH_EDITOR - Category = TEXT("Flow|Graph"); + Category = TEXT("Graph"); NodeDisplayStyle = FlowNodeStyle::SubGraph; AllowedAssignedAssetClasses = {UFlowAsset::StaticClass()}; diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp index 64fd2a2a3..184862c39 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp @@ -22,7 +22,7 @@ UFlowNode_Timer::UFlowNode_Timer(const FObjectInitializer& ObjectInitializer) , RemainingStepTime(0.0f) { #if WITH_EDITOR - Category = TEXT("Flow|Routing"); + Category = TEXT("Route"); NodeDisplayStyle = FlowNodeStyle::Latent; #endif diff --git a/Source/Flow/Private/Nodes/Utils/FlowNode_Checkpoint.cpp b/Source/Flow/Private/Nodes/Utils/FlowNode_Checkpoint.cpp index 3184abb8a..2e05037fe 100644 --- a/Source/Flow/Private/Nodes/Utils/FlowNode_Checkpoint.cpp +++ b/Source/Flow/Private/Nodes/Utils/FlowNode_Checkpoint.cpp @@ -11,7 +11,7 @@ UFlowNode_Checkpoint::UFlowNode_Checkpoint(const FObjectInitializer& ObjectIniti : Super(ObjectInitializer) { #if WITH_EDITOR - Category = TEXT("Flow|Save"); + Category = TEXT("Graph"); #endif } diff --git a/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp b/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp index 6ae5b104d..8a95bf641 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp @@ -13,7 +13,7 @@ UFlowNode_ComponentObserver::UFlowNode_ComponentObserver(const FObjectInitialize { #if WITH_EDITOR NodeDisplayStyle = FlowNodeStyle::Condition; - Category = TEXT("World"); + Category = TEXT("Actor"); #endif InputPins = {FFlowPin(TEXT("Start")), FFlowPin(TEXT("Stop"))}; diff --git a/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp b/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp index 717ab9b10..58018df24 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp @@ -20,8 +20,8 @@ UFlowNode_ExecuteComponent::UFlowNode_ExecuteComponent() : Super() { #if WITH_EDITOR - Category = TEXT("Flow|Owner"); -#endif // WITH_EDITOR + Category = TEXT("Actor"); +#endif } void UFlowNode_ExecuteComponent::InitializeInstance() diff --git a/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp b/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp index 5b5733fb8..d6218bab7 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp @@ -16,7 +16,7 @@ UFlowNode_NotifyActor::UFlowNode_NotifyActor(const FObjectInitializer& ObjectIni , NetMode(EFlowNetMode::Authority) { #if WITH_EDITOR - Category = TEXT("Notifies"); + Category = TEXT("Actor"); #endif } diff --git a/Source/Flow/Private/Nodes/World/FlowNode_OnActorRegistered.cpp b/Source/Flow/Private/Nodes/World/FlowNode_OnActorRegistered.cpp index 7a1ed67cb..5ae82eeee 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_OnActorRegistered.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_OnActorRegistered.cpp @@ -7,9 +7,6 @@ UFlowNode_OnActorRegistered::UFlowNode_OnActorRegistered(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { -#if WITH_EDITOR - Category = TEXT("World|Actor"); -#endif } void UFlowNode_OnActorRegistered::ObserveActor(TWeakObjectPtr Actor, TWeakObjectPtr Component) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_OnActorUnregistered.cpp b/Source/Flow/Private/Nodes/World/FlowNode_OnActorUnregistered.cpp index eac411c5d..988d4677b 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_OnActorUnregistered.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_OnActorUnregistered.cpp @@ -7,9 +7,6 @@ UFlowNode_OnActorUnregistered::UFlowNode_OnActorUnregistered(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { -#if WITH_EDITOR - Category = TEXT("World|Actor"); -#endif } void UFlowNode_OnActorUnregistered::ObserveActor(TWeakObjectPtr Actor, TWeakObjectPtr Component) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp b/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp index 0d03355ce..2583da273 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp @@ -10,7 +10,6 @@ UFlowNode_OnNotifyFromActor::UFlowNode_OnNotifyFromActor(const FObjectInitialize , bRetroactive(false) { #if WITH_EDITOR - Category = TEXT("Notifies"); NodeDisplayStyle = FlowNodeStyle::Condition; #endif } diff --git a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp index b52769417..98e9df4c5 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp +++ b/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp @@ -36,7 +36,7 @@ UFlowNode_PlayLevelSequence::UFlowNode_PlayLevelSequence(const FObjectInitialize , TimeDilation(1.0f) { #if WITH_EDITOR - Category = TEXT("World"); + Category = TEXT("Actor"); NodeDisplayStyle = FlowNodeStyle::Latent; #endif From 00df925e19a9dc8c615eafe335b34e7abaec9247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sun, 12 Jan 2025 18:39:47 +0100 Subject: [PATCH 314/485] Node classes moved to folders matching new Palette layout --- Source/Flow/Private/FlowAsset.cpp | 8 ++++---- Source/Flow/Private/FlowSubsystem.cpp | 2 +- .../Flow/Private/MovieScene/MovieSceneFlowTemplate.cpp | 2 +- .../{World => Actor}/FlowNode_ComponentObserver.cpp | 2 +- .../{World => Actor}/FlowNode_ExecuteComponent.cpp | 2 +- .../Nodes/{World => Actor}/FlowNode_NotifyActor.cpp | 2 +- .../{World => Actor}/FlowNode_OnActorRegistered.cpp | 2 +- .../{World => Actor}/FlowNode_OnActorUnregistered.cpp | 2 +- .../{World => Actor}/FlowNode_OnNotifyFromActor.cpp | 2 +- .../{World => Actor}/FlowNode_PlayLevelSequence.cpp | 2 +- .../Nodes/{Utils => Developer}/FlowNode_Log.cpp | 2 +- .../Nodes/{Utils => Graph}/FlowNode_Checkpoint.cpp | 2 +- .../{Route => Graph}/FlowNode_CustomEventBase.cpp | 2 +- .../Nodes/{Route => Graph}/FlowNode_CustomInput.cpp | 2 +- .../Nodes/{Route => Graph}/FlowNode_CustomOutput.cpp | 2 +- .../{DataPins => Graph}/FlowNode_DefineProperties.cpp | 2 +- .../Private/Nodes/{Route => Graph}/FlowNode_Finish.cpp | 2 +- .../Private/Nodes/{Route => Graph}/FlowNode_Start.cpp | 2 +- .../Nodes/{Route => Graph}/FlowNode_SubGraph.cpp | 2 +- .../Nodes/{Operators => Route}/FlowNode_LogicalAND.cpp | 2 +- .../Nodes/{Operators => Route}/FlowNode_LogicalOR.cpp | 2 +- .../{World => Actor}/FlowNode_ComponentObserver.h | 0 .../Nodes/{World => Actor}/FlowNode_ExecuteComponent.h | 0 .../Nodes/{World => Actor}/FlowNode_NotifyActor.h | 0 .../{World => Actor}/FlowNode_OnActorRegistered.h | 2 +- .../{World => Actor}/FlowNode_OnActorUnregistered.h | 2 +- .../{World => Actor}/FlowNode_OnNotifyFromActor.h | 2 +- .../{World => Actor}/FlowNode_PlayLevelSequence.h | 0 .../Public/Nodes/{Utils => Developer}/FlowNode_Log.h | 0 .../Nodes/{Utils => Graph}/FlowNode_Checkpoint.h | 0 .../Nodes/{Route => Graph}/FlowNode_CustomEventBase.h | 0 .../Nodes/{Route => Graph}/FlowNode_CustomInput.h | 0 .../Nodes/{Route => Graph}/FlowNode_CustomOutput.h | 0 .../{DataPins => Graph}/FlowNode_DefineProperties.h | 0 .../Public/Nodes/{Route => Graph}/FlowNode_Finish.h | 0 .../Public/Nodes/{Route => Graph}/FlowNode_Start.h | 3 +-- .../Public/Nodes/{Route => Graph}/FlowNode_SubGraph.h | 0 .../Nodes/{Operators => Route}/FlowNode_LogicalAND.h | 0 .../Nodes/{Operators => Route}/FlowNode_LogicalOR.h | 0 Source/FlowEditor/Private/Asset/FlowImportUtils.cpp | 2 +- .../Private/DetailCustomizations/FlowAssetDetails.cpp | 2 +- .../FlowNode_ComponentObserverDetails.cpp | 2 +- .../FlowNode_CustomEventBaseDetails.cpp | 2 +- .../FlowNode_PlayLevelSequenceDetails.cpp | 2 +- .../DetailCustomizations/FlowNode_SubGraphDetails.cpp | 2 +- Source/FlowEditor/Private/Find/FindInFlow.cpp | 2 +- Source/FlowEditor/Private/FlowEditorModule.cpp | 10 +++++----- Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp | 3 +-- Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp | 4 ++-- .../Private/Graph/Nodes/FlowGraphNode_Finish.cpp | 2 +- .../Private/Graph/Nodes/FlowGraphNode_Start.cpp | 2 +- .../Private/Graph/Nodes/FlowGraphNode_SubGraph.cpp | 2 +- .../Private/Graph/Widgets/SFlowGraphNode_SubGraph.cpp | 2 +- 53 files changed, 47 insertions(+), 49 deletions(-) rename Source/Flow/Private/Nodes/{World => Actor}/FlowNode_ComponentObserver.cpp (99%) rename Source/Flow/Private/Nodes/{World => Actor}/FlowNode_ExecuteComponent.cpp (99%) rename Source/Flow/Private/Nodes/{World => Actor}/FlowNode_NotifyActor.cpp (96%) rename Source/Flow/Private/Nodes/{World => Actor}/FlowNode_OnActorRegistered.cpp (90%) rename Source/Flow/Private/Nodes/{World => Actor}/FlowNode_OnActorUnregistered.cpp (92%) rename Source/Flow/Private/Nodes/{World => Actor}/FlowNode_OnNotifyFromActor.cpp (96%) rename Source/Flow/Private/Nodes/{World => Actor}/FlowNode_PlayLevelSequence.cpp (99%) rename Source/Flow/Private/Nodes/{Utils => Developer}/FlowNode_Log.cpp (98%) rename Source/Flow/Private/Nodes/{Utils => Graph}/FlowNode_Checkpoint.cpp (94%) rename Source/Flow/Private/Nodes/{Route => Graph}/FlowNode_CustomEventBase.cpp (96%) rename Source/Flow/Private/Nodes/{Route => Graph}/FlowNode_CustomInput.cpp (94%) rename Source/Flow/Private/Nodes/{Route => Graph}/FlowNode_CustomOutput.cpp (97%) rename Source/Flow/Private/Nodes/{DataPins => Graph}/FlowNode_DefineProperties.cpp (98%) rename Source/Flow/Private/Nodes/{Route => Graph}/FlowNode_Finish.cpp (92%) rename Source/Flow/Private/Nodes/{Route => Graph}/FlowNode_Start.cpp (99%) rename Source/Flow/Private/Nodes/{Route => Graph}/FlowNode_SubGraph.cpp (99%) rename Source/Flow/Private/Nodes/{Operators => Route}/FlowNode_LogicalAND.cpp (92%) rename Source/Flow/Private/Nodes/{Operators => Route}/FlowNode_LogicalOR.cpp (96%) rename Source/Flow/Public/Nodes/{World => Actor}/FlowNode_ComponentObserver.h (100%) rename Source/Flow/Public/Nodes/{World => Actor}/FlowNode_ExecuteComponent.h (100%) rename Source/Flow/Public/Nodes/{World => Actor}/FlowNode_NotifyActor.h (100%) rename Source/Flow/Public/Nodes/{World => Actor}/FlowNode_OnActorRegistered.h (91%) rename Source/Flow/Public/Nodes/{World => Actor}/FlowNode_OnActorUnregistered.h (92%) rename Source/Flow/Public/Nodes/{World => Actor}/FlowNode_OnNotifyFromActor.h (95%) rename Source/Flow/Public/Nodes/{World => Actor}/FlowNode_PlayLevelSequence.h (100%) rename Source/Flow/Public/Nodes/{Utils => Developer}/FlowNode_Log.h (100%) rename Source/Flow/Public/Nodes/{Utils => Graph}/FlowNode_Checkpoint.h (100%) rename Source/Flow/Public/Nodes/{Route => Graph}/FlowNode_CustomEventBase.h (100%) rename Source/Flow/Public/Nodes/{Route => Graph}/FlowNode_CustomInput.h (100%) rename Source/Flow/Public/Nodes/{Route => Graph}/FlowNode_CustomOutput.h (100%) rename Source/Flow/Public/Nodes/{DataPins => Graph}/FlowNode_DefineProperties.h (100%) rename Source/Flow/Public/Nodes/{Route => Graph}/FlowNode_Finish.h (100%) rename Source/Flow/Public/Nodes/{Route => Graph}/FlowNode_Start.h (98%) rename Source/Flow/Public/Nodes/{Route => Graph}/FlowNode_SubGraph.h (100%) rename Source/Flow/Public/Nodes/{Operators => Route}/FlowNode_LogicalAND.h (100%) rename Source/Flow/Public/Nodes/{Operators => Route}/FlowNode_LogicalOR.h (100%) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 88ebbba9a..b9b9ab116 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -9,10 +9,10 @@ #include "AddOns/FlowNodeAddOn.h" #include "Interfaces/FlowDataPinGeneratorNodeInterface.h" #include "Nodes/FlowNodeBase.h" -#include "Nodes/Route/FlowNode_CustomInput.h" -#include "Nodes/Route/FlowNode_CustomOutput.h" -#include "Nodes/Route/FlowNode_Start.h" -#include "Nodes/Route/FlowNode_SubGraph.h" +#include "Nodes/Graph/FlowNode_CustomInput.h" +#include "Nodes/Graph/FlowNode_CustomOutput.h" +#include "Nodes/Graph/FlowNode_Start.h" +#include "Nodes/Graph/FlowNode_SubGraph.h" #include "Engine/World.h" #include "Serialization/MemoryReader.h" diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index 04a1da79a..d7ec66aec 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -7,7 +7,7 @@ #include "FlowLogChannels.h" #include "FlowSave.h" #include "FlowSettings.h" -#include "Nodes/Route/FlowNode_SubGraph.h" +#include "Nodes/Graph/FlowNode_SubGraph.h" #include "Engine/GameInstance.h" #include "Engine/World.h" diff --git a/Source/Flow/Private/MovieScene/MovieSceneFlowTemplate.cpp b/Source/Flow/Private/MovieScene/MovieSceneFlowTemplate.cpp index 8ac6bdfc5..ad6e29590 100644 --- a/Source/Flow/Private/MovieScene/MovieSceneFlowTemplate.cpp +++ b/Source/Flow/Private/MovieScene/MovieSceneFlowTemplate.cpp @@ -2,7 +2,7 @@ #include "MovieScene/MovieSceneFlowTemplate.h" #include "MovieScene/MovieSceneFlowTrack.h" -#include "Nodes/World/FlowNode_PlayLevelSequence.h" +#include "Nodes/Actor/FlowNode_PlayLevelSequence.h" #include "Evaluation/MovieSceneEvaluation.h" #include "IMovieScenePlayer.h" diff --git a/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp b/Source/Flow/Private/Nodes/Actor/FlowNode_ComponentObserver.cpp similarity index 99% rename from Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp rename to Source/Flow/Private/Nodes/Actor/FlowNode_ComponentObserver.cpp index 8a95bf641..3020836a3 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_ComponentObserver.cpp +++ b/Source/Flow/Private/Nodes/Actor/FlowNode_ComponentObserver.cpp @@ -1,6 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "Nodes/World/FlowNode_ComponentObserver.h" +#include "Nodes/Actor/FlowNode_ComponentObserver.h" #include "FlowSubsystem.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_ComponentObserver) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp b/Source/Flow/Private/Nodes/Actor/FlowNode_ExecuteComponent.cpp similarity index 99% rename from Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp rename to Source/Flow/Private/Nodes/Actor/FlowNode_ExecuteComponent.cpp index 58018df24..ae0ee254c 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_ExecuteComponent.cpp +++ b/Source/Flow/Private/Nodes/Actor/FlowNode_ExecuteComponent.cpp @@ -1,6 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "Nodes/World/FlowNode_ExecuteComponent.h" +#include "Nodes/Actor/FlowNode_ExecuteComponent.h" #include "Interfaces/FlowCoreExecutableInterface.h" #include "Interfaces/FlowExternalExecutableInterface.h" #include "Interfaces/FlowContextPinSupplierInterface.h" diff --git a/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp b/Source/Flow/Private/Nodes/Actor/FlowNode_NotifyActor.cpp similarity index 96% rename from Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp rename to Source/Flow/Private/Nodes/Actor/FlowNode_NotifyActor.cpp index d6218bab7..9421a0353 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_NotifyActor.cpp +++ b/Source/Flow/Private/Nodes/Actor/FlowNode_NotifyActor.cpp @@ -1,6 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "Nodes/World/FlowNode_NotifyActor.h" +#include "Nodes/Actor/FlowNode_NotifyActor.h" #include "FlowComponent.h" #include "FlowSubsystem.h" diff --git a/Source/Flow/Private/Nodes/World/FlowNode_OnActorRegistered.cpp b/Source/Flow/Private/Nodes/Actor/FlowNode_OnActorRegistered.cpp similarity index 90% rename from Source/Flow/Private/Nodes/World/FlowNode_OnActorRegistered.cpp rename to Source/Flow/Private/Nodes/Actor/FlowNode_OnActorRegistered.cpp index 5ae82eeee..adf7c3619 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_OnActorRegistered.cpp +++ b/Source/Flow/Private/Nodes/Actor/FlowNode_OnActorRegistered.cpp @@ -1,6 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "Nodes/World/FlowNode_OnActorRegistered.h" +#include "Nodes/Actor/FlowNode_OnActorRegistered.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_OnActorRegistered) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_OnActorUnregistered.cpp b/Source/Flow/Private/Nodes/Actor/FlowNode_OnActorUnregistered.cpp similarity index 92% rename from Source/Flow/Private/Nodes/World/FlowNode_OnActorUnregistered.cpp rename to Source/Flow/Private/Nodes/Actor/FlowNode_OnActorUnregistered.cpp index 988d4677b..b9aa1c64d 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_OnActorUnregistered.cpp +++ b/Source/Flow/Private/Nodes/Actor/FlowNode_OnActorUnregistered.cpp @@ -1,6 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "Nodes/World/FlowNode_OnActorUnregistered.h" +#include "Nodes/Actor/FlowNode_OnActorUnregistered.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_OnActorUnregistered) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp b/Source/Flow/Private/Nodes/Actor/FlowNode_OnNotifyFromActor.cpp similarity index 96% rename from Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp rename to Source/Flow/Private/Nodes/Actor/FlowNode_OnNotifyFromActor.cpp index 2583da273..b6f073671 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_OnNotifyFromActor.cpp +++ b/Source/Flow/Private/Nodes/Actor/FlowNode_OnNotifyFromActor.cpp @@ -1,6 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "Nodes/World/FlowNode_OnNotifyFromActor.h" +#include "Nodes/Actor/FlowNode_OnNotifyFromActor.h" #include "FlowComponent.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_OnNotifyFromActor) diff --git a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp b/Source/Flow/Private/Nodes/Actor/FlowNode_PlayLevelSequence.cpp similarity index 99% rename from Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp rename to Source/Flow/Private/Nodes/Actor/FlowNode_PlayLevelSequence.cpp index 98e9df4c5..70fc34b31 100644 --- a/Source/Flow/Private/Nodes/World/FlowNode_PlayLevelSequence.cpp +++ b/Source/Flow/Private/Nodes/Actor/FlowNode_PlayLevelSequence.cpp @@ -1,6 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "Nodes/World/FlowNode_PlayLevelSequence.h" +#include "Nodes/Actor/FlowNode_PlayLevelSequence.h" #include "FlowAsset.h" #include "FlowLogChannels.h" diff --git a/Source/Flow/Private/Nodes/Utils/FlowNode_Log.cpp b/Source/Flow/Private/Nodes/Developer/FlowNode_Log.cpp similarity index 98% rename from Source/Flow/Private/Nodes/Utils/FlowNode_Log.cpp rename to Source/Flow/Private/Nodes/Developer/FlowNode_Log.cpp index 3bbd8ddbd..e531b4d57 100644 --- a/Source/Flow/Private/Nodes/Utils/FlowNode_Log.cpp +++ b/Source/Flow/Private/Nodes/Developer/FlowNode_Log.cpp @@ -1,6 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "Nodes/Utils/FlowNode_Log.h" +#include "Nodes/Developer/FlowNode_Log.h" #include "FlowLogChannels.h" #include "FlowSettings.h" diff --git a/Source/Flow/Private/Nodes/Utils/FlowNode_Checkpoint.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_Checkpoint.cpp similarity index 94% rename from Source/Flow/Private/Nodes/Utils/FlowNode_Checkpoint.cpp rename to Source/Flow/Private/Nodes/Graph/FlowNode_Checkpoint.cpp index 2e05037fe..6b51e9074 100644 --- a/Source/Flow/Private/Nodes/Utils/FlowNode_Checkpoint.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_Checkpoint.cpp @@ -1,6 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "Nodes/Utils/FlowNode_Checkpoint.h" +#include "Nodes/Graph/FlowNode_Checkpoint.h" #include "FlowSubsystem.h" #include "Kismet/GameplayStatics.h" diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_CustomEventBase.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_CustomEventBase.cpp similarity index 96% rename from Source/Flow/Private/Nodes/Route/FlowNode_CustomEventBase.cpp rename to Source/Flow/Private/Nodes/Graph/FlowNode_CustomEventBase.cpp index c45a29b5d..4ca81b5ca 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_CustomEventBase.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_CustomEventBase.cpp @@ -1,6 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "Nodes/Route/FlowNode_CustomEventBase.h" +#include "Nodes/Graph/FlowNode_CustomEventBase.h" #include "FlowSettings.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_CustomEventBase) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_CustomInput.cpp similarity index 94% rename from Source/Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp rename to Source/Flow/Private/Nodes/Graph/FlowNode_CustomInput.cpp index 1090a3310..31aac87cc 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_CustomInput.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_CustomInput.cpp @@ -1,6 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "Nodes/Route/FlowNode_CustomInput.h" +#include "Nodes/Graph/FlowNode_CustomInput.h" #include "FlowSettings.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_CustomInput) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_CustomOutput.cpp similarity index 97% rename from Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp rename to Source/Flow/Private/Nodes/Graph/FlowNode_CustomOutput.cpp index 26f647d89..b19912f47 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_CustomOutput.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_CustomOutput.cpp @@ -1,6 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "Nodes/Route/FlowNode_CustomOutput.h" +#include "Nodes/Graph/FlowNode_CustomOutput.h" #include "FlowAsset.h" #include "FlowSettings.h" diff --git a/Source/Flow/Private/Nodes/DataPins/FlowNode_DefineProperties.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_DefineProperties.cpp similarity index 98% rename from Source/Flow/Private/Nodes/DataPins/FlowNode_DefineProperties.cpp rename to Source/Flow/Private/Nodes/Graph/FlowNode_DefineProperties.cpp index 235815ac9..38c3b0acb 100644 --- a/Source/Flow/Private/Nodes/DataPins/FlowNode_DefineProperties.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_DefineProperties.cpp @@ -1,6 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "Nodes/DataPins/FlowNode_DefineProperties.h" +#include "Nodes/Graph/FlowNode_DefineProperties.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_DefineProperties) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_Finish.cpp similarity index 92% rename from Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp rename to Source/Flow/Private/Nodes/Graph/FlowNode_Finish.cpp index 108eeaa7b..e942e3629 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Finish.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_Finish.cpp @@ -1,6 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "Nodes/Route/FlowNode_Finish.h" +#include "Nodes/Graph/FlowNode_Finish.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_Finish) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_Start.cpp similarity index 99% rename from Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp rename to Source/Flow/Private/Nodes/Graph/FlowNode_Start.cpp index e15fe2e29..a75f1fbda 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Start.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_Start.cpp @@ -1,6 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "Nodes/Route/FlowNode_Start.h" +#include "Nodes/Graph/FlowNode_Start.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_Start) diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp similarity index 99% rename from Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp rename to Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp index 403cc1184..6c0e374a0 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_SubGraph.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp @@ -1,6 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "Nodes/Route/FlowNode_SubGraph.h" +#include "Nodes/Graph/FlowNode_SubGraph.h" #include "FlowAsset.h" #include "FlowSettings.h" diff --git a/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalAND.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_LogicalAND.cpp similarity index 92% rename from Source/Flow/Private/Nodes/Operators/FlowNode_LogicalAND.cpp rename to Source/Flow/Private/Nodes/Route/FlowNode_LogicalAND.cpp index d78f05940..94630d48c 100644 --- a/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalAND.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_LogicalAND.cpp @@ -1,6 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "Nodes/Operators/FlowNode_LogicalAND.h" +#include "Nodes/Route/FlowNode_LogicalAND.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_LogicalAND) diff --git a/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_LogicalOR.cpp similarity index 96% rename from Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp rename to Source/Flow/Private/Nodes/Route/FlowNode_LogicalOR.cpp index 5e90ea60a..04f965b74 100644 --- a/Source/Flow/Private/Nodes/Operators/FlowNode_LogicalOR.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_LogicalOR.cpp @@ -1,6 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "Nodes/Operators/FlowNode_LogicalOR.h" +#include "Nodes/Route/FlowNode_LogicalOR.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_LogicalOR) diff --git a/Source/Flow/Public/Nodes/World/FlowNode_ComponentObserver.h b/Source/Flow/Public/Nodes/Actor/FlowNode_ComponentObserver.h similarity index 100% rename from Source/Flow/Public/Nodes/World/FlowNode_ComponentObserver.h rename to Source/Flow/Public/Nodes/Actor/FlowNode_ComponentObserver.h diff --git a/Source/Flow/Public/Nodes/World/FlowNode_ExecuteComponent.h b/Source/Flow/Public/Nodes/Actor/FlowNode_ExecuteComponent.h similarity index 100% rename from Source/Flow/Public/Nodes/World/FlowNode_ExecuteComponent.h rename to Source/Flow/Public/Nodes/Actor/FlowNode_ExecuteComponent.h diff --git a/Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h b/Source/Flow/Public/Nodes/Actor/FlowNode_NotifyActor.h similarity index 100% rename from Source/Flow/Public/Nodes/World/FlowNode_NotifyActor.h rename to Source/Flow/Public/Nodes/Actor/FlowNode_NotifyActor.h diff --git a/Source/Flow/Public/Nodes/World/FlowNode_OnActorRegistered.h b/Source/Flow/Public/Nodes/Actor/FlowNode_OnActorRegistered.h similarity index 91% rename from Source/Flow/Public/Nodes/World/FlowNode_OnActorRegistered.h rename to Source/Flow/Public/Nodes/Actor/FlowNode_OnActorRegistered.h index d4045c667..b7bf05cd4 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_OnActorRegistered.h +++ b/Source/Flow/Public/Nodes/Actor/FlowNode_OnActorRegistered.h @@ -2,7 +2,7 @@ #pragma once -#include "Nodes/World/FlowNode_ComponentObserver.h" +#include "Nodes/Actor/FlowNode_ComponentObserver.h" #include "FlowNode_OnActorRegistered.generated.h" /** diff --git a/Source/Flow/Public/Nodes/World/FlowNode_OnActorUnregistered.h b/Source/Flow/Public/Nodes/Actor/FlowNode_OnActorUnregistered.h similarity index 92% rename from Source/Flow/Public/Nodes/World/FlowNode_OnActorUnregistered.h rename to Source/Flow/Public/Nodes/Actor/FlowNode_OnActorUnregistered.h index 655e98082..38b9199a2 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_OnActorUnregistered.h +++ b/Source/Flow/Public/Nodes/Actor/FlowNode_OnActorUnregistered.h @@ -2,7 +2,7 @@ #pragma once -#include "Nodes/World/FlowNode_ComponentObserver.h" +#include "Nodes/Actor/FlowNode_ComponentObserver.h" #include "FlowNode_OnActorUnregistered.generated.h" /** diff --git a/Source/Flow/Public/Nodes/World/FlowNode_OnNotifyFromActor.h b/Source/Flow/Public/Nodes/Actor/FlowNode_OnNotifyFromActor.h similarity index 95% rename from Source/Flow/Public/Nodes/World/FlowNode_OnNotifyFromActor.h rename to Source/Flow/Public/Nodes/Actor/FlowNode_OnNotifyFromActor.h index b64c4068b..9b9dda87a 100644 --- a/Source/Flow/Public/Nodes/World/FlowNode_OnNotifyFromActor.h +++ b/Source/Flow/Public/Nodes/Actor/FlowNode_OnNotifyFromActor.h @@ -2,7 +2,7 @@ #pragma once -#include "Nodes/World/FlowNode_ComponentObserver.h" +#include "Nodes/Actor/FlowNode_ComponentObserver.h" #include "FlowNode_OnNotifyFromActor.generated.h" /** diff --git a/Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h b/Source/Flow/Public/Nodes/Actor/FlowNode_PlayLevelSequence.h similarity index 100% rename from Source/Flow/Public/Nodes/World/FlowNode_PlayLevelSequence.h rename to Source/Flow/Public/Nodes/Actor/FlowNode_PlayLevelSequence.h diff --git a/Source/Flow/Public/Nodes/Utils/FlowNode_Log.h b/Source/Flow/Public/Nodes/Developer/FlowNode_Log.h similarity index 100% rename from Source/Flow/Public/Nodes/Utils/FlowNode_Log.h rename to Source/Flow/Public/Nodes/Developer/FlowNode_Log.h diff --git a/Source/Flow/Public/Nodes/Utils/FlowNode_Checkpoint.h b/Source/Flow/Public/Nodes/Graph/FlowNode_Checkpoint.h similarity index 100% rename from Source/Flow/Public/Nodes/Utils/FlowNode_Checkpoint.h rename to Source/Flow/Public/Nodes/Graph/FlowNode_Checkpoint.h diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_CustomEventBase.h b/Source/Flow/Public/Nodes/Graph/FlowNode_CustomEventBase.h similarity index 100% rename from Source/Flow/Public/Nodes/Route/FlowNode_CustomEventBase.h rename to Source/Flow/Public/Nodes/Graph/FlowNode_CustomEventBase.h diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_CustomInput.h b/Source/Flow/Public/Nodes/Graph/FlowNode_CustomInput.h similarity index 100% rename from Source/Flow/Public/Nodes/Route/FlowNode_CustomInput.h rename to Source/Flow/Public/Nodes/Graph/FlowNode_CustomInput.h diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_CustomOutput.h b/Source/Flow/Public/Nodes/Graph/FlowNode_CustomOutput.h similarity index 100% rename from Source/Flow/Public/Nodes/Route/FlowNode_CustomOutput.h rename to Source/Flow/Public/Nodes/Graph/FlowNode_CustomOutput.h diff --git a/Source/Flow/Public/Nodes/DataPins/FlowNode_DefineProperties.h b/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h similarity index 100% rename from Source/Flow/Public/Nodes/DataPins/FlowNode_DefineProperties.h rename to Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_Finish.h b/Source/Flow/Public/Nodes/Graph/FlowNode_Finish.h similarity index 100% rename from Source/Flow/Public/Nodes/Route/FlowNode_Finish.h rename to Source/Flow/Public/Nodes/Graph/FlowNode_Finish.h diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_Start.h b/Source/Flow/Public/Nodes/Graph/FlowNode_Start.h similarity index 98% rename from Source/Flow/Public/Nodes/Route/FlowNode_Start.h rename to Source/Flow/Public/Nodes/Graph/FlowNode_Start.h index c24fd8921..d47fd6343 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_Start.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_Start.h @@ -2,9 +2,8 @@ #pragma once -#include "Nodes/DataPins/FlowNode_DefineProperties.h" +#include "Nodes/Graph/FlowNode_DefineProperties.h" #include "Interfaces/FlowNodeWithExternalDataPinSupplierInterface.h" - #include "FlowNode_Start.generated.h" /** diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_SubGraph.h b/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h similarity index 100% rename from Source/Flow/Public/Nodes/Route/FlowNode_SubGraph.h rename to Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h diff --git a/Source/Flow/Public/Nodes/Operators/FlowNode_LogicalAND.h b/Source/Flow/Public/Nodes/Route/FlowNode_LogicalAND.h similarity index 100% rename from Source/Flow/Public/Nodes/Operators/FlowNode_LogicalAND.h rename to Source/Flow/Public/Nodes/Route/FlowNode_LogicalAND.h diff --git a/Source/Flow/Public/Nodes/Operators/FlowNode_LogicalOR.h b/Source/Flow/Public/Nodes/Route/FlowNode_LogicalOR.h similarity index 100% rename from Source/Flow/Public/Nodes/Operators/FlowNode_LogicalOR.h rename to Source/Flow/Public/Nodes/Route/FlowNode_LogicalOR.h diff --git a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp index e36b8f1b8..1e9deecdc 100644 --- a/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp +++ b/Source/FlowEditor/Private/Asset/FlowImportUtils.cpp @@ -10,7 +10,7 @@ #include "FlowAsset.h" #include "Nodes/FlowPin.h" -#include "Nodes/Route/FlowNode_Start.h" +#include "Nodes/Graph/FlowNode_Start.h" #include "AssetRegistry/AssetRegistryModule.h" #include "AssetToolsModule.h" diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowAssetDetails.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowAssetDetails.cpp index bcd475264..0b73d591d 100644 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowAssetDetails.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowAssetDetails.cpp @@ -2,7 +2,7 @@ #include "DetailCustomizations/FlowAssetDetails.h" #include "FlowAsset.h" -#include "Nodes/Route/FlowNode_SubGraph.h" +#include "Nodes/Graph/FlowNode_SubGraph.h" #include "DetailLayoutBuilder.h" #include "IDetailChildrenBuilder.h" diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowNode_ComponentObserverDetails.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_ComponentObserverDetails.cpp index 297fb3afa..68b0a7729 100644 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowNode_ComponentObserverDetails.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_ComponentObserverDetails.cpp @@ -1,7 +1,7 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "DetailCustomizations/FlowNode_ComponentObserverDetails.h" -#include "Nodes/World/FlowNode_ComponentObserver.h" +#include "Nodes/Actor/FlowNode_ComponentObserver.h" #include "DetailCategoryBuilder.h" #include "DetailLayoutBuilder.h" diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomEventBaseDetails.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomEventBaseDetails.cpp index 77295f743..a9743ce13 100644 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomEventBaseDetails.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_CustomEventBaseDetails.cpp @@ -2,7 +2,7 @@ #include "DetailCustomizations/FlowNode_CustomEventBaseDetails.h" #include "FlowAsset.h" -#include "Nodes/Route/FlowNode_CustomEventBase.h" +#include "Nodes/Graph/FlowNode_CustomEventBase.h" #include "DetailCategoryBuilder.h" #include "DetailLayoutBuilder.h" diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowNode_PlayLevelSequenceDetails.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_PlayLevelSequenceDetails.cpp index 76d5b1f4b..d9ee70957 100644 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowNode_PlayLevelSequenceDetails.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_PlayLevelSequenceDetails.cpp @@ -1,7 +1,7 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "DetailCustomizations/FlowNode_PlayLevelSequenceDetails.h" -#include "Nodes/World/FlowNode_PlayLevelSequence.h" +#include "Nodes/Actor/FlowNode_PlayLevelSequence.h" #include "DetailCategoryBuilder.h" #include "DetailLayoutBuilder.h" diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowNode_SubGraphDetails.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_SubGraphDetails.cpp index 1b6cf0f3f..d3b80e4d0 100644 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowNode_SubGraphDetails.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_SubGraphDetails.cpp @@ -4,7 +4,7 @@ #include "DetailLayoutBuilder.h" #include "FlowAsset.h" -#include "Nodes/Route/FlowNode_SubGraph.h" +#include "Nodes/Graph/FlowNode_SubGraph.h" void FFlowNode_SubGraphDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) { diff --git a/Source/FlowEditor/Private/Find/FindInFlow.cpp b/Source/FlowEditor/Private/Find/FindInFlow.cpp index b5c964763..59e4dfdb2 100644 --- a/Source/FlowEditor/Private/Find/FindInFlow.cpp +++ b/Source/FlowEditor/Private/Find/FindInFlow.cpp @@ -8,7 +8,7 @@ #include "FlowAsset.h" #include "Nodes/FlowNode.h" -#include "Nodes/Route/FlowNode_SubGraph.h" +#include "Nodes/Graph/FlowNode_SubGraph.h" #include "EdGraph/EdGraph.h" #include "EdGraph/EdGraphNode.h" diff --git a/Source/FlowEditor/Private/FlowEditorModule.cpp b/Source/FlowEditor/Private/FlowEditorModule.cpp index 8f50a55e0..72682cbee 100644 --- a/Source/FlowEditor/Private/FlowEditorModule.cpp +++ b/Source/FlowEditor/Private/FlowEditorModule.cpp @@ -34,11 +34,11 @@ #include "FlowAsset.h" #include "AddOns/FlowNodeAddOn.h" -#include "Nodes/Route/FlowNode_CustomInput.h" -#include "Nodes/Route/FlowNode_CustomOutput.h" -#include "Nodes/Route/FlowNode_SubGraph.h" -#include "Nodes/World/FlowNode_ComponentObserver.h" -#include "Nodes/World/FlowNode_PlayLevelSequence.h" +#include "Nodes/Actor/FlowNode_ComponentObserver.h" +#include "Nodes/Actor/FlowNode_PlayLevelSequence.h" +#include "Nodes/Graph/FlowNode_CustomInput.h" +#include "Nodes/Graph/FlowNode_CustomOutput.h" +#include "Nodes/Graph/FlowNode_SubGraph.h" #include "AssetToolsModule.h" #include "EdGraphUtilities.h" diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index d2bbd96c1..889a23440 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -8,10 +8,9 @@ #include "Graph/FlowGraphEditorSettings.h" #include "Graph/FlowGraphSchema_Actions.h" #include "Graph/Nodes/FlowGraphNode.h" -#include "Nodes/Route/FlowNode_SubGraph.h" +#include "Nodes/Graph/FlowNode_SubGraph.h" #include "EdGraphUtilities.h" -#include "GraphEditAction.h" #include "Framework/Application/SlateApplication.h" #include "Framework/Commands/GenericCommands.h" #include "GraphEditorActions.h" diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index 480a13d6a..38e268875 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -17,8 +17,8 @@ #include "Nodes/FlowNode.h" #include "Nodes/FlowNodeAddOnBlueprint.h" #include "Nodes/FlowNodeBlueprint.h" -#include "Nodes/Route/FlowNode_CustomInput.h" -#include "Nodes/Route/FlowNode_Start.h" +#include "Nodes/Graph/FlowNode_CustomInput.h" +#include "Nodes/Graph/FlowNode_Start.h" #include "Nodes/Route/FlowNode_Reroute.h" #include "AssetRegistry/AssetRegistryModule.h" diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Finish.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Finish.cpp index cfe7600cf..34ebc8098 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Finish.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Finish.cpp @@ -3,7 +3,7 @@ #include "Graph/Nodes/FlowGraphNode_Finish.h" #include "Graph/Widgets/SFlowGraphNode_Finish.h" -#include "Nodes/Route/FlowNode_Finish.h" +#include "Nodes/Graph/FlowNode_Finish.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraphNode_Finish) diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Start.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Start.cpp index 9169a33b4..63a555b43 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Start.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Start.cpp @@ -3,7 +3,7 @@ #include "Graph/Nodes/FlowGraphNode_Start.h" #include "Graph/Widgets/SFlowGraphNode_Start.h" -#include "Nodes/Route/FlowNode_Start.h" +#include "Nodes/Graph/FlowNode_Start.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraphNode_Start) diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_SubGraph.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_SubGraph.cpp index 85757e71b..26db97ce6 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_SubGraph.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_SubGraph.cpp @@ -3,7 +3,7 @@ #include "Graph/Nodes/FlowGraphNode_SubGraph.h" #include "Graph/Widgets/SFlowGraphNode_SubGraph.h" -#include "Nodes/Route/FlowNode_SubGraph.h" +#include "Nodes/Graph/FlowNode_SubGraph.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraphNode_SubGraph) diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode_SubGraph.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode_SubGraph.cpp index 0c601dc45..81ef46c19 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode_SubGraph.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode_SubGraph.cpp @@ -4,7 +4,7 @@ #include "Graph/FlowGraphEditorSettings.h" #include "FlowAsset.h" -#include "Nodes/Route/FlowNode_SubGraph.h" +#include "Nodes/Graph/FlowNode_SubGraph.h" #include "SGraphPreviewer.h" #include "Widgets/Layout/SBox.h" From 5c7f9220cd682e45c17beafba7cc6482a842caa0 Mon Sep 17 00:00:00 2001 From: MaksymKapelianovych <48297221+MaksymKapelianovych@users.noreply.github.com> Date: Sun, 12 Jan 2025 20:08:41 +0200 Subject: [PATCH 315/485] Several improvements for copy/pasting nodes: (#219) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Several improvements for copy/pasting nodes: 1. UFlowNode_Start now is substituted with UFlowNode_CustomInput when duplicating or copy/pasting. https://github.com/MothCocoon/FlowGraph/issues/205 2. Reset EventName in UFlowNode_CustomInput after duplicating or copy/pasting 3. Fix copy/pasting comments. https://github.com/MothCocoon/FlowGraph/issues/215 4. CanUserDeleteNode and CanDuplicateNode now use FlowNode's CDO if NodeInstance is NULL (always when pasting). This will prevent pasting nodes that cannot be duplicated, but are copied with other duplicable nodes. 5. Improve duplicating and copy/pasting of AddOns (as a reference was taken AIGraphEditor code) - duplicated AddOn is pasted on the same level as selected AddOn - allow pasting AddOns only if there is one SelectedNode - check if SelectedNode for paste can accept AddOn - fix copying AddOns if both AddOn and its ParentNode are selected - fix copying AddOns if there are other GraphNodes selected (not AddOns) * Fix pasting node into FlowAsset when FlowAsset does not allow to have such node * Revert fixes for copy/pasting nodes * Some fixes after merge * Removed unnecessary bool for duplicating * Clean unused imports and code after merging * integrate changes with mainline --------- Co-authored-by: Krzysztof Justyński --- .../Nodes/Graph/FlowNode_CustomInput.cpp | 5 +++++ .../Public/Nodes/Graph/FlowNode_CustomInput.h | 3 +++ .../Private/Graph/FlowGraphEditor.cpp | 19 +++++++++++-------- .../Private/Graph/FlowGraphSchema_Actions.cpp | 2 +- .../Private/Graph/Nodes/FlowGraphNode.cpp | 15 ++++++++++++++- .../Public/Graph/Nodes/FlowGraphNode.h | 1 + 6 files changed, 35 insertions(+), 10 deletions(-) diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_CustomInput.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_CustomInput.cpp index 31aac87cc..79f9e9101 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_CustomInput.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_CustomInput.cpp @@ -18,6 +18,11 @@ void UFlowNode_CustomInput::ExecuteInput(const FName& PinName) TriggerFirstOutput(true); } +void UFlowNode_CustomInput::PostEditImport() +{ + EventName = NAME_None; +} + #if WITH_EDITOR FText UFlowNode_CustomInput::GetNodeTitle() const { diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_CustomInput.h b/Source/Flow/Public/Nodes/Graph/FlowNode_CustomInput.h index 12f6012c0..549a0a0d8 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_CustomInput.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_CustomInput.h @@ -18,6 +18,9 @@ class FLOW_API UFlowNode_CustomInput : public UFlowNode_CustomEventBase protected: virtual void ExecuteInput(const FName& PinName) override; +public: + virtual void PostEditImport() override; + #if WITH_EDITOR public: virtual FText GetNodeTitle() const override; diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index 889a23440..b7626b374 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -531,14 +531,12 @@ bool SFlowGraphEditor::CanDeleteNodes() const { if (const UEdGraphNode* Node = Cast(*NodeIt)) { - if (!Node->CanUserDeleteNode()) + if (Node->CanUserDeleteNode()) { - return false; + return true; } } } - - return SelectedNodes.Num() > 0; } return false; @@ -592,13 +590,18 @@ void SFlowGraphEditor::CopySelectedNodes() const void SFlowGraphEditor::PrepareFlowGraphNodeForCopy(UFlowGraphNode& FlowGraphNode, const int32 ParentEdNodeIndex, FGraphPanelSelectionSet& NewSelectedNodes) { - FlowGraphNode.PrepareForCopying(); + const int32 ThisFlowGraphNodeIndex = NewSelectedNodes.Num(); + bool bAlreadyInSet = false; + NewSelectedNodes.Add(&FlowGraphNode, &bAlreadyInSet); - FlowGraphNode.CopySubNodeParentIndex = ParentEdNodeIndex; + if (bAlreadyInSet) + { + return; + } - const int32 ThisFlowGraphNodeIndex = NewSelectedNodes.Num(); + FlowGraphNode.PrepareForCopying(); + FlowGraphNode.CopySubNodeParentIndex = ParentEdNodeIndex; FlowGraphNode.CopySubNodeIndex = ThisFlowGraphNodeIndex; - NewSelectedNodes.Add(&FlowGraphNode); // append all subnodes for selection for (UFlowGraphNode* SubNode : FlowGraphNode.SubNodes) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp index abd8db3d2..9d73247f5 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp @@ -60,7 +60,7 @@ UFlowGraphNode* FFlowGraphSchemaAction_NewNode::CreateNode(UEdGraph* ParentGraph const TSubclassOf FlowNodeBaseClass = const_cast(NodeClass); const TSubclassOf GraphNodeClass = UFlowGraphSchema::GetAssignedGraphNodeClass(FlowNodeBaseClass); UFlowGraphNode* NewGraphNode = NewObject(ParentGraph, GraphNodeClass, NAME_None, RF_Transactional); - + // register to the graph NewGraphNode->CreateNewGuid(); ParentGraph->AddNode(NewGraphNode, false, bSelectNewNode); diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 8618a79db..a792f3239 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -46,7 +46,9 @@ UFlowGraphNode::UFlowGraphNode(const FObjectInitializer& ObjectInitializer) void UFlowGraphNode::SetNodeTemplate(UFlowNodeBase* InFlowNode) { + ensure(InFlowNode); NodeInstance = InFlowNode; + NodeInstanceClass = InFlowNode->GetClass(); } const UFlowNodeBase* UFlowGraphNode::GetNodeTemplate() const @@ -624,6 +626,17 @@ bool UFlowGraphNode::CanDuplicateNode() const return true; } +bool UFlowGraphNode::CanPasteHere( const UEdGraph* TargetGraph ) const +{ + const UFlowGraph* FlowGraph = Cast(TargetGraph); + if (FlowGraph == nullptr) + { + return false; + } + + return Super::CanPasteHere(TargetGraph) && FlowGraph->GetFlowAsset()->IsNodeOrAddOnClassAllowed(NodeInstanceClass.Get()); +} + TSharedPtr UFlowGraphNode::CreateVisualWidget() { return SNew(SFlowGraphNode, this); @@ -1899,7 +1912,7 @@ bool UFlowGraphNode::CanAcceptSubNodeAsChild(const UFlowGraphNode& SubNodeToCons if (OutReasonString) { - *OutReasonString = FString::Printf(TEXT("%s cannot accept AddOn type %s"), *GetClass()->GetName(), *OtherFlowNodeSubNode->GetClass()->GetName()); + *OutReasonString = FString::Printf(TEXT("%s cannot accept AddOn type %s"), *ThisFlowNodeBase->GetClass()->GetName(), *OtherFlowNodeSubNode->GetClass()->GetName()); } return false; diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h index ff8cc671c..1c2348744 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h @@ -107,6 +107,7 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode virtual void GetNodeContextMenuActions(class UToolMenu* Menu, class UGraphNodeContextMenuContext* Context) const override; virtual bool CanUserDeleteNode() const override; virtual bool CanDuplicateNode() const override; + virtual bool CanPasteHere( const UEdGraph* TargetGraph ) const override; virtual TSharedPtr CreateVisualWidget() override; virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override; virtual FLinearColor GetNodeTitleColor() const override; From ed6466cb2e77317b7329d2a0ea633add79f65c9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sun, 12 Jan 2025 19:10:11 +0100 Subject: [PATCH 316/485] cosmetic update to last PR --- Source/Flow/Private/Nodes/Graph/FlowNode_CustomInput.cpp | 1 + Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_CustomInput.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_CustomInput.cpp index 79f9e9101..f189ce4d4 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_CustomInput.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_CustomInput.cpp @@ -20,6 +20,7 @@ void UFlowNode_CustomInput::ExecuteInput(const FName& PinName) void UFlowNode_CustomInput::PostEditImport() { + // Reset EventName after duplicating or copy/pasting EventName = NAME_None; } diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp index 9d73247f5..abd8db3d2 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp @@ -60,7 +60,7 @@ UFlowGraphNode* FFlowGraphSchemaAction_NewNode::CreateNode(UEdGraph* ParentGraph const TSubclassOf FlowNodeBaseClass = const_cast(NodeClass); const TSubclassOf GraphNodeClass = UFlowGraphSchema::GetAssignedGraphNodeClass(FlowNodeBaseClass); UFlowGraphNode* NewGraphNode = NewObject(ParentGraph, GraphNodeClass, NAME_None, RF_Transactional); - + // register to the graph NewGraphNode->CreateNewGuid(); ParentGraph->AddNode(NewGraphNode, false, bSelectNewNode); From d8338dd741e44fcf73af17f495b4fec141bc0215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Mon, 13 Jan 2025 18:43:46 +0100 Subject: [PATCH 317/485] restored EngineAssetDefinitions --- Source/FlowEditor/FlowEditor.Build.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/FlowEditor/FlowEditor.Build.cs b/Source/FlowEditor/FlowEditor.Build.cs index 4d851cd0b..54954ebbd 100644 --- a/Source/FlowEditor/FlowEditor.Build.cs +++ b/Source/FlowEditor/FlowEditor.Build.cs @@ -18,12 +18,12 @@ public FlowEditor(ReadOnlyTargetRules target) : base(target) "AssetSearch", "EditorSubsystem", "Flow", - "MessageLog", - "AIModule", // For BlueprintNodeHelpers::DescribeProperty (could be copy/pasted out to remove editor-only dependency) + "MessageLog" }); PrivateDependencyModuleNames.AddRange(new[] { + "AIModule", // For BlueprintNodeHelpers::DescribeProperty (could be copy/pasted out to remove editor-only dependency) "ApplicationCore", "AssetDefinition", "AssetTools", @@ -38,6 +38,7 @@ public FlowEditor(ReadOnlyTargetRules target) : base(target) "EditorScriptingUtilities", "EditorStyle", "Engine", + "EngineAssetDefinitions", "GraphEditor", "GameplayTags", "InputCore", From a67425355107037239730f9cf930ef0b4678a5bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Thu, 16 Jan 2025 21:08:13 +0100 Subject: [PATCH 318/485] removed BlueprintReadWrite as blueprint util couldn't manipulate pins Exclude PinFriendlyName from localization #257 --- Source/Flow/Public/Nodes/FlowPin.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Flow/Public/Nodes/FlowPin.h b/Source/Flow/Public/Nodes/FlowPin.h index 567844df3..aa71588f0 100644 --- a/Source/Flow/Public/Nodes/FlowPin.h +++ b/Source/Flow/Public/Nodes/FlowPin.h @@ -20,14 +20,14 @@ struct FLOW_API FFlowPin GENERATED_BODY() // A logical name, used during execution of pin - UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "FlowPin") + UPROPERTY(EditDefaultsOnly, Category = "FlowPin") FName PinName; // An optional Display Name, you can use it to override PinName without the need to update graph connections - UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "FlowPin") + UPROPERTY(EditDefaultsOnly, Category = "FlowPin") FText PinFriendlyName; - UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "FlowPin") + UPROPERTY(EditDefaultsOnly, Category = "FlowPin") FString PinToolTip; protected: From 6379955da3aef80d9d1f69e61cb1b4b1e2b1c511 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Thu, 16 Jan 2025 21:17:16 +0100 Subject: [PATCH 319/485] removed support for UE 5.3 --- .../Private/LevelSequence/FlowLevelSequenceActor.cpp | 4 ---- .../Private/LevelSequence/FlowLevelSequencePlayer.cpp | 10 +--------- Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp | 5 ----- .../FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp | 4 ---- Source/FlowEditor/Private/MovieScene/FlowSection.cpp | 5 ----- 5 files changed, 1 insertion(+), 27 deletions(-) diff --git a/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp b/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp index 2453af928..a750b42d9 100644 --- a/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp +++ b/Source/Flow/Private/LevelSequence/FlowLevelSequenceActor.cpp @@ -23,11 +23,7 @@ void AFlowLevelSequenceActor::GetLifetimeReplicatedProps(TArraySetPlaybackSettings(PlaybackSettings); -#else GetSequencePlayer()->SetPlaybackSettings(PlaybackSettings); -#endif } void AFlowLevelSequenceActor::SetReplicatedLevelSequenceAsset(ULevelSequence* Asset) diff --git a/Source/Flow/Private/LevelSequence/FlowLevelSequencePlayer.cpp b/Source/Flow/Private/LevelSequence/FlowLevelSequencePlayer.cpp index 8fd0f0165..c05b556b6 100644 --- a/Source/Flow/Private/LevelSequence/FlowLevelSequencePlayer.cpp +++ b/Source/Flow/Private/LevelSequence/FlowLevelSequencePlayer.cpp @@ -41,12 +41,8 @@ UFlowLevelSequencePlayer* UFlowLevelSequencePlayer::CreateFlowLevelSequencePlaye FTransform SpawnTransform = FTransform::Identity; { // apply Transform Origin - // https://docs.unrealengine.com/5.0/en-US/creating-level-sequences-with-dynamic-transforms-in-unreal-engine/ -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 4 - if (IsValid(TransformOriginActor)) -#else + // https://dev.epicgames.com/documentation/en-us/unreal-engine/creating-level-sequences-with-dynamic-transforms-in-unreal-engine if (TransformOriginActor->IsValidLowLevel()) -#endif { // moving Level Sequence Actor might allow proper distance-based actor replication in networked games SpawnTransform = TransformOriginActor->GetTransform(); @@ -62,11 +58,7 @@ UFlowLevelSequencePlayer* UFlowLevelSequencePlayer::CreateFlowLevelSequencePlaye Actor->CameraSettings = CameraSettings; // apply Transform Origin to spawned actor -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 4 - if (IsValid(TransformOriginActor)) -#else if (TransformOriginActor->IsValidLowLevel()) -#endif { if (UDefaultLevelSequenceInstanceData* InstanceData = Cast(Actor->DefaultInstanceData)) { diff --git a/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp b/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp index 0adc6f730..ac1672261 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp @@ -48,12 +48,7 @@ FString FFlowGraphUtils::RemovePrefixFromNodeText(const FText& Source) Prefix = FName::NameToDisplayString(Prefix, false); if (SourceString.StartsWith(Prefix)) { -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 4 - SourceString.MidInline(Prefix.Len(), MAX_int32, false); -#else SourceString.MidInline(Prefix.Len(), MAX_int32, EAllowShrinking::No); -#endif - SourceString = SourceString.TrimStart(); } } diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index a792f3239..1d63cd3ea 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -402,11 +402,7 @@ void UFlowGraphNode::RewireOldPinsToNewPins(TArray& InOldPins) OldPin->bOrphanedPin = true; OldPin->bNotConnectable = true; OrphanedOldPins.Add(OldPin); -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 4 - InOldPins.RemoveAt(OldPinIndex, 1, false); -#else InOldPins.RemoveAt(OldPinIndex, 1, EAllowShrinking::No); -#endif } } diff --git a/Source/FlowEditor/Private/MovieScene/FlowSection.cpp b/Source/FlowEditor/Private/MovieScene/FlowSection.cpp index 382203fdd..db37fa34e 100644 --- a/Source/FlowEditor/Private/MovieScene/FlowSection.cpp +++ b/Source/FlowEditor/Private/MovieScene/FlowSection.cpp @@ -12,12 +12,7 @@ #include "Sections/MovieSceneEventSection.h" #include "SequencerSectionPainter.h" #include "SequencerTimeSliderController.h" - -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 4 -#include "CommonMovieSceneTools.h" -#else #include "TimeToPixel.h" -#endif #define LOCTEXT_NAMESPACE "FlowSection" From ca387ca431eefce3154251a9fac1cb90a86717bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Thu, 16 Jan 2025 21:33:18 +0100 Subject: [PATCH 320/485] removed obsolete local var which confused contributors --- Source/Flow/Private/FlowAsset.cpp | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index b9b9ab116..2cde8d0d4 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -247,9 +247,7 @@ bool UFlowAsset::IsFlowNodeClassInAllowedClasses(const UClass& FlowNodeClass, co for (const TSubclassOf AllowedNodeClass : AllowedNodeClasses) { // If a RequiredAncestor is provided, the AllowedNodeClass must be a subclass of the RequiredAncestor - if (AllowedNodeClass && - FlowNodeClass.IsChildOf(AllowedNodeClass) && - (!RequiredAncestor || AllowedNodeClass->IsChildOf(RequiredAncestor))) + if (AllowedNodeClass && FlowNodeClass.IsChildOf(AllowedNodeClass) && (!RequiredAncestor || AllowedNodeClass->IsChildOf(RequiredAncestor))) { bAllowedInAsset = true; @@ -348,7 +346,7 @@ void UFlowAsset::HarvestNodeConnections(UFlowNode* TargetNode) TargetNodes.Add(Pair.Value); } } - + // Remove any invalid nodes for (auto NodeIt = TargetNodes.CreateIterator(); NodeIt; ++NodeIt) { @@ -359,8 +357,6 @@ void UFlowAsset::HarvestNodeConnections(UFlowNode* TargetNode) } } - bool bAnyNodeDirty = false; - for (UFlowNode* FlowNode : TargetNodes) { bool bNodeDirty = false; @@ -369,7 +365,7 @@ void UFlowAsset::HarvestNodeConnections(UFlowNode* TargetNode) const TArray& GraphNodePins = FlowNode->GetGraphNode()->Pins; for (const UEdGraphPin* ThisPin : GraphNodePins) - { + { const bool bIsExecPin = FFlowPin::IsExecPinCategory(ThisPin->PinType.PinCategory); const bool bIsDataPin = FFlowPin::IsDataPinCategory(ThisPin->PinType.PinCategory); const bool bIsOutputPin = (ThisPin->Direction == EGPD_Output); @@ -397,11 +393,8 @@ void UFlowAsset::HarvestNodeConnections(UFlowNode* TargetNode) } // This check exists to ensure that we don't mark graph dirty, if none of connections changed - // Optimization: we need check it only until the first node would be marked dirty, as this already marks Flow Asset package dirty - if (bAnyNodeDirty == false) { const TMap& OldConnections = FlowNode->Connections; - if (FoundConnections.Num() != OldConnections.Num()) { bNodeDirty = true; @@ -427,7 +420,7 @@ void UFlowAsset::HarvestNodeConnections(UFlowNode* TargetNode) } } - if (bNodeDirty || bAnyNodeDirty) + if (bNodeDirty) { FlowNode->SetFlags(RF_Transactional); FlowNode->Modify(); @@ -436,9 +429,6 @@ void UFlowAsset::HarvestNodeConnections(UFlowNode* TargetNode) FlowNode->PostEditChange(); } } - - // NOTE (gtaylor) @mothdoctor, do we need to do anything with bGraphDirty [renamed by @HomerJohnston to bAnyNodeDirty] here? - // It's scope seems like we wanted to do something at this point. } bool UFlowAsset::TryUpdateManagedFlowPinsForNode(UFlowNode& FlowNode) @@ -1041,7 +1031,7 @@ TArray UFlowAsset::GetNodesInExecutionOrder(UFlowNode* FirstIterated } } FoundNodes.Shrink(); - + return FoundNodes; } From b8f49a81dd10e914cfb4167d6c966846e9d950dc Mon Sep 17 00:00:00 2001 From: LindyHopperGT <91915878+LindyHopperGT@users.noreply.github.com> Date: Tue, 21 Jan 2025 09:18:24 -0800 Subject: [PATCH 321/485] Updates for AI Flow GetBlackboardValues (#259) Added additional constructors for FFlowDataPinResult_Enum(), that are used in an AI Flow node: "GetBlackboardValues" Moved the RefreshContextPins() call to before ShouldReconstructNode(), so that it has an opportunity to rebuild the context pins source values prior to testing if the node needs to be rebuilt (which tests the context pin source values vs the pin widgets for differences). --- Source/Flow/Private/Nodes/FlowNodeBase.cpp | 3 +-- Source/Flow/Public/Nodes/FlowPin.h | 23 +++++++++++------- .../Types/FlowDataPinBlueprintLibrary.h | 24 +++++++++++++++++++ Source/Flow/Public/Types/FlowDataPinResults.h | 15 +++++++++++- ...FlowDataPinProperty_ClassCustomization.cpp | 3 ++- .../Private/Graph/Nodes/FlowGraphNode.cpp | 3 ++- 6 files changed, 58 insertions(+), 13 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp index e235d15c5..fe646b4b0 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBase.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -784,8 +784,7 @@ FString UFlowNodeBase::GetNodeDescription() const { return K2_GetNodeDescription(); } - -#endif // WITH_EDITOR +#endif void UFlowNodeBase::SetNodeConfigText(const FText& NodeConfigText) { diff --git a/Source/Flow/Public/Nodes/FlowPin.h b/Source/Flow/Public/Nodes/FlowPin.h index aa71588f0..e97ce39e1 100644 --- a/Source/Flow/Public/Nodes/FlowPin.h +++ b/Source/Flow/Public/Nodes/FlowPin.h @@ -20,19 +20,19 @@ struct FLOW_API FFlowPin GENERATED_BODY() // A logical name, used during execution of pin - UPROPERTY(EditDefaultsOnly, Category = "FlowPin") + UPROPERTY(EditDefaultsOnly, Category = FlowPin) FName PinName; // An optional Display Name, you can use it to override PinName without the need to update graph connections - UPROPERTY(EditDefaultsOnly, Category = "FlowPin") + UPROPERTY(EditDefaultsOnly, Category = FlowPin) FText PinFriendlyName; - UPROPERTY(EditDefaultsOnly, Category = "FlowPin") + UPROPERTY(EditDefaultsOnly, Category = FlowPin) FString PinToolTip; protected: // PinType (implies PinCategory) - UPROPERTY(EditAnywhere, Category = "FlowPin") + UPROPERTY(EditAnywhere, Category = FlowPin) EFlowPinType PinType = EFlowPinType::Exec; // Sub-category object @@ -43,25 +43,25 @@ struct FLOW_API FFlowPin #if WITH_EDITORONLY_DATA // Filter for limiting the compatible classes for this data pin. // This property is editor-only, but it is automatically copied into PinSubCategoryObject if the PinType matches (for runtime use). - UPROPERTY(EditAnywhere, Category = "FlowPin", meta = (EditCondition = "PinType == EFlowPinType::Class", EditConditionHides)) + UPROPERTY(EditAnywhere, Category = FlowPin, meta = (EditCondition = "PinType == EFlowPinType::Class", EditConditionHides)) TSubclassOf SubCategoryClassFilter = UClass::StaticClass(); // Filter for limiting the compatible object types for this data pin. // This property is editor-only, but it is automatically copied into PinSubCategoryObject if the PinType matches (for runtime use). - UPROPERTY(EditAnywhere, Category = "FlowPin", meta = (EditCondition = "PinType == EFlowPinType::Object", EditConditionHides)) + UPROPERTY(EditAnywhere, Category = FlowPin, meta = (EditCondition = "PinType == EFlowPinType::Object", EditConditionHides)) TSubclassOf SubCategoryObjectFilter = UObject::StaticClass(); // Configuration option for setting the EnumClass to a Blueprint Enum // (C++ enums must bind by name using SubCategoryEnumName, due to a limitation with UE's UEnum discovery). // This property is editor-only, but it is automatically copied into PinSubCategoryObject if the PinType matches (for runtime use). - UPROPERTY(EditAnywhere, Category = "FlowPin", meta = (EditCondition = "PinType == EFlowPinType::Enum", EditConditionHides)) + UPROPERTY(EditAnywhere, Category = FlowPin, meta = (EditCondition = "PinType == EFlowPinType::Enum", EditConditionHides)) TObjectPtr SubCategoryEnumClass = nullptr; // name of enum defined in c++ code, will take priority over asset from EnumType property // (this is a work-around because EnumClass cannot find C++ Enums, // so you need to type the name of the enum in here, manually) // See also: FFlowPin::PostEditChangedEnumName() - UPROPERTY(EditAnywhere, Category = "FlowPin", meta = (EditCondition = "PinType == EFlowPinType::Enum", EditConditionHides)) + UPROPERTY(EditAnywhere, Category = FlowPin, meta = (EditCondition = "PinType == EFlowPinType::Enum", EditConditionHides)) FString SubCategoryEnumName; #endif // WITH_EDITORONLY_DATA @@ -145,6 +145,13 @@ struct FLOW_API FFlowPin { } + FFlowPin(const FName& InPinName, const FText& InPinFriendlyName, const FString& InPinTooltip) + : PinName(InPinName) + , PinFriendlyName(InPinFriendlyName) + , PinToolTip(InPinTooltip) + { + } + FFlowPin(const FName& InPinName, const FText& InPinFriendlyName, EFlowPinType InFlowPinType, UObject* SubCategoryObject = nullptr) : PinName(InPinName) , PinFriendlyName(InPinFriendlyName) diff --git a/Source/Flow/Public/Types/FlowDataPinBlueprintLibrary.h b/Source/Flow/Public/Types/FlowDataPinBlueprintLibrary.h index 250635f10..f2838fc3e 100644 --- a/Source/Flow/Public/Types/FlowDataPinBlueprintLibrary.h +++ b/Source/Flow/Public/Types/FlowDataPinBlueprintLibrary.h @@ -15,6 +15,30 @@ class UFlowDataPinBlueprintLibrary : public UBlueprintFunctionLibrary public: + UFUNCTION(BlueprintPure, Category = FlowPin, Meta = (BlueprintThreadSafe, DisplayName = "Make Flow Pin")) + static + UPARAM(DisplayName = "Flow Pin") FFlowPin + MakeStruct( + FName PinName, + FText PinFriendlyName, + FString PinToolTip) + { + return FFlowPin(PinName, PinFriendlyName, PinToolTip); + } + + UFUNCTION(BlueprintPure, Category = FlowPin, Meta = (BlueprintThreadSafe, DisplayName = "Break Flow Pin")) + static void + BreakStruct( + UPARAM(DisplayName = "Flow Pin") FFlowPin Ref, + FName& OutPinName, + FText& OutPinFriendlyName, + FString& OutPinToolTip) + { + OutPinName = Ref.PinName; + OutPinFriendlyName = Ref.PinFriendlyName; + OutPinToolTip = Ref.PinToolTip; + } + // Recommend implementing AutoConvert_FlowDataPinProperty... for every EFlowPinType FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); diff --git a/Source/Flow/Public/Types/FlowDataPinResults.h b/Source/Flow/Public/Types/FlowDataPinResults.h index 26c114e1f..0d2e7f61e 100644 --- a/Source/Flow/Public/Types/FlowDataPinResults.h +++ b/Source/Flow/Public/Types/FlowDataPinResults.h @@ -32,7 +32,7 @@ struct FFlowDataPinResult public: FLOW_API FFlowDataPinResult() { } - FLOW_API FFlowDataPinResult(EFlowDataPinResolveResult InResult) : Result(InResult) { } + FLOW_API explicit FFlowDataPinResult(EFlowDataPinResolveResult InResult) : Result(InResult) { } }; // Recommend implementing FFlowDataPinResult... for every EFlowPinType @@ -187,6 +187,19 @@ struct FFlowDataPinResult_Enum : public FFlowDataPinResult , Value(InValue) , EnumClass(InEnumClass) { } + FLOW_API explicit FFlowDataPinResult_Enum(EFlowDataPinResolveResult InResult) : Super(InResult) { } + FLOW_API explicit FFlowDataPinResult_Enum(uint8 InEnumAsIntValue, UEnum& InEnumClass) + : Super(EFlowDataPinResolveResult::Success) + , Value() + , EnumClass(&InEnumClass) + { + const int32 EnumValueAsIndex = EnumClass->GetIndexByValue(InEnumAsIntValue); + const FText DisplayValueText = EnumClass->GetDisplayNameTextByIndex(EnumValueAsIndex); + const FName EnumValue = FName(DisplayValueText.ToString()); + + Value = EnumValue; + Result = EFlowDataPinResolveResult::Success; + } template static FFlowDataPinResult_Enum BuildResultFromNativeEnumValue(TUnrealNativeEnumType EnumValue) diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinProperty_ClassCustomization.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinProperty_ClassCustomization.cpp index 13212f888..30f1f7756 100644 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinProperty_ClassCustomization.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinProperty_ClassCustomization.cpp @@ -164,7 +164,8 @@ const UClass* FFlowDataPinProperty_ClassCustomizationBase::OnGetClass() const UClass* FFlowDataPinProperty_ClassCustomizationBase::BuildMetaClass() const { - return CachedMetaClassPtr.Get(); + UClass* MetaClass = CachedMetaClassPtr.Get(); + return MetaClass ? MetaClass : UObject::StaticClass(); } void FFlowDataPinProperty_ClassCustomizationBase::OnSetClass(const UClass* NewClass) diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 1d63cd3ea..4bb6febee 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -286,6 +286,8 @@ void UFlowGraphNode::InsertNewNode(UEdGraphPin* FromPin, UEdGraphPin* NewLinkPin void UFlowGraphNode::ReconstructNode() { + RefreshContextPins(); + if (!ShouldReconstructNode()) { // This ensures the graph editor 'Refresh' button still rebuilds all of the graph widgets even if the FlowGraphNode has nothing to update. @@ -311,7 +313,6 @@ void UFlowGraphNode::ReconstructNode() InputPins.Reset(); OutputPins.Reset(); - RefreshContextPins(); AllocateDefaultPins(); RewireOldPinsToNewPins(OldPins); From a036b05a868ed090ce63e4278c327549f8bafaa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Tue, 21 Jan 2025 18:30:04 +0100 Subject: [PATCH 322/485] added meta specifier to force using custom methods suggested by @MaksymKapelianovych --- Source/Flow/Public/Nodes/FlowPin.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Flow/Public/Nodes/FlowPin.h b/Source/Flow/Public/Nodes/FlowPin.h index e97ce39e1..c5e9bf2bd 100644 --- a/Source/Flow/Public/Nodes/FlowPin.h +++ b/Source/Flow/Public/Nodes/FlowPin.h @@ -14,7 +14,7 @@ class UClass; class UObject; class IPropertyHandle; -USTRUCT(BlueprintType) +USTRUCT(BlueprintType, meta = (HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStruct", HasNativeBreak = "/Script/Flow.FlowDataPinBlueprintLibrary.BreakStruct")) struct FLOW_API FFlowPin { GENERATED_BODY() From 5d38f54c2f2b7e5e1b31bcb45bf9b538f1bd4cf5 Mon Sep 17 00:00:00 2001 From: MaksymKapelianovych <48297221+MaksymKapelianovych@users.noreply.github.com> Date: Sat, 1 Feb 2025 14:34:21 +0200 Subject: [PATCH 323/485] Small addition for custom events to help find them in a large graphs. (#258) * Added button to select custom event node in graph * Fix code style --- Source/Flow/Private/FlowAsset.cpp | 9 +- Source/Flow/Public/FlowAsset.h | 1 + .../DetailCustomizations/FlowAssetDetails.cpp | 86 ++++++++++++++++++- .../DetailCustomizations/FlowAssetDetails.h | 12 +++ 4 files changed, 102 insertions(+), 6 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 2cde8d0d4..f931a14c2 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -956,11 +956,14 @@ void UFlowAsset::RemoveCustomOutput(const FName& EventName) UFlowNode_CustomInput* UFlowAsset::TryFindCustomInputNodeByEventName(const FName& EventName) const { - for (UFlowNode_CustomInput* InputNode : CustomInputNodes) + for (const TPair& Node : ObjectPtrDecay(Nodes)) { - if (IsValid(InputNode) && InputNode->GetEventName() == EventName) + if (UFlowNode_CustomInput* CustomInput = Cast(Node.Value)) { - return InputNode; + if (CustomInput->GetEventName() == EventName) + { + return CustomInput; + } } } diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 8dce99a67..e856cf38a 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -349,6 +349,7 @@ class FLOW_API UFlowAsset : public UObject TMap, TWeakObjectPtr> ActiveSubGraphs; // Optional entry points to the graph, similar to blueprint Custom Events + // Contains nodes only if it is initialized instance (see InitializeInstance, IsInstanceInitialized), empty otherwise UPROPERTY() TSet> CustomInputNodes; diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowAssetDetails.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowAssetDetails.cpp index 0b73d591d..415d2d6eb 100644 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowAssetDetails.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowAssetDetails.cpp @@ -7,17 +7,29 @@ #include "DetailLayoutBuilder.h" #include "IDetailChildrenBuilder.h" #include "PropertyCustomizationHelpers.h" + +#include "Graph/FlowGraphEditor.h" +#include "Graph/FlowGraphUtils.h" + +#include "Nodes/Graph/FlowNode_CustomInput.h" +#include "Nodes/Graph/FlowNode_CustomOutput.h" + #include "Widgets/Input/SEditableTextBox.h" +#include "Widgets/SBoxPanel.h" #define LOCTEXT_NAMESPACE "FlowAssetDetails" void FFlowAssetDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) { + DetailBuilder.GetObjectsBeingCustomized(ObjectsBeingEdited); + IDetailCategoryBuilder& FlowAssetCategory = DetailBuilder.EditCategory("SubGraph", LOCTEXT("SubGraphCategory", "Sub Graph")); TArray> ArrayPropertyHandles; - ArrayPropertyHandles.Add(DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UFlowAsset, CustomInputs))); - ArrayPropertyHandles.Add(DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UFlowAsset, CustomOutputs))); + CustomInputsHandle = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UFlowAsset, CustomInputs)); + CustomOutputsHandle = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UFlowAsset, CustomOutputs)); + ArrayPropertyHandles.Add(CustomInputsHandle); + ArrayPropertyHandles.Add(CustomOutputsHandle); for (const TSharedPtr& PropertyHandle : ArrayPropertyHandles) { if (PropertyHandle.IsValid() && PropertyHandle->AsArray().IsValid()) @@ -39,10 +51,29 @@ void FFlowAssetDetails::GenerateCustomPinArray(TSharedRef Prope PropertyRow.CustomWidget(false) .ValueContent() [ - SNew(SEditableTextBox) + SNew(SHorizontalBox) + + + SHorizontalBox::Slot() + .FillWidth(1.f) + .Padding(2.f, 0.f) + .VAlign(VAlign_Center) + [ + SNew(SEditableTextBox) .Text(this, &FFlowAssetDetails::GetCustomPinText, PropertyHandle) .OnTextCommitted_Static(&FFlowAssetDetails::OnCustomPinTextCommitted, PropertyHandle) .OnVerifyTextChanged_Static(&FFlowAssetDetails::VerifyNewCustomPinText) + ] + + + SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Center) + [ + PropertyCustomizationHelpers::MakeBrowseButton( + FSimpleDelegate::CreateRaw(this, &FFlowAssetDetails::OnBrowseClicked, PropertyHandle), + LOCTEXT("SelectEventNode", "Select Event Node in Graph"), + TAttribute::CreateRaw(this, &FFlowAssetDetails::IsBrowseEnabled, PropertyHandle), + true) // intentionally true, to set "correct" icon + ] ]; } @@ -73,4 +104,53 @@ bool FFlowAssetDetails::VerifyNewCustomPinText(const FText& InNewText, FText& Ou return true; } +void FFlowAssetDetails::OnBrowseClicked(TSharedRef PropertyHandle) +{ + ensure(ObjectsBeingEdited[0].IsValid()); + + UFlowAsset* Asset = Cast(ObjectsBeingEdited[0]); + UFlowNode_CustomEventBase* EventNode = GetCustomEventNode(PropertyHandle); + + if (EventNode) + { + TSharedPtr Editor = FFlowGraphUtils::GetFlowGraphEditor(Asset->GetGraph()); + Editor->ClearSelectionSet(); + Editor->SelectSingleNode(EventNode->GetGraphNode()); + Editor->ZoomToFit(true); + } +} + +bool FFlowAssetDetails::IsBrowseEnabled(TSharedRef PropertyHandle) const +{ + return GetCustomEventNode(PropertyHandle) != nullptr; +} + +UFlowNode_CustomEventBase* FFlowAssetDetails::GetCustomEventNode(TSharedRef PropertyHandle) const +{ + ensure(ObjectsBeingEdited[0].IsValid()); + + UFlowAsset* Asset = Cast(ObjectsBeingEdited[0]); + FName Text = FName(GetCustomPinText( PropertyHandle ).ToString()); + TSharedPtr ArrayHandle = PropertyHandle->GetParentHandle(); + + if (ArrayHandle->IsSamePropertyNode(CustomInputsHandle)) + { + UFlowNode_CustomInput* Input = Asset->TryFindCustomInputNodeByEventName(Text); + if (Input) + { + return Input; + } + } + else if (ArrayHandle->IsSamePropertyNode(CustomOutputsHandle)) + { + UFlowNode_CustomOutput* Output = Asset->TryFindCustomOutputNodeByEventName(Text); + if (Output) + { + return Output; + } + } + + return nullptr; +} + #undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowAssetDetails.h b/Source/FlowEditor/Public/DetailCustomizations/FlowAssetDetails.h index 016151dd1..56894f30a 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowAssetDetails.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowAssetDetails.h @@ -6,6 +6,8 @@ #include "Templates/SharedPointer.h" #include "Types/SlateEnums.h" +class UFlowNode_CustomEventBase; +class UFlowAsset; class IDetailChildrenBuilder; class IDetailLayoutBuilder; class IPropertyHandle; @@ -28,4 +30,14 @@ class FFlowAssetDetails final : public IDetailCustomization FText GetCustomPinText(TSharedRef PropertyHandle) const; static void OnCustomPinTextCommitted(const FText& InText, ETextCommit::Type InCommitType, TSharedRef PropertyHandle); static bool VerifyNewCustomPinText(const FText& InNewText, FText& OutErrorMessage); + + void OnBrowseClicked(TSharedRef PropertyHandle); + bool IsBrowseEnabled(TSharedRef PropertyHandle) const; + UFlowNode_CustomEventBase* GetCustomEventNode(TSharedRef PropertyHandle) const; + + TArray> ObjectsBeingEdited; + + TSharedPtr CustomInputsHandle; + TSharedPtr CustomOutputsHandle; + }; From a0854a221759971fc5b3e4ac547f9e1bed0b6ced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sat, 1 Feb 2025 15:41:51 +0100 Subject: [PATCH 324/485] renamed debugger class to avoid future confusion with naming other debugger subsystems Need for rename came up while reviewing this PR https://github.com/MothCocoon/FlowGraph/pull/199#issuecomment-2628978406 --- ...ystem.cpp => FlowDebugEditorSubsystem.cpp} | 34 +++++++++---------- .../Private/Graph/FlowGraphEditor.cpp | 4 +-- .../Private/Graph/Nodes/FlowGraphNode.cpp | 4 +-- ...Subsystem.h => FlowDebugEditorSubsystem.h} | 16 ++++----- 4 files changed, 29 insertions(+), 29 deletions(-) rename Source/FlowEditor/Private/Asset/{FlowDebuggerSubsystem.cpp => FlowDebugEditorSubsystem.cpp} (72%) rename Source/FlowEditor/Public/Asset/{FlowDebuggerSubsystem.h => FlowDebugEditorSubsystem.h} (81%) diff --git a/Source/FlowEditor/Private/Asset/FlowDebuggerSubsystem.cpp b/Source/FlowEditor/Private/Asset/FlowDebugEditorSubsystem.cpp similarity index 72% rename from Source/FlowEditor/Private/Asset/FlowDebuggerSubsystem.cpp rename to Source/FlowEditor/Private/Asset/FlowDebugEditorSubsystem.cpp index ee26f51d1..5f0581c61 100644 --- a/Source/FlowEditor/Private/Asset/FlowDebuggerSubsystem.cpp +++ b/Source/FlowEditor/Private/Asset/FlowDebugEditorSubsystem.cpp @@ -1,6 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "Asset/FlowDebuggerSubsystem.h" +#include "Asset/FlowDebugEditorSubsystem.h" #include "Asset/FlowAssetEditor.h" #include "Asset/FlowMessageLogListing.h" @@ -14,34 +14,34 @@ #include "UnrealEdGlobals.h" #include "Widgets/Notifications/SNotificationList.h" -#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowDebuggerSubsystem) +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowDebugEditorSubsystem) -#define LOCTEXT_NAMESPACE "FlowDebuggerSubsystem" +#define LOCTEXT_NAMESPACE "FlowDebugEditorSubsystem" -UFlowDebuggerSubsystem::UFlowDebuggerSubsystem() +UFlowDebugEditorSubsystem::UFlowDebugEditorSubsystem() { - FEditorDelegates::BeginPIE.AddUObject(this, &UFlowDebuggerSubsystem::OnBeginPIE); - FEditorDelegates::EndPIE.AddUObject(this, &UFlowDebuggerSubsystem::OnEndPIE); + FEditorDelegates::BeginPIE.AddUObject(this, &UFlowDebugEditorSubsystem::OnBeginPIE); + FEditorDelegates::EndPIE.AddUObject(this, &UFlowDebugEditorSubsystem::OnEndPIE); - UFlowSubsystem::OnInstancedTemplateAdded.BindUObject(this, &UFlowDebuggerSubsystem::OnInstancedTemplateAdded); - UFlowSubsystem::OnInstancedTemplateRemoved.BindUObject(this, &UFlowDebuggerSubsystem::OnInstancedTemplateRemoved); + UFlowSubsystem::OnInstancedTemplateAdded.BindUObject(this, &UFlowDebugEditorSubsystem::OnInstancedTemplateAdded); + UFlowSubsystem::OnInstancedTemplateRemoved.BindUObject(this, &UFlowDebugEditorSubsystem::OnInstancedTemplateRemoved); } -void UFlowDebuggerSubsystem::OnInstancedTemplateAdded(UFlowAsset* FlowAsset) +void UFlowDebugEditorSubsystem::OnInstancedTemplateAdded(UFlowAsset* FlowAsset) { if (!RuntimeLogs.Contains(FlowAsset)) { RuntimeLogs.Add(FlowAsset, FFlowMessageLogListing::GetLogListing(FlowAsset, EFlowLogType::Runtime)); - FlowAsset->OnRuntimeMessageAdded().AddUObject(this, &UFlowDebuggerSubsystem::OnRuntimeMessageAdded); + FlowAsset->OnRuntimeMessageAdded().AddUObject(this, &UFlowDebugEditorSubsystem::OnRuntimeMessageAdded); } } -void UFlowDebuggerSubsystem::OnInstancedTemplateRemoved(UFlowAsset* FlowAsset) const +void UFlowDebugEditorSubsystem::OnInstancedTemplateRemoved(UFlowAsset* FlowAsset) const { FlowAsset->OnRuntimeMessageAdded().RemoveAll(this); } -void UFlowDebuggerSubsystem::OnRuntimeMessageAdded(const UFlowAsset* FlowAsset, const TSharedRef& Message) const +void UFlowDebugEditorSubsystem::OnRuntimeMessageAdded(const UFlowAsset* FlowAsset, const TSharedRef& Message) const { const TSharedPtr Log = RuntimeLogs.FindRef(FlowAsset); if (Log.IsValid()) @@ -51,13 +51,13 @@ void UFlowDebuggerSubsystem::OnRuntimeMessageAdded(const UFlowAsset* FlowAsset, } } -void UFlowDebuggerSubsystem::OnBeginPIE(const bool bIsSimulating) +void UFlowDebugEditorSubsystem::OnBeginPIE(const bool bIsSimulating) { // clear all logs from a previous session RuntimeLogs.Empty(); } -void UFlowDebuggerSubsystem::OnEndPIE(const bool bIsSimulating) +void UFlowDebugEditorSubsystem::OnEndPIE(const bool bIsSimulating) { for (const TPair, TSharedPtr>& Log : RuntimeLogs) { @@ -65,7 +65,7 @@ void UFlowDebuggerSubsystem::OnEndPIE(const bool bIsSimulating) { FNotificationInfo Info{FText::FromString(TEXT("Flow Graph reported in-game issues"))}; Info.ExpireDuration = 15.0; - + Info.HyperlinkText = FText::Format(LOCTEXT("OpenFlowAssetHyperlink", "Open {0}"), FText::FromString(Log.Key->GetName())); Info.Hyperlink = FSimpleDelegate::CreateLambda([this, Log]() { @@ -107,7 +107,7 @@ bool AreAllGameWorldPaused() return bPaused; } -void UFlowDebuggerSubsystem::PausePlaySession() +void UFlowDebugEditorSubsystem::PausePlaySession() { bool bPaused = false; ForEachGameWorld([&](UWorld* World) @@ -124,7 +124,7 @@ void UFlowDebuggerSubsystem::PausePlaySession() } } -bool UFlowDebuggerSubsystem::IsPlaySessionPaused() +bool UFlowDebugEditorSubsystem::IsPlaySessionPaused() { return AreAllGameWorldPaused(); } diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index b7626b374..0babe29ed 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -3,7 +3,7 @@ #include "Graph/FlowGraphEditor.h" #include "Asset/FlowAssetEditor.h" -#include "Asset/FlowDebuggerSubsystem.h" +#include "Asset/FlowDebugEditorSubsystem.h" #include "FlowEditorCommands.h" #include "Graph/FlowGraphEditorSettings.h" #include "Graph/FlowGraphSchema_Actions.h" @@ -258,7 +258,7 @@ FGraphAppearanceInfo SFlowGraphEditor::GetGraphAppearanceInfo() const FGraphAppearanceInfo AppearanceInfo; AppearanceInfo.CornerText = GetCornerText(); - if (UFlowDebuggerSubsystem::IsPlaySessionPaused()) + if (UFlowDebugEditorSubsystem::IsPlaySessionPaused()) { AppearanceInfo.PIENotifyText = LOCTEXT("PausedLabel", "PAUSED"); } diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 4bb6febee..d24aab90b 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -3,7 +3,7 @@ #include "Graph/Nodes/FlowGraphNode.h" #include "AddOns/FlowNodeAddOn.h" -#include "Asset/FlowDebuggerSubsystem.h" +#include "Asset/FlowDebugEditorSubsystem.h" #include "FlowEditorCommands.h" #include "Graph/FlowGraph.h" #include "Graph/FlowGraphEditorSettings.h" @@ -1169,7 +1169,7 @@ void UFlowGraphNode::TryPausingSession(bool bPauseSession) FEditorDelegates::ResumePIE.AddUObject(this, &UFlowGraphNode::OnResumePIE); FEditorDelegates::EndPIE.AddUObject(this, &UFlowGraphNode::OnEndPIE); - UFlowDebuggerSubsystem::PausePlaySession(); + UFlowDebugEditorSubsystem::PausePlaySession(); } } diff --git a/Source/FlowEditor/Public/Asset/FlowDebuggerSubsystem.h b/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h similarity index 81% rename from Source/FlowEditor/Public/Asset/FlowDebuggerSubsystem.h rename to Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h index 391bc876f..0f9c3d6c7 100644 --- a/Source/FlowEditor/Public/Asset/FlowDebuggerSubsystem.h +++ b/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h @@ -4,7 +4,7 @@ #include "EditorSubsystem.h" #include "Logging/TokenizedMessage.h" -#include "FlowDebuggerSubsystem.generated.h" +#include "FlowDebugEditorSubsystem.generated.h" class UFlowAsset; class FFlowMessageLog; @@ -13,25 +13,25 @@ class FFlowMessageLog; ** Persistent subsystem supporting Flow Graph debugging */ UCLASS() -class FLOWEDITOR_API UFlowDebuggerSubsystem : public UEditorSubsystem +class FLOWEDITOR_API UFlowDebugEditorSubsystem : public UEditorSubsystem { GENERATED_BODY() - + public: - UFlowDebuggerSubsystem(); + UFlowDebugEditorSubsystem(); -protected: +protected: TMap, TSharedPtr> RuntimeLogs; void OnInstancedTemplateAdded(UFlowAsset* FlowAsset); void OnInstancedTemplateRemoved(UFlowAsset* FlowAsset) const; - + void OnRuntimeMessageAdded(const UFlowAsset* FlowAsset, const TSharedRef& Message) const; - + void OnBeginPIE(const bool bIsSimulating); void OnEndPIE(const bool bIsSimulating); -public: +public: static void PausePlaySession(); static bool IsPlaySessionPaused(); }; From 49f994d5e3ed089eb4dd37ae57ffda2407b1b33c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Mon, 10 Feb 2025 19:53:04 +0100 Subject: [PATCH 325/485] Removed a deprecated Call Owner Function feature --- .../World/FlowNode_CallOwnerFunction.cpp | 417 ------------------ .../Private/Types/FlowOwnerFunctionParams.cpp | 57 --- .../Private/Types/FlowOwnerFunctionRef.cpp | 84 ---- .../FlowNativeExecutableInterface.h | 24 - .../Nodes/World/FlowNode_CallOwnerFunction.h | 80 ---- .../Public/Types/FlowOwnerFunctionParams.h | 78 ---- .../Flow/Public/Types/FlowOwnerFunctionRef.h | 57 --- .../FlowOwnerFunctionRefCustomization.cpp | 161 ------- .../FlowEditor/Private/FlowEditorModule.cpp | 2 - .../FlowOwnerFunctionRefCustomization.h | 51 --- 10 files changed, 1011 deletions(-) delete mode 100644 Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp delete mode 100644 Source/Flow/Private/Types/FlowOwnerFunctionParams.cpp delete mode 100644 Source/Flow/Private/Types/FlowOwnerFunctionRef.cpp delete mode 100644 Source/Flow/Public/Interfaces/FlowNativeExecutableInterface.h delete mode 100644 Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h delete mode 100644 Source/Flow/Public/Types/FlowOwnerFunctionParams.h delete mode 100644 Source/Flow/Public/Types/FlowOwnerFunctionRef.h delete mode 100644 Source/FlowEditor/Private/DetailCustomizations/FlowOwnerFunctionRefCustomization.cpp delete mode 100644 Source/FlowEditor/Public/DetailCustomizations/FlowOwnerFunctionRefCustomization.h diff --git a/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp b/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp deleted file mode 100644 index 1670a4876..000000000 --- a/Source/Flow/Private/Nodes/World/FlowNode_CallOwnerFunction.cpp +++ /dev/null @@ -1,417 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#include "Nodes/World/FlowNode_CallOwnerFunction.h" - -#include "FlowAsset.h" -#include "FlowLogChannels.h" -#include "Interfaces/FlowOwnerInterface.h" -#include "Types/FlowOwnerFunctionParams.h" -#include "FlowSettings.h" - -#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_CallOwnerFunction) - -#define LOCTEXT_NAMESPACE "FlowNode" - -UFlowNode_CallOwnerFunction::UFlowNode_CallOwnerFunction(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) - , Params(nullptr) -{ -#if WITH_EDITOR - NodeDisplayStyle = FlowNodeStyle::Deprecated; - Category = TEXT("Deprecated"); -#endif // WITH_EDITOR -} - -void UFlowNode_CallOwnerFunction::ExecuteInput(const FName& PinName) -{ - Super::ExecuteInput(PinName); - - if (!IsValid(Params)) - { - UE_LOG(LogFlow, Error, TEXT("Expected a valid Params object")); - - return; - } - - IFlowOwnerInterface* FlowOwnerInterface = GetFlowOwnerInterface(); - if (!FlowOwnerInterface) - { - UE_LOG(LogFlow, Error, TEXT("Expected an owner that implements the IFlowOwnerInterface")); - - return; - } - - const UObject* FlowOwnerObject = CastChecked(FlowOwnerInterface); - const UClass* FlowOwnerClass = FlowOwnerObject->GetClass(); - check(IsValid(FlowOwnerClass)); - - if (!FunctionRef.TryResolveFunction(*FlowOwnerClass)) - { - UE_LOG( - LogFlow, - Error, - TEXT("Could not resolve function named %s with flow owner class %s"), - *FunctionRef.GetFunctionName().ToString(), - *FlowOwnerClass->GetName()); - - return; - } - - Params->PreExecute(*this, PinName); - - const FName ResultOutputName = FunctionRef.CallFunction(*FlowOwnerInterface, *Params); - - Params->PostExecute(); - - (void) TryExecuteOutputPin(ResultOutputName); -} - -bool UFlowNode_CallOwnerFunction::TryExecuteOutputPin(const FName& OutputName) -{ - if (OutputName.IsNone()) - { - return false; - } - - const bool bFinish = ShouldFinishForOutputName(OutputName); - TriggerOutput(OutputName, bFinish); - - return true; -} - -bool UFlowNode_CallOwnerFunction::ShouldFinishForOutputName(const FName& OutputName) const -{ - if (ensure(IsValid(Params))) - { - return Params->ShouldFinishForOutputName(OutputName); - } - - return true; -} - -#if WITH_EDITOR - -void UFlowNode_CallOwnerFunction::PostLoad() -{ - Super::PostLoad(); - - FObjectPropertyBase* ParamsProperty = FindFProperty(GetClass(), GET_MEMBER_NAME_CHECKED(UFlowNode_CallOwnerFunction, Params)); - check(ParamsProperty); - - // NOTE (gtaylor) This fixes corruption in FlowNodes that could have been caused with - // a previous version of the code (which was inadvisedly calling SetPropertyClass) - // to restore the correct PropertyClass for this node. - // (it could be removed in a future release, once all assets have been updated) - if (ParamsProperty->PropertyClass != UFlowOwnerFunctionParams::StaticClass()) - { - ParamsProperty->SetPropertyClass(UFlowOwnerFunctionParams::StaticClass()); - } -} - -bool UFlowNode_CallOwnerFunction::CanEditChange(const FProperty* InProperty) const -{ - if (!Super::CanEditChange(InProperty)) - { - return false; - } - - const FName PropertyName = InProperty->GetFName(); - - if (PropertyName == GET_MEMBER_NAME_CHECKED(UFlowNode_CallOwnerFunction, Params)) - { - return false; - } - - return true; -} - -void UFlowNode_CallOwnerFunction::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) -{ - Super::PostEditChangeProperty(PropertyChangedEvent); - - const FName MemberPropertyName = PropertyChangedEvent.MemberProperty->GetFName(); - - if (MemberPropertyName == GET_MEMBER_NAME_CHECKED(UFlowNode_CallOwnerFunction, Params)) - { - OnReconstructionRequested.ExecuteIfBound(); - } - - const FName PropertyName = PropertyChangedEvent.Property->GetFName(); - if (PropertyName == GET_MEMBER_NAME_CHECKED(FFlowOwnerFunctionRef, FunctionName)) - { - if (TryAllocateParamsInstance()) - { - OnReconstructionRequested.ExecuteIfBound(); - } - } -} - -EDataValidationResult UFlowNode_CallOwnerFunction::ValidateNode() -{ - const bool bHasFunction = FunctionRef.IsConfigured(); - if (!bHasFunction) - { - ValidationLog.Error(TEXT("CallOwnerFunction requires a valid Function reference"), this); - - return EDataValidationResult::Invalid; - } - - const bool bHasParams = IsValid(Params); - if (!bHasParams) - { - ValidationLog.Error(TEXT("CallOwnerFunction requires a valid Params object"), this); - - return EDataValidationResult::Invalid; - } - - checkf(bHasParams && bHasFunction, TEXT("This should be assured by the preceding logic")); - - const UClass* ExpectedOwnerClass = TryGetExpectedOwnerClass(); - if (!IsValid(ExpectedOwnerClass)) - { - ValidationLog.Error(TEXT("Invalid or null Expected Owner Class for this Flow Asset"), this); - - return EDataValidationResult::Invalid; - } - - // Check if the function can be found on the expected owner - const UFunction* Function = FunctionRef.TryResolveFunction(*ExpectedOwnerClass); - if (!IsValid(Function)) - { - ValidationLog.Error(TEXT("Could not resolve function for flow owner"), this); - - return EDataValidationResult::Invalid; - } - - // Check the function signature - if (!DoesFunctionHaveValidFlowOwnerFunctionSignature(*Function)) - { - ValidationLog.Error(TEXT("Flow Owner Function has an invalid signature"), this); - - return EDataValidationResult::Invalid; - } - - const UClass* RequiredParamsClass = GetRequiredParamsClass(); - checkf(IsValid(RequiredParamsClass), TEXT("GetRequiredParamsClass() cannot return null if DoesFunctionHaveValidFlowOwnerFunctionSignature() is true")); - - const UClass* ExistingParamsClass = GetExistingParamsClass(); - checkf(IsValid(ExistingParamsClass), TEXT("This should be assured, if bHasParams == true")); - - // Check if the params (existing) are compatible with the function's expected (required) params - if (!ExistingParamsClass->IsChildOf(RequiredParamsClass)) - { - ValidationLog.Error(TEXT("Params object is not of the correct type for the flow owner function"), this); - - return EDataValidationResult::Invalid; - } - - return EDataValidationResult::Valid; -} - -FString UFlowNode_CallOwnerFunction::GetStatusString() const -{ - if (ActivationState != EFlowNodeState::NeverActivated) - { - return UEnum::GetDisplayValueAsText(ActivationState).ToString(); - } - - return Super::GetStatusString(); -} - -UClass* UFlowNode_CallOwnerFunction::TryGetExpectedOwnerClass() const -{ - const UFlowAsset* FlowAsset = GetFlowAsset(); - if (IsValid(FlowAsset)) - { - return FlowAsset->GetExpectedOwnerClass(); - } - - return nullptr; -} - -bool UFlowNode_CallOwnerFunction::DoesFunctionHaveValidFlowOwnerFunctionSignature(const UFunction& Function) -{ - if (GetParamsClassForFunction(Function) == nullptr) - { - return false; - } - - checkf(Function.NumParms == 2, TEXT("This should be checked in GetParamsClassForFunction()")); - - if (!DoesFunctionHaveNameReturnType(Function)) - { - return false; - } - - return true; -} - -bool UFlowNode_CallOwnerFunction::DoesFunctionHaveNameReturnType(const UFunction& Function) -{ - checkf(Function.NumParms == 2, TEXT("This should have already been checked in DoesFunctionHaveValidFlowOwnerFunctionSignature()")); - - TFieldIterator Iterator(&Function); - - while (Iterator) - { - const bool bIsOutParm = EnumHasAllFlags(Iterator->PropertyFlags, CPF_Parm | CPF_OutParm); - - return bIsOutParm; - } - - return false; -} - -UClass* UFlowNode_CallOwnerFunction::GetParamsClassForFunction(const UFunction& Function) -{ - if (Function.NumParms != 2) - { - // Flow Owner Functions expect exactly two parameters: - // - FFlowOwnerFunctionParams* - // - FName (return) - // See FFlowOwnerFunctionSignature - - return nullptr; - } - - TFieldIterator Iterator(&Function); - - while (Iterator && (Iterator->PropertyFlags & CPF_Parm)) - { - const FObjectPropertyBase* Prop = *Iterator; - check(Prop); - - UClass* PropertyClass = Prop->PropertyClass; - - if (!IsValid(PropertyClass)) - { - return nullptr; - } - - if (!PropertyClass->IsChildOf()) - { - return nullptr; - } - - return PropertyClass; - } - - return nullptr; -} - -UClass* UFlowNode_CallOwnerFunction::GetParamsClassForFunctionName(const UClass& ExpectedOwnerClass, const FName& FunctionName) -{ - const UFunction* Function = ExpectedOwnerClass.FindFunctionByName(FunctionName); - if (IsValid(Function)) - { - return GetParamsClassForFunction(*Function); - } - - return nullptr; -} - -bool UFlowNode_CallOwnerFunction::TryAllocateParamsInstance() -{ - if (FunctionRef.GetFunctionName().IsNone()) - { - // Throw out the old params object (if any) - Params = nullptr; - - return false; - } - - const UClass* ExistingParamsClass = GetExistingParamsClass(); - const UClass* RequiredParamsClass = GetRequiredParamsClass(); - - if (!IsValid(RequiredParamsClass)) - { - return false; - } - - const bool bNeedsAllocateParams = !IsValid(ExistingParamsClass) || ExistingParamsClass != RequiredParamsClass; - - if (!bNeedsAllocateParams) - { - return false; - } - - // Throw out the old params object (if any) - Params = nullptr; - - // Create the new params object - Params = NewObject(this, RequiredParamsClass); - - return true; -} - -UClass* UFlowNode_CallOwnerFunction::GetRequiredParamsClass() const -{ - const UClass* ExpectedOwnerClass = TryGetExpectedOwnerClass(); - if (!IsValid(ExpectedOwnerClass)) - { - return nullptr; - } - - const FName FunctionNameAsName = FunctionRef.GetFunctionName(); - - if (FunctionNameAsName.IsNone()) - { - return nullptr; - } - - UClass* RequiredParamsClass = GetParamsClassForFunctionName(*ExpectedOwnerClass, FunctionNameAsName); - return RequiredParamsClass; -} - -UClass* UFlowNode_CallOwnerFunction::GetExistingParamsClass() const -{ - if (!IsValid(Params)) - { - return nullptr; - } - - UClass* ExistingParamsClass = Params->GetClass(); - return ExistingParamsClass; -} - -bool UFlowNode_CallOwnerFunction::IsAcceptableParamsPropertyClass(const UClass* ParamsClass) const -{ - if (!IsValid(ParamsClass)) - { - return false; - } - - if (!ParamsClass->IsChildOf()) - { - return false; - } - - const UClass* ExistingParamsClass = GetExistingParamsClass(); - - if (IsValid(ExistingParamsClass) && ParamsClass != ExistingParamsClass) - { - return false; - } - - return true; -} - -FText UFlowNode_CallOwnerFunction::GetNodeTitle() const -{ - const bool bUseAdaptiveNodeTitles = UFlowSettings::Get()->bUseAdaptiveNodeTitles; - - if (bUseAdaptiveNodeTitles && !FunctionRef.GetFunctionName().IsNone()) - { - const FText FunctionNameText = FText::FromName(FunctionRef.FunctionName); - - return FText::Format(LOCTEXT("CallOwnerFunction", "Call {0}"), {FunctionNameText}); - } - else - { - return Super::GetNodeTitle(); - } -} - -#endif // WITH_EDITOR - -#undef LOCTEXT_NAMESPACE diff --git a/Source/Flow/Private/Types/FlowOwnerFunctionParams.cpp b/Source/Flow/Private/Types/FlowOwnerFunctionParams.cpp deleted file mode 100644 index 3e18dee4c..000000000 --- a/Source/Flow/Private/Types/FlowOwnerFunctionParams.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#include "Types/FlowOwnerFunctionParams.h" -#include "Nodes/FlowNode.h" -#include "Nodes/World/FlowNode_CallOwnerFunction.h" - -UFlowOwnerFunctionParams::UFlowOwnerFunctionParams() - : Super() -{ -#if WITH_EDITOR - InputNames.Add(UFlowNode::DefaultInputPin.PinName); - OutputNames.Add(UFlowNode::DefaultOutputPin.PinName); -#endif //WITH_EDITOR -} - -void UFlowOwnerFunctionParams::PreExecute(UFlowNode_CallOwnerFunction& InSourceNode, const FName& InputPinName) -{ - SourceNode = &InSourceNode; - ExecutedInputPinName = InputPinName; - - BP_PreExecute(); -} - -void UFlowOwnerFunctionParams::PostExecute() -{ - BP_PostExecute(); - - SourceNode = nullptr; - ExecutedInputPinName = NAME_None; -} - -bool UFlowOwnerFunctionParams::ShouldFinishForOutputName_Implementation(const FName& OutputName) const -{ - // Blueprint subclasses may want to declare certain outputs as "non-finishing" - // but by default, all outputs are 'finishing' - return true; -} - -TArray UFlowOwnerFunctionParams::BP_GetInputNames() const -{ - if (IsValid(SourceNode)) - { - return SourceNode->GetInputNames(); - } - - return TArray(); -} - -TArray UFlowOwnerFunctionParams::BP_GetOutputNames() const -{ - if (IsValid(SourceNode)) - { - return SourceNode->GetOutputNames(); - } - - return TArray(); -} diff --git a/Source/Flow/Private/Types/FlowOwnerFunctionRef.cpp b/Source/Flow/Private/Types/FlowOwnerFunctionRef.cpp deleted file mode 100644 index eee59d153..000000000 --- a/Source/Flow/Private/Types/FlowOwnerFunctionRef.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#include "Types/FlowOwnerFunctionRef.h" -#include "Types/FlowOwnerFunctionParams.h" -#include "Interfaces/FlowOwnerInterface.h" -#include "FlowLogChannels.h" - -#include "UObject/Class.h" -#include "Logging/LogMacros.h" - -UFunction* FFlowOwnerFunctionRef::TryResolveFunction(const UClass& InClass) -{ - if (IsConfigured()) - { - Function = InClass.FindFunctionByName(FunctionName); - } - else - { - Function = nullptr; - } - - return Function; -} - -FName FFlowOwnerFunctionRef::CallFunction(IFlowOwnerInterface& InFlowOwnerInterface, UFlowOwnerFunctionParams& InParams) const -{ - if (!IsResolved()) - { - const UObject* FlowOwnerObject = CastChecked(&InFlowOwnerInterface); - - UE_LOG( - LogFlow, - Error, - TEXT("Could not resolve function named %s with flow owner class %s"), - *FunctionName.ToString(), - *FlowOwnerObject->GetClass()->GetName()); - - return NAME_None; - } - - UObject* FlowOwnerObject = CastChecked(&InFlowOwnerInterface); - - struct FFlowOwnerFunctionRef_Parms - { - // Single FunctionParams object parameter - UFlowOwnerFunctionParams* Params; - - // Return value - FName OutputPinName; - }; - - FFlowOwnerFunctionRef_Parms Parms = { &InParams, NAME_None }; - - // Call the owner function itself - FlowOwnerObject->ProcessEvent(Function, &Parms); - - // Ensure the return value is valid - if (!Parms.OutputPinName.IsNone()) - { - const TArray OutputNames = InParams.GatherOutputNames(); - - if (!OutputNames.Contains(Parms.OutputPinName)) - { - FString OutputNamesStr = TEXT("None"); - for (const FName& OutputName : OutputNames) - { - OutputNamesStr += TEXT(", ") + OutputName.ToString(); - } - - UE_LOG( - LogFlow, - Error, - TEXT("Flow Owner Function %s returned an invalid OutputPinName '%s', which is not in the valid outputs: { %s }"), - *FunctionName.ToString(), - *Parms.OutputPinName.ToString(), - *OutputNamesStr); - - // Replace the invalid output pin name with None - Parms.OutputPinName = NAME_None; - } - } - - return Parms.OutputPinName; -} diff --git a/Source/Flow/Public/Interfaces/FlowNativeExecutableInterface.h b/Source/Flow/Public/Interfaces/FlowNativeExecutableInterface.h deleted file mode 100644 index 364395847..000000000 --- a/Source/Flow/Public/Interfaces/FlowNativeExecutableInterface.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#pragma once - -#include "UObject/Interface.h" - -#include "FlowNativeExecutableInterface.generated.h" - -UINTERFACE(DisplayName = "[DEPRECATED] Flow Native Executable Interface", meta = (CannotImplementInterfaceInBlueprint, Deprecated)) -class UFlowNativeExecutableInterface : public UInterface -{ - GENERATED_BODY() -}; - -class IFlowNativeExecutableInterface -{ - GENERATED_BODY() - -public: - - // NOTE (gtaylor) All of these functions have been moved into UFlowNodeBase. - // Keeping the empty interface existing for a time until all of the assets are updated, - // to prevent an assert. -}; diff --git a/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h b/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h deleted file mode 100644 index cf7c514bf..000000000 --- a/Source/Flow/Public/Nodes/World/FlowNode_CallOwnerFunction.h +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#pragma once - -#include "GameplayTagContainer.h" - -#include "Types/FlowOwnerFunctionRef.h" -#include "Nodes/FlowNode.h" - -#include "FlowNode_CallOwnerFunction.generated.h" - -class UFlowOwnerFunctionParams; -class IFlowOwnerInterface; - -// Example signature for valid Flow Owner Functions -typedef TFunction FFlowOwnerFunctionSignature; - -/** - * FlowNode to call an owner function - * - Owner must implement IFlowOwnerInterface - * - Callable functions must take a single input parameter deriving from UFlowOwnerFunctionParams - * and return FName for the Output event to trigger (or "None" to trigger none of the outputs) - */ -UCLASS(NotBlueprintable, meta = (DisplayName = "Call Owner Function")) -class FLOW_API UFlowNode_CallOwnerFunction : public UFlowNode -{ - GENERATED_UCLASS_BODY() - -public: - -#if WITH_EDITOR - // UObject - virtual void PostLoad() override; - virtual bool CanEditChange(const FProperty* InProperty) const override; - virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override; - // -- - - // UFlowNode - virtual FText GetNodeTitle() const override; - virtual EDataValidationResult ValidateNode() override; - - virtual FString GetStatusString() const override; - // -- -#endif // WITH_EDITOR - - UClass* GetRequiredParamsClass() const; - UClass* GetExistingParamsClass() const; - - bool IsAcceptableParamsPropertyClass(const UClass* ParamsClass) const; - - UClass* TryGetExpectedOwnerClass() const; - - static bool DoesFunctionHaveValidFlowOwnerFunctionSignature(const UFunction& Function); - - static UClass* GetParamsClassForFunctionName(const UClass& ExpectedOwnerClass, const FName& FunctionName); - static UClass* GetParamsClassForFunction(const UFunction& Function); - -protected: - // UFlowNode - virtual void ExecuteInput(const FName& PinName) override; - // -- - - bool ShouldFinishForOutputName(const FName& OutputName) const; - bool TryExecuteOutputPin(const FName& OutputName); - - bool TryAllocateParamsInstance(); - - // Helper function for DoesFunctionHaveValidFlowOwnerFunctionSignature() - static bool DoesFunctionHaveNameReturnType(const UFunction& Function); - -protected: - // Function reference on the expected owner to call - // DEPRECATED - Sunsetting this feature from FlowGraph with the next release. Custom FlowNodes are a better mechanism to use - UPROPERTY(EditAnywhere, Category = "Call Owner", meta = (DisplayName = "DEPRECATED - Function")) - FFlowOwnerFunctionRef FunctionRef; - - // Parameter object to pass to the function when called - UPROPERTY(EditAnywhere, Category = "Call Owner", Instanced) - TObjectPtr Params; -}; diff --git a/Source/Flow/Public/Types/FlowOwnerFunctionParams.h b/Source/Flow/Public/Types/FlowOwnerFunctionParams.h deleted file mode 100644 index a9b31984b..000000000 --- a/Source/Flow/Public/Types/FlowOwnerFunctionParams.h +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#pragma once - -#include "FlowOwnerFunctionParams.generated.h" - -// Forward Declarations -class UFlowNode_CallOwnerFunction; - -UCLASS(BlueprintType, Blueprintable, EditInlineNew) -class FLOW_API UFlowOwnerFunctionParams : public UObject -{ - GENERATED_BODY() - -public: - - UFlowOwnerFunctionParams(); - - void PreExecute(UFlowNode_CallOwnerFunction& InSourceNode, const FName& InputPinName); - void PostExecute(); - - UFUNCTION(BlueprintNativeEvent, BlueprintPure, Category = "FlowOwnerFunction") - bool ShouldFinishForOutputName(const FName& OutputName) const; - -#if WITH_EDITORONLY_DATA - const TArray& GetInputNames() const { return InputNames; } - const TArray& GetOutputNames() const { return OutputNames; } -#endif // WITH_EDITORONLY_DATA - - // Get the input/output pin names for the SourceNode. - // Slightly slower than the editor-only counterparts owing to the deep copy of the array. - // Valid only if called between PreExecute() and PostExecute(), inclusive - TArray GatherInputNames() const { return BP_GetInputNames(); } - TArray GatherOutputNames() const { return BP_GetOutputNames(); } - -protected: - - // Called prior to the owner executing the function described by this object. - // Can be overridden to prepare the stateful data before execution. - UFUNCTION(BlueprintImplementableEvent, Category = "FlowOwnerFunction", DisplayName = "PreExecute") - void BP_PreExecute(); - - // Cleans up the stateful data in this Params struct. - // Can be overridden to cleanup the stateful data after execution. - UFUNCTION(BlueprintImplementableEvent, Category = "FlowOwnerFunction", DisplayName = "PostExecute") - void BP_PostExecute(); - - // Get the input pin names for the SourceNode - // Valid only if called between PreExecute() and PostExecute(), inclusive - UFUNCTION(BlueprintCallable, BlueprintPure, Category = "FlowOwnerFunction", DisplayName = "GetInputNames") - TArray BP_GetInputNames() const; - - // Get the output pin names for the SourceNode - // Valid only if called between PreExecute() and PostExecute(), inclusive - UFUNCTION(BlueprintCallable, BlueprintPure, Category = "FlowOwnerFunction", DisplayName = "GetOutputNames") - TArray BP_GetOutputNames() const; - -protected: - - // CallOwnerObjectFunction node that is executing this set of function params. - // Valid only if called between PreExecute() and PostExecute(), inclusive - UFlowNode_CallOwnerFunction* SourceNode = nullptr; - - // This is the Name from the Input Pin that caused this node to Execute. - // Valid only if called between PreExecute() and PostExecute(), inclusive - UPROPERTY(Transient, BlueprintReadOnly, Category = "FlowOwnerFunction") - FName ExecutedInputPinName; - -#if WITH_EDITORONLY_DATA - // Input pin names for this function - UPROPERTY(EditDefaultsOnly, Category = "FlowOwnerFunction") - TArray InputNames; - - // Output pin names for this function - UPROPERTY(EditDefaultsOnly, Category = "FlowOwnerFunction") - TArray OutputNames; -#endif // WITH_EDITORONLY_DATA -}; diff --git a/Source/Flow/Public/Types/FlowOwnerFunctionRef.h b/Source/Flow/Public/Types/FlowOwnerFunctionRef.h deleted file mode 100644 index a3581d67b..000000000 --- a/Source/Flow/Public/Types/FlowOwnerFunctionRef.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#pragma once - -#include "CoreMinimal.h" -#include "Templates/SubclassOf.h" - -#include "FlowOwnerFunctionRef.generated.h" - -// Forward Declarations -class UFlowOwnerFunctionParams; -class IFlowOwnerInterface; - -// Similar to FAnimNodeFunctionRef, providing a FName-based function binding -// that is resolved at runtime -USTRUCT(BlueprintType) -struct FFlowOwnerFunctionRef -{ - GENERATED_BODY() - - // For GET_MEMBER_NAME_CHECKED access - friend class UFlowNode_CallOwnerFunction; - friend class FFlowOwnerFunctionRefCustomization; - -public: - - // Resolves the function and returns the UFunction - UFunction* TryResolveFunction(const UClass& InClass); - - // Returns a the resolved function - // (assumes TryResolveFunction was called previously) - UFunction* GetResolvedFunction() const { return Function; } - - // Call the function and return the Output Pin Name result - FName CallFunction(IFlowOwnerInterface& InFlowOwnerInterface, UFlowOwnerFunctionParams& InParams) const; - - // Accessors - FName GetFunctionName() const { return FunctionName; } - bool IsConfigured() const { return !FunctionName.IsNone(); } - bool IsResolved() const { return ::IsValid(Function); } - -protected: - - // The name of the function to call - UPROPERTY(VisibleAnywhere, Category = "FlowOwnerFunction") - FName FunctionName = NAME_None; - - // The function to call - // (resolved by looking for a function named FunctionName on the ExpectedOwnerClass) - UPROPERTY(Transient) - TObjectPtr Function = nullptr; - -#if WITH_EDITORONLY_DATA - UPROPERTY(VisibleAnywhere, Category = "FlowOwnerFunction", meta = (DisplayName = "Function Parameters Class")) - TSubclassOf ParamsClass; -#endif // WITH_EDITORONLY_DATA -}; diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowOwnerFunctionRefCustomization.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowOwnerFunctionRefCustomization.cpp deleted file mode 100644 index 69cf4798c..000000000 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowOwnerFunctionRefCustomization.cpp +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#include "DetailCustomizations/FlowOwnerFunctionRefCustomization.h" - -#include "FlowAsset.h" -#include "Interfaces/FlowOwnerInterface.h" -#include "Nodes/FlowNode.h" -#include "Nodes/World/FlowNode_CallOwnerFunction.h" - -#include "UObject/UnrealType.h" -#include "Types/FlowOwnerFunctionParams.h" - -void FFlowOwnerFunctionRefCustomization::CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) -{ - // Do not include children properties (the header is all we need to show for this struct) -} - -TSharedPtr FFlowOwnerFunctionRefCustomization::GetCuratedNamePropertyHandle() const -{ - check(StructPropertyHandle->IsValidHandle()); - - TSharedPtr FoundHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowOwnerFunctionRef, FunctionName)); - check(FoundHandle); - - return FoundHandle; -} - -TArray FFlowOwnerFunctionRefCustomization::GetCuratedNameOptions() const -{ - TArray Results; - - const UClass* ExpectedOwnerClass = TryGetExpectedOwnerClass(); - if (!IsValid(ExpectedOwnerClass)) - { - return Results; - } - - const UFlowNode_CallOwnerFunction* FlowNodeOwner = Cast(TryGetFlowNodeOuter()); - if (!IsValid(FlowNodeOwner)) - { - return Results; - } - - Results = GetFlowOwnerFunctionRefs(*FlowNodeOwner, *ExpectedOwnerClass); - - return Results; -} - -const UClass* FFlowOwnerFunctionRefCustomization::TryGetExpectedOwnerClass() const -{ - const UFlowNode* NodeOwner = TryGetFlowNodeOuter(); - if (!IsValid(NodeOwner)) - { - return nullptr; - } - - const UFlowAsset* FlowAsset = NodeOwner->GetFlowAsset(); - if (!IsValid(FlowAsset)) - { - return nullptr; - } - - UClass* ExpectedOwnerClass = FlowAsset->GetExpectedOwnerClass(); - return ExpectedOwnerClass; -} - -TArray FFlowOwnerFunctionRefCustomization::GetFlowOwnerFunctionRefs( - const UFlowNode_CallOwnerFunction& FlowNodeOwner, - const UClass& ExpectedOwnerClass) -{ - TArray ValidFunctionNames; - - // Gather a list of potential functions - TSet PotentialFunctionNames; - - const UClass* CurClass = &ExpectedOwnerClass; - while (IsValid(CurClass)) - { - TArray CurClassFunctionNames; - CurClass->GenerateFunctionList(CurClassFunctionNames); - - PotentialFunctionNames.Append(CurClassFunctionNames); - - // Recurse to include all of the Super(s) names - CurClass = CurClass->GetSuperClass(); - } - - if (PotentialFunctionNames.Num() == 0) - { - return ValidFunctionNames; - } - - ValidFunctionNames.Reserve(PotentialFunctionNames.Num()); - - // Filter out any unusable functions (that do not match the expected signature) - for (const FName& PotentialFunctionName : PotentialFunctionNames) - { - const UFunction* PotentialFunction = ExpectedOwnerClass.FindFunctionByName(PotentialFunctionName); - check(IsValid(PotentialFunction)); - - if (IsFunctionUsable(*PotentialFunction, FlowNodeOwner)) - { - ValidFunctionNames.Add(PotentialFunctionName); - } - } - - return ValidFunctionNames; -} - -bool FFlowOwnerFunctionRefCustomization::IsFunctionUsable(const UFunction& Function, const UFlowNode_CallOwnerFunction& FlowNodeOwner) -{ - if (!UFlowNode_CallOwnerFunction::DoesFunctionHaveValidFlowOwnerFunctionSignature(Function)) - { - return false; - } - - return true; -} - -void FFlowOwnerFunctionRefCustomization::SetCuratedName(const FName& NewFunctionName) -{ - TSharedPtr FunctionNameHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowOwnerFunctionRef, FunctionName)); - - check(FunctionNameHandle); - - FunctionNameHandle->SetPerObjectValue(0, NewFunctionName.ToString()); -} - -bool FFlowOwnerFunctionRefCustomization::TryGetCuratedName(FName& OutName) const -{ - const FFlowOwnerFunctionRef* FlowOwnerFunction = GetFlowOwnerFunctionRef(); - if (FlowOwnerFunction) - { - OutName = FlowOwnerFunction->FunctionName; - - return true; - } - else - { - return false; - } -} - -UFlowNode* FFlowOwnerFunctionRefCustomization::TryGetFlowNodeOuter() const -{ - check(StructPropertyHandle->IsValidHandle()); - - TArray OuterObjects; - StructPropertyHandle->GetOuterObjects(OuterObjects); - - for (UObject* OuterObject : OuterObjects) - { - UFlowNode* FlowNodeOuter = Cast(OuterObject); - if (IsValid(FlowNodeOuter)) - { - return FlowNodeOuter; - } - } - - return nullptr; -} diff --git a/Source/FlowEditor/Private/FlowEditorModule.cpp b/Source/FlowEditor/Private/FlowEditorModule.cpp index 72682cbee..115534ecd 100644 --- a/Source/FlowEditor/Private/FlowEditorModule.cpp +++ b/Source/FlowEditor/Private/FlowEditorModule.cpp @@ -23,7 +23,6 @@ #include "DetailCustomizations/FlowNode_PlayLevelSequenceDetails.h" #include "DetailCustomizations/FlowNode_SubGraphDetails.h" #include "DetailCustomizations/FlowNodeAddOn_Details.h" -#include "DetailCustomizations/FlowOwnerFunctionRefCustomization.h" #include "DetailCustomizations/FlowActorOwnerComponentRefCustomization.h" #include "DetailCustomizations/FlowDataPinPropertyCustomizations.h" #include "DetailCustomizations/FlowDataPinProperty_ClassCustomization.h" @@ -225,7 +224,6 @@ void FFlowEditorModule::RegisterDetailCustomizations() RegisterCustomClassLayout(UFlowNode_CustomOutput::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_CustomOutputDetails::MakeInstance)); RegisterCustomClassLayout(UFlowNode_PlayLevelSequence::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_PlayLevelSequenceDetails::MakeInstance)); RegisterCustomClassLayout(UFlowNode_SubGraph::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_SubGraphDetails::MakeInstance)); - RegisterCustomStructLayout(*FFlowOwnerFunctionRef::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowOwnerFunctionRefCustomization::MakeInstance)); RegisterCustomStructLayout(*FFlowActorOwnerComponentRef::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowActorOwnerComponentRefCustomization::MakeInstance)); RegisterCustomStructLayout(*FFlowPin::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowPinCustomization::MakeInstance)); RegisterCustomStructLayout(*FFlowNamedDataPinOutputProperty::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowNamedDataPinOutputPropertyCustomization::MakeInstance)); diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowOwnerFunctionRefCustomization.h b/Source/FlowEditor/Public/DetailCustomizations/FlowOwnerFunctionRefCustomization.h deleted file mode 100644 index ccc2f8b77..000000000 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowOwnerFunctionRefCustomization.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#pragma once - -#include "UnrealExtensions/IFlowCuratedNamePropertyCustomization.h" - -#include "Types/FlowOwnerFunctionRef.h" - - -// Forward Declaration -class UFlowAsset; -class UFlowNode; -class UObject; -class UClass; -class UFunction; -class UFlowNode_CallOwnerFunction; - - -// Details customization for FFlowOwnerFunctionRef -class FFlowOwnerFunctionRefCustomization : public IFlowCuratedNamePropertyCustomization -{ -private: - typedef IFlowCuratedNamePropertyCustomization Super; - -public: - static TSharedRef MakeInstance() { return MakeShareable(new FFlowOwnerFunctionRefCustomization()); } - -protected: - - // IPropertyTypeCustomization - virtual void CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; - // -- - - // ICuratedNamePropertyCustomization - virtual TSharedPtr GetCuratedNamePropertyHandle() const override; - virtual void SetCuratedName(const FName& NewName) override; - virtual bool TryGetCuratedName(FName& OutName) const override; - virtual TArray GetCuratedNameOptions() const override; - // -- - - // Accessor to return the actual struct being edited - FORCEINLINE FFlowOwnerFunctionRef* GetFlowOwnerFunctionRef() const - { return IFlowExtendedPropertyTypeCustomization::TryGetTypedStructValue(StructPropertyHandle); } - - const UClass* TryGetExpectedOwnerClass() const; - UFlowNode* TryGetFlowNodeOuter() const; - - static TArray GetFlowOwnerFunctionRefs(const UFlowNode_CallOwnerFunction& FlowNodeOwner, const UClass& ExpectedOwnerClass); - - static bool IsFunctionUsable(const UFunction& Function, const UFlowNode_CallOwnerFunction& FlowNodeOwner); -}; From c8f43b959d5554abb11cf8f4dbc35441db7503c7 Mon Sep 17 00:00:00 2001 From: pixelchamp <119065627+fchampoux@users.noreply.github.com> Date: Wed, 12 Feb 2025 12:01:19 -0500 Subject: [PATCH 326/485] RemoveSubFlow - Set NodeOwningThisAssetInstance after FinishFlow (#263) Make sure to set the NodeOwningThisAssetInstance after the FinishFlow call, as it may be needed in the FinishFlow method --- Source/Flow/Private/FlowSubsystem.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index d7ec66aec..4ea5c5681 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -204,12 +204,14 @@ void UFlowSubsystem::RemoveSubFlow(UFlowNode_SubGraph* SubGraphNode, const EFlow if (InstancedSubFlows.Contains(SubGraphNode)) { UFlowAsset* AssetInstance = InstancedSubFlows[SubGraphNode]; - AssetInstance->NodeOwningThisAssetInstance = nullptr; - + SubGraphNode->GetFlowAsset()->ActiveSubGraphs.Remove(SubGraphNode); InstancedSubFlows.Remove(SubGraphNode); AssetInstance->FinishFlow(FinishPolicy); + + // Make sure to set the NodeOwningThisAssetInstance after the FinishFlow call, as it may be needed in the FinishFlow method + AssetInstance->NodeOwningThisAssetInstance = nullptr; } } From 6dc3eb9fb2de3be89fbd40c7cc3621e5bae4d88c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Thu, 13 Feb 2025 16:07:20 +0100 Subject: [PATCH 327/485] restored old name of method to be removed after the next plugin release --- Source/Flow/Public/Nodes/FlowNode.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 055d5c4dc..0857485cb 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -167,6 +167,9 @@ class FLOW_API UFlowNode void SetConnections(const TMap& InConnections) { Connections = InConnections; } FConnectedPin GetConnection(const FName OutputName) const { return Connections.FindRef(OutputName); } + UE_DEPRECATED(5.5, "Please use GatherConnectedNodes instead.") + TSet GetConnectedNodes() const { return GatherConnectedNodes(); } + UFUNCTION(BlueprintPure, Category= "FlowNode") TSet GatherConnectedNodes() const; From d0d8d2b7249038fe34e9af8cdf9312fdcd5dbc22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sun, 16 Feb 2025 18:21:55 +0100 Subject: [PATCH 328/485] Exposed CreateFlowInstance as a public method. Useful in projects where Root-SubGraph relations are "replaced" with a loose set of graphs, i.e. card games. --- Source/Flow/Public/FlowSubsystem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Flow/Public/FlowSubsystem.h b/Source/Flow/Public/FlowSubsystem.h index bb763eb59..9b884fab9 100644 --- a/Source/Flow/Public/FlowSubsystem.h +++ b/Source/Flow/Public/FlowSubsystem.h @@ -93,9 +93,9 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem UFlowAsset* CreateSubFlow(UFlowNode_SubGraph* SubGraphNode, const FString& SavedInstanceName = FString(), const bool bPreloading = false); void RemoveSubFlow(UFlowNode_SubGraph* SubGraphNode, const EFlowFinishPolicy FinishPolicy); +public: UFlowAsset* CreateFlowInstance(const TWeakObjectPtr Owner, TSoftObjectPtr FlowAsset, FString NewInstanceName = FString()); -public: virtual void AddInstancedTemplate(UFlowAsset* Template); virtual void RemoveInstancedTemplate(UFlowAsset* Template); From 8685a8f2eb3114c88f2d722a34ec51fb0a5243cd Mon Sep 17 00:00:00 2001 From: MaksymKapelianovych <48297221+MaksymKapelianovych@users.noreply.github.com> Date: Wed, 19 Feb 2025 18:05:58 +0200 Subject: [PATCH 329/485] Fix PassThrough nodes in loops (#267) --- Source/Flow/Private/Nodes/FlowNode.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 08b2c87af..6237305d2 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -919,6 +919,12 @@ void UFlowNode::Finish() void UFlowNode::Deactivate() { + if (SignalMode == EFlowSignalMode::PassThrough) + { + // there is nothing to deactivate, node was never active + return; + } + if (GetFlowAsset()->FinishPolicy == EFlowFinishPolicy::Abort) { ActivationState = EFlowNodeState::Aborted; From aea03ac5411fa771c9db94bafd56b25c2d3e692f Mon Sep 17 00:00:00 2001 From: Rhillion Date: Wed, 19 Feb 2025 16:06:11 +0000 Subject: [PATCH 330/485] Added missing calls to parent class Cleanup method (#269) --- Source/Flow/Private/Nodes/Actor/FlowNode_ComponentObserver.cpp | 2 ++ Source/Flow/Private/Nodes/Actor/FlowNode_PlayLevelSequence.cpp | 2 ++ Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp | 2 ++ Source/Flow/Private/Nodes/Route/FlowNode_Counter.cpp | 2 ++ Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp | 2 ++ Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp | 2 ++ Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp | 2 ++ 7 files changed, 14 insertions(+) diff --git a/Source/Flow/Private/Nodes/Actor/FlowNode_ComponentObserver.cpp b/Source/Flow/Private/Nodes/Actor/FlowNode_ComponentObserver.cpp index 3020836a3..10d2ef8d3 100644 --- a/Source/Flow/Private/Nodes/Actor/FlowNode_ComponentObserver.cpp +++ b/Source/Flow/Private/Nodes/Actor/FlowNode_ComponentObserver.cpp @@ -145,6 +145,8 @@ void UFlowNode_ComponentObserver::Cleanup() RegisteredActors.Empty(); SuccessCount = 0; + + Super::Cleanup(); } #if WITH_EDITOR diff --git a/Source/Flow/Private/Nodes/Actor/FlowNode_PlayLevelSequence.cpp b/Source/Flow/Private/Nodes/Actor/FlowNode_PlayLevelSequence.cpp index 70fc34b31..9cfbd4f2b 100644 --- a/Source/Flow/Private/Nodes/Actor/FlowNode_PlayLevelSequence.cpp +++ b/Source/Flow/Private/Nodes/Actor/FlowNode_PlayLevelSequence.cpp @@ -300,6 +300,8 @@ void UFlowNode_PlayLevelSequence::Cleanup() #if ENABLE_VISUAL_LOG UE_VLOG(this, LogFlow, Log, TEXT("Finished playback: %s"), *Sequence.ToString()); #endif + + Super::Cleanup(); } FString UFlowNode_PlayLevelSequence::GetPlaybackProgress() const diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp index 6c0e374a0..047737d36 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp @@ -86,6 +86,8 @@ void UFlowNode_SubGraph::Cleanup() { GetFlowSubsystem()->RemoveSubFlow(this, EFlowFinishPolicy::Keep); } + + Super::Cleanup(); } void UFlowNode_SubGraph::ForceFinishNode() diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Counter.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Counter.cpp index f6a4cd701..43f94de55 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Counter.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Counter.cpp @@ -65,6 +65,8 @@ void UFlowNode_Counter::ExecuteInput(const FName& PinName) void UFlowNode_Counter::Cleanup() { CurrentSum = 0; + + Super::Cleanup(); } #if WITH_EDITOR diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp index a7e05289e..ca16183b3 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionMultiGate.cpp @@ -96,6 +96,8 @@ void UFlowNode_ExecutionMultiGate::Cleanup() { NextOutput = 0; Completed.Reset(); + + Super::Cleanup(); } #if WITH_EDITOR diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp index 386dbaa81..4dc8509ea 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_ExecutionSequence.cpp @@ -42,6 +42,8 @@ void UFlowNode_ExecutionSequence::OnLoad_Implementation() void UFlowNode_ExecutionSequence::Cleanup() { ExecutedConnections.Empty(); + + Super::Cleanup(); } void UFlowNode_ExecutionSequence::ExecuteNewConnections() diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp index 184862c39..2e842dd9c 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp @@ -155,6 +155,8 @@ void UFlowNode_Timer::Cleanup() StepTimerHandle.Invalidate(); SumOfSteps = 0.0f; + + Super::Cleanup(); } void UFlowNode_Timer::OnSave_Implementation() From 1551ce34696f224c292e99ece42ec1e77d824de1 Mon Sep 17 00:00:00 2001 From: jnucc <120216868+jnucc@users.noreply.github.com> Date: Thu, 27 Feb 2025 15:12:11 -0500 Subject: [PATCH 331/485] Adaptive Build fix for when all FlowGraph is built as separate source files. (#273) --- .../Public/Interfaces/FlowDataPinPropertyProviderInterface.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/Flow/Public/Interfaces/FlowDataPinPropertyProviderInterface.h b/Source/Flow/Public/Interfaces/FlowDataPinPropertyProviderInterface.h index 24130d709..f5225da2a 100644 --- a/Source/Flow/Public/Interfaces/FlowDataPinPropertyProviderInterface.h +++ b/Source/Flow/Public/Interfaces/FlowDataPinPropertyProviderInterface.h @@ -2,7 +2,12 @@ #pragma once +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 5 #include "InstancedStruct.h" +#else +#include "StructUtils/InstancedStruct.h" +#endif + #include "UObject/Interface.h" #include "FlowDataPinPropertyProviderInterface.generated.h" From 8e5165f1f66ec9f921cea8b47892c314c8420547 Mon Sep 17 00:00:00 2001 From: Kyle Wilcox Date: Thu, 27 Feb 2025 16:35:08 -0700 Subject: [PATCH 332/485] UFlowGraphNode::ReconstructNode and associated functions rewrite to clean up graph behavior (#264) * Rewrite UFlowGraphNode::ReconstructNode() function to fix bugs 1) Make all pin types and sources correctly update when modified, 2) fix unnecessary dirtying of flow asset upon opening (unnecessary Modify() calls) * Additional cleanup of graph refresh & reconstruct node behavior - Added UFlowGraph "IsSaving" state; made UFlowGraphNode skip node reconstruction during save - Added UFlowGraphNode "IsDestroyingNode" state, made UFlowGraphNode skip node reconstruction during node deletion - Fixed minor bug from previous commit which broke certain transaction undos in ReconstructNode() * Additional cleanup - Added 2 missing FlowNode Modify() calls - Replaced logs with ensures - Cleanup Lambda --- Source/Flow/Private/FlowAsset.cpp | 11 +- Source/FlowEditor/Private/Graph/FlowGraph.cpp | 13 +- .../Private/Graph/Nodes/FlowGraphNode.cpp | 381 ++++++++++-------- Source/FlowEditor/Public/Graph/FlowGraph.h | 4 + .../Public/Graph/Nodes/FlowGraphNode.h | 15 +- 5 files changed, 243 insertions(+), 181 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index f931a14c2..33fbf4d3d 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -316,7 +316,11 @@ void UFlowAsset::RegisterNode(const FGuid& NewGuid, UFlowNode* NewNode) Nodes.Emplace(NewGuid, NewNode); HarvestNodeConnections(); - (void)TryUpdateManagedFlowPinsForNode(*NewNode); + + if (TryUpdateManagedFlowPinsForNode(*NewNode)) + { + (void) NewNode->OnReconstructionRequested.ExecuteIfBound(); + } } void UFlowAsset::UnregisterNode(const FGuid& NodeGuid) @@ -492,11 +496,6 @@ bool UFlowAsset::TryUpdateManagedFlowPinsForNode(UFlowNode& FlowNode) { FlowNode.SetAutoOutputDataPins(WorkingData.AutoOutputDataPinsNext); } - - if (FlowNode.GraphNode) - { - FlowNode.OnReconstructionRequested.ExecuteIfBound(); - } } FlowNode.PostEditChange(); diff --git a/Source/FlowEditor/Private/Graph/FlowGraph.cpp b/Source/FlowEditor/Private/Graph/FlowGraph.cpp index 826205701..a2d7281db 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraph.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraph.cpp @@ -93,16 +93,9 @@ void UFlowGraph::RefreshGraph() } } + // This function will (eventually) result in all graph nodes being reconstructed UnlockUpdates(); } - - // refresh nodes - TArray FlowGraphNodes; - GetNodesOfClass(FlowGraphNodes); - for (UFlowGraphNode* GraphNode : FlowGraphNodes) - { - GraphNode->OnGraphRefresh(); - } } void UFlowGraph::RecursivelyRefreshAddOns(UFlowGraphNode& FromFlowGraphNode) @@ -222,7 +215,11 @@ void UFlowGraph::OnLoaded() void UFlowGraph::OnSave() { + bIsSavingGraph = true; + UpdateAsset(); + + bIsSavingGraph = false; } void UFlowGraph::Initialize() diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index d24aab90b..ccf857277 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -286,48 +286,49 @@ void UFlowGraphNode::InsertNewNode(UEdGraphPin* FromPin, UEdGraphPin* NewLinkPin void UFlowGraphNode::ReconstructNode() { - RefreshContextPins(); - - if (!ShouldReconstructNode()) + if (!CanReconstructNode()) { - // This ensures the graph editor 'Refresh' button still rebuilds all of the graph widgets even if the FlowGraphNode has nothing to update. - (void) OnReconstructNodeCompleted.ExecuteIfBound(); - return; } bIsReconstructingNode = true; + FScopedTransaction Transaction(LOCTEXT("ReconstructNode", "Reconstruct Node"), !GUndo); + + bool bNodeDataPinsUpdated = TryUpdateAutoDataPins(); // This must be called first, it updates the underlying data for data pins of the flow node + bool bNodeExecPinsUpdated = TryUpdateNodePins(); // Updates all pins of the flow node (native pins, meta auto pins, and context pins which include data pins for now) + bool bAreGraphPinsMismatched = !CheckGraphPinsMatchNodePins(); // This must be called last since it checks the existing graph node against the cleaned up FlowNode instance - TArray OldPins(Pins); - - // Harvest the auto-generated pins before refreshing context pins - if (UFlowNode* FlowNode = Cast(NodeInstance)) + bool bGraphNodeRequiresReconstruction = bNeedsFullReconstruction || bNodeDataPinsUpdated || bNodeExecPinsUpdated || bAreGraphPinsMismatched; + + if (bGraphNodeRequiresReconstruction) { - if (UFlowAsset* FlowAsset = NodeInstance->GetFlowAsset()) + Modify(); + + TArray OldPins(Pins); + + Pins.Reset(); + InputPins.Reset(); + OutputPins.Reset(); + + AllocateDefaultPins(); + RewireOldPinsToNewPins(OldPins); + + // Destroy old pins + for (UEdGraphPin* OldPin : OldPins) { - FlowAsset->TryUpdateManagedFlowPinsForNode(*FlowNode); + OldPin->Modify(); + OldPin->BreakAllPinLinks(); + DestroyPin(OldPin); } - } - Pins.Reset(); - InputPins.Reset(); - OutputPins.Reset(); - - AllocateDefaultPins(); - RewireOldPinsToNewPins(OldPins); - - // Destroy old pins - for (UEdGraphPin* OldPin : OldPins) - { - OldPin->Modify(); - OldPin->BreakAllPinLinks(); - DestroyPin(OldPin); + bNeedsFullReconstruction = false; } - - bNeedsFullReconstruction = false; - bIsReconstructingNode = false; + // This ensures the graph editor 'Refresh' button still rebuilds all of the graph widgets even if the FlowGraphNode has nothing to update + // Ideally we could get rid of the 'Refresh' button but I think it will keep being useful, esp. for users making rough custom widgets (void) OnReconstructNodeCompleted.ExecuteIfBound(); + + bIsReconstructingNode = false; } void UFlowGraphNode::AllocateDefaultPins() @@ -1018,69 +1019,6 @@ void UFlowGraphNode::RemoveInstancePin(UEdGraphPin* Pin) GetGraph()->NotifyNodeChanged(this); } -void UFlowGraphNode::RefreshContextPins() -{ - if (GIsTransacting) - { - return; - } - - UFlowNode* FlowNode = Cast(NodeInstance); - if (!IsValid(FlowNode)) - { - return; - } - - // Update the auto-generated pins before refreshing context pins - bool bChangedAutoFlowPins = false; - if (UFlowAsset* FlowAsset = NodeInstance->GetFlowAsset()) - { - bChangedAutoFlowPins = FlowAsset->TryUpdateManagedFlowPinsForNode(*FlowNode); - } - - bool bIsLoad = false; - if (const UFlowGraph* FlowGraph = GetFlowGraph()) - { - bIsLoad = FlowGraph->IsLoadingGraph(); - } - - // Confirm that we should be refreshing context pins - const bool bIsAllowedToRefreshPins = !bIsLoad || NodeInstance->CanRefreshContextPinsOnLoad(); - const bool bShouldConsiderRefreshingContextPins = bIsAllowedToRefreshPins && (SupportsContextPins() || bHasContextPins); - const bool bShouldRefreshContextPins = bShouldConsiderRefreshingContextPins || bChangedAutoFlowPins || bNeedsFullReconstruction; - - if (!bShouldRefreshContextPins) - { - return; - } - - const TArray ContextInputs = FlowNode->GetContextInputs(); - const TArray ContextOutputs = FlowNode->GetContextOutputs(); - - const bool bPrevHasContextPins = bHasContextPins; - bHasContextPins = !ContextInputs.IsEmpty() || !ContextOutputs.IsEmpty(); - - // Skip the rest if the node went from no ContextPins to no ContextPins - const bool bMaintainedNoContextPins = !bPrevHasContextPins && !bHasContextPins; - - if (bMaintainedNoContextPins) - { - // We don't have contextual pins to account for; or the contextual pins have not changed. We can skip now. - return; - } - - const FScopedTransaction Transaction(LOCTEXT("RefreshContextPins", "Refresh Context Pins")); - - Modify(); - - const UFlowNode* NodeDefaults = FlowNode->GetClass()->GetDefaultObject(); - - FlowNode->InputPins = NodeDefaults->InputPins; - FlowNode->AddInputPins(ContextInputs); - - FlowNode->OutputPins = NodeDefaults->OutputPins; - FlowNode->AddOutputPins(ContextOutputs); -} void UFlowGraphNode::GetPinHoverText(const UEdGraphPin& Pin, FString& HoverTextOut) const { @@ -1299,68 +1237,6 @@ void UFlowGraphNode::LogError(const FString& MessageToLog, const UFlowNodeBase* } } -bool UFlowGraphNode::HavePinsChanged() const -{ - const UFlowNode* FlowNodeInstance = Cast(NodeInstance); - if (!IsValid(FlowNodeInstance)) - { - // default to having changed because we don't have a way to confirm that the pins have remained intact. - return true; - } - - // Get all pins of the FlowNode itself. We use the CDO because it inherently knows about the built-in pins for this node. - const UFlowNode* FlowNodeCDO = FlowNodeInstance->GetClass()->GetDefaultObject(); - check(IsValid(FlowNodeCDO)); - - TArray AllFlowNodePins = FlowNodeCDO->GetInputPins(); - AllFlowNodePins.Append(FlowNodeCDO->GetOutputPins()); - - AllFlowNodePins.Append(FlowNodeInstance->GetContextInputs()); - AllFlowNodePins.Append(FlowNodeInstance->GetContextOutputs()); - - // Invalid FlowNode pins need to be stripped from the comparison - for (int i = AllFlowNodePins.Num() - 1; i >= 0; --i) - { - if (!AllFlowNodePins[i].IsValid()) - { - AllFlowNodePins.RemoveAtSwap(i, EAllowShrinking::No); - } - } - - // Get the current FlowGraphNode pins list - orphaned pins need to be stripped from the current pins. - TArray AllGraphNodePins = Pins; - for (int i = AllGraphNodePins.Num() - 1; i >= 0; --i) - { - if (AllGraphNodePins[i]->bOrphanedPin) - { - AllGraphNodePins.RemoveAtSwap(i, EAllowShrinking::No); - } - } - - // Compare valid pin counts - if (AllGraphNodePins.Num() != AllFlowNodePins.Num()) - { - return true; - } - - // Compare valid pin names - for (const FFlowPin& FlowNodePin : AllFlowNodePins) - { - if (!AllGraphNodePins.ContainsByPredicate([&FlowNodePin](UEdGraphPin* GraphNodePin) - { - return GraphNodePin->PinName == FlowNodePin.PinName; - })) - { - // Could not match the pin from the flow node with any of the EdPins array. - // we have a mismatch between the ed graph pins and the flow node, something changed. - return true; - } - } - - // Nothing changed - return false; -} - void UFlowGraphNode::ResetNodeOwner() { if (NodeInstance) @@ -1693,6 +1569,8 @@ void UFlowGraphNode::InsertSubNodeAt(UFlowGraphNode* SubNode, int32 DropIndex) void UFlowGraphNode::DestroyNode() { + bIsDestroyingNode = true; + if (ParentNode) { ParentNode->RemoveSubNode(this); @@ -1705,6 +1583,8 @@ void UFlowGraphNode::DestroyNode() } UEdGraphNode::DestroyNode(); + + bIsDestroyingNode = false; } bool UFlowGraphNode::UsesBlueprint() const @@ -1784,36 +1664,217 @@ void UFlowGraphNode::ValidateGraphNode(FFlowMessageLog& MessageLog) const } } -bool UFlowGraphNode::ShouldReconstructNode() const +bool UFlowGraphNode::CanReconstructNode() const { - if (GIsTransacting) + // Global states that should prevent ReconstructNode from running + if (GIsTransacting || bIsReconstructingNode || bIsDestroyingNode) { return false; } - // If the graph is locked, we shouldn't reconstruct nodes - // (all nodes will all be reconstructed when the graph is unlocked) + // This should never happen + if (!ensureMsgf(IsValid(NodeInstance), TEXT("FlowGraphNode has no NodeInstance, graph may be corrupt! Flow Asset: %s"), *GetFlowAsset()->GetName())) + { + return false; + } + + // This should never happen + if (!ensureMsgf(IsValid(GetGraph()), TEXT("FlowGraphNode has no owner graph, graph may be corrupt! Flow Node Instance: %s"), *NodeInstance->GetName())) + { + return false; + } + + // Don't do anything if the Flow Graph is preventing it if (const UFlowGraph* FlowGraph = GetFlowGraph()) { + if (FlowGraph->IsSavingGraph()) + { + return false; + } + if (FlowGraph->IsLocked()) { return false; } } - if (bIsReconstructingNode) + return true; +} + +void CleanInvalidPins(TArray& Array) +{ + for (int i = Array.Num() - 1; i >= 0; --i) + { + if (!Array[i].IsValid()) + { + Array.RemoveAtSwap(i, EAllowShrinking::No); + } + } +} + +void CleanInvalidPins(TArray& Array) +{ + for (int i = Array.Num() - 1; i >= 0; --i) + { + if (Array[i]->bOrphanedPin) + { + Array.RemoveAtSwap(i, EAllowShrinking::No); + } + } +} + +bool CheckPinsMatch(const TArray& LeftPins, const TArray& RightPins) +{ + if (LeftPins.Num() != RightPins.Num()) { return false; } + + for (const FFlowPin& Left : LeftPins) + { + auto PinsAreEqualPredicate = [&Left] (const FFlowPin& Right) + { + bool bNameMatch = Left.PinName == Right.PinName; + bool bTypematch = Left.GetPinType() == Right.GetPinType(); + return bNameMatch && bTypematch; + }; + + // For each required pin, make sure the existing pins array contains a pin that matches by name and type + if (!RightPins.ContainsByPredicate(PinsAreEqualPredicate)) + { + // Something didn't match! + return false; + } + } + + return true; +} - if (!bNeedsFullReconstruction && !HavePinsChanged()) +bool CheckPinsMatch(const TArray& GraphPins, const TArray& NodePins) +{ + // Compare valid pin counts + if (GraphPins.Num() != NodePins.Num()) { return false; } + // Compare valid pin names + for (const FFlowPin& FlowNodePin : NodePins) + { + if (!GraphPins.ContainsByPredicate([&FlowNodePin](UEdGraphPin* GraphNodePin) + { + return GraphNodePin->PinName == FlowNodePin.PinName; + })) + { + // Could not match the pin from the flow node with any of the EdPins array. + // we have a mismatch between the ed graph pins and the flow node, something changed. + return false; + } + } + return true; } +bool UFlowGraphNode::TryUpdateNodePins() const +{ + UFlowNode* FlowNodeInstance = Cast(NodeInstance); + if (!IsValid(FlowNodeInstance)) + { + // default to having changed because we don't have a way to confirm that the pins have remained intact. + return true; + } + + // ------------ + // Get all pins of the FlowNode itself + const UFlowNode* FlowNodeCDO = FlowNodeInstance->GetClass()->GetDefaultObject(); + check(IsValid(FlowNodeCDO)); + + // We grab basic built-in input/output pins from the CDO + // We grab extra required pins from the actual node as generated context pins (this includes both data pins and other context exec pins) + TArray RequiredNodeInputPins = FlowNodeCDO->GetInputPins(); + RequiredNodeInputPins.Append(FlowNodeInstance->GetContextInputs()); + CleanInvalidPins(RequiredNodeInputPins); + + TArray RequiredNodeOutputPins = FlowNodeCDO->GetOutputPins(); + RequiredNodeOutputPins.Append(FlowNodeInstance->GetContextOutputs()); + CleanInvalidPins(RequiredNodeOutputPins); + + // ------------ + // Get all existing pins of the flow node instance + TArray ExistingNodeInputPins = FlowNodeInstance->GetInputPins(); + CleanInvalidPins(ExistingNodeInputPins); + + TArray ExistingNodeOutputPins = FlowNodeInstance->GetOutputPins(); + CleanInvalidPins(ExistingNodeOutputPins); + + // ------------ + // If required pins don't match existing pins, brute force replace them + + bool bPinsChanged = false; + + if (!CheckPinsMatch(RequiredNodeInputPins, ExistingNodeInputPins)) + { + FlowNodeInstance->Modify(); + + FlowNodeInstance->InputPins.Empty(RequiredNodeInputPins.Num()); + FlowNodeInstance->AddInputPins(RequiredNodeInputPins); // We could just copy it, but this function could do more things one day + + bPinsChanged = true; + } + + if (!CheckPinsMatch(RequiredNodeOutputPins, ExistingNodeOutputPins)) + { + FlowNodeInstance->Modify(); + + FlowNodeInstance->OutputPins.Empty(RequiredNodeOutputPins.Num()); + FlowNodeInstance->AddOutputPins(RequiredNodeOutputPins); // We could just copy it, but this function could do more things one day + + bPinsChanged = true; + } + + return bPinsChanged; +} + +bool UFlowGraphNode::TryUpdateAutoDataPins() const +{ + // Attempt to update the manged / auto-generated pins + if (UFlowAsset* FlowAsset = NodeInstance->GetFlowAsset()) + { + if (UFlowNode* FlowNodeInstance = Cast(NodeInstance)) + { + bool bUpdatedManagedFlowPins = FlowAsset->TryUpdateManagedFlowPinsForNode(*FlowNodeInstance); + + if (bUpdatedManagedFlowPins) + { + return true; + } + } + } + + return false; +} + +bool UFlowGraphNode::CheckGraphPinsMatchNodePins() +{ + UFlowNode* FlowNodeInstance = Cast(NodeInstance); + + if (!IsValid(FlowNodeInstance)) + { + return false; + } + + // Get the existing node pins - invalid pins need to be stripped from the check + TArray ExistingNodePins = FlowNodeInstance->GetInputPins(); + ExistingNodePins.Append(FlowNodeInstance->GetOutputPins()); + CleanInvalidPins(ExistingNodePins); + + // Get the current FlowGraphNode pins list - orphaned pins need to be stripped from the check + TArray AllGraphNodePins = Pins; + CleanInvalidPins(AllGraphNodePins); + + return CheckPinsMatch(AllGraphNodePins, ExistingNodePins); +} + bool UFlowGraphNode::IsAncestorNode(const UFlowGraphNode& OtherNode) const { const UFlowGraphNode* CurParentNode = ParentNode; diff --git a/Source/FlowEditor/Public/Graph/FlowGraph.h b/Source/FlowEditor/Public/Graph/FlowGraph.h index c5478d432..5363860d7 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraph.h +++ b/Source/FlowEditor/Public/Graph/FlowGraph.h @@ -38,6 +38,8 @@ class FLOWEDITOR_API UFlowGraph : public UEdGraph // is currently loading the Flow Graph (used to suppress some work during load) uint32 bIsLoadingGraph : 1; + bool bIsSavingGraph = false; + public: static void CreateGraph(UFlowAsset* InFlowAsset); static void CreateGraph(UFlowAsset* InFlowAsset, TSubclassOf FlowSchema); @@ -93,4 +95,6 @@ class FLOWEDITOR_API UFlowGraph : public UEdGraph void UnlockUpdates(); bool IsLoadingGraph() const { return bIsLoadingGraph; } + + bool IsSavingGraph() const { return bIsSavingGraph; } }; diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h index 1c2348744..fa9e2b19f 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h @@ -38,6 +38,7 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode bool bBlueprintCompilationPending; bool bIsReconstructingNode; + bool bIsDestroyingNode; bool bNeedsFullReconstruction; static bool bFlowAssetsLoaded; @@ -164,7 +165,13 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode void ValidateGraphNode(FFlowMessageLog& MessageLog) const; protected: - bool ShouldReconstructNode() const; + bool CanReconstructNode() const; + + bool TryUpdateNodePins() const; + + bool TryUpdateAutoDataPins() const; + + bool CheckGraphPinsMatchNodePins(); ////////////////////////////////////////////////////////////////////////// // Pins @@ -198,10 +205,6 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode // Call node and graph updates manually, if using bBatchRemoval void RemoveInstancePin(UEdGraphPin* Pin); -protected: - // Create pins from the context asset, i.e. Sequencer events - void RefreshContextPins(); - public: // UEdGraphNode virtual void GetPinHoverText(const UEdGraphPin& Pin, FString& HoverTextOut) const override; @@ -312,8 +315,6 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode void LogError(const FString& MessageToLog, const UFlowNodeBase* FlowNodeBase) const; - bool HavePinsChanged() const; - public: /** instance class */ From f4986d2bfb178a33dd2b4b4f7cfddb5213037264 Mon Sep 17 00:00:00 2001 From: MaksymKapelianovych <48297221+MaksymKapelianovych@users.noreply.github.com> Date: Fri, 28 Feb 2025 02:04:37 +0200 Subject: [PATCH 333/485] Flow Traits are now stored locally for every user (#199) Node and pin breakpoints are now stored locally for every user. This is thanks to moving data structures out of UFlowGraphNode, we no longer need to save assets to remember added breakpoints. Data is now stored in .ini and handled by created for this purpose `UFlowDebuggerSettings`. (contributed by Maksym Kapelianovych) * Added a new module to Flow plugin, FlowDebugger which is a DeveloperTool module. This might help with building a separate cook-only graph debugger. If anyone would be willing to implement such a feature, of course. * POTENTIALLY BREAKING CHANGE: FFlowPinTrait struct has been renamed to FFlowBreakpoint and moved to the FlowDebugger module. If you utilize this struct in your custom code, please re-add it to your project code. --- Flow.uplugin | 5 + Source/Flow/Private/Nodes/FlowPin.cpp | 81 ------ Source/Flow/Public/Nodes/FlowPin.h | 44 --- Source/FlowDebugger/FlowDebugger.Build.cs | 26 ++ .../Private/Debugger/FlowDebuggerSettings.cpp | 9 + .../Debugger/FlowDebuggerSubsystem.cpp | 252 ++++++++++++++++++ .../Private/FlowDebuggerModule.cpp | 17 ++ .../Public/Debugger/FlowDebuggerSettings.h | 26 ++ .../Public/Debugger/FlowDebuggerSubsystem.h | 64 +++++ .../Public/Debugger/FlowDebuggerTypes.h | 56 ++++ .../FlowDebugger/Public/FlowDebuggerModule.h | 12 + Source/FlowEditor/FlowEditor.Build.cs | 1 + .../Private/Graph/FlowGraphEditor.cpp | 137 +++++----- .../Private/Graph/FlowGraphEditorSettings.cpp | 9 +- .../Private/Graph/Nodes/FlowGraphNode.cpp | 84 +++--- .../Private/Graph/Widgets/SFlowGraphNode.cpp | 163 ++++++----- .../Public/Asset/FlowDebugEditorSubsystem.h | 11 +- .../FlowEditor/Public/Graph/FlowGraphEditor.h | 6 +- .../Public/Graph/FlowGraphEditorSettings.h | 5 +- .../Public/Graph/Nodes/FlowGraphNode.h | 8 +- .../Public/Graph/Widgets/SFlowGraphNode.h | 28 +- 21 files changed, 694 insertions(+), 350 deletions(-) create mode 100644 Source/FlowDebugger/FlowDebugger.Build.cs create mode 100644 Source/FlowDebugger/Private/Debugger/FlowDebuggerSettings.cpp create mode 100644 Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp create mode 100644 Source/FlowDebugger/Private/FlowDebuggerModule.cpp create mode 100644 Source/FlowDebugger/Public/Debugger/FlowDebuggerSettings.h create mode 100644 Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h create mode 100644 Source/FlowDebugger/Public/Debugger/FlowDebuggerTypes.h create mode 100644 Source/FlowDebugger/Public/FlowDebuggerModule.h diff --git a/Flow.uplugin b/Flow.uplugin index bc3200afd..297f648a9 100644 --- a/Flow.uplugin +++ b/Flow.uplugin @@ -19,6 +19,11 @@ "Type" : "Runtime", "LoadingPhase" : "PreDefault" }, + { + "Name" : "FlowDebugger", + "Type" : "DeveloperTool", + "LoadingPhase" : "PreDefault" + }, { "Name" : "FlowEditor", "Type" : "Editor", diff --git a/Source/Flow/Private/Nodes/FlowPin.cpp b/Source/Flow/Private/Nodes/FlowPin.cpp index ab3b8ba28..f592b4c7f 100644 --- a/Source/Flow/Private/Nodes/FlowPin.cpp +++ b/Source/Flow/Private/Nodes/FlowPin.cpp @@ -51,87 +51,6 @@ FORCEINLINE FString FPinRecord::DoubleDigit(const int32 Number) } #endif -////////////////////////////////////////////////////////////////////////// -// Pin Trait - -void FFlowPinTrait::AllowTrait() -{ - if (!bTraitAllowed) - { - bTraitAllowed = true; - bEnabled = true; - } -} - -void FFlowPinTrait::DisallowTrait() -{ - if (bTraitAllowed) - { - bTraitAllowed = false; - bEnabled = false; - } -} - -bool FFlowPinTrait::IsAllowed() const -{ - return bTraitAllowed; -} - -void FFlowPinTrait::EnableTrait() -{ - if (bTraitAllowed && !bEnabled) - { - bEnabled = true; - } -} - -void FFlowPinTrait::DisableTrait() -{ - if (bTraitAllowed && bEnabled) - { - bEnabled = false; - } -} - -void FFlowPinTrait::ToggleTrait() -{ - if (bTraitAllowed) - { - bTraitAllowed = false; - bEnabled = false; - } - else - { - bTraitAllowed = true; - bEnabled = true; - } -} - -bool FFlowPinTrait::CanEnable() const -{ - return bTraitAllowed && !bEnabled; -} - -bool FFlowPinTrait::IsEnabled() const -{ - return bTraitAllowed && bEnabled; -} - -void FFlowPinTrait::MarkAsHit() -{ - bHit = true; -} - -void FFlowPinTrait::ResetHit() -{ - bHit = false; -} - -bool FFlowPinTrait::IsHit() const -{ - return bHit; -} - ////////////////////////////////////////////////////////////////////////// // Flow Pin diff --git a/Source/Flow/Public/Nodes/FlowPin.h b/Source/Flow/Public/Nodes/FlowPin.h index c5e9bf2bd..7492c5038 100644 --- a/Source/Flow/Public/Nodes/FlowPin.h +++ b/Source/Flow/Public/Nodes/FlowPin.h @@ -402,47 +402,3 @@ struct FLOW_API FPinRecord FORCEINLINE static FString DoubleDigit(const int32 Number); }; #endif - -// It can represent any trait added on the specific node instance, i.e. breakpoint -USTRUCT() -struct FLOW_API FFlowPinTrait -{ - GENERATED_USTRUCT_BODY() - -protected: - UPROPERTY() - uint8 bTraitAllowed : 1; - - uint8 bEnabled : 1; - uint8 bHit : 1; - -public: - FFlowPinTrait() - : bTraitAllowed(false) - , bEnabled(false) - , bHit(false) - { - }; - - explicit FFlowPinTrait(const bool bInitialState) - : bTraitAllowed(bInitialState) - , bEnabled(bInitialState) - , bHit(false) - { - }; - - void AllowTrait(); - void DisallowTrait(); - bool IsAllowed() const; - - void EnableTrait(); - void DisableTrait(); - void ToggleTrait(); - - bool CanEnable() const; - bool IsEnabled() const; - - void MarkAsHit(); - void ResetHit(); - bool IsHit() const; -}; diff --git a/Source/FlowDebugger/FlowDebugger.Build.cs b/Source/FlowDebugger/FlowDebugger.Build.cs new file mode 100644 index 000000000..15450d9c4 --- /dev/null +++ b/Source/FlowDebugger/FlowDebugger.Build.cs @@ -0,0 +1,26 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +using UnrealBuildTool; + +public class FlowDebugger : ModuleRules +{ + public FlowDebugger(ReadOnlyTargetRules target) : base(target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicDependencyModuleNames.AddRange(new[] + { + "Flow" + }); + + PrivateDependencyModuleNames.AddRange(new[] + { + "Core", + "CoreUObject", + "DeveloperSettings", + "Engine", + "Slate", + "SlateCore", + }); + } +} \ No newline at end of file diff --git a/Source/FlowDebugger/Private/Debugger/FlowDebuggerSettings.cpp b/Source/FlowDebugger/Private/Debugger/FlowDebuggerSettings.cpp new file mode 100644 index 000000000..899566135 --- /dev/null +++ b/Source/FlowDebugger/Private/Debugger/FlowDebuggerSettings.cpp @@ -0,0 +1,9 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Debugger/FlowDebuggerSettings.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowDebuggerSettings) + +UFlowDebuggerSettings::UFlowDebuggerSettings() +{ +} diff --git a/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp b/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp new file mode 100644 index 000000000..80e98ca04 --- /dev/null +++ b/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp @@ -0,0 +1,252 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Debugger/FlowDebuggerSubsystem.h" +#include "Debugger/FlowDebuggerSettings.h" + +#include "Editor/UnrealEdEngine.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowDebuggerSubsystem) + +#define LOCTEXT_NAMESPACE "FlowDebuggerSubsystem" + +UFlowDebuggerSubsystem::UFlowDebuggerSubsystem() +{ +} + +bool UFlowDebuggerSubsystem::ShouldCreateSubsystem(UObject* Outer) const +{ + // Only create an instance if there is no override implementation defined elsewhere + TArray ChildClasses; + GetDerivedClasses(GetClass(), ChildClasses, false); + if (ChildClasses.Num() > 0) + { + return false; + } + + return true; +} + +void UFlowDebuggerSubsystem::AddBreakpoint(const UEdGraphNode* Node) +{ + UFlowDebuggerSettings* Settings = GetMutableDefault(); + FFlowBreakpoint& NodeBreakpoint = Settings->NodeBreakpoints.FindOrAdd(Node->NodeGuid); + + NodeBreakpoint.SetEnabled(true); + SaveSettings(); +} + +void UFlowDebuggerSubsystem::AddBreakpoint(const UEdGraphPin* Pin) +{ + UFlowDebuggerSettings* Settings = GetMutableDefault(); + FFlowBreakpoint& PinBreakpoint = Settings->PinBreakpoints.FindOrAdd(Pin->PinId); + + PinBreakpoint.SetEnabled(true); + SaveSettings(); +} + +void UFlowDebuggerSubsystem::RemoveAllBreakpoints(const UEdGraphNode* Node) +{ + UFlowDebuggerSettings* Settings = GetMutableDefault(); + + if (Settings->NodeBreakpoints.Contains(Node->NodeGuid)) + { + Settings->NodeBreakpoints.Remove(Node->NodeGuid); + SaveSettings(); + } + + for (const UEdGraphPin* Pin : Node->Pins) + { + if (Settings->PinBreakpoints.Contains(Pin->PinId)) + { + Settings->PinBreakpoints.Remove(Pin->PinId); + SaveSettings(); + } + } +} + +void UFlowDebuggerSubsystem::RemoveNodeBreakpoint(const UEdGraphNode* Node) +{ + UFlowDebuggerSettings* Settings = GetMutableDefault(); + if (Settings->NodeBreakpoints.Contains(Node->NodeGuid)) + { + Settings->NodeBreakpoints.Remove(Node->NodeGuid); + SaveSettings(); + } +} + +void UFlowDebuggerSubsystem::RemovePinBreakpoint(const UEdGraphPin* Pin) +{ + UFlowDebuggerSettings* Settings = GetMutableDefault(); + if (Settings->PinBreakpoints.Contains(Pin->PinId)) + { + Settings->PinBreakpoints.Remove(Pin->PinId); + SaveSettings(); + } +} + +void UFlowDebuggerSubsystem::RemoveObsoletePinBreakpoints(const UEdGraphNode* Node) +{ + // UFlowDebuggerSettings* Settings = GetMutableDefault(); + // if (FFlowBreakpoint* NodeBreakpoint = Settings->NodeBreakpoints.Find(Node->NodeGuid)) + // { + // TSet PinGuids; + // PinGuids.Reserve(Node->Pins.Num()); + // for (const UEdGraphPin* Pin : Node->Pins) + // { + // PinGuids.Emplace(Pin->PinId); + // } + // + // Settings->PinBreakpoints.RemoveAllSwap([PinGuids](const FFlowBreakpoint& PinBreakpoint) + // { + // return !PinGuids.Contains(PinBreakpoint.GetPinId()); + // }); + // + // // if all settings data is default, we can remove it from the map + // if (*NodeBreakpoint == FFlowBreakpoint()) + // { + // Settings->NodeBreakpoints.Remove(Node->NodeGuid); + // } + // + // SaveSettings(); + // } +} + +void UFlowDebuggerSubsystem::ToggleBreakpoint(const UEdGraphNode* Node) +{ + if (FindBreakpoint(Node) == nullptr) + { + AddBreakpoint(Node); + } + else + { + RemoveNodeBreakpoint(Node); + } +} + +void UFlowDebuggerSubsystem::ToggleBreakpoint(const UEdGraphPin* Pin) +{ + if (FindBreakpoint(Pin) == nullptr) + { + AddBreakpoint(Pin); + } + else + { + RemovePinBreakpoint(Pin); + } +} + +FFlowBreakpoint* UFlowDebuggerSubsystem::FindBreakpoint(const UEdGraphNode* Node) +{ + UFlowDebuggerSettings* Settings = GetMutableDefault(); + return Settings->NodeBreakpoints.Find(Node->NodeGuid); +} + +FFlowBreakpoint* UFlowDebuggerSubsystem::FindBreakpoint(const UEdGraphPin* Pin) +{ + UFlowDebuggerSettings* Settings = GetMutableDefault(); + return Settings->PinBreakpoints.Find(Pin->PinId); +} + +void UFlowDebuggerSubsystem::SetBreakpointEnabled(const UEdGraphNode* Node, const bool bEnabled) +{ + if (FFlowBreakpoint* NodeBreakpoint = FindBreakpoint(Node)) + { + NodeBreakpoint->SetEnabled(bEnabled); + SaveSettings(); + } +} + +void UFlowDebuggerSubsystem::SetBreakpointEnabled(const UEdGraphPin* Pin, const bool bEnabled) +{ + if (FFlowBreakpoint* PinBreakpoint = FindBreakpoint(Pin)) + { + PinBreakpoint->SetEnabled(bEnabled); + SaveSettings(); + } +} + +bool UFlowDebuggerSubsystem::IsBreakpointEnabled(const UEdGraphNode* Node) +{ + if (const FFlowBreakpoint* PinBreakpoint = FindBreakpoint(Node)) + { + return PinBreakpoint->IsEnabled(); + } + + return false; +} + +bool UFlowDebuggerSubsystem::IsBreakpointEnabled(const UEdGraphPin* Pin) +{ + if (const FFlowBreakpoint* PinBreakpoint = FindBreakpoint(Pin)) + { + return PinBreakpoint->IsEnabled(); + } + + return false; +} + +bool UFlowDebuggerSubsystem::MarkAsHit(const UEdGraphNode* Node) +{ + if (FFlowBreakpoint* NodeBreakpoint = FindBreakpoint(Node)) + { + NodeBreakpoint->MarkAsHit(true); + return true; + } + + return false; +} + +bool UFlowDebuggerSubsystem::MarkAsHit(const UEdGraphPin* Pin) +{ + if (FFlowBreakpoint* PinBreakpoint = FindBreakpoint(Pin)) + { + PinBreakpoint->MarkAsHit(true); + return true; + } + + return false; +} + +void UFlowDebuggerSubsystem::ResetHit(const UEdGraphNode* Node) +{ + if (FFlowBreakpoint* NodeBreakpoint = FindBreakpoint(Node)) + { + NodeBreakpoint->MarkAsHit(false); + } +} + +void UFlowDebuggerSubsystem::ResetHit(const UEdGraphPin* Pin) +{ + if (FFlowBreakpoint* PinBreakpoint = FindBreakpoint(Pin)) + { + PinBreakpoint->MarkAsHit(false); + } +} + +bool UFlowDebuggerSubsystem::IsBreakpointHit(const UEdGraphNode* Node) +{ + if (const FFlowBreakpoint* NodeBreakpoint = FindBreakpoint(Node)) + { + return NodeBreakpoint->IsHit(); + } + + return false; +} + +bool UFlowDebuggerSubsystem::IsBreakpointHit(const UEdGraphPin* Pin) +{ + if (const FFlowBreakpoint* PinBreakpoint = FindBreakpoint(Pin)) + { + return PinBreakpoint->IsHit(); + } + + return false; +} + +void UFlowDebuggerSubsystem::SaveSettings() +{ + UFlowDebuggerSettings* Settings = GetMutableDefault(); + Settings->SaveConfig(); +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/FlowDebugger/Private/FlowDebuggerModule.cpp b/Source/FlowDebugger/Private/FlowDebuggerModule.cpp new file mode 100644 index 000000000..b96e61dd5 --- /dev/null +++ b/Source/FlowDebugger/Private/FlowDebuggerModule.cpp @@ -0,0 +1,17 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "FlowDebuggerModule.h" + +#define LOCTEXT_NAMESPACE "FlowDebuggerModule" + +void FFlowDebuggerModule::StartupModule() +{ +} + +void FFlowDebuggerModule::ShutdownModule() +{ +} + +#undef LOCTEXT_NAMESPACE + +IMPLEMENT_MODULE(FFlowDebuggerModule, FlowDebugger) \ No newline at end of file diff --git a/Source/FlowDebugger/Public/Debugger/FlowDebuggerSettings.h b/Source/FlowDebugger/Public/Debugger/FlowDebuggerSettings.h new file mode 100644 index 000000000..7748914bf --- /dev/null +++ b/Source/FlowDebugger/Public/Debugger/FlowDebuggerSettings.h @@ -0,0 +1,26 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Engine/DeveloperSettings.h" + +#include "FlowDebuggerTypes.h" +#include "FlowDebuggerSettings.generated.h" + +/** + * + */ +UCLASS(Config = EditorPerProjectUserSettings, meta = (DisplayName = "Flow Debugger")) +class FLOWDEBUGGER_API UFlowDebuggerSettings : public UDeveloperSettings +{ + GENERATED_BODY() + +public: + UFlowDebuggerSettings(); + + UPROPERTY(config) + TMap NodeBreakpoints; + + UPROPERTY(config) + TMap PinBreakpoints; +}; diff --git a/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h b/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h new file mode 100644 index 000000000..cf92da663 --- /dev/null +++ b/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h @@ -0,0 +1,64 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Subsystems/EngineSubsystem.h" + +#include "Debugger/FlowDebuggerTypes.h" +#include "FlowDebuggerSubsystem.generated.h" + +class UEdGraphNode; +class UEdGraphPin; + +/** + * Persistent subsystem supporting Flow Graph debugging. + * It might be utilized to use cook-specific graph debugger. + */ +UCLASS() +class FLOWDEBUGGER_API UFlowDebuggerSubsystem : public UEngineSubsystem +{ + GENERATED_BODY() + +public: + UFlowDebuggerSubsystem(); + + virtual bool ShouldCreateSubsystem(UObject* Outer) const override; + + virtual void PausePlaySession() {} + virtual bool IsPlaySessionPaused() { return false; } + + virtual void AddBreakpoint(const UEdGraphNode* Node); + virtual void AddBreakpoint(const UEdGraphPin* Pin); + + virtual void RemoveAllBreakpoints(const UEdGraphNode* Node); + virtual void RemoveNodeBreakpoint(const UEdGraphNode* Node); + virtual void RemovePinBreakpoint(const UEdGraphPin* Pin); + + /** Removes obsolete pin breakpoints for provided. Pin list can be changed during node reconstruction. */ + virtual void RemoveObsoletePinBreakpoints(const UEdGraphNode* Node); + + virtual void ToggleBreakpoint(const UEdGraphNode* Node); + virtual void ToggleBreakpoint(const UEdGraphPin* Pin); + + virtual FFlowBreakpoint* FindBreakpoint(const UEdGraphNode* Node); + virtual FFlowBreakpoint* FindBreakpoint(const UEdGraphPin* Pin); + + virtual void SetBreakpointEnabled(const UEdGraphNode* Node, bool bEnabled); + virtual void SetBreakpointEnabled(const UEdGraphPin* Pin, bool bEnabled); + + virtual bool IsBreakpointEnabled(const UEdGraphNode* Node); + virtual bool IsBreakpointEnabled(const UEdGraphPin* Pin); + + virtual bool MarkAsHit(const UEdGraphNode* Node); + virtual bool MarkAsHit(const UEdGraphPin* Pin); + + virtual void ResetHit(const UEdGraphNode* Node); + virtual void ResetHit(const UEdGraphPin* Pin); + + virtual bool IsBreakpointHit(const UEdGraphNode* Node); + virtual bool IsBreakpointHit(const UEdGraphPin* Pin); + +protected: + /** Saves any modifications made to breakpoints */ + virtual void SaveSettings(); +}; diff --git a/Source/FlowDebugger/Public/Debugger/FlowDebuggerTypes.h b/Source/FlowDebugger/Public/Debugger/FlowDebuggerTypes.h new file mode 100644 index 000000000..8173ef9a8 --- /dev/null +++ b/Source/FlowDebugger/Public/Debugger/FlowDebuggerTypes.h @@ -0,0 +1,56 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "FlowDebuggerTypes.generated.h" + +// It can represent any trait added on the specific node instance, i.e. breakpoint +USTRUCT() +struct FLOWDEBUGGER_API FFlowBreakpoint +{ + GENERATED_USTRUCT_BODY() + +protected: + /** Pin that the trait is placed on. Zero filled if this trait is for a node, not a pin */ + UPROPERTY() + FGuid PinId; + + UPROPERTY() + uint8 bEnabled : 1; + + uint8 bHit : 1; + +public: + FFlowBreakpoint() + : bEnabled(false) + , bHit(false) + { + }; + + explicit FFlowBreakpoint(const bool bInitialState) + : bEnabled(bInitialState) + , bHit(false) + { + }; + + explicit FFlowBreakpoint(const FGuid& InPinId, const bool bInitialState) + : PinId(InPinId) + , bEnabled(bInitialState) + , bHit(false) + { + }; + + void SetEnabled(const bool bNowEnabled) { bEnabled = bNowEnabled; } + void MarkAsHit(const bool bNowHit) { bHit = bNowHit; } + + FGuid GetPinId() const { return PinId; } + bool MatchesGuid(const FGuid& OtherGuid) const { return PinId == OtherGuid; } + + bool IsEnabled() const { return bEnabled; } + bool IsHit() const { return bHit; } + + bool operator==(const FFlowBreakpoint& Other) const + { + return PinId == Other.PinId; + } +}; diff --git a/Source/FlowDebugger/Public/FlowDebuggerModule.h b/Source/FlowDebugger/Public/FlowDebuggerModule.h new file mode 100644 index 000000000..d52112483 --- /dev/null +++ b/Source/FlowDebugger/Public/FlowDebuggerModule.h @@ -0,0 +1,12 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Modules/ModuleInterface.h" + +class FLOWDEBUGGER_API FFlowDebuggerModule : public IModuleInterface +{ +public: + virtual void StartupModule() override; + virtual void ShutdownModule() override; +}; diff --git a/Source/FlowEditor/FlowEditor.Build.cs b/Source/FlowEditor/FlowEditor.Build.cs index 54954ebbd..ac2ab93bb 100644 --- a/Source/FlowEditor/FlowEditor.Build.cs +++ b/Source/FlowEditor/FlowEditor.Build.cs @@ -18,6 +18,7 @@ public FlowEditor(ReadOnlyTargetRules target) : base(target) "AssetSearch", "EditorSubsystem", "Flow", + "FlowDebugger", "MessageLog" }); diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index 0babe29ed..eb4a33845 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -3,13 +3,14 @@ #include "Graph/FlowGraphEditor.h" #include "Asset/FlowAssetEditor.h" -#include "Asset/FlowDebugEditorSubsystem.h" #include "FlowEditorCommands.h" #include "Graph/FlowGraphEditorSettings.h" #include "Graph/FlowGraphSchema_Actions.h" #include "Graph/Nodes/FlowGraphNode.h" #include "Nodes/Graph/FlowNode_SubGraph.h" +#include "Debugger/FlowDebuggerSubsystem.h" + #include "EdGraphUtilities.h" #include "Framework/Application/SlateApplication.h" #include "Framework/Commands/GenericCommands.h" @@ -27,9 +28,10 @@ void SFlowGraphEditor::Construct(const FArguments& InArgs, const TSharedPtrGetFlowAsset(); - DetailsView = InArgs._DetailsView; + DebuggerSubsystem = GEngine->GetEngineSubsystem(); + BindGraphCommands(); SGraphEditor::FArguments Arguments; @@ -258,7 +260,7 @@ FGraphAppearanceInfo SFlowGraphEditor::GetGraphAppearanceInfo() const FGraphAppearanceInfo AppearanceInfo; AppearanceInfo.CornerText = GetCornerText(); - if (UFlowDebugEditorSubsystem::IsPlaySessionPaused()) + if (DebuggerSubsystem.IsValid() && DebuggerSubsystem->IsPlaySessionPaused()) { AppearanceInfo.PIENotifyText = LOCTEXT("PausedLabel", "PAUSED"); } @@ -462,6 +464,11 @@ void SFlowGraphEditor::DeleteSelectedNodes() { if (const UFlowGraphNode* FlowGraphNode = Cast(Node)) { + if (DebuggerSubsystem.IsValid()) + { + DebuggerSubsystem->RemoveAllBreakpoints(FlowGraphNode); + } + if (const UFlowNode* FlowNode = Cast(FlowGraphNode->GetFlowNodeBase())) { const FGuid NodeGuid = FlowNode->GetGuid(); @@ -894,7 +901,7 @@ bool SFlowGraphEditor::CanPasteNodesAsSubNodes(const TSet& NodesT // (we assume the rest of the subnode tree is valid when put into the copy buffer) if (NodeToPaste->CopySubNodeParentIndex != INDEX_NONE) { - // a non-INDEX_NONE parent index indicates the subnode is is a non-root subnode in the NodesToPaste set + // a non-INDEX_NONE parent index indicates the subnode is a non-root subnode in the NodesToPaste set continue; } @@ -1087,28 +1094,28 @@ bool SFlowGraphEditor::CanRemovePin() void SFlowGraphEditor::OnAddBreakpoint() const { - for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) + check(DebuggerSubsystem.IsValid()); + for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - SelectedNode->NodeBreakpoint.AllowTrait(); + DebuggerSubsystem->AddBreakpoint(SelectedNode); } } void SFlowGraphEditor::OnAddPinBreakpoint() { - if (UEdGraphPin* Pin = GetGraphPinForMenu()) + check(DebuggerSubsystem.IsValid()); + if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) - { - GraphNode->PinBreakpoints.Add(Pin, FFlowPinTrait(true)); - } + DebuggerSubsystem->AddBreakpoint(Pin); } } bool SFlowGraphEditor::CanAddBreakpoint() const { + check(DebuggerSubsystem.IsValid()); for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - return !SelectedNode->NodeBreakpoint.IsAllowed(); + return DebuggerSubsystem->FindBreakpoint(SelectedNode) == nullptr; } return false; @@ -1116,12 +1123,10 @@ bool SFlowGraphEditor::CanAddBreakpoint() const bool SFlowGraphEditor::CanAddPinBreakpoint() { - if (UEdGraphPin* Pin = GetGraphPinForMenu()) + check(DebuggerSubsystem.IsValid()); + if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) - { - return !GraphNode->PinBreakpoints.Contains(Pin) || !GraphNode->PinBreakpoints[Pin].IsAllowed(); - } + return DebuggerSubsystem->FindBreakpoint(Pin) == nullptr; } return false; @@ -1129,28 +1134,28 @@ bool SFlowGraphEditor::CanAddPinBreakpoint() void SFlowGraphEditor::OnRemoveBreakpoint() const { - for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) + check(DebuggerSubsystem.IsValid()); + for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - SelectedNode->NodeBreakpoint.DisallowTrait(); + DebuggerSubsystem->RemoveNodeBreakpoint(SelectedNode); } } void SFlowGraphEditor::OnRemovePinBreakpoint() { - if (UEdGraphPin* Pin = GetGraphPinForMenu()) + check(DebuggerSubsystem.IsValid()); + if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) - { - GraphNode->PinBreakpoints.Remove(Pin); - } + DebuggerSubsystem->RemovePinBreakpoint(Pin); } } bool SFlowGraphEditor::CanRemoveBreakpoint() const { + check(DebuggerSubsystem.IsValid()); for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - return SelectedNode->NodeBreakpoint.IsAllowed(); + return DebuggerSubsystem->FindBreakpoint(SelectedNode) != nullptr; } return false; @@ -1158,12 +1163,10 @@ bool SFlowGraphEditor::CanRemoveBreakpoint() const bool SFlowGraphEditor::CanRemovePinBreakpoint() { - if (UEdGraphPin* Pin = GetGraphPinForMenu()) + check(DebuggerSubsystem.IsValid()); + if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - if (const UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) - { - return GraphNode->PinBreakpoints.Contains(Pin); - } + return DebuggerSubsystem->FindBreakpoint(Pin) != nullptr; } return false; @@ -1171,36 +1174,28 @@ bool SFlowGraphEditor::CanRemovePinBreakpoint() void SFlowGraphEditor::OnEnableBreakpoint() const { - for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) + check(DebuggerSubsystem.IsValid()); + for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - SelectedNode->NodeBreakpoint.EnableTrait(); + DebuggerSubsystem->SetBreakpointEnabled(SelectedNode, true); } } void SFlowGraphEditor::OnEnablePinBreakpoint() { - if (UEdGraphPin* Pin = GetGraphPinForMenu()) + check(DebuggerSubsystem.IsValid()); + if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) - { - GraphNode->PinBreakpoints[Pin].EnableTrait(); - } + DebuggerSubsystem->SetBreakpointEnabled(Pin, true); } } -bool SFlowGraphEditor::CanEnableBreakpoint() +bool SFlowGraphEditor::CanEnableBreakpoint() const { - if (UEdGraphPin* Pin = GetGraphPinForMenu()) - { - if (const UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) - { - return GraphNode->PinBreakpoints.Contains(Pin); - } - } - for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - return SelectedNode->NodeBreakpoint.CanEnable(); + const FFlowBreakpoint* Breakpoint = DebuggerSubsystem->FindBreakpoint(SelectedNode); + return Breakpoint && !Breakpoint->IsEnabled(); } return false; @@ -1208,12 +1203,10 @@ bool SFlowGraphEditor::CanEnableBreakpoint() bool SFlowGraphEditor::CanEnablePinBreakpoint() { - if (UEdGraphPin* Pin = GetGraphPinForMenu()) + if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) - { - return GraphNode->PinBreakpoints.Contains(Pin) && GraphNode->PinBreakpoints[Pin].CanEnable(); - } + const FFlowBreakpoint* Breakpoint = DebuggerSubsystem->FindBreakpoint(Pin); + return Breakpoint && !Breakpoint->IsEnabled(); } return false; @@ -1221,28 +1214,29 @@ bool SFlowGraphEditor::CanEnablePinBreakpoint() void SFlowGraphEditor::OnDisableBreakpoint() const { - for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) + check(DebuggerSubsystem.IsValid()); + for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - SelectedNode->NodeBreakpoint.DisableTrait(); + DebuggerSubsystem->SetBreakpointEnabled(SelectedNode, false); } } void SFlowGraphEditor::OnDisablePinBreakpoint() { - if (UEdGraphPin* Pin = GetGraphPinForMenu()) + check(DebuggerSubsystem.IsValid()); + if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) - { - GraphNode->PinBreakpoints[Pin].DisableTrait(); - } + DebuggerSubsystem->SetBreakpointEnabled(Pin, false); } } bool SFlowGraphEditor::CanDisableBreakpoint() const { + check(DebuggerSubsystem.IsValid()); for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - return SelectedNode->NodeBreakpoint.IsEnabled(); + const FFlowBreakpoint* Breakpoint = DebuggerSubsystem->FindBreakpoint(SelectedNode); + return Breakpoint && Breakpoint->IsEnabled(); } return false; @@ -1250,12 +1244,11 @@ bool SFlowGraphEditor::CanDisableBreakpoint() const bool SFlowGraphEditor::CanDisablePinBreakpoint() { - if (UEdGraphPin* Pin = GetGraphPinForMenu()) + check(DebuggerSubsystem.IsValid()); + if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) - { - return GraphNode->PinBreakpoints.Contains(Pin) && GraphNode->PinBreakpoints[Pin].IsEnabled(); - } + const FFlowBreakpoint* Breakpoint = DebuggerSubsystem->FindBreakpoint(Pin); + return Breakpoint && Breakpoint->IsEnabled(); } return false; @@ -1263,21 +1256,19 @@ bool SFlowGraphEditor::CanDisablePinBreakpoint() void SFlowGraphEditor::OnToggleBreakpoint() const { - for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) + check(DebuggerSubsystem.IsValid()); + for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - SelectedNode->NodeBreakpoint.ToggleTrait(); + DebuggerSubsystem->ToggleBreakpoint(SelectedNode); } } void SFlowGraphEditor::OnTogglePinBreakpoint() { - if (UEdGraphPin* Pin = GetGraphPinForMenu()) + check(DebuggerSubsystem.IsValid()); + if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - if (UFlowGraphNode* GraphNode = Cast(Pin->GetOwningNode())) - { - GraphNode->PinBreakpoints.Add(Pin, FFlowPinTrait()); - GraphNode->PinBreakpoints[Pin].ToggleTrait(); - } + DebuggerSubsystem->ToggleBreakpoint(Pin); } } diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp index 65bcd4428..766ed1b87 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp @@ -5,9 +5,8 @@ #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraphEditorSettings) -UFlowGraphEditorSettings::UFlowGraphEditorSettings(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) - , NodeDoubleClickTarget(EFlowNodeDoubleClickTarget::PrimaryAssetOrNodeDefinition) +UFlowGraphEditorSettings::UFlowGraphEditorSettings() + : NodeDoubleClickTarget(EFlowNodeDoubleClickTarget::PrimaryAssetOrNodeDefinition) , bShowNodeClass(false) , bShowNodeDescriptionWhilePlaying(true) , bEnforceFriendlyPinNames(false) @@ -24,8 +23,8 @@ UFlowGraphEditorSettings::UFlowGraphEditorSettings(const FObjectInitializer& Obj void UFlowGraphEditorSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { Super::PostEditChangeProperty(PropertyChangedEvent); - - if (PropertyChangedEvent.GetMemberPropertyName() == GET_MEMBER_NAME_CHECKED( UFlowGraphEditorSettings, bShowNodeClass )) + + if (PropertyChangedEvent.GetMemberPropertyName() == GET_MEMBER_NAME_CHECKED(UFlowGraphEditorSettings, bShowNodeClass)) { GetDefault()->ForceVisualizationCacheClear(); } diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index ccf857277..7657be3b1 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -2,8 +2,10 @@ #include "Graph/Nodes/FlowGraphNode.h" +#include "FlowAsset.h" #include "AddOns/FlowNodeAddOn.h" -#include "Asset/FlowDebugEditorSubsystem.h" +#include "Nodes/FlowNode.h" + #include "FlowEditorCommands.h" #include "Graph/FlowGraph.h" #include "Graph/FlowGraphEditorSettings.h" @@ -11,8 +13,8 @@ #include "Graph/FlowGraphSettings.h" #include "Graph/Widgets/SFlowGraphNode.h" #include "Graph/Widgets/SGraphEditorActionMenuFlow.h" -#include "FlowAsset.h" -#include "Nodes/FlowNode.h" + +#include "Debugger/FlowDebuggerSubsystem.h" #include "BlueprintNodeHelpers.h" #include "Developer/ToolMenus/Public/ToolMenus.h" @@ -60,7 +62,7 @@ UFlowNodeBase* UFlowGraphNode::GetFlowNodeBase() const { if (NodeInstance) { - if (UFlowNode* FlowNode = Cast(NodeInstance)) + if (const UFlowNode* FlowNode = Cast(NodeInstance)) { if (const UFlowAsset* InspectedInstance = FlowNode->GetFlowAsset()->GetInspectedInstance()) { @@ -321,6 +323,12 @@ void UFlowGraphNode::ReconstructNode() DestroyPin(OldPin); } + // clear breakpoints for destroyed pins + if (UFlowDebuggerSubsystem* DebuggerSubsystem = GEngine->GetEngineSubsystem()) + { + DebuggerSubsystem->RemoveObsoletePinBreakpoints(this); + } + bNeedsFullReconstruction = false; } @@ -428,6 +436,7 @@ void UFlowGraphNode::RewireOldPinsToNewPins(TArray& InOldPins) OutputPins.Add(OrphanedPin); break; } + default: ; } } } @@ -439,16 +448,6 @@ void UFlowGraphNode::ReconstructSinglePin(UEdGraphPin* NewPin, UEdGraphPin* OldP // Copy over modified persistent data NewPin->MovePersistentDataFromOldPin(*OldPin); - - // Update the in breakpoints as the old pin will be going the way of the dodo - for (TPair& PinBreakpoint : PinBreakpoints) - { - if (PinBreakpoint.Key.Get() == OldPin) - { - PinBreakpoint.Key = NewPin; - break; - } - } } void UFlowGraphNode::GetNodeContextMenuActions(class UToolMenu* Menu, class UGraphNodeContextMenuContext* Context) const @@ -895,7 +894,10 @@ void UFlowGraphNode::RemoveOrphanedPin(UEdGraphPin* Pin) const FScopedTransaction Transaction(LOCTEXT("RemoveOrphanedPin", "Remove Orphaned Pin")); Modify(); - PinBreakpoints.Remove(Pin); + if (UFlowDebuggerSubsystem* DebuggerSubsystem = GEngine->GetEngineSubsystem()) + { + DebuggerSubsystem->RemovePinBreakpoint(Pin); + } Pin->MarkAsGarbage(); Pins.Remove(Pin); @@ -989,7 +991,10 @@ void UFlowGraphNode::RemoveInstancePin(UEdGraphPin* Pin) const FScopedTransaction Transaction(LOCTEXT("RemoveInstancePin", "Remove Instance Pin")); Modify(); - PinBreakpoints.Remove(Pin); + if (UFlowDebuggerSubsystem* DebuggerSubsystem = GEngine->GetEngineSubsystem()) + { + DebuggerSubsystem->RemovePinBreakpoint(Pin); + } UFlowNode* FlowNode = Cast(NodeInstance); if (Pin->Direction == EGPD_Input) @@ -1019,7 +1024,6 @@ void UFlowGraphNode::RemoveInstancePin(UEdGraphPin* Pin) GetGraph()->NotifyNodeChanged(this); } - void UFlowGraphNode::GetPinHoverText(const UEdGraphPin& Pin, FString& HoverTextOut) const { // start with the default hover text (from the pin's tool-tip) @@ -1073,10 +1077,16 @@ const FName& UFlowGraphNode::GetPinCategoryFromFlowPin(const FFlowPin& FlowPin) void UFlowGraphNode::OnInputTriggered(const int32 Index) { - if (InputPins.IsValidIndex(Index) && PinBreakpoints.Contains(InputPins[Index])) + if (InputPins.IsValidIndex(Index)) { - PinBreakpoints[InputPins[Index]].MarkAsHit(); - TryPausingSession(true); + if (UFlowDebuggerSubsystem* DebuggerSubsystem = GEngine->GetEngineSubsystem()) + { + if (DebuggerSubsystem->MarkAsHit(InputPins[Index])) + { + TryPausingSession(true); + } + } + } TryPausingSession(false); @@ -1084,10 +1094,15 @@ void UFlowGraphNode::OnInputTriggered(const int32 Index) void UFlowGraphNode::OnOutputTriggered(const int32 Index) { - if (OutputPins.IsValidIndex(Index) && PinBreakpoints.Contains(OutputPins[Index])) + if (OutputPins.IsValidIndex(Index)) { - PinBreakpoints[OutputPins[Index]].MarkAsHit(); - TryPausingSession(true); + if (UFlowDebuggerSubsystem* DebuggerSubsystem = GEngine->GetEngineSubsystem()) + { + if (DebuggerSubsystem->MarkAsHit(OutputPins[Index])) + { + TryPausingSession(true); + } + } } TryPausingSession(false); @@ -1096,10 +1111,13 @@ void UFlowGraphNode::OnOutputTriggered(const int32 Index) void UFlowGraphNode::TryPausingSession(bool bPauseSession) { // Node breakpoints waits on any pin triggered - if (NodeBreakpoint.IsEnabled()) + UFlowDebuggerSubsystem* DebuggerSubsystem = GEngine->GetEngineSubsystem(); + if (DebuggerSubsystem) { - NodeBreakpoint.MarkAsHit(); - bPauseSession = true; + if (DebuggerSubsystem->MarkAsHit(this)) + { + bPauseSession = true; + } } if (bPauseSession) @@ -1107,7 +1125,10 @@ void UFlowGraphNode::TryPausingSession(bool bPauseSession) FEditorDelegates::ResumePIE.AddUObject(this, &UFlowGraphNode::OnResumePIE); FEditorDelegates::EndPIE.AddUObject(this, &UFlowGraphNode::OnEndPIE); - UFlowDebugEditorSubsystem::PausePlaySession(); + if (DebuggerSubsystem) + { + DebuggerSubsystem->PausePlaySession(); + } } } @@ -1126,10 +1147,13 @@ void UFlowGraphNode::ResetBreakpoints() FEditorDelegates::ResumePIE.RemoveAll(this); FEditorDelegates::EndPIE.RemoveAll(this); - NodeBreakpoint.ResetHit(); - for (TPair& PinBreakpoint : PinBreakpoints) + if (UFlowDebuggerSubsystem* DebuggerSubsystem = GEngine->GetEngineSubsystem()) { - PinBreakpoint.Value.ResetHit(); + DebuggerSubsystem->ResetHit(this); + for (const UEdGraphPin* Pin : Pins) + { + DebuggerSubsystem->ResetHit(Pin); + } } } diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp index 6ca9ca6b7..718fb800b 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp @@ -5,8 +5,11 @@ #include "FlowEditorStyle.h" #include "Graph/FlowGraph.h" #include "Graph/FlowGraphSettings.h" + #include "Nodes/FlowNode.h" +#include "Debugger/FlowDebuggerSubsystem.h" + #include "EdGraph/EdGraphPin.h" #include "Editor.h" #include "GraphEditorSettings.h" @@ -53,9 +56,10 @@ const FLinearColor SFlowGraphNode::ConfigBoxColor = FLinearColor(0.04f, 0.04f, 0 void SFlowGraphNode::Construct(const FArguments& InArgs, UFlowGraphNode* InNode) { GraphNode = InNode; - FlowGraphNode = InNode; + DebuggerSubsystem = GEngine->GetEngineSubsystem(); + check(FlowGraphNode); FlowGraphNode->OnSignalModeChanged.BindRaw(this, &SFlowGraphNode::UpdateGraphNode); FlowGraphNode->OnReconstructNodeCompleted.BindRaw(this, &SFlowGraphNode::UpdateGraphNode); @@ -122,19 +126,21 @@ const FSlateBrush* SFlowGraphNode::GetShadowBrush(bool bSelected) const void SFlowGraphNode::GetOverlayBrushes(bool bSelected, const FVector2D WidgetSize, TArray& Brushes) const { + check(DebuggerSubsystem.IsValid()); + // Node breakpoint - if (FlowGraphNode->NodeBreakpoint.IsAllowed()) + if (const FFlowBreakpoint* NodeBreakpoint = DebuggerSubsystem->FindBreakpoint(FlowGraphNode)) { FOverlayBrushInfo NodeBrush; - if (FlowGraphNode->NodeBreakpoint.IsHit()) + if (NodeBreakpoint->IsHit()) { NodeBrush.Brush = FFlowEditorStyle::Get()->GetBrush(TEXT("FlowGraph.BreakpointHit")); NodeBrush.OverlayOffset.X = WidgetSize.X - 12.0f; } else { - NodeBrush.Brush = FFlowEditorStyle::Get()->GetBrush(FlowGraphNode->NodeBreakpoint.IsEnabled() ? TEXT("FlowGraph.BreakpointEnabled") : TEXT("FlowGraph.BreakpointDisabled")); + NodeBrush.Brush = FFlowEditorStyle::Get()->GetBrush(NodeBreakpoint->IsEnabled() ? TEXT("FlowGraph.BreakpointEnabled") : TEXT("FlowGraph.BreakpointDisabled")); NodeBrush.OverlayOffset.X = WidgetSize.X; } @@ -144,41 +150,41 @@ void SFlowGraphNode::GetOverlayBrushes(bool bSelected, const FVector2D WidgetSiz } // Pin breakpoints - for (const TPair& PinBreakpoint : FlowGraphNode->PinBreakpoints) + for (UEdGraphPin* Pin : FlowGraphNode->Pins) { - if (PinBreakpoint.Key.Get()->Direction == EGPD_Input) + if (const FFlowBreakpoint* PinBreakpoint = DebuggerSubsystem->FindBreakpoint(Pin)) { - GetPinBrush(true, WidgetSize.X, FlowGraphNode->InputPins.IndexOfByKey(PinBreakpoint.Key.Get()), PinBreakpoint.Value, Brushes); - } - else - { - GetPinBrush(false, WidgetSize.X, FlowGraphNode->OutputPins.IndexOfByKey(PinBreakpoint.Key.Get()), PinBreakpoint.Value, Brushes); + if (Pin->Direction == EGPD_Input) + { + GetPinBrush(true, WidgetSize.X, FlowGraphNode->InputPins.IndexOfByKey(Pin), PinBreakpoint, Brushes); + } + else + { + GetPinBrush(false, WidgetSize.X, FlowGraphNode->OutputPins.IndexOfByKey(Pin), PinBreakpoint, Brushes); + } } } } -void SFlowGraphNode::GetPinBrush(const bool bLeftSide, const float WidgetWidth, const int32 PinIndex, const FFlowPinTrait& Breakpoint, TArray& Brushes) const +void SFlowGraphNode::GetPinBrush(const bool bLeftSide, const float WidgetWidth, const int32 PinIndex, const FFlowBreakpoint* Breakpoint, TArray& Brushes) const { - if (Breakpoint.IsAllowed()) - { - FOverlayBrushInfo PinBrush; - - if (Breakpoint.IsHit()) - { - PinBrush.Brush = FFlowEditorStyle::Get()->GetBrush(TEXT("FlowGraph.PinBreakpointHit")); - PinBrush.OverlayOffset.X = bLeftSide ? 0.0f : (WidgetWidth - 36.0f); - PinBrush.OverlayOffset.Y = 12.0f + PinIndex * 28.0f; - } - else - { - PinBrush.Brush = FFlowEditorStyle::Get()->GetBrush(Breakpoint.IsEnabled() ? TEXT("FlowGraph.BreakpointEnabled") : TEXT("FlowGraph.BreakpointDisabled")); - PinBrush.OverlayOffset.X = bLeftSide ? -24.0f : WidgetWidth; - PinBrush.OverlayOffset.Y = 16.0f + PinIndex * 28.0f; - } + FOverlayBrushInfo PinBrush; - PinBrush.AnimationEnvelope = FVector2D(0.f, 10.f); - Brushes.Add(PinBrush); + if (Breakpoint->IsHit()) + { + PinBrush.Brush = FFlowEditorStyle::Get()->GetBrush(TEXT("FlowGraph.PinBreakpointHit")); + PinBrush.OverlayOffset.X = bLeftSide ? 0.0f : (WidgetWidth - 36.0f); + PinBrush.OverlayOffset.Y = 12.0f + PinIndex * 28.0f; + } + else + { + PinBrush.Brush = FFlowEditorStyle::Get()->GetBrush(Breakpoint->IsEnabled() ? TEXT("FlowGraph.BreakpointEnabled") : TEXT("FlowGraph.BreakpointDisabled")); + PinBrush.OverlayOffset.X = bLeftSide ? -24.0f : WidgetWidth; + PinBrush.OverlayOffset.Y = 16.0f + PinIndex * 28.0f; } + + PinBrush.AnimationEnvelope = FVector2D(0.f, 10.f); + Brushes.Add(PinBrush); } BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION @@ -188,7 +194,7 @@ void SFlowGraphNode::UpdateGraphNode() InputPins.Empty(); OutputPins.Empty(); - // Reset variables that are going to be exposed, in case we are refreshing an already setup node. + // Reset variables that are going to be exposed, in case we are refreshing an already set node. RightNodeBox.Reset(); LeftNodeBox.Reset(); @@ -278,7 +284,7 @@ void SFlowGraphNode::UpdateGraphNode() DefaultTitleAreaWidget ]; - // Setup a meta tag for this node + // Set up a meta tag for this node FGraphNodeMetaData TagMeta(TEXT("FlowGraphNode")); PopulateMetaTag(&TagMeta); @@ -399,7 +405,7 @@ FSlateColor SFlowGraphNode::GetConfigBoxBackgroundColor() const return NodeColor; } -void SFlowGraphNode::CreateBelowPinControls(TSharedPtr InnerVerticalBox) +void SFlowGraphNode::CreateBelowPinControls(const TSharedPtr InnerVerticalBox) { static const FMargin ConfigBoxPadding = FMargin(2.0f, 0.0f, 1.0f, 0.0); @@ -423,7 +429,7 @@ void SFlowGraphNode::CreateBelowPinControls(TSharedPtr InnerVertic CreateOrRebuildSubNodeBox(InnerVerticalBox); } -void SFlowGraphNode::AddSubNodeWidget(TSharedPtr NewSubNodeWidget) +void SFlowGraphNode::AddSubNodeWidget(const TSharedPtr& NewSubNodeWidget) { if (OwnerGraphPanelPtr.IsValid()) { @@ -457,8 +463,8 @@ FMargin SFlowGraphNode::ComputeSubNodeChildIndentPaddingMargin() const constexpr float HorizontalDefaultPadding = 2.0f; constexpr float IndentedHorizontalPadding = 6.0f; constexpr float RightPadding = HorizontalDefaultPadding; - float LeftPadding = HorizontalDefaultPadding; - + + float LeftPadding; if (ParentDepth > 0) { // Increase the padding by the parent depth for this node @@ -469,15 +475,10 @@ FMargin SFlowGraphNode::ComputeSubNodeChildIndentPaddingMargin() const LeftPadding = 0.0f; } - return - FMargin( - LeftPadding, - VerticalDefaultPadding, - RightPadding, - VerticalDefaultPadding); + return FMargin(LeftPadding, VerticalDefaultPadding, RightPadding, VerticalDefaultPadding); } -void SFlowGraphNode::CreateConfigText(TSharedPtr InnerVerticalBox) +void SFlowGraphNode::CreateConfigText(const TSharedPtr& InnerVerticalBox) { static const FMargin ConfigTextPadding = FMargin(2.0f, 0.0f, 0.0f, 3.0f); @@ -494,12 +495,9 @@ void SFlowGraphNode::CreateConfigText(TSharedPtr InnerVerticalBox) FText SFlowGraphNode::GetNodeConfigText() const { - if (const UFlowGraphNode* MyNode = CastChecked(GraphNode)) + if (const UFlowNodeBase* FlowNodeBase = FlowGraphNode->GetFlowNodeBase()) { - if (UFlowNodeBase* NodeInstance = MyNode->GetFlowNodeBase()) - { - return NodeInstance->GetNodeConfigText(); - } + return FlowNodeBase->GetNodeConfigText(); } return FText::GetEmpty(); @@ -520,7 +518,7 @@ EVisibility SFlowGraphNode::GetNodeConfigTextVisibility() const return EVisibility::Collapsed; } -void SFlowGraphNode::CreateOrRebuildSubNodeBox(TSharedPtr InnerVerticalBox) +void SFlowGraphNode::CreateOrRebuildSubNodeBox(const TSharedPtr& InnerVerticalBox) { if (SubNodeBox.IsValid()) { @@ -753,7 +751,7 @@ TSharedPtr SFlowGraphNode::GetComplexTooltip() return IDocumentation::Get()->CreateToolTip(TAttribute(this, &SGraphNode::GetNodeTooltip), nullptr, GraphNode->GetDocumentationLink(), GraphNode->GetDocumentationExcerptName()); } -void SFlowGraphNode::CreateInputSideAddButton(TSharedPtr OutputBox) +void SFlowGraphNode::CreateInputSideAddButton(const TSharedPtr OutputBox) { if (FlowGraphNode->CanUserAddInput()) { @@ -780,7 +778,7 @@ void SFlowGraphNode::CreateInputSideAddButton(TSharedPtr OutputBox } } -void SFlowGraphNode::CreateOutputSideAddButton(TSharedPtr OutputBox) +void SFlowGraphNode::CreateOutputSideAddButton(const TSharedPtr OutputBox) { if (FlowGraphNode->CanUserAddOutput()) { @@ -864,7 +862,7 @@ FReply SFlowGraphNode::OnAddFlowPin(const EEdGraphPinDirection Direction) return FReply::Handled(); } -void SFlowGraphNode::AddSubNode(TSharedPtr SubNodeWidget) +void SFlowGraphNode::AddSubNode(const TSharedPtr SubNodeWidget) { SubNodes.Add(SubNodeWidget); @@ -881,15 +879,14 @@ FText SFlowGraphNode::GetTitle() const FText SFlowGraphNode::GetDescription() const { - UFlowGraphNode* MyNode = CastChecked(GraphNode); - return MyNode ? MyNode->GetDescription() : FText::GetEmpty(); + return FlowGraphNode ? FlowGraphNode->GetDescription() : FText::GetEmpty(); } EVisibility SFlowGraphNode::GetDescriptionVisibility() const { // LOD this out once things get too small - TSharedPtr MyOwnerPanel = GetOwnerPanel(); - return (!MyOwnerPanel.IsValid() || MyOwnerPanel->GetCurrentLOD() > EGraphRenderingLOD::LowDetail) ? EVisibility::Visible : EVisibility::Collapsed; + const TSharedPtr OwnerPanel = GetOwnerPanel(); + return (!OwnerPanel.IsValid() || OwnerPanel->GetCurrentLOD() > EGraphRenderingLOD::LowDetail) ? EVisibility::Visible : EVisibility::Collapsed; } void SFlowGraphNode::AddPin(const TSharedRef& PinToAdd) @@ -897,8 +894,7 @@ void SFlowGraphNode::AddPin(const TSharedRef& PinToAdd) PinToAdd->SetOwner(SharedThis(this)); const UEdGraphPin* PinObj = PinToAdd->GetPinObj(); - const bool bAdvancedParameter = PinObj && PinObj->bAdvancedView; - if (bAdvancedParameter) + if (PinObj && PinObj->bAdvancedView) { PinToAdd->SetVisibility(TAttribute(PinToAdd, &SGraphPin::IsPinVisibleAsAdvanced)); } @@ -934,8 +930,7 @@ FReply SFlowGraphNode::OnMouseMove(const FGeometry& SenderGeometry, const FPoint if (MouseEvent.IsMouseButtonDown(EKeys::LeftMouseButton) && !(GEditor->bIsSimulatingInEditor || GEditor->PlayWorld)) { // if we are holding mouse over a subnode - UFlowGraphNode* TestNode = Cast(GraphNode); - if (TestNode && TestNode->IsSubNode()) + if (FlowGraphNode && FlowGraphNode->IsSubNode()) { const TSharedRef& Panel = GetOwnerPanel().ToSharedRef(); const TSharedRef& Node = SharedThis(this); @@ -966,10 +961,9 @@ TSharedRef SFlowGraphNode::GetNodeUnderMouse(const FGeometry& MyGeom FReply SFlowGraphNode::OnMouseButtonDown(const FGeometry& SenderGeometry, const FPointerEvent& MouseEvent) { - UFlowGraphNode* TestNode = Cast(GraphNode); - if (TestNode && TestNode->IsSubNode()) + if (FlowGraphNode && FlowGraphNode->IsSubNode()) { - GetOwnerPanel()->SelectionManager.ClickedOnNode(GraphNode, MouseEvent); + GetOwnerPanel()->SelectionManager.ClickedOnNode(FlowGraphNode, MouseEvent); return FReply::Handled(); } @@ -1009,9 +1003,7 @@ TSharedPtr SFlowGraphNode::GetSubNodeUnderCursor(const FGeometry& Wi // Recurse if the subnode has subnodes SFlowGraphNode* ResultFlowGraphNode = static_cast(ResultNode.Get()); const FGeometry& ChildWidgetGeometry = ArrangedChildren[HoveredIndex].Geometry; - TSharedPtr ResultFlowGraphNodeSubNode = ResultFlowGraphNode->GetSubNodeUnderCursor(ChildWidgetGeometry, MouseEvent); - - if (ResultFlowGraphNodeSubNode) + if (TSharedPtr ResultFlowGraphNodeSubNode = ResultFlowGraphNode->GetSubNodeUnderCursor(ChildWidgetGeometry, MouseEvent)) { return ResultFlowGraphNodeSubNode; } @@ -1019,7 +1011,7 @@ TSharedPtr SFlowGraphNode::GetSubNodeUnderCursor(const FGeometry& Wi return ResultNode; } -void SFlowGraphNode::SetDragMarker(bool bEnabled) +void SFlowGraphNode::SetDragMarker(const bool bEnabled) { bDragMarkerVisible = bEnabled; } @@ -1032,15 +1024,14 @@ EVisibility SFlowGraphNode::GetDragOverMarkerVisibility() const void SFlowGraphNode::OnDragEnter(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) { // Is someone dragging a node? - TSharedPtr DragConnectionOp = DragDropEvent.GetOperationAs(); + const TSharedPtr DragConnectionOp = DragDropEvent.GetOperationAs(); if (DragConnectionOp.IsValid()) { // Inform the Drag and Drop operation that we are hovering over this node. - TSharedPtr SubNode = GetSubNodeUnderCursor(MyGeometry, DragDropEvent); + const TSharedPtr SubNode = GetSubNodeUnderCursor(MyGeometry, DragDropEvent); DragConnectionOp->SetHoveredNode(SubNode.IsValid() ? SubNode : SharedThis(this)); - UFlowGraphNode* TestNode = Cast(GraphNode); - if (DragConnectionOp->IsValidOperation() && TestNode && TestNode->IsSubNode()) + if (DragConnectionOp->IsValidOperation() && FlowGraphNode && FlowGraphNode->IsSubNode()) { SetDragMarker(true); } @@ -1052,11 +1043,11 @@ void SFlowGraphNode::OnDragEnter(const FGeometry& MyGeometry, const FDragDropEve FReply SFlowGraphNode::OnDragOver(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) { // Is someone dragging a node? - TSharedPtr DragConnectionOp = DragDropEvent.GetOperationAs(); + const TSharedPtr DragConnectionOp = DragDropEvent.GetOperationAs(); if (DragConnectionOp.IsValid()) { // Inform the Drag and Drop operation that we are hovering over this node. - TSharedPtr SubNode = GetSubNodeUnderCursor(MyGeometry, DragDropEvent); + const TSharedPtr SubNode = GetSubNodeUnderCursor(MyGeometry, DragDropEvent); DragConnectionOp->SetHoveredNode(SubNode.IsValid() ? SubNode : SharedThis(this)); } return SGraphNode::OnDragOver(MyGeometry, DragDropEvent); @@ -1064,11 +1055,11 @@ FReply SFlowGraphNode::OnDragOver(const FGeometry& MyGeometry, const FDragDropEv void SFlowGraphNode::OnDragLeave(const FDragDropEvent& DragDropEvent) { - TSharedPtr DragConnectionOp = DragDropEvent.GetOperationAs(); + const TSharedPtr DragConnectionOp = DragDropEvent.GetOperationAs(); if (DragConnectionOp.IsValid()) { // Inform the Drag and Drop operation that we are not hovering any pins - DragConnectionOp->SetHoveredNode(TSharedPtr(NULL)); + DragConnectionOp->SetHoveredNode(TSharedPtr(nullptr)); } SetDragMarker(false); @@ -1079,7 +1070,7 @@ FReply SFlowGraphNode::OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& { SetDragMarker(false); - TSharedPtr DragNodeOp = DragDropEvent.GetOperationAs(); + const TSharedPtr DragNodeOp = DragDropEvent.GetOperationAs(); if (DragNodeOp.IsValid()) { if (!DragNodeOp->IsValidOperation()) @@ -1087,14 +1078,13 @@ FReply SFlowGraphNode::OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& return FReply::Handled(); } - const float DragTime = float(FPlatformTime::Seconds() - DragNodeOp->StartTime); + const float DragTime = static_cast(FPlatformTime::Seconds() - DragNodeOp->StartTime); if (DragTime < 0.5f) { return FReply::Handled(); } - UFlowGraphNode* MyNode = Cast(GraphNode); - if (MyNode == nullptr) + if (FlowGraphNode == nullptr) { return FReply::Unhandled(); } @@ -1108,11 +1098,10 @@ FReply SFlowGraphNode::OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& const TArray< TSharedRef >& DraggedNodes = DragNodeOp->GetNodes(); RemoveDraggedSubNodes(DraggedNodes, bReorderOperation); - bool bShouldDropAsSubNodesOfDropTargetNode = bReorderOperation || ShouldDropDraggedNodesAsSubNodes(DraggedNodes, DropTargetNode); - - // Setup the DropTarget pointers based on the type of drop we've decided to do: - UFlowGraphNode* DropTargetParentNode = MyNode; + const bool bShouldDropAsSubNodesOfDropTargetNode = bReorderOperation || ShouldDropDraggedNodesAsSubNodes(DraggedNodes, DropTargetNode); + // Set up the DropTarget pointers based on the type of drop we've decided to do: + UFlowGraphNode* DropTargetParentNode; if (bShouldDropAsSubNodesOfDropTargetNode) { DropTargetParentNode = DropTargetNode; @@ -1156,12 +1145,12 @@ FReply SFlowGraphNode::OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& return SGraphNode::OnDrop(MyGeometry, DragDropEvent); } -bool SFlowGraphNode::ShouldDropDraggedNodesAsSubNodes(const TArray>& DraggedNodes, UFlowGraphNode* DropTargetNode) const +bool SFlowGraphNode::ShouldDropDraggedNodesAsSubNodes(const TArray>& DraggedNodes, const UFlowGraphNode* DropTargetNode) { TSet DraggedFlowGraphNodes; for (int32 Idx = 0; Idx < DraggedNodes.Num(); Idx++) { - UFlowGraphNode* DraggedNode = Cast(DraggedNodes[Idx]->GetNodeObj()); + const UFlowGraphNode* DraggedNode = Cast(DraggedNodes[Idx]->GetNodeObj()); if (IsValid(DraggedNode)) { DraggedFlowGraphNodes.Add(DraggedNode); @@ -1172,7 +1161,7 @@ bool SFlowGraphNode::ShouldDropDraggedNodesAsSubNodes(const TArray(*It); - // Check if all of the dragged nodes can be stopped as a subnode + // Check if all the dragged nodes can be stopped as a subnode // (if not ALL, then we cannot drop ANY of them) const bool bCanDropDraggedNodeAsSubNode = DropTargetNode->CanAcceptSubNodeAsChild(*DraggedNode, DraggedFlowGraphNodes); @@ -1185,7 +1174,7 @@ bool SFlowGraphNode::ShouldDropDraggedNodesAsSubNodes(const TArray >& DraggedNodes, bool& bInOutReorderOperation) +void SFlowGraphNode::RemoveDraggedSubNodes(const TArray< TSharedRef >& DraggedNodes, bool& bInOutReorderOperation) const { for (int32 Idx = 0; Idx < DraggedNodes.Num(); Idx++) { diff --git a/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h b/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h index 0f9c3d6c7..7585c99af 100644 --- a/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h +++ b/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h @@ -2,18 +2,19 @@ #pragma once -#include "EditorSubsystem.h" #include "Logging/TokenizedMessage.h" + +#include "Debugger/FlowDebuggerSubsystem.h" #include "FlowDebugEditorSubsystem.generated.h" class UFlowAsset; class FFlowMessageLog; /** -** Persistent subsystem supporting Flow Graph debugging + * Editor-only extension of debugger subsystem. Supports Message Log. */ UCLASS() -class FLOWEDITOR_API UFlowDebugEditorSubsystem : public UEditorSubsystem +class FLOWEDITOR_API UFlowDebugEditorSubsystem : public UFlowDebuggerSubsystem { GENERATED_BODY() @@ -32,6 +33,6 @@ class FLOWEDITOR_API UFlowDebugEditorSubsystem : public UEditorSubsystem void OnEndPIE(const bool bIsSimulating); public: - static void PausePlaySession(); - static bool IsPlaySessionPaused(); + virtual void PausePlaySession() override; + virtual bool IsPlaySessionPaused() override; }; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h index 2bafbfcc0..a303dc4a8 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h @@ -9,6 +9,7 @@ class FFlowAssetEditor; class IDetailsView; +class UFlowDebuggerSubsystem; /** * @@ -29,9 +30,10 @@ class FLOWEDITOR_API SFlowGraphEditor : public SGraphEditor TWeakPtr FlowAssetEditor; TSharedPtr DetailsView; - TSharedPtr CommandList; + TWeakObjectPtr DebuggerSubsystem; + public: void Construct(const FArguments& InArgs, const TSharedPtr InAssetEditor); @@ -122,7 +124,7 @@ class FLOWEDITOR_API SFlowGraphEditor : public SGraphEditor void OnEnableBreakpoint() const; void OnEnablePinBreakpoint(); - bool CanEnableBreakpoint(); + bool CanEnableBreakpoint() const; bool CanEnablePinBreakpoint(); void OnDisableBreakpoint() const; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h index 6e3546702..f19656734 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h @@ -19,7 +19,10 @@ enum class EFlowNodeDoubleClickTarget : uint8 UCLASS(Config = EditorPerProjectUserSettings, meta = (DisplayName = "Flow Graph")) class FLOWEDITOR_API UFlowGraphEditorSettings : public UDeveloperSettings { - GENERATED_UCLASS_BODY() + GENERATED_BODY() + +public: + UFlowGraphEditorSettings(); static UFlowGraphEditorSettings* Get() { return StaticClass()->GetDefaultObject(); } diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h index fa9e2b19f..25db72dd7 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h @@ -77,9 +77,6 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode // Graph node public: - UPROPERTY() - FFlowPinTrait NodeBreakpoint; - // UEdGraphNode virtual bool CanCreateUnderSpecifiedSchema(const UEdGraphSchema* Schema) const override; virtual void AutowireNewNode(UEdGraphPin* FromPin) override; @@ -101,7 +98,7 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode // variants of K2Node methods void RewireOldPinsToNewPins(TArray& InOldPins); - void ReconstructSinglePin(UEdGraphPin* NewPin, UEdGraphPin* OldPin); + static void ReconstructSinglePin(UEdGraphPin* NewPin, UEdGraphPin* OldPin); // -- // UEdGraphNode @@ -180,9 +177,6 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode TArray InputPins; TArray OutputPins; - UPROPERTY() - TMap PinBreakpoints; - void CreateInputPin(const FFlowPin& FlowPin, const int32 Index = INDEX_NONE); void CreateOutputPin(const FFlowPin& FlowPin, const int32 Index = INDEX_NONE); diff --git a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h index 17116380f..3a8985b40 100644 --- a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h @@ -7,6 +7,8 @@ #include "Graph/Nodes/FlowGraphNode.h" +class UFlowDebuggerSubsystem; + class FLOWEDITOR_API SFlowGraphPinExec : public SGraphPinExec { public: @@ -26,7 +28,7 @@ class FLOWEDITOR_API SFlowGraphNode : public SGraphNode void Construct(const FArguments& InArgs, UFlowGraphNode* InNode); - virtual ~SFlowGraphNode(); + virtual ~SFlowGraphNode() override; protected: // SNodePanel::SNode @@ -36,7 +38,7 @@ class FLOWEDITOR_API SFlowGraphNode : public SGraphNode // -- // SGraphNode - virtual void GetPinBrush(const bool bLeftSide, const float WidgetWidth, const int32 PinIndex, const FFlowPinTrait& Breakpoint, TArray& Brushes) const; + virtual void GetPinBrush(const bool bLeftSide, const float WidgetWidth, const int32 PinIndex, const struct FFlowBreakpoint* Breakpoint, TArray& Brushes) const; virtual FText GetTitle() const; virtual FText GetDescription() const; @@ -97,14 +99,14 @@ class FLOWEDITOR_API SFlowGraphNode : public SGraphNode protected: /** adds a sub node widget inside current node */ - void AddSubNodeWidget(TSharedPtr NewSubNodeWidget); + void AddSubNodeWidget(const TSharedPtr& NewSubNodeWidget); /** removes dragged subnodes from the current node, * bInOutReorderOperation reports if this is a simple "reorder" internally within the node or * if one or more of the removed SubNodes will be removed from the node completely */ - void RemoveDraggedSubNodes(const TArray< TSharedRef >& DraggedNodes, bool& bInOutReorderOperation); + void RemoveDraggedSubNodes(const TArray< TSharedRef >& DraggedNodes, bool& bInOutReorderOperation) const; - bool ShouldDropDraggedNodesAsSubNodes(const TArray>& DraggedNodes, UFlowGraphNode* DropTargetNode) const; + static bool ShouldDropDraggedNodesAsSubNodes(const TArray>& DraggedNodes, const UFlowGraphNode* DropTargetNode); /** gets decorator or service node if one is found under mouse cursor */ TSharedPtr GetSubNodeUnderCursor(const FGeometry& WidgetGeometry, const FPointerEvent& MouseEvent); @@ -117,28 +119,24 @@ class FLOWEDITOR_API SFlowGraphNode : public SGraphNode FMargin ComputeSubNodeChildIndentPaddingMargin() const; - void CreateConfigText(TSharedPtr MainBox); + void CreateConfigText(const TSharedPtr& MainBox); FText GetNodeConfigText() const; EVisibility GetNodeConfigTextVisibility() const; - void CreateOrRebuildSubNodeBox(TSharedPtr MainBox); + void CreateOrRebuildSubNodeBox(const TSharedPtr& MainBox); bool IsFlowGraphNodeSelected(UFlowGraphNode* Node) const; protected: - // The FlowGraphNode this slate widget is representing + // The graph node this slate widget is representing UFlowGraphNode* FlowGraphNode = nullptr; - // SubNodes that are embedded in this widget - TArray> SubNodes; + // Subsystem pointer cached to avoid retrieving it every frame + TWeakObjectPtr DebuggerSubsystem; - // Is the drag marker currently visible? bool bDragMarkerVisible = false; - - // Box to hold the SubNode widgets + TArray> SubNodes; TSharedPtr SubNodeBox; - - // Config Text Block widget TSharedPtr ConfigTextBlock; public: From 5eb7b92548fcca6863c849e881b1e50d12401f8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Fri, 28 Feb 2025 17:37:45 +0100 Subject: [PATCH 334/485] moved LoadSynchronous() calls out of public method This way external code is allowed to use async loading on its assets. Methods operating on asset templates turned back to protected, as it doesn't make sense to duplicate logic of CreateFlowInstance method. --- Source/Flow/Private/FlowSubsystem.cpp | 5 ++--- Source/Flow/Public/FlowSubsystem.h | 6 ++++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index 4ea5c5681..4e802c7f6 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -168,7 +168,7 @@ UFlowAsset* UFlowSubsystem::CreateSubFlow(UFlowNode_SubGraph* SubGraphNode, cons if (!InstancedSubFlows.Contains(SubGraphNode)) { const TWeakObjectPtr Owner = SubGraphNode->GetFlowAsset() ? SubGraphNode->GetFlowAsset()->GetOwner() : nullptr; - NewInstance = CreateFlowInstance(Owner, SubGraphNode->Asset, SavedInstanceName); + NewInstance = CreateFlowInstance(Owner, SubGraphNode->Asset.LoadSynchronous(), SavedInstanceName); if (NewInstance) { @@ -215,9 +215,8 @@ void UFlowSubsystem::RemoveSubFlow(UFlowNode_SubGraph* SubGraphNode, const EFlow } } -UFlowAsset* UFlowSubsystem::CreateFlowInstance(const TWeakObjectPtr Owner, TSoftObjectPtr FlowAsset, FString NewInstanceName) +UFlowAsset* UFlowSubsystem::CreateFlowInstance(const TWeakObjectPtr Owner, UFlowAsset* LoadedFlowAsset, FString NewInstanceName) { - UFlowAsset* LoadedFlowAsset = FlowAsset.LoadSynchronous(); if (LoadedFlowAsset == nullptr) { return nullptr; diff --git a/Source/Flow/Public/FlowSubsystem.h b/Source/Flow/Public/FlowSubsystem.h index 9b884fab9..675b9177d 100644 --- a/Source/Flow/Public/FlowSubsystem.h +++ b/Source/Flow/Public/FlowSubsystem.h @@ -93,12 +93,14 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem UFlowAsset* CreateSubFlow(UFlowNode_SubGraph* SubGraphNode, const FString& SavedInstanceName = FString(), const bool bPreloading = false); void RemoveSubFlow(UFlowNode_SubGraph* SubGraphNode, const EFlowFinishPolicy FinishPolicy); -public: - UFlowAsset* CreateFlowInstance(const TWeakObjectPtr Owner, TSoftObjectPtr FlowAsset, FString NewInstanceName = FString()); +public: + UFlowAsset* CreateFlowInstance(const TWeakObjectPtr Owner, UFlowAsset* LoadedFlowAsset, FString NewInstanceName = FString()); +protected: virtual void AddInstancedTemplate(UFlowAsset* Template); virtual void RemoveInstancedTemplate(UFlowAsset* Template); +public: /* Returns all assets instanced by object from another system like World Settings */ UFUNCTION(BlueprintPure, Category = "FlowSubsystem") TMap GetRootInstances() const; From 28404b6a0f7f3f6404343f8258d47b5c1867999b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Fri, 28 Feb 2025 18:30:21 +0100 Subject: [PATCH 335/485] removed obsolete redirects --- Config/BaseFlow.ini | 6 ++---- Config/DefaultFlow.ini | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Config/BaseFlow.ini b/Config/BaseFlow.ini index b5289a16d..9e22e6ad2 100644 --- a/Config/BaseFlow.ini +++ b/Config/BaseFlow.ini @@ -1,6 +1,4 @@ [CoreRedirects] +ClassRedirects=(OldName="/Script/Flow.FlowNode_CustomEvent",NewName="/Script/Flow.FlowNode_CustomInput") -+PropertyRedirects=(OldName="FlowAsset.CustomEvents",NewName="CustomInputs") -+PropertyRedirects=(OldName="FlowGraphNode.FlowNode",NewName="NodeInstance") -+StructRedirects=(OldName="/Script/Flow.FlowBreakpoint",NewName="/Script/Flow.FlowPinTrait") -+PropertyRedirects=(OldName="FlowPinTrait.bHasBreakpoint",NewName="bAllowed") \ No newline at end of file ++PropertyRedirects=(OldName="FlowAsset.CustomEvents",NewName="FlowAsset.CustomInputs") ++PropertyRedirects=(OldName="FlowGraphNode.FlowNode",NewName="FlowGraphNode.NodeInstance") diff --git a/Config/DefaultFlow.ini b/Config/DefaultFlow.ini index b5289a16d..9e22e6ad2 100644 --- a/Config/DefaultFlow.ini +++ b/Config/DefaultFlow.ini @@ -1,6 +1,4 @@ [CoreRedirects] +ClassRedirects=(OldName="/Script/Flow.FlowNode_CustomEvent",NewName="/Script/Flow.FlowNode_CustomInput") -+PropertyRedirects=(OldName="FlowAsset.CustomEvents",NewName="CustomInputs") -+PropertyRedirects=(OldName="FlowGraphNode.FlowNode",NewName="NodeInstance") -+StructRedirects=(OldName="/Script/Flow.FlowBreakpoint",NewName="/Script/Flow.FlowPinTrait") -+PropertyRedirects=(OldName="FlowPinTrait.bHasBreakpoint",NewName="bAllowed") \ No newline at end of file ++PropertyRedirects=(OldName="FlowAsset.CustomEvents",NewName="FlowAsset.CustomInputs") ++PropertyRedirects=(OldName="FlowGraphNode.FlowNode",NewName="FlowGraphNode.NodeInstance") From c42ccf8424853a326e3c1ba43d5d8ace03751d3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Fri, 28 Feb 2025 18:58:06 +0100 Subject: [PATCH 336/485] cosmetic cleanup of classes heavily touched by multiple persons static analysis fixes, auto-formatting applied --- Source/Flow/Private/Nodes/FlowPin.cpp | 227 +++++++++--------- Source/Flow/Public/Nodes/FlowPin.h | 2 +- .../Private/Graph/Nodes/FlowGraphNode.cpp | 205 +++++++--------- .../Public/Graph/Nodes/FlowGraphNode.h | 22 +- 4 files changed, 204 insertions(+), 252 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowPin.cpp b/Source/Flow/Private/Nodes/FlowPin.cpp index f592b4c7f..741a63e1c 100644 --- a/Source/Flow/Private/Nodes/FlowPin.cpp +++ b/Source/Flow/Private/Nodes/FlowPin.cpp @@ -69,7 +69,7 @@ const TArray& FFlowPin::GetFlowPinTypeEnumValuesWithoutSpaces() // Do a one-time caching of the string-names for this enum, // since we need to de-spacify it and everything.... - for (EFlowPinType PinType : TEnumRange()) + for (const EFlowPinType PinType : TEnumRange()) { FString StringValue = UEnum::GetDisplayValueAsText(PinType).ToString(); StringValue.RemoveSpacesInline(); @@ -102,7 +102,7 @@ bool FFlowPin::ArePinArraysMatchingNamesAndTypes(const TArray& Left, c return true; } -void FFlowPin::SetPinType(EFlowPinType InFlowPinType, UObject* SubCategoryObject) +void FFlowPin::SetPinType(const EFlowPinType InFlowPinType, UObject* SubCategoryObject) { if (PinType == InFlowPinType) { @@ -123,81 +123,81 @@ void FFlowPin::TrySetStructSubCategoryObjectFromPinType() // Set the PinSubCategoryObject based on the PinType (if appropriate) switch (PinType) { - case EFlowPinType::Vector: - { - PinSubCategoryObject = TBaseStructure::Get(); - } - break; + case EFlowPinType::Vector: + { + PinSubCategoryObject = TBaseStructure::Get(); + } + break; - case EFlowPinType::Rotator: - { - PinSubCategoryObject = TBaseStructure::Get(); - } - break; + case EFlowPinType::Rotator: + { + PinSubCategoryObject = TBaseStructure::Get(); + } + break; - case EFlowPinType::Transform: - { - PinSubCategoryObject = TBaseStructure::Get(); - } - break; + case EFlowPinType::Transform: + { + PinSubCategoryObject = TBaseStructure::Get(); + } + break; - case EFlowPinType::GameplayTag: - { - PinSubCategoryObject = TBaseStructure::Get(); - } - break; + case EFlowPinType::GameplayTag: + { + PinSubCategoryObject = TBaseStructure::Get(); + } + break; - case EFlowPinType::GameplayTagContainer: - { - PinSubCategoryObject = TBaseStructure::Get(); - } - break; + case EFlowPinType::GameplayTagContainer: + { + PinSubCategoryObject = TBaseStructure::Get(); + } + break; - case EFlowPinType::InstancedStruct: - { - PinSubCategoryObject = TBaseStructure::Get(); - } - break; + case EFlowPinType::InstancedStruct: + { + PinSubCategoryObject = TBaseStructure::Get(); + } + break; - case EFlowPinType::Enum: - { - // Clear the PinSubCategoryObject if it is not an Enum - UObject* PinSubCategoryObjectPtr = PinSubCategoryObject.Get(); - if (PinSubCategoryObjectPtr && !PinSubCategoryObjectPtr->IsA()) + case EFlowPinType::Enum: { - PinSubCategoryObject = nullptr; + // Clear the PinSubCategoryObject if it is not an Enum + const UObject* PinSubCategoryObjectPtr = PinSubCategoryObject.Get(); + if (PinSubCategoryObjectPtr && !PinSubCategoryObjectPtr->IsA()) + { + PinSubCategoryObject = nullptr; + } } - } - break; + break; - case EFlowPinType::Object: - { - // Clear the PinSubCategoryObject if it is not a Object - UObject* PinSubCategoryObjectPtr = PinSubCategoryObject.Get(); - if (PinSubCategoryObjectPtr && !PinSubCategoryObjectPtr->IsA()) + case EFlowPinType::Object: { - PinSubCategoryObject = nullptr; + // Clear the PinSubCategoryObject if it is not an Object + const UObject* PinSubCategoryObjectPtr = PinSubCategoryObject.Get(); + if (PinSubCategoryObjectPtr && !PinSubCategoryObjectPtr->IsA()) + { + PinSubCategoryObject = nullptr; + } } - } - break; + break; - case EFlowPinType::Class: - { - // Clear the PinSubCategoryObject if it is not a Class - UObject* PinSubCategoryObjectPtr = PinSubCategoryObject.Get(); - if (PinSubCategoryObjectPtr && !PinSubCategoryObjectPtr->IsA()) + case EFlowPinType::Class: { - PinSubCategoryObject = nullptr; + // Clear the PinSubCategoryObject if it is not a Class + const UObject* PinSubCategoryObjectPtr = PinSubCategoryObject.Get(); + if (PinSubCategoryObjectPtr && !PinSubCategoryObjectPtr->IsA()) + { + PinSubCategoryObject = nullptr; + } } - } - break; + break; - default: - { - // Clear the PinSubCategoryObject for all PinTypes that do not use it. - PinSubCategoryObject = nullptr; - } - break; + default: + { + // Clear the PinSubCategoryObject for all PinTypes that do not use it. + PinSubCategoryObject = nullptr; + } + break; } } @@ -207,49 +207,49 @@ const FName& FFlowPin::GetPinCategoryFromPinType(EFlowPinType FlowPinType) switch (FlowPinType) { - case EFlowPinType::Exec: - return FFlowPin::PC_Exec; + case EFlowPinType::Exec: + return FFlowPin::PC_Exec; - case EFlowPinType::Bool: - return FFlowPin::PC_Boolean; + case EFlowPinType::Bool: + return FFlowPin::PC_Boolean; - case EFlowPinType::Int: - return FFlowPin::PC_Int; + case EFlowPinType::Int: + return FFlowPin::PC_Int; - case EFlowPinType::Float: - return FFlowPin::PC_Float; + case EFlowPinType::Float: + return FFlowPin::PC_Float; - case EFlowPinType::Name: - return FFlowPin::PC_Name; + case EFlowPinType::Name: + return FFlowPin::PC_Name; - case EFlowPinType::String: - return FFlowPin::PC_String; + case EFlowPinType::String: + return FFlowPin::PC_String; - case EFlowPinType::Text: - return FFlowPin::PC_Text; + case EFlowPinType::Text: + return FFlowPin::PC_Text; - case EFlowPinType::Enum: - return FFlowPin::PC_Enum; + case EFlowPinType::Enum: + return FFlowPin::PC_Enum; - case EFlowPinType::Vector: - case EFlowPinType::Rotator: - case EFlowPinType::Transform: - case EFlowPinType::GameplayTag: - case EFlowPinType::GameplayTagContainer: - case EFlowPinType::InstancedStruct: - return FFlowPin::PC_Struct; + case EFlowPinType::Vector: + case EFlowPinType::Rotator: + case EFlowPinType::Transform: + case EFlowPinType::GameplayTag: + case EFlowPinType::GameplayTagContainer: + case EFlowPinType::InstancedStruct: + return FFlowPin::PC_Struct; - case EFlowPinType::Object: - return FFlowPin::PC_Object; + case EFlowPinType::Object: + return FFlowPin::PC_Object; - case EFlowPinType::Class: - return FFlowPin::PC_Class; + case EFlowPinType::Class: + return FFlowPin::PC_Class; - default: - { - static const FName NameNone = NAME_None; - return NameNone; - } + default: + { + static const FName NameNone = NAME_None; + return NameNone; + } } } @@ -263,39 +263,32 @@ void FFlowPin::PostEditChangedPinTypeOrSubCategorySource() switch (PinType) { - case EFlowPinType::Class: - { + case EFlowPinType::Class: PinSubCategoryObject = SubCategoryClassFilter; - } - break; + break; - case EFlowPinType::Object: - { + case EFlowPinType::Object: PinSubCategoryObject = SubCategoryObjectFilter; - } - break; + break; - case EFlowPinType::Enum: - { - if (!SubCategoryEnumName.IsEmpty()) + case EFlowPinType::Enum: { - SubCategoryEnumClass = UClass::TryFindTypeSlow(SubCategoryEnumName, EFindFirstObjectOptions::ExactClass); - - if (SubCategoryEnumClass != nullptr && !FFlowPin::ValidateEnum(*SubCategoryEnumClass)) + if (!SubCategoryEnumName.IsEmpty()) { - SubCategoryEnumClass = nullptr; + SubCategoryEnumClass = UClass::TryFindTypeSlow(SubCategoryEnumName, EFindFirstObjectOptions::ExactClass); + if (SubCategoryEnumClass != nullptr && !FFlowPin::ValidateEnum(*SubCategoryEnumClass)) + { + SubCategoryEnumClass = nullptr; + } } - } - PinSubCategoryObject = SubCategoryEnumClass; - } - break; + PinSubCategoryObject = SubCategoryEnumClass; + } + break; - default: - { + default: TrySetStructSubCategoryObjectFromPinType(); - } - break; + break; } } diff --git a/Source/Flow/Public/Nodes/FlowPin.h b/Source/Flow/Public/Nodes/FlowPin.h index 7492c5038..94d733b02 100644 --- a/Source/Flow/Public/Nodes/FlowPin.h +++ b/Source/Flow/Public/Nodes/FlowPin.h @@ -206,7 +206,7 @@ struct FLOW_API FFlowPin static bool ValidateEnum(const UEnum& EnumType); #endif // WITH_EDITOR - void SetPinType(EFlowPinType InFlowPinType, UObject* SubCategoryObject = nullptr); + void SetPinType(const EFlowPinType InFlowPinType, UObject* SubCategoryObject = nullptr); EFlowPinType GetPinType() const { return PinType; } static const FName& GetPinCategoryFromPinType(EFlowPinType FlowPinType); static const TArray& GetFlowPinTypeEnumValuesWithoutSpaces(); diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 7657be3b1..cc3438f46 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -41,16 +41,17 @@ UFlowGraphNode::UFlowGraphNode(const FObjectInitializer& ObjectInitializer) , NodeInstance(nullptr) , bBlueprintCompilationPending(false) , bIsReconstructingNode(false) + , bIsDestroyingNode(false) , bNeedsFullReconstruction(false) { OrphanedPinSaveMode = ESaveOrphanPinMode::SaveAll; } -void UFlowGraphNode::SetNodeTemplate(UFlowNodeBase* InFlowNode) +void UFlowGraphNode::SetNodeTemplate(UFlowNodeBase* InNodeInstance) { - ensure(InFlowNode); - NodeInstance = InFlowNode; - NodeInstanceClass = InFlowNode->GetClass(); + ensure(InNodeInstance); + NodeInstance = InNodeInstance; + NodeInstanceClass = InNodeInstance->GetClass(); } const UFlowNodeBase* UFlowGraphNode::GetNodeTemplate() const @@ -128,23 +129,23 @@ void UFlowGraphNode::PostPlacedNewNode() SubscribeToExternalChanges(); // NOTE - NodeInstance can be already spawned by paste operation, don't override it - if (NodeInstanceClass.IsPending()) { NodeInstanceClass.LoadSynchronous(); } - UClass* NodeClass = NodeInstanceClass.Get(); - if (NodeClass && (NodeInstance == nullptr)) + if (NodeInstance == nullptr) { - UEdGraph* MyGraph = GetGraph(); - UObject* GraphOwner = MyGraph ? MyGraph->GetOuter() : nullptr; - if (GraphOwner) + if (const UClass* NodeClass = NodeInstanceClass.Get()) { - NodeInstance = Cast(NewObject(GraphOwner, NodeClass)); - NodeInstance->SetFlags(RF_Transactional); + const UEdGraph* Graph = GetGraph(); + if (Graph && Graph->GetOuter()) + { + NodeInstance = NewObject(Graph->GetOuter(), NodeClass); + NodeInstance->SetFlags(RF_Transactional); - InitializeInstance(); + InitializeInstance(); + } } } } @@ -199,9 +200,8 @@ void UFlowGraphNode::OnExternalChange() Modify(); bNeedsFullReconstruction = true; - ReconstructNode(); - + GetGraph()->NotifyNodeChanged(this); } @@ -292,22 +292,21 @@ void UFlowGraphNode::ReconstructNode() { return; } - + bIsReconstructingNode = true; FScopedTransaction Transaction(LOCTEXT("ReconstructNode", "Reconstruct Node"), !GUndo); - bool bNodeDataPinsUpdated = TryUpdateAutoDataPins(); // This must be called first, it updates the underlying data for data pins of the flow node - bool bNodeExecPinsUpdated = TryUpdateNodePins(); // Updates all pins of the flow node (native pins, meta auto pins, and context pins which include data pins for now) - bool bAreGraphPinsMismatched = !CheckGraphPinsMatchNodePins(); // This must be called last since it checks the existing graph node against the cleaned up FlowNode instance - - bool bGraphNodeRequiresReconstruction = bNeedsFullReconstruction || bNodeDataPinsUpdated || bNodeExecPinsUpdated || bAreGraphPinsMismatched; + const bool bNodeDataPinsUpdated = TryUpdateAutoDataPins(); // This must be called first, it updates the underlying data for data pins of the Flow Node + const bool bNodeExecPinsUpdated = TryUpdateNodePins(); // Updates all pins of the Flow Node (native pins, meta auto pins, and context pins which include data pins for now) + const bool bAreGraphPinsMismatched = !CheckGraphPinsMatchNodePins(); // This must be called last since it checks the existing graph node against the cleaned up Flow Node instance + const bool bGraphNodeRequiresReconstruction = bNeedsFullReconstruction || bNodeDataPinsUpdated || bNodeExecPinsUpdated || bAreGraphPinsMismatched; if (bGraphNodeRequiresReconstruction) { Modify(); TArray OldPins(Pins); - + Pins.Reset(); InputPins.Reset(); OutputPins.Reset(); @@ -322,19 +321,19 @@ void UFlowGraphNode::ReconstructNode() OldPin->BreakAllPinLinks(); DestroyPin(OldPin); } - - // clear breakpoints for destroyed pins - if (UFlowDebuggerSubsystem* DebuggerSubsystem = GEngine->GetEngineSubsystem()) - { - DebuggerSubsystem->RemoveObsoletePinBreakpoints(this); - } - + + // clear breakpoints for destroyed pins + if (UFlowDebuggerSubsystem* DebuggerSubsystem = GEngine->GetEngineSubsystem()) + { + DebuggerSubsystem->RemoveObsoletePinBreakpoints(this); + } + bNeedsFullReconstruction = false; } - // This ensures the graph editor 'Refresh' button still rebuilds all of the graph widgets even if the FlowGraphNode has nothing to update - // Ideally we could get rid of the 'Refresh' button but I think it will keep being useful, esp. for users making rough custom widgets - (void) OnReconstructNodeCompleted.ExecuteIfBound(); + // This ensures the graph editor 'Refresh' button still rebuilds all the graph widgets even if the FlowGraphNode has nothing to update + // Ideally we could get rid of the 'Refresh' button, but I think it will keep being useful, esp. for users making rough custom widgets + (void)OnReconstructNodeCompleted.ExecuteIfBound(); bIsReconstructingNode = false; } @@ -427,15 +426,11 @@ void UFlowGraphNode::RewireOldPinsToNewPins(TArray& InOldPins) switch (OrphanedPin->Direction) { case EGPD_Input: - { InputPins.Add(OrphanedPin); break; - } case EGPD_Output: - { OutputPins.Add(OrphanedPin); break; - } default: ; } } @@ -623,14 +618,14 @@ bool UFlowGraphNode::CanDuplicateNode() const return true; } -bool UFlowGraphNode::CanPasteHere( const UEdGraph* TargetGraph ) const +bool UFlowGraphNode::CanPasteHere(const UEdGraph* TargetGraph) const { const UFlowGraph* FlowGraph = Cast(TargetGraph); if (FlowGraph == nullptr) { return false; } - + return Super::CanPasteHere(TargetGraph) && FlowGraph->GetFlowAsset()->IsNodeOrAddOnClassAllowed(NodeInstanceClass.Get()); } @@ -725,8 +720,7 @@ UFlowNode* UFlowGraphNode::GetInspectedNodeInstance() const EFlowNodeState UFlowGraphNode::GetActivationState() const { - const UFlowNode* FlowNode = Cast(NodeInstance); - if (FlowNode) + if (const UFlowNode* FlowNode = Cast(NodeInstance)) { if (const UFlowNode* InspectedInstance = FlowNode->GetInspectedInstance()) { @@ -739,8 +733,7 @@ EFlowNodeState UFlowGraphNode::GetActivationState() const FString UFlowGraphNode::GetStatusString() const { - const UFlowNode* FlowNode = Cast(NodeInstance); - if (FlowNode) + if (const UFlowNode* FlowNode = Cast(NodeInstance)) { if (const UFlowNode* InspectedInstance = FlowNode->GetInspectedInstance()) { @@ -753,8 +746,7 @@ FString UFlowGraphNode::GetStatusString() const FLinearColor UFlowGraphNode::GetStatusBackgroundColor() const { - const UFlowNode* FlowNode = Cast(NodeInstance); - if (FlowNode) + if (const UFlowNode* FlowNode = Cast(NodeInstance)) { if (const UFlowNode* InspectedInstance = FlowNode->GetInspectedInstance()) { @@ -771,8 +763,7 @@ FLinearColor UFlowGraphNode::GetStatusBackgroundColor() const bool UFlowGraphNode::IsContentPreloaded() const { - const UFlowNode* FlowNode = Cast(NodeInstance); - if (FlowNode) + if (const UFlowNode* FlowNode = Cast(NodeInstance)) { if (const UFlowNode* InspectedInstance = FlowNode->GetInspectedInstance()) { @@ -802,8 +793,7 @@ void UFlowGraphNode::JumpToDefinition() const { if (FSourceCodeNavigation::CanNavigateToClass(NodeInstance->GetClass())) { - const bool bSucceeded = FSourceCodeNavigation::NavigateToClass(NodeInstance->GetClass()); - if (bSucceeded) + if (FSourceCodeNavigation::NavigateToClass(NodeInstance->GetClass())) { return; } @@ -811,8 +801,7 @@ void UFlowGraphNode::JumpToDefinition() const // Failing that, fall back to the older method which will still get the file open assuming it exists FString NativeParentClassHeaderPath; - const bool bFileFound = FSourceCodeNavigation::FindClassHeaderPath(NodeInstance->GetClass(), NativeParentClassHeaderPath) && (IFileManager::Get().FileSize(*NativeParentClassHeaderPath) != INDEX_NONE); - if (bFileFound) + if (FSourceCodeNavigation::FindClassHeaderPath(NodeInstance->GetClass(), NativeParentClassHeaderPath) && (IFileManager::Get().FileSize(*NativeParentClassHeaderPath) != INDEX_NONE)) { const FString AbsNativeParentClassHeaderPath = FPaths::ConvertRelativePathToFull(NativeParentClassHeaderPath); FSourceCodeNavigation::OpenSourceFile(AbsNativeParentClassHeaderPath); @@ -903,7 +892,7 @@ void UFlowGraphNode::RemoveOrphanedPin(UEdGraphPin* Pin) Pins.Remove(Pin); ReconstructNode(); - + GetGraph()->NotifyNodeChanged(this); } @@ -914,37 +903,37 @@ bool UFlowGraphNode::SupportsContextPins() const bool UFlowGraphNode::CanUserAddInput() const { - UFlowNode* FlowNode = Cast(NodeInstance); + const UFlowNode* FlowNode = Cast(NodeInstance); return FlowNode && FlowNode->CanUserAddInput() && InputPins.Num() < 256; } bool UFlowGraphNode::CanUserAddOutput() const { - UFlowNode* FlowNode = Cast(NodeInstance); + const UFlowNode* FlowNode = Cast(NodeInstance); return FlowNode && FlowNode->CanUserAddOutput() && OutputPins.Num() < 256; } bool UFlowGraphNode::CanUserRemoveInput(const UEdGraphPin* Pin) const { - UFlowNode* FlowNode = Cast(NodeInstance); + const UFlowNode* FlowNode = Cast(NodeInstance); return FlowNode && !FlowNode->GetClass()->GetDefaultObject()->InputPins.Contains(Pin->PinName); } bool UFlowGraphNode::CanUserRemoveOutput(const UEdGraphPin* Pin) const { - UFlowNode* FlowNode = Cast(NodeInstance); + const UFlowNode* FlowNode = Cast(NodeInstance); return FlowNode && !FlowNode->GetClass()->GetDefaultObject()->OutputPins.Contains(Pin->PinName); } void UFlowGraphNode::AddUserInput() { - UFlowNode* FlowNode = Cast(NodeInstance); + const UFlowNode* FlowNode = Cast(NodeInstance); AddInstancePin(EGPD_Input, FlowNode->CountNumberedInputs()); } void UFlowGraphNode::AddUserOutput() { - UFlowNode* FlowNode = Cast(NodeInstance); + const UFlowNode* FlowNode = Cast(NodeInstance); AddInstancePin(EGPD_Output, FlowNode->CountNumberedOutputs()); } @@ -1086,7 +1075,6 @@ void UFlowGraphNode::OnInputTriggered(const int32 Index) TryPausingSession(true); } } - } TryPausingSession(false); @@ -1182,8 +1170,7 @@ void UFlowGraphNode::ForcePinActivation(const FEdGraphPinReference PinReference) void UFlowGraphNode::SetSignalMode(const EFlowSignalMode Mode) { - UFlowNode* FlowNode = Cast(NodeInstance); - if (FlowNode) + if (UFlowNode* FlowNode = Cast(NodeInstance)) { FlowNode->SignalMode = Mode; OnSignalModeChanged.ExecuteIfBound(); @@ -1198,7 +1185,7 @@ EFlowSignalMode UFlowGraphNode::GetSignalMode() const return EFlowSignalMode::Enabled; } - UFlowNode* FlowNode = Cast(NodeInstance); + const UFlowNode* FlowNode = Cast(NodeInstance); if (IsValid(FlowNode)) { return FlowNode->SignalMode; @@ -1211,7 +1198,7 @@ EFlowSignalMode UFlowGraphNode::GetSignalMode() const bool UFlowGraphNode::CanSetSignalMode(const EFlowSignalMode Mode) const { - UFlowNode* FlowNode = Cast(NodeInstance); + const UFlowNode* FlowNode = Cast(NodeInstance); return FlowNode ? (FlowNode->AllowedSignalModes.Contains(Mode) && FlowNode->SignalMode != Mode) : false; } @@ -1242,7 +1229,7 @@ void UFlowGraphNode::PostEditUndo() UFlowAsset* UFlowGraphNode::GetFlowAsset() const { - if (UFlowGraph* FlowGraph = GetFlowGraph()) + if (const UFlowGraph* FlowGraph = GetFlowGraph()) { if (UFlowAsset* FlowAsset = FlowGraph->GetFlowAsset()) { @@ -1255,7 +1242,7 @@ UFlowAsset* UFlowGraphNode::GetFlowAsset() const void UFlowGraphNode::LogError(const FString& MessageToLog, const UFlowNodeBase* FlowNodeBase) const { - if (UFlowAsset* FlowAsset = GetFlowAsset()) + if (const UFlowAsset* FlowAsset = GetFlowAsset()) { FlowAsset->LogError(MessageToLog, FlowNodeBase); } @@ -1265,13 +1252,13 @@ void UFlowGraphNode::ResetNodeOwner() { if (NodeInstance) { - UEdGraph* MyGraph = GetGraph(); - UObject* GraphOwner = MyGraph ? MyGraph->GetOuter() : nullptr; + const UEdGraph* Graph = GetGraph(); + UObject* GraphOwner = Graph ? Graph->GetOuter() : nullptr; NodeInstance->Rename(nullptr, GraphOwner, REN_DontCreateRedirectors | REN_DoNotDirty); NodeInstance->ClearFlags(RF_Transient); - for (auto& SubNode : SubNodes) + for (const TObjectPtr& SubNode : SubNodes) { SubNode->ResetNodeOwner(); } @@ -1344,11 +1331,11 @@ void UFlowGraphNode::NodeConnectionListChanged() { Super::NodeConnectionListChanged(); - UFlowGraph* Graph = Cast(GetGraph()); - - Graph->GetFlowAsset()->HarvestNodeConnections(Cast(GetFlowNodeBase())); - - GetFlowGraph()->NotifyNodeChanged(this); + if (UFlowGraph* Graph = GetFlowGraph()) + { + Graph->GetFlowAsset()->HarvestNodeConnections(Cast(GetFlowNodeBase())); + Graph->NotifyNodeChanged(this); + } } FString UFlowGraphNode::GetPropertyNameAndValueForDiff(const FProperty* Prop, const uint8* PropertyAddr) const @@ -1369,7 +1356,7 @@ void UFlowGraphNode::SetParentNodeForSubNode(UFlowGraphNode* InParentNode) void UFlowGraphNode::RebuildRuntimeAddOnsFromEditorSubNodes() { - // NOTE (gtaylor) Whenever we change the SubNodes array, we need to mirror the changes + // Whenever we change the SubNodes array, we need to mirror the changes // across to the AddOns array in the runtime instance data if (IsValid(NodeInstance)) @@ -1378,7 +1365,7 @@ void UFlowGraphNode::RebuildRuntimeAddOnsFromEditorSubNodes() NodeInstanceAddOns.Reset(); NodeInstanceAddOns.Reserve(SubNodes.Num()); - for (UFlowGraphNode* SubNode : SubNodes) + for (const UFlowGraphNode* SubNode : SubNodes) { if (!IsValid(SubNode)) { @@ -1443,11 +1430,7 @@ void UFlowGraphNode::FindDiffs(UEdGraphNode* OtherNode, FDiffResults& Results) DiffSubNodes(LOCTEXT("AddOnDiffDisplayName", "AddOn"), SubNodes, OtherGraphNode->SubNodes, Results); } -void UFlowGraphNode::DiffSubNodes( - const FText& NodeTypeDisplayName, - const TArray& LhsSubNodes, - const TArray& RhsSubNodes, - FDiffResults& Results) +void UFlowGraphNode::DiffSubNodes(const FText& NodeTypeDisplayName, const TArray& LhsSubNodes, const TArray& RhsSubNodes, FDiffResults& Results) { TArray NodeMatches; TSet MatchedRhsNodes; @@ -1577,7 +1560,7 @@ int32 UFlowGraphNode::FindSubNodeDropIndex(UFlowGraphNode* SubNode) const return InsertIndex; } -void UFlowGraphNode::InsertSubNodeAt(UFlowGraphNode* SubNode, int32 DropIndex) +void UFlowGraphNode::InsertSubNodeAt(UFlowGraphNode* SubNode, const int32 DropIndex) { if (DropIndex > -1) { @@ -1594,7 +1577,7 @@ void UFlowGraphNode::InsertSubNodeAt(UFlowGraphNode* SubNode, int32 DropIndex) void UFlowGraphNode::DestroyNode() { bIsDestroyingNode = true; - + if (ParentNode) { ParentNode->RemoveSubNode(this); @@ -1607,7 +1590,7 @@ void UFlowGraphNode::DestroyNode() } UEdGraphNode::DestroyNode(); - + bIsDestroyingNode = false; } @@ -1657,9 +1640,7 @@ void UFlowGraphNode::ValidateGraphNode(FFlowMessageLog& MessageLog) const if (!NodeInstance) { // Missing the node instance! - MessageLog.Error(TEXT("FlowGraphNode is missing its UFlowNode instance!"), nullptr); - return; } @@ -1695,7 +1676,7 @@ bool UFlowGraphNode::CanReconstructNode() const { return false; } - + // This should never happen if (!ensureMsgf(IsValid(NodeInstance), TEXT("FlowGraphNode has no NodeInstance, graph may be corrupt! Flow Asset: %s"), *GetFlowAsset()->GetName())) { @@ -1753,16 +1734,16 @@ bool CheckPinsMatch(const TArray& LeftPins, const TArray& Ri { return false; } - + for (const FFlowPin& Left : LeftPins) { - auto PinsAreEqualPredicate = [&Left] (const FFlowPin& Right) + auto PinsAreEqualPredicate = [&Left](const FFlowPin& Right) { - bool bNameMatch = Left.PinName == Right.PinName; - bool bTypematch = Left.GetPinType() == Right.GetPinType(); - return bNameMatch && bTypematch; + const bool bNameMatch = Left.PinName == Right.PinName; + const bool bTypeMatch = Left.GetPinType() == Right.GetPinType(); + return bNameMatch && bTypeMatch; }; - + // For each required pin, make sure the existing pins array contains a pin that matches by name and type if (!RightPins.ContainsByPredicate(PinsAreEqualPredicate)) { @@ -1785,7 +1766,7 @@ bool CheckPinsMatch(const TArray& GraphPins, const TArrayPinName == FlowNodePin.PinName; })) @@ -1835,11 +1816,11 @@ bool UFlowGraphNode::TryUpdateNodePins() const // If required pins don't match existing pins, brute force replace them bool bPinsChanged = false; - + if (!CheckPinsMatch(RequiredNodeInputPins, ExistingNodeInputPins)) { FlowNodeInstance->Modify(); - + FlowNodeInstance->InputPins.Empty(RequiredNodeInputPins.Num()); FlowNodeInstance->AddInputPins(RequiredNodeInputPins); // We could just copy it, but this function could do more things one day @@ -1849,7 +1830,7 @@ bool UFlowGraphNode::TryUpdateNodePins() const if (!CheckPinsMatch(RequiredNodeOutputPins, ExistingNodeOutputPins)) { FlowNodeInstance->Modify(); - + FlowNodeInstance->OutputPins.Empty(RequiredNodeOutputPins.Num()); FlowNodeInstance->AddOutputPins(RequiredNodeOutputPins); // We could just copy it, but this function could do more things one day @@ -1862,26 +1843,22 @@ bool UFlowGraphNode::TryUpdateNodePins() const bool UFlowGraphNode::TryUpdateAutoDataPins() const { // Attempt to update the manged / auto-generated pins - if (UFlowAsset* FlowAsset = NodeInstance->GetFlowAsset()) + UFlowAsset* FlowAsset = NodeInstance->GetFlowAsset(); + UFlowNode* FlowNodeInstance = Cast(NodeInstance); + if (FlowAsset && FlowNodeInstance) { - if (UFlowNode* FlowNodeInstance = Cast(NodeInstance)) + if (FlowAsset->TryUpdateManagedFlowPinsForNode(*FlowNodeInstance)) { - bool bUpdatedManagedFlowPins = FlowAsset->TryUpdateManagedFlowPinsForNode(*FlowNodeInstance); - - if (bUpdatedManagedFlowPins) - { - return true; - } + return true; } } return false; } -bool UFlowGraphNode::CheckGraphPinsMatchNodePins() -{ - UFlowNode* FlowNodeInstance = Cast(NodeInstance); - +bool UFlowGraphNode::CheckGraphPinsMatchNodePins() const +{ + const UFlowNode* FlowNodeInstance = Cast(NodeInstance); if (!IsValid(FlowNodeInstance)) { return false; @@ -1891,7 +1868,7 @@ bool UFlowGraphNode::CheckGraphPinsMatchNodePins() TArray ExistingNodePins = FlowNodeInstance->GetInputPins(); ExistingNodePins.Append(FlowNodeInstance->GetOutputPins()); CleanInvalidPins(ExistingNodePins); - + // Get the current FlowGraphNode pins list - orphaned pins need to be stripped from the check TArray AllGraphNodePins = Pins; CleanInvalidPins(AllGraphNodePins); @@ -1922,26 +1899,20 @@ void UFlowGraphNode::RebuildPinArraysOnLoad() switch (Pin->Direction) { case EGPD_Input: - { InputPins.Add(Pin); break; - } case EGPD_Output: - { OutputPins.Add(Pin); break; - } default: - { UE_LOG(LogFlow, Error, TEXT("Encountered Pin with invalid direction!")); - } } } } -bool UFlowGraphNode::CanAcceptSubNodeAsChild(const UFlowGraphNode& SubNodeToConsider, const TSet& AllRootSubNodesToPaste, FString* OutReasonString) const +bool UFlowGraphNode::CanAcceptSubNodeAsChild(const UFlowGraphNode& OtherSubNode, const TSet& AllRootSubNodesToPaste, FString* OutReasonString) const { - const UFlowNodeBase* OtherFlowNodeSubNode = SubNodeToConsider.NodeInstance; + const UFlowNodeBase* OtherFlowNodeSubNode = OtherSubNode.NodeInstance; if (!OtherFlowNodeSubNode) { @@ -1953,7 +1924,7 @@ bool UFlowGraphNode::CanAcceptSubNodeAsChild(const UFlowGraphNode& SubNodeToCons return false; } - if (IsAncestorNode(SubNodeToConsider)) + if (IsAncestorNode(OtherSubNode)) { if (OutReasonString) { diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h index 25db72dd7..a8dd92697 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h @@ -48,7 +48,7 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode UPROPERTY() TArray> AssignedNodeClasses; - void SetNodeTemplate(UFlowNodeBase* InFlowNodeBase); + void SetNodeTemplate(UFlowNodeBase* InNodeInstance); const UFlowNodeBase* GetNodeTemplate() const; UFlowNodeBase* GetFlowNodeBase() const; @@ -115,9 +115,7 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode // -- void CreateAttachAddOnSubMenu(UToolMenu* Menu, UEdGraph* Graph) const; - bool CanAcceptSubNodeAsChild(const UFlowGraphNode& OtherSubNode, const TSet& AllRootSubNodesToPaste, FString* OutReasonString = nullptr) const; - bool IsAncestorNode(const UFlowGraphNode& OtherNode) const; protected: @@ -165,10 +163,8 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode bool CanReconstructNode() const; bool TryUpdateNodePins() const; - bool TryUpdateAutoDataPins() const; - - bool CheckGraphPinsMatchNodePins(); + bool CheckGraphPinsMatchNodePins() const; ////////////////////////////////////////////////////////////////////////// // Pins @@ -259,11 +255,7 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode void OnUpdateAsset(int32 UpdateFlags) { RebuildRuntimeAddOnsFromEditorSubNodes(); } void RebuildRuntimeAddOnsFromEditorSubNodes(); - static void DiffSubNodes( - const FText& NodeTypeDisplayName, - const TArray& LhsSubNodes, - const TArray& RhsSubNodes, - FDiffResults& Results); + static void DiffSubNodes(const FText& NodeTypeDisplayName, const TArray& LhsSubNodes, const TArray& RhsSubNodes, FDiffResults& Results); //~ Begin UObject Interface #if WITH_EDITOR @@ -271,11 +263,9 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode #endif // End UObject - // @return the input pin for this state virtual UEdGraphPin* GetInputPin(int32 InputIndex = 0) const; - // @return the output pin for this state virtual UEdGraphPin* GetOutputPin(int32 InputIndex = 0) const; - virtual UEdGraph* GetBoundGraph() const { return NULL; } + virtual UEdGraph* GetBoundGraph() const { return nullptr; } virtual FText GetDescription() const; @@ -286,7 +276,7 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode virtual void OnSubNodeAdded(UFlowGraphNode* SubNode); virtual int32 FindSubNodeDropIndex(UFlowGraphNode* SubNode) const; - virtual void InsertSubNodeAt(UFlowGraphNode* SubNode, int32 DropIndex); + virtual void InsertSubNodeAt(UFlowGraphNode* SubNode, const int32 DropIndex); /** check if node is subnode */ virtual bool IsSubNode() const; @@ -304,13 +294,11 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode bool UsesBlueprint() const; protected: - virtual void ResetNodeOwner(); void LogError(const FString& MessageToLog, const UFlowNodeBase* FlowNodeBase) const; public: - /** instance class */ UPROPERTY() TSoftClassPtr NodeInstanceClass; From 193326686fdb6fc5f04fe4b5702722ef7bc6f11f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Fri, 28 Feb 2025 19:05:48 +0100 Subject: [PATCH 337/485] CIS compiliation fixes --- .../Private/Debugger/FlowDebuggerSubsystem.cpp | 7 ++----- Source/FlowDebugger/Private/FlowDebuggerModule.cpp | 4 +++- Source/FlowEditor/Private/FlowEditorModule.cpp | 3 ++- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp b/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp index 80e98ca04..0a14be142 100644 --- a/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp +++ b/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp @@ -3,12 +3,11 @@ #include "Debugger/FlowDebuggerSubsystem.h" #include "Debugger/FlowDebuggerSettings.h" -#include "Editor/UnrealEdEngine.h" +#include "EdGraph/EdGraphNode.h" +#include "EdGraph/EdGraphPin.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowDebuggerSubsystem) -#define LOCTEXT_NAMESPACE "FlowDebuggerSubsystem" - UFlowDebuggerSubsystem::UFlowDebuggerSubsystem() { } @@ -248,5 +247,3 @@ void UFlowDebuggerSubsystem::SaveSettings() UFlowDebuggerSettings* Settings = GetMutableDefault(); Settings->SaveConfig(); } - -#undef LOCTEXT_NAMESPACE diff --git a/Source/FlowDebugger/Private/FlowDebuggerModule.cpp b/Source/FlowDebugger/Private/FlowDebuggerModule.cpp index b96e61dd5..d0ce92e97 100644 --- a/Source/FlowDebugger/Private/FlowDebuggerModule.cpp +++ b/Source/FlowDebugger/Private/FlowDebuggerModule.cpp @@ -2,6 +2,8 @@ #include "FlowDebuggerModule.h" +#include "Modules/ModuleManager.h" + #define LOCTEXT_NAMESPACE "FlowDebuggerModule" void FFlowDebuggerModule::StartupModule() @@ -14,4 +16,4 @@ void FFlowDebuggerModule::ShutdownModule() #undef LOCTEXT_NAMESPACE -IMPLEMENT_MODULE(FFlowDebuggerModule, FlowDebugger) \ No newline at end of file +IMPLEMENT_MODULE(FFlowDebuggerModule, FlowDebugger) diff --git a/Source/FlowEditor/Private/FlowEditorModule.cpp b/Source/FlowEditor/Private/FlowEditorModule.cpp index 115534ecd..4a7baa208 100644 --- a/Source/FlowEditor/Private/FlowEditorModule.cpp +++ b/Source/FlowEditor/Private/FlowEditorModule.cpp @@ -15,6 +15,8 @@ #include "Pins/SFlowInputPinHandle.h" #include "Pins/SFlowOutputPinHandle.h" +#include "FlowModule.h" + #include "DetailCustomizations/FlowAssetDetails.h" #include "DetailCustomizations/FlowNode_Details.h" #include "DetailCustomizations/FlowNode_ComponentObserverDetails.h" @@ -41,7 +43,6 @@ #include "AssetToolsModule.h" #include "EdGraphUtilities.h" -#include "FlowModule.h" #include "IAssetSearchModule.h" #include "Framework/MultiBox/MultiBoxBuilder.h" #include "ISequencerChannelInterface.h" // ignore Rider's false "unused include" warning From 284dcabe23ec9b297cc4b1f2f9b4c6dca7dce742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sat, 1 Mar 2025 17:52:08 +0100 Subject: [PATCH 338/485] static analysis fixes --- Source/Flow/Private/Nodes/FlowNodeBase.cpp | 4 ++-- Source/Flow/Public/Nodes/FlowNodeBase.h | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp index fe646b4b0..6b4f5f7aa 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBase.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -204,7 +204,7 @@ const FFlowPin* UFlowNodeBase::FindFlowPinByName(const FName& PinName, const TAr FFlowPin* UFlowNodeBase::FindFlowPinByName(const FName& PinName, TArray& FlowPins) { - return FlowPins.FindByPredicate([&PinName](FFlowPin& FlowPin) + return FlowPins.FindByPredicate([&PinName](const FFlowPin& FlowPin) { return FlowPin.PinName == PinName; }); @@ -886,7 +886,7 @@ void UFlowNodeBase::LogVerbose(FString Message) const #if !UE_BUILD_SHIPPING bool UFlowNodeBase::BuildMessage(FString& Message) const { - UFlowAsset* FlowAsset = GetFlowAsset(); + const UFlowAsset* FlowAsset = GetFlowAsset(); if (FlowAsset && FlowAsset->GetTemplateAsset()) // this is runtime log which is should be only called on runtime instances of asset { const FString TemplatePath = FlowAsset->GetTemplateAsset()->GetPathName(); diff --git a/Source/Flow/Public/Nodes/FlowNodeBase.h b/Source/Flow/Public/Nodes/FlowNodeBase.h index a9efca067..24f2d41ba 100644 --- a/Source/Flow/Public/Nodes/FlowNodeBase.h +++ b/Source/Flow/Public/Nodes/FlowNodeBase.h @@ -7,10 +7,9 @@ #include "Interfaces/FlowCoreExecutableInterface.h" #include "Interfaces/FlowContextPinSupplierInterface.h" #include "FlowMessageLog.h" -#include "FlowTags.h" +#include "FlowTags.h" // used by subclasses #include "FlowTypes.h" #include "Types/FlowDataPinResults.h" -#include "NativeGameplayTags.h" #include "FlowNodeBase.generated.h" @@ -23,7 +22,7 @@ class IFlowOwnerInterface; class IFlowDataPinValueSupplierInterface; struct FFlowPin; -#if WITH_EDITOR +#if WITH_EDITORONLY_DATA DECLARE_DELEGATE(FFlowNodeEvent); #endif From 3f3c6655e1377c847e12ca2a0b7aa92a20e8a6bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sat, 1 Mar 2025 18:55:00 +0100 Subject: [PATCH 339/485] Don't try to use MessageLog in Standalone, it only throws error to log --- Source/Flow/Private/Nodes/FlowNodeBase.cpp | 29 ++++++++++++++-------- Source/Flow/Public/FlowMessageLog.h | 2 +- Source/Flow/Public/Nodes/FlowNodeBase.h | 2 +- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp index 6b4f5f7aa..19c218c84 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBase.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -701,7 +701,7 @@ FText UFlowNodeBase::GetNodeToolTip() const return FText::FromString(BlueprintTitle); } } - + return GetClass()->GetToolTipText(); } @@ -714,20 +714,20 @@ FText UFlowNodeBase::GetNodeConfigText() const FText UFlowNodeBase::GetGeneratedDisplayName() const { static const FName NAME_GeneratedDisplayName(TEXT("GeneratedDisplayName")); - + if (GetClass()->ClassGeneratedBy) { UClass* Class = Cast(GetClass()->ClassGeneratedBy)->GeneratedClass; return Class->GetMetaDataText(NAME_GeneratedDisplayName); } - + return GetClass()->GetMetaDataText(NAME_GeneratedDisplayName); } void UFlowNodeBase::EnsureNodeDisplayStyle() { // todo: remove in Flow 2.1 - + // Backward compatibility update to convert NodeStyle to NodeDisplayStyle FLOW_ASSERT_ENUM_MAX(EFlowNodeStyle, 7); @@ -832,9 +832,12 @@ void UFlowNodeBase::LogError(FString Message, const EFlowOnScreenMessageType OnS // Output Log UE_LOG(LogFlow, Error, TEXT("%s"), *Message); - // Message Log #if WITH_EDITOR - GetFlowAsset()->GetTemplateAsset()->LogError(Message, this); + if (GEditor) + { + // Message Log + GetFlowAsset()->GetTemplateAsset()->LogError(Message, this); + } #endif } #endif @@ -848,9 +851,12 @@ void UFlowNodeBase::LogWarning(FString Message) const // Output Log UE_LOG(LogFlow, Warning, TEXT("%s"), *Message); - // Message Log #if WITH_EDITOR - GetFlowAsset()->GetTemplateAsset()->LogWarning(Message, this); + if (GEditor) + { + // Message Log + GetFlowAsset()->GetTemplateAsset()->LogWarning(Message, this); + } #endif } #endif @@ -864,9 +870,12 @@ void UFlowNodeBase::LogNote(FString Message) const // Output Log UE_LOG(LogFlow, Log, TEXT("%s"), *Message); - // Message Log #if WITH_EDITOR - GetFlowAsset()->GetTemplateAsset()->LogNote(Message, this); + if (GEditor) + { + // Message Log + GetFlowAsset()->GetTemplateAsset()->LogNote(Message, this); + } #endif } #endif diff --git a/Source/Flow/Public/FlowMessageLog.h b/Source/Flow/Public/FlowMessageLog.h index 4a5a0542f..859bf37f0 100644 --- a/Source/Flow/Public/FlowMessageLog.h +++ b/Source/Flow/Public/FlowMessageLog.h @@ -81,7 +81,7 @@ class FLOW_API FFlowMessageLog protected: template - void AddMessage(FName MessageID, const TCHAR* Format, TSharedRef& Message, T* Object) + void AddMessage(const FName MessageID, const TCHAR* Format, TSharedRef& Message, T* Object) { Message->SetIdentifier(MessageID); diff --git a/Source/Flow/Public/Nodes/FlowNodeBase.h b/Source/Flow/Public/Nodes/FlowNodeBase.h index 24f2d41ba..76d8236fc 100644 --- a/Source/Flow/Public/Nodes/FlowNodeBase.h +++ b/Source/Flow/Public/Nodes/FlowNodeBase.h @@ -272,7 +272,7 @@ class FLOW_API UFlowNodeBase UPROPERTY() TObjectPtr GraphNode; - + #if WITH_EDITORONLY_DATA protected: UPROPERTY(EditDefaultsOnly, Category = "FlowNode") From 1a4a2d95b441108200a54e4d077af9d08fe93e12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sat, 1 Mar 2025 20:36:23 +0100 Subject: [PATCH 340/485] Decoupled triggering breakpoints from the Flow Editor module * UFlowGraphNode no longer operate on breakpoints. * UFlowDebuggerSubsystem binds directly to runtime OnPinTriggered delegate. * Pin breakpoint is now identified as NodeGuid and PinName instead of UEdGraphPin. This way it's possible to bind to OnPinTriggered delegate outside of editor! Events shall be receive in Standalone game, non-shipping game builds. * Wrapped UEdGraphNode pointer in runtime Flow Node class with WITH_EDITORONLY_DATA as this isn't even loaded in Standalone game. --- Source/Flow/Private/FlowAsset.cpp | 22 +- Source/Flow/Private/FlowSubsystem.cpp | 2 +- Source/Flow/Private/Nodes/FlowNode.cpp | 45 +-- Source/Flow/Private/Nodes/FlowNodeBase.cpp | 9 +- Source/Flow/Public/FlowAsset.h | 56 ++-- Source/Flow/Public/FlowSubsystem.h | 4 +- Source/Flow/Public/Nodes/FlowNodeBase.h | 20 +- .../Debugger/FlowDebuggerSubsystem.cpp | 272 ++++++++++++------ .../Public/Debugger/FlowDebuggerSettings.h | 5 +- .../Public/Debugger/FlowDebuggerSubsystem.h | 59 ++-- .../Public/Debugger/FlowDebuggerTypes.h | 61 ++-- .../Asset/FlowDebugEditorSubsystem.cpp | 77 ++--- Source/FlowEditor/Private/Graph/FlowGraph.cpp | 15 - .../Private/Graph/FlowGraphEditor.cpp | 74 +++-- .../Private/Graph/Nodes/FlowGraphNode.cpp | 89 +----- .../Private/Graph/Widgets/SFlowGraphNode.cpp | 4 +- .../Public/Asset/FlowDebugEditorSubsystem.h | 15 +- Source/FlowEditor/Public/Graph/FlowGraph.h | 9 - .../FlowEditor/Public/Graph/FlowGraphEditor.h | 4 +- .../Public/Graph/Nodes/FlowGraphNode.h | 14 - 20 files changed, 401 insertions(+), 455 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 33fbf4d3d..feae76a92 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -31,7 +31,7 @@ FString UFlowAsset::ValidationError_NullNodeInstance = TEXT("Node with GUID {0} UFlowAsset::UFlowAsset(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , bWorldBound(true) -#if WITH_EDITOR +#if WITH_EDITORONLY_DATA , FlowGraph(nullptr) #endif , AllowedNodeClasses({UFlowNodeBase::StaticClass()}) @@ -293,14 +293,6 @@ bool UFlowAsset::CanFlowAssetReferenceFlowNode(const UClass& FlowNodeClass, FTex return true; } -TSharedPtr UFlowAsset::FlowGraphInterface = nullptr; - -void UFlowAsset::SetFlowGraphInterface(TSharedPtr InFlowAssetEditor) -{ - check(!FlowGraphInterface.IsValid()); - FlowGraphInterface = InFlowAssetEditor; -} - UFlowNode* UFlowAsset::CreateNode(const UClass* NodeClass, UEdGraphNode* GraphNode) { UFlowNode* NewNode = NewObject(this, NodeClass, NAME_None, RF_Transactional); @@ -1473,9 +1465,9 @@ bool UFlowAsset::IsBoundToWorld_Implementation() void UFlowAsset::LogError(const FString& MessageToLog, const UFlowNodeBase* Node) const { // this is runtime log which is should be only called on runtime instances of asset - if (TemplateAsset == nullptr) + if (TemplateAsset) { - UE_LOG(LogFlow, Log, TEXT("Attempted to use Runtime Log on null template asset %s"), *MessageToLog); + UE_LOG(LogFlow, Log, TEXT("Attempted to use Runtime Log on asset instance %s"), *MessageToLog); } if (RuntimeLog.Get()) @@ -1488,9 +1480,9 @@ void UFlowAsset::LogError(const FString& MessageToLog, const UFlowNodeBase* Node void UFlowAsset::LogWarning(const FString& MessageToLog, const UFlowNodeBase* Node) const { // this is runtime log which is should be only called on runtime instances of asset - if (TemplateAsset == nullptr) + if (TemplateAsset) { - UE_LOG(LogFlow, Log, TEXT("Attempted to use Runtime Log on null template asset %s"), *MessageToLog); + UE_LOG(LogFlow, Log, TEXT("Attempted to use Runtime Log on asset instance %s"), *MessageToLog); } if (RuntimeLog.Get()) @@ -1503,9 +1495,9 @@ void UFlowAsset::LogWarning(const FString& MessageToLog, const UFlowNodeBase* No void UFlowAsset::LogNote(const FString& MessageToLog, const UFlowNodeBase* Node) const { // this is runtime log which is should be only called on runtime instances of asset - if (TemplateAsset == nullptr) + if (TemplateAsset) { - UE_LOG(LogFlow, Log, TEXT("Attempted to use Runtime Log on null template asset %s"), *MessageToLog); + UE_LOG(LogFlow, Log, TEXT("Attempted to use Runtime Log on asset instance %s"), *MessageToLog); } if (RuntimeLog.Get()) diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index 4e802c7f6..ea085c801 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -17,7 +17,7 @@ #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowSubsystem) -#if WITH_EDITOR +#if !UE_BUILD_SHIPPING FNativeFlowAssetEvent UFlowSubsystem::OnInstancedTemplateAdded; FNativeFlowAssetEvent UFlowSubsystem::OnInstancedTemplateRemoved; #endif diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 6237305d2..b53c2a9a4 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -76,21 +76,15 @@ void UFlowNode::PostLoad() bool UFlowNode::IsSupportedInputPinName(const FName& PinName) const { + const FFlowPin* InputPin = FindFlowPinByName(PinName, InputPins); + if (AddOns.IsEmpty()) { - checkf(FindFlowPinByName(PinName, InputPins), TEXT("Only AddOns should introduce unknown Pins to a FlowNode, so if we have no AddOns, we should have no unknown pins")); - + checkf(InputPin, TEXT("Only AddOns should introduce unknown Pins to a FlowNode, so if we have no AddOns, we should have no unknown pins")); return true; } - if (const FFlowPin* FoundInputFlowPin = FindFlowPinByName(PinName, InputPins)) - { - return true; - } - else - { - return false; - } + return (InputPin != nullptr); } void UFlowNode::AddInputPins(const TArray& Pins) @@ -813,23 +807,19 @@ void UFlowNode::TriggerInput(const FName& PinName, const EFlowPinActivationType TArray& Records = InputRecords.FindOrAdd(PinName); Records.Add(FPinRecord(FApp::GetCurrentTime(), ActivationType)); - LogVerbose(FString::Printf(TEXT("Triggering input %s."), *PinName.ToString())); -#endif // UE_BUILD_SHIPPING - -#if WITH_EDITOR - if (GEditor && UFlowAsset::GetFlowGraphInterface().IsValid()) + if (const UFlowAsset* FlowAssetTemplate = GetFlowAsset()->GetTemplateAsset()) { - UFlowAsset::GetFlowGraphInterface()->OnInputTriggered(GraphNode, InputPins.IndexOfByKey(PinName)); + (void)FlowAssetTemplate->OnPinTriggered.ExecuteIfBound(NodeGuid, PinName); } -#endif // WITH_EDITOR +#endif } +#if !UE_BUILD_SHIPPING else { -#if !UE_BUILD_SHIPPING LogError(FString::Printf(TEXT("Input Pin name %s invalid"), *PinName.ToString())); -#endif // UE_BUILD_SHIPPING return; } +#endif switch (SignalMode) { @@ -883,24 +873,15 @@ void UFlowNode::TriggerOutput(const FName PinName, const bool bFinish /*= false* TArray& Records = OutputRecords.FindOrAdd(PinName); Records.Add(FPinRecord(FApp::GetCurrentTime(), ActivationType)); - LogVerbose(FString::Printf(TEXT("\n Triggering output: %s. bFinish: %s "), *PinName.ToString(), bFinish ? TEXT("true") : TEXT("false"))); - -#if WITH_EDITOR - if (GEditor && UFlowAsset::GetFlowGraphInterface().IsValid()) + if (const UFlowAsset* FlowAssetTemplate = GetFlowAsset()->GetTemplateAsset()) { - UFlowAsset::GetFlowGraphInterface()->OnOutputTriggered(GraphNode, OutputPins.IndexOfByKey(PinName)); + FlowAssetTemplate->OnPinTriggered.ExecuteIfBound(NodeGuid, PinName); } -#endif } else { LogError(FString::Printf(TEXT("Output Pin name %s invalid"), *PinName.ToString())); } -#endif // UE_BUILD_SHIPPING - -#if WITH_EDITOR - LogVerbose(FString::Printf(TEXT("\n Description: %s"), *GetNodeDescription())); - LogVerbose(FString::Printf(TEXT("\n Status: %s"), *GetStatusStringForNodeAndAddOns())); #endif // call the next node @@ -924,7 +905,7 @@ void UFlowNode::Deactivate() // there is nothing to deactivate, node was never active return; } - + if (GetFlowAsset()->FinishPolicy == EFlowFinishPolicy::Abort) { ActivationState = EFlowNodeState::Aborted; @@ -1077,7 +1058,7 @@ FString UFlowNode::GetStatusStringForNodeAndAddOns() const FString CombinedStatusString = GetStatusString(); // Give all of the AddOns a chance to add their status strings as well - (void) ForEachAddOnConst( + (void)ForEachAddOnConst( [&CombinedStatusString](const UFlowNodeAddOn& AddOn) { const FString AddOnStatusString = AddOn.GetStatusString(); diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp index 19c218c84..31635b92b 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBase.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -30,8 +30,8 @@ using namespace EFlowForEachAddOnFunctionReturnValue_Classifiers; UFlowNodeBase::UFlowNodeBase(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) - , GraphNode(nullptr) #if WITH_EDITORONLY_DATA + , GraphNode(nullptr) , bDisplayNodeTitleWithoutPrefix(true) , bCanDelete(true) , bCanDuplicate(true) @@ -575,17 +575,14 @@ EFlowForEachAddOnFunctionReturnValue UFlowNodeBase::ForEachAddOnForClass(const U return ReturnValue; } +#if WITH_EDITOR void UFlowNodeBase::PostLoad() { Super::PostLoad(); -#if WITH_EDITOR EnsureNodeDisplayStyle(); -#endif } -#if WITH_EDITOR - void UFlowNodeBase::SetGraphNode(UEdGraphNode* NewGraphNode) { GraphNode = NewGraphNode; @@ -726,7 +723,7 @@ FText UFlowNodeBase::GetGeneratedDisplayName() const void UFlowNodeBase::EnsureNodeDisplayStyle() { - // todo: remove in Flow 2.1 + // todo: remove in Flow 2.2 // Backward compatibility update to convert NodeStyle to NodeDisplayStyle FLOW_ASSERT_ENUM_MAX(EFlowNodeStyle, 7); diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index e856cf38a..f0736c287 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -22,21 +22,9 @@ class UEdGraph; class UEdGraphNode; class UFlowAsset; -#if WITH_EDITOR - -/** Interface for calling the graph editor methods */ -class FLOW_API IFlowGraphInterface -{ -public: - IFlowGraphInterface() {} - virtual ~IFlowGraphInterface() {} - - virtual void OnInputTriggered(UEdGraphNode* GraphNode, const int32 Index) const {} - virtual void OnOutputTriggered(UEdGraphNode* GraphNode, const int32 Index) const {} -}; - +#if !UE_BUILD_SHIPPING DECLARE_DELEGATE(FFlowGraphEvent); - +DECLARE_DELEGATE_TwoParams(FFlowSignalEvent, const FGuid& /*NodeGuid*/, const FName& /*PinName*/); #endif // Working Data struct for the Harvest Data Pins operation @@ -96,7 +84,7 @@ class FLOW_API UFlowAsset : public UObject bool bWorldBound; ////////////////////////////////////////////////////////////////////////// -// Graph +// Graph (editor-only) #if WITH_EDITOR public: @@ -108,13 +96,24 @@ class FLOW_API UFlowAsset : public UObject virtual void PostDuplicate(bool bDuplicateForPIE) override; virtual void PostLoad() override; // -- +#endif +#if WITH_EDITORONLY_DATA public: FSimpleDelegate OnDetailsRefreshRequested; static FString ValidationError_NodeClassNotAllowed; static FString ValidationError_NullNodeInstance; +private: + UPROPERTY() + TObjectPtr FlowGraph; +#endif + +#if WITH_EDITOR +public: + UEdGraph* GetGraph() const { return FlowGraph; } + virtual EDataValidationResult ValidateAsset(FFlowMessageLog& MessageLog); // Returns whether the node class is allowed in this flow asset @@ -129,25 +128,6 @@ class FLOW_API UFlowAsset : public UObject bool IsFlowNodeClassInDeniedClasses(const UClass& FlowNodeClass) const; #endif - // IFlowGraphInterface -#if WITH_EDITORONLY_DATA - -private: - UPROPERTY() - TObjectPtr FlowGraph; - - static TSharedPtr FlowGraphInterface; -#endif - -public: -#if WITH_EDITOR - UEdGraph* GetGraph() const { return FlowGraph; }; - - static void SetFlowGraphInterface(TSharedPtr InFlowAssetEditor); - static TSharedPtr GetFlowGraphInterface() { return FlowGraphInterface; }; -#endif - // -- - ////////////////////////////////////////////////////////////////////////// // Nodes @@ -285,7 +265,7 @@ class FLOW_API UFlowAsset : public UObject void AddCustomOutput(const FName& EventName); void RemoveCustomOutput(const FName& EventName); -#endif // WITH_EDITOR +#endif ////////////////////////////////////////////////////////////////////////// // Instances of the template asset @@ -406,7 +386,6 @@ class FLOW_API UFlowAsset : public UObject TWeakObjectPtr GetFlowInstance(UFlowNode_SubGraph* SubGraphNode) const; protected: - void TriggerCustomInput_FromSubGraph(UFlowNode_SubGraph* Node, const FName& EventName) const; void TriggerCustomOutput(const FName& EventName); @@ -415,6 +394,11 @@ class FLOW_API UFlowAsset : public UObject void FinishNode(UFlowNode* Node); void ResetNodes(); +#if !UE_BUILD_SHIPPING +public: + FFlowSignalEvent OnPinTriggered; +#endif + public: UFlowSubsystem* GetFlowSubsystem() const; FName GetDisplayName() const; diff --git a/Source/Flow/Public/FlowSubsystem.h b/Source/Flow/Public/FlowSubsystem.h index 675b9177d..a70406ecf 100644 --- a/Source/Flow/Public/FlowSubsystem.h +++ b/Source/Flow/Public/FlowSubsystem.h @@ -49,7 +49,7 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem UPROPERTY() TMap, TObjectPtr> InstancedSubFlows; -#if WITH_EDITOR +#if !UE_BUILD_SHIPPING public: /* Called after creating the first instance of given Flow Asset */ static FNativeFlowAssetEvent OnInstancedTemplateAdded; @@ -57,7 +57,7 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem /* Called just before removing the last instance of given Flow Asset */ static FNativeFlowAssetEvent OnInstancedTemplateRemoved; #endif - + protected: UPROPERTY() TObjectPtr LoadedSaveGame; diff --git a/Source/Flow/Public/Nodes/FlowNodeBase.h b/Source/Flow/Public/Nodes/FlowNodeBase.h index 76d8236fc..a8578305b 100644 --- a/Source/Flow/Public/Nodes/FlowNodeBase.h +++ b/Source/Flow/Public/Nodes/FlowNodeBase.h @@ -261,20 +261,19 @@ class FLOW_API UFlowNodeBase UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Class") FFlowDataPinResult_Class TryResolveDataPinAsClass(const FName& PinName) const; - // Public only for for TResolveDataPinWorkingData's use + // Public only for TResolveDataPinWorkingData's use EFlowDataPinResolveResult TryResolveDataPinPrerequisites(const FName& PinName, const UFlowNode*& FlowNode, const FFlowPin*& FlowPin, EFlowPinType PinType) const; public: ////////////////////////////////////////////////////////////////////////// // Editor -// (some editor symbols exposed to enabled creation of non-editor tooling) - - UPROPERTY() - TObjectPtr GraphNode; #if WITH_EDITORONLY_DATA protected: + UPROPERTY() + TObjectPtr GraphNode; + UPROPERTY(EditDefaultsOnly, Category = "FlowNode") uint8 bDisplayNodeTitleWithoutPrefix : 1; @@ -292,13 +291,12 @@ class FLOW_API UFlowNodeBase FFlowMessageLog ValidationLog; #endif // WITH_EDITORONLY_DATA +#if WITH_EDITOR public: - UEdGraphNode* GetGraphNode() const { return GraphNode; } - virtual void PostLoad() override; - -#if WITH_EDITOR + void SetGraphNode(UEdGraphNode* NewGraphNode); + UEdGraphNode* GetGraphNode() const { return GraphNode; } // Set up UFlowNodeBase when being opened for edit in the editor virtual void SetupForEditing(UEdGraphNode& EdGraphNode); @@ -316,7 +314,7 @@ class FLOW_API UFlowNodeBase // Called by owning FlowNode to add to its Status String. // (may be multi-line) virtual FString GetStatusString() const; -#endif +#endif // WITH_EDITOR protected: // Information displayed while node is working - displayed over node as NodeInfoPopup @@ -361,7 +359,7 @@ class FLOW_API UFlowNodeBase protected: void EnsureNodeDisplayStyle(); -#endif +#endif // WITH_EDITOR protected: // Set the editor-only Config Text diff --git a/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp b/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp index 0a14be142..0b977d536 100644 --- a/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp +++ b/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp @@ -3,13 +3,22 @@ #include "Debugger/FlowDebuggerSubsystem.h" #include "Debugger/FlowDebuggerSettings.h" +#include "FlowAsset.h" +#include "FlowSubsystem.h" + #include "EdGraph/EdGraphNode.h" #include "EdGraph/EdGraphPin.h" +#include "Engine/Engine.h" +#include "Engine/GameInstance.h" +#include "GameFramework/GameModeBase.h" +#include "GameFramework/WorldSettings.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowDebuggerSubsystem) UFlowDebuggerSubsystem::UFlowDebuggerSubsystem() { + UFlowSubsystem::OnInstancedTemplateAdded.BindUObject(this, &ThisClass::OnInstancedTemplateAdded); + UFlowSubsystem::OnInstancedTemplateRemoved.BindUObject(this, &ThisClass::OnInstancedTemplateRemoved); } bool UFlowDebuggerSubsystem::ShouldCreateSubsystem(UObject* Outer) const @@ -25,148 +34,199 @@ bool UFlowDebuggerSubsystem::ShouldCreateSubsystem(UObject* Outer) const return true; } -void UFlowDebuggerSubsystem::AddBreakpoint(const UEdGraphNode* Node) +void UFlowDebuggerSubsystem::OnInstancedTemplateAdded(UFlowAsset* AssetTemplate) +{ + AssetTemplate->OnPinTriggered.BindUObject(this, &ThisClass::OnPinTriggered); +} + +void UFlowDebuggerSubsystem::OnInstancedTemplateRemoved(UFlowAsset* AssetTemplate) const +{ + AssetTemplate->OnPinTriggered.Unbind(); +} + +void UFlowDebuggerSubsystem::OnPinTriggered(const FGuid& NodeGuid, const FName& PinName) +{ + if (FindBreakpoint(NodeGuid, PinName)) + { + MarkAsHit(NodeGuid, PinName); + } + + // Node breakpoints waits on any pin triggered + MarkAsHit(NodeGuid); +} + +void UFlowDebuggerSubsystem::AddBreakpoint(const FGuid& NodeGuid) { UFlowDebuggerSettings* Settings = GetMutableDefault(); - FFlowBreakpoint& NodeBreakpoint = Settings->NodeBreakpoints.FindOrAdd(Node->NodeGuid); + FNodeBreakpoint& NodeBreakpoint = Settings->NodeBreakpoints.FindOrAdd(NodeGuid); - NodeBreakpoint.SetEnabled(true); + NodeBreakpoint.Breakpoint.SetActive(true); SaveSettings(); } -void UFlowDebuggerSubsystem::AddBreakpoint(const UEdGraphPin* Pin) +void UFlowDebuggerSubsystem::AddBreakpoint(const FGuid& NodeGuid, const FName& PinName) { UFlowDebuggerSettings* Settings = GetMutableDefault(); - FFlowBreakpoint& PinBreakpoint = Settings->PinBreakpoints.FindOrAdd(Pin->PinId); + FNodeBreakpoint& NodeBreakpoint = Settings->NodeBreakpoints.FindOrAdd(NodeGuid); + FFlowBreakpoint& PinBreakpoint = NodeBreakpoint.PinBreakpoints.FindOrAdd(PinName); PinBreakpoint.SetEnabled(true); SaveSettings(); } -void UFlowDebuggerSubsystem::RemoveAllBreakpoints(const UEdGraphNode* Node) +void UFlowDebuggerSubsystem::RemoveAllBreakpoints(const FGuid& NodeGuid) { UFlowDebuggerSettings* Settings = GetMutableDefault(); - if (Settings->NodeBreakpoints.Contains(Node->NodeGuid)) + if (Settings->NodeBreakpoints.Contains(NodeGuid)) { - Settings->NodeBreakpoints.Remove(Node->NodeGuid); + Settings->NodeBreakpoints.Remove(NodeGuid); SaveSettings(); } +} - for (const UEdGraphPin* Pin : Node->Pins) +void UFlowDebuggerSubsystem::RemoveNodeBreakpoint(const FGuid& NodeGuid) +{ + UFlowDebuggerSettings* Settings = GetMutableDefault(); + if (FNodeBreakpoint* NodeBreakpoint = Settings->NodeBreakpoints.Find(NodeGuid)) { - if (Settings->PinBreakpoints.Contains(Pin->PinId)) + if (NodeBreakpoint->PinBreakpoints.IsEmpty()) { - Settings->PinBreakpoints.Remove(Pin->PinId); - SaveSettings(); + // no pin breakpoints here, remove the entire entry + Settings->NodeBreakpoints.Remove(NodeGuid); + } + else + { + // there are pin breakpoints here, only deactivate node breakpoint + NodeBreakpoint->Breakpoint.SetActive(false); } + + SaveSettings(); } } -void UFlowDebuggerSubsystem::RemoveNodeBreakpoint(const UEdGraphNode* Node) +void UFlowDebuggerSubsystem::RemovePinBreakpoint(const FGuid& NodeGuid, const FName& PinName) { UFlowDebuggerSettings* Settings = GetMutableDefault(); - if (Settings->NodeBreakpoints.Contains(Node->NodeGuid)) + if (FNodeBreakpoint* NodeBreakpoint = Settings->NodeBreakpoints.Find(NodeGuid)) { - Settings->NodeBreakpoints.Remove(Node->NodeGuid); + if (NodeBreakpoint->PinBreakpoints.Contains(PinName)) + { + NodeBreakpoint->PinBreakpoints.Remove(PinName); + } + + if (!NodeBreakpoint->Breakpoint.IsActive() && NodeBreakpoint->PinBreakpoints.IsEmpty()) + { + // no breakpoints remained, remove the entire entry + Settings->NodeBreakpoints.Remove(NodeGuid); + } + SaveSettings(); } } -void UFlowDebuggerSubsystem::RemovePinBreakpoint(const UEdGraphPin* Pin) +#if WITH_EDITOR +void UFlowDebuggerSubsystem::RemoveObsoletePinBreakpoints(const UEdGraphNode* Node) { UFlowDebuggerSettings* Settings = GetMutableDefault(); - if (Settings->PinBreakpoints.Contains(Pin->PinId)) + if (FNodeBreakpoint* NodeBreakpoint = Settings->NodeBreakpoints.Find(Node->NodeGuid)) { - Settings->PinBreakpoints.Remove(Pin->PinId); - SaveSettings(); + bool bAnythingRemoved = false; + + TSet PinNames; + PinNames.Reserve(Node->Pins.Num()); + for (const UEdGraphPin* Pin : Node->Pins) + { + PinNames.Emplace(Pin->PinName); + } + + for (TPair& PinBreakpoint : NodeBreakpoint->PinBreakpoints) + { + if (!PinNames.Contains(PinBreakpoint.Key)) + { + NodeBreakpoint->PinBreakpoints.Remove(PinBreakpoint.Key); + bAnythingRemoved = true; + } + } + + if (NodeBreakpoint->IsEmpty()) + { + Settings->NodeBreakpoints.Remove(Node->NodeGuid); + bAnythingRemoved = true; + } + + if (bAnythingRemoved) + { + SaveSettings(); + } } } +#endif -void UFlowDebuggerSubsystem::RemoveObsoletePinBreakpoints(const UEdGraphNode* Node) +void UFlowDebuggerSubsystem::ToggleBreakpoint(const FGuid& NodeGuid) { - // UFlowDebuggerSettings* Settings = GetMutableDefault(); - // if (FFlowBreakpoint* NodeBreakpoint = Settings->NodeBreakpoints.Find(Node->NodeGuid)) - // { - // TSet PinGuids; - // PinGuids.Reserve(Node->Pins.Num()); - // for (const UEdGraphPin* Pin : Node->Pins) - // { - // PinGuids.Emplace(Pin->PinId); - // } - // - // Settings->PinBreakpoints.RemoveAllSwap([PinGuids](const FFlowBreakpoint& PinBreakpoint) - // { - // return !PinGuids.Contains(PinBreakpoint.GetPinId()); - // }); - // - // // if all settings data is default, we can remove it from the map - // if (*NodeBreakpoint == FFlowBreakpoint()) - // { - // Settings->NodeBreakpoints.Remove(Node->NodeGuid); - // } - // - // SaveSettings(); - // } -} - -void UFlowDebuggerSubsystem::ToggleBreakpoint(const UEdGraphNode* Node) -{ - if (FindBreakpoint(Node) == nullptr) - { - AddBreakpoint(Node); + if (FindBreakpoint(NodeGuid) == nullptr) + { + AddBreakpoint(NodeGuid); } else { - RemoveNodeBreakpoint(Node); + RemoveNodeBreakpoint(NodeGuid); } } -void UFlowDebuggerSubsystem::ToggleBreakpoint(const UEdGraphPin* Pin) +void UFlowDebuggerSubsystem::ToggleBreakpoint(const FGuid& NodeGuid, const FName& PinName) { - if (FindBreakpoint(Pin) == nullptr) + if (FindBreakpoint(NodeGuid, PinName) == nullptr) { - AddBreakpoint(Pin); + AddBreakpoint(NodeGuid, PinName); } else { - RemovePinBreakpoint(Pin); + RemovePinBreakpoint(NodeGuid, PinName); } } -FFlowBreakpoint* UFlowDebuggerSubsystem::FindBreakpoint(const UEdGraphNode* Node) +FFlowBreakpoint* UFlowDebuggerSubsystem::FindBreakpoint(const FGuid& NodeGuid) { UFlowDebuggerSettings* Settings = GetMutableDefault(); - return Settings->NodeBreakpoints.Find(Node->NodeGuid); + FNodeBreakpoint* NodeBreakpoint = Settings->NodeBreakpoints.Find(NodeGuid); + if (NodeBreakpoint && NodeBreakpoint->Breakpoint.IsActive()) + { + return &NodeBreakpoint->Breakpoint; + } + + return nullptr; } -FFlowBreakpoint* UFlowDebuggerSubsystem::FindBreakpoint(const UEdGraphPin* Pin) +FFlowBreakpoint* UFlowDebuggerSubsystem::FindBreakpoint(const FGuid& NodeGuid, const FName& PinName) { UFlowDebuggerSettings* Settings = GetMutableDefault(); - return Settings->PinBreakpoints.Find(Pin->PinId); + FNodeBreakpoint* NodeBreakpoint = Settings->NodeBreakpoints.Find(NodeGuid); + return NodeBreakpoint ? NodeBreakpoint->PinBreakpoints.Find(PinName) : nullptr; } -void UFlowDebuggerSubsystem::SetBreakpointEnabled(const UEdGraphNode* Node, const bool bEnabled) +void UFlowDebuggerSubsystem::SetBreakpointEnabled(const FGuid& NodeGuid, const bool bEnabled) { - if (FFlowBreakpoint* NodeBreakpoint = FindBreakpoint(Node)) + if (FFlowBreakpoint* NodeBreakpoint = FindBreakpoint(NodeGuid)) { NodeBreakpoint->SetEnabled(bEnabled); SaveSettings(); } } -void UFlowDebuggerSubsystem::SetBreakpointEnabled(const UEdGraphPin* Pin, const bool bEnabled) +void UFlowDebuggerSubsystem::SetBreakpointEnabled(const FGuid& NodeGuid, const FName& PinName, const bool bEnabled) { - if (FFlowBreakpoint* PinBreakpoint = FindBreakpoint(Pin)) + if (FFlowBreakpoint* PinBreakpoint = FindBreakpoint(NodeGuid, PinName)) { PinBreakpoint->SetEnabled(bEnabled); SaveSettings(); } } -bool UFlowDebuggerSubsystem::IsBreakpointEnabled(const UEdGraphNode* Node) +bool UFlowDebuggerSubsystem::IsBreakpointEnabled(const FGuid& NodeGuid) { - if (const FFlowBreakpoint* PinBreakpoint = FindBreakpoint(Node)) + if (const FFlowBreakpoint* PinBreakpoint = FindBreakpoint(NodeGuid)) { return PinBreakpoint->IsEnabled(); } @@ -174,9 +234,9 @@ bool UFlowDebuggerSubsystem::IsBreakpointEnabled(const UEdGraphNode* Node) return false; } -bool UFlowDebuggerSubsystem::IsBreakpointEnabled(const UEdGraphPin* Pin) +bool UFlowDebuggerSubsystem::IsBreakpointEnabled(const FGuid& NodeGuid, const FName& PinName) { - if (const FFlowBreakpoint* PinBreakpoint = FindBreakpoint(Pin)) + if (const FFlowBreakpoint* PinBreakpoint = FindBreakpoint(NodeGuid, PinName)) { return PinBreakpoint->IsEnabled(); } @@ -184,47 +244,87 @@ bool UFlowDebuggerSubsystem::IsBreakpointEnabled(const UEdGraphPin* Pin) return false; } -bool UFlowDebuggerSubsystem::MarkAsHit(const UEdGraphNode* Node) +void UFlowDebuggerSubsystem::MarkAsHit(const FGuid& NodeGuid) { - if (FFlowBreakpoint* NodeBreakpoint = FindBreakpoint(Node)) + if (FFlowBreakpoint* NodeBreakpoint = FindBreakpoint(NodeGuid)) { NodeBreakpoint->MarkAsHit(true); - return true; + PauseSession(); } - - return false; } -bool UFlowDebuggerSubsystem::MarkAsHit(const UEdGraphPin* Pin) +void UFlowDebuggerSubsystem::MarkAsHit(const FGuid& NodeGuid, const FName& PinName) { - if (FFlowBreakpoint* PinBreakpoint = FindBreakpoint(Pin)) + if (FFlowBreakpoint* PinBreakpoint = FindBreakpoint(NodeGuid, PinName)) { PinBreakpoint->MarkAsHit(true); - return true; + PauseSession(); } +} - return false; +void UFlowDebuggerSubsystem::PauseSession() +{ + SetPause(true); +} + +void UFlowDebuggerSubsystem::ResumeSession() +{ + SetPause(false); } -void UFlowDebuggerSubsystem::ResetHit(const UEdGraphNode* Node) +void UFlowDebuggerSubsystem::SetPause(const bool bPause) { - if (FFlowBreakpoint* NodeBreakpoint = FindBreakpoint(Node)) + // experimental implementation, untested, shows intent for future development + // here be dragons: same as APlayerController::SetPause, but we allow debugger to pause on clients + if (const UWorld* World = GEngine->GetWorldFromContextObject(this, EGetWorldErrorMode::LogAndReturnNull)) { - NodeBreakpoint->MarkAsHit(false); + if (const UGameInstance* GameInstance = World->GetGameInstance()) + { + if (APlayerController* PlayerController = GameInstance->GetFirstLocalPlayerController()) + { + if (AGameModeBase* const GameMode = GetWorld()->GetAuthGameMode()) + { + const bool bCurrentPauseState = PlayerController->IsPaused(); + if (bPause && !bCurrentPauseState) + { + GameMode->SetPause(PlayerController); + + if (AWorldSettings* WorldSettings = PlayerController->GetWorldSettings()) + { + WorldSettings->ForceNetUpdate(); + } + } + else if (!bPause && bCurrentPauseState) + { + if (GameMode->ClearPause()) + { + ClearHitBreakpoints(); + } + } + } + } + } } } -void UFlowDebuggerSubsystem::ResetHit(const UEdGraphPin* Pin) +void UFlowDebuggerSubsystem::ClearHitBreakpoints() { - if (FFlowBreakpoint* PinBreakpoint = FindBreakpoint(Pin)) + UFlowDebuggerSettings* Settings = GetMutableDefault(); + + for (TPair& NodeBreakpoint : Settings->NodeBreakpoints) { - PinBreakpoint->MarkAsHit(false); + NodeBreakpoint.Value.Breakpoint.MarkAsHit(false); + + for (TPair& PinBreakpoint : NodeBreakpoint.Value.PinBreakpoints) + { + PinBreakpoint.Value.MarkAsHit(false); + } } } -bool UFlowDebuggerSubsystem::IsBreakpointHit(const UEdGraphNode* Node) +bool UFlowDebuggerSubsystem::IsBreakpointHit(const FGuid& NodeGuid) { - if (const FFlowBreakpoint* NodeBreakpoint = FindBreakpoint(Node)) + if (const FFlowBreakpoint* NodeBreakpoint = FindBreakpoint(NodeGuid)) { return NodeBreakpoint->IsHit(); } @@ -232,9 +332,9 @@ bool UFlowDebuggerSubsystem::IsBreakpointHit(const UEdGraphNode* Node) return false; } -bool UFlowDebuggerSubsystem::IsBreakpointHit(const UEdGraphPin* Pin) +bool UFlowDebuggerSubsystem::IsBreakpointHit(const FGuid& NodeGuid, const FName& PinName) { - if (const FFlowBreakpoint* PinBreakpoint = FindBreakpoint(Pin)) + if (const FFlowBreakpoint* PinBreakpoint = FindBreakpoint(NodeGuid, PinName)) { return PinBreakpoint->IsHit(); } diff --git a/Source/FlowDebugger/Public/Debugger/FlowDebuggerSettings.h b/Source/FlowDebugger/Public/Debugger/FlowDebuggerSettings.h index 7748914bf..b09f249ad 100644 --- a/Source/FlowDebugger/Public/Debugger/FlowDebuggerSettings.h +++ b/Source/FlowDebugger/Public/Debugger/FlowDebuggerSettings.h @@ -19,8 +19,5 @@ class FLOWDEBUGGER_API UFlowDebuggerSettings : public UDeveloperSettings UFlowDebuggerSettings(); UPROPERTY(config) - TMap NodeBreakpoints; - - UPROPERTY(config) - TMap PinBreakpoints; + TMap NodeBreakpoints; }; diff --git a/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h b/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h index cf92da663..c83e004cd 100644 --- a/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h +++ b/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h @@ -8,7 +8,7 @@ #include "FlowDebuggerSubsystem.generated.h" class UEdGraphNode; -class UEdGraphPin; +class UFlowAsset; /** * Persistent subsystem supporting Flow Graph debugging. @@ -24,41 +24,52 @@ class FLOWDEBUGGER_API UFlowDebuggerSubsystem : public UEngineSubsystem virtual bool ShouldCreateSubsystem(UObject* Outer) const override; - virtual void PausePlaySession() {} - virtual bool IsPlaySessionPaused() { return false; } +protected: + virtual void OnInstancedTemplateAdded(UFlowAsset* AssetTemplate); + virtual void OnInstancedTemplateRemoved(UFlowAsset* AssetTemplate) const; + + virtual void OnPinTriggered(const FGuid& NodeGuid, const FName& PinName); - virtual void AddBreakpoint(const UEdGraphNode* Node); - virtual void AddBreakpoint(const UEdGraphPin* Pin); +public: + virtual void AddBreakpoint(const FGuid& NodeGuid); + virtual void AddBreakpoint(const FGuid& NodeGuid, const FName& PinName); - virtual void RemoveAllBreakpoints(const UEdGraphNode* Node); - virtual void RemoveNodeBreakpoint(const UEdGraphNode* Node); - virtual void RemovePinBreakpoint(const UEdGraphPin* Pin); + virtual void RemoveAllBreakpoints(const FGuid& NodeGuid); + virtual void RemoveNodeBreakpoint(const FGuid& NodeGuid); + virtual void RemovePinBreakpoint(const FGuid& NodeGuid, const FName& PinName); +#if WITH_EDITOR /** Removes obsolete pin breakpoints for provided. Pin list can be changed during node reconstruction. */ virtual void RemoveObsoletePinBreakpoints(const UEdGraphNode* Node); +#endif - virtual void ToggleBreakpoint(const UEdGraphNode* Node); - virtual void ToggleBreakpoint(const UEdGraphPin* Pin); - - virtual FFlowBreakpoint* FindBreakpoint(const UEdGraphNode* Node); - virtual FFlowBreakpoint* FindBreakpoint(const UEdGraphPin* Pin); + virtual void ToggleBreakpoint(const FGuid& NodeGuid); + virtual void ToggleBreakpoint(const FGuid& NodeGuid, const FName& PinName); - virtual void SetBreakpointEnabled(const UEdGraphNode* Node, bool bEnabled); - virtual void SetBreakpointEnabled(const UEdGraphPin* Pin, bool bEnabled); + virtual FFlowBreakpoint* FindBreakpoint(const FGuid& NodeGuid); + virtual FFlowBreakpoint* FindBreakpoint(const FGuid& NodeGuid, const FName& PinName); - virtual bool IsBreakpointEnabled(const UEdGraphNode* Node); - virtual bool IsBreakpointEnabled(const UEdGraphPin* Pin); + virtual void SetBreakpointEnabled(const FGuid& NodeGuid, bool bEnabled); + virtual void SetBreakpointEnabled(const FGuid& NodeGuid, const FName& PinName, bool bEnabled); - virtual bool MarkAsHit(const UEdGraphNode* Node); - virtual bool MarkAsHit(const UEdGraphPin* Pin); + virtual bool IsBreakpointEnabled(const FGuid& NodeGuid); + virtual bool IsBreakpointEnabled(const FGuid& NodeGuid, const FName& PinName); - virtual void ResetHit(const UEdGraphNode* Node); - virtual void ResetHit(const UEdGraphPin* Pin); +protected: + virtual void MarkAsHit(const FGuid& NodeGuid); + virtual void MarkAsHit(const FGuid& NodeGuid, const FName& PinName); + + virtual void PauseSession(); + virtual void ResumeSession(); + void SetPause(const bool bPause); - virtual bool IsBreakpointHit(const UEdGraphNode* Node); - virtual bool IsBreakpointHit(const UEdGraphPin* Pin); + virtual void ClearHitBreakpoints(); -protected: +public: + virtual bool IsBreakpointHit(const FGuid& NodeGuid); + virtual bool IsBreakpointHit(const FGuid& NodeGuid, const FName& PinName); + +private: /** Saves any modifications made to breakpoints */ virtual void SaveSettings(); }; diff --git a/Source/FlowDebugger/Public/Debugger/FlowDebuggerTypes.h b/Source/FlowDebugger/Public/Debugger/FlowDebuggerTypes.h index 8173ef9a8..ec5435784 100644 --- a/Source/FlowDebugger/Public/Debugger/FlowDebuggerTypes.h +++ b/Source/FlowDebugger/Public/Debugger/FlowDebuggerTypes.h @@ -4,53 +4,70 @@ #include "FlowDebuggerTypes.generated.h" -// It can represent any trait added on the specific node instance, i.e. breakpoint USTRUCT() struct FLOWDEBUGGER_API FFlowBreakpoint { - GENERATED_USTRUCT_BODY() + GENERATED_BODY() protected: - /** Pin that the trait is placed on. Zero filled if this trait is for a node, not a pin */ + // Applies only to node breakpoint + // Pin breakpoints are deactivated by removing element from FNodeBreakpoint::PinBreakpoints UPROPERTY() - FGuid PinId; + bool bActive; UPROPERTY() uint8 bEnabled : 1; - + + UPROPERTY(Transient) uint8 bHit : 1; public: FFlowBreakpoint() - : bEnabled(false) + : bActive(false) + , bEnabled(false) , bHit(false) { }; - explicit FFlowBreakpoint(const bool bInitialState) - : bEnabled(bInitialState) - , bHit(false) + void SetActive(const bool bNowActive) { - }; + bActive = bNowActive; + bEnabled = bNowActive; + } - explicit FFlowBreakpoint(const FGuid& InPinId, const bool bInitialState) - : PinId(InPinId) - , bEnabled(bInitialState) - , bHit(false) + void SetEnabled(const bool bNowEnabled) { - }; + bEnabled = bNowEnabled; + } - void SetEnabled(const bool bNowEnabled) { bEnabled = bNowEnabled; } - void MarkAsHit(const bool bNowHit) { bHit = bNowHit; } + void MarkAsHit(const bool bNowHit) + { + bHit = bNowHit; + } - FGuid GetPinId() const { return PinId; } - bool MatchesGuid(const FGuid& OtherGuid) const { return PinId == OtherGuid; } - + bool IsActive() const { return bActive; } bool IsEnabled() const { return bEnabled; } bool IsHit() const { return bHit; } +}; + +USTRUCT() +struct FLOWDEBUGGER_API FNodeBreakpoint +{ + GENERATED_BODY() + +public: + UPROPERTY() + FFlowBreakpoint Breakpoint; + + UPROPERTY() + TMap PinBreakpoints; + + FNodeBreakpoint() + { + }; - bool operator==(const FFlowBreakpoint& Other) const + bool IsEmpty() const { - return PinId == Other.PinId; + return !Breakpoint.IsActive() && PinBreakpoints.IsEmpty(); } }; diff --git a/Source/FlowEditor/Private/Asset/FlowDebugEditorSubsystem.cpp b/Source/FlowEditor/Private/Asset/FlowDebugEditorSubsystem.cpp index 5f0581c61..b30394a0e 100644 --- a/Source/FlowEditor/Private/Asset/FlowDebugEditorSubsystem.cpp +++ b/Source/FlowEditor/Private/Asset/FlowDebugEditorSubsystem.cpp @@ -4,8 +4,6 @@ #include "Asset/FlowAssetEditor.h" #include "Asset/FlowMessageLogListing.h" -#include "FlowSubsystem.h" - #include "Editor/UnrealEdEngine.h" #include "Engine/Engine.h" #include "Engine/World.h" @@ -20,30 +18,32 @@ UFlowDebugEditorSubsystem::UFlowDebugEditorSubsystem() { - FEditorDelegates::BeginPIE.AddUObject(this, &UFlowDebugEditorSubsystem::OnBeginPIE); - FEditorDelegates::EndPIE.AddUObject(this, &UFlowDebugEditorSubsystem::OnEndPIE); - - UFlowSubsystem::OnInstancedTemplateAdded.BindUObject(this, &UFlowDebugEditorSubsystem::OnInstancedTemplateAdded); - UFlowSubsystem::OnInstancedTemplateRemoved.BindUObject(this, &UFlowDebugEditorSubsystem::OnInstancedTemplateRemoved); + FEditorDelegates::BeginPIE.AddUObject(this, &ThisClass::OnBeginPIE); + FEditorDelegates::ResumePIE.AddUObject(this, &ThisClass::OnResumePIE); + FEditorDelegates::EndPIE.AddUObject(this, &ThisClass::OnEndPIE); } -void UFlowDebugEditorSubsystem::OnInstancedTemplateAdded(UFlowAsset* FlowAsset) +void UFlowDebugEditorSubsystem::OnInstancedTemplateAdded(UFlowAsset* AssetTemplate) { - if (!RuntimeLogs.Contains(FlowAsset)) + Super::OnInstancedTemplateAdded(AssetTemplate); + + if (!RuntimeLogs.Contains(AssetTemplate)) { - RuntimeLogs.Add(FlowAsset, FFlowMessageLogListing::GetLogListing(FlowAsset, EFlowLogType::Runtime)); - FlowAsset->OnRuntimeMessageAdded().AddUObject(this, &UFlowDebugEditorSubsystem::OnRuntimeMessageAdded); + RuntimeLogs.Add(AssetTemplate, FFlowMessageLogListing::GetLogListing(AssetTemplate, EFlowLogType::Runtime)); + AssetTemplate->OnRuntimeMessageAdded().AddUObject(this, &UFlowDebugEditorSubsystem::OnRuntimeMessageAdded); } } -void UFlowDebugEditorSubsystem::OnInstancedTemplateRemoved(UFlowAsset* FlowAsset) const +void UFlowDebugEditorSubsystem::OnInstancedTemplateRemoved(UFlowAsset* AssetTemplate) const { - FlowAsset->OnRuntimeMessageAdded().RemoveAll(this); + AssetTemplate->OnRuntimeMessageAdded().RemoveAll(this); + + Super::OnInstancedTemplateRemoved(AssetTemplate); } -void UFlowDebugEditorSubsystem::OnRuntimeMessageAdded(const UFlowAsset* FlowAsset, const TSharedRef& Message) const +void UFlowDebugEditorSubsystem::OnRuntimeMessageAdded(const UFlowAsset* AssetTemplate, const TSharedRef& Message) const { - const TSharedPtr Log = RuntimeLogs.FindRef(FlowAsset); + const TSharedPtr Log = RuntimeLogs.FindRef(AssetTemplate); if (Log.IsValid()) { Log->AddMessage(Message); @@ -57,8 +57,15 @@ void UFlowDebugEditorSubsystem::OnBeginPIE(const bool bIsSimulating) RuntimeLogs.Empty(); } +void UFlowDebugEditorSubsystem::OnResumePIE(const bool bIsSimulating) +{ + ClearHitBreakpoints(); +} + void UFlowDebugEditorSubsystem::OnEndPIE(const bool bIsSimulating) { + ClearHitBreakpoints(); + for (const TPair, TSharedPtr>& Log : RuntimeLogs) { if (Log.Key.IsValid() && Log.Value->NumMessages(EMessageSeverity::Warning) > 0) @@ -85,48 +92,12 @@ void UFlowDebugEditorSubsystem::OnEndPIE(const bool bIsSimulating) } } -void ForEachGameWorld(const TFunction& Func) +void UFlowDebugEditorSubsystem::PauseSession() { - for (const FWorldContext& PieContext : GUnrealEd->GetWorldContexts()) - { - UWorld* PlayWorld = PieContext.World(); - if (PlayWorld && PlayWorld->IsGameWorld()) - { - Func(PlayWorld); - } - } -} - -bool AreAllGameWorldPaused() -{ - bool bPaused = true; - ForEachGameWorld([&](const UWorld* World) - { - bPaused = bPaused && World->bDebugPauseExecution; - }); - return bPaused; -} - -void UFlowDebugEditorSubsystem::PausePlaySession() -{ - bool bPaused = false; - ForEachGameWorld([&](UWorld* World) - { - if (!World->bDebugPauseExecution) - { - World->bDebugPauseExecution = true; - bPaused = true; - } - }); - if (bPaused) + if (!GUnrealEd->SetPIEWorldsPaused(true)) { GUnrealEd->PlaySessionPaused(); } } -bool UFlowDebugEditorSubsystem::IsPlaySessionPaused() -{ - return AreAllGameWorldPaused(); -} - #undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/Graph/FlowGraph.cpp b/Source/FlowEditor/Private/Graph/FlowGraph.cpp index a2d7281db..14bfa5881 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraph.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraph.cpp @@ -15,27 +15,12 @@ #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraph) -void FFlowGraphInterface::OnInputTriggered(UEdGraphNode* GraphNode, const int32 Index) const -{ - CastChecked(GraphNode)->OnInputTriggered(Index); -} - -void FFlowGraphInterface::OnOutputTriggered(UEdGraphNode* GraphNode, const int32 Index) const -{ - CastChecked(GraphNode)->OnOutputTriggered(Index); -} - UFlowGraph::UFlowGraph(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , GraphVersion(0) { bLockUpdates = false; bIsLoadingGraph = false; - - if (!UFlowAsset::GetFlowGraphInterface().IsValid()) - { - UFlowAsset::SetFlowGraphInterface(MakeShared()); - } } void UFlowGraph::CreateGraph(UFlowAsset* InFlowAsset) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index eb4a33845..4cf5e1449 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -12,6 +12,7 @@ #include "Debugger/FlowDebuggerSubsystem.h" #include "EdGraphUtilities.h" +#include "Editor/UnrealEdEngine.h" #include "Framework/Application/SlateApplication.h" #include "Framework/Commands/GenericCommands.h" #include "GraphEditorActions.h" @@ -20,6 +21,7 @@ #include "LevelEditor.h" #include "Modules/ModuleManager.h" #include "ScopedTransaction.h" +#include "UnrealEdGlobals.h" #include "Widgets/Docking/SDockTab.h" #define LOCTEXT_NAMESPACE "FlowGraphEditor" @@ -260,7 +262,7 @@ FGraphAppearanceInfo SFlowGraphEditor::GetGraphAppearanceInfo() const FGraphAppearanceInfo AppearanceInfo; AppearanceInfo.CornerText = GetCornerText(); - if (DebuggerSubsystem.IsValid() && DebuggerSubsystem->IsPlaySessionPaused()) + if (IsPlaySessionPaused()) { AppearanceInfo.PIENotifyText = LOCTEXT("PausedLabel", "PAUSED"); } @@ -307,6 +309,11 @@ void SFlowGraphEditor::OnCreateComment() const CommentAction.PerformAction(FlowAsset->GetGraph(), nullptr, GetPasteLocation()); } +bool SFlowGraphEditor::IsTabFocused() const +{ + return FlowAssetEditor.Pin()->IsTabFocused(FFlowAssetEditor::GraphTab); +} + bool SFlowGraphEditor::CanEdit() { return GEditor->PlayWorld == nullptr; @@ -317,9 +324,20 @@ bool SFlowGraphEditor::IsPIE() return GEditor->PlayWorld != nullptr; } -bool SFlowGraphEditor::IsTabFocused() const +bool SFlowGraphEditor::IsPlaySessionPaused() { - return FlowAssetEditor.Pin()->IsTabFocused(FFlowAssetEditor::GraphTab); + bool bPaused = true; + + for (const FWorldContext& PieContext : GUnrealEd->GetWorldContexts()) + { + const UWorld* PlayWorld = PieContext.World(); + if (PlayWorld && PlayWorld->IsGameWorld()) + { + bPaused = bPaused && PlayWorld->bDebugPauseExecution; + } + } + + return bPaused; } void SFlowGraphEditor::SelectSingleNode(UEdGraphNode* Node) @@ -462,17 +480,15 @@ void SFlowGraphEditor::DeleteSelectedNodes() UEdGraphNode* Node = CastChecked(*NodeIt); if (Node->CanUserDeleteNode()) { + if (DebuggerSubsystem.IsValid()) + { + DebuggerSubsystem->RemoveAllBreakpoints(Node->NodeGuid); + } + if (const UFlowGraphNode* FlowGraphNode = Cast(Node)) { - if (DebuggerSubsystem.IsValid()) - { - DebuggerSubsystem->RemoveAllBreakpoints(FlowGraphNode); - } - if (const UFlowNode* FlowNode = Cast(FlowGraphNode->GetFlowNodeBase())) { - const FGuid NodeGuid = FlowNode->GetGuid(); - // If the user is pressing shift then try and reconnect the pins if (FSlateApplication::Get().GetModifierKeys().IsShiftDown()) { @@ -482,7 +498,7 @@ void SFlowGraphEditor::DeleteSelectedNodes() GetCurrentGraph()->GetSchema()->BreakNodeLinks(*Node); Node->DestroyNode(); - FlowAsset->UnregisterNode(NodeGuid); + FlowAsset->UnregisterNode(FlowNode->GetGuid()); continue; } } @@ -1097,7 +1113,7 @@ void SFlowGraphEditor::OnAddBreakpoint() const check(DebuggerSubsystem.IsValid()); for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - DebuggerSubsystem->AddBreakpoint(SelectedNode); + DebuggerSubsystem->AddBreakpoint(SelectedNode->NodeGuid); } } @@ -1106,7 +1122,7 @@ void SFlowGraphEditor::OnAddPinBreakpoint() check(DebuggerSubsystem.IsValid()); if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - DebuggerSubsystem->AddBreakpoint(Pin); + DebuggerSubsystem->AddBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName); } } @@ -1115,7 +1131,7 @@ bool SFlowGraphEditor::CanAddBreakpoint() const check(DebuggerSubsystem.IsValid()); for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - return DebuggerSubsystem->FindBreakpoint(SelectedNode) == nullptr; + return DebuggerSubsystem->FindBreakpoint(SelectedNode->NodeGuid) == nullptr; } return false; @@ -1126,7 +1142,7 @@ bool SFlowGraphEditor::CanAddPinBreakpoint() check(DebuggerSubsystem.IsValid()); if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - return DebuggerSubsystem->FindBreakpoint(Pin) == nullptr; + return DebuggerSubsystem->FindBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName) == nullptr; } return false; @@ -1137,7 +1153,7 @@ void SFlowGraphEditor::OnRemoveBreakpoint() const check(DebuggerSubsystem.IsValid()); for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - DebuggerSubsystem->RemoveNodeBreakpoint(SelectedNode); + DebuggerSubsystem->RemoveNodeBreakpoint(SelectedNode->NodeGuid); } } @@ -1146,7 +1162,7 @@ void SFlowGraphEditor::OnRemovePinBreakpoint() check(DebuggerSubsystem.IsValid()); if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - DebuggerSubsystem->RemovePinBreakpoint(Pin); + DebuggerSubsystem->RemovePinBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName); } } @@ -1155,7 +1171,7 @@ bool SFlowGraphEditor::CanRemoveBreakpoint() const check(DebuggerSubsystem.IsValid()); for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - return DebuggerSubsystem->FindBreakpoint(SelectedNode) != nullptr; + return DebuggerSubsystem->FindBreakpoint(SelectedNode->NodeGuid) != nullptr; } return false; @@ -1166,7 +1182,7 @@ bool SFlowGraphEditor::CanRemovePinBreakpoint() check(DebuggerSubsystem.IsValid()); if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - return DebuggerSubsystem->FindBreakpoint(Pin) != nullptr; + return DebuggerSubsystem->FindBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName) != nullptr; } return false; @@ -1177,7 +1193,7 @@ void SFlowGraphEditor::OnEnableBreakpoint() const check(DebuggerSubsystem.IsValid()); for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - DebuggerSubsystem->SetBreakpointEnabled(SelectedNode, true); + DebuggerSubsystem->SetBreakpointEnabled(SelectedNode->NodeGuid, true); } } @@ -1186,7 +1202,7 @@ void SFlowGraphEditor::OnEnablePinBreakpoint() check(DebuggerSubsystem.IsValid()); if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - DebuggerSubsystem->SetBreakpointEnabled(Pin, true); + DebuggerSubsystem->SetBreakpointEnabled(Pin->GetOwningNode()->NodeGuid, Pin->PinName, true); } } @@ -1194,7 +1210,7 @@ bool SFlowGraphEditor::CanEnableBreakpoint() const { for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - const FFlowBreakpoint* Breakpoint = DebuggerSubsystem->FindBreakpoint(SelectedNode); + const FFlowBreakpoint* Breakpoint = DebuggerSubsystem->FindBreakpoint(SelectedNode->NodeGuid); return Breakpoint && !Breakpoint->IsEnabled(); } @@ -1205,7 +1221,7 @@ bool SFlowGraphEditor::CanEnablePinBreakpoint() { if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - const FFlowBreakpoint* Breakpoint = DebuggerSubsystem->FindBreakpoint(Pin); + const FFlowBreakpoint* Breakpoint = DebuggerSubsystem->FindBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName); return Breakpoint && !Breakpoint->IsEnabled(); } @@ -1217,7 +1233,7 @@ void SFlowGraphEditor::OnDisableBreakpoint() const check(DebuggerSubsystem.IsValid()); for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - DebuggerSubsystem->SetBreakpointEnabled(SelectedNode, false); + DebuggerSubsystem->SetBreakpointEnabled(SelectedNode->NodeGuid, false); } } @@ -1226,7 +1242,7 @@ void SFlowGraphEditor::OnDisablePinBreakpoint() check(DebuggerSubsystem.IsValid()); if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - DebuggerSubsystem->SetBreakpointEnabled(Pin, false); + DebuggerSubsystem->SetBreakpointEnabled(Pin->GetOwningNode()->NodeGuid, Pin->PinName, false); } } @@ -1235,7 +1251,7 @@ bool SFlowGraphEditor::CanDisableBreakpoint() const check(DebuggerSubsystem.IsValid()); for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - const FFlowBreakpoint* Breakpoint = DebuggerSubsystem->FindBreakpoint(SelectedNode); + const FFlowBreakpoint* Breakpoint = DebuggerSubsystem->FindBreakpoint(SelectedNode->NodeGuid); return Breakpoint && Breakpoint->IsEnabled(); } @@ -1247,7 +1263,7 @@ bool SFlowGraphEditor::CanDisablePinBreakpoint() check(DebuggerSubsystem.IsValid()); if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - const FFlowBreakpoint* Breakpoint = DebuggerSubsystem->FindBreakpoint(Pin); + const FFlowBreakpoint* Breakpoint = DebuggerSubsystem->FindBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName); return Breakpoint && Breakpoint->IsEnabled(); } @@ -1259,7 +1275,7 @@ void SFlowGraphEditor::OnToggleBreakpoint() const check(DebuggerSubsystem.IsValid()); for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - DebuggerSubsystem->ToggleBreakpoint(SelectedNode); + DebuggerSubsystem->ToggleBreakpoint(SelectedNode->NodeGuid); } } @@ -1268,7 +1284,7 @@ void SFlowGraphEditor::OnTogglePinBreakpoint() check(DebuggerSubsystem.IsValid()); if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - DebuggerSubsystem->ToggleBreakpoint(Pin); + DebuggerSubsystem->ToggleBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName); } } diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index cc3438f46..2e40e0915 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -6,6 +6,8 @@ #include "AddOns/FlowNodeAddOn.h" #include "Nodes/FlowNode.h" +#include "Debugger/FlowDebuggerSubsystem.h" + #include "FlowEditorCommands.h" #include "Graph/FlowGraph.h" #include "Graph/FlowGraphEditorSettings.h" @@ -14,8 +16,6 @@ #include "Graph/Widgets/SFlowGraphNode.h" #include "Graph/Widgets/SGraphEditorActionMenuFlow.h" -#include "Debugger/FlowDebuggerSubsystem.h" - #include "BlueprintNodeHelpers.h" #include "Developer/ToolMenus/Public/ToolMenus.h" #include "DiffResults.h" @@ -885,7 +885,7 @@ void UFlowGraphNode::RemoveOrphanedPin(UEdGraphPin* Pin) if (UFlowDebuggerSubsystem* DebuggerSubsystem = GEngine->GetEngineSubsystem()) { - DebuggerSubsystem->RemovePinBreakpoint(Pin); + DebuggerSubsystem->RemovePinBreakpoint(NodeGuid, Pin->PinName); } Pin->MarkAsGarbage(); @@ -982,7 +982,7 @@ void UFlowGraphNode::RemoveInstancePin(UEdGraphPin* Pin) if (UFlowDebuggerSubsystem* DebuggerSubsystem = GEngine->GetEngineSubsystem()) { - DebuggerSubsystem->RemovePinBreakpoint(Pin); + DebuggerSubsystem->RemovePinBreakpoint(NodeGuid, Pin->PinName); } UFlowNode* FlowNode = Cast(NodeInstance); @@ -1064,87 +1064,6 @@ const FName& UFlowGraphNode::GetPinCategoryFromFlowPin(const FFlowPin& FlowPin) return FFlowPin::GetPinCategoryFromPinType(FlowPin.GetPinType()); } -void UFlowGraphNode::OnInputTriggered(const int32 Index) -{ - if (InputPins.IsValidIndex(Index)) - { - if (UFlowDebuggerSubsystem* DebuggerSubsystem = GEngine->GetEngineSubsystem()) - { - if (DebuggerSubsystem->MarkAsHit(InputPins[Index])) - { - TryPausingSession(true); - } - } - } - - TryPausingSession(false); -} - -void UFlowGraphNode::OnOutputTriggered(const int32 Index) -{ - if (OutputPins.IsValidIndex(Index)) - { - if (UFlowDebuggerSubsystem* DebuggerSubsystem = GEngine->GetEngineSubsystem()) - { - if (DebuggerSubsystem->MarkAsHit(OutputPins[Index])) - { - TryPausingSession(true); - } - } - } - - TryPausingSession(false); -} - -void UFlowGraphNode::TryPausingSession(bool bPauseSession) -{ - // Node breakpoints waits on any pin triggered - UFlowDebuggerSubsystem* DebuggerSubsystem = GEngine->GetEngineSubsystem(); - if (DebuggerSubsystem) - { - if (DebuggerSubsystem->MarkAsHit(this)) - { - bPauseSession = true; - } - } - - if (bPauseSession) - { - FEditorDelegates::ResumePIE.AddUObject(this, &UFlowGraphNode::OnResumePIE); - FEditorDelegates::EndPIE.AddUObject(this, &UFlowGraphNode::OnEndPIE); - - if (DebuggerSubsystem) - { - DebuggerSubsystem->PausePlaySession(); - } - } -} - -void UFlowGraphNode::OnResumePIE(const bool bIsSimulating) -{ - ResetBreakpoints(); -} - -void UFlowGraphNode::OnEndPIE(const bool bIsSimulating) -{ - ResetBreakpoints(); -} - -void UFlowGraphNode::ResetBreakpoints() -{ - FEditorDelegates::ResumePIE.RemoveAll(this); - FEditorDelegates::EndPIE.RemoveAll(this); - - if (UFlowDebuggerSubsystem* DebuggerSubsystem = GEngine->GetEngineSubsystem()) - { - DebuggerSubsystem->ResetHit(this); - for (const UEdGraphPin* Pin : Pins) - { - DebuggerSubsystem->ResetHit(Pin); - } - } -} - void UFlowGraphNode::ForcePinActivation(const FEdGraphPinReference PinReference) const { UFlowNode* InspectedNodeInstance = GetInspectedNodeInstance(); diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp index 718fb800b..7c4af8a0d 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp @@ -129,7 +129,7 @@ void SFlowGraphNode::GetOverlayBrushes(bool bSelected, const FVector2D WidgetSiz check(DebuggerSubsystem.IsValid()); // Node breakpoint - if (const FFlowBreakpoint* NodeBreakpoint = DebuggerSubsystem->FindBreakpoint(FlowGraphNode)) + if (const FFlowBreakpoint* NodeBreakpoint = DebuggerSubsystem->FindBreakpoint(FlowGraphNode->NodeGuid)) { FOverlayBrushInfo NodeBrush; @@ -152,7 +152,7 @@ void SFlowGraphNode::GetOverlayBrushes(bool bSelected, const FVector2D WidgetSiz // Pin breakpoints for (UEdGraphPin* Pin : FlowGraphNode->Pins) { - if (const FFlowBreakpoint* PinBreakpoint = DebuggerSubsystem->FindBreakpoint(Pin)) + if (const FFlowBreakpoint* PinBreakpoint = DebuggerSubsystem->FindBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName)) { if (Pin->Direction == EGPD_Input) { diff --git a/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h b/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h index 7585c99af..3cbe13fa8 100644 --- a/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h +++ b/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h @@ -24,15 +24,14 @@ class FLOWEDITOR_API UFlowDebugEditorSubsystem : public UFlowDebuggerSubsystem protected: TMap, TSharedPtr> RuntimeLogs; - void OnInstancedTemplateAdded(UFlowAsset* FlowAsset); - void OnInstancedTemplateRemoved(UFlowAsset* FlowAsset) const; + virtual void OnInstancedTemplateAdded(UFlowAsset* AssetTemplate) override; + virtual void OnInstancedTemplateRemoved(UFlowAsset* AssetTemplate) const override; - void OnRuntimeMessageAdded(const UFlowAsset* FlowAsset, const TSharedRef& Message) const; + void OnRuntimeMessageAdded(const UFlowAsset* AssetTemplate, const TSharedRef& Message) const; - void OnBeginPIE(const bool bIsSimulating); - void OnEndPIE(const bool bIsSimulating); + virtual void OnBeginPIE(const bool bIsSimulating); + virtual void OnResumePIE(const bool bIsSimulating); + virtual void OnEndPIE(const bool bIsSimulating); -public: - virtual void PausePlaySession() override; - virtual bool IsPlaySessionPaused() override; + virtual void PauseSession() override; }; diff --git a/Source/FlowEditor/Public/Graph/FlowGraph.h b/Source/FlowEditor/Public/Graph/FlowGraph.h index 5363860d7..4c9decc2c 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraph.h +++ b/Source/FlowEditor/Public/Graph/FlowGraph.h @@ -11,15 +11,6 @@ class SFlowGraphEditor; class UFlowGraphNode; class UFlowGraphSchema; -class FLOWEDITOR_API FFlowGraphInterface : public IFlowGraphInterface -{ -public: - virtual ~FFlowGraphInterface() override {} - - virtual void OnInputTriggered(UEdGraphNode* GraphNode, const int32 Index) const override; - virtual void OnOutputTriggered(UEdGraphNode* GraphNode, const int32 Index) const override; -}; - UCLASS() class FLOWEDITOR_API UFlowGraph : public UEdGraph { diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h index a303dc4a8..ac575ea86 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h @@ -50,9 +50,11 @@ class FLOWEDITOR_API SFlowGraphEditor : public SGraphEditor void OnCreateComment() const; public: + virtual bool IsTabFocused() const; + static bool CanEdit(); static bool IsPIE(); - virtual bool IsTabFocused() const; + static bool IsPlaySessionPaused(); virtual void SelectSingleNode(UEdGraphNode* Node); diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h index a8dd92697..e044caad3 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h @@ -208,20 +208,6 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode // (accounting for FFlowPin structs that predate the PinCategory field) static const FName& GetPinCategoryFromFlowPin(const FFlowPin& FlowPin); -////////////////////////////////////////////////////////////////////////// -// Breakpoints - -public: - void OnInputTriggered(const int32 Index); - void OnOutputTriggered(const int32 Index); - -private: - void TryPausingSession(bool bPauseSession); - - void OnResumePIE(const bool bIsSimulating); - void OnEndPIE(const bool bIsSimulating); - void ResetBreakpoints(); - ////////////////////////////////////////////////////////////////////////// // Execution Override From e9dc758c69e381b5166df66f9286b86ea2a71b1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Mon, 3 Mar 2025 20:41:22 +0100 Subject: [PATCH 341/485] cosmetic pass, shortened input param names for readability and should fit easily fit the screen in single line --- .../Nodes/Graph/FlowNode_DefineProperties.cpp | 14 +++------ .../Private/Nodes/Graph/FlowNode_SubGraph.cpp | 31 ++++++++----------- .../FlowDataPinGeneratorNodeInterface.h | 6 +--- .../Nodes/Graph/FlowNode_DefineProperties.h | 12 ++----- .../Public/Nodes/Graph/FlowNode_SubGraph.h | 20 +++++------- 5 files changed, 29 insertions(+), 54 deletions(-) diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_DefineProperties.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_DefineProperties.cpp index 38c3b0acb..dc55e7a43 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_DefineProperties.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_DefineProperties.cpp @@ -15,7 +15,7 @@ UFlowNode_DefineProperties::UFlowNode_DefineProperties(const FObjectInitializer& InputPins.Empty(); OutputPins.Empty(); - AllowedSignalModes = { EFlowSignalMode::Enabled, EFlowSignalMode::Disabled }; + AllowedSignalModes = {EFlowSignalMode::Enabled, EFlowSignalMode::Disabled}; } bool UFlowNode_DefineProperties::TryFindPropertyByRemappedPinName( @@ -40,19 +40,15 @@ bool UFlowNode_DefineProperties::TryFindPropertyByRemappedPinName( } #if WITH_EDITOR - -void UFlowNode_DefineProperties::AutoGenerateDataPins( - TMap& InOutPinNameToBoundPropertyNameMap, - TArray& InOutInputDataPins, - TArray& InOutOutputDataPins) const +void UFlowNode_DefineProperties::AutoGenerateDataPins(TMap& PinNameToBoundPropertyMap, TArray& InputDataPins, TArray& OutputDataPins) const { for (const FFlowNamedDataPinOutputProperty& DataPinProperty : OutputProperties) { if (DataPinProperty.IsValid()) { - InOutPinNameToBoundPropertyNameMap.Add(DataPinProperty.Name, DataPinProperty.Name); + PinNameToBoundPropertyMap.Add(DataPinProperty.Name, DataPinProperty.Name); - InOutOutputDataPins.AddUnique(DataPinProperty.CreateFlowPin()); + OutputDataPins.AddUnique(DataPinProperty.CreateFlowPin()); } } } @@ -70,7 +66,7 @@ void UFlowNode_DefineProperties::PostEditChangeChainProperty(FPropertyChangedCha // The DetailsCustomization for FFlowDataPinProperties isn't being called when using an InstancedStruct // so we need to call this refresh by hand... - if (PropertyChainEvent.ChangeType == EPropertyChangeType::ValueSet && + if (PropertyChainEvent.ChangeType == EPropertyChangeType::ValueSet && Property->GetFName() == GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Enum, EnumName)) { for (FFlowNamedDataPinOutputProperty& OutputProperty : OutputProperties) diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp index 047737d36..bb7f1f448 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp @@ -62,11 +62,11 @@ void UFlowNode_SubGraph::ExecuteInput(const FName& PinName) { LogError(FString::Printf(TEXT("Asset %s cannot be instance, probably is the same as the asset owning this SubGraph node."), *Asset.ToString())); } - + Finish(); return; } - + if (PinName == TEXT("Start")) { if (GetFlowSubsystem()) @@ -110,7 +110,7 @@ FText UFlowNode_SubGraph::GetNodeTitle() const { if (UFlowSettings::Get()->bUseAdaptiveNodeTitles && !Asset.IsNull()) { - return FText::Format(LOCTEXT("SubGraphTitle", "{0}\n{1}"), { Super::GetNodeTitle(), FText::FromString(Asset.ToSoftObjectPath().GetAssetName()) }); + return FText::Format(LOCTEXT("SubGraphTitle", "{0}\n{1}"), {Super::GetNodeTitle(), FText::FromString(Asset.ToSoftObjectPath().GetAssetName())}); } return Super::GetNodeTitle(); @@ -148,7 +148,7 @@ TArray UFlowNode_SubGraph::GetContextInputs() const if (!Asset.IsNull()) { - (void) Asset.LoadSynchronous(); + (void)Asset.LoadSynchronous(); for (const FName& PinName : Asset.Get()->GetCustomInputs()) { @@ -168,7 +168,7 @@ TArray UFlowNode_SubGraph::GetContextOutputs() const if (!Asset.IsNull()) { - (void) Asset.LoadSynchronous(); + (void)Asset.LoadSynchronous(); for (const FName& PinName : Asset.Get()->GetCustomOutputs()) { @@ -182,25 +182,20 @@ TArray UFlowNode_SubGraph::GetContextOutputs() const return ContextOutputPins; } -void UFlowNode_SubGraph::AutoGenerateDataPins( - TMap& InOutPinNameToBoundPropertyNameMap, - TArray& InOutInputDataPins, - TArray& InOutOutputDataPins) const +void UFlowNode_SubGraph::AutoGenerateDataPins(TMap& PinNameToBoundPropertyMap, TArray& InputDataPins, TArray& OutputDataPins) const { if (Asset.IsNull()) { return; } - (void) Asset.LoadSynchronous(); + (void)Asset.LoadSynchronous(); - for (auto& KV : Asset->Nodes) + for (TPair>& Node : Asset->Nodes) { - UFlowNode* FlowNode = KV.Value; - - if (IFlowNodeWithExternalDataPinSupplierInterface* ExternalPinSuppliedNode = Cast(FlowNode)) + if (const IFlowNodeWithExternalDataPinSupplierInterface* ExternalPinSuppliedNode = Cast(Node.Value)) { - // If subgraph's current flownode uses an external data supplier (that will be this subgraph node), + // If subgraph's current Flow Node uses an external data supplier (that will be this subgraph node), // We need to scrape the external input pins from the node and add them to our auto-generated pins list TArray ExternalInputPins; @@ -208,10 +203,10 @@ void UFlowNode_SubGraph::AutoGenerateDataPins( { for (const FFlowPin& FlowPin : ExternalInputPins) { - InOutPinNameToBoundPropertyNameMap.Add(FlowPin.PinName, FlowPin.PinName); + PinNameToBoundPropertyMap.Add(FlowPin.PinName, FlowPin.PinName); } - InOutInputDataPins.Append(ExternalInputPins); + InputDataPins.Append(ExternalInputPins); } } } @@ -270,4 +265,4 @@ void UFlowNode_SubGraph::SubscribeToAssetChanges() } #endif -#undef LOCTEXT_NAMESPACE \ No newline at end of file +#undef LOCTEXT_NAMESPACE diff --git a/Source/Flow/Public/Interfaces/FlowDataPinGeneratorNodeInterface.h b/Source/Flow/Public/Interfaces/FlowDataPinGeneratorNodeInterface.h index 87a9ad214..2ebe5f3a5 100644 --- a/Source/Flow/Public/Interfaces/FlowDataPinGeneratorNodeInterface.h +++ b/Source/Flow/Public/Interfaces/FlowDataPinGeneratorNodeInterface.h @@ -19,11 +19,7 @@ class FLOW_API IFlowDataPinGeneratorNodeInterface GENERATED_BODY() public: - #if WITH_EDITOR - virtual void AutoGenerateDataPins( - TMap& InOutPinNameToBoundPropertyNameMap, - TArray& InOutInputDataPins, - TArray& InOutOutputDataPins) const = 0; + virtual void AutoGenerateDataPins(TMap& PinNameToBoundPropertyMap, TArray& InputDataPins, TArray& OutputDataPins) const = 0; #endif }; diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h b/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h index 1ecaeeae9..ea1e84515 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h @@ -12,21 +12,17 @@ * FlowNode to define data pin property literals for use connecting to data pin inputs in a flow graph */ UCLASS(Blueprintable, meta = (DisplayName = "Define Properties")) -class FLOW_API UFlowNode_DefineProperties - : public UFlowNode - , public IFlowDataPinGeneratorNodeInterface +class FLOW_API UFlowNode_DefineProperties : public UFlowNode, public IFlowDataPinGeneratorNodeInterface { GENERATED_UCLASS_BODY() protected: - // Instance-defined properties. // These will auto-generate a matching pin that is bound to its property as its data source. UPROPERTY(EditAnywhere, Category = "Configuration", DisplayName = Properties) TArray OutputProperties; public: - #if WITH_EDITOR // IFlowContextPinSupplierInterface virtual bool SupportsContextPins() const override { return Super::SupportsContextPins() || !OutputProperties.IsEmpty(); } @@ -37,15 +33,11 @@ class FLOW_API UFlowNode_DefineProperties // -- // IFlowDataPinGeneratorNodeInterface - virtual void AutoGenerateDataPins( - TMap& InOutPinNameToBoundPropertyNameMap, - TArray& InOutInputDataPins, - TArray& InOutOutputDataPins) const override; + virtual void AutoGenerateDataPins(TMap& PinNameToBoundPropertyMap, TArray& InputDataPins, TArray& OutputDataPins) const override; // -- #endif protected: - virtual bool TryFindPropertyByRemappedPinName( const FName& RemappedPinName, const FProperty*& OutFoundProperty, diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h b/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h index f8c26ad3d..b116d0332 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h @@ -11,19 +11,16 @@ * Creates instance of provided Flow Asset and starts its execution */ UCLASS(NotBlueprintable, meta = (DisplayName = "Sub Graph")) -class FLOW_API UFlowNode_SubGraph - : public UFlowNode - , public IFlowDataPinGeneratorNodeInterface +class FLOW_API UFlowNode_SubGraph : public UFlowNode, public IFlowDataPinGeneratorNodeInterface { GENERATED_UCLASS_BODY() - friend class UFlowAsset; friend class FFlowNode_SubGraphDetails; friend class UFlowSubsystem; static FFlowPin StartPin; static FFlowPin FinishPin; - + private: UPROPERTY(EditAnywhere, Category = "Graph") TSoftObjectPtr Asset; @@ -34,13 +31,13 @@ class FLOW_API UFlowNode_SubGraph */ UPROPERTY(EditAnywhere, Category = "Graph") bool bCanInstanceIdenticalAsset; - + UPROPERTY(SaveGame) FString SavedAssetInstanceName; protected: virtual bool CanBeAssetInstanced() const; - + virtual void PreloadContent() override; virtual void FlushContent() override; @@ -55,6 +52,7 @@ class FLOW_API UFlowNode_SubGraph #if WITH_EDITORONLY_DATA + protected: // All the classes allowed to be used as assets on this subgraph node UPROPERTY() @@ -66,6 +64,7 @@ class FLOW_API UFlowNode_SubGraph #endif #if WITH_EDITOR + public: // IFlowContextPinSupplierInterface virtual bool SupportsContextPins() const override { return true; } @@ -77,7 +76,7 @@ class FLOW_API UFlowNode_SubGraph virtual FString GetNodeDescription() const override; virtual UObject* GetAssetToEdit() override; virtual EDataValidationResult ValidateNode() override; - + // UObject virtual void PostLoad() override; virtual void PreEditChange(FProperty* PropertyAboutToChange) override; @@ -89,10 +88,7 @@ class FLOW_API UFlowNode_SubGraph // -- // IFlowDataPinGeneratorNodeInterface - virtual void AutoGenerateDataPins( - TMap& InOutPinNameToBoundPropertyNameMap, - TArray& InOutInputDataPins, - TArray& InOutOutputDataPins) const override; + virtual void AutoGenerateDataPins(TMap& PinNameToBoundPropertyMap, TArray& InputDataPins, TArray& OutputDataPins) const override; // -- private: From b0cae24cfdc294ce1aa67fdd6390e307c64073bd Mon Sep 17 00:00:00 2001 From: MaksymKapelianovych <48297221+MaksymKapelianovych@users.noreply.github.com> Date: Fri, 7 Mar 2025 18:20:06 +0200 Subject: [PATCH 342/485] Fix issue https://github.com/MothCocoon/FlowGraph/issues/277 by reintroducing some of deleted checks (#279) --- .../Private/Graph/Nodes/FlowGraphNode.cpp | 16 ++++++++++++++++ .../Public/Graph/Nodes/FlowGraphNode.h | 4 ---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 2e40e0915..db323ad8f 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -1708,6 +1708,22 @@ bool UFlowGraphNode::TryUpdateNodePins() const return true; } + bool bIsLoad = false; + if (const UFlowGraph* FlowGraph = GetFlowGraph()) + { + bIsLoad = FlowGraph->IsLoadingGraph(); + } + + // Confirm that we should be refreshing context pins + const bool bIsAllowedToRefreshPins = !bIsLoad || NodeInstance->CanRefreshContextPinsOnLoad(); + const bool bShouldConsiderRefreshingContextPins = bIsAllowedToRefreshPins && (SupportsContextPins()); + const bool bShouldRefreshContextPins = bShouldConsiderRefreshingContextPins || bNeedsFullReconstruction; + + if (!bShouldRefreshContextPins) + { + return false; + } + // ------------ // Get all pins of the FlowNode itself const UFlowNode* FlowNodeCDO = FlowNodeInstance->GetClass()->GetDefaultObject(); diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h index e044caad3..6ecaca4d7 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h @@ -305,10 +305,6 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode UPROPERTY() bool bIsSubNode = false; - /** if set, this node has context pins from the last RefreshContextPins */ - UPROPERTY() - bool bHasContextPins = false; - /** error message for node */ UPROPERTY() FString ErrorMessage; From d7c34317e78f8d203eaf1488e3cb6e968c75b7eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sat, 8 Mar 2025 10:17:07 +0100 Subject: [PATCH 343/485] #272 prevent crash while accessing soft pointer to force-deleted Flow Asset --- .../Private/Nodes/Graph/FlowNode_SubGraph.cpp | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp index bb7f1f448..98d42b3e2 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp @@ -149,12 +149,14 @@ TArray UFlowNode_SubGraph::GetContextInputs() const if (!Asset.IsNull()) { (void)Asset.LoadSynchronous(); - - for (const FName& PinName : Asset.Get()->GetCustomInputs()) + if (Asset.IsValid()) { - if (!PinName.IsNone()) + for (const FName& PinName : Asset->GetCustomInputs()) { - ContextInputPins.AddUnique(FFlowPin(PinName)); + if (!PinName.IsNone()) + { + ContextInputPins.AddUnique(FFlowPin(PinName)); + } } } } @@ -169,12 +171,14 @@ TArray UFlowNode_SubGraph::GetContextOutputs() const if (!Asset.IsNull()) { (void)Asset.LoadSynchronous(); - - for (const FName& PinName : Asset.Get()->GetCustomOutputs()) + if (Asset.IsValid()) { - if (!PinName.IsNone()) + for (const FName& PinName : Asset->GetCustomOutputs()) { - ContextOutputPins.AddUnique(FFlowPin(PinName)); + if (!PinName.IsNone()) + { + ContextOutputPins.AddUnique(FFlowPin(PinName)); + } } } } @@ -190,6 +194,10 @@ void UFlowNode_SubGraph::AutoGenerateDataPins(TMap& PinNameToBound } (void)Asset.LoadSynchronous(); + if (!Asset.IsValid()) + { + return; + } for (TPair>& Node : Asset->Nodes) { From 1a524b9d751f38e49c735c52820f5823c3d1a918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sat, 8 Mar 2025 10:22:41 +0100 Subject: [PATCH 344/485] cosmetic update --- Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index db323ad8f..ffe2d2d23 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -128,7 +128,7 @@ void UFlowGraphNode::PostPlacedNewNode() SubscribeToExternalChanges(); - // NOTE - NodeInstance can be already spawned by paste operation, don't override it + // note: NodeInstance can be already spawned by paste operation, don't override it if (NodeInstanceClass.IsPending()) { NodeInstanceClass.LoadSynchronous(); From 08f27eeb559941c7ccd312b9a45f0c6606965253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl?= Date: Fri, 4 Apr 2025 10:36:54 +0300 Subject: [PATCH 345/485] Prevent crash in permanent error log (#287) * Use weak pointer instead of raw pointer in log lambda * Remove extra copy of weak pointer --- Source/Flow/Private/FlowComponent.cpp | 4 ++-- Source/Flow/Private/Nodes/FlowNodeBase.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Flow/Private/FlowComponent.cpp b/Source/Flow/Private/FlowComponent.cpp index a762291f0..9ff943a59 100644 --- a/Source/Flow/Private/FlowComponent.cpp +++ b/Source/Flow/Private/FlowComponent.cpp @@ -273,11 +273,11 @@ void UFlowComponent::LogError(FString Message, const EFlowOnScreenMessageType On { if (UViewportStatsSubsystem* StatsSubsystem = GetWorld()->GetSubsystem()) { - StatsSubsystem->AddDisplayDelegate([this, Message](FText& OutText, FLinearColor& OutColor) + StatsSubsystem->AddDisplayDelegate([WeakThis = TWeakObjectPtr(this), Message](FText& OutText, FLinearColor& OutColor) { OutText = FText::FromString(Message); OutColor = FLinearColor::Red; - return IsValid(this); + return WeakThis.IsValid(); }); } } diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp index 31635b92b..fedefb834 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBase.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -811,12 +811,12 @@ void UFlowNodeBase::LogError(FString Message, const EFlowOnScreenMessageType OnS { if (UViewportStatsSubsystem* StatsSubsystem = GetWorld()->GetSubsystem()) { - StatsSubsystem->AddDisplayDelegate([this, Message](FText& OutText, FLinearColor& OutColor) + StatsSubsystem->AddDisplayDelegate([WeakThis = TWeakObjectPtr(this), Message](FText& OutText, FLinearColor& OutColor) { OutText = FText::FromString(Message); OutColor = FLinearColor::Red; - return IsValid(this) && GetFlowNodeSelfOrOwner()->GetActivationState() != EFlowNodeState::NeverActivated; + return WeakThis.IsValid() && WeakThis->GetFlowNodeSelfOrOwner()->GetActivationState() != EFlowNodeState::NeverActivated; }); } } From 54475a041b1c452ae0d984f680931e7be187b579 Mon Sep 17 00:00:00 2001 From: Michael Cenger <49336079+MichaelCenger@users.noreply.github.com> Date: Fri, 4 Apr 2025 09:40:05 +0200 Subject: [PATCH 346/485] Fix issues with FlowComponent IdentityTags replication. (#285) Fixes: - Changes to identity tags while offline (NM_Standalone) did not replicate to Clients if going online at some later point. - Changes to identity tags on the server before BeginPlay was called on the FlowComponent would never replicate to clients - Multiple calls to UFlowComponent::AddIdentityTag(s) or UFlowComponent::RemoveIdentityTag(s) within a single net update did not get replicated properly. It only would replicate the last of each respective operation. --- Source/Flow/Private/FlowComponent.cpp | 127 ++++++++++++++------------ Source/Flow/Public/FlowComponent.h | 16 +--- 2 files changed, 73 insertions(+), 70 deletions(-) diff --git a/Source/Flow/Private/FlowComponent.cpp b/Source/Flow/Private/FlowComponent.cpp index 9ff943a59..92d1cd0d1 100644 --- a/Source/Flow/Private/FlowComponent.cpp +++ b/Source/Flow/Private/FlowComponent.cpp @@ -39,15 +39,13 @@ void UFlowComponent::GetLifetimeReplicatedProps(TArray& OutLi FDoRepLifetimeParams Params; Params.bIsPushBased = true; - DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, AddedIdentityTags, Params); - DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, RemovedIdentityTags, Params); + DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, IdentityTags, Params); DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, RecentlySentNotifyTags, Params); DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, NotifyTagsFromGraph, Params); DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, NotifyTagsFromAnotherComponent, Params); #else - DOREPLIFETIME(ThisClass, AddedIdentityTags); - DOREPLIFETIME(ThisClass, RemovedIdentityTags); + DOREPLIFETIME(ThisClass, IdentityTags); DOREPLIFETIME(ThisClass, RecentlySentNotifyTags); DOREPLIFETIME(ThisClass, NotifyTagsFromGraph); @@ -114,7 +112,12 @@ void UFlowComponent::AddIdentityTag(const FGameplayTag Tag, const EFlowNetMode N if (IsFlowNetMode(NetMode) && Tag.IsValid() && !IdentityTags.HasTagExact(Tag)) { IdentityTags.AddTag(Tag); - +#if WITH_PUSH_MODEL + if (GetNetMode() < NM_Client) + { + MARK_PROPERTY_DIRTY_FROM_NAME(UFlowComponent, IdentityTags, this); + } +#endif if (HasBegunPlay()) { OnIdentityTagsAdded.Broadcast(this, FGameplayTagContainer(Tag)); @@ -123,14 +126,6 @@ void UFlowComponent::AddIdentityTag(const FGameplayTag Tag, const EFlowNetMode N { FlowSubsystem->OnIdentityTagAdded(this, Tag); } - - if (IsNetMode(NM_DedicatedServer) || IsNetMode(NM_ListenServer)) - { - AddedIdentityTags = FGameplayTagContainer(Tag); -#if WITH_PUSH_MODEL - MARK_PROPERTY_DIRTY_FROM_NAME(UFlowComponent, AddedIdentityTags, this); -#endif - } } } } @@ -150,21 +145,22 @@ void UFlowComponent::AddIdentityTags(FGameplayTagContainer Tags, const EFlowNetM } } - if (ValidatedTags.Num() > 0 && HasBegunPlay()) + if (ValidatedTags.Num() > 0) { - OnIdentityTagsAdded.Broadcast(this, ValidatedTags); - - if (UFlowSubsystem* FlowSubsystem = GetFlowSubsystem()) +#if WITH_PUSH_MODEL + if (GetNetMode() < NM_Client) { - FlowSubsystem->OnIdentityTagsAdded(this, ValidatedTags); + MARK_PROPERTY_DIRTY_FROM_NAME(UFlowComponent, IdentityTags, this); } - - if (IsNetMode(NM_DedicatedServer) || IsNetMode(NM_ListenServer)) - { - AddedIdentityTags = ValidatedTags; -#if WITH_PUSH_MODEL - MARK_PROPERTY_DIRTY_FROM_NAME(UFlowComponent, AddedIdentityTags, this); #endif + if (HasBegunPlay()) + { + OnIdentityTagsAdded.Broadcast(this, ValidatedTags); + + if (UFlowSubsystem* FlowSubsystem = GetFlowSubsystem()) + { + FlowSubsystem->OnIdentityTagsAdded(this, ValidatedTags); + } } } } @@ -175,7 +171,12 @@ void UFlowComponent::RemoveIdentityTag(const FGameplayTag Tag, const EFlowNetMod if (IsFlowNetMode(NetMode) && Tag.IsValid() && IdentityTags.HasTagExact(Tag)) { IdentityTags.RemoveTag(Tag); - +#if WITH_PUSH_MODEL + if (GetNetMode() < NM_Client) + { + MARK_PROPERTY_DIRTY_FROM_NAME(UFlowComponent, IdentityTags, this); + } +#endif if (HasBegunPlay()) { OnIdentityTagsRemoved.Broadcast(this, FGameplayTagContainer(Tag)); @@ -184,14 +185,6 @@ void UFlowComponent::RemoveIdentityTag(const FGameplayTag Tag, const EFlowNetMod { FlowSubsystem->OnIdentityTagRemoved(this, Tag); } - - if (IsNetMode(NM_DedicatedServer) || IsNetMode(NM_ListenServer)) - { - RemovedIdentityTags = FGameplayTagContainer(Tag); -#if WITH_PUSH_MODEL - MARK_PROPERTY_DIRTY_FROM_NAME(UFlowComponent, RemovedIdentityTags, this); -#endif - } } } } @@ -211,45 +204,67 @@ void UFlowComponent::RemoveIdentityTags(FGameplayTagContainer Tags, const EFlowN } } - if (ValidatedTags.Num() > 0 && HasBegunPlay()) + if (ValidatedTags.Num() > 0) { - OnIdentityTagsRemoved.Broadcast(this, ValidatedTags); - - if (UFlowSubsystem* FlowSubsystem = GetWorld()->GetGameInstance()->GetSubsystem()) +#if WITH_PUSH_MODEL + if (GetNetMode() < NM_Client) { - FlowSubsystem->OnIdentityTagsRemoved(this, ValidatedTags); + MARK_PROPERTY_DIRTY_FROM_NAME(UFlowComponent, IdentityTags, this); } - - if (IsNetMode(NM_DedicatedServer) || IsNetMode(NM_ListenServer)) - { - RemovedIdentityTags = ValidatedTags; -#if WITH_PUSH_MODEL - MARK_PROPERTY_DIRTY_FROM_NAME(UFlowComponent, RemovedIdentityTags, this); #endif + if (HasBegunPlay()) + { + OnIdentityTagsRemoved.Broadcast(this, ValidatedTags); + + if (UFlowSubsystem* FlowSubsystem = GetWorld()->GetGameInstance()->GetSubsystem()) + { + FlowSubsystem->OnIdentityTagsRemoved(this, ValidatedTags); + } } } } } -void UFlowComponent::OnRep_AddedIdentityTags() +void UFlowComponent::OnRep_IdentityTags(const FGameplayTagContainer& PreviousTags) { - IdentityTags.AppendTags(AddedIdentityTags); - OnIdentityTagsAdded.Broadcast(this, AddedIdentityTags); + FGameplayTagContainer addedTags; - if (UFlowSubsystem* FlowSubsystem = GetFlowSubsystem()) + //Any tags that are now in the IdentityTags container but haven't been previously must have been added. + for (const FGameplayTag& tag : IdentityTags) { - FlowSubsystem->OnIdentityTagsAdded(this, AddedIdentityTags); + if (!PreviousTags.HasTagExact(tag)) + { + addedTags.AddTag(tag); + } } -} -void UFlowComponent::OnRep_RemovedIdentityTags() -{ - IdentityTags.RemoveTags(RemovedIdentityTags); - OnIdentityTagsRemoved.Broadcast(this, RemovedIdentityTags); + if (addedTags.Num() > 0) + { + OnIdentityTagsAdded.Broadcast(this, addedTags); - if (UFlowSubsystem* FlowSubsystem = GetWorld()->GetGameInstance()->GetSubsystem()) + if (UFlowSubsystem* FlowSubsystem = GetFlowSubsystem()) + { + FlowSubsystem->OnIdentityTagsAdded(this, addedTags); + } + } + + FGameplayTagContainer removedTags; + //Any tags that have been in the IdentityTags container previously but aren't in it anymore after the replication update must have been removed. + for (const FGameplayTag& tag : PreviousTags) { - FlowSubsystem->OnIdentityTagsRemoved(this, RemovedIdentityTags); + if (!IdentityTags.HasTagExact(tag)) + { + removedTags.AddTag(tag); + } + } + if (removedTags.Num() > 0) + { + OnIdentityTagsRemoved.Broadcast(this, removedTags); + + if (UFlowSubsystem* FlowSubsystem = GetFlowSubsystem()) + { + FlowSubsystem->OnIdentityTagsRemoved(this, removedTags); + } } } diff --git a/Source/Flow/Public/FlowComponent.h b/Source/Flow/Public/FlowComponent.h index cf2d00b13..d579e2568 100644 --- a/Source/Flow/Public/FlowComponent.h +++ b/Source/Flow/Public/FlowComponent.h @@ -53,18 +53,9 @@ class FLOW_API UFlowComponent : public UActorComponent, public IFlowOwnerInterfa ////////////////////////////////////////////////////////////////////////// // Identity Tags - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Flow") + UPROPERTY(EditAnywhere, BlueprintReadOnly, ReplicatedUsing = OnRep_IdentityTags, Category = "Flow") FGameplayTagContainer IdentityTags; -private: - // Used to replicate tags added during gameplay - UPROPERTY(ReplicatedUsing = OnRep_AddedIdentityTags) - FGameplayTagContainer AddedIdentityTags; - - // Used to replicate tags removed during gameplay - UPROPERTY(ReplicatedUsing = OnRep_RemovedIdentityTags) - FGameplayTagContainer RemovedIdentityTags; - public: virtual void BeginPlay() override; virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; @@ -88,10 +79,7 @@ class FLOW_API UFlowComponent : public UActorComponent, public IFlowOwnerInterfa private: UFUNCTION() - void OnRep_AddedIdentityTags(); - - UFUNCTION() - void OnRep_RemovedIdentityTags(); + void OnRep_IdentityTags(const FGameplayTagContainer& PreviousTags); public: UPROPERTY(BlueprintAssignable, Category = "Flow") From 71c727b36574e1f9688128470f534edceb089179 Mon Sep 17 00:00:00 2001 From: jnucc <120216868+jnucc@users.noreply.github.com> Date: Fri, 4 Apr 2025 03:50:28 -0400 Subject: [PATCH 347/485] =?UTF-8?q?ViewportStatsSubsystem=20delegates:=20w?= =?UTF-8?q?ork=20with=20TWeakObjectPtr<>=20instead=20=E2=80=A6=20(#286)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ViewportStatsSubsystem delegates: work with TWeakObjectPtr<> instead of checking for IsValid(this), which will always return nullptr once UFlowComponent/UFlowNodeBase is destroyed. * Update FlowNodeBase.cpp --------- Co-authored-by: Krzysiek Justyński --- Source/Flow/Private/FlowComponent.cpp | 15 ++++++++++----- Source/Flow/Private/Nodes/FlowNodeBase.cpp | 17 +++++++++++------ 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/Source/Flow/Private/FlowComponent.cpp b/Source/Flow/Private/FlowComponent.cpp index 92d1cd0d1..36605fe0f 100644 --- a/Source/Flow/Private/FlowComponent.cpp +++ b/Source/Flow/Private/FlowComponent.cpp @@ -284,15 +284,20 @@ void UFlowComponent::LogError(FString Message, const EFlowOnScreenMessageType On if (OnScreenMessageType == EFlowOnScreenMessageType::Permanent) { - if (GetWorld()) + if (UWorld* World = GetWorld()) { - if (UViewportStatsSubsystem* StatsSubsystem = GetWorld()->GetSubsystem()) + if (UViewportStatsSubsystem* StatsSubsystem = World->GetSubsystem()) { StatsSubsystem->AddDisplayDelegate([WeakThis = TWeakObjectPtr(this), Message](FText& OutText, FLinearColor& OutColor) { - OutText = FText::FromString(Message); - OutColor = FLinearColor::Red; - return WeakThis.IsValid(); + if (WeakThis.Valid()) + { + OutText = FText::FromString(Message); + OutColor = FLinearColor::Red; + return true; + } + + return false; }); } } diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp index fedefb834..bdf834412 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBase.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -807,16 +807,21 @@ void UFlowNodeBase::LogError(FString Message, const EFlowOnScreenMessageType OnS // OnScreen Message if (OnScreenMessageType == EFlowOnScreenMessageType::Permanent) { - if (GetWorld()) + if (UWorld* World = GetWorld()) { - if (UViewportStatsSubsystem* StatsSubsystem = GetWorld()->GetSubsystem()) + if (UViewportStatsSubsystem* StatsSubsystem = World->GetSubsystem()) { StatsSubsystem->AddDisplayDelegate([WeakThis = TWeakObjectPtr(this), Message](FText& OutText, FLinearColor& OutColor) { - OutText = FText::FromString(Message); - OutColor = FLinearColor::Red; - - return WeakThis.IsValid() && WeakThis->GetFlowNodeSelfOrOwner()->GetActivationState() != EFlowNodeState::NeverActivated; + const UFlowNodeBase* ThisPtr = WeakThis.Get(); + if (ThisPtr && ThisPtr->GetFlowNodeSelfOrOwner()->GetActivationState() != EFlowNodeState::NeverActivated) + { + OutText = FText::FromString(Message); + OutColor = FLinearColor::Red; + return true; + } + + return false; }); } } From f0f43a1d62ccd2f8ec3d77f3e798f99b6e4c7781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Fri, 4 Apr 2025 10:05:35 +0200 Subject: [PATCH 348/485] merge fix, style fixes --- Source/Flow/Private/FlowComponent.cpp | 38 +++++++++++----------- Source/Flow/Private/Nodes/FlowNodeBase.cpp | 4 +-- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Source/Flow/Private/FlowComponent.cpp b/Source/Flow/Private/FlowComponent.cpp index 36605fe0f..b6cb6c9ce 100644 --- a/Source/Flow/Private/FlowComponent.cpp +++ b/Source/Flow/Private/FlowComponent.cpp @@ -227,43 +227,43 @@ void UFlowComponent::RemoveIdentityTags(FGameplayTagContainer Tags, const EFlowN void UFlowComponent::OnRep_IdentityTags(const FGameplayTagContainer& PreviousTags) { - FGameplayTagContainer addedTags; - //Any tags that are now in the IdentityTags container but haven't been previously must have been added. - for (const FGameplayTag& tag : IdentityTags) + // Any tags that are now in the IdentityTags container but haven't been previously must have been added. + FGameplayTagContainer AddedTags; + for (const FGameplayTag& Tag : IdentityTags) { - if (!PreviousTags.HasTagExact(tag)) + if (!PreviousTags.HasTagExact(Tag)) { - addedTags.AddTag(tag); + AddedTags.AddTag(Tag); } } - if (addedTags.Num() > 0) + if (AddedTags.Num() > 0) { - OnIdentityTagsAdded.Broadcast(this, addedTags); + OnIdentityTagsAdded.Broadcast(this, AddedTags); if (UFlowSubsystem* FlowSubsystem = GetFlowSubsystem()) { - FlowSubsystem->OnIdentityTagsAdded(this, addedTags); + FlowSubsystem->OnIdentityTagsAdded(this, AddedTags); } } - - FGameplayTagContainer removedTags; - //Any tags that have been in the IdentityTags container previously but aren't in it anymore after the replication update must have been removed. - for (const FGameplayTag& tag : PreviousTags) + + // Any tags that have been in the IdentityTags container previously but aren't in it anymore after the replication update must have been removed. + FGameplayTagContainer RemovedTags; + for (const FGameplayTag& Tag : PreviousTags) { - if (!IdentityTags.HasTagExact(tag)) + if (!IdentityTags.HasTagExact(Tag)) { - removedTags.AddTag(tag); + RemovedTags.AddTag(Tag); } } - if (removedTags.Num() > 0) + if (RemovedTags.Num() > 0) { - OnIdentityTagsRemoved.Broadcast(this, removedTags); + OnIdentityTagsRemoved.Broadcast(this, RemovedTags); if (UFlowSubsystem* FlowSubsystem = GetFlowSubsystem()) { - FlowSubsystem->OnIdentityTagsRemoved(this, removedTags); + FlowSubsystem->OnIdentityTagsRemoved(this, RemovedTags); } } } @@ -288,9 +288,9 @@ void UFlowComponent::LogError(FString Message, const EFlowOnScreenMessageType On { if (UViewportStatsSubsystem* StatsSubsystem = World->GetSubsystem()) { - StatsSubsystem->AddDisplayDelegate([WeakThis = TWeakObjectPtr(this), Message](FText& OutText, FLinearColor& OutColor) + StatsSubsystem->AddDisplayDelegate([WeakThis = TWeakObjectPtr(this), Message](FText& OutText, FLinearColor& OutColor) { - if (WeakThis.Valid()) + if (WeakThis.Get()) { OutText = FText::FromString(Message); OutColor = FLinearColor::Red; diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp index bdf834412..5d2b15899 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBase.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -811,9 +811,9 @@ void UFlowNodeBase::LogError(FString Message, const EFlowOnScreenMessageType OnS { if (UViewportStatsSubsystem* StatsSubsystem = World->GetSubsystem()) { - StatsSubsystem->AddDisplayDelegate([WeakThis = TWeakObjectPtr(this), Message](FText& OutText, FLinearColor& OutColor) + StatsSubsystem->AddDisplayDelegate([WeakThis = TWeakObjectPtr(this), Message](FText& OutText, FLinearColor& OutColor) { - const UFlowNodeBase* ThisPtr = WeakThis.Get(); + const UFlowNodeBase* ThisPtr = WeakThis.Get(); if (ThisPtr && ThisPtr->GetFlowNodeSelfOrOwner()->GetActivationState() != EFlowNodeState::NeverActivated) { OutText = FText::FromString(Message); From cf85bc50f51152a0db45bedc290726355a45ee13 Mon Sep 17 00:00:00 2001 From: Ryan DowlingSoka Date: Fri, 4 Apr 2025 01:14:34 -0700 Subject: [PATCH 349/485] Fix copy-paste breaking reroute node directionality (#281) --- Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp | 7 +++++++ Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h | 1 + 2 files changed, 8 insertions(+) diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index ffe2d2d23..924bb9977 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -161,6 +161,13 @@ void UFlowGraphNode::PrepareForCopying() } } +void UFlowGraphNode::PostPasteNode() +{ + Super::PostPasteNode(); + //prep reconstruct the node, necessary for copy-paste to handle the reconstruct. + bNeedsFullReconstruction = true; +} + void UFlowGraphNode::PostCopyNode() { // Make sure this NodeInstance is owned by the FlowAsset it's being pasted into diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h index 6ecaca4d7..e2109f941 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h @@ -62,6 +62,7 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode // UEdGraphNode virtual void PostPlacedNewNode() override; virtual void PrepareForCopying() override; + virtual void PostPasteNode() override; // -- void PostCopyNode(); From 7af99fa2c2f55740b1b69d0904e5352875cf52c8 Mon Sep 17 00:00:00 2001 From: MaksymKapelianovych <48297221+MaksymKapelianovych@users.noreply.github.com> Date: Thu, 10 Apr 2025 20:56:05 +0300 Subject: [PATCH 350/485] Add menu and toolbar extensibility managers. This will allow to add new options to menu and toolbar for custom flow asset types without the need to extend/override FFlowAssetEditor. (#284) --- Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp | 10 ++++++++++ Source/FlowEditor/Private/FlowEditorModule.cpp | 6 ++++++ Source/FlowEditor/Public/Asset/FlowAssetEditor.h | 1 + Source/FlowEditor/Public/FlowEditorModule.h | 8 +++++++- 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index c825e33be..4e451e1b6 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -25,6 +25,7 @@ #include "Misc/UObjectToken.h" #include "Modules/ModuleManager.h" #include "PropertyEditorModule.h" +#include "FlowEditorModule.h" #include "ToolMenus.h" #include "Widgets/Docking/SDockTab.h" @@ -366,6 +367,8 @@ void FFlowAssetEditor::InitFlowAssetEditor(const EToolkitMode::Type Mode, const constexpr bool bCreateDefaultToolbar = true; InitAssetEditor(Mode, InitToolkitHost, TEXT("FlowEditorApp"), StandaloneDefaultLayout, bCreateDefaultStandaloneMenu, bCreateDefaultToolbar, ObjectToEdit, false); + InitalizeExtenders(); + RegenerateMenusAndToolbars(); } @@ -420,6 +423,13 @@ void FFlowAssetEditor::BindToolbarCommands() FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanGoToParentInstance)); } +void FFlowAssetEditor::InitalizeExtenders() +{ + FFlowEditorModule* FlowEditorModule = &FModuleManager::LoadModuleChecked("FlowEditor"); + AddMenuExtender(FlowEditorModule->GetMenuExtensibilityManager()->GetAllExtenders(GetToolkitCommands(), GetEditingObjects())); + AddToolbarExtender(FlowEditorModule->GetToolBarExtensibilityManager()->GetAllExtenders(GetToolkitCommands(), GetEditingObjects())); +} + void FFlowAssetEditor::RefreshAsset() { // attempt to refresh graph, fix common issues automatically diff --git a/Source/FlowEditor/Private/FlowEditorModule.cpp b/Source/FlowEditor/Private/FlowEditorModule.cpp index 4a7baa208..5f0893b1c 100644 --- a/Source/FlowEditor/Private/FlowEditorModule.cpp +++ b/Source/FlowEditor/Private/FlowEditorModule.cpp @@ -59,6 +59,9 @@ FAssetCategoryPath FFLowAssetCategoryPaths::Flow(LOCTEXT("Flow", "Flow")); void FFlowEditorModule::StartupModule() { + MenuExtensibilityManager = MakeShared(); + ToolBarExtensibilityManager = MakeShared(); + FFlowEditorStyle::Initialize(); TrySetFlowNodeDisplayStyleDefaults(); @@ -101,6 +104,9 @@ void FFlowEditorModule::StartupModule() void FFlowEditorModule::ShutdownModule() { + MenuExtensibilityManager.Reset(); + ToolBarExtensibilityManager.Reset(); + FFlowEditorStyle::Shutdown(); UnregisterDetailCustomizations(); diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index 881f6c26e..8c479b8e0 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -126,6 +126,7 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit protected: virtual void CreateToolbar(); virtual void BindToolbarCommands(); + virtual void InitalizeExtenders(); virtual void RefreshAsset(); virtual void RefreshDetails(); diff --git a/Source/FlowEditor/Public/FlowEditorModule.h b/Source/FlowEditor/Public/FlowEditorModule.h index 49b578249..71bc523bc 100644 --- a/Source/FlowEditor/Public/FlowEditorModule.h +++ b/Source/FlowEditor/Public/FlowEditorModule.h @@ -20,7 +20,7 @@ struct FLOWEDITOR_API FFLowAssetCategoryPaths : EAssetCategoryPaths static FAssetCategoryPath Flow; }; -class FLOWEDITOR_API FFlowEditorModule : public IModuleInterface +class FLOWEDITOR_API FFlowEditorModule : public IModuleInterface, public IHasMenuExtensibility, public IHasToolBarExtensibility { public: static EAssetTypeCategories::Type FlowAssetCategory; @@ -30,10 +30,16 @@ class FLOWEDITOR_API FFlowEditorModule : public IModuleInterface TSet CustomClassLayouts; TSet CustomStructLayouts; + TSharedPtr MenuExtensibilityManager; + TSharedPtr ToolBarExtensibilityManager; + public: virtual void StartupModule() override; virtual void ShutdownModule() override; + virtual TSharedPtr GetMenuExtensibilityManager() override { return MenuExtensibilityManager; } + virtual TSharedPtr GetToolBarExtensibilityManager() override { return ToolBarExtensibilityManager; } + private: void TrySetFlowNodeDisplayStyleDefaults() const; From 31a1c39a445e05f67aba2c0a1e0150433745e6ae Mon Sep 17 00:00:00 2001 From: jnucc <120216868+jnucc@users.noreply.github.com> Date: Thu, 10 Apr 2025 13:56:51 -0400 Subject: [PATCH 351/485] https://github.com/MothCocoon/FlowGraph/issues/276 - Ctrl+C & Ctrl+V Issue With Graph (#289) On pasted nodes, if there is no involved add-on, proceed with the pasting, making it work like in Flow 1.6. --- .../FlowEditor/Private/Graph/FlowGraphEditor.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index 4cf5e1449..ca631db32 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -23,6 +23,7 @@ #include "ScopedTransaction.h" #include "UnrealEdGlobals.h" #include "Widgets/Docking/SDockTab.h" +#include "Algo/AnyOf.h" #define LOCTEXT_NAMESPACE "FlowGraphEditor" @@ -670,7 +671,10 @@ void SFlowGraphEditor::PasteNodesHere(const FVector2D& Location) FlowGraph->LockUpdates(); const TArray PasteTargetNodes = DerivePasteTargetNodesFromSelectedNodes(); - checkf(PasteTargetNodes.Num() <= 1, TEXT("This should be enforced in CanPasteNodes()")); + if (Algo::AnyOf(PasteTargetNodes, [](UFlowGraphNode* Node) { return Node && !Node->SubNodes.IsEmpty(); })) + { + checkf(PasteTargetNodes.Num() <= 1, TEXT("This should be enforced in CanPasteNodes()")); + } UFlowGraphNode* PasteTargetNode = !PasteTargetNodes.IsEmpty() ? PasteTargetNodes.Top() : nullptr; @@ -839,9 +843,11 @@ bool SFlowGraphEditor::CanPasteNodes() const return false; } - // Disallow paste when multiple target nodes are selected. + // Disallow paste when multiple target nodes are selected, and if there are subnodes involved. const TArray PasteTargetNodes = DerivePasteTargetNodesFromSelectedNodes(); - if (PasteTargetNodes.Num() > 1) + const bool bHasSubNodes = Algo::AnyOf(PasteTargetNodes, [](UFlowGraphNode* Node) { return Node && !Node->SubNodes.IsEmpty(); }); + + if (bHasSubNodes && PasteTargetNodes.Num() > 1) { // NOTE (gtaylor) It's possible we could support multi-paste, but we'd need to rework PasteNodesHere() // to understand how to paste copies onto each target node. @@ -881,7 +887,7 @@ bool SFlowGraphEditor::CanPasteNodes() const }; // If pasting onto a selected node, confirm that the paste operation is legal - if (PasteTargetNodes.Num() >= 1) + if (bHasSubNodes && PasteTargetNodes.Num() >= 1) { checkf(PasteTargetNodes.Num() == 1, TEXT("This is enforced earlier in this function, just confirming the code stays that way here.")); From f04759dc1aa20c882ee9ac23eedbe6ad97cda06a Mon Sep 17 00:00:00 2001 From: jnucc <120216868+jnucc@users.noreply.github.com> Date: Thu, 10 Apr 2025 13:57:28 -0400 Subject: [PATCH 352/485] Flow Breakpoint fixes (#290) Add CanPlaceBreakpoints() functionality in UFlowGraphNode, return false on UFlowGraphNode_Reroute. In SFlowGraphEditor, in Breakpoint() functions, add conditions to check if CanPlaceBreakpoint() on the selected node is true. Also, since multiple breakpoints on nodes can be set/unset at the same time, make the checks in these functions too. Since they are in a for loop, don't return immediately at the first element, but check if it makes sense to return true on the first item that returns true. Fix breakpoints that could erroneously be added on reroute nodes through the context menu, or through F9, while not being visible at all. In UFlowDebuggerSubsystem::MarkAsHit() functions, check if the NodeBreakpoint is enabled. --- .../Debugger/FlowDebuggerSubsystem.cpp | 14 +- .../Private/Graph/FlowGraphEditor.cpp | 140 +++++++++++++++--- .../Private/Graph/Nodes/FlowGraphNode.cpp | 5 + .../Graph/Nodes/FlowGraphNode_Reroute.cpp | 5 + .../Public/Graph/Nodes/FlowGraphNode.h | 1 + .../Graph/Nodes/FlowGraphNode_Reroute.h | 2 + 6 files changed, 139 insertions(+), 28 deletions(-) diff --git a/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp b/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp index 0b977d536..b9543a4aa 100644 --- a/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp +++ b/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp @@ -248,8 +248,11 @@ void UFlowDebuggerSubsystem::MarkAsHit(const FGuid& NodeGuid) { if (FFlowBreakpoint* NodeBreakpoint = FindBreakpoint(NodeGuid)) { - NodeBreakpoint->MarkAsHit(true); - PauseSession(); + if (NodeBreakpoint->IsEnabled()) + { + NodeBreakpoint->MarkAsHit(true); + PauseSession(); + } } } @@ -257,8 +260,11 @@ void UFlowDebuggerSubsystem::MarkAsHit(const FGuid& NodeGuid, const FName& PinNa { if (FFlowBreakpoint* PinBreakpoint = FindBreakpoint(NodeGuid, PinName)) { - PinBreakpoint->MarkAsHit(true); - PauseSession(); + if (PinBreakpoint->IsEnabled()) + { + PinBreakpoint->MarkAsHit(true); + PauseSession(); + } } } diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index ca631db32..370d3616f 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -1119,7 +1119,10 @@ void SFlowGraphEditor::OnAddBreakpoint() const check(DebuggerSubsystem.IsValid()); for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - DebuggerSubsystem->AddBreakpoint(SelectedNode->NodeGuid); + if (SelectedNode->CanPlaceBreakpoints()) + { + DebuggerSubsystem->AddBreakpoint(SelectedNode->NodeGuid); + } } } @@ -1128,7 +1131,11 @@ void SFlowGraphEditor::OnAddPinBreakpoint() check(DebuggerSubsystem.IsValid()); if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - DebuggerSubsystem->AddBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName); + const UFlowGraphNode* OwningNode = Cast(Pin->GetOwningNode()); + if (!OwningNode || OwningNode->CanPlaceBreakpoints()) + { + DebuggerSubsystem->AddBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName); + } } } @@ -1137,7 +1144,13 @@ bool SFlowGraphEditor::CanAddBreakpoint() const check(DebuggerSubsystem.IsValid()); for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - return DebuggerSubsystem->FindBreakpoint(SelectedNode->NodeGuid) == nullptr; + if (SelectedNode->CanPlaceBreakpoints()) + { + if (DebuggerSubsystem->FindBreakpoint(SelectedNode->NodeGuid) == nullptr) + { + return true; + } + } } return false; @@ -1148,7 +1161,11 @@ bool SFlowGraphEditor::CanAddPinBreakpoint() check(DebuggerSubsystem.IsValid()); if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - return DebuggerSubsystem->FindBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName) == nullptr; + const UFlowGraphNode* OwningNode = Cast(Pin->GetOwningNode()); + if (!OwningNode || OwningNode->CanPlaceBreakpoints()) + { + return DebuggerSubsystem->FindBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName) == nullptr; + } } return false; @@ -1159,7 +1176,10 @@ void SFlowGraphEditor::OnRemoveBreakpoint() const check(DebuggerSubsystem.IsValid()); for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - DebuggerSubsystem->RemoveNodeBreakpoint(SelectedNode->NodeGuid); + if (SelectedNode->CanPlaceBreakpoints()) + { + DebuggerSubsystem->RemoveNodeBreakpoint(SelectedNode->NodeGuid); + } } } @@ -1168,7 +1188,11 @@ void SFlowGraphEditor::OnRemovePinBreakpoint() check(DebuggerSubsystem.IsValid()); if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - DebuggerSubsystem->RemovePinBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName); + const UFlowGraphNode* OwningNode = Cast(Pin->GetOwningNode()); + if (!OwningNode || OwningNode->CanPlaceBreakpoints()) + { + DebuggerSubsystem->RemovePinBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName); + } } } @@ -1177,7 +1201,13 @@ bool SFlowGraphEditor::CanRemoveBreakpoint() const check(DebuggerSubsystem.IsValid()); for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - return DebuggerSubsystem->FindBreakpoint(SelectedNode->NodeGuid) != nullptr; + if (SelectedNode->CanPlaceBreakpoints()) + { + if (DebuggerSubsystem->FindBreakpoint(SelectedNode->NodeGuid) != nullptr) + { + return true; + } + } } return false; @@ -1188,7 +1218,11 @@ bool SFlowGraphEditor::CanRemovePinBreakpoint() check(DebuggerSubsystem.IsValid()); if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - return DebuggerSubsystem->FindBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName) != nullptr; + const UFlowGraphNode* OwningNode = Cast(Pin->GetOwningNode()); + if (!OwningNode || OwningNode->CanPlaceBreakpoints()) + { + return DebuggerSubsystem->FindBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName) != nullptr; + } } return false; @@ -1199,7 +1233,10 @@ void SFlowGraphEditor::OnEnableBreakpoint() const check(DebuggerSubsystem.IsValid()); for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - DebuggerSubsystem->SetBreakpointEnabled(SelectedNode->NodeGuid, true); + if (SelectedNode->CanPlaceBreakpoints()) + { + DebuggerSubsystem->SetBreakpointEnabled(SelectedNode->NodeGuid, true); + } } } @@ -1208,7 +1245,11 @@ void SFlowGraphEditor::OnEnablePinBreakpoint() check(DebuggerSubsystem.IsValid()); if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - DebuggerSubsystem->SetBreakpointEnabled(Pin->GetOwningNode()->NodeGuid, Pin->PinName, true); + const UFlowGraphNode* OwningNode = Cast(Pin->GetOwningNode()); + if (!OwningNode || OwningNode->CanPlaceBreakpoints()) + { + DebuggerSubsystem->SetBreakpointEnabled(Pin->GetOwningNode()->NodeGuid, Pin->PinName, true); + } } } @@ -1216,8 +1257,14 @@ bool SFlowGraphEditor::CanEnableBreakpoint() const { for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - const FFlowBreakpoint* Breakpoint = DebuggerSubsystem->FindBreakpoint(SelectedNode->NodeGuid); - return Breakpoint && !Breakpoint->IsEnabled(); + if (SelectedNode->CanPlaceBreakpoints()) + { + const FFlowBreakpoint* Breakpoint = DebuggerSubsystem->FindBreakpoint(SelectedNode->NodeGuid); + if (Breakpoint && !Breakpoint->IsEnabled()) + { + return true; + } + } } return false; @@ -1227,8 +1274,12 @@ bool SFlowGraphEditor::CanEnablePinBreakpoint() { if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - const FFlowBreakpoint* Breakpoint = DebuggerSubsystem->FindBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName); - return Breakpoint && !Breakpoint->IsEnabled(); + const UFlowGraphNode* OwningNode = Cast(Pin->GetOwningNode()); + if (!OwningNode || OwningNode->CanPlaceBreakpoints()) + { + const FFlowBreakpoint* Breakpoint = DebuggerSubsystem->FindBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName); + return Breakpoint && !Breakpoint->IsEnabled(); + } } return false; @@ -1239,7 +1290,10 @@ void SFlowGraphEditor::OnDisableBreakpoint() const check(DebuggerSubsystem.IsValid()); for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - DebuggerSubsystem->SetBreakpointEnabled(SelectedNode->NodeGuid, false); + if (SelectedNode->CanPlaceBreakpoints()) + { + DebuggerSubsystem->SetBreakpointEnabled(SelectedNode->NodeGuid, false); + } } } @@ -1248,7 +1302,11 @@ void SFlowGraphEditor::OnDisablePinBreakpoint() check(DebuggerSubsystem.IsValid()); if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - DebuggerSubsystem->SetBreakpointEnabled(Pin->GetOwningNode()->NodeGuid, Pin->PinName, false); + const UFlowGraphNode* OwningNode = Cast(Pin->GetOwningNode()); + if (!OwningNode || OwningNode->CanPlaceBreakpoints()) + { + DebuggerSubsystem->SetBreakpointEnabled(Pin->GetOwningNode()->NodeGuid, Pin->PinName, false); + } } } @@ -1257,8 +1315,14 @@ bool SFlowGraphEditor::CanDisableBreakpoint() const check(DebuggerSubsystem.IsValid()); for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - const FFlowBreakpoint* Breakpoint = DebuggerSubsystem->FindBreakpoint(SelectedNode->NodeGuid); - return Breakpoint && Breakpoint->IsEnabled(); + if (SelectedNode->CanPlaceBreakpoints()) + { + const FFlowBreakpoint* Breakpoint = DebuggerSubsystem->FindBreakpoint(SelectedNode->NodeGuid); + if (Breakpoint && Breakpoint->IsEnabled()) + { + return true; + } + } } return false; @@ -1269,8 +1333,12 @@ bool SFlowGraphEditor::CanDisablePinBreakpoint() check(DebuggerSubsystem.IsValid()); if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - const FFlowBreakpoint* Breakpoint = DebuggerSubsystem->FindBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName); - return Breakpoint && Breakpoint->IsEnabled(); + const UFlowGraphNode* OwningNode = Cast(Pin->GetOwningNode()); + if (!OwningNode || OwningNode->CanPlaceBreakpoints()) + { + const FFlowBreakpoint* Breakpoint = DebuggerSubsystem->FindBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName); + return Breakpoint && Breakpoint->IsEnabled(); + } } return false; @@ -1281,7 +1349,10 @@ void SFlowGraphEditor::OnToggleBreakpoint() const check(DebuggerSubsystem.IsValid()); for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { - DebuggerSubsystem->ToggleBreakpoint(SelectedNode->NodeGuid); + if (SelectedNode->CanPlaceBreakpoints()) + { + DebuggerSubsystem->ToggleBreakpoint(SelectedNode->NodeGuid); + } } } @@ -1290,18 +1361,39 @@ void SFlowGraphEditor::OnTogglePinBreakpoint() check(DebuggerSubsystem.IsValid()); if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - DebuggerSubsystem->ToggleBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName); + const UFlowGraphNode* OwningNode = Cast(Pin->GetOwningNode()); + if (!OwningNode || OwningNode->CanPlaceBreakpoints()) + { + DebuggerSubsystem->ToggleBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName); + } } } bool SFlowGraphEditor::CanToggleBreakpoint() const { - return GetSelectedFlowNodes().Num() > 0; + for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) + { + if (SelectedNode->CanPlaceBreakpoints()) + { + return true; + } + } + + return false; } bool SFlowGraphEditor::CanTogglePinBreakpoint() { - return GetGraphPinForMenu() != nullptr; + if (const UEdGraphPin* Pin = GetGraphPinForMenu()) + { + const UFlowGraphNode* OwningNode = Cast(Pin->GetOwningNode()); + if (!OwningNode || OwningNode->CanPlaceBreakpoints()) + { + return true; + } + } + + return false; } void SFlowGraphEditor::SetSignalMode(const EFlowSignalMode Mode) const diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 924bb9977..bc6c3b39d 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -217,6 +217,11 @@ void UFlowGraphNode::OnGraphRefresh() ReconstructNode(); } +bool UFlowGraphNode::CanPlaceBreakpoints() const +{ + return true; +} + bool UFlowGraphNode::CanCreateUnderSpecifiedSchema(const UEdGraphSchema* Schema) const { return Schema->IsA(UFlowGraphSchema::StaticClass()); diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Reroute.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Reroute.cpp index 90fca7064..63422adcd 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Reroute.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Reroute.cpp @@ -24,3 +24,8 @@ bool UFlowGraphNode_Reroute::ShouldDrawNodeAsControlPointOnly(int32& OutInputPin OutOutputPinIndex = 1; return true; } + +bool UFlowGraphNode_Reroute::CanPlaceBreakpoints() const +{ + return false; +} diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h index e2109f941..cabe0854d 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h @@ -73,6 +73,7 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode public: virtual void OnGraphRefresh(); + virtual bool CanPlaceBreakpoints() const; ////////////////////////////////////////////////////////////////////////// // Graph node diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_Reroute.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_Reroute.h index 41364c825..a3c6dbd88 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_Reroute.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_Reroute.h @@ -14,4 +14,6 @@ class FLOWEDITOR_API UFlowGraphNode_Reroute : public UFlowGraphNode virtual TSharedPtr CreateVisualWidget() override; virtual bool ShouldDrawNodeAsControlPointOnly(int32& OutInputPinIndex, int32& OutOutputPinIndex) const override; // -- + + virtual bool CanPlaceBreakpoints() const override; }; From e6d5b9e39634df0fa829232e915ea1c82db40c5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Thu, 15 May 2025 19:36:32 +0200 Subject: [PATCH 353/485] UE 5.6 support --- Source/FlowEditor/FlowEditor.Build.cs | 1 + .../Private/Graph/FlowGraphSchema.cpp | 19 ++++++++++++++++++- .../FlowEditor/Public/Graph/FlowGraphSchema.h | 7 +++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/Source/FlowEditor/FlowEditor.Build.cs b/Source/FlowEditor/FlowEditor.Build.cs index ac2ab93bb..43e0cb3d1 100644 --- a/Source/FlowEditor/FlowEditor.Build.cs +++ b/Source/FlowEditor/FlowEditor.Build.cs @@ -57,6 +57,7 @@ public FlowEditor(ReadOnlyTargetRules target) : base(target) "PropertyPath", "RenderCore", "Sequencer", + "SequencerCore", "Slate", "SlateCore", "SourceControl", diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index 38e268875..21004681a 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -26,10 +26,15 @@ #include "EdGraphSchema_K2.h" #include "Editor.h" #include "Engine/MemberReference.h" -#include "Kismet/BlueprintTypeConversions.h" #include "Kismet2/KismetEditorUtilities.h" #include "ScopedTransaction.h" +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 6 +#include "Kismet/BlueprintTypeConversions.h" +#else +#include "Runtime/Engine/Internal/Kismet/BlueprintTypeConversions.h" +#endif + #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraphSchema) #define LOCTEXT_NAMESPACE "FlowGraphSchema" @@ -811,6 +816,8 @@ TSharedPtr UFlowGraphSchema::GetCreateCommentAction() cons return TSharedPtr(static_cast(new FFlowGraphSchemaAction_NewComment)); } + +PRAGMA_DISABLE_DEPRECATION_WARNINGS void UFlowGraphSchema::OnPinConnectionDoubleCicked(UEdGraphPin* PinA, UEdGraphPin* PinB, const FVector2D& GraphPosition) const { if (!FFlowPin::IsExecPinCategory(PinA->PinType.PinCategory) || !FFlowPin::IsExecPinCategory(PinB->PinType.PinCategory)) @@ -832,6 +839,16 @@ void UFlowGraphSchema::OnPinConnectionDoubleCicked(UEdGraphPin* PinA, UEdGraphPi PinA->MakeLinkTo((PinA->Direction == EGPD_Output) ? NewReroute->InputPins[0] : NewReroute->OutputPins[0]); PinB->MakeLinkTo((PinB->Direction == EGPD_Output) ? NewReroute->InputPins[0] : NewReroute->OutputPins[0]); } +PRAGMA_ENABLE_DEPRECATION_WARNINGS + +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 6 +void UFlowGraphSchema::OnPinConnectionDoubleCicked(UEdGraphPin* PinA, UEdGraphPin* PinB, const FVector2f& GraphPosition) const +{ + PRAGMA_DISABLE_DEPRECATION_WARNINGS + return OnPinConnectionDoubleCicked(PinA, PinB, FVector2D(GraphPosition)); + PRAGMA_ENABLE_DEPRECATION_WARNINGS +} +#endif bool UFlowGraphSchema::IsCacheVisualizationOutOfDate(int32 InVisualizationCacheID) const { diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h index ed907685a..9aa8d2db4 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h @@ -53,7 +53,14 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema virtual void BreakPinLinks(UEdGraphPin& TargetPin, bool bSendsNodeNotification) const override; virtual int32 GetNodeSelectionCount(const UEdGraph* Graph) const override; virtual TSharedPtr GetCreateCommentAction() const override; + + PRAGMA_DISABLE_DEPRECATION_WARNINGS virtual void OnPinConnectionDoubleCicked(UEdGraphPin* PinA, UEdGraphPin* PinB, const FVector2D& GraphPosition) const override; + PRAGMA_ENABLE_DEPRECATION_WARNINGS +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 6 + virtual void OnPinConnectionDoubleCicked(UEdGraphPin* PinA, UEdGraphPin* PinB, const FVector2f& GraphPosition) const override; +#endif + virtual bool IsCacheVisualizationOutOfDate(int32 InVisualizationCacheID) const override; virtual int32 GetCurrentVisualizationCacheID() const override; virtual void ForceVisualizationCacheClear() const override; From daa3fefbc04abd0a29401eac13e38ce136b3f69b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Thu, 15 May 2025 20:13:34 +0200 Subject: [PATCH 354/485] Addressed UE 5.6 deprecations --- .../Private/Asset/FlowObjectDiff.cpp | 8 ++++ .../FlowGraphConnectionDrawingPolicy.cpp | 42 ++++++++++++------- .../Private/Graph/FlowGraphEditor.cpp | 17 ++++++++ .../Graph/FlowGraphConnectionDrawingPolicy.h | 10 +++-- .../FlowEditor/Public/Graph/FlowGraphEditor.h | 5 +++ 5 files changed, 64 insertions(+), 18 deletions(-) diff --git a/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp b/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp index 54b54b700..32c270716 100644 --- a/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp +++ b/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp @@ -51,11 +51,19 @@ void FFlowObjectDiff::InitializeDetailsDiffFromNode(UEdGraphNode* Node, const UO if (NodeDiffType == ENodeDiffType::Old && !OldDetailsView.IsValid()) { +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 6 OldDetailsView = MakeShared(Object, FOnDisplayedPropertiesChanged()); +#else + OldDetailsView = MakeShared(Object); +#endif } else if (NodeDiffType == ENodeDiffType::New && !NewDetailsView.IsValid()) { +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 6 NewDetailsView = MakeShared(Object, FOnDisplayedPropertiesChanged()); +#else + NewDetailsView = MakeShared(Object); +#endif } } diff --git a/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp b/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp index 963cee22e..895375833 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp @@ -112,7 +112,11 @@ void FFlowGraphConnectionDrawingPolicy::BuildPaths() } } +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 6 void FFlowGraphConnectionDrawingPolicy::DrawConnection(int32 LayerId, const FVector2D& Start, const FVector2D& End, const FConnectionParams& Params) +#else +void FFlowGraphConnectionDrawingPolicy::DrawConnection(int32 LayerId, const FVector2f& Start, const FVector2f& End, const FConnectionParams& Params) +#endif { switch (UFlowGraphSettings::Get()->ConnectionDrawType) { @@ -120,7 +124,11 @@ void FFlowGraphConnectionDrawingPolicy::DrawConnection(int32 LayerId, const FVec FConnectionDrawingPolicy::DrawConnection(LayerId, Start, End, Params); break; case EFlowConnectionDrawType::Circuit: +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 6 + DrawCircuitSpline(LayerId, FVector2f(Start), FVector2f(End), Params); +#else DrawCircuitSpline(LayerId, Start, End, Params); +#endif break; default: ; } @@ -219,14 +227,14 @@ void FFlowGraphConnectionDrawingPolicy::Draw(TMap, FArranged FConnectionDrawingPolicy::Draw(InPinGeometries, ArrangedNodes); } -void FFlowGraphConnectionDrawingPolicy::DrawCircuitSpline(const int32& LayerId, const FVector2D& Start, const FVector2D& End, const FConnectionParams& Params) const +void FFlowGraphConnectionDrawingPolicy::DrawCircuitSpline(const int32& LayerId, const FVector2f& Start, const FVector2f& End, const FConnectionParams& Params) const { - const FVector2D StartingPoint = FVector2D(Start.X + UFlowGraphSettings::Get()->CircuitConnectionSpacing.X, Start.Y); - const FVector2D EndPoint = FVector2D(End.X - UFlowGraphSettings::Get()->CircuitConnectionSpacing.Y, End.Y); - const FVector2D ControlPoint = GetControlPoint(StartingPoint, EndPoint); + const FVector2f StartingPoint = FVector2f(Start.X + UFlowGraphSettings::Get()->CircuitConnectionSpacing.X, Start.Y); + const FVector2f EndPoint = FVector2f(End.X - UFlowGraphSettings::Get()->CircuitConnectionSpacing.Y, End.Y); + const FVector2f ControlPoint = GetControlPoint(StartingPoint, EndPoint); - const FVector2D StartDirection = (Params.StartDirection == EGPD_Output) ? FVector2D(1.0f, 0.0f) : FVector2D(-1.0f, 0.0f); - const FVector2D EndDirection = (Params.EndDirection == EGPD_Input) ? FVector2D(1.0f, 0.0f) : FVector2D(-1.0f, 0.0f); + const FVector2f StartDirection = (Params.StartDirection == EGPD_Output) ? FVector2f(1.0f, 0.0f) : FVector2f(-1.0f, 0.0f); + const FVector2f EndDirection = (Params.EndDirection == EGPD_Input) ? FVector2f(1.0f, 0.0f) : FVector2f(-1.0f, 0.0f); DrawCircuitConnection(LayerId, Start, StartDirection, StartingPoint, EndDirection, Params); DrawCircuitConnection(LayerId, StartingPoint, StartDirection, ControlPoint, EndDirection, Params); @@ -234,7 +242,7 @@ void FFlowGraphConnectionDrawingPolicy::DrawCircuitSpline(const int32& LayerId, DrawCircuitConnection(LayerId, EndPoint, StartDirection, End, EndDirection, Params); } -void FFlowGraphConnectionDrawingPolicy::DrawCircuitConnection(const int32& LayerId, const FVector2D& Start, const FVector2D& StartDirection, const FVector2D& End, const FVector2D& EndDirection, const FConnectionParams& Params) const +void FFlowGraphConnectionDrawingPolicy::DrawCircuitConnection(const int32& LayerId, const FVector2f& Start, const FVector2f& StartDirection, const FVector2f& End, const FVector2f& EndDirection, const FConnectionParams& Params) const { FSlateDrawElement::MakeDrawSpaceSpline(DrawElementsList, LayerId, Start, StartDirection, End, EndDirection, Params.WireThickness, ESlateDrawEffect::None, Params.WireColor); @@ -242,14 +250,18 @@ void FFlowGraphConnectionDrawingPolicy::DrawCircuitConnection(const int32& Layer { // This table maps distance along curve to alpha FInterpCurve SplineReparamTable; +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 6 + const float SplineLength = MakeSplineReparamTable(FVector2D(Start), FVector2D(StartDirection), FVector2D(End), FVector2D(EndDirection), SplineReparamTable); +#else const float SplineLength = MakeSplineReparamTable(Start, StartDirection, End, EndDirection, SplineReparamTable); +#endif // Draw bubbles on the spline if (Params.bDrawBubbles) { const float BubbleSpacing = 64.f * ZoomFactor; const float BubbleSpeed = 192.f * ZoomFactor; - const FVector2D BubbleSize = BubbleImage->ImageSize * ZoomFactor * 0.2f * Params.WireThickness; + const FVector2f BubbleSize = BubbleImage->ImageSize * ZoomFactor * 0.2f * Params.WireThickness; const float Time = (FPlatformTime::Seconds() - GStartTime); const float BubbleOffset = FMath::Fmod(Time * BubbleSpeed, BubbleSpacing); @@ -260,7 +272,7 @@ void FFlowGraphConnectionDrawingPolicy::DrawCircuitConnection(const int32& Layer if (Distance < SplineLength) { const float Alpha = SplineReparamTable.Eval(Distance, 0.f); - FVector2D BubblePos = FMath::CubicInterp(Start, StartDirection, End, EndDirection, Alpha); + FVector2f BubblePos = FMath::CubicInterp(Start, StartDirection, End, EndDirection, Alpha); BubblePos -= (BubbleSize * 0.5f); FSlateDrawElement::MakeBox(DrawElementsList, LayerId, FPaintGeometry(BubblePos, BubbleSize, ZoomFactor), BubbleImage, ESlateDrawEffect::None, Params.WireColor); @@ -270,9 +282,9 @@ void FFlowGraphConnectionDrawingPolicy::DrawCircuitConnection(const int32& Layer } } -FVector2D FFlowGraphConnectionDrawingPolicy::GetControlPoint(const FVector2D& Source, const FVector2D& Target) +FVector2f FFlowGraphConnectionDrawingPolicy::GetControlPoint(const FVector2f& Source, const FVector2f& Target) { - const FVector2D Delta = Target - Source; + const FVector2f Delta = Target - Source; const float Tangent = FMath::Tan(UFlowGraphSettings::Get()->CircuitConnectionAngle * (PI / 180.f)); const float DeltaX = FMath::Abs(Delta.X); @@ -281,7 +293,7 @@ FVector2D FFlowGraphConnectionDrawingPolicy::GetControlPoint(const FVector2D& So const float SlopeWidth = DeltaY / Tangent; if (DeltaX > SlopeWidth) { - return Delta.X > 0.f ? FVector2D(Target.X - SlopeWidth, Source.Y) : FVector2D(Source.X - SlopeWidth, Target.Y); + return Delta.X > 0.f ? FVector2f(Target.X - SlopeWidth, Source.Y) : FVector2f(Source.X - SlopeWidth, Target.Y); } const float SlopeHeight = DeltaX * Tangent; @@ -289,16 +301,16 @@ FVector2D FFlowGraphConnectionDrawingPolicy::GetControlPoint(const FVector2D& So { if (Delta.Y > 0.f) { - return Delta.X < 0.f ? FVector2D(Source.X, Target.Y - SlopeHeight) : FVector2D(Target.X, Source.Y + SlopeHeight); + return Delta.X < 0.f ? FVector2f(Source.X, Target.Y - SlopeHeight) : FVector2f(Target.X, Source.Y + SlopeHeight); } if (Delta.X < 0.f) { - return FVector2D(Source.X, Target.Y + SlopeHeight); + return FVector2f(Source.X, Target.Y + SlopeHeight); } } - return FVector2D(Target.X, Source.Y - SlopeHeight); + return FVector2f(Target.X, Source.Y - SlopeHeight); } bool FFlowGraphConnectionDrawingPolicy::ShouldChangeTangentForReroute(UFlowGraphNode_Reroute* Reroute) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index 370d3616f..19066bd05 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -46,7 +46,11 @@ void SFlowGraphEditor::Construct(const FArguments& InArgs, const TSharedPtr(FlowAsset->GetGraph())); +#else + Arguments._GraphEvents.OnSpawnNodeByShortcutAtLocation = FOnSpawnNodeByShortcutAtLocation::CreateStatic(&SFlowGraphEditor::OnSpawnGraphNodeByShortcut, static_cast(FlowAsset->GetGraph())); +#endif SGraphEditor::Construct(Arguments); } @@ -286,7 +290,11 @@ void SFlowGraphEditor::RedoGraphAction() GEditor->RedoTransaction(); } +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 6 FReply SFlowGraphEditor::OnSpawnGraphNodeByShortcut(FInputChord InChord, const FVector2D& InPosition, UEdGraph* InGraph) +#else +FReply SFlowGraphEditor::OnSpawnGraphNodeByShortcut(FInputChord InChord, const FVector2f& InPosition, UEdGraph* InGraph) +#endif { UEdGraph* Graph = InGraph; @@ -296,6 +304,7 @@ FReply SFlowGraphEditor::OnSpawnGraphNodeByShortcut(FInputChord InChord, const F if (Action.IsValid()) { TArray DummyPins; + Action->PerformAction(Graph, DummyPins, InPosition); return FReply::Handled(); } @@ -307,7 +316,11 @@ FReply SFlowGraphEditor::OnSpawnGraphNodeByShortcut(FInputChord InChord, const F void SFlowGraphEditor::OnCreateComment() const { FFlowGraphSchemaAction_NewComment CommentAction; +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 6 CommentAction.PerformAction(FlowAsset->GetGraph(), nullptr, GetPasteLocation()); +#else + CommentAction.PerformAction(FlowAsset->GetGraph(), nullptr, GetPasteLocation2f()); +#endif } bool SFlowGraphEditor::IsTabFocused() const @@ -657,7 +670,11 @@ bool SFlowGraphEditor::CanCopyNodes() const void SFlowGraphEditor::PasteNodes() { +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 6 PasteNodesHere(GetPasteLocation()); +#else + PasteNodesHere(GetPasteLocation2f()); +#endif } void SFlowGraphEditor::PasteNodesHere(const FVector2D& Location) diff --git a/Source/FlowEditor/Public/Graph/FlowGraphConnectionDrawingPolicy.h b/Source/FlowEditor/Public/Graph/FlowGraphConnectionDrawingPolicy.h index 410a78ae7..d38f55d85 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphConnectionDrawingPolicy.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphConnectionDrawingPolicy.h @@ -54,15 +54,19 @@ class FLOWEDITOR_API FFlowGraphConnectionDrawingPolicy : public FConnectionDrawi void BuildPaths(); // FConnectionDrawingPolicy interface +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 6 virtual void DrawConnection(int32 LayerId, const FVector2D& Start, const FVector2D& End, const FConnectionParams& Params) override; +#else + virtual void DrawConnection(int32 LayerId, const FVector2f& Start, const FVector2f& End, const FConnectionParams& Params); +#endif virtual void DetermineWiringStyle(UEdGraphPin* OutputPin, UEdGraphPin* InputPin, FConnectionParams& Params) override; virtual void Draw(TMap, FArrangedWidget>& PinGeometries, FArrangedChildren& ArrangedNodes) override; // End of FConnectionDrawingPolicy interface protected: - void DrawCircuitSpline(const int32& LayerId, const FVector2D& Start, const FVector2D& End, const FConnectionParams& Params) const; - void DrawCircuitConnection(const int32& LayerId, const FVector2D& Start, const FVector2D& StartDirection, const FVector2D& End, const FVector2D& EndDirection, const FConnectionParams& Params) const; - static FVector2D GetControlPoint(const FVector2D& Source, const FVector2D& Target); + void DrawCircuitSpline(const int32& LayerId, const FVector2f& Start, const FVector2f& End, const FConnectionParams& Params) const; + void DrawCircuitConnection(const int32& LayerId, const FVector2f& Start, const FVector2f& StartDirection, const FVector2f& End, const FVector2f& EndDirection, const FConnectionParams& Params) const; + static FVector2f GetControlPoint(const FVector2f& Source, const FVector2f& Target); bool ShouldChangeTangentForReroute(class UFlowGraphNode_Reroute* Reroute); bool FindPinCenter(const UEdGraphPin* Pin, FVector2D& OutCenter) const; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h index ac575ea86..c910d60d4 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h @@ -46,7 +46,12 @@ class FLOWEDITOR_API SFlowGraphEditor : public SGraphEditor static void UndoGraphAction(); static void RedoGraphAction(); +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 6 static FReply OnSpawnGraphNodeByShortcut(FInputChord InChord, const FVector2D& InPosition, UEdGraph* InGraph); +#else + static FReply OnSpawnGraphNodeByShortcut(FInputChord InChord, const FVector2f& InPosition, UEdGraph* InGraph); +#endif + void OnCreateComment() const; public: From 1b3b16e46da95124161668aa8ae1f1c46d869b5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sat, 24 May 2025 11:57:47 +0200 Subject: [PATCH 355/485] Addressed UE 5.6 deprecation --- .../Widgets/SGraphEditorActionMenuFlow.cpp | 22 +++++++------- .../Widgets/SGraphEditorActionMenuFlow.h | 29 ++++++++++--------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/Widgets/SGraphEditorActionMenuFlow.cpp b/Source/FlowEditor/Private/Graph/Widgets/SGraphEditorActionMenuFlow.cpp index 328c9beb1..d429228c6 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SGraphEditorActionMenuFlow.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SGraphEditorActionMenuFlow.cpp @@ -1,8 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "Graph/Widgets/SGraphEditorActionMenuFlow.h" - -#include "Graph/Nodes/FlowGraphNode.h" #include "Graph/FlowGraphSchema.h" #include "EdGraph/EdGraph.h" @@ -22,7 +20,7 @@ SGraphEditorActionMenuFlow::~SGraphEditorActionMenuFlow() OnClosedCallback.ExecuteIfBound(); } -void SGraphEditorActionMenuFlow::Construct( const FArguments& InArgs ) +void SGraphEditorActionMenuFlow::Construct(const FArguments& InArgs) { this->GraphObj = InArgs._GraphObj; this->GraphNode = InArgs._GraphNode; @@ -33,9 +31,9 @@ void SGraphEditorActionMenuFlow::Construct( const FArguments& InArgs ) this->SubNodeFlags = InArgs._SubNodeFlags; // Build the widget layout - SBorder::Construct( SBorder::FArguments() - .BorderImage( FAppStyle::GetBrush("Menu.Background") ) - .Padding(5.f) + SBorder::Construct(SBorder::FArguments() + .BorderImage(FAppStyle::GetBrush("Menu.Background")) + .Padding(5.f) [ // Achieving fixed width by nesting items within a fixed width box. SNew(SBox) @@ -80,21 +78,25 @@ TSharedRef SGraphEditorActionMenuFlow::GetFilterTextBox() return GraphActionMenu->GetFilterTextBox(); } -void SGraphEditorActionMenuFlow::OnActionSelected( const TArray< TSharedPtr >& SelectedAction, ESelectInfo::Type InSelectionType ) +void SGraphEditorActionMenuFlow::OnActionSelected(const TArray>& SelectedAction, ESelectInfo::Type InSelectionType) { - if (InSelectionType == ESelectInfo::OnMouseClick || InSelectionType == ESelectInfo::OnKeyPress || SelectedAction.Num() == 0) + if (InSelectionType == ESelectInfo::OnMouseClick || InSelectionType == ESelectInfo::OnKeyPress || SelectedAction.Num() == 0) { bool bDoDismissMenus = false; if (GraphObj) { - for ( int32 ActionIndex = 0; ActionIndex < SelectedAction.Num(); ActionIndex++ ) + for (int32 ActionIndex = 0; ActionIndex < SelectedAction.Num(); ActionIndex++) { TSharedPtr CurrentAction = SelectedAction[ActionIndex]; - if ( CurrentAction.IsValid() ) + if (CurrentAction.IsValid()) { +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 6 + CurrentAction->PerformAction(GraphObj, DraggedFromPins, FVector2D(NewNodePosition)); +#else CurrentAction->PerformAction(GraphObj, DraggedFromPins, NewNodePosition); +#endif bDoDismissMenus = true; } } diff --git a/Source/FlowEditor/Public/Graph/Widgets/SGraphEditorActionMenuFlow.h b/Source/FlowEditor/Public/Graph/Widgets/SGraphEditorActionMenuFlow.h index 1d2336f8b..7355ab842 100644 --- a/Source/FlowEditor/Public/Graph/Widgets/SGraphEditorActionMenuFlow.h +++ b/Source/FlowEditor/Public/Graph/Widgets/SGraphEditorActionMenuFlow.h @@ -29,23 +29,24 @@ class FLOWEDITOR_API SGraphEditorActionMenuFlow : public SBorder { public: SLATE_BEGIN_ARGS(SGraphEditorActionMenuFlow) - : _GraphObj( static_cast(nullptr) ) + : _GraphObj(static_cast(nullptr)) , _GraphNode(nullptr) - , _NewNodePosition( FVector2D::ZeroVector ) - , _AutoExpandActionMenu( false ) + , _NewNodePosition(FVector2f::ZeroVector) + , _AutoExpandActionMenu(false) , _SubNodeFlags(0) - {} + { + } - SLATE_ARGUMENT( UEdGraph*, GraphObj ) - SLATE_ARGUMENT( UEdGraphNode*, GraphNode) - SLATE_ARGUMENT( FVector2D, NewNodePosition ) - SLATE_ARGUMENT( TArray, DraggedFromPins ) - SLATE_ARGUMENT( SGraphEditor::FActionMenuClosed, OnClosedCallback ) - SLATE_ARGUMENT( bool, AutoExpandActionMenu ) - SLATE_ARGUMENT( int32, SubNodeFlags) + SLATE_ARGUMENT(UEdGraph*, GraphObj) + SLATE_ARGUMENT(UEdGraphNode*, GraphNode) + SLATE_ARGUMENT(FVector2f, NewNodePosition) + SLATE_ARGUMENT(TArray, DraggedFromPins) + SLATE_ARGUMENT(SGraphEditor::FActionMenuClosed, OnClosedCallback) + SLATE_ARGUMENT(bool, AutoExpandActionMenu) + SLATE_ARGUMENT(int32, SubNodeFlags) SLATE_END_ARGS() - void Construct( const FArguments& InArgs ); + void Construct(const FArguments& InArgs); ~SGraphEditorActionMenuFlow(); @@ -55,14 +56,14 @@ class FLOWEDITOR_API SGraphEditorActionMenuFlow : public SBorder UEdGraph* GraphObj; UEdGraphNode* GraphNode; TArray DraggedFromPins; - FVector2D NewNodePosition; + FVector2f NewNodePosition; bool AutoExpandActionMenu; int32 SubNodeFlags; SGraphEditor::FActionMenuClosed OnClosedCallback; TSharedPtr GraphActionMenu; - void OnActionSelected( const TArray< TSharedPtr >& SelectedAction, ESelectInfo::Type InSelectionType ); + void OnActionSelected(const TArray>& SelectedAction, ESelectInfo::Type InSelectionType); /** Callback used to populate all actions list in SGraphActionMenu */ void CollectAllActions(FGraphActionListBuilderBase& OutAllActions); From e0d94a6657c75ac88bd08a0957c6a84cd165e719 Mon Sep 17 00:00:00 2001 From: MaksymKapelianovych <48297221+MaksymKapelianovych@users.noreply.github.com> Date: Sat, 31 May 2025 19:52:10 +0300 Subject: [PATCH 356/485] Added check for PinFriendlyName. (#291) * Added check for PinFriendlyName. This will force node to rebuild its pins if only PinFriendlyName is changed. * Added check for PinFriendlyName --- Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index bc6c3b39d..8edd89c8d 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -1671,8 +1671,9 @@ bool CheckPinsMatch(const TArray& LeftPins, const TArray& Ri auto PinsAreEqualPredicate = [&Left](const FFlowPin& Right) { const bool bNameMatch = Left.PinName == Right.PinName; + const bool bFriendlyNameMatch = Left.PinFriendlyName.EqualTo(Right.PinFriendlyName); const bool bTypeMatch = Left.GetPinType() == Right.GetPinType(); - return bNameMatch && bTypeMatch; + return bNameMatch && bFriendlyNameMatch && bTypeMatch; }; // For each required pin, make sure the existing pins array contains a pin that matches by name and type @@ -1699,7 +1700,7 @@ bool CheckPinsMatch(const TArray& GraphPins, const TArrayPinName == FlowNodePin.PinName; + return GraphNodePin->PinName == FlowNodePin.PinName && GraphNodePin->PinFriendlyName.EqualTo(FlowNodePin.PinFriendlyName); })) { // Could not match the pin from the flow node with any of the EdPins array. From fbeff21a980c0a27b0613e8049cc1a09a3dd4df8 Mon Sep 17 00:00:00 2001 From: Numblaze Date: Sat, 31 May 2025 18:52:23 +0200 Subject: [PATCH 357/485] * FlowSubsystem::LoadRootFlow takes the bAllowMultipleInstances parameter to allow loading multiple instances of a flow, in the same way you can start multiple instances of a root flow (#293) * FlowComponent calls FlowSubsystem::LoadRootFlow with the value of its public parameter bAllowMultipleInstances --- Source/Flow/Private/FlowComponent.cpp | 2 +- Source/Flow/Private/FlowSubsystem.cpp | 4 ++-- Source/Flow/Public/FlowSubsystem.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Flow/Private/FlowComponent.cpp b/Source/Flow/Private/FlowComponent.cpp index b6cb6c9ce..fc1dc359e 100644 --- a/Source/Flow/Private/FlowComponent.cpp +++ b/Source/Flow/Private/FlowComponent.cpp @@ -543,7 +543,7 @@ void UFlowComponent::LoadRootFlow() { VerifyIdentityTags(); - GetFlowSubsystem()->LoadRootFlow(this, RootFlow, SavedAssetInstanceName); + GetFlowSubsystem()->LoadRootFlow(this, RootFlow, SavedAssetInstanceName, bAllowMultipleInstances); SavedAssetInstanceName = FString(); } } diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index ea085c801..946610632 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -375,7 +375,7 @@ void UFlowSubsystem::OnGameLoaded(UFlowSaveGame* SaveGame) // it's recommended to do this by overriding method in the subclass } -void UFlowSubsystem::LoadRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const FString& SavedAssetInstanceName) +void UFlowSubsystem::LoadRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const FString& SavedAssetInstanceName, const bool bAllowMultipleInstances) { if (FlowAsset == nullptr || SavedAssetInstanceName.IsEmpty()) { @@ -387,7 +387,7 @@ void UFlowSubsystem::LoadRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const F if (AssetRecord.InstanceName == SavedAssetInstanceName && (FlowAsset->IsBoundToWorld() == false || AssetRecord.WorldName == GetWorld()->GetName())) { - UFlowAsset* LoadedInstance = CreateRootFlow(Owner, FlowAsset, false); + UFlowAsset* LoadedInstance = CreateRootFlow(Owner, FlowAsset, bAllowMultipleInstances); if (LoadedInstance) { LoadedInstance->LoadInstance(AssetRecord); diff --git a/Source/Flow/Public/FlowSubsystem.h b/Source/Flow/Public/FlowSubsystem.h index a70406ecf..4852cb519 100644 --- a/Source/Flow/Public/FlowSubsystem.h +++ b/Source/Flow/Public/FlowSubsystem.h @@ -131,7 +131,7 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem virtual void OnGameLoaded(UFlowSaveGame* SaveGame); UFUNCTION(BlueprintCallable, Category = "FlowSubsystem") - virtual void LoadRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const FString& SavedAssetInstanceName); + virtual void LoadRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const FString& SavedAssetInstanceName, const bool bAllowMultipleInstances); UFUNCTION(BlueprintCallable, Category = "FlowSubsystem") virtual void LoadSubFlow(UFlowNode_SubGraph* SubGraphNode, const FString& SavedAssetInstanceName); From c4c33f5513791c3f4efd5a49177383d574e3f1c9 Mon Sep 17 00:00:00 2001 From: IRSMsoso Date: Sat, 31 May 2025 11:52:44 -0500 Subject: [PATCH 358/485] Fix Linux packaged build by passing by reference (#294) --- Source/Flow/Private/FlowAsset.cpp | 7 ++++--- Source/Flow/Public/FlowAsset.h | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index feae76a92..8671016c5 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -224,7 +224,7 @@ bool UFlowAsset::CanFlowAssetUseFlowNodeClass(const UClass& FlowNodeClass) const bool UFlowAsset::IsFlowNodeClassInDeniedClasses(const UClass& FlowNodeClass) const { - for (const TSubclassOf DeniedNodeClass : DeniedNodeClasses) + for (const TSubclassOf& DeniedNodeClass : DeniedNodeClasses) { if (DeniedNodeClass && FlowNodeClass.IsChildOf(DeniedNodeClass)) { @@ -239,12 +239,13 @@ bool UFlowAsset::IsFlowNodeClassInDeniedClasses(const UClass& FlowNodeClass) con return false; } -bool UFlowAsset::IsFlowNodeClassInAllowedClasses(const UClass& FlowNodeClass, const TSubclassOf RequiredAncestor) const +bool UFlowAsset::IsFlowNodeClassInAllowedClasses(const UClass& FlowNodeClass, + const TSubclassOf& RequiredAncestor) const { if (AllowedNodeClasses.Num() > 0) { bool bAllowedInAsset = false; - for (const TSubclassOf AllowedNodeClass : AllowedNodeClasses) + for (const TSubclassOf& AllowedNodeClass : AllowedNodeClasses) { // If a RequiredAncestor is provided, the AllowedNodeClass must be a subclass of the RequiredAncestor if (AllowedNodeClass && FlowNodeClass.IsChildOf(AllowedNodeClass) && (!RequiredAncestor || AllowedNodeClass->IsChildOf(RequiredAncestor))) diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index f0736c287..0e2963463 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -124,7 +124,7 @@ class FLOW_API UFlowAsset : public UObject bool CanFlowAssetUseFlowNodeClass(const UClass& FlowNodeClass) const; bool CanFlowAssetReferenceFlowNode(const UClass& FlowNodeClass, FText* OutOptionalFailureReason = nullptr) const; - bool IsFlowNodeClassInAllowedClasses(const UClass& FlowNodeClass, const TSubclassOf RequiredAncestor = nullptr) const; + bool IsFlowNodeClassInAllowedClasses(const UClass& FlowNodeClass, const TSubclassOf& RequiredAncestor = nullptr) const; bool IsFlowNodeClassInDeniedClasses(const UClass& FlowNodeClass) const; #endif From 50e3003b7e03053a70810485bb461bdab67384ff Mon Sep 17 00:00:00 2001 From: Bohdon Sayre Date: Sat, 9 Aug 2025 04:47:33 -0400 Subject: [PATCH 359/485] fix missing includes hidden by unity build (#304) --- Source/FlowEditor/Public/Asset/SFlowDiff.h | 1 + Source/FlowEditor/Public/FlowEditorModule.h | 1 + Source/FlowEditor/Public/Graph/FlowGraphSchema.h | 1 + 3 files changed, 3 insertions(+) diff --git a/Source/FlowEditor/Public/Asset/SFlowDiff.h b/Source/FlowEditor/Public/Asset/SFlowDiff.h index d64093aa8..5b4ea888e 100644 --- a/Source/FlowEditor/Public/Asset/SFlowDiff.h +++ b/Source/FlowEditor/Public/Asset/SFlowDiff.h @@ -4,6 +4,7 @@ #include "IDetailsView.h" #include "DiffResults.h" +#include "GraphEditor.h" #include "SDetailsDiff.h" #include "Textures/SlateIcon.h" diff --git a/Source/FlowEditor/Public/FlowEditorModule.h b/Source/FlowEditor/Public/FlowEditorModule.h index 71bc523bc..018bfd314 100644 --- a/Source/FlowEditor/Public/FlowEditorModule.h +++ b/Source/FlowEditor/Public/FlowEditorModule.h @@ -6,6 +6,7 @@ #include "IAssetTypeActions.h" #include "Modules/ModuleInterface.h" #include "PropertyEditorDelegates.h" +#include "Toolkits/AssetEditorToolkit.h" #include "Toolkits/IToolkit.h" class FSlateStyleSet; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h index 9aa8d2db4..c90c269ea 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h @@ -3,6 +3,7 @@ #pragma once #include "EdGraph/EdGraphSchema.h" +#include "Runtime/Launch/Resources/Version.h" #include "Templates/SubclassOf.h" #include "FlowGraphSchema.generated.h" From 867bf1ecb93e0135c4411728a7c7c436cfabab10 Mon Sep 17 00:00:00 2001 From: Greggory Addison <48114595+GreggoryAddison-AntiHeroGameStudio@users.noreply.github.com> Date: Sat, 9 Aug 2025 03:48:10 -0500 Subject: [PATCH 360/485] [Flow Graph Editor - SFlowGraphNode] (#301) Fixed an issue where break point overlay brushes weren't showing properly due to deprecated code in 5.6 --- Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp | 4 ++++ Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp index 7c4af8a0d..25d96645f 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp @@ -124,7 +124,11 @@ const FSlateBrush* SFlowGraphNode::GetShadowBrush(bool bSelected) const return SGraphNode::GetShadowBrush(bSelected); } +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 6 void SFlowGraphNode::GetOverlayBrushes(bool bSelected, const FVector2D WidgetSize, TArray& Brushes) const +#else +void SFlowGraphNode::GetOverlayBrushes(bool bSelected, const FVector2f& WidgetSize, TArray& Brushes) const +#endif { check(DebuggerSubsystem.IsValid()); diff --git a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h index 3a8985b40..a7977813b 100644 --- a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h @@ -34,7 +34,12 @@ class FLOWEDITOR_API SFlowGraphNode : public SGraphNode // SNodePanel::SNode virtual void GetNodeInfoPopups(FNodeInfoContext* Context, TArray& Popups) const override; virtual const FSlateBrush* GetShadowBrush(bool bSelected) const override; - virtual void GetOverlayBrushes(bool bSelected, const FVector2D WidgetSize, TArray& Brushes) const override; + + #if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 6 +virtual void GetOverlayBrushes(bool bSelected, const FVector2D WidgetSize, TArray& Brushes) const override; +#else +virtual void GetOverlayBrushes(bool bSelected, const FVector2f& WidgetSize, TArray& Brushes) const override; +#endif // -- // SGraphNode From ece0515988bcec2db1284bacce05f3785ce00d13 Mon Sep 17 00:00:00 2001 From: LindyHopperGT <91915878+LindyHopperGT@users.noreply.github.com> Date: Wed, 20 Aug 2025 08:38:18 -0700 Subject: [PATCH 361/485] Updates from Riot version end of Q2 2025 (#305) * Updates from Riot version q2 2025 - Adjusted how we subscribe to external UFlowGraphNode changes - Renamed FFlowNamedDataPinOutputPropertyCustomization (which is now also used for input pins) - Added enum-based constructors for FFlowDataPinResult* structs - Fixed InstancedStruct.h reference for UE 5.4 - Added FLOW_API to GetFlowPinType() functions to allow it being called from extension plugins (eg, AIFlowGraph) - Added FDataValidationContext mutable param to ValidateNode() functions, to allow it to interoperate better with newer-style validation functions, which take the parameter. - Renamed the parameterless ValidateNode() function to DEPRECATED_ValidateNode(). This will break compiles until the name is changed OR the parameter is added and the ValidateNode() function is updated to use it - Updated all of the standard flow nodes' ValidateNode functions to use the context parameter. - Created a new UFlowNode_FormatText that formats text using input pins and FText formatting engine - Changed the UFlowNode_Log to format text (a la UFlowNode_FormatText) to generate its logged output string. - Changed UFlowNode_Log to inherit from UFlowNode_DefineProperties, so that it can have input properties added on the instance - Renamed UFlowNode_DefineProperties::OutputProperties to NamedProperties, so that it can be used as the super class for UFlowNode_FormatText - Added GetRandomSeed() function to UFlowNode. The default version uses the hash from the node's GUID. This can be overridden in subclasses (which we do) to any implementation that suits the client code. - Added an IsFinishedState() classifier function for EFlowNodeState, to error-proof checking node state for 'finished' states - Updated the ForEachAddOn templates to support a parameter to control how the function should recurse into child addons (or not). - Fixed UFlowNode_ExecuteComponent to handle injected components correctly in validation - Fixed UFlowNode_ExecuteComponent to conform to the new style of pin generation, now using ContextPins (the old method didn't work after a refactor with flow graph node reconstruction) - Updated UFlowNode_ExecuteComponent to allow the component to supply data pin output values - Updated the networking of runtime IdentityTag changes - Added UFlowAsset::GatherNodesConnectedToAllInputs helper function - Extracted UFlowNodeAddOn::FindOwningFlowNode() functionality into a function * Remerge with Flow Main in prep for PR Took more changes from Flow Main to not stomp on their changes. * Update for ExecuteComponent's validation fix * Restore original version of this validator * Fix syntax error from last merge to restore the validation fixes to ExecuteComponent --- Config/BaseFlow.ini | 2 + Config/DefaultFlow.ini | 2 + Source/Flow/Private/AddOns/FlowNodeAddOn.cpp | 49 ++- Source/Flow/Private/FlowAsset.cpp | 21 + .../Nodes/Actor/FlowNode_ExecuteComponent.cpp | 390 ++++++++++++++++-- .../Private/Nodes/Developer/FlowNode_Log.cpp | 12 +- Source/Flow/Private/Nodes/FlowNode.cpp | 2 +- Source/Flow/Private/Nodes/FlowNodeBase.cpp | 304 ++++++++++++-- .../Nodes/Graph/FlowNode_CustomEventBase.cpp | 2 +- .../Nodes/Graph/FlowNode_DefineProperties.cpp | 56 ++- .../Nodes/Graph/FlowNode_FormatText.cpp | 115 ++++++ .../Private/Nodes/Graph/FlowNode_Start.cpp | 4 +- .../Private/Types/FlowDataPinProperties.cpp | 24 +- .../Types/FlowInjectComponentsHelper.cpp | 2 +- Source/Flow/Public/AddOns/FlowNodeAddOn.h | 12 + .../AddOns/FlowNodeAddOn_PredicateAND.h | 2 +- .../Public/AddOns/FlowNodeAddOn_PredicateOR.h | 2 +- Source/Flow/Public/FlowAsset.h | 7 +- Source/Flow/Public/FlowTypes.h | 24 ++ .../FlowDataPinPropertyProviderInterface.h | 8 +- .../Nodes/Actor/FlowNode_ExecuteComponent.h | 26 ++ .../Public/Nodes/Developer/FlowNode_Log.h | 6 +- Source/Flow/Public/Nodes/FlowNode.h | 8 +- Source/Flow/Public/Nodes/FlowNodeBase.h | 46 ++- .../Nodes/Graph/FlowNode_DefineProperties.h | 6 +- .../Public/Nodes/Graph/FlowNode_FormatText.h | 42 ++ .../Flow/Public/Types/FlowDataPinProperties.h | 125 ++++-- Source/Flow/Public/Types/FlowDataPinResults.h | 16 +- .../Private/Asset/FlowObjectDiff.cpp | 1 + ...amedDataPinOutputPropertyCustomization.cpp | 6 +- .../FlowEditor/Private/FlowEditorModule.cpp | 2 +- .../Widgets/SGraphEditorActionMenuFlow.cpp | 1 + ...wNamedDataPinOutputPropertyCustomization.h | 4 +- Source/FlowEditor/Public/FlowEditorModule.h | 1 + .../Graph/FlowGraphConnectionDrawingPolicy.h | 1 + .../FlowEditor/Public/Graph/FlowGraphEditor.h | 1 + .../FlowEditor/Public/Graph/FlowGraphSchema.h | 2 + 37 files changed, 1182 insertions(+), 152 deletions(-) create mode 100644 Source/Flow/Private/Nodes/Graph/FlowNode_FormatText.cpp create mode 100644 Source/Flow/Public/Nodes/Graph/FlowNode_FormatText.h diff --git a/Config/BaseFlow.ini b/Config/BaseFlow.ini index 9e22e6ad2..fe5504715 100644 --- a/Config/BaseFlow.ini +++ b/Config/BaseFlow.ini @@ -2,3 +2,5 @@ +ClassRedirects=(OldName="/Script/Flow.FlowNode_CustomEvent",NewName="/Script/Flow.FlowNode_CustomInput") +PropertyRedirects=(OldName="FlowAsset.CustomEvents",NewName="FlowAsset.CustomInputs") +PropertyRedirects=(OldName="FlowGraphNode.FlowNode",NewName="FlowGraphNode.NodeInstance") ++StructRedirects=(OldName="/Script/Flow.FlowNamedDataPinOutputProperty",NewName="/Script/Flow.FlowNamedDataPinProperty") ++PropertyRedirects=(OldName="FlowNode_DefineProperties.OutputProperties",NewName="NamedProperties") diff --git a/Config/DefaultFlow.ini b/Config/DefaultFlow.ini index 9e22e6ad2..fe5504715 100644 --- a/Config/DefaultFlow.ini +++ b/Config/DefaultFlow.ini @@ -2,3 +2,5 @@ +ClassRedirects=(OldName="/Script/Flow.FlowNode_CustomEvent",NewName="/Script/Flow.FlowNode_CustomInput") +PropertyRedirects=(OldName="FlowAsset.CustomEvents",NewName="FlowAsset.CustomInputs") +PropertyRedirects=(OldName="FlowGraphNode.FlowNode",NewName="FlowGraphNode.NodeInstance") ++StructRedirects=(OldName="/Script/Flow.FlowNamedDataPinOutputProperty",NewName="/Script/Flow.FlowNamedDataPinProperty") ++PropertyRedirects=(OldName="FlowNode_DefineProperties.OutputProperties",NewName="NamedProperties") diff --git a/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp b/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp index 996f2745a..67da90d0e 100644 --- a/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp +++ b/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp @@ -3,6 +3,7 @@ #include "AddOns/FlowNodeAddOn.h" #include "FlowLogChannels.h" + #include "Nodes/FlowNode.h" #include "Misc/RuntimeErrors.h" @@ -72,6 +73,35 @@ UFlowNode* UFlowNodeAddOn::GetFlowNode() const return FlowNode; } +UFlowNode* UFlowNodeAddOn::FindOwningFlowNode() const +{ + UObject* OuterObject = GetOuter(); + UFlowNode* ParentFlowNode = nullptr; + + while (IsValid(OuterObject)) + { + ParentFlowNode = Cast(OuterObject); + if (ParentFlowNode) + { + break; + } + + OuterObject = OuterObject->GetOuter(); + } + + return ParentFlowNode; +} + +int32 UFlowNodeAddOn::GetRandomSeed() const +{ + if (ensure(FlowNode)) + { + return FlowNode->GetRandomSeed(); + } + + return 0; +} + bool UFlowNodeAddOn::IsSupportedInputPinName(const FName& PinName) const { if (InputPins.IsEmpty()) @@ -91,18 +121,8 @@ bool UFlowNodeAddOn::IsSupportedInputPinName(const FName& PinName) const void UFlowNodeAddOn::CacheFlowNode() { - UObject* OuterObject = GetOuter(); - while (IsValid(OuterObject)) - { - FlowNode = Cast(OuterObject); - if (FlowNode) - { - break; - } - - OuterObject = OuterObject->GetOuter(); - } - + FlowNode = FindOwningFlowNode(); + ensureAsRuntimeWarning(FlowNode); } @@ -137,4 +157,9 @@ TArray UFlowNodeAddOn::GetContextOutputs() const { return GetPinsForContext(OutputPins); } + +void UFlowNodeAddOn::RequestReconstructionOnOwningFlowNode() const +{ + (void) OnAddOnRequestedParentReconstruction.ExecuteIfBound(); +} #endif // WITH_EDITOR diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 8671016c5..14b971f4f 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -1030,6 +1030,27 @@ TArray UFlowAsset::GetNodesInExecutionOrder(UFlowNode* FirstIterated return FoundNodes; } +TArray UFlowAsset::GatherNodesConnectedToAllInputs() const +{ + TSet> IteratedNodes; + TArray ConnectedNodes; + + // Nodes connected to the Start node + UFlowNode* DefaultEntryNode = GetDefaultEntryNode(); + GetNodesInExecutionOrder_Recursive(DefaultEntryNode, IteratedNodes, ConnectedNodes); + + // Nodes connected to Custom Input node(s) + for (const TPair& Node : ObjectPtrDecay(Nodes)) + { + if (UFlowNode_CustomInput* CustomInput = Cast(Node.Value)) + { + GetNodesInExecutionOrder_Recursive(CustomInput, IteratedNodes, ConnectedNodes); + } + } + + return ConnectedNodes; +} + void UFlowAsset::AddInstance(UFlowAsset* Instance) { ActiveInstances.Add(Instance); diff --git a/Source/Flow/Private/Nodes/Actor/FlowNode_ExecuteComponent.cpp b/Source/Flow/Private/Nodes/Actor/FlowNode_ExecuteComponent.cpp index ae0ee254c..5714c31e9 100644 --- a/Source/Flow/Private/Nodes/Actor/FlowNode_ExecuteComponent.cpp +++ b/Source/Flow/Private/Nodes/Actor/FlowNode_ExecuteComponent.cpp @@ -22,6 +22,9 @@ UFlowNode_ExecuteComponent::UFlowNode_ExecuteComponent() #if WITH_EDITOR Category = TEXT("Actor"); #endif + + InputPins.Reset(); + OutputPins.Reset(); } void UFlowNode_ExecuteComponent::InitializeInstance() @@ -193,6 +196,345 @@ void UFlowNode_ExecuteComponent::ExecuteInput(const FName& PinName) { LogError(FString::Printf(TEXT("Could not ExecuteInput %s, because the component was missing or could not be resolved."), *PinName.ToString())); } + + // Trigger the default output (if the output pins weren't replaced, + // and the node hasn't already Finished) + if (OutputPins.Contains(UFlowNode::DefaultOutputPin.PinName) && !HasFinished()) + { + constexpr bool bFinish = false; + TriggerOutput(UFlowNode::DefaultOutputPin.PinName, bFinish); + } +} + +#if WITH_EDITOR +TArray UFlowNode_ExecuteComponent::GetContextInputs() const +{ + TArray ContextInputs; + if (UActorComponent* ResolvedComp = GetResolvedComponent()) + { + if (const IFlowContextPinSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) + { + ContextInputs = PinSupplierInterface->GetContextInputs(); + } + } + else if (const UActorComponent* ExpectedComponent = TryGetExpectedComponent()) + { + if (const IFlowContextPinSupplierInterface* PinSupplierInterface = Cast(ExpectedComponent)) + { + ContextInputs = PinSupplierInterface->GetContextInputs(); + } + } + + if (ContextInputs.IsEmpty()) + { + // Add the default input if none are desired by the component + ContextInputs.Add(UFlowNode::DefaultInputPin); + } + + ContextInputs.Append(Super::GetContextInputs()); + + return ContextInputs; +} + +TArray UFlowNode_ExecuteComponent::GetContextOutputs() const +{ + TArray ContextOutputs; + if (UActorComponent* ResolvedComp = GetResolvedComponent()) + { + if (const IFlowContextPinSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) + { + ContextOutputs = PinSupplierInterface->GetContextOutputs(); + } + } + else if (const UActorComponent* ExpectedComponent = TryGetExpectedComponent()) + { + if (const IFlowContextPinSupplierInterface* PinSupplierInterface = Cast(ExpectedComponent)) + { + ContextOutputs = PinSupplierInterface->GetContextOutputs(); + } + } + + if (ContextOutputs.IsEmpty()) + { + // Add the default output if none are desired by the component + ContextOutputs.Add(UFlowNode::DefaultOutputPin); + } + + ContextOutputs.Append(Super::GetContextOutputs()); + + return ContextOutputs; +} +#endif // WITH_EDITOR + +bool UFlowNode_ExecuteComponent::CanSupplyDataPinValues_Implementation() const +{ + if (UActorComponent* ResolvedComp = GetResolvedComponent()) + { + if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) + { + if (IFlowDataPinValueSupplierInterface::Execute_CanSupplyDataPinValues(ResolvedComp)) + { + return true; + } + } + } + + return Super::CanSupplyDataPinValues_Implementation(); +} + +FFlowDataPinResult_Bool UFlowNode_ExecuteComponent::TrySupplyDataPinAsBool_Implementation(const FName& PinName) const +{ + if (UActorComponent* ResolvedComp = GetResolvedComponent()) + { + if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) + { + const FFlowDataPinResult_Bool PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsBool(ResolvedComp, PinName); + if (PinResult.Result == EFlowDataPinResolveResult::Success) + { + return PinResult; + } + } + } + + return Super::TrySupplyDataPinAsBool_Implementation(PinName); +} + +FFlowDataPinResult_Int UFlowNode_ExecuteComponent::TrySupplyDataPinAsInt_Implementation(const FName& PinName) const +{ + if (UActorComponent* ResolvedComp = GetResolvedComponent()) + { + if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) + { + const FFlowDataPinResult_Int PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsInt(ResolvedComp, PinName); + if (PinResult.Result == EFlowDataPinResolveResult::Success) + { + return PinResult; + } + } + } + + return Super::TrySupplyDataPinAsInt_Implementation(PinName); +} + +FFlowDataPinResult_Float UFlowNode_ExecuteComponent::TrySupplyDataPinAsFloat_Implementation(const FName& PinName) const +{ + if (UActorComponent* ResolvedComp = GetResolvedComponent()) + { + if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) + { + const FFlowDataPinResult_Float PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsFloat(ResolvedComp, PinName); + if (PinResult.Result == EFlowDataPinResolveResult::Success) + { + return PinResult; + } + } + } + + return Super::TrySupplyDataPinAsFloat_Implementation(PinName); +} + +FFlowDataPinResult_Name UFlowNode_ExecuteComponent::TrySupplyDataPinAsName_Implementation(const FName& PinName) const +{ + if (UActorComponent* ResolvedComp = GetResolvedComponent()) + { + if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) + { + const FFlowDataPinResult_Name PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsName(ResolvedComp, PinName); + if (PinResult.Result == EFlowDataPinResolveResult::Success) + { + return PinResult; + } + } + } + + return Super::TrySupplyDataPinAsName_Implementation(PinName); +} + +FFlowDataPinResult_String UFlowNode_ExecuteComponent::TrySupplyDataPinAsString_Implementation(const FName& PinName) const +{ + if (UActorComponent* ResolvedComp = GetResolvedComponent()) + { + if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) + { + const FFlowDataPinResult_String PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsString(ResolvedComp, PinName); + if (PinResult.Result == EFlowDataPinResolveResult::Success) + { + return PinResult; + } + } + } + + return Super::TrySupplyDataPinAsString_Implementation(PinName); +} + +FFlowDataPinResult_Text UFlowNode_ExecuteComponent::TrySupplyDataPinAsText_Implementation(const FName& PinName) const +{ + if (UActorComponent* ResolvedComp = GetResolvedComponent()) + { + if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) + { + const FFlowDataPinResult_Text PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsText(ResolvedComp, PinName); + if (PinResult.Result == EFlowDataPinResolveResult::Success) + { + return PinResult; + } + } + } + + return Super::TrySupplyDataPinAsText_Implementation(PinName); +} + +FFlowDataPinResult_Enum UFlowNode_ExecuteComponent::TrySupplyDataPinAsEnum_Implementation(const FName& PinName) const +{ + if (UActorComponent* ResolvedComp = GetResolvedComponent()) + { + if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) + { + const FFlowDataPinResult_Enum PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsEnum(ResolvedComp, PinName); + if (PinResult.Result == EFlowDataPinResolveResult::Success) + { + return PinResult; + } + } + } + + return Super::TrySupplyDataPinAsEnum_Implementation(PinName); +} + +FFlowDataPinResult_Vector UFlowNode_ExecuteComponent::TrySupplyDataPinAsVector_Implementation(const FName& PinName) const +{ + if (UActorComponent* ResolvedComp = GetResolvedComponent()) + { + if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) + { + const FFlowDataPinResult_Vector PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsVector(ResolvedComp, PinName); + if (PinResult.Result == EFlowDataPinResolveResult::Success) + { + return PinResult; + } + } + } + + return Super::TrySupplyDataPinAsVector_Implementation(PinName); +} + +FFlowDataPinResult_Rotator UFlowNode_ExecuteComponent::TrySupplyDataPinAsRotator_Implementation(const FName& PinName) const +{ + if (UActorComponent* ResolvedComp = GetResolvedComponent()) + { + if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) + { + const FFlowDataPinResult_Rotator PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsRotator(ResolvedComp, PinName); + if (PinResult.Result == EFlowDataPinResolveResult::Success) + { + return PinResult; + } + } + } + + return Super::TrySupplyDataPinAsRotator_Implementation(PinName); +} + +FFlowDataPinResult_Transform UFlowNode_ExecuteComponent::TrySupplyDataPinAsTransform_Implementation(const FName& PinName) const +{ + if (UActorComponent* ResolvedComp = GetResolvedComponent()) + { + if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) + { + const FFlowDataPinResult_Transform PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsTransform(ResolvedComp, PinName); + if (PinResult.Result == EFlowDataPinResolveResult::Success) + { + return PinResult; + } + } + } + + return Super::TrySupplyDataPinAsTransform_Implementation(PinName); +} + +FFlowDataPinResult_GameplayTag UFlowNode_ExecuteComponent::TrySupplyDataPinAsGameplayTag_Implementation(const FName& PinName) const +{ + if (UActorComponent* ResolvedComp = GetResolvedComponent()) + { + if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) + { + const FFlowDataPinResult_GameplayTag PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsGameplayTag(ResolvedComp, PinName); + if (PinResult.Result == EFlowDataPinResolveResult::Success) + { + return PinResult; + } + } + } + + return Super::TrySupplyDataPinAsGameplayTag_Implementation(PinName); +} + +FFlowDataPinResult_GameplayTagContainer UFlowNode_ExecuteComponent::TrySupplyDataPinAsGameplayTagContainer_Implementation(const FName& PinName) const +{ + if (UActorComponent* ResolvedComp = GetResolvedComponent()) + { + if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) + { + const FFlowDataPinResult_GameplayTagContainer PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsGameplayTagContainer(ResolvedComp, PinName); + if (PinResult.Result == EFlowDataPinResolveResult::Success) + { + return PinResult; + } + } + } + + return Super::TrySupplyDataPinAsGameplayTagContainer_Implementation(PinName); +} + +FFlowDataPinResult_InstancedStruct UFlowNode_ExecuteComponent::TrySupplyDataPinAsInstancedStruct_Implementation(const FName& PinName) const +{ + if (UActorComponent* ResolvedComp = GetResolvedComponent()) + { + if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) + { + const FFlowDataPinResult_InstancedStruct PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsInstancedStruct(ResolvedComp, PinName); + if (PinResult.Result == EFlowDataPinResolveResult::Success) + { + return PinResult; + } + } + } + + return Super::TrySupplyDataPinAsInstancedStruct_Implementation(PinName); +} + +FFlowDataPinResult_Object UFlowNode_ExecuteComponent::TrySupplyDataPinAsObject_Implementation(const FName& PinName) const +{ + if (UActorComponent* ResolvedComp = GetResolvedComponent()) + { + if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) + { + const FFlowDataPinResult_Object PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsObject(ResolvedComp, PinName); + if (PinResult.Result == EFlowDataPinResolveResult::Success) + { + return PinResult; + } + } + } + + return Super::TrySupplyDataPinAsObject_Implementation(PinName); +} + +FFlowDataPinResult_Class UFlowNode_ExecuteComponent::TrySupplyDataPinAsClass_Implementation(const FName& PinName) const +{ + if (UActorComponent* ResolvedComp = GetResolvedComponent()) + { + if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) + { + const FFlowDataPinResult_Class PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsClass(ResolvedComp, PinName); + if (PinResult.Result == EFlowDataPinResolveResult::Success) + { + return PinResult; + } + } + } + + return Super::TrySupplyDataPinAsClass_Implementation(PinName); } bool UFlowNode_ExecuteComponent::TryInjectComponent() @@ -306,6 +648,19 @@ UActorComponent* UFlowNode_ExecuteComponent::TryResolveComponent() return ResolvedComp; } +UActorComponent* UFlowNode_ExecuteComponent::GetResolvedComponent() const +{ + // This version of the function assumes the component has already been resolved previously. + // (using TryResolveComponent) + UActorComponent* ResolvedComp = ComponentRef.GetResolvedComponent(); + if (IsValid(ResolvedComp)) + { + return ResolvedComp; + } + + return nullptr; +} + #if WITH_EDITOR const UActorComponent* UFlowNode_ExecuteComponent::TryGetExpectedComponent() const { @@ -381,31 +736,20 @@ void UFlowNode_ExecuteComponent::RefreshComponentSource() void UFlowNode_ExecuteComponent::RefreshPins() { - bool bChangedPins = false; - - const UActorComponent* ExpectedComponent = TryGetExpectedComponent(); - if (const IFlowContextPinSupplierInterface* ContextPinSupplierInterface = Cast(ExpectedComponent)) - { - const TArray NewInputPins = ContextPinSupplierInterface->GetContextInputs(); - bChangedPins = RebuildPinArray(NewInputPins, InputPins, DefaultInputPin) || bChangedPins; - - const TArray NewOutputPins = ContextPinSupplierInterface->GetContextOutputs(); - bChangedPins = RebuildPinArray(NewOutputPins, OutputPins, DefaultOutputPin) || bChangedPins; - } - else - { - bChangedPins = RebuildPinArray(TArray(&DefaultInputPin.PinName, 1), InputPins, DefaultInputPin) || bChangedPins; - bChangedPins = RebuildPinArray(TArray(&DefaultOutputPin.PinName, 1), OutputPins, DefaultOutputPin) || bChangedPins; - } - - if (bChangedPins) - { - OnReconstructionRequested.ExecuteIfBound(); - } + OnReconstructionRequested.ExecuteIfBound(); } EDataValidationResult UFlowNode_ExecuteComponent::ValidateNode() { + const EDataValidationResult SuperResult = Super::ValidateNode(); + + EDataValidationResult FinalResult = CombineDataValidationResults(SuperResult, EDataValidationResult::Valid); + + if (IsValid(ComponentTemplate) || IsValid(ComponentClass)) + { + return FinalResult; + } + const bool bHasComponent = ComponentRef.IsConfigured(); if (!bHasComponent) { @@ -447,8 +791,8 @@ EDataValidationResult UFlowNode_ExecuteComponent::ValidateNode() return EDataValidationResult::Invalid; } } - - return EDataValidationResult::Valid; + + return FinalResult; } FString UFlowNode_ExecuteComponent::GetStatusString() const diff --git a/Source/Flow/Private/Nodes/Developer/FlowNode_Log.cpp b/Source/Flow/Private/Nodes/Developer/FlowNode_Log.cpp index e531b4d57..5a6f66d0a 100644 --- a/Source/Flow/Private/Nodes/Developer/FlowNode_Log.cpp +++ b/Source/Flow/Private/Nodes/Developer/FlowNode_Log.cpp @@ -2,7 +2,6 @@ #include "Nodes/Developer/FlowNode_Log.h" #include "FlowLogChannels.h" -#include "FlowSettings.h" #include "Engine/Engine.h" @@ -22,6 +21,9 @@ UFlowNode_Log::UFlowNode_Log(const FObjectInitializer& ObjectInitializer) Category = TEXT("Developer"); NodeDisplayStyle = FlowNodeStyle::Developer; #endif + + InputPins = { UFlowNode::DefaultInputPin }; + OutputPins = { UFlowNode::DefaultOutputPin }; } void UFlowNode_Log::ExecuteInput(const FName& PinName) @@ -36,6 +38,14 @@ void UFlowNode_Log::ExecuteInput(const FName& PinName) MessageResult.SetValue(Message); } + // Format Message with named properties + FText FormattedText; + if (TryFormatTextWithNamedPropertiesAsParameters(FText::FromString(MessageResult.Value), FormattedText)) + { + MessageResult.Value = FormattedText.ToString(); + } + + // Display the message check(MessageResult.Result == EFlowDataPinResolveResult::Success); switch (Verbosity) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index b53c2a9a4..048339277 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -853,7 +853,7 @@ void UFlowNode::TriggerFirstOutput(const bool bFinish) void UFlowNode::TriggerOutput(const FName PinName, const bool bFinish /*= false*/, const EFlowPinActivationType ActivationType /*= Default*/) { - if (ActivationState == EFlowNodeState::Completed || ActivationState == EFlowNodeState::Aborted) + if (HasFinished()) { // do not trigger output if node is already finished or aborted LogError(TEXT("Trying to TriggerOutput after finished or aborted")); diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp index 5d2b15899..7c2bdcbdb 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBase.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -8,6 +8,7 @@ #include "FlowSubsystem.h" #include "FlowTypes.h" #include "Interfaces/FlowDataPinValueSupplierInterface.h" +#include "Types/FlowArray.h" #include "Components/ActorComponent.h" #if WITH_EDITOR @@ -351,6 +352,28 @@ IFlowOwnerInterface* UFlowNodeBase::GetFlowOwnerInterface() const return nullptr; } +TArray UFlowNodeBase::BuildFlowNodeBaseAncestorChain(UFlowNodeBase& FromFlowNodeBase, bool bIncludeFromFlowNodeBase) +{ + TArray AncestorChain; + + UFlowNodeBase* CurOuter = Cast(FromFlowNodeBase.GetOuter()); + while (IsValid(CurOuter)) + { + AncestorChain.Add(CurOuter); + + CurOuter = Cast(CurOuter->GetOuter()); + } + + FlowArray::ReverseArray(AncestorChain); + + if (bIncludeFromFlowNodeBase) + { + AncestorChain.Add(&FromFlowNodeBase); + } + + return AncestorChain; +} + IFlowOwnerInterface* UFlowNodeBase::TryGetFlowOwnerInterfaceFromRootFlowOwner(UObject& RootFlowOwner, const UClass& ExpectedOwnerClass) { const UClass* RootFlowOwnerClass = RootFlowOwner.GetClass(); @@ -441,7 +464,9 @@ EFlowAddOnAcceptResult UFlowNodeBase::CheckAcceptFlowNodeAddOnChild( } #endif // WITH_EDITOR -EFlowForEachAddOnFunctionReturnValue UFlowNodeBase::ForEachAddOnConst(const FConstFlowNodeAddOnFunction& Function) const +EFlowForEachAddOnFunctionReturnValue UFlowNodeBase::ForEachAddOnConst( + const FConstFlowNodeAddOnFunction& Function, + EFlowForEachAddOnChildRule AddOnChildRule) const { FLOW_ASSERT_ENUM_MAX(EFlowForEachAddOnFunctionReturnValue, 3); @@ -461,18 +486,24 @@ EFlowForEachAddOnFunctionReturnValue UFlowNodeBase::ForEachAddOnConst(const FCon break; } - ReturnValue = AddOn->ForEachAddOnConst(Function); - - if (!ShouldContinueForEach(ReturnValue)) + FLOW_ASSERT_ENUM_MAX(EFlowForEachAddOnChildRule, 2); + if (AddOnChildRule == EFlowForEachAddOnChildRule::AllChildren) { - break; + ReturnValue = AddOn->ForEachAddOnConst(Function); + + if (!ShouldContinueForEach(ReturnValue)) + { + break; + } } } return ReturnValue; } -EFlowForEachAddOnFunctionReturnValue UFlowNodeBase::ForEachAddOn(const FFlowNodeAddOnFunction& Function) const +EFlowForEachAddOnFunctionReturnValue UFlowNodeBase::ForEachAddOn( + const FFlowNodeAddOnFunction& Function, + EFlowForEachAddOnChildRule AddOnChildRule) const { FLOW_ASSERT_ENUM_MAX(EFlowForEachAddOnFunctionReturnValue, 3); @@ -492,18 +523,25 @@ EFlowForEachAddOnFunctionReturnValue UFlowNodeBase::ForEachAddOn(const FFlowNode break; } - ReturnValue = AddOn->ForEachAddOn(Function); - - if (!ShouldContinueForEach(ReturnValue)) + FLOW_ASSERT_ENUM_MAX(EFlowForEachAddOnChildRule, 2); + if (AddOnChildRule == EFlowForEachAddOnChildRule::AllChildren) { - break; + ReturnValue = AddOn->ForEachAddOn(Function); + + if (!ShouldContinueForEach(ReturnValue)) + { + break; + } } } return ReturnValue; } -EFlowForEachAddOnFunctionReturnValue UFlowNodeBase::ForEachAddOnForClassConst(const UClass& InterfaceOrClass, const FConstFlowNodeAddOnFunction& Function) const +EFlowForEachAddOnFunctionReturnValue UFlowNodeBase::ForEachAddOnForClassConst( + const UClass& InterfaceOrClass, + const FConstFlowNodeAddOnFunction& Function, + EFlowForEachAddOnChildRule AddOnChildRule) const { FLOW_ASSERT_ENUM_MAX(EFlowForEachAddOnFunctionReturnValue, 3); @@ -516,9 +554,7 @@ EFlowForEachAddOnFunctionReturnValue UFlowNodeBase::ForEachAddOnForClassConst(co continue; } - // InterfaceOrClass can either be the AddOn's UClass (or its superclass) - // or an interface (the UClass version) that its UClass implements - if (AddOn->IsA(&InterfaceOrClass) || AddOn->GetClass()->ImplementsInterface(&InterfaceOrClass)) + if (AddOn->IsClassOrImplementsInterface(InterfaceOrClass)) { ReturnValue = Function(*AddOn); @@ -528,18 +564,25 @@ EFlowForEachAddOnFunctionReturnValue UFlowNodeBase::ForEachAddOnForClassConst(co } } - ReturnValue = AddOn->ForEachAddOnForClassConst(InterfaceOrClass, Function); - - if (!ShouldContinueForEach(ReturnValue)) + FLOW_ASSERT_ENUM_MAX(EFlowForEachAddOnChildRule, 2); + if (AddOnChildRule == EFlowForEachAddOnChildRule::AllChildren) { - break; + ReturnValue = AddOn->ForEachAddOnForClassConst(InterfaceOrClass, Function); + + if (!ShouldContinueForEach(ReturnValue)) + { + break; + } } } return ReturnValue; } -EFlowForEachAddOnFunctionReturnValue UFlowNodeBase::ForEachAddOnForClass(const UClass& InterfaceOrClass, const FFlowNodeAddOnFunction& Function) const +EFlowForEachAddOnFunctionReturnValue UFlowNodeBase::ForEachAddOnForClass( + const UClass& InterfaceOrClass, + const FFlowNodeAddOnFunction& Function, + EFlowForEachAddOnChildRule AddOnChildRule) const { FLOW_ASSERT_ENUM_MAX(EFlowForEachAddOnFunctionReturnValue, 3); @@ -552,9 +595,7 @@ EFlowForEachAddOnFunctionReturnValue UFlowNodeBase::ForEachAddOnForClass(const U continue; } - // InterfaceOrClass can either be the AddOn's UClass (or its superclass) - // or an interface (the UClass version) that its UClass implements - if (AddOn->IsA(&InterfaceOrClass) || AddOn->GetClass()->ImplementsInterface(&InterfaceOrClass)) + if (AddOn->IsClassOrImplementsInterface(InterfaceOrClass)) { ReturnValue = Function(*AddOn); @@ -564,11 +605,15 @@ EFlowForEachAddOnFunctionReturnValue UFlowNodeBase::ForEachAddOnForClass(const U } } - ReturnValue = AddOn->ForEachAddOnForClass(InterfaceOrClass, Function); - - if (!ShouldContinueForEach(ReturnValue)) + FLOW_ASSERT_ENUM_MAX(EFlowForEachAddOnChildRule, 2); + if (AddOnChildRule == EFlowForEachAddOnChildRule::AllChildren) { - break; + ReturnValue = AddOn->ForEachAddOnForClass(InterfaceOrClass, Function); + + if (!ShouldContinueForEach(ReturnValue)) + { + break; + } } } @@ -699,7 +744,6 @@ FText UFlowNodeBase::GetNodeToolTip() const } } - return GetClass()->GetToolTipText(); } @@ -910,6 +954,212 @@ bool UFlowNodeBase::BuildMessage(FString& Message) const } #endif +bool UFlowNodeBase::TryAddValueToFormatNamedArguments(const FFlowNamedDataPinProperty& NamedDataPinProperty, FFormatNamedArguments& InOutArguments) const +{ + const FFlowDataPinProperty* FlowDataPinProperty = NamedDataPinProperty.DataPinProperty.GetPtr(); + if (!FlowDataPinProperty) + { + return false; + } + + const EFlowPinType FlowPinType = FlowDataPinProperty->GetFlowPinType(); + + FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); + switch (FlowPinType) + { + case EFlowPinType::Exec: + { + LogError(TEXT("Cannot add Exec pin value to FFormatNamedArguments")); + } + break; + + case EFlowPinType::InstancedStruct: + { + LogError(TEXT("Cannot add InstancedStruct pin value to FFormatNamedArguments")); + } + break; + + case EFlowPinType::Bool: + { + const FFlowDataPinResult_Bool ResolvedResult = TryResolveDataPinAsBool(NamedDataPinProperty.Name); + if (ResolvedResult.Result == EFlowDataPinResolveResult::Success) + { + InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(ResolvedResult.Value)); + + return true; + } + } + break; + + case EFlowPinType::Int: + { + const FFlowDataPinResult_Int ResolvedResult = TryResolveDataPinAsInt(NamedDataPinProperty.Name); + if (ResolvedResult.Result == EFlowDataPinResolveResult::Success) + { + InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(ResolvedResult.Value)); + + return true; + } + } + break; + + case EFlowPinType::Float: + { + const FFlowDataPinResult_Float ResolvedResult = TryResolveDataPinAsFloat(NamedDataPinProperty.Name); + if (ResolvedResult.Result == EFlowDataPinResolveResult::Success) + { + InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(ResolvedResult.Value)); + + return true; + } + } + break; + + case EFlowPinType::Name: + { + const FFlowDataPinResult_Name ResolvedResult = TryResolveDataPinAsName(NamedDataPinProperty.Name); + if (ResolvedResult.Result == EFlowDataPinResolveResult::Success) + { + InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(FText::FromString(ResolvedResult.Value.ToString()))); + + return true; + } + } + break; + + case EFlowPinType::String: + { + const FFlowDataPinResult_String ResolvedResult = TryResolveDataPinAsString(NamedDataPinProperty.Name); + if (ResolvedResult.Result == EFlowDataPinResolveResult::Success) + { + InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(FText::FromString(ResolvedResult.Value))); + + return true; + } + } + break; + + case EFlowPinType::Text: + { + const FFlowDataPinResult_Text ResolvedResult = TryResolveDataPinAsText(NamedDataPinProperty.Name); + if (ResolvedResult.Result == EFlowDataPinResolveResult::Success) + { + InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(ResolvedResult.Value)); + + return true; + } + } + break; + + case EFlowPinType::Enum: + { + const FFlowDataPinResult_Enum ResolvedResult = TryResolveDataPinAsEnum(NamedDataPinProperty.Name); + if (ResolvedResult.Result == EFlowDataPinResolveResult::Success) + { + InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(FText::FromString(ResolvedResult.Value.ToString()))); + + return true; + } + } + break; + + case EFlowPinType::Vector: + { + const FFlowDataPinResult_Vector ResolvedResult = TryResolveDataPinAsVector(NamedDataPinProperty.Name); + if (ResolvedResult.Result == EFlowDataPinResolveResult::Success) + { + InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(FText::FromString(ResolvedResult.Value.ToString()))); + + return true; + } + } + break; + + case EFlowPinType::Rotator: + { + const FFlowDataPinResult_Rotator ResolvedResult = TryResolveDataPinAsRotator(NamedDataPinProperty.Name); + if (ResolvedResult.Result == EFlowDataPinResolveResult::Success) + { + InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(FText::FromString(ResolvedResult.Value.ToString()))); + + return true; + } + } + break; + + case EFlowPinType::Transform: + { + const FFlowDataPinResult_Transform ResolvedResult = TryResolveDataPinAsTransform(NamedDataPinProperty.Name); + if (ResolvedResult.Result == EFlowDataPinResolveResult::Success) + { + InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(FText::FromString(ResolvedResult.Value.ToString()))); + + return true; + } + } + break; + + case EFlowPinType::GameplayTag: + { + const FFlowDataPinResult_GameplayTag ResolvedResult = TryResolveDataPinAsGameplayTag(NamedDataPinProperty.Name); + if (ResolvedResult.Result == EFlowDataPinResolveResult::Success) + { + InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(FText::FromString(ResolvedResult.Value.ToString()))); + + return true; + } + } + break; + + case EFlowPinType::GameplayTagContainer: + { + const FFlowDataPinResult_GameplayTagContainer ResolvedResult = TryResolveDataPinAsGameplayTagContainer(NamedDataPinProperty.Name); + if (ResolvedResult.Result == EFlowDataPinResolveResult::Success) + { + InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(FText::FromString(ResolvedResult.Value.ToString()))); + + return true; + } + } + break; + + case EFlowPinType::Object: + { + const FFlowDataPinResult_Object ResolvedResult = TryResolveDataPinAsObject(NamedDataPinProperty.Name); + if (ResolvedResult.Result == EFlowDataPinResolveResult::Success) + { + if (IsValid(ResolvedResult.Value)) + { + InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(FText::FromString(ResolvedResult.Value->GetName()))); + } + else + { + InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(FText::FromString(TEXT("null")))); + } + + return true; + } + } + break; + + case EFlowPinType::Class: + { + const FFlowDataPinResult_Class ResolvedResult = TryResolveDataPinAsClass(NamedDataPinProperty.Name); + if (ResolvedResult.Result == EFlowDataPinResolveResult::Success) + { + InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(FText::FromString(ResolvedResult.GetAsSoftClass().ToString()))); + + return true; + } + } + break; + + default: break; + } + + return false; +} + EFlowDataPinResolveResult UFlowNodeBase::TryResolveDataPinPrerequisites(const FName& PinName, const UFlowNode*& FlowNode, const FFlowPin*& FlowPin, EFlowPinType PinType) const { FlowNode = GetFlowNodeSelfOrOwner(); diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_CustomEventBase.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_CustomEventBase.cpp index 4ca81b5ca..f9453778a 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_CustomEventBase.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_CustomEventBase.cpp @@ -25,7 +25,7 @@ void UFlowNode_CustomEventBase::SetEventName(const FName& InEventName) #if WITH_EDITOR // Must reconstruct the visual representation if anything that is included in AdaptiveNodeTitles changes OnReconstructionRequested.ExecuteIfBound(); -#endif // WITH_EDITOR +#endif } } diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_DefineProperties.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_DefineProperties.cpp index dc55e7a43..fd835b546 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_DefineProperties.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_DefineProperties.cpp @@ -8,7 +8,7 @@ UFlowNode_DefineProperties::UFlowNode_DefineProperties(const FObjectInitializer& : Super(ObjectInitializer) { #if WITH_EDITOR - NodeDisplayStyle = FlowNodeStyle::InOut; + NodeDisplayStyle = FlowNodeStyle::Terminal; Category = TEXT("Graph"); #endif @@ -26,7 +26,7 @@ bool UFlowNode_DefineProperties::TryFindPropertyByRemappedPinName( { // The start node stores its properties in instanced structs in an array, so look there first - for (const FFlowNamedDataPinOutputProperty& NamedProperty : OutputProperties) + for (const FFlowNamedDataPinProperty& NamedProperty : NamedProperties) { if (NamedProperty.Name == RemappedPinName && NamedProperty.IsValid()) { @@ -36,19 +36,30 @@ bool UFlowNode_DefineProperties::TryFindPropertyByRemappedPinName( } } - return Super::TryFindPropertyByPinName(RemappedPinName, OutFoundProperty, OutFoundInstancedStruct, InOutResult); + return Super::TryFindPropertyByRemappedPinName(RemappedPinName, OutFoundProperty, OutFoundInstancedStruct, InOutResult); } #if WITH_EDITOR void UFlowNode_DefineProperties::AutoGenerateDataPins(TMap& PinNameToBoundPropertyMap, TArray& InputDataPins, TArray& OutputDataPins) const { - for (const FFlowNamedDataPinOutputProperty& DataPinProperty : OutputProperties) + for (const FFlowNamedDataPinProperty& DataPinProperty : NamedProperties) { if (DataPinProperty.IsValid()) { PinNameToBoundPropertyMap.Add(DataPinProperty.Name, DataPinProperty.Name); - OutputDataPins.AddUnique(DataPinProperty.CreateFlowPin()); + if (DataPinProperty.IsInputProperty()) + { + InputDataPins.AddUnique(DataPinProperty.CreateFlowPin()); + } + else if (DataPinProperty.IsOutputProperty()) + { + OutputDataPins.AddUnique(DataPinProperty.CreateFlowPin()); + } + else + { + LogError(TEXT("DataPin must be either an Input or Output property!")); + } } } } @@ -69,18 +80,18 @@ void UFlowNode_DefineProperties::PostEditChangeChainProperty(FPropertyChangedCha if (PropertyChainEvent.ChangeType == EPropertyChangeType::ValueSet && Property->GetFName() == GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Enum, EnumName)) { - for (FFlowNamedDataPinOutputProperty& OutputProperty : OutputProperties) + for (FFlowNamedDataPinProperty& NamedProperty : NamedProperties) { - if (!OutputProperty.IsValid()) + if (!NamedProperty.IsValid()) { continue; } - const FFlowDataPinProperty& FlowDataPinProperty = OutputProperty.DataPinProperty.Get(); + const FFlowDataPinProperty& FlowDataPinProperty = NamedProperty.DataPinProperty.Get(); if (FlowDataPinProperty.GetFlowPinType() == EFlowPinType::Enum) { - FFlowDataPinOutputProperty_Enum& EnumProperty = OutputProperty.DataPinProperty.GetMutable(); + FFlowDataPinOutputProperty_Enum& EnumProperty = NamedProperty.DataPinProperty.GetMutable(); EnumProperty.OnEnumNameChanged(); } @@ -100,10 +111,35 @@ void UFlowNode_DefineProperties::PostEditChangeChainProperty(FPropertyChangedCha const uint32 PropertyChangedTypeFlags = (PropertyChainEvent.ChangeType & RelevantChangeTypesForReconstructionMask); const bool bIsRelevantChangeTypeForReconstruction = PropertyChangedTypeFlags != 0; - const bool bChangedOutputProperties = Property->GetFName() == GET_MEMBER_NAME_CHECKED(UFlowNode_DefineProperties, OutputProperties); + const bool bChangedOutputProperties = Property->GetFName() == GET_MEMBER_NAME_CHECKED(UFlowNode_DefineProperties, NamedProperties); if (bIsRelevantChangeTypeForReconstruction && bChangedOutputProperties) { OnReconstructionRequested.ExecuteIfBound(); } } #endif // WITH_EDITOR + +bool UFlowNode_DefineProperties::TryFormatTextWithNamedPropertiesAsParameters(const FText& FormatText, FText& OutFormattedText) const +{ + if (NamedProperties.IsEmpty()) + { + return false; + } + + FFormatNamedArguments Arguments; + for (const FFlowNamedDataPinProperty& NamedProperty : NamedProperties) + { + if (!NamedProperty.Name.IsValid()) + { + LogWarning(TEXT("Could not format text with a nameless named property")); + } + else if (!TryAddValueToFormatNamedArguments(NamedProperty, Arguments)) + { + LogWarning(FString::Printf(TEXT("Could not format text for named property %s"), *NamedProperty.Name.ToString())); + } + } + + OutFormattedText = FText::Format(FormatText, Arguments); + + return true; +} diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_FormatText.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_FormatText.cpp new file mode 100644 index 000000000..83adf38c0 --- /dev/null +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_FormatText.cpp @@ -0,0 +1,115 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Nodes/Graph/FlowNode_FormatText.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_FormatText) + +#define LOCTEXT_NAMESPACE "FlowNode_FormatText" + +const FName UFlowNode_FormatText::OUTPIN_TextOutput("Formatted Text"); + +UFlowNode_FormatText::UFlowNode_FormatText(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ +#if WITH_EDITOR + Category = TEXT("Graph"); + NodeDisplayStyle = FlowNodeStyle::Terminal; +#endif + + OutputPins.Add(FFlowPin(OUTPIN_TextOutput, EFlowPinType::Text)); +} + +FFlowDataPinResult_Name UFlowNode_FormatText::TrySupplyDataPinAsName_Implementation(const FName& PinName) const +{ + FText FormattedText; + const EFlowDataPinResolveResult FormatResult = TryResolveFormatText(PinName, FormattedText); + if (FormatResult != EFlowDataPinResolveResult::Invalid) + { + if (FormatResult == EFlowDataPinResolveResult::Success) + { + return FFlowDataPinResult_Name(FName(FormattedText.ToString())); + } + else + { + return FFlowDataPinResult_Name(FormatResult); + } + } + + return Super::TrySupplyDataPinAsName_Implementation(PinName); +} + +FFlowDataPinResult_String UFlowNode_FormatText::TrySupplyDataPinAsString_Implementation(const FName& PinName) const +{ + FText FormattedText; + const EFlowDataPinResolveResult FormatResult = TryResolveFormatText(PinName, FormattedText); + if (FormatResult != EFlowDataPinResolveResult::Invalid) + { + if (FormatResult == EFlowDataPinResolveResult::Success) + { + return FFlowDataPinResult_String(FormattedText.ToString()); + } + else + { + return FFlowDataPinResult_String(FormatResult); + } + } + + return Super::TrySupplyDataPinAsString_Implementation(PinName); +} + +FFlowDataPinResult_Text UFlowNode_FormatText::TrySupplyDataPinAsText_Implementation(const FName& PinName) const +{ + FText FormattedText; + const EFlowDataPinResolveResult FormatResult = TryResolveFormatText(PinName, FormattedText); + if (FormatResult != EFlowDataPinResolveResult::Invalid) + { + if (FormatResult == EFlowDataPinResolveResult::Success) + { + return FFlowDataPinResult_Text(FormattedText); + } + else + { + return FFlowDataPinResult_Text(FormatResult); + } + } + + return Super::TrySupplyDataPinAsText_Implementation(PinName); +} + +EFlowDataPinResolveResult UFlowNode_FormatText::TryResolveFormatText(const FName& PinName, FText& OutFormattedText) const +{ + if (PinName == OUTPIN_TextOutput) + { + if (TryFormatTextWithNamedPropertiesAsParameters(FormatText, OutFormattedText)) + { + return EFlowDataPinResolveResult::Success; + } + else + { + return EFlowDataPinResolveResult::FailedWithError; + } + } + + return EFlowDataPinResolveResult::Invalid; +} + +#if WITH_EDITOR + +void UFlowNode_FormatText::UpdateNodeConfigText_Implementation() +{ + constexpr bool bErrorIfInputPinNotFound = false; + const bool bIsInputConnected = IsInputConnected(GET_MEMBER_NAME_CHECKED(ThisClass, FormatText), bErrorIfInputPinNotFound); + + if (bIsInputConnected) + { + SetNodeConfigText(FText()); + } + else + { + SetNodeConfigText(FormatText); + } +} + +#endif + +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_Start.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_Start.cpp index a75f1fbda..3e30befa8 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_Start.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_Start.cpp @@ -31,7 +31,7 @@ void UFlowNode_Start::SetDataPinValueSupplier(IFlowDataPinValueSupplierInterface bool UFlowNode_Start::TryAppendExternalInputPins(TArray& InOutPins) const { // Add pins for all of the Flow DataPin Properties - for (const FFlowNamedDataPinOutputProperty& DataPinProperty : OutputProperties) + for (const FFlowNamedDataPinProperty& DataPinProperty : NamedProperties) { if (DataPinProperty.IsValid()) { @@ -39,7 +39,7 @@ bool UFlowNode_Start::TryAppendExternalInputPins(TArray& InOutPins) co } } - return !OutputProperties.IsEmpty(); + return !NamedProperties.IsEmpty(); } #endif // WITH_EDITOR diff --git a/Source/Flow/Private/Types/FlowDataPinProperties.cpp b/Source/Flow/Private/Types/FlowDataPinProperties.cpp index c640938c7..1f11e190a 100644 --- a/Source/Flow/Private/Types/FlowDataPinProperties.cpp +++ b/Source/Flow/Private/Types/FlowDataPinProperties.cpp @@ -79,7 +79,7 @@ void FFlowDataPinOutputProperty_Enum::OnEnumNameChanged() } } -FText FFlowNamedDataPinOutputProperty::BuildHeaderText() const +FText FFlowNamedDataPinProperty::BuildHeaderText() const { EFlowPinType PinType = EFlowPinType::Invalid; @@ -88,7 +88,7 @@ FText FFlowNamedDataPinOutputProperty::BuildHeaderText() const PinType = DataPinPropertyPtr->GetFlowPinType(); } - return FText::Format(LOCTEXT("FlowNamedDataPinOutputPropertyHeader", "{0} ({1})"), { FText::FromName(Name), UEnum::GetDisplayValueAsText(PinType) }); + return FText::Format(LOCTEXT("FlowNamedDataPinPropertyHeader", "{0} ({1})"), { FText::FromName(Name), UEnum::GetDisplayValueAsText(PinType) }); } UClass* FFlowDataPinOutputProperty_Class::DeriveMetaClass(const FProperty& MetaDataProperty) const @@ -167,6 +167,26 @@ UClass* FFlowDataPinOutputProperty_Object::TryGetObjectClassFromProperty(const F } #endif +bool FFlowNamedDataPinProperty::IsInputProperty() const +{ + if (const FFlowDataPinProperty* DataPinPropertyPtr = DataPinProperty.GetPtr()) + { + return DataPinPropertyPtr->IsInputProperty(); + } + + return false; +} + +bool FFlowNamedDataPinProperty::IsOutputProperty() const +{ + if (const FFlowDataPinProperty* DataPinPropertyPtr = DataPinProperty.GetPtr()) + { + return !DataPinPropertyPtr->IsInputProperty(); + } + + return false; +} + FFlowDataPinOutputProperty_Object::FFlowDataPinOutputProperty_Object(UObject* InValue, UClass* InClassFilter) : Super() #if WITH_EDITOR diff --git a/Source/Flow/Private/Types/FlowInjectComponentsHelper.cpp b/Source/Flow/Private/Types/FlowInjectComponentsHelper.cpp index 21a9853fa..59e7259ff 100644 --- a/Source/Flow/Private/Types/FlowInjectComponentsHelper.cpp +++ b/Source/Flow/Private/Types/FlowInjectComponentsHelper.cpp @@ -59,7 +59,7 @@ UActorComponent* FFlowInjectComponentsHelper::TryCreateComponentInstanceForActor { const EObjectFlags InstanceFlags = ComponentTemplate.GetFlags() | RF_Transient; - UActorComponent* ComponentInstance = NewObject(&Actor, ComponentTemplate.GetFName(), InstanceFlags, &ComponentTemplate); + UActorComponent* ComponentInstance = NewObject(&Actor, ComponentTemplate.GetClass(), ComponentTemplate.GetFName(), InstanceFlags, &ComponentTemplate); return ComponentInstance; } diff --git a/Source/Flow/Public/AddOns/FlowNodeAddOn.h b/Source/Flow/Public/AddOns/FlowNodeAddOn.h index e066a2c4f..30a9b63d0 100644 --- a/Source/Flow/Public/AddOns/FlowNodeAddOn.h +++ b/Source/Flow/Public/AddOns/FlowNodeAddOn.h @@ -64,16 +64,28 @@ class UFlowNodeAddOn : public UFlowNodeBase // -- // UFlowNodeAddOn + + //// The FlowNode that contains this AddOn + // (accessible only when initialized, runtime only) UFUNCTION(BlueprintCallable, BlueprintPure, Category = "FlowNodeAddon", DisplayName = "Get Flow Node") FLOW_API UFlowNode* GetFlowNode() const; + + // Will crawl the hierarchy until it finds a flow node (addons can be attached to other add-ons). + FLOW_API UFlowNode* FindOwningFlowNode() const; // -- + // Returns a random seed suitable for this flow node addon + // by default, uses the seed for the Flow Node that this addon is attached to. + FLOW_API virtual int32 GetRandomSeed() const override; + #if WITH_EDITOR // IFlowContextPinSupplierInterface FLOW_API virtual bool SupportsContextPins() const override { return Super::SupportsContextPins() || (!InputPins.IsEmpty() || !OutputPins.IsEmpty()); } FLOW_API virtual TArray GetContextInputs() const override; FLOW_API virtual TArray GetContextOutputs() const override; // -- + + FLOW_API void RequestReconstructionOnOwningFlowNode() const; #endif // WITH_EDITOR protected: diff --git a/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateAND.h b/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateAND.h index 381fbb632..c04202ebe 100644 --- a/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateAND.h +++ b/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateAND.h @@ -28,5 +28,5 @@ class UFlowNodeAddOn_PredicateAND virtual bool EvaluatePredicate_Implementation() const override; // -- - static bool EvaluatePredicateAND(const TArray& AddOns); + FLOW_API static bool EvaluatePredicateAND(const TArray& AddOns); }; diff --git a/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateOR.h b/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateOR.h index 0df21db22..2748506a4 100644 --- a/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateOR.h +++ b/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateOR.h @@ -28,5 +28,5 @@ class UFlowNodeAddOn_PredicateOR virtual bool EvaluatePredicate_Implementation() const override; // -- - static bool EvaluatePredicateOR(const TArray& AddOns); + FLOW_API static bool EvaluatePredicateOR(const TArray& AddOns); }; diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 0e2963463..38b9e206d 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -213,11 +213,14 @@ class FLOW_API UFlowAsset : public UObject UFUNCTION(BlueprintPure, Category = "FlowAsset") virtual UFlowNode* GetDefaultEntryNode() const; + // Gathers all of the nodes that are connected to the Start & Custom Inputs of the flow graph + TArray GatherNodesConnectedToAllInputs() const; + UFUNCTION(BlueprintPure, Category = "FlowAsset", meta = (DeterminesOutputType = "FlowNodeClass")) TArray GetNodesInExecutionOrder(UFlowNode* FirstIteratedNode, const TSubclassOf FlowNodeClass); template - void GetNodesInExecutionOrder(UFlowNode* FirstIteratedNode, TArray& OutNodes) + void GetNodesInExecutionOrder(UFlowNode* FirstIteratedNode, TArray& OutNodes) const { static_assert(TPointerIsConvertibleFromTo::Value, "'T' template parameter to GetNodesInExecutionOrder must be derived from UFlowNode"); @@ -230,7 +233,7 @@ class FLOW_API UFlowAsset : public UObject protected: template - void GetNodesInExecutionOrder_Recursive(UFlowNode* Node, TSet>& IteratedNodes, TArray& OutNodes) + void GetNodesInExecutionOrder_Recursive(UFlowNode* Node, TSet>& IteratedNodes, TArray& OutNodes) const { IteratedNodes.Add(Node); diff --git a/Source/Flow/Public/FlowTypes.h b/Source/Flow/Public/FlowTypes.h index d53b4b79a..301615181 100644 --- a/Source/Flow/Public/FlowTypes.h +++ b/Source/Flow/Public/FlowTypes.h @@ -38,9 +38,18 @@ enum class EFlowNodeState : uint8 Max UMETA(Hidden), Invalid UMETA(Hidden), Min = 0 UMETA(Hidden), + + // State subrange for states that count as "Finished" + FinishedFirst = Completed UMETA(Hidden), + FinishedLast = Aborted UMETA(Hidden), }; FLOW_ENUM_RANGE_VALUES(EFlowNodeState) +namespace EFlowNodeState_Classifiers +{ + FORCEINLINE bool IsFinishedState(EFlowNodeState State) { return FLOW_IS_ENUM_IN_SUBRANGE(State, EFlowNodeState::Finished); } +} + // Finish Policy value is read by Flow Node // Nodes have opportunity to terminate themselves differently if Flow Graph has been aborted // Example: Spawn node might despawn all actors if Flow Graph is aborted, not completed @@ -160,3 +169,18 @@ namespace EFlowForEachAddOnFunctionReturnValue_Classifiers { FORCEINLINE bool ShouldContinueForEach(EFlowForEachAddOnFunctionReturnValue Result) { return FLOW_IS_ENUM_IN_SUBRANGE(Result, EFlowForEachAddOnFunctionReturnValue::ContinueForEach); } } + +UENUM() +enum class EFlowForEachAddOnChildRule : int8 +{ + // Apply the Function to all child addons (and children of addons, etc.) + AllChildren, + + // Apply the Function to immediate child addons only (do not apply to their children) + ImmediateChildrenOnly, + + Max UMETA(Hidden), + Invalid = -1 UMETA(Hidden), + Min = 0 UMETA(Hidden), +}; +FLOW_ENUM_RANGE_VALUES(EFlowForEachAddOnChildRule); diff --git a/Source/Flow/Public/Interfaces/FlowDataPinPropertyProviderInterface.h b/Source/Flow/Public/Interfaces/FlowDataPinPropertyProviderInterface.h index f5225da2a..63789d62d 100644 --- a/Source/Flow/Public/Interfaces/FlowDataPinPropertyProviderInterface.h +++ b/Source/Flow/Public/Interfaces/FlowDataPinPropertyProviderInterface.h @@ -2,10 +2,12 @@ #pragma once -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 5 -#include "InstancedStruct.h" -#else +#include "Runtime/Launch/Resources/Version.h" + +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 4 #include "StructUtils/InstancedStruct.h" +#else +#include "InstancedStruct.h" #endif #include "UObject/Interface.h" diff --git a/Source/Flow/Public/Nodes/Actor/FlowNode_ExecuteComponent.h b/Source/Flow/Public/Nodes/Actor/FlowNode_ExecuteComponent.h index 6b34ac0c1..c4d8a4c58 100644 --- a/Source/Flow/Public/Nodes/Actor/FlowNode_ExecuteComponent.h +++ b/Source/Flow/Public/Nodes/Actor/FlowNode_ExecuteComponent.h @@ -64,7 +64,32 @@ class FLOW_API UFlowNode_ExecuteComponent : public UFlowNode virtual void UpdateNodeConfigText_Implementation() override; // -- + // IFlowDataPinValueSupplierInterface + virtual bool CanSupplyDataPinValues_Implementation() const override; + virtual FFlowDataPinResult_Bool TrySupplyDataPinAsBool_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Int TrySupplyDataPinAsInt_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Float TrySupplyDataPinAsFloat_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Name TrySupplyDataPinAsName_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_String TrySupplyDataPinAsString_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Text TrySupplyDataPinAsText_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Enum TrySupplyDataPinAsEnum_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Vector TrySupplyDataPinAsVector_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Rotator TrySupplyDataPinAsRotator_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Transform TrySupplyDataPinAsTransform_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_GameplayTag TrySupplyDataPinAsGameplayTag_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_GameplayTagContainer TrySupplyDataPinAsGameplayTagContainer_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_InstancedStruct TrySupplyDataPinAsInstancedStruct_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Object TrySupplyDataPinAsObject_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Class TrySupplyDataPinAsClass_Implementation(const FName& PinName) const override; + // -- + #if WITH_EDITOR + // IFlowContextPinSupplierInterface + virtual bool SupportsContextPins() const override { return true; } + virtual TArray GetContextInputs() const override; + virtual TArray GetContextOutputs() const override; + // -- + // UObject virtual void PostLoad() override; virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override; @@ -90,6 +115,7 @@ class FLOW_API UFlowNode_ExecuteComponent : public UFlowNode bool TryInjectComponent(); UActorComponent* TryResolveComponent(); + UActorComponent* GetResolvedComponent() const; TSubclassOf TryGetExpectedActorOwnerClass() const; protected: diff --git a/Source/Flow/Public/Nodes/Developer/FlowNode_Log.h b/Source/Flow/Public/Nodes/Developer/FlowNode_Log.h index 9f2df3120..0a88727ed 100644 --- a/Source/Flow/Public/Nodes/Developer/FlowNode_Log.h +++ b/Source/Flow/Public/Nodes/Developer/FlowNode_Log.h @@ -2,7 +2,7 @@ #pragma once -#include "Nodes/FlowNode.h" +#include "Nodes/Graph/FlowNode_DefineProperties.h" #include "FlowNode_Log.generated.h" // Variant of ELogVerbosity @@ -22,7 +22,7 @@ enum class EFlowLogVerbosity : uint8 * Optionally shows message on screen */ UCLASS(NotBlueprintable, meta = (DisplayName = "Log", Keywords = "print")) -class FLOW_API UFlowNode_Log : public UFlowNode +class FLOW_API UFlowNode_Log : public UFlowNode_DefineProperties { GENERATED_UCLASS_BODY() @@ -45,7 +45,9 @@ class FLOW_API UFlowNode_Log : public UFlowNode FColor TextColor; protected: + // IFlowCoreExecutableInterface virtual void ExecuteInput(const FName& PinName) override; + // -- #if WITH_EDITOR public: diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 0857485cb..b9da34695 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -74,6 +74,11 @@ class FLOW_API UFlowNode UFUNCTION(BlueprintPure, Category = "FlowNode") const FGuid& GetGuid() const { return NodeGuid; } + // Returns a random seed suitable for this flow node, + // by default based on the node Guid, + // but may be overridden in subclasses to supply some other value. + virtual int32 GetRandomSeed() const override { return GetTypeHash(NodeGuid); } + public: virtual bool CanFinishGraph() const { return false; } @@ -315,6 +320,7 @@ class FLOW_API UFlowNode public: EFlowNodeState GetActivationState() const { return ActivationState; } + bool HasFinished() const { return EFlowNodeState_Classifiers::IsFinishedState(ActivationState); } #if !UE_BUILD_SHIPPING @@ -335,9 +341,9 @@ class FLOW_API UFlowNode protected: void Deactivate(); +public: virtual void TriggerFirstOutput(const bool bFinish) override; virtual void TriggerOutput(FName PinName, const bool bFinish = false, const EFlowPinActivationType ActivationType = EFlowPinActivationType::Default) override; -public: virtual void Finish() override; private: diff --git a/Source/Flow/Public/Nodes/FlowNodeBase.h b/Source/Flow/Public/Nodes/FlowNodeBase.h index a8578305b..b9f9d5169 100644 --- a/Source/Flow/Public/Nodes/FlowNodeBase.h +++ b/Source/Flow/Public/Nodes/FlowNodeBase.h @@ -21,6 +21,7 @@ class UEdGraphNode; class IFlowOwnerInterface; class IFlowDataPinValueSupplierInterface; struct FFlowPin; +struct FFlowNamedDataPinProperty; #if WITH_EDITORONLY_DATA DECLARE_DELEGATE(FFlowNodeEvent); @@ -113,6 +114,10 @@ class FLOW_API UFlowNodeBase UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (HidePin = "ActivationType")) virtual void TriggerOutputPin(const FFlowOutputPinHandle Pin, const bool bFinish = false, const EFlowPinActivationType ActivationType = EFlowPinActivationType::Default); + // Returns a random seed suitable for this flow node base + UFUNCTION(BlueprintPure, Category = "FlowNode") + virtual int32 GetRandomSeed() const PURE_VIRTUAL(GetRandomSeed, return 0;); + ////////////////////////////////////////////////////////////////////////// // Pins @@ -156,6 +161,8 @@ class FLOW_API UFlowNodeBase // NOTE - will consider a UActorComponent owner's owning actor if appropriate IFlowOwnerInterface* GetFlowOwnerInterface() const; + static TArray BuildFlowNodeBaseAncestorChain(UFlowNodeBase& FromFlowNodeBase, bool bIncludeFromFlowNodeBase); + protected: // Helper functions for GetFlowOwnerInterface() static IFlowOwnerInterface* TryGetFlowOwnerInterfaceFromRootFlowOwner(UObject& RootFlowOwner, const UClass& ExpectedOwnerClass); @@ -188,25 +195,38 @@ class FLOW_API UFlowNodeBase EFlowAddOnAcceptResult CheckAcceptFlowNodeAddOnChild(const UFlowNodeAddOn* AddOnTemplate, const TArray& AdditionalAddOnsToAssumeAreChildren) const; #endif // WITH_EDITOR - // Call a function for all of this object's AddOns (recursively iterating AddOns inside AddOn) - EFlowForEachAddOnFunctionReturnValue ForEachAddOnConst(const FConstFlowNodeAddOnFunction& Function) const; - EFlowForEachAddOnFunctionReturnValue ForEachAddOn(const FFlowNodeAddOnFunction& Function) const; + bool IsClassOrImplementsInterface(const UClass& InterfaceOrClass) const + { + // InterfaceOrClass can either be the AddOn's UClass (or its superclass) + // or an interface (the UClass version) that its UClass implements + return IsA(&InterfaceOrClass) || GetClass()->ImplementsInterface(&InterfaceOrClass); + } template + bool IsClassOrImplementsInterface() const + { + return IsClassOrImplementsInterface(*TInterfaceOrClass::StaticClass()); + } + + // Call a function for all of this object's AddOns (recursively iterating AddOns inside AddOn) + EFlowForEachAddOnFunctionReturnValue ForEachAddOnConst(const FConstFlowNodeAddOnFunction& Function, EFlowForEachAddOnChildRule AddOnChildRule = EFlowForEachAddOnChildRule::AllChildren) const; + EFlowForEachAddOnFunctionReturnValue ForEachAddOn(const FFlowNodeAddOnFunction& Function, EFlowForEachAddOnChildRule AddOnChildRule = EFlowForEachAddOnChildRule::AllChildren) const; + + template EFlowForEachAddOnFunctionReturnValue ForEachAddOnForClassConst(const FConstFlowNodeAddOnFunction Function) const { - return ForEachAddOnForClassConst(*TInterfaceOrClass::StaticClass(), Function); + return ForEachAddOnForClassConst(*TInterfaceOrClass::StaticClass(), Function, TAddOnChildRule); } - EFlowForEachAddOnFunctionReturnValue ForEachAddOnForClassConst(const UClass& InterfaceOrClass, const FConstFlowNodeAddOnFunction& Function) const; + EFlowForEachAddOnFunctionReturnValue ForEachAddOnForClassConst(const UClass& InterfaceOrClass, const FConstFlowNodeAddOnFunction& Function, EFlowForEachAddOnChildRule AddOnChildRule = EFlowForEachAddOnChildRule::AllChildren) const; - template + template EFlowForEachAddOnFunctionReturnValue ForEachAddOnForClass(const FFlowNodeAddOnFunction Function) const { - return ForEachAddOnForClass(*TInterfaceOrClass::StaticClass(), Function); + return ForEachAddOnForClass(*TInterfaceOrClass::StaticClass(), Function, TAddOnChildRule); } - EFlowForEachAddOnFunctionReturnValue ForEachAddOnForClass(const UClass& InterfaceOrClass, const FFlowNodeAddOnFunction& Function) const; + EFlowForEachAddOnFunctionReturnValue ForEachAddOnForClass(const UClass& InterfaceOrClass, const FFlowNodeAddOnFunction& Function, EFlowForEachAddOnChildRule AddOnChildRule = EFlowForEachAddOnChildRule::AllChildren) const; public: @@ -264,6 +284,10 @@ class FLOW_API UFlowNodeBase // Public only for TResolveDataPinWorkingData's use EFlowDataPinResolveResult TryResolveDataPinPrerequisites(const FName& PinName, const UFlowNode*& FlowNode, const FFlowPin*& FlowPin, EFlowPinType PinType) const; +protected: + + bool TryAddValueToFormatNamedArguments(const FFlowNamedDataPinProperty& NamedDataPinProperty, FFormatNamedArguments& InOutArguments) const; + public: ////////////////////////////////////////////////////////////////////////// @@ -288,6 +312,7 @@ class FLOW_API UFlowNodeBase TSubclassOf ReplacedBy; FFlowNodeEvent OnReconstructionRequested; + FFlowNodeEvent OnAddOnRequestedParentReconstruction; FFlowMessageLog ValidationLog; #endif // WITH_EDITORONLY_DATA @@ -314,7 +339,10 @@ class FLOW_API UFlowNodeBase // Called by owning FlowNode to add to its Status String. // (may be multi-line) virtual FString GetStatusString() const; -#endif // WITH_EDITOR + + void RequestReconstruction() const { (void) OnReconstructionRequested.ExecuteIfBound(); }; + +#endif protected: // Information displayed while node is working - displayed over node as NodeInfoPopup diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h b/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h index ea1e84515..0573362bc 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h @@ -20,12 +20,12 @@ class FLOW_API UFlowNode_DefineProperties : public UFlowNode, public IFlowDataPi // Instance-defined properties. // These will auto-generate a matching pin that is bound to its property as its data source. UPROPERTY(EditAnywhere, Category = "Configuration", DisplayName = Properties) - TArray OutputProperties; + TArray NamedProperties; public: #if WITH_EDITOR // IFlowContextPinSupplierInterface - virtual bool SupportsContextPins() const override { return Super::SupportsContextPins() || !OutputProperties.IsEmpty(); } + virtual bool SupportsContextPins() const override { return Super::SupportsContextPins() || !NamedProperties.IsEmpty(); } // -- // UObject @@ -37,6 +37,8 @@ class FLOW_API UFlowNode_DefineProperties : public UFlowNode, public IFlowDataPi // -- #endif + bool TryFormatTextWithNamedPropertiesAsParameters(const FText& FormatText, FText& OutFormattedText) const; + protected: virtual bool TryFindPropertyByRemappedPinName( const FName& RemappedPinName, diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_FormatText.h b/Source/Flow/Public/Nodes/Graph/FlowNode_FormatText.h new file mode 100644 index 000000000..02d5addea --- /dev/null +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_FormatText.h @@ -0,0 +1,42 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Nodes/Graph/FlowNode_DefineProperties.h" + +#include "FlowNode_FormatText.generated.h" + +/** + * Formats a text string using the standard UE FText formatting system + * using input pins as parameters and the output is delivered to OUTPIN_TextOutput + */ +UCLASS(NotBlueprintable, meta = (DisplayName = "Format Text", Keywords = "print")) +class FLOW_API UFlowNode_FormatText : public UFlowNode_DefineProperties +{ + GENERATED_UCLASS_BODY() + +private: + // Format text string + // (uses standard Unreal "FText" formatting: eg, {PinName} will refer to input called PinName) + // Note - complex types are exported "ToString" and InstancedStruct is not supported + UPROPERTY(EditAnywhere, Category = "Flow", meta = (DefaultForInputFlowPin, FlowPinType = Text)) + FText FormatText; + +protected: + +#if WITH_EDITOR +public: + virtual void UpdateNodeConfigText_Implementation() override; +#endif + + EFlowDataPinResolveResult TryResolveFormatText(const FName& PinName, FText& OutFormattedText) const; + +public: + // IFlowDataPinValueSupplierInterface + virtual FFlowDataPinResult_Name TrySupplyDataPinAsName_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_String TrySupplyDataPinAsString_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult_Text TrySupplyDataPinAsText_Implementation(const FName& PinName) const override; + // -- + + static const FName OUTPIN_TextOutput; +}; diff --git a/Source/Flow/Public/Types/FlowDataPinProperties.h b/Source/Flow/Public/Types/FlowDataPinProperties.h index d71277416..86345025d 100644 --- a/Source/Flow/Public/Types/FlowDataPinProperties.h +++ b/Source/Flow/Public/Types/FlowDataPinProperties.h @@ -9,12 +9,11 @@ #include "Runtime/Launch/Resources/Version.h" #include "UObject/Class.h" -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 5 -#include "InstancedStruct.h" -#else +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 4 #include "StructUtils/InstancedStruct.h" +#else +#include "InstancedStruct.h" #endif - #include "FlowDataPinProperties.generated.h" class FStructProperty; @@ -29,7 +28,8 @@ struct FFlowDataPinProperty virtual ~FFlowDataPinProperty() { } - virtual EFlowPinType GetFlowPinType() const { return EFlowPinType::Invalid; } + FLOW_API virtual EFlowPinType GetFlowPinType() const { return EFlowPinType::Invalid; } + FLOW_API virtual bool IsInputProperty() const { return false; } #if WITH_EDITOR FLOW_API static FFlowPin CreateFlowPin(const FName& PinName, const TInstancedStruct& DataPinProperty); @@ -78,7 +78,7 @@ struct FFlowDataPinOutputProperty_Bool : public FFlowDataPinProperty FFlowDataPinOutputProperty_Bool() { } FFlowDataPinOutputProperty_Bool(bool InValue) : Value(InValue) { } - virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Bool; } + FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Bool; } }; // Wrapper struct for a int64 that will generate and link to a Data Pin with its same name @@ -97,7 +97,7 @@ struct FFlowDataPinOutputProperty_Int64 : public FFlowDataPinProperty FFlowDataPinOutputProperty_Int64() { } FFlowDataPinOutputProperty_Int64(int64 InValue) : Value(InValue) { } - virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Int; } + FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Int; } }; // Wrapper struct for a int32 that will generate and link to a Data Pin with its same name @@ -116,7 +116,7 @@ struct FFlowDataPinOutputProperty_Int32 : public FFlowDataPinProperty FFlowDataPinOutputProperty_Int32() { } FFlowDataPinOutputProperty_Int32(int32 InValue) : Value(InValue) { } - virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Int; } + FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Int; } }; // Wrapper struct for a Double (64bit float) that will generate and link to a Data Pin with its same name @@ -135,7 +135,7 @@ struct FFlowDataPinOutputProperty_Double : public FFlowDataPinProperty FFlowDataPinOutputProperty_Double() { } FFlowDataPinOutputProperty_Double(double InValue) : Value(InValue) { } - virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Float; } + FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Float; } }; // Wrapper struct for a Float (32bit) that will generate and link to a Data Pin with its same name @@ -154,7 +154,7 @@ struct FFlowDataPinOutputProperty_Float : public FFlowDataPinProperty FFlowDataPinOutputProperty_Float() { } FFlowDataPinOutputProperty_Float(float InValue) : Value(InValue) { } - virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Float; } + FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Float; } }; // Wrapper struct for a FName that will generate and link to a Data Pin with its same name @@ -173,7 +173,7 @@ struct FFlowDataPinOutputProperty_Name : public FFlowDataPinProperty FFlowDataPinOutputProperty_Name() { } FFlowDataPinOutputProperty_Name(const FName& InValue) : Value(InValue) { } - virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Name; } + FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Name; } }; // Wrapper struct for a FString that will generate and link to a Data Pin with its same name @@ -192,7 +192,7 @@ struct FFlowDataPinOutputProperty_String : public FFlowDataPinProperty FFlowDataPinOutputProperty_String() { } FFlowDataPinOutputProperty_String(const FString& InValue) : Value(InValue) { } - virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::String; } + FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::String; } }; // Wrapper struct for a FText that will generate and link to a Data Pin with its same name @@ -211,7 +211,7 @@ struct FFlowDataPinOutputProperty_Text : public FFlowDataPinProperty FFlowDataPinOutputProperty_Text() { } FFlowDataPinOutputProperty_Text(const FText& InValue) : Value(InValue) { } - virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Text; } + FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Text; } }; // Wrapper struct for an enum that will generate and link to a Data Pin with its same name @@ -247,7 +247,7 @@ struct FFlowDataPinOutputProperty_Enum : public FFlowDataPinProperty , EnumClass(InEnumClass) { } - virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Enum; } + FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Enum; } #if WITH_EDITOR FLOW_API void OnEnumNameChanged(); @@ -270,7 +270,7 @@ struct FFlowDataPinOutputProperty_Vector : public FFlowDataPinProperty FFlowDataPinOutputProperty_Vector() {} FFlowDataPinOutputProperty_Vector(const FVector& InValue) : Value(InValue) { } - virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Vector; } + FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Vector; } }; // Wrapper struct for a FRotator that will generate and link to a Data Pin with its same name @@ -289,7 +289,7 @@ struct FFlowDataPinOutputProperty_Rotator : public FFlowDataPinProperty FFlowDataPinOutputProperty_Rotator() {} FFlowDataPinOutputProperty_Rotator(const FRotator& InValue) : Value(InValue) { } - virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Rotator; } + FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Rotator; } }; // Wrapper struct for a FTransform that will generate and link to a Data Pin with its same name @@ -308,7 +308,7 @@ struct FFlowDataPinOutputProperty_Transform : public FFlowDataPinProperty FFlowDataPinOutputProperty_Transform() {} FFlowDataPinOutputProperty_Transform(const FTransform& InValue) : Value(InValue) { } - virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Transform; } + FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Transform; } }; // Wrapper struct for a FGameplayTag that will generate and link to a Data Pin with its same name @@ -327,7 +327,7 @@ struct FFlowDataPinOutputProperty_GameplayTag : public FFlowDataPinProperty FFlowDataPinOutputProperty_GameplayTag() {} FFlowDataPinOutputProperty_GameplayTag(const FGameplayTag& InValue) : Value(InValue) { } - virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::GameplayTag; } + FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::GameplayTag; } }; // Wrapper struct for a FGameplayTagContainer that will generate and link to a Data Pin with its same name @@ -346,7 +346,7 @@ struct FFlowDataPinOutputProperty_GameplayTagContainer : public FFlowDataPinProp FFlowDataPinOutputProperty_GameplayTagContainer() {} FFlowDataPinOutputProperty_GameplayTagContainer(const FGameplayTagContainer& InValue) : Value(InValue) { } - virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::GameplayTagContainer; } + FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::GameplayTagContainer; } }; // Wrapper struct for a FInstancedStruct that will generate and link to a Data Pin with its same name @@ -365,7 +365,7 @@ struct FFlowDataPinOutputProperty_InstancedStruct : public FFlowDataPinProperty FFlowDataPinOutputProperty_InstancedStruct() {} FFlowDataPinOutputProperty_InstancedStruct(const FInstancedStruct& InValue) : Value(InValue) { } - virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::InstancedStruct; } + FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::InstancedStruct; } }; // Wrapper struct for a UObject that will generate and link to a Data Pin with its same name @@ -393,7 +393,7 @@ struct FFlowDataPinOutputProperty_Object : public FFlowDataPinProperty #if WITH_EDITORONLY_DATA UPROPERTY(EditAnywhere, Category = DataPins, meta = (AllowAbstract)) - TObjectPtr ClassFilter = nullptr; + TObjectPtr ClassFilter = UObject::StaticClass(); #endif // WITH_EDITORONLY_DATA public: @@ -401,7 +401,7 @@ struct FFlowDataPinOutputProperty_Object : public FFlowDataPinProperty FFlowDataPinOutputProperty_Object() {} FLOW_API FFlowDataPinOutputProperty_Object(UObject* InValue, UClass* InClassFilter = nullptr); - virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Object; } + FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Object; } UObject* GetObjectValue() const { return ReferenceValue ? ReferenceValue : InlineValue; } void SetObjectValue(UObject* InValue); @@ -429,7 +429,7 @@ struct FFlowDataPinOutputProperty_Class : public FFlowDataPinProperty #if WITH_EDITORONLY_DATA UPROPERTY(EditAnywhere, Category = DataPins, meta = (AllowAbstract)) - TObjectPtr ClassFilter = nullptr; + TObjectPtr ClassFilter = UObject::StaticClass(); #endif // WITH_EDITORONLY_DATA public: @@ -442,7 +442,7 @@ struct FFlowDataPinOutputProperty_Class : public FFlowDataPinProperty #endif { } - virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Class; } + FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Class; } #if WITH_EDITOR UClass* DeriveMetaClass(const FProperty& MetaDataProperty) const; @@ -456,8 +456,8 @@ struct FFlowDataPinOutputProperty_Class : public FFlowDataPinProperty // Wrapper for FFlowDataPinProperty that is used for flow nodes that add // dynamic properties, with associated data pins, on the flow node instance // (as opposed to C++ or blueprint compile-time). -USTRUCT(BlueprintType, DisplayName = "Flow Named Output DataPin Property") -struct FFlowNamedDataPinOutputProperty +USTRUCT(BlueprintType, DisplayName = "Flow Named DataPin Property") +struct FFlowNamedDataPinProperty { GENERATED_BODY() @@ -473,10 +473,13 @@ struct FFlowNamedDataPinOutputProperty public: - FFlowNamedDataPinOutputProperty() { } + FFlowNamedDataPinProperty() { } bool IsValid() const { return Name != NAME_None && DataPinProperty.GetPtr() != nullptr; } + bool IsInputProperty() const; + bool IsOutputProperty() const; + #if WITH_EDITOR FFlowPin CreateFlowPin() const { return FFlowDataPinProperty::CreateFlowPin(Name, DataPinProperty); } @@ -488,151 +491,185 @@ struct FFlowNamedDataPinOutputProperty // "Hidden" to keep them out of the TInstancedStruct selection list (but they can still be authored as properties in blueprint) // "DefaultForInputFlowPin" to change them to an Defaulted-Input property (rather than an output property) -USTRUCT(BlueprintType, DisplayName = "Bool - Input Flow Data Pin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "Bool")) +USTRUCT(BlueprintType, DisplayName = "Bool - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Bool")) struct FFlowDataPinInputProperty_Bool : public FFlowDataPinOutputProperty_Bool { GENERATED_BODY() FFlowDataPinInputProperty_Bool(bool InValue = false) : Super(InValue) { } + + FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "Int64 - Input Flow Data Pin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "Int")) +USTRUCT(BlueprintType, DisplayName = "Int64 - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Int")) struct FFlowDataPinInputProperty_Int64 : public FFlowDataPinOutputProperty_Int64 { GENERATED_BODY() FFlowDataPinInputProperty_Int64(int64 InValue = 0) : Super(InValue) { } + + FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "Int - Input Flow Data Pin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "Int")) +USTRUCT(BlueprintType, DisplayName = "Int - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Int")) struct FFlowDataPinInputProperty_Int32 : public FFlowDataPinOutputProperty_Int32 { GENERATED_BODY() FFlowDataPinInputProperty_Int32(int32 InValue = 0) : Super(InValue) { } + + FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "Double (float64) - Input Flow Data Pin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "Float")) +USTRUCT(BlueprintType, DisplayName = "Double (float64) - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Float")) struct FFlowDataPinInputProperty_Double : public FFlowDataPinOutputProperty_Double { GENERATED_BODY() FFlowDataPinInputProperty_Double(double InValue = 0.0) : Super(InValue) { } + + FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "Float - Input Flow Data Pin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "Float")) +USTRUCT(BlueprintType, DisplayName = "Float - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Float")) struct FFlowDataPinInputProperty_Float : public FFlowDataPinOutputProperty_Float { GENERATED_BODY() FFlowDataPinInputProperty_Float(float InValue = 0.0f) : Super(InValue) { } + + FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "Name - Input Flow Data Pin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "Name")) +USTRUCT(BlueprintType, DisplayName = "Name - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Name")) struct FFlowDataPinInputProperty_Name : public FFlowDataPinOutputProperty_Name { GENERATED_BODY() FFlowDataPinInputProperty_Name() : Super() { } FFlowDataPinInputProperty_Name(const FName& InValue) : Super(InValue) { } + + FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "String - Input Flow Data Pin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "String")) +USTRUCT(BlueprintType, DisplayName = "String - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "String")) struct FFlowDataPinInputProperty_String : public FFlowDataPinOutputProperty_String { GENERATED_BODY() FFlowDataPinInputProperty_String() : Super() { } FFlowDataPinInputProperty_String(const FString& InValue) : Super(InValue) { } + + FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "Text - Input Flow Data Pin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "Text")) +USTRUCT(BlueprintType, DisplayName = "Text - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Text")) struct FFlowDataPinInputProperty_Text : public FFlowDataPinOutputProperty_Text { GENERATED_BODY() FFlowDataPinInputProperty_Text() : Super() { } FFlowDataPinInputProperty_Text(const FText& InValue) : Super(InValue) { } + + FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "Enum - Input Flow Data Pin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "Enum")) +USTRUCT(BlueprintType, DisplayName = "Enum - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Enum")) struct FFlowDataPinInputProperty_Enum : public FFlowDataPinOutputProperty_Enum { GENERATED_BODY() FFlowDataPinInputProperty_Enum() : Super() { } FFlowDataPinInputProperty_Enum(const FName& InValue, UEnum* InEnumClass) : Super(InValue, InEnumClass) { } + + FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "Vector - Input Flow Data Pin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "Vector")) +USTRUCT(BlueprintType, DisplayName = "Vector - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Vector")) struct FFlowDataPinInputProperty_Vector : public FFlowDataPinOutputProperty_Vector { GENERATED_BODY() FFlowDataPinInputProperty_Vector() : Super() { } FFlowDataPinInputProperty_Vector(const FVector& InValue) : Super(InValue) { } + + FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "Rotator - Input Flow Data Pin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "Rotator")) +USTRUCT(BlueprintType, DisplayName = "Rotator - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Rotator")) struct FFlowDataPinInputProperty_Rotator : public FFlowDataPinOutputProperty_Rotator { GENERATED_BODY() FFlowDataPinInputProperty_Rotator() : Super() { } FFlowDataPinInputProperty_Rotator(const FRotator& InValue) : Super(InValue) { } + + FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "Transform - Input Flow Data Pin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "Transform")) +USTRUCT(BlueprintType, DisplayName = "Transform - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Transform")) struct FFlowDataPinInputProperty_Transform : public FFlowDataPinOutputProperty_Transform { GENERATED_BODY() FFlowDataPinInputProperty_Transform() : Super() { } FFlowDataPinInputProperty_Transform(const FTransform& InValue) : Super(InValue) { } + + FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "GameplayTag - Input Flow Data Pin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "GameplayTag")) +USTRUCT(BlueprintType, DisplayName = "GameplayTag - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "GameplayTag")) struct FFlowDataPinInputProperty_GameplayTag : public FFlowDataPinOutputProperty_GameplayTag { GENERATED_BODY() FFlowDataPinInputProperty_GameplayTag() : Super() { } FFlowDataPinInputProperty_GameplayTag(const FGameplayTag& InValue) : Super(InValue) { } + + FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "GameplayTagContainer - Input Flow DataPin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "GameplayTagContainer")) +USTRUCT(BlueprintType, DisplayName = "GameplayTagContainer - Input Flow DataPin Property", meta = (DefaultForInputFlowPin, FlowPinType = "GameplayTagContainer")) struct FFlowDataPinInputProperty_GameplayTagContainer : public FFlowDataPinOutputProperty_GameplayTagContainer { GENERATED_BODY() FFlowDataPinInputProperty_GameplayTagContainer() : Super() { } FFlowDataPinInputProperty_GameplayTagContainer(const FGameplayTagContainer& InValue) : Super(InValue) { } + + FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "InstancedStruct - Input Flow DataPin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "InstancedStruct")) +USTRUCT(BlueprintType, DisplayName = "InstancedStruct - Input Flow DataPin Property", meta = (DefaultForInputFlowPin, FlowPinType = "InstancedStruct")) struct FFlowDataPinInputProperty_InstancedStruct : public FFlowDataPinOutputProperty_InstancedStruct { GENERATED_BODY() FFlowDataPinInputProperty_InstancedStruct() : Super() { } FFlowDataPinInputProperty_InstancedStruct(const FInstancedStruct& InValue) : Super(InValue) { } + + FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "Object - Input Flow DataPin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "Object")) +USTRUCT(BlueprintType, DisplayName = "Object - Input Flow DataPin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Object")) struct FFlowDataPinInputProperty_Object : public FFlowDataPinOutputProperty_Object { GENERATED_BODY() FFlowDataPinInputProperty_Object() : Super() { } FFlowDataPinInputProperty_Object(UObject* InValue, UClass* InClassFilter) : Super(InValue, InClassFilter) { } + + FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "Class - Input Flow DataPin Property", meta = (Hidden, DefaultForInputFlowPin, FlowPinType = "Class")) +USTRUCT(BlueprintType, DisplayName = "Class - Input Flow DataPin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Class")) struct FFlowDataPinInputProperty_Class : public FFlowDataPinOutputProperty_Class { GENERATED_BODY() FFlowDataPinInputProperty_Class() : Super() { } FFlowDataPinInputProperty_Class(const FSoftClassPath& InValue, UClass* InClassFilter) : Super(InValue, InClassFilter) { } + + FLOW_API virtual bool IsInputProperty() const override { return true; } }; diff --git a/Source/Flow/Public/Types/FlowDataPinResults.h b/Source/Flow/Public/Types/FlowDataPinResults.h index 0d2e7f61e..838a57ef9 100644 --- a/Source/Flow/Public/Types/FlowDataPinResults.h +++ b/Source/Flow/Public/Types/FlowDataPinResults.h @@ -51,6 +51,7 @@ struct FFlowDataPinResult_Bool : public FFlowDataPinResult public: FLOW_API FFlowDataPinResult_Bool() { } + FLOW_API FFlowDataPinResult_Bool(EFlowDataPinResolveResult InResult) : Super(InResult) { } FLOW_API FFlowDataPinResult_Bool(bool InValue) : Super(EFlowDataPinResolveResult::Success) , Value(InValue) @@ -70,6 +71,7 @@ struct FFlowDataPinResult_Int : public FFlowDataPinResult public: FLOW_API FFlowDataPinResult_Int() { } + FLOW_API FFlowDataPinResult_Int(EFlowDataPinResolveResult InResult) : Super(InResult) { } FLOW_API FFlowDataPinResult_Int(int64 InValue) : Super(EFlowDataPinResolveResult::Success) , Value(InValue) @@ -89,6 +91,7 @@ struct FFlowDataPinResult_Float : public FFlowDataPinResult public: FLOW_API FFlowDataPinResult_Float() { } + FLOW_API FFlowDataPinResult_Float(EFlowDataPinResolveResult InResult) : Super(InResult) { } FLOW_API FFlowDataPinResult_Float(double InValue) : Super(EFlowDataPinResolveResult::Success) , Value(InValue) @@ -108,6 +111,7 @@ struct FFlowDataPinResult_Name : public FFlowDataPinResult public: FLOW_API FFlowDataPinResult_Name() { } + FLOW_API FFlowDataPinResult_Name(EFlowDataPinResolveResult InResult) : Super(InResult) { } FLOW_API FFlowDataPinResult_Name(const FName& InValue) : Super(EFlowDataPinResolveResult::Success) , Value(InValue) @@ -131,6 +135,7 @@ struct FFlowDataPinResult_String : public FFlowDataPinResult public: FLOW_API FFlowDataPinResult_String() { } + FLOW_API FFlowDataPinResult_String(EFlowDataPinResolveResult InResult) : Super(InResult) { } FLOW_API FFlowDataPinResult_String(const FString& InValue) : Super(EFlowDataPinResolveResult::Success) , Value(InValue) @@ -154,6 +159,7 @@ struct FFlowDataPinResult_Text : public FFlowDataPinResult public: FLOW_API FFlowDataPinResult_Text() { } + FLOW_API FFlowDataPinResult_Text(EFlowDataPinResolveResult InResult) : Super(InResult) { } FLOW_API FFlowDataPinResult_Text(const FText& InValue) : Super(EFlowDataPinResolveResult::Success) , Value(InValue) @@ -182,12 +188,12 @@ struct FFlowDataPinResult_Enum : public FFlowDataPinResult public: FLOW_API FFlowDataPinResult_Enum() { } + FLOW_API FFlowDataPinResult_Enum(EFlowDataPinResolveResult InResult) : Super(InResult) { } FLOW_API FFlowDataPinResult_Enum(const FName& InValue, UEnum* InEnumClass) : Super(EFlowDataPinResolveResult::Success) , Value(InValue) , EnumClass(InEnumClass) { } - FLOW_API explicit FFlowDataPinResult_Enum(EFlowDataPinResolveResult InResult) : Super(InResult) { } FLOW_API explicit FFlowDataPinResult_Enum(uint8 InEnumAsIntValue, UEnum& InEnumClass) : Super(EFlowDataPinResolveResult::Success) , Value() @@ -252,6 +258,7 @@ struct FFlowDataPinResult_Vector : public FFlowDataPinResult public: FLOW_API FFlowDataPinResult_Vector() { } + FLOW_API FFlowDataPinResult_Vector(EFlowDataPinResolveResult InResult) : Super(InResult) { } FLOW_API FFlowDataPinResult_Vector(const FVector& InValue) : Super(EFlowDataPinResolveResult::Success) , Value(InValue) @@ -271,6 +278,7 @@ struct FFlowDataPinResult_Rotator : public FFlowDataPinResult public: FLOW_API FFlowDataPinResult_Rotator() { } + FLOW_API FFlowDataPinResult_Rotator(EFlowDataPinResolveResult InResult) : Super(InResult) { } FLOW_API FFlowDataPinResult_Rotator(const FRotator& InValue) : Super(EFlowDataPinResolveResult::Success) , Value(InValue) @@ -290,6 +298,7 @@ struct FFlowDataPinResult_Transform : public FFlowDataPinResult public: FLOW_API FFlowDataPinResult_Transform() { } + FLOW_API FFlowDataPinResult_Transform(EFlowDataPinResolveResult InResult) : Super(InResult) { } FLOW_API FFlowDataPinResult_Transform(const FTransform& InValue) : Super(EFlowDataPinResolveResult::Success) , Value(InValue) @@ -309,6 +318,7 @@ struct FFlowDataPinResult_GameplayTag : public FFlowDataPinResult public: FLOW_API FFlowDataPinResult_GameplayTag() { } + FLOW_API FFlowDataPinResult_GameplayTag(EFlowDataPinResolveResult InResult) : Super(InResult) { } FLOW_API FFlowDataPinResult_GameplayTag(const FGameplayTag& InValue) : Super(EFlowDataPinResolveResult::Success) , Value(InValue) @@ -328,6 +338,7 @@ struct FFlowDataPinResult_GameplayTagContainer : public FFlowDataPinResult public: FLOW_API FFlowDataPinResult_GameplayTagContainer() { } + FLOW_API FFlowDataPinResult_GameplayTagContainer(EFlowDataPinResolveResult InResult) : Super(InResult) { } FLOW_API FFlowDataPinResult_GameplayTagContainer(const FGameplayTagContainer& InValue) : Super(EFlowDataPinResolveResult::Success) , Value(InValue) @@ -347,6 +358,7 @@ struct FFlowDataPinResult_InstancedStruct : public FFlowDataPinResult public: FLOW_API FFlowDataPinResult_InstancedStruct() { } + FLOW_API FFlowDataPinResult_InstancedStruct(EFlowDataPinResolveResult InResult) : Super(InResult) { } FLOW_API FFlowDataPinResult_InstancedStruct(const FInstancedStruct& InValue) : Super(EFlowDataPinResolveResult::Success) , Value(InValue) @@ -366,6 +378,7 @@ struct FFlowDataPinResult_Object : public FFlowDataPinResult public: FLOW_API FFlowDataPinResult_Object() { } + FLOW_API FFlowDataPinResult_Object(EFlowDataPinResolveResult InResult) : Super(InResult) { } FLOW_API FFlowDataPinResult_Object(UObject* InValue); FLOW_API void SetValueFromPropertyWrapper(const FFlowDataPinOutputProperty_Object& InPropertyWrapper); @@ -393,6 +406,7 @@ struct FFlowDataPinResult_Class : public FFlowDataPinResult public: FLOW_API FFlowDataPinResult_Class() { } + FLOW_API FFlowDataPinResult_Class(EFlowDataPinResolveResult InResult) : Super(InResult) { } FLOW_API FFlowDataPinResult_Class(const FSoftClassPath& InValuePath); FLOW_API FFlowDataPinResult_Class(UClass* InValueClass); diff --git a/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp b/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp index 32c270716..891550b15 100644 --- a/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp +++ b/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp @@ -5,6 +5,7 @@ #include "Asset/FlowDiffControl.h" #include "Nodes/FlowNodeBase.h" #include "EdGraph/EdGraph.h" +#include "Runtime/Launch/Resources/Version.h" #include "Graph/Nodes/FlowGraphNode.h" #include "SBlueprintDiff.h" diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowNamedDataPinOutputPropertyCustomization.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowNamedDataPinOutputPropertyCustomization.cpp index c9ed8c249..194def98c 100644 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowNamedDataPinOutputPropertyCustomization.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowNamedDataPinOutputPropertyCustomization.cpp @@ -2,11 +2,11 @@ #include "DetailCustomizations/FlowNamedDataPinOutputPropertyCustomization.h" -FText FFlowNamedDataPinOutputPropertyCustomization::BuildHeaderText() const +FText FFlowNamedDataPinPropertyCustomization::BuildHeaderText() const { - if (const FFlowNamedDataPinOutputProperty* FlowNamedDataPinOutputProperty = IFlowExtendedPropertyTypeCustomization::TryGetTypedStructValue(StructPropertyHandle)) + if (const FFlowNamedDataPinProperty* FlowNamedDataPinProperty = IFlowExtendedPropertyTypeCustomization::TryGetTypedStructValue(StructPropertyHandle)) { - return FlowNamedDataPinOutputProperty->BuildHeaderText(); + return FlowNamedDataPinProperty->BuildHeaderText(); } return Super::BuildHeaderText(); diff --git a/Source/FlowEditor/Private/FlowEditorModule.cpp b/Source/FlowEditor/Private/FlowEditorModule.cpp index 5f0893b1c..94582f2f7 100644 --- a/Source/FlowEditor/Private/FlowEditorModule.cpp +++ b/Source/FlowEditor/Private/FlowEditorModule.cpp @@ -233,7 +233,7 @@ void FFlowEditorModule::RegisterDetailCustomizations() RegisterCustomClassLayout(UFlowNode_SubGraph::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_SubGraphDetails::MakeInstance)); RegisterCustomStructLayout(*FFlowActorOwnerComponentRef::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowActorOwnerComponentRefCustomization::MakeInstance)); RegisterCustomStructLayout(*FFlowPin::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowPinCustomization::MakeInstance)); - RegisterCustomStructLayout(*FFlowNamedDataPinOutputProperty::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowNamedDataPinOutputPropertyCustomization::MakeInstance)); + RegisterCustomStructLayout(*FFlowNamedDataPinProperty::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowNamedDataPinPropertyCustomization::MakeInstance)); RegisterCustomStructLayout(*FFlowDataPinOutputProperty_Bool::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinOutputProperty_BoolCustomization::MakeInstance)); RegisterCustomStructLayout(*FFlowDataPinOutputProperty_Int64::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinOutputProperty_Int64Customization::MakeInstance)); diff --git a/Source/FlowEditor/Private/Graph/Widgets/SGraphEditorActionMenuFlow.cpp b/Source/FlowEditor/Private/Graph/Widgets/SGraphEditorActionMenuFlow.cpp index d429228c6..4bf786b77 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SGraphEditorActionMenuFlow.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SGraphEditorActionMenuFlow.cpp @@ -14,6 +14,7 @@ #include "Templates/Casts.h" #include "Types/SlateStructs.h" #include "Widgets/Layout/SBox.h" +#include "Runtime/Launch/Resources/Version.h" SGraphEditorActionMenuFlow::~SGraphEditorActionMenuFlow() { diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowNamedDataPinOutputPropertyCustomization.h b/Source/FlowEditor/Public/DetailCustomizations/FlowNamedDataPinOutputPropertyCustomization.h index f24e3192e..f63e1e18b 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowNamedDataPinOutputPropertyCustomization.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowNamedDataPinOutputPropertyCustomization.h @@ -7,12 +7,12 @@ #include "Types/FlowDataPinProperties.h" // Details customization for FFlowPin -class FFlowNamedDataPinOutputPropertyCustomization : public IFlowExtendedPropertyTypeCustomization +class FFlowNamedDataPinPropertyCustomization : public IFlowExtendedPropertyTypeCustomization { typedef IFlowExtendedPropertyTypeCustomization Super; public: - static TSharedRef MakeInstance() { return MakeShareable(new FFlowNamedDataPinOutputPropertyCustomization()); } + static TSharedRef MakeInstance() { return MakeShareable(new FFlowNamedDataPinPropertyCustomization()); } protected: diff --git a/Source/FlowEditor/Public/FlowEditorModule.h b/Source/FlowEditor/Public/FlowEditorModule.h index 018bfd314..1c0f09890 100644 --- a/Source/FlowEditor/Public/FlowEditorModule.h +++ b/Source/FlowEditor/Public/FlowEditorModule.h @@ -8,6 +8,7 @@ #include "PropertyEditorDelegates.h" #include "Toolkits/AssetEditorToolkit.h" #include "Toolkits/IToolkit.h" +#include "Toolkits/AssetEditorToolkit.h" class FSlateStyleSet; class FToolBarBuilder; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphConnectionDrawingPolicy.h b/Source/FlowEditor/Public/Graph/FlowGraphConnectionDrawingPolicy.h index d38f55d85..93dd074bd 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphConnectionDrawingPolicy.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphConnectionDrawingPolicy.h @@ -4,6 +4,7 @@ #include "ConnectionDrawingPolicy.h" #include "EdGraphUtilities.h" +#include "Runtime/Launch/Resources/Version.h" UENUM() enum class EFlowConnectionDrawType : uint8 diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h index c910d60d4..085b1fe47 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h @@ -4,6 +4,7 @@ #include "GraphEditor.h" #include "Widgets/DeclarativeSyntaxSupport.h" +#include "Runtime/Launch/Resources/Version.h" #include "FlowGraph.h" diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h index c90c269ea..493ad45db 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h @@ -5,6 +5,8 @@ #include "EdGraph/EdGraphSchema.h" #include "Runtime/Launch/Resources/Version.h" #include "Templates/SubclassOf.h" +#include "Runtime/Launch/Resources/Version.h" + #include "FlowGraphSchema.generated.h" class UFlowAsset; From a1c301b6aef12aa8c7d9fcf67fe0a0448eb27a74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Wed, 20 Aug 2025 18:55:21 +0200 Subject: [PATCH 362/485] OnNodeDoubleClicked logic moved to the Graph Node itself, allowing to override the default logic --- .../Public/Nodes/Graph/FlowNode_SubGraph.h | 2 + .../Private/Graph/FlowGraphEditor.cpp | 52 ++----------------- .../Private/Graph/Nodes/FlowGraphNode.cpp | 40 ++++++++++++++ .../Graph/Nodes/FlowGraphNode_SubGraph.cpp | 13 +++++ .../Public/Graph/Nodes/FlowGraphNode.h | 3 ++ .../Graph/Nodes/FlowGraphNode_SubGraph.h | 4 ++ 6 files changed, 66 insertions(+), 48 deletions(-) diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h b/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h index b116d0332..5f7f89f0d 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h @@ -14,6 +14,8 @@ UCLASS(NotBlueprintable, meta = (DisplayName = "Sub Graph")) class FLOW_API UFlowNode_SubGraph : public UFlowNode, public IFlowDataPinGeneratorNodeInterface { GENERATED_UCLASS_BODY() + +public: friend class UFlowAsset; friend class FFlowNode_SubGraphDetails; friend class UFlowSubsystem; diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index 19066bd05..ccdd093fb 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -4,10 +4,8 @@ #include "Asset/FlowAssetEditor.h" #include "FlowEditorCommands.h" -#include "Graph/FlowGraphEditorSettings.h" #include "Graph/FlowGraphSchema_Actions.h" #include "Graph/Nodes/FlowGraphNode.h" -#include "Nodes/Graph/FlowNode_SubGraph.h" #include "Debugger/FlowDebuggerSubsystem.h" @@ -688,7 +686,7 @@ void SFlowGraphEditor::PasteNodesHere(const FVector2D& Location) FlowGraph->LockUpdates(); const TArray PasteTargetNodes = DerivePasteTargetNodesFromSelectedNodes(); - if (Algo::AnyOf(PasteTargetNodes, [](UFlowGraphNode* Node) { return Node && !Node->SubNodes.IsEmpty(); })) + if (Algo::AnyOf(PasteTargetNodes, [](const UFlowGraphNode* Node) { return Node && !Node->SubNodes.IsEmpty(); })) { checkf(PasteTargetNodes.Num() <= 1, TEXT("This should be enforced in CanPasteNodes()")); } @@ -862,7 +860,7 @@ bool SFlowGraphEditor::CanPasteNodes() const // Disallow paste when multiple target nodes are selected, and if there are subnodes involved. const TArray PasteTargetNodes = DerivePasteTargetNodesFromSelectedNodes(); - const bool bHasSubNodes = Algo::AnyOf(PasteTargetNodes, [](UFlowGraphNode* Node) { return Node && !Node->SubNodes.IsEmpty(); }); + const bool bHasSubNodes = Algo::AnyOf(PasteTargetNodes, [](const UFlowGraphNode* Node) { return Node && !Node->SubNodes.IsEmpty(); }); if (bHasSubNodes && PasteTargetNodes.Num() > 1) { @@ -976,51 +974,9 @@ bool SFlowGraphEditor::CanDuplicateNodes() const void SFlowGraphEditor::OnNodeDoubleClicked(class UEdGraphNode* Node) const { - UFlowNodeBase* FlowNodeBase = Cast(Node)->GetFlowNodeBase(); - UFlowNode* FlowNode = Cast(FlowNodeBase); - - if (IsValid(FlowNodeBase)) + if (const UFlowGraphNode* FlowGraphNode = Cast(Node)) { - if (UFlowGraphEditorSettings::Get()->NodeDoubleClickTarget == EFlowNodeDoubleClickTarget::NodeDefinition) - { - Node->JumpToDefinition(); - } - else - { - FString AssetPath; - UObject* AssetToEdit = nullptr; - - if (FlowNode) - { - AssetPath = FlowNode->GetAssetPath(); - AssetToEdit = FlowNode->GetAssetToEdit(); - } - - if (!AssetPath.IsEmpty()) - { - GEditor->GetEditorSubsystem()->OpenEditorForAsset(AssetPath); - } - else if (AssetToEdit) - { - GEditor->GetEditorSubsystem()->OpenEditorForAsset(AssetToEdit); - - if (IsPIE()) - { - if (UFlowNode_SubGraph* SubGraphNode = Cast(FlowNode)) - { - const TWeakObjectPtr SubFlowInstance = SubGraphNode->GetFlowAsset()->GetFlowInstance(SubGraphNode); - if (SubFlowInstance.IsValid()) - { - SubGraphNode->GetFlowAsset()->GetTemplateAsset()->SetInspectedInstance(SubFlowInstance->GetDisplayName()); - } - } - } - } - else if (UFlowGraphEditorSettings::Get()->NodeDoubleClickTarget == EFlowNodeDoubleClickTarget::PrimaryAssetOrNodeDefinition) - { - Node->JumpToDefinition(); - } - } + FlowGraphNode->OnNodeDoubleClicked(); } } diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 8edd89c8d..50056584d 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -836,6 +836,46 @@ bool UFlowGraphNode::SupportsCommentBubble() const return Super::SupportsCommentBubble(); } +void UFlowGraphNode::OnNodeDoubleClicked() const +{ + UFlowNodeBase* FlowNodeBase = GetFlowNodeBase(); + if (IsValid(FlowNodeBase)) + { + if (UFlowGraphEditorSettings::Get()->NodeDoubleClickTarget == EFlowNodeDoubleClickTarget::NodeDefinition) + { + JumpToDefinition(); + } + else + { + FString AssetPath; + UObject* AssetToEdit = nullptr; + if (UFlowNode* FlowNode = Cast(FlowNodeBase)) + { + AssetPath = FlowNode->GetAssetPath(); + AssetToEdit = FlowNode->GetAssetToEdit(); + } + + if (!AssetPath.IsEmpty()) + { + GEditor->GetEditorSubsystem()->OpenEditorForAsset(AssetPath); + } + else if (AssetToEdit) + { + GEditor->GetEditorSubsystem()->OpenEditorForAsset(AssetToEdit); + + if (GEditor->PlayWorld != nullptr) + { + OnNodeDoubleClickedInPIE(); + } + } + else if (UFlowGraphEditorSettings::Get()->NodeDoubleClickTarget == EFlowNodeDoubleClickTarget::PrimaryAssetOrNodeDefinition) + { + JumpToDefinition(); + } + } + } +} + void UFlowGraphNode::CreateInputPin(const FFlowPin& FlowPin, const int32 Index /*= INDEX_NONE*/) { if (FlowPin.PinName.IsNone()) diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_SubGraph.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_SubGraph.cpp index 26db97ce6..4030ba39b 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_SubGraph.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_SubGraph.cpp @@ -3,6 +3,7 @@ #include "Graph/Nodes/FlowGraphNode_SubGraph.h" #include "Graph/Widgets/SFlowGraphNode_SubGraph.h" +#include "FlowAsset.h" #include "Nodes/Graph/FlowNode_SubGraph.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraphNode_SubGraph) @@ -17,3 +18,15 @@ TSharedPtr UFlowGraphNode_SubGraph::CreateVisualWidget() { return SNew(SFlowGraphNode_SubGraph, this); } + +void UFlowGraphNode_SubGraph::OnNodeDoubleClickedInPIE() const +{ + UFlowNode_SubGraph* SubGraphNode = Cast(GetFlowNodeBase()); + ensureAlways(SubGraphNode); + + const TWeakObjectPtr SubFlowInstance = GetFlowAsset()->GetFlowInstance(SubGraphNode); + if (SubFlowInstance.IsValid()) + { + SubGraphNode->GetFlowAsset()->GetTemplateAsset()->SetInspectedInstance(SubFlowInstance->GetDisplayName()); + } +} diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h index cabe0854d..a9d715862 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h @@ -156,6 +156,9 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode virtual bool SupportsCommentBubble() const override; // -- + virtual void OnNodeDoubleClicked() const; + virtual void OnNodeDoubleClickedInPIE() const {}; + /** check if node has any errors, used for assigning colors on graph */ virtual bool HasErrors() const; diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_SubGraph.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_SubGraph.h index ae9f1c682..eac30e74e 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_SubGraph.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_SubGraph.h @@ -13,4 +13,8 @@ class FLOWEDITOR_API UFlowGraphNode_SubGraph : public UFlowGraphNode // UEdGraphNode virtual TSharedPtr CreateVisualWidget() override; // -- + + // UFlowGraphNode + virtual void OnNodeDoubleClickedInPIE() const override; + // -- }; From 61f5cedbe4460ac60cff9ade77fc44978f53f16e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Wed, 20 Aug 2025 19:07:08 +0200 Subject: [PATCH 363/485] reverted pull request change as this breaks compilation in UE 5.5 and above --- Source/Flow/Public/Types/FlowDataPinProperties.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Source/Flow/Public/Types/FlowDataPinProperties.h b/Source/Flow/Public/Types/FlowDataPinProperties.h index 86345025d..94f39d050 100644 --- a/Source/Flow/Public/Types/FlowDataPinProperties.h +++ b/Source/Flow/Public/Types/FlowDataPinProperties.h @@ -9,11 +9,12 @@ #include "Runtime/Launch/Resources/Version.h" #include "UObject/Class.h" -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 4 -#include "StructUtils/InstancedStruct.h" -#else +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 5 #include "InstancedStruct.h" +#else +#include "StructUtils/InstancedStruct.h" #endif + #include "FlowDataPinProperties.generated.h" class FStructProperty; From b9ea7292c3fa574622614e068c310a4670b0a9e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Wed, 20 Aug 2025 19:19:36 +0200 Subject: [PATCH 364/485] cosmetic include cleanup some includes got duplicated, added in two independent pull requests --- Source/Flow/Public/AddOns/FlowNodeAddOn.h | 2 +- .../Interfaces/FlowDataPinPropertyProviderInterface.h | 3 +-- Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp | 7 +++---- .../Private/Graph/Widgets/SGraphEditorActionMenuFlow.cpp | 2 +- Source/FlowEditor/Public/FlowEditorModule.h | 1 - Source/FlowEditor/Public/Graph/FlowGraphEditor.h | 2 +- Source/FlowEditor/Public/Graph/FlowGraphSchema.h | 1 - 7 files changed, 7 insertions(+), 11 deletions(-) diff --git a/Source/Flow/Public/AddOns/FlowNodeAddOn.h b/Source/Flow/Public/AddOns/FlowNodeAddOn.h index 30a9b63d0..47fb5ab4c 100644 --- a/Source/Flow/Public/AddOns/FlowNodeAddOn.h +++ b/Source/Flow/Public/AddOns/FlowNodeAddOn.h @@ -65,7 +65,7 @@ class UFlowNodeAddOn : public UFlowNodeBase // UFlowNodeAddOn - //// The FlowNode that contains this AddOn + // The FlowNode that contains this AddOn // (accessible only when initialized, runtime only) UFUNCTION(BlueprintCallable, BlueprintPure, Category = "FlowNodeAddon", DisplayName = "Get Flow Node") FLOW_API UFlowNode* GetFlowNode() const; diff --git a/Source/Flow/Public/Interfaces/FlowDataPinPropertyProviderInterface.h b/Source/Flow/Public/Interfaces/FlowDataPinPropertyProviderInterface.h index 63789d62d..e1e62c465 100644 --- a/Source/Flow/Public/Interfaces/FlowDataPinPropertyProviderInterface.h +++ b/Source/Flow/Public/Interfaces/FlowDataPinPropertyProviderInterface.h @@ -3,6 +3,7 @@ #pragma once #include "Runtime/Launch/Resources/Version.h" +#include "UObject/Interface.h" #if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 4 #include "StructUtils/InstancedStruct.h" @@ -10,8 +11,6 @@ #include "InstancedStruct.h" #endif -#include "UObject/Interface.h" - #include "FlowDataPinPropertyProviderInterface.generated.h" struct FFlowDataPinProperty; diff --git a/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp b/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp index 891550b15..370ff0bd9 100644 --- a/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp +++ b/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp @@ -3,15 +3,14 @@ #include "Asset/FlowObjectDiff.h" #include "Asset/FlowDiffControl.h" +#include "Graph/Nodes/FlowGraphNode.h" #include "Nodes/FlowNodeBase.h" + +#include "DiffResults.h" #include "EdGraph/EdGraph.h" #include "Runtime/Launch/Resources/Version.h" - -#include "Graph/Nodes/FlowGraphNode.h" #include "SBlueprintDiff.h" -#include "DiffResults.h" - ///////////////////////////////////////////////////////////////////////////// /// FFlowNodePropertyDiff FFlowObjectDiffArgs::FFlowObjectDiffArgs(TWeakPtr InFlowNodeDiff, const FSingleObjectDiffEntry& InPropertyDiff) diff --git a/Source/FlowEditor/Private/Graph/Widgets/SGraphEditorActionMenuFlow.cpp b/Source/FlowEditor/Private/Graph/Widgets/SGraphEditorActionMenuFlow.cpp index 4bf786b77..cd64c76e5 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SGraphEditorActionMenuFlow.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SGraphEditorActionMenuFlow.cpp @@ -9,12 +9,12 @@ #include "HAL/PlatformCrt.h" #include "Layout/Margin.h" #include "Misc/Attribute.h" +#include "Runtime/Launch/Resources/Version.h" #include "SGraphActionMenu.h" #include "Styling/AppStyle.h" #include "Templates/Casts.h" #include "Types/SlateStructs.h" #include "Widgets/Layout/SBox.h" -#include "Runtime/Launch/Resources/Version.h" SGraphEditorActionMenuFlow::~SGraphEditorActionMenuFlow() { diff --git a/Source/FlowEditor/Public/FlowEditorModule.h b/Source/FlowEditor/Public/FlowEditorModule.h index 1c0f09890..018bfd314 100644 --- a/Source/FlowEditor/Public/FlowEditorModule.h +++ b/Source/FlowEditor/Public/FlowEditorModule.h @@ -8,7 +8,6 @@ #include "PropertyEditorDelegates.h" #include "Toolkits/AssetEditorToolkit.h" #include "Toolkits/IToolkit.h" -#include "Toolkits/AssetEditorToolkit.h" class FSlateStyleSet; class FToolBarBuilder; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h index 085b1fe47..331ebb1e1 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h @@ -3,8 +3,8 @@ #pragma once #include "GraphEditor.h" -#include "Widgets/DeclarativeSyntaxSupport.h" #include "Runtime/Launch/Resources/Version.h" +#include "Widgets/DeclarativeSyntaxSupport.h" #include "FlowGraph.h" diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h index 493ad45db..070f82151 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h @@ -5,7 +5,6 @@ #include "EdGraph/EdGraphSchema.h" #include "Runtime/Launch/Resources/Version.h" #include "Templates/SubclassOf.h" -#include "Runtime/Launch/Resources/Version.h" #include "FlowGraphSchema.generated.h" From 2a0973c93ec7cdbba96fb01a5004b2c1552fdd79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Thu, 21 Aug 2025 16:56:03 +0200 Subject: [PATCH 365/485] bumped plugin version --- Flow.uplugin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.uplugin b/Flow.uplugin index 297f648a9..89f9c3c30 100644 --- a/Flow.uplugin +++ b/Flow.uplugin @@ -1,6 +1,6 @@ { "FileVersion" : 3, - "Version" : 2.1, + "Version" : 2.2, "FriendlyName" : "Flow", "Description" : "Design-agnostic node editor for scripting game’s flow.", "Category" : "Gameplay", From 2a0cc73258aa82fe35b0897b6f22af34aa436c60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Thu, 21 Aug 2025 17:07:11 +0200 Subject: [PATCH 366/485] Removed support for UE 5.4 --- Flow.uplugin | 4 ---- Source/Flow/Flow.Build.cs | 3 +-- Source/Flow/Private/FlowAsset.cpp | 8 +------- Source/Flow/Private/Nodes/FlowPin.cpp | 6 ------ .../FlowDataPinPropertyProviderInterface.h | 9 +-------- Source/Flow/Public/Types/FlowDataPinProperties.h | 12 ++---------- Source/Flow/Public/Types/FlowDataPinResults.h | 9 +-------- Source/FlowEditor/FlowEditor.Build.cs | 1 - Source/FlowEditor/Private/Find/FindInFlow.cpp | 3 --- 9 files changed, 6 insertions(+), 49 deletions(-) diff --git a/Flow.uplugin b/Flow.uplugin index 89f9c3c30..d0e0e654b 100644 --- a/Flow.uplugin +++ b/Flow.uplugin @@ -42,10 +42,6 @@ { "Name": "EngineAssetDefinitions", "Enabled": true - }, - { - "Name": "StructUtils", - "Enabled": true } ] } \ No newline at end of file diff --git a/Source/Flow/Flow.Build.cs b/Source/Flow/Flow.Build.cs index 7feac77b2..74df20a4c 100644 --- a/Source/Flow/Flow.Build.cs +++ b/Source/Flow/Flow.Build.cs @@ -10,8 +10,7 @@ public Flow(ReadOnlyTargetRules target) : base(target) PublicDependencyModuleNames.AddRange(new[] { - "LevelSequence", - "StructUtils", + "LevelSequence" }); PrivateDependencyModuleNames.AddRange(new[] diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 14b971f4f..521ed3e36 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -272,15 +272,9 @@ bool UFlowAsset::CanFlowAssetReferenceFlowNode(const UClass& FlowNodeClass, FTex return false; } + // Confirm plugin reference restrictions are being respected FAssetReferenceFilterContext AssetReferenceFilterContext; - -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 5 - AssetReferenceFilterContext.ReferencingAssets.Add(FAssetData(this)); -#else AssetReferenceFilterContext.AddReferencingAsset(FAssetData(this)); -#endif - - // Confirm plugin reference restrictions are being respected const TSharedPtr FlowAssetReferenceFilter = GEditor->MakeAssetReferenceFilter(AssetReferenceFilterContext); if (FlowAssetReferenceFilter.IsValid()) { diff --git a/Source/Flow/Private/Nodes/FlowPin.cpp b/Source/Flow/Private/Nodes/FlowPin.cpp index 741a63e1c..69de7d244 100644 --- a/Source/Flow/Private/Nodes/FlowPin.cpp +++ b/Source/Flow/Private/Nodes/FlowPin.cpp @@ -6,13 +6,7 @@ #include "GameplayTagContainer.h" #include "Misc/DateTime.h" #include "Misc/MessageDialog.h" -#include "Runtime/Launch/Resources/Version.h" - -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 5 -#include "InstancedStruct.h" -#else #include "StructUtils/InstancedStruct.h" -#endif #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowPin) diff --git a/Source/Flow/Public/Interfaces/FlowDataPinPropertyProviderInterface.h b/Source/Flow/Public/Interfaces/FlowDataPinPropertyProviderInterface.h index e1e62c465..e91924248 100644 --- a/Source/Flow/Public/Interfaces/FlowDataPinPropertyProviderInterface.h +++ b/Source/Flow/Public/Interfaces/FlowDataPinPropertyProviderInterface.h @@ -2,15 +2,8 @@ #pragma once -#include "Runtime/Launch/Resources/Version.h" -#include "UObject/Interface.h" - -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 4 #include "StructUtils/InstancedStruct.h" -#else -#include "InstancedStruct.h" -#endif - +#include "UObject/Interface.h" #include "FlowDataPinPropertyProviderInterface.generated.h" struct FFlowDataPinProperty; diff --git a/Source/Flow/Public/Types/FlowDataPinProperties.h b/Source/Flow/Public/Types/FlowDataPinProperties.h index 94f39d050..a40a0e24f 100644 --- a/Source/Flow/Public/Types/FlowDataPinProperties.h +++ b/Source/Flow/Public/Types/FlowDataPinProperties.h @@ -2,19 +2,11 @@ #pragma once -#include "Nodes/FlowPin.h" - #include "GameplayTagContainer.h" -#include "Kismet/BlueprintFunctionLibrary.h" -#include "Runtime/Launch/Resources/Version.h" -#include "UObject/Class.h" - -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 5 -#include "InstancedStruct.h" -#else #include "StructUtils/InstancedStruct.h" -#endif +#include "UObject/Class.h" +#include "Nodes/FlowPin.h" #include "FlowDataPinProperties.generated.h" class FStructProperty; diff --git a/Source/Flow/Public/Types/FlowDataPinResults.h b/Source/Flow/Public/Types/FlowDataPinResults.h index 838a57ef9..d186d6ea9 100644 --- a/Source/Flow/Public/Types/FlowDataPinResults.h +++ b/Source/Flow/Public/Types/FlowDataPinResults.h @@ -2,17 +2,10 @@ #pragma once -#include "Types/FlowPinEnums.h" - #include "GameplayTagContainer.h" -#include "Runtime/Launch/Resources/Version.h" - -#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 5 -#include "InstancedStruct.h" -#else #include "StructUtils/InstancedStruct.h" -#endif +#include "Types/FlowPinEnums.h" #include "FlowDataPinResults.generated.h" struct FInstancedStruct; diff --git a/Source/FlowEditor/FlowEditor.Build.cs b/Source/FlowEditor/FlowEditor.Build.cs index 43e0cb3d1..86bf67a09 100644 --- a/Source/FlowEditor/FlowEditor.Build.cs +++ b/Source/FlowEditor/FlowEditor.Build.cs @@ -61,7 +61,6 @@ public FlowEditor(ReadOnlyTargetRules target) : base(target) "Slate", "SlateCore", "SourceControl", - "StructUtils", "ToolMenus", "UnrealEd" }); diff --git a/Source/FlowEditor/Private/Find/FindInFlow.cpp b/Source/FlowEditor/Private/Find/FindInFlow.cpp index 59e4dfdb2..d098da8d5 100644 --- a/Source/FlowEditor/Private/Find/FindInFlow.cpp +++ b/Source/FlowEditor/Private/Find/FindInFlow.cpp @@ -219,9 +219,6 @@ void SFindInFlow::Construct( const FArguments& InArgs, TSharedPtr Date: Thu, 21 Aug 2025 17:12:10 +0200 Subject: [PATCH 367/485] included added to a proper header --- Source/Flow/Public/Types/FlowDataPinBlueprintLibrary.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Flow/Public/Types/FlowDataPinBlueprintLibrary.h b/Source/Flow/Public/Types/FlowDataPinBlueprintLibrary.h index f2838fc3e..7183d9146 100644 --- a/Source/Flow/Public/Types/FlowDataPinBlueprintLibrary.h +++ b/Source/Flow/Public/Types/FlowDataPinBlueprintLibrary.h @@ -2,9 +2,10 @@ #pragma once +#include "Kismet/BlueprintFunctionLibrary.h" + #include "FlowDataPinProperties.h" #include "FlowDataPinResults.h" - #include "FlowDataPinBlueprintLibrary.generated.h" // Auto-cast operators for blueprint to their inner types From 1b6d0e522504ffa4b0baf519bcd72d0b0aff8123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Fri, 26 Sep 2025 20:03:46 +0200 Subject: [PATCH 368/485] UE 5.7 support --- Source/FlowEditor/Public/Asset/FlowDiffControl.h | 5 +++++ Source/FlowEditor/Public/Asset/FlowObjectDiff.h | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/Source/FlowEditor/Public/Asset/FlowDiffControl.h b/Source/FlowEditor/Public/Asset/FlowDiffControl.h index 3c750564f..5240636ff 100644 --- a/Source/FlowEditor/Public/Asset/FlowDiffControl.h +++ b/Source/FlowEditor/Public/Asset/FlowDiffControl.h @@ -4,7 +4,12 @@ #include "Asset/FlowObjectDiff.h" #include "DiffResults.h" + +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 7 #include "Editor/Kismet/Private/DiffControl.h" +#else +#include "Editor/Kismet/Internal/DiffControl.h" +#endif class FBlueprintDifferenceTreeEntry; class SFlowDiff; diff --git a/Source/FlowEditor/Public/Asset/FlowObjectDiff.h b/Source/FlowEditor/Public/Asset/FlowObjectDiff.h index 8af6a47a3..d1896fabe 100644 --- a/Source/FlowEditor/Public/Asset/FlowObjectDiff.h +++ b/Source/FlowEditor/Public/Asset/FlowObjectDiff.h @@ -2,7 +2,11 @@ #pragma once +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 7 #include "Editor/Kismet/Private/DiffControl.h" +#else +#include "Editor/Kismet/Internal/DiffControl.h" +#endif class FBlueprintDifferenceTreeEntry; class FDetailsDiff; From cccaa349b1464e55d90d1a6f9a1f7a70d180eb9c Mon Sep 17 00:00:00 2001 From: Numblaze Date: Tue, 7 Oct 2025 18:40:11 +0200 Subject: [PATCH 369/485] Enhance validation in `FlowGraphNode` by adding null checks for `FlowAsset` before accessing inspected instance. (#309) --- Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 50056584d..f041c0280 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -65,9 +65,12 @@ UFlowNodeBase* UFlowGraphNode::GetFlowNodeBase() const { if (const UFlowNode* FlowNode = Cast(NodeInstance)) { - if (const UFlowAsset* InspectedInstance = FlowNode->GetFlowAsset()->GetInspectedInstance()) + if (const UFlowAsset* FlowAsset = FlowNode->GetFlowAsset()) { - return InspectedInstance->GetNode(FlowNode->GetGuid()); + if (const UFlowAsset* InspectedInstance = FlowAsset->GetInspectedInstance()) + { + return InspectedInstance->GetNode(FlowNode->GetGuid()); + } } } From 63525b33c7dbff8be0c798a0692aed5e6f7f3c66 Mon Sep 17 00:00:00 2001 From: DemonViglu <113577565+DemonViglu@users.noreply.github.com> Date: Wed, 8 Oct 2025 00:41:22 +0800 Subject: [PATCH 370/485] A Datapin Logic bug I guess (#306) commit --- Source/Flow/Private/Nodes/FlowNodeBase.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp index 7c2bdcbdb..85420b9ba 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBase.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -1199,13 +1199,12 @@ bool TResolveDataPinWorkingData::TrySetupWorkin if (!FlowNode->TryGetFlowDataPinSupplierDatasForPinName(FlowPin->PinName, PinValueSupplierDatas)) { + // If we could not build the PinValueDataSuppliers array, + // then the pin must be disconnected and have no default value available. + DataPinResult.Result = EFlowDataPinResolveResult::FailedUnconnected; return false; } - // If we could not build the PinValueDataSuppliers array, - // then the pin must be disconnected and have no default value available. - DataPinResult.Result = EFlowDataPinResolveResult::FailedUnconnected; - return true; } From 63d3ffa0a60ea707bf7ea312851403d65ba335a8 Mon Sep 17 00:00:00 2001 From: LindyHopperGT <91915878+LindyHopperGT@users.noreply.github.com> Date: Sun, 11 Jan 2026 13:19:26 -0800 Subject: [PATCH 371/485] Flow graph data pins refactor (#313) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [FlowAssetParams] Initial implementation of FlowAssetParams FlowAssetParams are a new data asset, that is optionally managed by the FlowAsset, sourcing properties from the Start node of the graph. These params can be subclassed to create overrides. At runtime, either the base or a compatible subclass may be used to provide data for start data pins in the flow graph. - Added FlowAssetParams - Added support to FlowAsset to optionally create and sync with FlowAssetParams base - Added AssetDefinition_FlorAssetParams to add 'create child', for creating 'subclassed' params that override some elements - Updated FFlowNamedDataPinProperty to have Guid and other properties used in the FlowAssetParams use-case - Added FlowNamedPropertiesSupplierInterface - Added FlowAssetProviderInterface - FFlowAssetParamsPtr - wrapper class for details customization - Added Params support to FlowComponent & FlowSubsystem Also: - Removed stale references to IFlowOwnerInterface - Merged over some deltas from IFlowExtended/Curated Property Customization * More files for FlowAssetParams * [Flow] Added new Data Pin Property wrapper type and subclasses Each of our existing FFlowDataPinProperty* classes adapted to FFlowDataPinValue* (and details customization) & FFlowDataPinType* classes These new wrappers implement IsInputPin as a configurable and add Array support (in anticipation of Flow Data Pin array support) Added registration subsystem for these types (which registers the standard types) These are not used yet, but they will be later in the refactor * Flow Data Pins Refactor Flow: Incremented UFlowGraph::GraphVersion to 2, created a data migration function (UpgradeAllFlowNodePins) FlowPinType namespace templates for the bulk of the "Supply/Resolve" pipeline support for data pins Updated standard FFlowPinType and FFlowDataPinValue subclasses to use them with the new resolve pipeline Reworked FlowSchema's pin compatibility checks to be more orderly, simpler and data-driven connectivity Created policies for schema connectivity rules for the standard types Updated FFlowNamedDataPinProperty to use FFlowDataPinValue as its property payload (including migrate functions from the old data) Updated FlowDataPinBlueprintLibrary with new auto-converts and functions to support data pin manipulation in blueprint Updated FlowNodeBase with the new Resolve pathway entry points & related refactors Updated FlowNode with the new Supply pathway entry points & related refactors Updated FlowPin to deprecateEnum PinType and add ContainerType (for Array data pins) and PinTypeName (the replacement for PinType Enum) Removed overrides TrySupplyDataPinAs... (now replaced by general version) Removed TrySupplyDataPinAs... variants from the IFlowDataPinValueSupplierInterface (leaving only the general replacement) Ported TryResolveDataPinAs... specialized versions to use the general version internally Adapted uses of TryResolveDataPinAs... to the general version TryResolveDataPin FlowNode (and AddOn) details customizations now inherit from TFlowDataPinValueOwnerCustomization, which adds a RequestRebuild() for rebuilding flow node details in a way that correctly rebuilds the FFlowDataPinValue customizations Refactored FlowAsset's automatic pin generation mechanism to be more clean, simple and work with the new system Details customizations for FFlowDataPinValue & specific subclasses Details customization for IFlowDataPinValueOwnerInterface implementers (via template) Added some details customization rebuild hooks into IFlowDataPinValueOwnerInterface to support FFlowDataPinValue details rebuilding Updated some flow nodes to use new Resolve functions (eg, Log, DefineProperties, Start, FormatText, etc.) Reworked FFlowPinSubsystem's api slightly Created new test classes and assets in FlowGraph_DataPinsTest AIFlow: Added FlowBlackboardEntry subclasses for ActorArray and LocationArray Updated all of the UFlowBlackboardEntryValue classes to use FFlowDataPinValue api's Updated SetBlackboardValues/SetBlackboardValuesOnActor to minimally function, and deprecated them Introduced combined and updated SetBlackboardValuesV2 replacement for the former SetBlackboardValues* nodes Updated GetBlackboardValues to work with the new APIs * removed some commented-out code that slipped in. removed some commented-out code that slipped in. * Missing file? * static analysis fixes, typos fixes, removing unused symbols removed some unused includes, unimplemented methods in customization codes, some unused variables in functions a few more const and static functions and functions parameters * removed leftover after CallOwnerFunction * reverted node category change as this would be confusing for community * Added information on experimental status of this feature. * Preventing instantiating the original class in case users would inherit after this subsystem class. * this change should be submitted separately --------- Co-authored-by: Krzysztof Justyński --- Flow.uplugin | 2 +- Source/Flow/Flow.Build.cs | 3 + Source/Flow/Private/Asset/FlowAssetParams.cpp | 386 +++ .../Private/Asset/FlowAssetParamsTypes.cpp | 11 + .../Private/Asset/FlowAssetParamsUtils.cpp | 119 + Source/Flow/Private/FlowAsset.cpp | 676 ++--- Source/Flow/Private/FlowComponent.cpp | 4 +- .../Private/FlowExecutableActorComponent.cpp | 48 + Source/Flow/Private/FlowPinSubsystem.cpp | 83 + Source/Flow/Private/FlowSubsystem.cpp | 8 +- .../Interfaces/FlowAssetProviderInterface.cpp | 9 + .../Nodes/Actor/FlowNode_ExecuteComponent.cpp | 363 +-- .../Actor/FlowNode_PlayLevelSequence.cpp | 5 +- .../Private/Nodes/Developer/FlowNode_Log.cpp | 54 +- Source/Flow/Private/Nodes/FlowNode.cpp | 369 ++- Source/Flow/Private/Nodes/FlowNodeBase.cpp | 811 ++---- Source/Flow/Private/Nodes/FlowPin.cpp | 331 +-- .../Nodes/Graph/FlowNode_CustomInput.cpp | 4 +- .../Nodes/Graph/FlowNode_CustomOutput.cpp | 4 +- .../Nodes/Graph/FlowNode_DefineProperties.cpp | 61 +- .../Nodes/Graph/FlowNode_FormatText.cpp | 79 +- .../Private/Nodes/Graph/FlowNode_Start.cpp | 220 +- .../Private/Nodes/Graph/FlowNode_SubGraph.cpp | 18 +- .../Private/Nodes/Route/FlowNode_Timer.cpp | 16 +- .../Types/FlowAutoDataPinsWorkingData.cpp | 129 + .../Types/FlowDataPinBlueprintLibrary.cpp | 2358 ++++++++++++++++- .../Private/Types/FlowDataPinProperties.cpp | 193 +- .../Flow/Private/Types/FlowDataPinResults.cpp | 14 +- .../Flow/Private/Types/FlowDataPinValue.cpp | 16 + .../Types/FlowDataPinValuesStandard.cpp | 608 +++++ .../Types/FlowNamedDataPinProperty.cpp | 37 + Source/Flow/Private/Types/FlowPinType.cpp | 100 + .../Types/FlowPinTypeNamesStandard.cpp | 162 ++ .../Private/Types/FlowPinTypesStandard.cpp | 503 ++++ Source/Flow/Public/Asset/FlowAssetParams.h | 111 + .../Flow/Public/Asset/FlowAssetParamsTypes.h | 67 + .../Flow/Public/Asset/FlowAssetParamsUtils.h | 44 + .../Public/Asset/FlowPinTypeMatchPolicy.h | 43 + Source/Flow/Public/FlowAsset.h | 90 +- Source/Flow/Public/FlowComponent.h | 15 +- .../Public/FlowExecutableActorComponent.h | 61 + Source/Flow/Public/FlowPinSubsystem.h | 63 + Source/Flow/Public/FlowSubsystem.h | 5 +- .../Interfaces/FlowAssetProviderInterface.h | 29 + .../FlowContextPinSupplierInterface.h | 3 + .../FlowDataPinGeneratorInterface.h | 25 + .../FlowDataPinGeneratorNodeInterface.h | 25 - .../FlowDataPinPropertyProviderInterface.h | 8 +- .../FlowDataPinValueOwnerInterface.h | 43 + .../FlowDataPinValueSupplierInterface.h | 83 +- .../FlowNamedPropertiesSupplierInterface.h | 30 + .../Public/Interfaces/FlowOwnerInterface.h | 19 - .../Nodes/Actor/FlowNode_ExecuteComponent.h | 28 +- .../Public/Nodes/Developer/FlowNode_Log.h | 4 + Source/Flow/Public/Nodes/FlowNode.h | 625 +---- Source/Flow/Public/Nodes/FlowNodeBase.h | 202 +- Source/Flow/Public/Nodes/FlowPin.h | 178 +- .../Public/Nodes/Graph/FlowNode_CustomInput.h | 2 +- .../Nodes/Graph/FlowNode_CustomOutput.h | 2 +- .../Nodes/Graph/FlowNode_DefineProperties.h | 26 +- .../Public/Nodes/Graph/FlowNode_FormatText.h | 4 +- .../Flow/Public/Nodes/Graph/FlowNode_Start.h | 19 +- .../Public/Nodes/Graph/FlowNode_SubGraph.h | 13 +- Source/Flow/Public/Types/FlowArray.h | 17 + .../Types/FlowAutoDataPinsWorkingData.h | 28 + .../Types/FlowDataPinBlueprintLibrary.h | 899 +++++-- .../Flow/Public/Types/FlowDataPinProperties.h | 254 +- .../FlowDataPinPropertyToValueMigration.h | 409 +++ Source/Flow/Public/Types/FlowDataPinResults.h | 85 +- Source/Flow/Public/Types/FlowDataPinValue.h | 63 + .../Public/Types/FlowDataPinValuesStandard.h | 496 ++++ .../Public/Types/FlowNamedDataPinProperty.h | 98 + Source/Flow/Public/Types/FlowPinEnums.h | 122 +- Source/Flow/Public/Types/FlowPinType.h | 55 + Source/Flow/Public/Types/FlowPinTypeName.h | 33 + .../Public/Types/FlowPinTypeNamesStandard.h | 38 + .../Public/Types/FlowPinTypeNodeTemplates.h | 70 + .../Flow/Public/Types/FlowPinTypeTemplates.h | 1025 +++++++ .../Flow/Public/Types/FlowPinTypesStandard.h | 519 ++++ Source/Flow/Public/Types/FlowStructUtils.h | 102 + .../Asset/AssetDefinition_FlowAsset.cpp | 12 +- .../Asset/AssetDefinition_FlowAssetParams.cpp | 152 ++ .../Private/Asset/FlowObjectDiff.cpp | 6 +- Source/FlowEditor/Private/Asset/SFlowDiff.cpp | 122 +- .../FlowAssetParamsPtrCustomization.cpp | 192 ++ .../FlowDataPinPropertyCustomizationBase.cpp | 32 - ...FlowDataPinProperty_ClassCustomization.cpp | 180 -- .../FlowDataPinProperty_EnumCustomization.cpp | 109 - ...lowDataPinProperty_ObjectCustomization.cpp | 150 -- .../FlowDataPinValueCustomization.cpp | 548 ++++ .../FlowDataPinValueCustomization_Class.cpp | 386 +++ .../FlowDataPinValueCustomization_Enum.cpp | 433 +++ .../FlowDataPinValueCustomization_Object.cpp | 297 +++ ...FlowNamedDataPinPropertyCustomization.cpp} | 3 +- .../FlowNodeAddOn_Details.cpp | 5 + .../DetailCustomizations/FlowNode_Details.cpp | 9 +- .../FlowEditor/Private/FlowEditorModule.cpp | 66 +- Source/FlowEditor/Private/Graph/FlowGraph.cpp | 38 +- .../Private/Graph/FlowGraphNodesPolicy.cpp | 54 + .../Private/Graph/FlowGraphPinFactory.cpp | 25 - .../Private/Graph/FlowGraphSchema.cpp | 418 +-- .../Private/Graph/FlowGraphSchema_Actions.cpp | 9 +- .../Private/Graph/FlowGraphSettings.cpp | 12 + .../Private/Graph/Nodes/FlowGraphNode.cpp | 74 +- .../Private/Graph/Widgets/SFlowGraphNode.cpp | 6 +- .../IFlowCuratedNamePropertyCustomization.cpp | 34 +- ...IFlowExtendedPropertyTypeCustomization.cpp | 3 - .../Asset/AssetDefinition_FlowAssetParams.h | 24 + .../FlowEditor/Public/Asset/FlowDiffControl.h | 1 + .../FlowEditor/Public/Asset/FlowObjectDiff.h | 1 + Source/FlowEditor/Public/Asset/SFlowDiff.h | 15 +- .../FlowAssetParamsPtrCustomization.h | 24 + .../FlowDataPinPropertyCustomizationBase.h | 24 - .../FlowDataPinPropertyCustomizations.h | 32 - .../FlowDataPinProperty_ClassCustomization.h | 52 - .../FlowDataPinProperty_EnumCustomization.h | 53 - .../FlowDataPinProperty_ObjectCustomization.h | 48 - .../FlowDataPinValueCustomization.h | 138 + .../FlowDataPinValueCustomization_Class.h | 81 + .../FlowDataPinValueCustomization_Enum.h | 79 + .../FlowDataPinValueCustomization_Object.h | 67 + .../FlowDataPinValueOwnerCustomization.h | 62 + .../FlowDataPinValueOwnerCustomizations.h | 13 + .../FlowDataPinValueStandardCustomizations.h | 27 + ...> FlowNamedDataPinPropertyCustomization.h} | 2 - .../FlowNodeAddOn_Details.h | 6 +- .../DetailCustomizations/FlowNode_Details.h | 6 +- .../FlowPinCustomization.h | 2 +- Source/FlowEditor/Public/FlowEditorModule.h | 2 +- Source/FlowEditor/Public/Graph/FlowGraph.h | 4 + .../Public/Graph/FlowGraphNodesPolicy.h | 30 + .../Public/Graph/FlowGraphPinFactory.h | 12 - .../FlowEditor/Public/Graph/FlowGraphSchema.h | 28 +- .../Public/Graph/FlowGraphSettings.h | 8 + .../Public/Graph/Nodes/FlowGraphNode.h | 9 +- .../Public/Graph/Widgets/SFlowGraphNode.h | 7 +- .../IFlowCuratedNamePropertyCustomization.h | 11 +- .../UnrealExtensions/VisibilityArrayBuilder.h | 278 ++ 138 files changed, 13833 insertions(+), 4672 deletions(-) create mode 100644 Source/Flow/Private/Asset/FlowAssetParams.cpp create mode 100644 Source/Flow/Private/Asset/FlowAssetParamsTypes.cpp create mode 100644 Source/Flow/Private/Asset/FlowAssetParamsUtils.cpp create mode 100644 Source/Flow/Private/FlowExecutableActorComponent.cpp create mode 100644 Source/Flow/Private/FlowPinSubsystem.cpp create mode 100644 Source/Flow/Private/Interfaces/FlowAssetProviderInterface.cpp create mode 100644 Source/Flow/Private/Types/FlowAutoDataPinsWorkingData.cpp create mode 100644 Source/Flow/Private/Types/FlowDataPinValue.cpp create mode 100644 Source/Flow/Private/Types/FlowDataPinValuesStandard.cpp create mode 100644 Source/Flow/Private/Types/FlowNamedDataPinProperty.cpp create mode 100644 Source/Flow/Private/Types/FlowPinType.cpp create mode 100644 Source/Flow/Private/Types/FlowPinTypeNamesStandard.cpp create mode 100644 Source/Flow/Private/Types/FlowPinTypesStandard.cpp create mode 100644 Source/Flow/Public/Asset/FlowAssetParams.h create mode 100644 Source/Flow/Public/Asset/FlowAssetParamsTypes.h create mode 100644 Source/Flow/Public/Asset/FlowAssetParamsUtils.h create mode 100644 Source/Flow/Public/Asset/FlowPinTypeMatchPolicy.h create mode 100644 Source/Flow/Public/FlowExecutableActorComponent.h create mode 100644 Source/Flow/Public/FlowPinSubsystem.h create mode 100644 Source/Flow/Public/Interfaces/FlowAssetProviderInterface.h create mode 100644 Source/Flow/Public/Interfaces/FlowDataPinGeneratorInterface.h delete mode 100644 Source/Flow/Public/Interfaces/FlowDataPinGeneratorNodeInterface.h create mode 100644 Source/Flow/Public/Interfaces/FlowDataPinValueOwnerInterface.h create mode 100644 Source/Flow/Public/Interfaces/FlowNamedPropertiesSupplierInterface.h delete mode 100644 Source/Flow/Public/Interfaces/FlowOwnerInterface.h create mode 100644 Source/Flow/Public/Types/FlowAutoDataPinsWorkingData.h create mode 100644 Source/Flow/Public/Types/FlowDataPinPropertyToValueMigration.h create mode 100644 Source/Flow/Public/Types/FlowDataPinValue.h create mode 100644 Source/Flow/Public/Types/FlowDataPinValuesStandard.h create mode 100644 Source/Flow/Public/Types/FlowNamedDataPinProperty.h create mode 100644 Source/Flow/Public/Types/FlowPinType.h create mode 100644 Source/Flow/Public/Types/FlowPinTypeName.h create mode 100644 Source/Flow/Public/Types/FlowPinTypeNamesStandard.h create mode 100644 Source/Flow/Public/Types/FlowPinTypeNodeTemplates.h create mode 100644 Source/Flow/Public/Types/FlowPinTypeTemplates.h create mode 100644 Source/Flow/Public/Types/FlowPinTypesStandard.h create mode 100644 Source/Flow/Public/Types/FlowStructUtils.h create mode 100644 Source/FlowEditor/Private/Asset/AssetDefinition_FlowAssetParams.cpp create mode 100644 Source/FlowEditor/Private/DetailCustomizations/FlowAssetParamsPtrCustomization.cpp delete mode 100644 Source/FlowEditor/Private/DetailCustomizations/FlowDataPinPropertyCustomizationBase.cpp delete mode 100644 Source/FlowEditor/Private/DetailCustomizations/FlowDataPinProperty_ClassCustomization.cpp delete mode 100644 Source/FlowEditor/Private/DetailCustomizations/FlowDataPinProperty_EnumCustomization.cpp delete mode 100644 Source/FlowEditor/Private/DetailCustomizations/FlowDataPinProperty_ObjectCustomization.cpp create mode 100644 Source/FlowEditor/Private/DetailCustomizations/FlowDataPinValueCustomization.cpp create mode 100644 Source/FlowEditor/Private/DetailCustomizations/FlowDataPinValueCustomization_Class.cpp create mode 100644 Source/FlowEditor/Private/DetailCustomizations/FlowDataPinValueCustomization_Enum.cpp create mode 100644 Source/FlowEditor/Private/DetailCustomizations/FlowDataPinValueCustomization_Object.cpp rename Source/FlowEditor/Private/DetailCustomizations/{FlowNamedDataPinOutputPropertyCustomization.cpp => FlowNamedDataPinPropertyCustomization.cpp} (78%) create mode 100644 Source/FlowEditor/Private/Graph/FlowGraphNodesPolicy.cpp create mode 100644 Source/FlowEditor/Public/Asset/AssetDefinition_FlowAssetParams.h create mode 100644 Source/FlowEditor/Public/DetailCustomizations/FlowAssetParamsPtrCustomization.h delete mode 100644 Source/FlowEditor/Public/DetailCustomizations/FlowDataPinPropertyCustomizationBase.h delete mode 100644 Source/FlowEditor/Public/DetailCustomizations/FlowDataPinPropertyCustomizations.h delete mode 100644 Source/FlowEditor/Public/DetailCustomizations/FlowDataPinProperty_ClassCustomization.h delete mode 100644 Source/FlowEditor/Public/DetailCustomizations/FlowDataPinProperty_EnumCustomization.h delete mode 100644 Source/FlowEditor/Public/DetailCustomizations/FlowDataPinProperty_ObjectCustomization.h create mode 100644 Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization.h create mode 100644 Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization_Class.h create mode 100644 Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization_Enum.h create mode 100644 Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization_Object.h create mode 100644 Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueOwnerCustomization.h create mode 100644 Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueOwnerCustomizations.h create mode 100644 Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueStandardCustomizations.h rename Source/FlowEditor/Public/DetailCustomizations/{FlowNamedDataPinOutputPropertyCustomization.h => FlowNamedDataPinPropertyCustomization.h} (92%) create mode 100644 Source/FlowEditor/Public/Graph/FlowGraphNodesPolicy.h create mode 100644 Source/FlowEditor/Public/UnrealExtensions/VisibilityArrayBuilder.h diff --git a/Flow.uplugin b/Flow.uplugin index d0e0e654b..ec3fc0671 100644 --- a/Flow.uplugin +++ b/Flow.uplugin @@ -1,4 +1,4 @@ -{ +{ "FileVersion" : 3, "Version" : 2.2, "FriendlyName" : "Flow", diff --git a/Source/Flow/Flow.Build.cs b/Source/Flow/Flow.Build.cs index 74df20a4c..3e5613ce3 100644 --- a/Source/Flow/Flow.Build.cs +++ b/Source/Flow/Flow.Build.cs @@ -31,7 +31,10 @@ public Flow(ReadOnlyTargetRules target) : base(target) { PublicDependencyModuleNames.AddRange(new[] { + "GraphEditor", "MessageLog", + "PropertyEditor", + "SourceControl", "UnrealEd" }); } diff --git a/Source/Flow/Private/Asset/FlowAssetParams.cpp b/Source/Flow/Private/Asset/FlowAssetParams.cpp new file mode 100644 index 000000000..62ae577f7 --- /dev/null +++ b/Source/Flow/Private/Asset/FlowAssetParams.cpp @@ -0,0 +1,386 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Asset/FlowAssetParams.h" +#include "FlowAsset.h" +#include "FlowLogChannels.h" +#include "Asset/FlowAssetParamsUtils.h" +#include "Types/FlowDataPinValuesStandard.h" + +#if WITH_EDITOR +#include "Misc/DataValidation.h" +#include "UObject/ObjectSaveContext.h" +#endif + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowAssetParams) + +#if WITH_EDITOR +void UFlowAssetParams::PostLoad() +{ + Super::PostLoad(); + + if (!HasAnyFlags(RF_ArchetypeObject | RF_ClassDefaultObject)) + { + // Migrate the named properties over to the new structs + + bool bMadeAnyChanges = false; + for (FFlowNamedDataPinProperty& NamedProperty : Properties) + { + bMadeAnyChanges |= NamedProperty.FixupDataPinProperty(); + } + + if (bMadeAnyChanges) + { + ModifyAndRebuildPropertiesMap(); + } + } + + const EFlowReconcilePropertiesResult ReconcileResult = ReconcilePropertiesWithParentParams(); + if (EFlowReconcilePropertiesResult_Classifiers::IsErrorResult(ReconcileResult)) + { + UE_LOG(LogFlow, Error, TEXT("Failed to reconcile ParentParams during PostLoad() for %s: %s"), + *GetPathName(), *UEnum::GetDisplayValueAsText(ReconcileResult).ToString()); + } +} + +void UFlowAssetParams::PreSaveRoot(FObjectPreSaveRootContext ObjectSaveContext) +{ + Super::PreSaveRoot(ObjectSaveContext); + + const EFlowReconcilePropertiesResult ReconcileResult = ReconcilePropertiesWithParentParams(); + if (EFlowReconcilePropertiesResult_Classifiers::IsErrorResult(ReconcileResult)) + { + UE_LOG(LogFlow, Error, TEXT("Failed to reconcile ParentParams during PreSaveRoot() for %s: %s"), + *GetPathName(), *UEnum::GetDisplayValueAsText(ReconcileResult).ToString()); + } +} +#endif + +void UFlowAssetParams::Serialize(FArchive& Ar) +{ +#if WITH_EDITOR + if (Ar.IsCooking()) + { + const EFlowReconcilePropertiesResult ReconcileResult = ReconcilePropertiesWithParentParams(); + if (EFlowReconcilePropertiesResult_Classifiers::IsErrorResult(ReconcileResult)) + { + UE_LOG(LogFlow, Error, TEXT("Failed to reconcile ParentParams during cooking for %s: %s"), + *GetPathName(), *UEnum::GetDisplayValueAsText(ReconcileResult).ToString()); + } + } + +#endif + Super::Serialize(Ar); +} + +UFlowAsset* UFlowAssetParams::ProvideFlowAsset() const +{ +#if WITH_EDITOR + return OwnerFlowAsset.LoadSynchronous(); +#else + // We don't have knowledge of the OwnerFlowAsset in non-editor builds + checkNoEntry(); + return nullptr; +#endif +} + +#if WITH_EDITOR +EDataValidationResult UFlowAssetParams::IsDataValid(FDataValidationContext& Context) const +{ + EDataValidationResult Result = Super::IsDataValid(Context); + + if (OwnerFlowAsset.IsNull()) + { + Context.AddError(FText::FromString(TEXT("OwnerFlowAsset is null"))); + Result = CombineDataValidationResults(Result, EDataValidationResult::Invalid); + } + else if (!OwnerFlowAsset.IsValid() && !OwnerFlowAsset.LoadSynchronous()) + { + Context.AddError(FText::FromString(FString::Printf(TEXT("Failed to load OwnerFlowAsset: %s"), *OwnerFlowAsset.ToString()))); + Result = CombineDataValidationResults(Result, EDataValidationResult::Invalid); + } + + const EFlowReconcilePropertiesResult CycleResult = CheckForParentCycle(); + if (EFlowReconcilePropertiesResult_Classifiers::IsErrorResult(CycleResult)) + { + Context.AddError(FText::FromString(TEXT("Cyclic inheritance detected"))); + Result = CombineDataValidationResults(Result, EDataValidationResult::Invalid); + } + + TSet SeenGuids; + for (int32 Index = 0; Index < Properties.Num(); ++Index) + { + const FFlowNamedDataPinProperty& Property = Properties[Index]; + if (Property.Name == NAME_None) + { + Context.AddError(FText::FromString(FString::Printf(TEXT("Property at index %d has invalid name"), Index))); + Result = CombineDataValidationResults(Result, EDataValidationResult::Invalid); + } + + if (!Property.DataPinValue.IsValid()) + { + Context.AddError(FText::FromString(FString::Printf(TEXT("Property at index %d has invalid DataPinValue"), Index))); + Result = CombineDataValidationResults(Result, EDataValidationResult::Invalid); + } + + if (!Property.Guid.IsValid()) + { + Context.AddError(FText::FromString(FString::Printf(TEXT("Property at index %d has invalid Guid"), Index))); + Result = CombineDataValidationResults(Result, EDataValidationResult::Invalid); + } + else if (SeenGuids.Contains(Property.Guid)) + { + Context.AddError(FText::FromString(FString::Printf(TEXT("Duplicate Guid found for property at index %d"), Index))); + Result = CombineDataValidationResults(Result, EDataValidationResult::Invalid); + } + else + { + SeenGuids.Add(Property.Guid); + } + + if (Property.bMayChangeNameAndType) + { + Context.AddError(FText::FromString(FString::Printf(TEXT("Property at index %d has bMayChangeNameAndType = true in UFlowAssetParams"), Index))); + Result = CombineDataValidationResults(Result, EDataValidationResult::Invalid); + } + } + + return Result; +} + +EFlowReconcilePropertiesResult UFlowAssetParams::ReconcilePropertiesWithStartNode( + const FDateTime& FlowAssetLastSaveTimeStamp, + const TSoftObjectPtr& InOwnerFlowAsset, + TArray& MutablePropertiesFromStartNode) +{ + OwnerFlowAsset = InOwnerFlowAsset; + + if (OwnerFlowAsset.IsNull()) + { + return EFlowReconcilePropertiesResult::Error_InvalidAsset; + } + + const EFlowReconcilePropertiesResult PropertiesMatchResult = FFlowAssetParamsUtils::CheckPropertiesMatch(Properties, MutablePropertiesFromStartNode); + const FDateTime ParamsTimestamp = FFlowAssetParamsUtils::GetLastSavedTimestampForObject(this); + + if (FlowAssetLastSaveTimeStamp >= ParamsTimestamp || + EFlowReconcilePropertiesResult_Classifiers::IsErrorResult(PropertiesMatchResult)) + { + ConfigureFlowAssetParams(InOwnerFlowAsset, nullptr, MutablePropertiesFromStartNode); + + return EFlowReconcilePropertiesResult::ParamsPropertiesUpdated; + } + + MutablePropertiesFromStartNode = Properties; + + FFlowNamedDataPinProperty::ConfigurePropertiesForFlowAssetParams(MutablePropertiesFromStartNode); + + return EFlowReconcilePropertiesResult::AssetPropertyValuesUpdated; +} + +EFlowReconcilePropertiesResult UFlowAssetParams::ReconcilePropertiesWithParentParams() +{ + const EFlowReconcilePropertiesResult CycleResult = CheckForParentCycle(); + if (EFlowReconcilePropertiesResult_Classifiers::IsErrorResult(CycleResult)) + { + return CycleResult; + } + + if (ParentParams.AssetPtr.IsNull()) + { + return EFlowReconcilePropertiesResult::NoChanges; + } + + UFlowAssetParams* Parent = ParentParams.AssetPtr.LoadSynchronous(); + if (!Parent) + { + UE_LOG(LogFlow, Warning, TEXT("Failed to load ParentParams: %s"), *ParentParams.AssetPtr.ToString()); + + return EFlowReconcilePropertiesResult::Error_UnloadableParent; + } + + const EFlowReconcilePropertiesResult ParentResult = Parent->ReconcilePropertiesWithParentParams(); + if (EFlowReconcilePropertiesResult_Classifiers::IsErrorResult(ParentResult)) + { + return ParentResult; + } + + const TArray& ParentProps = Parent->Properties; + TArray NewProperties; + + for (const FFlowNamedDataPinProperty& ParentProp : ParentProps) + { + FFlowNamedDataPinProperty* LocalProp = FFlowAssetParamsUtils::FindPropertyByGuid(Properties, ParentProp.Guid); + if (LocalProp != nullptr) + { + // We have a version of ParentProp locally. + // Determine if our local property has been modified since our last reconcile. + // A local property is considered modified if we've never added it to PropertyMap or is different from what currently exists in PropertyMap. + bool bLocalPropHasChanged = true; + if (PropertyMap.Contains(LocalProp->Name)) + { + FFlowNamedDataPinProperty PreviousLocalProp = *LocalProp; + PreviousLocalProp.DataPinValue = PropertyMap[LocalProp->Name]; + bLocalPropHasChanged = !FFlowAssetParamsUtils::ArePropertiesEqual(*LocalProp, PreviousLocalProp); + } + + if (bLocalPropHasChanged) + { + // If the local property has been changed then compare it to the parent value to determine if it is an override or not. + if (FFlowAssetParamsUtils::ArePropertiesEqual(*LocalProp, ParentProp)) + { + FFlowNamedDataPinProperty& NewProp = NewProperties.Add_GetRef(ParentProp); + NewProp.bIsOverride = false; + } + else + { + FFlowNamedDataPinProperty& NewProp = NewProperties.Add_GetRef(*LocalProp); + NewProp.Name = ParentProp.Name; + NewProp.bIsOverride = true; + } + } + else + { + // If the local property has not been changed then check whether it is an override. + // Overrides will get copied over while non-overrides will be updated to match the parent. + if (LocalProp->bIsOverride) + { + FFlowNamedDataPinProperty& NewProp = NewProperties.Add_GetRef(*LocalProp); + NewProp.Name = ParentProp.Name; + NewProp.bIsOverride = true; + } + else + { + FFlowNamedDataPinProperty& NewProp = NewProperties.Add_GetRef(ParentProp); + NewProp.bIsOverride = false; + } + } + } + else + { + // We do not have a version of ParentProp. Just make a non-override copy. + FFlowNamedDataPinProperty& NewProp = NewProperties.Add_GetRef(ParentProp); + NewProp.bIsOverride = false; + } + } + + for (FFlowNamedDataPinProperty& LocalProp : Properties) + { + if (!FFlowAssetParamsUtils::FindPropertyByGuid(ParentProps, LocalProp.Guid)) + { + LocalProp.bIsOverride = true; + + NewProperties.Add(LocalProp); + } + } + + Properties = NewProperties; + + ModifyAndRebuildPropertiesMap(); + + return EFlowReconcilePropertiesResult::ParamsPropertiesUpdated; +} + +void UFlowAssetParams::ConfigureFlowAssetParams(TSoftObjectPtr OwnerAsset, TSoftObjectPtr InParentParams, const TArray& InProperties) +{ + ParentParams.AssetPtr = InParentParams; + OwnerFlowAsset = OwnerAsset; + Properties = InProperties; + FFlowNamedDataPinProperty::ConfigurePropertiesForFlowAssetParams(Properties); + + ModifyAndRebuildPropertiesMap(); +} + +bool UFlowAssetParams::CanModifyFlowDataPinType() const +{ + // These are set by the Flow asset, which is authoritative + return false; +} + +bool UFlowAssetParams::ShowFlowDataPinValueInputPinCheckbox() const +{ + // These are set by the Flow asset, which is authoritative + return false; +} + +bool UFlowAssetParams::ShowFlowDataPinValueClassFilter(const FFlowDataPinValue* Value) const +{ + return true; +} + +bool UFlowAssetParams::CanEditFlowDataPinValueClassFilter(const FFlowDataPinValue* Value) const +{ + // These are set by the Flow asset, which is authoritative + return false; +} + +EFlowReconcilePropertiesResult UFlowAssetParams::CheckForParentCycle() const +{ + TSet> Visited; + TSoftObjectPtr Current = ParentParams.AssetPtr; + + while (!Current.IsNull()) + { + if (Visited.Contains(Current)) + { + UE_LOG(LogFlow, Error, TEXT("Cyclic inheritance detected at: %s"), *Current.ToString()); + return EFlowReconcilePropertiesResult::Error_CyclicInheritance; + } + + Visited.Add(Current); + const UFlowAssetParams* CurrentParams = Current.LoadSynchronous(); + if (!CurrentParams) + { + UE_LOG(LogFlow, Warning, TEXT("Failed to load ParentParams: %s"), *Current.ToString()); + return EFlowReconcilePropertiesResult::Error_UnloadableParent; + } + + Current = CurrentParams->ParentParams.AssetPtr; + } + + return EFlowReconcilePropertiesResult::NoChanges; +} + +void UFlowAssetParams::ModifyAndRebuildPropertiesMap() +{ + Modify(); + + RebuildPropertiesMap(); + + MarkPackageDirty(); +} + +void UFlowAssetParams::RebuildPropertiesMap() +{ + PropertyMap.Reset(); + + for (const FFlowNamedDataPinProperty& Prop : Properties) + { + if (Prop.IsValid()) + { + PropertyMap.Add(Prop.Name, Prop.DataPinValue); + } + else + { + UE_LOG(LogFlow, Warning, TEXT("Skipping invalid property %s during rebuild for %s"), *Prop.Name.ToString(), *GetPathName()); + } + } +} +#endif + +bool UFlowAssetParams::CanSupplyDataPinValues_Implementation() const +{ + return !PropertyMap.IsEmpty(); +} + +FFlowDataPinResult UFlowAssetParams::TrySupplyDataPin_Implementation(FName PinName) const +{ + if (const TInstancedStruct* Found = PropertyMap.Find(PinName)) + { + FFlowDataPinResult DataPinResult(EFlowDataPinResolveResult::Success); + DataPinResult.ResultValue = (*Found); + + return DataPinResult; + } + + return FFlowDataPinResult(EFlowDataPinResolveResult::FailedUnknownPin); +} diff --git a/Source/Flow/Private/Asset/FlowAssetParamsTypes.cpp b/Source/Flow/Private/Asset/FlowAssetParamsTypes.cpp new file mode 100644 index 000000000..48043832b --- /dev/null +++ b/Source/Flow/Private/Asset/FlowAssetParamsTypes.cpp @@ -0,0 +1,11 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Asset/FlowAssetParamsTypes.h" +#include "Asset/FlowAssetParams.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowAssetParamsTypes) + +UFlowAssetParams* FFlowAssetParamsPtr::ResolveFlowAssetParams() const +{ + return AssetPtr.LoadSynchronous(); +} diff --git a/Source/Flow/Private/Asset/FlowAssetParamsUtils.cpp b/Source/Flow/Private/Asset/FlowAssetParamsUtils.cpp new file mode 100644 index 000000000..6cf293d5e --- /dev/null +++ b/Source/Flow/Private/Asset/FlowAssetParamsUtils.cpp @@ -0,0 +1,119 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Asset/FlowAssetParamsUtils.h" +#include "Types/FlowNamedDataPinProperty.h" + +#include "Misc/DateTime.h" +#include "HAL/FileManager.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowAssetParamsUtils) + +#if WITH_EDITOR +FDateTime FFlowAssetParamsUtils::GetLastSavedTimestampForObject(const UObject* Object) +{ + if (!Object) + { + return FDateTime::MinValue(); + } + + const FString PackagePath = Object->GetPathName(); + return IFileManager::Get().GetTimeStamp(*PackagePath); +} + +EFlowReconcilePropertiesResult FFlowAssetParamsUtils::CheckPropertiesMatch( + const TArray& PropertiesA, + const TArray& PropertiesB) +{ + if (PropertiesA.Num() != PropertiesB.Num()) + { + return EFlowReconcilePropertiesResult::Error_PropertyCountMismatch; + } + + for (int32 Index = 0; Index < PropertiesA.Num(); ++Index) + { + const FFlowNamedDataPinProperty& PropA = PropertiesA[Index]; + const FFlowNamedDataPinProperty& PropB = PropertiesB[Index]; + const UScriptStruct* ScriptStructA = PropA.DataPinValue.GetScriptStruct(); + const UScriptStruct* ScriptStructB = PropB.DataPinValue.GetScriptStruct(); + + if (PropA.Name != PropB.Name || + ScriptStructA != ScriptStructB || + !IsValid(ScriptStructA)) + { + return EFlowReconcilePropertiesResult::Error_PropertyTypeMismatch; + } + } + + return EFlowReconcilePropertiesResult::NoChanges; +} + +const FFlowNamedDataPinProperty* FFlowAssetParamsUtils::FindPropertyByGuid( + const TArray& Props, + const FGuid& Guid) +{ + for (const FFlowNamedDataPinProperty& Prop : Props) + { + if (Prop.Guid == Guid) + { + return &Prop; + } + } + + return nullptr; +} + +FFlowNamedDataPinProperty* FFlowAssetParamsUtils::FindPropertyByGuid( + TArray& Props, + const FGuid& Guid) +{ + for (FFlowNamedDataPinProperty& Prop : Props) + { + if (Prop.Guid == Guid) + { + return &Prop; + } + } + + return nullptr; +} + +bool FFlowAssetParamsUtils::ArePropertyArraysEqual( + const TArray& A, + const TArray& B) +{ + if (A.Num() != B.Num()) + { + return false; + } + + for (int32 Index = 0; Index < A.Num(); ++Index) + { + if (!ArePropertiesEqual(A[Index], B[Index])) + { + return false; + } + } + + return true; +} + +bool FFlowAssetParamsUtils::ArePropertiesEqual( + const FFlowNamedDataPinProperty& A, + const FFlowNamedDataPinProperty& B) +{ + if (A.Name != B.Name || A.Guid != B.Guid) + { + return false; + } + + const UScriptStruct* ScriptStructA = A.DataPinValue.GetScriptStruct(); + const UScriptStruct* ScriptStructB = B.DataPinValue.GetScriptStruct(); + if (ScriptStructA != ScriptStructB) + { + return false; + } + + return A.DataPinValue == B.DataPinValue; +} + +#endif diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 521ed3e36..df435c480 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -5,22 +5,33 @@ #include "FlowLogChannels.h" #include "FlowSettings.h" #include "FlowSubsystem.h" - #include "AddOns/FlowNodeAddOn.h" -#include "Interfaces/FlowDataPinGeneratorNodeInterface.h" +#include "Asset/FlowAssetParams.h" +#include "Asset/FlowAssetParamsUtils.h" #include "Nodes/FlowNodeBase.h" #include "Nodes/Graph/FlowNode_CustomInput.h" #include "Nodes/Graph/FlowNode_CustomOutput.h" #include "Nodes/Graph/FlowNode_Start.h" #include "Nodes/Graph/FlowNode_SubGraph.h" +#include "Types/FlowAutoDataPinsWorkingData.h" +#include "Types/FlowDataPinValue.h" +#include "Types/FlowStructUtils.h" #include "Engine/World.h" #include "Serialization/MemoryReader.h" #include "Serialization/MemoryWriter.h" #if WITH_EDITOR +#include "AssetRegistry/AssetRegistryModule.h" +#include "AssetToolsModule.h" +#include "ContentBrowserModule.h" +#include "IContentBrowserSingleton.h" #include "Editor.h" #include "Editor/EditorEngine.h" +#include "Modules/ModuleManager.h" +#include "SourceControlHelpers.h" +#include "UObject/ObjectSaveContext.h" +#include "UObject/Package.h" FString UFlowAsset::ValidationError_NodeClassNotAllowed = TEXT("Node class {0} is not allowed in this asset."); FString UFlowAsset::ValidationError_NullNodeInstance = TEXT("Node with GUID {0} is NULL"); @@ -83,23 +94,148 @@ void UFlowAsset::PostLoad() { Super::PostLoad(); - // If we removed or moved a flow node blueprint (and there is no redirector) we might loose the reference to it resulting - // in null pointers in the Nodes FGUID->UFlowNode* Map. So here we iterate over all the Nodes and remove all pairs that - // are nulled out. + const UPackage* Package = GetPackage(); + if (IsValid(Package) && !FPackageName::IsTempPackage(Package->GetPathName())) + { + // If we removed or moved a flow node blueprint (and there is no redirector) we might loose the reference to it resulting + // in null pointers in the Nodes FGUID->UFlowNode* Map. So here we iterate over all the Nodes and remove all pairs that + // are nulled out. + + TSet NodesToRemoveGUID; + + for (const TPair& Node : GetNodes()) + { + if (!IsValid(Node.Value)) + { + NodesToRemoveGUID.Emplace(Node.Key); + } + } + + for (const FGuid& Guid : NodesToRemoveGUID) + { + UnregisterNode(Guid); + } + + ReconcileBaseAssetParams(FFlowAssetParamsUtils::GetLastSavedTimestampForObject(this)); + } +} + +void UFlowAsset::PreSaveRoot(FObjectPreSaveRootContext ObjectSaveContext) +{ + ReconcileBaseAssetParams(FDateTime::Now()); +} + +void UFlowAsset::ReconcileBaseAssetParams(const FDateTime& AssetLastSavedTimestamp) +{ + if (BaseAssetParams.AssetPtr.IsNull()) + { + return; + } + + UFlowAssetParams* BaseAssetParamsPtr = BaseAssetParams.AssetPtr.LoadSynchronous(); + if (!IsValid(BaseAssetParamsPtr)) + { + UE_LOG(LogFlow, Error, TEXT("Failed to load BaseAssetParams: %s"), *BaseAssetParams.AssetPtr.ToString()); + return; + } + + IFlowNamedPropertiesSupplierInterface* NamedPropertiesSupplier = Cast(GetDefaultEntryNode()); + if (!NamedPropertiesSupplier) + { + UE_LOG(LogFlow, Error, TEXT("No NamedPropertiesSupplier (e.g., Start node) found in FlowAsset: %s"), *GetPathName()); + return; + } + + TArray& MutableStartNodeProperties = NamedPropertiesSupplier->GetMutableNamedProperties(); + const EFlowReconcilePropertiesResult ReconcileResult = + BaseAssetParamsPtr->ReconcilePropertiesWithStartNode(AssetLastSavedTimestamp, this, MutableStartNodeProperties); + + if (EFlowReconcilePropertiesResult_Classifiers::IsErrorResult(ReconcileResult)) + { + UE_LOG(LogFlow, Error, TEXT("Failed to reconcile BaseAssetParams for %s: %s"), + *BaseAssetParamsPtr->GetPathName(), *UEnum::GetDisplayValueAsText(ReconcileResult).ToString()); + } +} + +UFlowAssetParams* UFlowAsset::GenerateParamsFromStartNode() +{ + if (BaseAssetParams.AssetPtr.IsValid()) + { + UE_LOG(LogFlow, Warning, TEXT("BaseAssetParams already exists for %s: %s"), *GetPathName(), *BaseAssetParams.AssetPtr.ToString()); + return BaseAssetParams.AssetPtr.LoadSynchronous(); + } + + // Get the Start node + IFlowNamedPropertiesSupplierInterface* NamedPropertiesSupplier = Cast(GetDefaultEntryNode()); + if (!NamedPropertiesSupplier) + { + UE_LOG(LogFlow, Error, TEXT("No valid Start node found for generating params in %s"), *GetPathName()); + return nullptr; + } + + // Determine the params asset name + const FString ParamsAssetName = GenerateParamsAssetName(); + if (ParamsAssetName.IsEmpty()) + { + UE_LOG(LogFlow, Error, TEXT("Generated empty params asset name for %s"), *GetPathName()); + return nullptr; + } - TSet NodesToRemoveGUID; + // Create the params asset + FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked("AssetTools"); + const FString PackagePath = FPackageName::GetLongPackagePath(GetPackage()->GetPathName()); + FString UniquePackageName, UniqueAssetName; + AssetToolsModule.Get().CreateUniqueAssetName(PackagePath + TEXT("/") + ParamsAssetName, TEXT(""), UniquePackageName, UniqueAssetName); - for (auto& [Guid, Node] : GetNodes()) + UFlowAssetParams* NewParams = Cast( + AssetToolsModule.Get().CreateAsset(UniqueAssetName, PackagePath, UFlowAssetParams::StaticClass(), nullptr)); + if (!IsValid(NewParams)) { - if (!IsValid(Node)) + UE_LOG(LogFlow, Error, TEXT("Failed to create Flow Asset Params: %s"), *UniqueAssetName); + return nullptr; + } + + // Reconfigure with the new properties + NewParams->ConfigureFlowAssetParams(this, nullptr, NamedPropertiesSupplier->GetMutableNamedProperties()); + + // Source control integration + if (USourceControlHelpers::IsAvailable()) + { + const FString FileName = USourceControlHelpers::PackageFilename(NewParams->GetPathName()); + if (!USourceControlHelpers::CheckOutOrAddFile(FileName)) { - NodesToRemoveGUID.Emplace(Guid); + UE_LOG(LogFlow, Warning, TEXT("Failed to check out/add %s; saved in-memory only"), *NewParams->GetPathName()); } } - for (const FGuid& Guid : NodesToRemoveGUID) + // Assign to BaseAssetParams and sync Content Browser + BaseAssetParams.AssetPtr = NewParams; + + FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked("AssetRegistry"); + AssetRegistryModule.Get().AssetCreated(NewParams); + + FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked("ContentBrowser"); + TArray AssetsToSync = { NewParams }; + ContentBrowserModule.Get().SyncBrowserToAssets(AssetsToSync, true); + + return NewParams; +} + +FString UFlowAsset::GenerateParamsAssetName() const +{ + const FString FlowAssetName = GetName(); + + const int32 UnderscoreIndex = FlowAssetName.Find(TEXT("_"), ESearchCase::CaseSensitive); + + if (UnderscoreIndex != INDEX_NONE) + { + const FString Prefix = FlowAssetName.Left(UnderscoreIndex); + const FString Suffix = FlowAssetName.Mid(UnderscoreIndex + 1); + return FString::Printf(TEXT("%sParams_%s"), *Prefix, *Suffix); + } + else { - UnregisterNode(Guid); + return FlowAssetName + TEXT("Params"); } } @@ -358,7 +494,7 @@ void UFlowAsset::HarvestNodeConnections(UFlowNode* TargetNode) for (const UEdGraphPin* ThisPin : GraphNodePins) { const bool bIsExecPin = FFlowPin::IsExecPinCategory(ThisPin->PinType.PinCategory); - const bool bIsDataPin = FFlowPin::IsDataPinCategory(ThisPin->PinType.PinCategory); + const bool bIsDataPin = !bIsExecPin; const bool bIsOutputPin = (ThisPin->Direction == EGPD_Output); const bool bIsInputPin = (ThisPin->Direction == EGPD_Input); const bool bHasAtLeastOneConnection = ThisPin->LinkedTo.Num() > 0; @@ -421,57 +557,53 @@ void UFlowAsset::HarvestNodeConnections(UFlowNode* TargetNode) } } } - -bool UFlowAsset::TryUpdateManagedFlowPinsForNode(UFlowNode& FlowNode) + +bool UFlowAsset::TryGetDefaultForInputPinName(const FStructProperty& StructProperty, const void* Container, FString& OutString) { - const UClass* FlowNodeClass = FlowNode.GetClass(); - if (!IsValid(FlowNodeClass)) + // We also look in the USTRUCT for DefaultForInputFlowPin + const FString* DefaultForInputFlowPinName = StructProperty.Struct->FindMetaData(FFlowPin::MetadataKey_DefaultForInputFlowPin); + + if (DefaultForInputFlowPinName) { - return false; + OutString = *DefaultForInputFlowPinName; + + return true; } - // Setup the working data struct - FFlowHarvestDataPinsWorkingData WorkingData = - FFlowHarvestDataPinsWorkingData( - FlowNode, - FlowNode.GetPinNameToBoundPropertyNameMap(), - FlowNode.GetAutoInputDataPins(), - FlowNode.GetAutoOutputDataPins()); + // For blueprint use, we allow the Value structs to set input pins via editor-only data - // Some nodes can auto-generate some pins directly, - // so let them append their pins into our arrays first. - if (IFlowDataPinGeneratorNodeInterface* AutoGeneratorNode = Cast(&FlowNode)) + const FFlowDataPinValue* DataPinValue = FlowStructUtils::CastStructValue(StructProperty, Container); + if (DataPinValue && DataPinValue->IsInputPin()) { - AutoGeneratorNode->AutoGenerateDataPins( - WorkingData.PinNameToBoundPropertyNameMapNext, - WorkingData.AutoInputDataPinsNext, - WorkingData.AutoOutputDataPinsNext); - } + OutString.Empty(); - // Try to harvest pins to auto-generate and/or bind to for each property in the flow node - for (TFieldIterator PropertyIt(FlowNodeClass); PropertyIt; ++PropertyIt) - { - HarvestFlowPinMetadataForProperty(*PropertyIt, WorkingData); + return true; } - // Check if the pin name to bound property map changed - WorkingData.bPinNameMapChanged |= WorkingData.DidPinNameToBoundPropertyNameMapChange(); + return false; +} + +bool UFlowAsset::TryUpdateManagedFlowPinsForNode(UFlowNode& FlowNode) +{ + // Set up the working data struct + FFlowAutoDataPinsWorkingData WorkingData = + FFlowAutoDataPinsWorkingData( + FlowNode.GetAutoInputDataPins(), + FlowNode.GetAutoOutputDataPins()); + + // Allow the node to auto-generate data pins + FlowNode.AutoGenerateDataPins(WorkingData); // If the auto-generated data pins array changed, it counts as dirty as well const bool bAutoInputDataPinsChanged = WorkingData.DidAutoInputDataPinsChange(); const bool bAutoOutputDataPinsChanged = WorkingData.DidAutoOutputDataPinsChange(); - if (WorkingData.bPinNameMapChanged || bAutoInputDataPinsChanged || bAutoOutputDataPinsChanged) + if (bAutoInputDataPinsChanged || bAutoOutputDataPinsChanged) { FlowNode.SetFlags(RF_Transactional); FlowNode.Modify(); // Lock-in the data that changed. - if (WorkingData.bPinNameMapChanged) - { - FlowNode.SetPinNameToBoundPropertyNameMap(WorkingData.PinNameToBoundPropertyNameMapNext); - } - if (bAutoInputDataPinsChanged || bAutoOutputDataPinsChanged) { if (bAutoInputDataPinsChanged) @@ -493,394 +625,6 @@ bool UFlowAsset::TryUpdateManagedFlowPinsForNode(UFlowNode& FlowNode) return false; } -void UFlowAsset::HarvestFlowPinMetadataForProperty(const FProperty* Property, FFlowHarvestDataPinsWorkingData& InOutData) -{ - FText PinDisplayName = Property->GetDisplayNameText(); - const FName& PinAuthoredName = Property->GetFName(); - - // Default assumption is the pin is will be a output pin, if no metadata is specified (ie, bIsSourceForOutputPin == false), - // because this is the most common case (the auto-generated input-pin-from-property case is only for defaulting) - TArray* FlowPinArray = &InOutData.AutoOutputDataPinsNext; - - const FString* SourceForOutputFlowPinName = Property->FindMetaData(FFlowPin::MetadataKey_SourceForOutputFlowPin); - const FString* DefaultForInputFlowPinName = Property->FindMetaData(FFlowPin::MetadataKey_DefaultForInputFlowPin); - - if (SourceForOutputFlowPinName && DefaultForInputFlowPinName) - { - LogError( - FString::Printf(TEXT("Error. A property cannot be both a %s and %s"), - *FFlowPin::MetadataKey_SourceForOutputFlowPin.ToString(), - *FFlowPin::MetadataKey_DefaultForInputFlowPin.ToString()), - InOutData.FlowNode); - - return; - } - - if (SourceForOutputFlowPinName) - { - const FString SpecifyOutputPinNameString = *SourceForOutputFlowPinName; - - if (SpecifyOutputPinNameString.Len() > 0) - { - // Replace the default PinDisplayName with the name specified in the Metadata value - PinDisplayName = FText::FromString(SpecifyOutputPinNameString); - } - } - else if (DefaultForInputFlowPinName) - { - const FString SpecifyInputPinNameString = *DefaultForInputFlowPinName; - - if (SpecifyInputPinNameString.Len() > 0) - { - // Replace the default PinDisplayName with the name specified in the Metadata value - PinDisplayName = FText::FromString(SpecifyInputPinNameString); - } - - // If the property is a Default Input for a data pin, then we need to generate the pin in the - // Input Pins array. - FlowPinArray = &InOutData.AutoInputDataPinsNext; - } - - // Check for relevant metadata keys on the property's USTRUCT() - const FStructProperty* StructProperty = CastField(Property); - if (StructProperty && StructProperty->Struct) - { - const UScriptStruct* ScriptStruct = StructProperty->Struct; - - // We also look in the USTRUCT for DefaultForInputFlowPin - DefaultForInputFlowPinName = ScriptStruct->FindMetaData(FFlowPin::MetadataKey_DefaultForInputFlowPin); - if (DefaultForInputFlowPinName) - { - // If the property is a Default Input for a data pin, then we need to generate the pin in the - // Input Pins array. - FlowPinArray = &InOutData.AutoInputDataPinsNext; - } - - if (const FString* AutoPinType = ScriptStruct->FindMetaData(FFlowPin::MetadataKey_FlowPinType)) - { - const bool bIsInputPin = DefaultForInputFlowPinName != nullptr; - - // Auto-generate the pin for this property - if (!TryCreateFlowDataPinFromMetadataValue(*AutoPinType, *InOutData.FlowNode, *Property, PinDisplayName, bIsInputPin, FlowPinArray)) - { - LogError(FString::Printf(TEXT("Error. Unknown value %s for metadata %s"), **AutoPinType, *FFlowPin::MetadataKey_FlowPinType.ToString()), InOutData.FlowNode); - - return; - } - - // Add a binding for the new pin to its property - AddDataPinPropertyBindingToMap( - PinAuthoredName, - Property->GetFName(), - InOutData); - - return; - } - } - - const FString* AutoPinType = Property->FindMetaData(FFlowPin::MetadataKey_FlowPinType); - - if (!SourceForOutputFlowPinName && !DefaultForInputFlowPinName && !AutoPinType) - { - // If we didn't detect any the relevent metadata keys, we can exit early - - return; - } - - if (AutoPinType) - { - // Auto-generate the desired pin for this property - const bool bIsInputPin = DefaultForInputFlowPinName != nullptr; - - if (!TryCreateFlowDataPinFromMetadataValue(*AutoPinType, *InOutData.FlowNode, *Property, PinDisplayName, bIsInputPin, FlowPinArray)) - { - LogError(FString::Printf(TEXT("Unknown value %s for metadata %s"), **AutoPinType, *FFlowPin::MetadataKey_FlowPinType.ToString()), InOutData.FlowNode); - - return; - } - } - else if (SourceForOutputFlowPinName) - { - // Bind to the output data pin to source from the property (but do not auto-generate the pin) - - FFlowPin* FoundFlowPin = InOutData.FlowNode->FindOutputPinByName(PinAuthoredName); - if (!FoundFlowPin) - { - LogError(FString::Printf(TEXT("Could not find bound data pin named %s for property %s"), *PinAuthoredName.ToString(), *Property->GetName()), InOutData.FlowNode); - - return; - } - } - else if (DefaultForInputFlowPinName) - { - // Bind to the input data pin to default its value from the property (but do not auto-generate the pin) - - FFlowPin* FoundFlowPin = InOutData.FlowNode->FindInputPinByName(PinAuthoredName); - if (!FoundFlowPin) - { - LogError(FString::Printf(TEXT("Could not find bound data pin named %s for property %s"), *PinAuthoredName.ToString(), *Property->GetName()), InOutData.FlowNode); - - return; - } - } - - // Add a binding for the data pin to its property - AddDataPinPropertyBindingToMap( - PinAuthoredName, - Property->GetFName(), - InOutData); -} - -void UFlowAsset::AddDataPinPropertyBindingToMap( - const FName& PinAuthoredName, - const FName& PropertyAuthoredName, - FFlowHarvestDataPinsWorkingData& InOutData) -{ - // Add a new entry in the map for this DataPin name to the property it sources from - InOutData.PinNameToBoundPropertyNameMapNext.Add(PinAuthoredName, PropertyAuthoredName); -} - -template -void AddPinForPinType(EFlowPinType PinType, UFlowNode& FlowNode, const FProperty& Property, const FText& PinDisplayName, TArray* InOutDataPinsNext) -{ - const FName& PinAuthoredName = Property.GetFName(); - - // Some of the FlowPinTypes require a SubCategoryObject to fully define the type, so - // we need to find that for the cases that it applies to. - - FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); - - FFlowPin& NewFlowPin = InOutDataPinsNext->Add_GetRef(FFlowPin(PinAuthoredName, PinDisplayName)); - switch (PinType) - { - case EFlowPinType::Enum: - { - UEnum* EnumClass = nullptr; - - if (const FStructProperty* StructProperty = CastField(&Property)) - { - // Check for a wrapper struct to get the enum data from - const UStruct* ScriptStruct = TEnumProperty::StaticStruct(); - if (StructProperty->Struct == ScriptStruct) - { - TEnumProperty ValueStruct; - StructProperty->GetValue_InContainer(&FlowNode, &ValueStruct); - - EnumClass = ValueStruct.EnumClass; - } - } - else if (const FEnumProperty* EnumProperty = CastField(&Property)) - { - // Get the enum data from the FEnumProperty - EnumClass = EnumProperty->GetEnum(); - } - - NewFlowPin.SetPinType(PinType, EnumClass); - } - break; - - case EFlowPinType::Vector: - { - UScriptStruct* ValueStructType = FFlowDataPinProperty::FindScriptStructForFlowDataPinProperty(Property); - NewFlowPin.SetPinType(PinType, ValueStructType); - } - break; - - case EFlowPinType::Rotator: - { - UScriptStruct* ValueStructType = FFlowDataPinProperty::FindScriptStructForFlowDataPinProperty(Property); - NewFlowPin.SetPinType(PinType, ValueStructType); - } - break; - - case EFlowPinType::Transform: - { - UScriptStruct* ValueStructType = FFlowDataPinProperty::FindScriptStructForFlowDataPinProperty(Property); - NewFlowPin.SetPinType(PinType, ValueStructType); - } - break; - - case EFlowPinType::GameplayTag: - { - UScriptStruct* ValueStructType = FFlowDataPinProperty::FindScriptStructForFlowDataPinProperty(Property); - NewFlowPin.SetPinType(PinType, ValueStructType); - } - break; - - case EFlowPinType::GameplayTagContainer: - { - UScriptStruct* ValueStructType = FFlowDataPinProperty::FindScriptStructForFlowDataPinProperty(Property); - NewFlowPin.SetPinType(PinType, ValueStructType); - } - break; - - case EFlowPinType::InstancedStruct: - { - UScriptStruct* ValueStructType = FFlowDataPinProperty::FindScriptStructForFlowDataPinProperty(Property); - NewFlowPin.SetPinType(PinType, ValueStructType); - } - break; - - case EFlowPinType::Object: - { - UClass* Class = nullptr; - if (const FStructProperty* StructProperty = CastField(&Property)) - { - const UStruct* ScriptStruct = TObjectProperty::StaticStruct(); - static const UStruct* SoftObjectPathStruct = TBaseStructure::Get(); - - if (StructProperty->Struct == ScriptStruct) - { - TObjectProperty ValueStruct; - StructProperty->GetValue_InContainer(&FlowNode, &ValueStruct); - - // Get the Object property's base UClass from the FFlowDataPinProperty - Class = ValueStruct.DeriveObjectClass(*StructProperty); - } - else if (StructProperty->Struct == SoftObjectPathStruct) - { - // Get the Object property's base UClass from the struct property's MetaData - Class = FFlowDataPinOutputProperty_Object::TryGetObjectClassFromProperty(*StructProperty); - } - } - else if (const FObjectProperty* ObjectProperty = CastField(&Property)) - { - // Get the Object property's base UClass from the property's MetaData - Class = ObjectProperty->PropertyClass; - } - else if (const FSoftObjectProperty* SoftObjectProperty = CastField(&Property)) - { - // Get the Object property's base UClass from the property's MetaData - Class = SoftObjectProperty->PropertyClass; - } - else if (const FWeakObjectProperty* WeakObjectProperty = CastField(&Property)) - { - // Get the Object property's base UClass from the property's MetaData - Class = WeakObjectProperty->PropertyClass; - } - else if (const FLazyObjectProperty* LazyObjectProperty = CastField(&Property)) - { - // Get the Object property's base UClass from the property's MetaData - Class = LazyObjectProperty->PropertyClass; - } - - NewFlowPin.SetPinType(PinType, Class); - } - break; - - case EFlowPinType::Class: - { - UClass* Class = nullptr; - if (const FStructProperty* StructProperty = CastField(&Property)) - { - const UStruct* ScriptStruct = TClassProperty::StaticStruct(); - static const UStruct* SoftClassPathStruct = TBaseStructure::Get(); - - if (StructProperty->Struct == ScriptStruct) - { - TClassProperty ValueStruct; - StructProperty->GetValue_InContainer(&FlowNode, &ValueStruct); - - // Get the Class property's base UClass from the FFlowDataPinProperty - Class = ValueStruct.DeriveMetaClass(*StructProperty); - } - else if (StructProperty->Struct == SoftClassPathStruct) - { - // Get the Class property's base UClass from the struct property's MetaData - Class = FFlowDataPinOutputProperty_Class::TryGetMetaClassFromProperty(*StructProperty); - } - } - else if (const FClassProperty* ClassProperty = CastField(&Property)) - { - // Get the Class property's base UClass from the property's MetaData - Class = ClassProperty->MetaClass; - } - else if (const FSoftClassProperty* SoftClassProperty = CastField(&Property)) - { - // Get the Class property's base UClass from the property's MetaData - Class = SoftClassProperty->MetaClass; - } - - NewFlowPin.SetPinType(PinType, Class); - } - break; - - default: - { - NewFlowPin.SetPinType(PinType); - } - break; - } -} - -bool UFlowAsset::TryCreateFlowDataPinFromMetadataValue( - const FString& MetadataValue, - UFlowNode& FlowNode, - const FProperty& Property, - const FText& PinDisplayName, - const bool bIsInputPin, - TArray* InOutDataPinsNext) const -{ - check(InOutDataPinsNext); - - const TArray& CachedEnumValueNames = FFlowPin::GetFlowPinTypeEnumValuesWithoutSpaces(); - - const FName MetadataValueAsName = FName(MetadataValue); - - for (EFlowPinType PinType : TEnumRange()) - { - const int32 PinTypeAsInt = FlowEnum::ToInt(PinType); - check(CachedEnumValueNames.IsValidIndex(PinTypeAsInt)); - const FName& EnumValueAsName = CachedEnumValueNames[PinTypeAsInt]; - - if (MetadataValueAsName == EnumValueAsName) - { - if (bIsInputPin) - { - AddPinForPinType< - FFlowDataPinInputProperty_Enum, - FFlowDataPinInputProperty_Vector, - FFlowDataPinInputProperty_Rotator, - FFlowDataPinInputProperty_Transform, - FFlowDataPinInputProperty_GameplayTag, - FFlowDataPinInputProperty_GameplayTagContainer, - FFlowDataPinInputProperty_InstancedStruct, - FFlowDataPinInputProperty_Object, - FFlowDataPinInputProperty_Class>( - PinType, - FlowNode, - Property, - PinDisplayName, - InOutDataPinsNext); - } - else - { - AddPinForPinType< - FFlowDataPinOutputProperty_Enum, - FFlowDataPinOutputProperty_Vector, - FFlowDataPinOutputProperty_Rotator, - FFlowDataPinOutputProperty_Transform, - FFlowDataPinOutputProperty_GameplayTag, - FFlowDataPinOutputProperty_GameplayTagContainer, - FFlowDataPinOutputProperty_InstancedStruct, - FFlowDataPinOutputProperty_Object, - FFlowDataPinOutputProperty_Class>( - PinType, - FlowNode, - Property, - PinDisplayName, - InOutDataPinsNext); - } - - return true; - } - } - - // Subclasses of UFlowAsset can extend the supported MetadataValues -> FFlowPin mappings - return false; -} - #endif UFlowNode* UFlowAsset::GetDefaultEntryNode() const @@ -1045,6 +789,32 @@ TArray UFlowAsset::GatherNodesConnectedToAllInputs() const return ConnectedNodes; } +TArray UFlowAsset::GatherPinsConnectedToPin(const FConnectedPin& Pin) const +{ + TArray ConnectedPins; + + // Connections are only stored on one of the Nodes they connect depending on pin type. + // As such, we need to iterate all Nodes to find all possible Connections for the Pin. + for (const auto& GuidNodePair : Nodes) + { + if (IsValid(GuidNodePair.Value)) + { + ConnectedPins.Append(GuidNodePair.Value->GetKnownConnectionsToPin(Pin)); + } + } + + return ConnectedPins; +} + +TArray UFlowAsset::GetAllNodes() const +{ + TArray> AllNodes; + AllNodes.Reserve(Nodes.Num()); + Nodes.GenerateValueArray(AllNodes); + + return ObjectPtrDecay(AllNodes); +} + void UFlowAsset::AddInstance(UFlowAsset* Instance) { ActiveInstances.Add(Instance); @@ -1308,7 +1078,7 @@ void UFlowAsset::TriggerCustomOutput(const FName& EventName) } } -void UFlowAsset::TriggerInput(const FGuid& NodeGuid, const FName& PinName) +void UFlowAsset::TriggerInput(const FGuid& NodeGuid, const FName& PinName, const FConnectedPin& FromPin) { if (UFlowNode* Node = Nodes.FindRef(NodeGuid)) { @@ -1523,43 +1293,3 @@ void UFlowAsset::LogNote(const FString& MessageToLog, const UFlowNodeBase* Node) } } #endif - -#if WITH_EDITOR -bool FFlowHarvestDataPinsWorkingData::DidPinNameToBoundPropertyNameMapChange() const -{ - if (PinNameToBoundPropertyNameMapPrev.Num() != PinNameToBoundPropertyNameMapNext.Num()) - { - return true; - } - - for (const auto& KV : PinNameToBoundPropertyNameMapPrev) - { - const FName& PinNameFromPrev = KV.Key; - const FName& PropertyNameFromPrev = KV.Value; - - const FName* FoundPropertyNameInNext = PinNameToBoundPropertyNameMapNext.Find(PinNameFromPrev); - if (!FoundPropertyNameInNext) - { - return true; - } - - if (*FoundPropertyNameInNext != PropertyNameFromPrev) - { - return true; - } - } - - return false; -} - -bool FFlowHarvestDataPinsWorkingData::DidAutoInputDataPinsChange() const -{ - return !FFlowPin::ArePinArraysMatchingNamesAndTypes(AutoInputDataPinsPrev, AutoInputDataPinsNext); -} - -bool FFlowHarvestDataPinsWorkingData::DidAutoOutputDataPinsChange() const -{ - return !FFlowPin::ArePinArraysMatchingNamesAndTypes(AutoOutputDataPinsPrev, AutoOutputDataPinsNext); -} - -#endif diff --git a/Source/Flow/Private/FlowComponent.cpp b/Source/Flow/Private/FlowComponent.cpp index fc1dc359e..de7eb5bd7 100644 --- a/Source/Flow/Private/FlowComponent.cpp +++ b/Source/Flow/Private/FlowComponent.cpp @@ -2,6 +2,7 @@ #include "FlowComponent.h" +#include "Asset/FlowAssetParams.h" #include "FlowAsset.h" #include "FlowLogChannels.h" #include "FlowSettings.h" @@ -450,7 +451,8 @@ void UFlowComponent::StartRootFlow() { VerifyIdentityTags(); - FlowSubsystem->StartRootFlow(this, RootFlow, bAllowMultipleInstances); + const TScriptInterface RootFlowParamsAsInterface = RootFlowParams.ResolveFlowAssetParams(); + FlowSubsystem->StartRootFlow(this, RootFlow, RootFlowParamsAsInterface, bAllowMultipleInstances); } } } diff --git a/Source/Flow/Private/FlowExecutableActorComponent.cpp b/Source/Flow/Private/FlowExecutableActorComponent.cpp new file mode 100644 index 000000000..e46967677 --- /dev/null +++ b/Source/Flow/Private/FlowExecutableActorComponent.cpp @@ -0,0 +1,48 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "FlowExecutableActorComponent.h" +#include "Nodes/FlowNode.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowExecutableActorComponent) + +#if WITH_EDITOR +bool UFlowExecutableActorComponent::CanModifyFlowDataPinType() const +{ + return IsDefaultObject(); +} + +bool UFlowExecutableActorComponent::ShowFlowDataPinValueInputPinCheckbox() const +{ + return IsDefaultObject(); +} + +bool UFlowExecutableActorComponent::ShowFlowDataPinValueClassFilter(const FFlowDataPinValue* Value) const +{ + return IsDefaultObject(); +} + +bool UFlowExecutableActorComponent::CanEditFlowDataPinValueClassFilter(const FFlowDataPinValue* Value) const +{ + return IsDefaultObject(); +} + +void UFlowExecutableActorComponent::SetFlowDataPinValuesRebuildDelegate(FSimpleDelegate InDelegate) +{ + FlowDataPinValuesRebuildDelegate = InDelegate; +} + +void UFlowExecutableActorComponent::RequestFlowDataPinValuesDetailsRebuild() +{ + if (FlowDataPinValuesRebuildDelegate.IsBound()) + { + FlowDataPinValuesRebuildDelegate.Execute(); + } +} +#endif + +void UFlowExecutableActorComponent::PreActivateExternalFlowExecutable(UFlowNodeBase& FlowNodeBase) +{ + IFlowExternalExecutableInterface::PreActivateExternalFlowExecutable(FlowNodeBase); + + FlowNodeProxy = &FlowNodeBase; +} diff --git a/Source/Flow/Private/FlowPinSubsystem.cpp b/Source/Flow/Private/FlowPinSubsystem.cpp new file mode 100644 index 000000000..3fc28d9ae --- /dev/null +++ b/Source/Flow/Private/FlowPinSubsystem.cpp @@ -0,0 +1,83 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "FlowPinSubsystem.h" +#include "Types/FlowPinTypesStandard.h" + +#include "Engine/Engine.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowPinSubsystem) + +UFlowPinSubsystem* UFlowPinSubsystem::Get() +{ + return GEngine->GetEngineSubsystem(); +} + +bool UFlowPinSubsystem::ShouldCreateSubsystem(UObject* Outer) const +{ + // Only create an instance if there is no override implementation defined elsewhere + TArray ChildClasses; + GetDerivedClasses(GetClass(), ChildClasses, false); + return (ChildClasses.Num() == 0); +} + +void UFlowPinSubsystem::Initialize(FSubsystemCollectionBase& Collection) +{ + Super::Initialize(Collection); + + check(PinTypes.IsEmpty()); + + // Register standard types + RegisterPinType(); + RegisterPinType(); + RegisterPinType(); + RegisterPinType(); + RegisterPinType(); + RegisterPinType(); + RegisterPinType(); + RegisterPinType(); + RegisterPinType(); + RegisterPinType(); + RegisterPinType(); + RegisterPinType(); + RegisterPinType(); + RegisterPinType(); + RegisterPinType(); + RegisterPinType(); + RegisterPinType(); +} + +void UFlowPinSubsystem::Deinitialize() +{ + UnregisterAllPinTypes(); + + Super::Deinitialize(); +} + +void UFlowPinSubsystem::UnregisterAllPinTypes() +{ + const TArray PinTypeNames = GetPinTypeNames(); + for (const FFlowPinTypeName& PinTypeName : PinTypeNames) + { + UnregisterPinType(PinTypeName); + } + + check(PinTypes.IsEmpty()); +} + +void UFlowPinSubsystem::RegisterPinType(const FFlowPinTypeName& TypeName, const TInstancedStruct& PinType) +{ + PinTypes.Add(TypeName, PinType); +} + +void UFlowPinSubsystem::UnregisterPinType(const FFlowPinTypeName& TypeName) +{ + PinTypes.Remove(TypeName); +} + +TArray UFlowPinSubsystem::GetPinTypeNames() const +{ + TArray TypeNames; + PinTypes.GetKeys(TypeNames); + + return TypeNames; +} diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index 946610632..70a88eafe 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -76,15 +76,13 @@ void UFlowSubsystem::AbortActiveFlows() RootInstances.Empty(); } -void UFlowSubsystem::StartRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const bool bAllowMultipleInstances /* = true */) +void UFlowSubsystem::StartRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const TScriptInterface DataPinValueSupplier, const bool bAllowMultipleInstances) { if (FlowAsset) { if (UFlowAsset* NewFlow = CreateRootFlow(Owner, FlowAsset, bAllowMultipleInstances)) { - // todo: (gtaylor) In the future, we may want to provide a way to set a data pin value supplier - // for the root flow graph. - NewFlow->StartFlow(); + NewFlow->StartFlow(DataPinValueSupplier.GetInterface()); } } #if WITH_EDITOR @@ -664,4 +662,4 @@ void UFlowSubsystem::FindComponents(const FGameplayTagContainer& Tags, const EGa } } -#undef LOCTEXT_NAMESPACE +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/Flow/Private/Interfaces/FlowAssetProviderInterface.cpp b/Source/Flow/Private/Interfaces/FlowAssetProviderInterface.cpp new file mode 100644 index 000000000..bbf8e4583 --- /dev/null +++ b/Source/Flow/Private/Interfaces/FlowAssetProviderInterface.cpp @@ -0,0 +1,9 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Interfaces/FlowAssetProviderInterface.h" +#include "FlowAsset.h" + +UFlowAsset* IFlowAssetProviderInterface::ProvideFlowAsset() const +{ + return Execute_K2_ProvideFlowAsset(Cast(this)); +} diff --git a/Source/Flow/Private/Nodes/Actor/FlowNode_ExecuteComponent.cpp b/Source/Flow/Private/Nodes/Actor/FlowNode_ExecuteComponent.cpp index 5714c31e9..c53d05182 100644 --- a/Source/Flow/Private/Nodes/Actor/FlowNode_ExecuteComponent.cpp +++ b/Source/Flow/Private/Nodes/Actor/FlowNode_ExecuteComponent.cpp @@ -125,7 +125,7 @@ void UFlowNode_ExecuteComponent::OnActivate() } else { - UE_LOG(LogFlow, Error, TEXT("Expected a valid UActorComponent that implemented the IFlowExternalExecutableInterface")); + UE_LOG(LogFlow, Error, TEXT("Expected a valid UActorComponent that implemented the IFlowExternalExecutableInterface (%s)"), *ResolvedComp->GetClass()->GetName()); } if (IFlowCoreExecutableInterface* ComponentAsCoreExecutable = Cast(ResolvedComp)) @@ -138,7 +138,7 @@ void UFlowNode_ExecuteComponent::OnActivate() } else { - UE_LOG(LogFlow, Error, TEXT("Expected a valid UActorComponent that implemented the IFlowCoreExecutableInterface")); + UE_LOG(LogFlow, Error, TEXT("Expected a valid UActorComponent that implemented the IFlowCoreExecutableInterface (%s)"), *ResolvedComp->GetClass()->GetName()); } } } @@ -196,32 +196,35 @@ void UFlowNode_ExecuteComponent::ExecuteInput(const FName& PinName) { LogError(FString::Printf(TEXT("Could not ExecuteInput %s, because the component was missing or could not be resolved."), *PinName.ToString())); } - - // Trigger the default output (if the output pins weren't replaced, - // and the node hasn't already Finished) - if (OutputPins.Contains(UFlowNode::DefaultOutputPin.PinName) && !HasFinished()) - { - constexpr bool bFinish = false; - TriggerOutput(UFlowNode::DefaultOutputPin.PinName, bFinish); - } } #if WITH_EDITOR TArray UFlowNode_ExecuteComponent::GetContextInputs() const { TArray ContextInputs; - if (UActorComponent* ResolvedComp = GetResolvedComponent()) + const UActorComponent* ResolvedComp = GetResolvedComponent(); + + if (!IsValid(ResolvedComp)) { - if (const IFlowContextPinSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) - { - ContextInputs = PinSupplierInterface->GetContextInputs(); - } + // If we don't have a Resolved Component object yet, try to find the expected component object. For + // injected components this will return the CDO + ResolvedComp = TryGetExpectedComponent(); } - else if (const UActorComponent* ExpectedComponent = TryGetExpectedComponent()) + + // NOTE: we have to call GetClass on the Resolved Component; not StaticClass(). This makes it so we can handle classes that only implement the + // interface in Blueprints. + if (ResolvedComp && ResolvedComp->GetClass()->ImplementsInterface(UFlowContextPinSupplierInterface::StaticClass())) { - if (const IFlowContextPinSupplierInterface* PinSupplierInterface = Cast(ExpectedComponent)) + if (const IFlowContextPinSupplierInterface* CompAsStaticInterface = Cast(ResolvedComp)) + { + // The native (static) class implements the interface, so we call it directly (default implementation provided by the interface will call the K2 BP version of it). + // Ee assume that the implementor of the interface is responsible for invoking Execute_K2_GetContextInputs in their overrides of GetContextInputs() + ContextInputs = CompAsStaticInterface->GetContextInputs(); + } + else { - ContextInputs = PinSupplierInterface->GetContextInputs(); + // Only the BP class implements the interface, so we call it here. + ContextInputs = IFlowContextPinSupplierInterface::Execute_K2_GetContextInputs(ResolvedComp); } } @@ -239,18 +242,29 @@ TArray UFlowNode_ExecuteComponent::GetContextInputs() const TArray UFlowNode_ExecuteComponent::GetContextOutputs() const { TArray ContextOutputs; - if (UActorComponent* ResolvedComp = GetResolvedComponent()) + const UActorComponent* ResolvedComp = GetResolvedComponent(); + + if (!IsValid(ResolvedComp)) { - if (const IFlowContextPinSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) - { - ContextOutputs = PinSupplierInterface->GetContextOutputs(); - } + // If we don't have a Resolved Component object yet, try to find the expected component object. For + // injected components this will return the CDO + ResolvedComp = TryGetExpectedComponent(); } - else if (const UActorComponent* ExpectedComponent = TryGetExpectedComponent()) + + // NOTE: we have to call GetClass on the Resolved Component; not StaticClass(). This makes it so we can handle classes that only implement the + // interface in Blueprints. + if (ResolvedComp && ResolvedComp->GetClass()->ImplementsInterface(UFlowContextPinSupplierInterface::StaticClass())) { - if (const IFlowContextPinSupplierInterface* PinSupplierInterface = Cast(ExpectedComponent)) + if (const IFlowContextPinSupplierInterface* CompAsStaticInterface = Cast(ResolvedComp)) + { + // The native (static) class implements the interface, so we call it directly (default implementation provided by the interface will call the K2 BP version of it. + // we assume that the implementor of the interface is responsible for invoking K2_GetContextOutputs in their overrides of GetContextOutputs() + ContextOutputs = CompAsStaticInterface->GetContextOutputs(); + } + else { - ContextOutputs = PinSupplierInterface->GetContextOutputs(); + // Only the BP class implements the interface, so we call it here. + ContextOutputs = IFlowContextPinSupplierInterface::Execute_K2_GetContextOutputs(ResolvedComp); } } @@ -264,277 +278,19 @@ TArray UFlowNode_ExecuteComponent::GetContextOutputs() const return ContextOutputs; } -#endif // WITH_EDITOR - -bool UFlowNode_ExecuteComponent::CanSupplyDataPinValues_Implementation() const -{ - if (UActorComponent* ResolvedComp = GetResolvedComponent()) - { - if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) - { - if (IFlowDataPinValueSupplierInterface::Execute_CanSupplyDataPinValues(ResolvedComp)) - { - return true; - } - } - } - - return Super::CanSupplyDataPinValues_Implementation(); -} - -FFlowDataPinResult_Bool UFlowNode_ExecuteComponent::TrySupplyDataPinAsBool_Implementation(const FName& PinName) const -{ - if (UActorComponent* ResolvedComp = GetResolvedComponent()) - { - if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) - { - const FFlowDataPinResult_Bool PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsBool(ResolvedComp, PinName); - if (PinResult.Result == EFlowDataPinResolveResult::Success) - { - return PinResult; - } - } - } - - return Super::TrySupplyDataPinAsBool_Implementation(PinName); -} - -FFlowDataPinResult_Int UFlowNode_ExecuteComponent::TrySupplyDataPinAsInt_Implementation(const FName& PinName) const -{ - if (UActorComponent* ResolvedComp = GetResolvedComponent()) - { - if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) - { - const FFlowDataPinResult_Int PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsInt(ResolvedComp, PinName); - if (PinResult.Result == EFlowDataPinResolveResult::Success) - { - return PinResult; - } - } - } - - return Super::TrySupplyDataPinAsInt_Implementation(PinName); -} - -FFlowDataPinResult_Float UFlowNode_ExecuteComponent::TrySupplyDataPinAsFloat_Implementation(const FName& PinName) const -{ - if (UActorComponent* ResolvedComp = GetResolvedComponent()) - { - if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) - { - const FFlowDataPinResult_Float PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsFloat(ResolvedComp, PinName); - if (PinResult.Result == EFlowDataPinResolveResult::Success) - { - return PinResult; - } - } - } - - return Super::TrySupplyDataPinAsFloat_Implementation(PinName); -} - -FFlowDataPinResult_Name UFlowNode_ExecuteComponent::TrySupplyDataPinAsName_Implementation(const FName& PinName) const -{ - if (UActorComponent* ResolvedComp = GetResolvedComponent()) - { - if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) - { - const FFlowDataPinResult_Name PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsName(ResolvedComp, PinName); - if (PinResult.Result == EFlowDataPinResolveResult::Success) - { - return PinResult; - } - } - } - - return Super::TrySupplyDataPinAsName_Implementation(PinName); -} -FFlowDataPinResult_String UFlowNode_ExecuteComponent::TrySupplyDataPinAsString_Implementation(const FName& PinName) const -{ - if (UActorComponent* ResolvedComp = GetResolvedComponent()) - { - if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) - { - const FFlowDataPinResult_String PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsString(ResolvedComp, PinName); - if (PinResult.Result == EFlowDataPinResolveResult::Success) - { - return PinResult; - } - } - } - - return Super::TrySupplyDataPinAsString_Implementation(PinName); -} - -FFlowDataPinResult_Text UFlowNode_ExecuteComponent::TrySupplyDataPinAsText_Implementation(const FName& PinName) const -{ - if (UActorComponent* ResolvedComp = GetResolvedComponent()) - { - if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) - { - const FFlowDataPinResult_Text PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsText(ResolvedComp, PinName); - if (PinResult.Result == EFlowDataPinResolveResult::Success) - { - return PinResult; - } - } - } - - return Super::TrySupplyDataPinAsText_Implementation(PinName); -} - -FFlowDataPinResult_Enum UFlowNode_ExecuteComponent::TrySupplyDataPinAsEnum_Implementation(const FName& PinName) const -{ - if (UActorComponent* ResolvedComp = GetResolvedComponent()) - { - if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) - { - const FFlowDataPinResult_Enum PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsEnum(ResolvedComp, PinName); - if (PinResult.Result == EFlowDataPinResolveResult::Success) - { - return PinResult; - } - } - } - - return Super::TrySupplyDataPinAsEnum_Implementation(PinName); -} - -FFlowDataPinResult_Vector UFlowNode_ExecuteComponent::TrySupplyDataPinAsVector_Implementation(const FName& PinName) const -{ - if (UActorComponent* ResolvedComp = GetResolvedComponent()) - { - if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) - { - const FFlowDataPinResult_Vector PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsVector(ResolvedComp, PinName); - if (PinResult.Result == EFlowDataPinResolveResult::Success) - { - return PinResult; - } - } - } - - return Super::TrySupplyDataPinAsVector_Implementation(PinName); -} - -FFlowDataPinResult_Rotator UFlowNode_ExecuteComponent::TrySupplyDataPinAsRotator_Implementation(const FName& PinName) const -{ - if (UActorComponent* ResolvedComp = GetResolvedComponent()) - { - if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) - { - const FFlowDataPinResult_Rotator PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsRotator(ResolvedComp, PinName); - if (PinResult.Result == EFlowDataPinResolveResult::Success) - { - return PinResult; - } - } - } - - return Super::TrySupplyDataPinAsRotator_Implementation(PinName); -} - -FFlowDataPinResult_Transform UFlowNode_ExecuteComponent::TrySupplyDataPinAsTransform_Implementation(const FName& PinName) const -{ - if (UActorComponent* ResolvedComp = GetResolvedComponent()) - { - if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) - { - const FFlowDataPinResult_Transform PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsTransform(ResolvedComp, PinName); - if (PinResult.Result == EFlowDataPinResolveResult::Success) - { - return PinResult; - } - } - } - - return Super::TrySupplyDataPinAsTransform_Implementation(PinName); -} - -FFlowDataPinResult_GameplayTag UFlowNode_ExecuteComponent::TrySupplyDataPinAsGameplayTag_Implementation(const FName& PinName) const -{ - if (UActorComponent* ResolvedComp = GetResolvedComponent()) - { - if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) - { - const FFlowDataPinResult_GameplayTag PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsGameplayTag(ResolvedComp, PinName); - if (PinResult.Result == EFlowDataPinResolveResult::Success) - { - return PinResult; - } - } - } - - return Super::TrySupplyDataPinAsGameplayTag_Implementation(PinName); -} - -FFlowDataPinResult_GameplayTagContainer UFlowNode_ExecuteComponent::TrySupplyDataPinAsGameplayTagContainer_Implementation(const FName& PinName) const -{ - if (UActorComponent* ResolvedComp = GetResolvedComponent()) - { - if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) - { - const FFlowDataPinResult_GameplayTagContainer PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsGameplayTagContainer(ResolvedComp, PinName); - if (PinResult.Result == EFlowDataPinResolveResult::Success) - { - return PinResult; - } - } - } - - return Super::TrySupplyDataPinAsGameplayTagContainer_Implementation(PinName); -} - -FFlowDataPinResult_InstancedStruct UFlowNode_ExecuteComponent::TrySupplyDataPinAsInstancedStruct_Implementation(const FName& PinName) const -{ - if (UActorComponent* ResolvedComp = GetResolvedComponent()) - { - if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) - { - const FFlowDataPinResult_InstancedStruct PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsInstancedStruct(ResolvedComp, PinName); - if (PinResult.Result == EFlowDataPinResolveResult::Success) - { - return PinResult; - } - } - } - - return Super::TrySupplyDataPinAsInstancedStruct_Implementation(PinName); -} +#endif // WITH_EDITOR -FFlowDataPinResult_Object UFlowNode_ExecuteComponent::TrySupplyDataPinAsObject_Implementation(const FName& PinName) const +void UFlowNode_ExecuteComponent::GatherPotentialPropertyOwnersForDataPins(TArray& InOutOwners) const { - if (UActorComponent* ResolvedComp = GetResolvedComponent()) - { - if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) - { - const FFlowDataPinResult_Object PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsObject(ResolvedComp, PinName); - if (PinResult.Result == EFlowDataPinResolveResult::Success) - { - return PinResult; - } - } - } + Super::GatherPotentialPropertyOwnersForDataPins(InOutOwners); - return Super::TrySupplyDataPinAsObject_Implementation(PinName); -} - -FFlowDataPinResult_Class UFlowNode_ExecuteComponent::TrySupplyDataPinAsClass_Implementation(const FName& PinName) const -{ - if (UActorComponent* ResolvedComp = GetResolvedComponent()) + // Can also source properties from the resolved component (runtime) or expected component (in-editor) + const UActorComponent* ResolvedComp = GetResolvedOrExpectedComponent(); + if (IsValid(ResolvedComp)) { - if (IFlowDataPinValueSupplierInterface* PinSupplierInterface = Cast(ResolvedComp)) - { - const FFlowDataPinResult_Class PinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsClass(ResolvedComp, PinName); - if (PinResult.Result == EFlowDataPinResolveResult::Success) - { - return PinResult; - } - } + InOutOwners.AddUnique(ResolvedComp); } - - return Super::TrySupplyDataPinAsClass_Implementation(PinName); } bool UFlowNode_ExecuteComponent::TryInjectComponent() @@ -624,6 +380,25 @@ bool UFlowNode_ExecuteComponent::TryInjectComponent() return true; } +const UActorComponent* UFlowNode_ExecuteComponent::GetResolvedOrExpectedComponent() const +{ + const UActorComponent* ResolvedComp = ComponentRef.GetResolvedComponent(); + if (IsValid(ResolvedComp)) + { + return ResolvedComp; + } + +#if WITH_EDITOR + const UActorComponent* ExpectedComp = TryGetExpectedComponent(); + if (IsValid(ExpectedComp)) + { + return ExpectedComp; + } +#endif + + return nullptr; +} + UActorComponent* UFlowNode_ExecuteComponent::TryResolveComponent() { UActorComponent* ResolvedComp = ComponentRef.GetResolvedComponent(); @@ -753,7 +528,7 @@ EDataValidationResult UFlowNode_ExecuteComponent::ValidateNode() const bool bHasComponent = ComponentRef.IsConfigured(); if (!bHasComponent) { - ValidationLog.Error(TEXT("ExectuteComponent requires a valid Compoennt reference"), this); + ValidationLog.Error(TEXT("ExecuteComponent requires a valid Compoennt reference"), this); return EDataValidationResult::Invalid; } @@ -816,7 +591,7 @@ TSubclassOf UFlowNode_ExecuteComponent::TryGetExpectedActorOwnerClass() return nullptr; } -FText UFlowNode_ExecuteComponent::GetNodeTitle() const +FText UFlowNode_ExecuteComponent::K2_GetNodeTitle_Implementation() const { if (UFlowSettings::Get()->bUseAdaptiveNodeTitles) { @@ -868,7 +643,7 @@ FText UFlowNode_ExecuteComponent::GetNodeTitle() const } } - return Super::GetNodeTitle(); + return Super::K2_GetNodeTitle_Implementation(); } #endif // WITH_EDITOR diff --git a/Source/Flow/Private/Nodes/Actor/FlowNode_PlayLevelSequence.cpp b/Source/Flow/Private/Nodes/Actor/FlowNode_PlayLevelSequence.cpp index 9cfbd4f2b..fa1b636a2 100644 --- a/Source/Flow/Private/Nodes/Actor/FlowNode_PlayLevelSequence.cpp +++ b/Source/Flow/Private/Nodes/Actor/FlowNode_PlayLevelSequence.cpp @@ -75,9 +75,10 @@ TArray UFlowNode_PlayLevelSequence::GetContextOutputs() const { for (const FString& EventName : FlowSection->GetAllEntryPoints()) { - if (!EventName.IsEmpty() && !Pins.Contains(EventName)) + FFlowPin NewEventPin(EventName); + if (!EventName.IsEmpty() && !Pins.Contains(NewEventPin)) { - Pins.Emplace(EventName); + Pins.Emplace(NewEventPin); } } } diff --git a/Source/Flow/Private/Nodes/Developer/FlowNode_Log.cpp b/Source/Flow/Private/Nodes/Developer/FlowNode_Log.cpp index 5a6f66d0a..794625eed 100644 --- a/Source/Flow/Private/Nodes/Developer/FlowNode_Log.cpp +++ b/Source/Flow/Private/Nodes/Developer/FlowNode_Log.cpp @@ -29,62 +29,80 @@ UFlowNode_Log::UFlowNode_Log(const FObjectInitializer& ObjectInitializer) void UFlowNode_Log::ExecuteInput(const FName& PinName) { // Get the Message from either the default (Message property) or the data pin (if connected) - FFlowDataPinResult_String MessageResult = TryResolveDataPinAsString(GET_MEMBER_NAME_CHECKED(UFlowNode_Log, Message)); + FString ResolvedMessage; + const EFlowDataPinResolveResult MessageResult = TryResolveDataPinValue(GET_MEMBER_NAME_CHECKED(ThisClass, Message), ResolvedMessage); - if (MessageResult.Result == EFlowDataPinResolveResult::FailedMissingPin) + // #FlowDataPinLegacy - retire this backward compatibility when we remove legacy data pin support? + FLOW_ASSERT_ENUM_MAX(EFlowDataPinResolveResult, 8); + if (MessageResult == EFlowDataPinResolveResult::FailedUnknownPin) { // Handle lookup of a FlowNode_Log that predated DataPins - MessageResult.Result = EFlowDataPinResolveResult::Success; - MessageResult.SetValue(Message); + ResolvedMessage = Message; } + // -- // Format Message with named properties - FText FormattedText; - if (TryFormatTextWithNamedPropertiesAsParameters(FText::FromString(MessageResult.Value), FormattedText)) - { - MessageResult.Value = FormattedText.ToString(); - } + FText FormattedMessage = FText::FromString(ResolvedMessage); + (void) TryFormatTextWithNamedPropertiesAsParameters(FormattedMessage, FormattedMessage); // Display the message - check(MessageResult.Result == EFlowDataPinResolveResult::Success); switch (Verbosity) { case EFlowLogVerbosity::Error: - UE_LOG(LogFlow, Error, TEXT("%s"), *MessageResult.Value); + UE_LOG(LogFlow, Error, TEXT("%s"), *FormattedMessage.ToString()); break; case EFlowLogVerbosity::Warning: - UE_LOG(LogFlow, Warning, TEXT("%s"), *MessageResult.Value); + UE_LOG(LogFlow, Warning, TEXT("%s"), *FormattedMessage.ToString()); break; case EFlowLogVerbosity::Display: - UE_LOG(LogFlow, Display, TEXT("%s"), *MessageResult.Value); + UE_LOG(LogFlow, Display, TEXT("%s"), *FormattedMessage.ToString()); break; case EFlowLogVerbosity::Log: - UE_LOG(LogFlow, Log, TEXT("%s"), *MessageResult.Value); + UE_LOG(LogFlow, Log, TEXT("%s"), *FormattedMessage.ToString()); break; case EFlowLogVerbosity::Verbose: - UE_LOG(LogFlow, Verbose, TEXT("%s"), *MessageResult.Value); + UE_LOG(LogFlow, Verbose, TEXT("%s"), *FormattedMessage.ToString()); break; case EFlowLogVerbosity::VeryVerbose: - UE_LOG(LogFlow, VeryVerbose, TEXT("%s"), *MessageResult.Value); + UE_LOG(LogFlow, VeryVerbose, TEXT("%s"), *FormattedMessage.ToString()); break; default: ; } if (bPrintToScreen) { - GEngine->AddOnScreenDebugMessage(-1, Duration, TextColor, MessageResult.Value); + GEngine->AddOnScreenDebugMessage(-1, Duration, TextColor, FormattedMessage.ToString()); } TriggerFirstOutput(true); } #if WITH_EDITOR +void UFlowNode_Log::PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChainEvent) +{ + const auto& Property = PropertyChainEvent.PropertyChain.GetActiveMemberNode()->GetValue(); + + if (Property->GetFName() == GET_MEMBER_NAME_CHECKED(ThisClass, NamedProperties)) + { + for (FFlowNamedDataPinProperty& NamedProperty : NamedProperties) + { + const UScriptStruct* ScriptStruct = NamedProperty.DataPinValue.GetScriptStruct(); + if (IsValid(ScriptStruct) && ScriptStruct->IsChildOf()) + { + FFlowDataPinValue& Value = NamedProperty.DataPinValue.GetMutable(); + Value.bIsInputPin = true; + } + } + } + + Super::PostEditChangeChainProperty(PropertyChainEvent); +} void UFlowNode_Log::UpdateNodeConfigText_Implementation() { constexpr bool bErrorIfInputPinNotFound = false; - const bool bIsInputConnected = IsInputConnected(GET_MEMBER_NAME_CHECKED(UFlowNode_Log, Message), bErrorIfInputPinNotFound); + const bool bIsInputConnected = IsInputConnected(GET_MEMBER_NAME_CHECKED(ThisClass, Message), bErrorIfInputPinNotFound); if (bIsInputConnected) { diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 048339277..be876bee5 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -6,7 +6,9 @@ #include "FlowAsset.h" #include "FlowSettings.h" #include "Interfaces/FlowNodeWithExternalDataPinSupplierInterface.h" -#include "Types/FlowDataPinProperties.h" +#include "Types/FlowPinType.h" +#include "Types/FlowDataPinValue.h" +#include "Types/FlowAutoDataPinsWorkingData.h" #include "Components/ActorComponent.h" #if WITH_EDITOR @@ -59,21 +61,62 @@ void UFlowNode::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEve if (PropertyName == GET_MEMBER_NAME_CHECKED(UFlowNode, InputPins) || PropertyName == GET_MEMBER_NAME_CHECKED(UFlowNode, OutputPins) || MemberPropertyName == GET_MEMBER_NAME_CHECKED(UFlowNode, InputPins) || MemberPropertyName == GET_MEMBER_NAME_CHECKED(UFlowNode, OutputPins)) { - // Potentially need to rebuild the pins from the this node + // Potentially need to rebuild the pins from this node OnReconstructionRequested.ExecuteIfBound(); } } +EDataValidationResult UFlowNode::ValidateNode() +{ + EDataValidationResult ValidationResult = EDataValidationResult::Valid; + + // Validate that output and input pins have unique names + TSet UniquePinNames; + ValidateFlowPinArrayIsUnique(InputPins, UniquePinNames, ValidationResult); + ValidateFlowPinArrayIsUnique(OutputPins, UniquePinNames, ValidationResult); + + return ValidationResult; +} + +void UFlowNode::ValidateFlowPinArrayIsUnique(const TArray& FlowPins, TSet& InOutUniquePinNames, EDataValidationResult& InOutResult) +{ + for (const FFlowPin& FlowPin : FlowPins) + { + const FName& ThisPinName = FlowPin.PinName; + if (InOutUniquePinNames.Contains(ThisPinName)) + { + ValidationLog.Warning( + *FString::Printf( + TEXT("All pin names on a flow node must be unique, pin name %s is duplicated"), + *ThisPinName.ToString()), + this); + + InOutResult = EDataValidationResult::Invalid; + } + else + { + InOutUniquePinNames.Add(FlowPin.PinName); + } + } +} + +#endif + void UFlowNode::PostLoad() { Super::PostLoad(); +#if WITH_EDITOR // fix Class Default Object FixNode(nullptr); -} - #endif + if (!HasAnyFlags(RF_ArchetypeObject | RF_ClassDefaultObject)) + { + FixupDataPinTypes(); + } +} + bool UFlowNode::IsSupportedInputPinName(const FName& PinName) const { const FFlowPin* InputPin = FindFlowPinByName(PinName, InputPins); @@ -364,11 +407,6 @@ void UFlowNode::RemoveUserOutput(const FName& PinName) } } -void UFlowNode::SetPinNameToBoundPropertyNameMap(const TMap& Map) -{ - PinNameToBoundPropertyNameMap = Map; -} - void UFlowNode::SetAutoInputDataPins(const TArray& AutoInputPins) { AutoInputDataPins = AutoInputPins; @@ -379,30 +417,163 @@ void UFlowNode::SetAutoOutputDataPins(const TArray& AutoOutputPins) AutoOutputDataPins = AutoOutputPins; } +void UFlowNode::SetConnections(const TMap& InConnections) +{ + TMap OldConnections = Connections; + Connections = InConnections; + OnConnectionsChanged(OldConnections); +} + #endif // WITH_EDITOR -bool UFlowNode::CanSupplyDataPinValues_Implementation() const +// #FlowDataPinLegacy +void UFlowNode::FixupDataPinTypes() +{ + FixupDataPinTypesForArray(InputPins); + FixupDataPinTypesForArray(OutputPins); +#if WITH_EDITOR + FixupDataPinTypesForArray(AutoInputDataPins); + FixupDataPinTypesForArray(AutoOutputDataPins); +#endif +} + +void UFlowNode::FixupDataPinTypesForArray(TArray& MutableDataPinArray) +{ + for (FFlowPin& MutableFlowPin : MutableDataPinArray) + { + FixupDataPinTypesForPin(MutableFlowPin); + } +} + +void UFlowNode::FixupDataPinTypesForPin(FFlowPin& MutableDataPin) +{ + const FFlowPinTypeName NewPinTypeName = FFlowPin::GetPinTypeNameForLegacyPinType(MutableDataPin.PinType); + + if (!NewPinTypeName.IsNone()) + { + MutableDataPin.SetPinTypeName(NewPinTypeName); + } + + if (MutableDataPin.GetPinTypeName().IsNone()) + { + // Ensure we have a pin type even if the enum was invalid before + MutableDataPin.SetPinTypeName(FFlowPinType_Exec::GetPinTypeNameStatic()); + } + + MutableDataPin.PinType = EFlowPinType::Invalid; +} + +// -- + +bool UFlowNode::TryFindPropertyByPinName( + const UObject& PropertyOwnerObject, + const FName& PinName, + const FProperty*& OutFoundProperty, + TInstancedStruct& OutFoundInstancedStruct) const +{ + return UFlowNode::TryFindPropertyByPinName_Static(PropertyOwnerObject, PinName, OutFoundProperty, OutFoundInstancedStruct); +} + +bool UFlowNode::TryFindPropertyByPinName_Static( + const UObject& PropertyOwnerObject, + const FName& PinName, + const FProperty*& OutFoundProperty, + TInstancedStruct& OutFoundInstancedStruct) { - if (!PinNameToBoundPropertyNameMap.IsEmpty()) + // Try direct property match + OutFoundProperty = PropertyOwnerObject.GetClass()->FindPropertyByName(PinName); + if (OutFoundProperty) { + const FStructProperty* StructProperty = CastField(OutFoundProperty); + if (StructProperty && StructProperty->Struct->IsChildOf(FFlowDataPinValue::StaticStruct())) + { + // Initialize to match property's struct + OutFoundInstancedStruct.InitializeAsScriptStruct(StructProperty->Struct); + + StructProperty->GetValue_InContainer(&PropertyOwnerObject, OutFoundInstancedStruct.GetMutableMemory()); + return true; + } + + // Raw property (e.g., bool, TArray) is valid return true; } return false; } +void UFlowNode::GatherPotentialPropertyOwnersForDataPins(TArray& InOutOwners) const +{ + // TODO (gtaylor) Also add any AddOns that can supply data pins, when/if we want to add AddOn data pin supply support + + InOutOwners.AddUnique(this); +} + +FFlowDataPinResult UFlowNode::TrySupplyDataPin_Implementation(FName PinName) const +{ + const FFlowPin* FlowPin = FindOutputPinByName(PinName); + if (!FlowPin) + { + // Also look in the Input Pins (for supplying default values for unconnected pins) + FlowPin = FindInputPinByName(PinName); + if (!FlowPin) + { + return FFlowDataPinResult(EFlowDataPinResolveResult::FailedUnknownPin); + } + } + + const FFlowPinType* DataPinType = FlowPin->ResolveFlowPinType(); + if (!DataPinType) + { + return FFlowDataPinResult(EFlowDataPinResolveResult::FailedMismatchedType); + } + + FFlowDataPinResult SuppliedResult; + if (TryGatherPropertyOwnersAndPopulateResult(PinName, *DataPinType, *FlowPin, SuppliedResult)) + { + return SuppliedResult; + } + + return FFlowDataPinResult(EFlowDataPinResolveResult::FailedUnknownPin); +} + +bool UFlowNode::TryGatherPropertyOwnersAndPopulateResult( + const FName& PinName, + const FFlowPinType& DataPinType, + const FFlowPin& FlowPin, + FFlowDataPinResult& OutSuppliedResult) const +{ + // Gather all of the potential providers for this DataPin + TArray PropertyOwnerObjects; + GatherPotentialPropertyOwnersForDataPins(PropertyOwnerObjects); + + // Look through all of the potential providers + for (const UObject* PropertyOwnerObject : PropertyOwnerObjects) + { + const UFlowNode& FlowNodeThis = *this; + + checkf(IsValid(PropertyOwnerObject), TEXT("Every UObject provided by GatherPotentialPropertyOwnersForDataPins must be valid")); + + if (DataPinType.PopulateResult(*PropertyOwnerObject, FlowNodeThis, FlowPin, OutSuppliedResult)) + { + return true; + } + } + + return false; +} + bool UFlowNode::TryGetFlowDataPinSupplierDatasForPinName( const FName& PinName, - TArray& InOutPinValueSupplierDatas) const + TFlowPinValueSupplierDataArray& InOutPinValueSupplierDatas) const { const IFlowDataPinValueSupplierInterface* ThisAsPinValueSupplier = Cast(this); - // This function will build the priority-ordered array of data suppliers for a given PinName. + // This function will build the inverse-priority-ordered array of data suppliers for a given PinName. // It works in two modes: // - Standard case - Add a connected node as the priority supplier, and this node as the default value supplier // - Exception case - for External data supplied nodes, we recurse (below) to crawl further and add the supplier // for the external supplier's node. In practice, this is a node (A) connected to a Start node, which is - // supplied by its outer SubGraph node, which sources its values from the nodes tha are connected to the external inputs + // supplied by its outer SubGraph node, which sources its values from the nodes that are connected to the external inputs // that the subgraph node added as inputs for its instanced subgraph). The external supplier's value has top priority, // then it falls to the standard case sources (as above). @@ -415,7 +586,7 @@ bool UFlowNode::TryGetFlowDataPinSupplierDatasForPinName( NewPinValueSupplier.SupplierPinName = PinName; // Put this node as the backup supplier - InOutPinValueSupplierDatas.Insert(NewPinValueSupplier, 0); + InOutPinValueSupplierDatas.Add(NewPinValueSupplier); } // If the pin is connected, try to add the connected node as the priority supplier @@ -434,7 +605,7 @@ bool UFlowNode::TryGetFlowDataPinSupplierDatasForPinName( { ConnectedPinValueSupplier.PinValueSupplier = SupplierFlowNodeAsInterface; - InOutPinValueSupplierDatas.Insert(ConnectedPinValueSupplier, 0); + InOutPinValueSupplierDatas.Add(ConnectedPinValueSupplier); } // Exception case for nodes with external suppliers, recurse here to crawl further @@ -452,49 +623,6 @@ bool UFlowNode::TryGetFlowDataPinSupplierDatasForPinName( return !InOutPinValueSupplierDatas.IsEmpty(); } -bool UFlowNode::TryFindPropertyByPinName( - const FName& PinName, - const FProperty*& OutFoundProperty, - TInstancedStruct& OutFoundInstancedStruct, - EFlowDataPinResolveResult& InOutResult) const -{ - const FName* RemappedPinName = PinNameToBoundPropertyNameMap.Find(PinName); - if (!RemappedPinName) - { - InOutResult = EFlowDataPinResolveResult::FailedUnknownPin; - - return false; - } - - if (!TryFindPropertyByRemappedPinName(*RemappedPinName, OutFoundProperty, OutFoundInstancedStruct, InOutResult)) - { - return false; - } - - return true; -} - -bool UFlowNode::TryFindPropertyByRemappedPinName( - const FName& RemappedPinName, - const FProperty*& OutFoundProperty, - TInstancedStruct& OutFoundInstancedStruct, - EFlowDataPinResolveResult& InOutResult) const -{ - const UClass* ThisClass = GetClass(); - OutFoundProperty = ThisClass->FindPropertyByName(RemappedPinName); - - if (!OutFoundProperty) - { - LogError(FString::Printf(TEXT("Could not find property %s, but expected to"), *RemappedPinName.ToString()), EFlowOnScreenMessageType::Temporary); - - InOutResult = EFlowDataPinResolveResult::FailedWithError; - - return false; - } - - return true; -} - TSet UFlowNode::GatherConnectedNodes() const { TSet Result; @@ -576,16 +704,16 @@ bool UFlowNode::IsInputConnected(const FFlowPin& FlowPin) const return false; } - if (FlowPin.IsDataPin()) - { - return FindConnectedNodeForPinFast(FlowPin.PinName); - } - else + if (FlowPin.IsExecPin()) { // We don't cache the input exec pins for fast lookup in Connections, so use the slow path for them: return FindConnectedNodeForPinSlow(FlowPin.PinName); } + else + { + return FindConnectedNodeForPinFast(FlowPin.PinName); + } } bool UFlowNode::IsOutputConnected(const FFlowPin& FlowPin) const @@ -669,82 +797,30 @@ bool UFlowNode::FindConnectedNodeForPinSlow(const FName& PinName, FGuid* OutGuid return false; } -// Must implement TrySupplyDataPinAs... for every EFlowPinType -FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); - -FFlowDataPinResult_Bool UFlowNode::TrySupplyDataPinAsBool_Implementation(const FName& PinName) const -{ - return TrySupplyDataPinAsType(PinName); -} - -FFlowDataPinResult_Int UFlowNode::TrySupplyDataPinAsInt_Implementation(const FName& PinName) const -{ - return TrySupplyDataPinAsNumericType(PinName); -} - -FFlowDataPinResult_Float UFlowNode::TrySupplyDataPinAsFloat_Implementation(const FName& PinName) const -{ - return TrySupplyDataPinAsNumericType(PinName); -} - -FFlowDataPinResult_Name UFlowNode::TrySupplyDataPinAsName_Implementation(const FName& PinName) const -{ - return TrySupplyDataPinAsAnyTextType(PinName); -} - -FFlowDataPinResult_String UFlowNode::TrySupplyDataPinAsString_Implementation(const FName& PinName) const -{ - return TrySupplyDataPinAsAnyTextType(PinName); -} - -FFlowDataPinResult_Text UFlowNode::TrySupplyDataPinAsText_Implementation(const FName& PinName) const -{ - return TrySupplyDataPinAsAnyTextType(PinName); -} - -FFlowDataPinResult_Enum UFlowNode::TrySupplyDataPinAsEnum_Implementation(const FName& PinName) const -{ - return TrySupplyDataPinAsEnumType(PinName); -} - -FFlowDataPinResult_Vector UFlowNode::TrySupplyDataPinAsVector_Implementation(const FName& PinName) const -{ - return TrySupplyDataPinAsStructType(PinName); -} - -FFlowDataPinResult_Rotator UFlowNode::TrySupplyDataPinAsRotator_Implementation(const FName& PinName) const -{ - return TrySupplyDataPinAsStructType(PinName); -} - -FFlowDataPinResult_Transform UFlowNode::TrySupplyDataPinAsTransform_Implementation(const FName& PinName) const -{ - return TrySupplyDataPinAsStructType(PinName); -} - -FFlowDataPinResult_GameplayTag UFlowNode::TrySupplyDataPinAsGameplayTag_Implementation(const FName& PinName) const -{ - return TrySupplyDataPinAsStructType(PinName); -} - -FFlowDataPinResult_GameplayTagContainer UFlowNode::TrySupplyDataPinAsGameplayTagContainer_Implementation(const FName& PinName) const -{ - return TrySupplyDataPinAsStructType(PinName); -} - -FFlowDataPinResult_InstancedStruct UFlowNode::TrySupplyDataPinAsInstancedStruct_Implementation(const FName& PinName) const -{ - return TrySupplyDataPinAsStructType(PinName); -} - -FFlowDataPinResult_Object UFlowNode::TrySupplyDataPinAsObject_Implementation(const FName& PinName) const +TArray UFlowNode::GetKnownConnectionsToPin(const FConnectedPin& Pin) const { - return TrySupplyDataPinAsUObjectType(PinName); -} + TArray ConnectedPins; -FFlowDataPinResult_Class UFlowNode::TrySupplyDataPinAsClass_Implementation(const FName& PinName) const -{ - return TrySupplyDataPinAsUClassType(PinName); + if (Pin.NodeGuid == NodeGuid) + { + const FConnectedPin& Connection = Connections.FindRef(Pin.PinName); + if (Connection.NodeGuid.IsValid()) + { + ConnectedPins.Add(Connection); + } + } + else + { + for (const TPair& Connection : Connections) + { + if (Connection.Value.NodeGuid == Pin.NodeGuid && Connection.Value.PinName == Pin.PinName) + { + ConnectedPins.Emplace(NodeGuid, Connection.Key); + } + } + } + + return ConnectedPins; } void UFlowNode::RecursiveFindNodesByClass(UFlowNode* Node, const TSubclassOf Class, uint8 Depth, TArray& OutNodes) @@ -888,7 +964,7 @@ void UFlowNode::TriggerOutput(const FName PinName, const bool bFinish /*= false* if (OutputPins.Contains(PinName) && Connections.Contains(PinName)) { const FConnectedPin FlowPin = GetConnection(PinName); - GetFlowAsset()->TriggerInput(FlowPin.NodeGuid, FlowPin.PinName); + GetFlowAsset()->TriggerInput(FlowPin.NodeGuid, FlowPin.PinName, FConnectedPin(GetGuid(), PinName)); } } @@ -1015,6 +1091,21 @@ TArray UFlowNode::GetPinRecords(const FName& PinName, const EEdGraph } } +void UFlowNode::AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const +{ + // Gather all of the potential providers for this DataPin + TArray PropertyOwnerObjects; + GatherPotentialPropertyOwnersForDataPins(PropertyOwnerObjects); + + // GenerateDataPins for all of the potential providers + for (const UObject* PropertyOwnerObject : PropertyOwnerObjects) + { + checkf(IsValid(PropertyOwnerObject), TEXT("Every UObject provided by GatherPotentialPropertyOwnersForDataPins must be valid")); + + InOutWorkingData.AddFlowDataPinsForClassProperties(*PropertyOwnerObject); + } +} + #endif FString UFlowNode::GetIdentityTagDescription(const FGameplayTag& Tag) diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp index 85420b9ba..cfd97e4cd 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBase.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -2,13 +2,17 @@ #include "Nodes/FlowNodeBase.h" -#include "AddOns/FlowNodeAddOn.h" #include "FlowAsset.h" #include "FlowLogChannels.h" #include "FlowSubsystem.h" #include "FlowTypes.h" +#include "AddOns/FlowNodeAddOn.h" #include "Interfaces/FlowDataPinValueSupplierInterface.h" +#include "Interfaces/FlowNamedPropertiesSupplierInterface.h" #include "Types/FlowArray.h" +#include "Types/FlowDataPinResults.h" +#include "Types/FlowPinTypesStandard.h" +#include "Types/FlowNamedDataPinProperty.h" #include "Components/ActorComponent.h" #if WITH_EDITOR @@ -136,7 +140,7 @@ void UFlowNodeBase::OnActivate() void UFlowNodeBase::ExecuteInputForSelfAndAddOns(const FName& PinName) { // AddOns can introduce input pins to Nodes without the Node being aware of the addition. - // To ensure that Nodes and AddOns only get the input pins signalled that they expect, + // To ensure that Nodes and AddOns only get the input pins signaled that they expect, // we are filtering the PinName vs. the expected InputPins before carrying on with the ExecuteInput if (IsSupportedInputPinName(PinName)) @@ -319,39 +323,6 @@ UObject* UFlowNodeBase::TryGetRootFlowObjectOwner() const return nullptr; } -IFlowOwnerInterface* UFlowNodeBase::GetFlowOwnerInterface() const -{ - const UFlowAsset* FlowAsset = GetFlowAsset(); - if (!IsValid(FlowAsset)) - { - return nullptr; - } - - const UClass* ExpectedOwnerClass = FlowAsset->GetExpectedOwnerClass(); - if (!IsValid(ExpectedOwnerClass)) - { - return nullptr; - } - - UObject* RootFlowOwner = FlowAsset->GetOwner(); - if (!IsValid(RootFlowOwner)) - { - return nullptr; - } - - if (IFlowOwnerInterface* FlowOwnerInterface = TryGetFlowOwnerInterfaceFromRootFlowOwner(*RootFlowOwner, *ExpectedOwnerClass)) - { - return FlowOwnerInterface; - } - - if (IFlowOwnerInterface* FlowOwnerInterface = TryGetFlowOwnerInterfaceActor(*RootFlowOwner, *ExpectedOwnerClass)) - { - return FlowOwnerInterface; - } - - return nullptr; -} - TArray UFlowNodeBase::BuildFlowNodeBaseAncestorChain(UFlowNodeBase& FromFlowNodeBase, bool bIncludeFromFlowNodeBase) { TArray AncestorChain; @@ -374,47 +345,6 @@ TArray UFlowNodeBase::BuildFlowNodeBaseAncestorChain(UFlowNodeBa return AncestorChain; } -IFlowOwnerInterface* UFlowNodeBase::TryGetFlowOwnerInterfaceFromRootFlowOwner(UObject& RootFlowOwner, const UClass& ExpectedOwnerClass) -{ - const UClass* RootFlowOwnerClass = RootFlowOwner.GetClass(); - if (!IsValid(RootFlowOwnerClass)) - { - return nullptr; - } - - if (!RootFlowOwnerClass->IsChildOf(&ExpectedOwnerClass)) - { - return nullptr; - } - - // If the immediate owner is the expected class type, return its FlowOwnerInterface - return CastChecked(&RootFlowOwner); -} - -IFlowOwnerInterface* UFlowNodeBase::TryGetFlowOwnerInterfaceActor(UObject& RootFlowOwner, const UClass& ExpectedOwnerClass) -{ - // Special case if the immediate owner is a component, also consider the component's owning actor - const UActorComponent* FlowComponent = Cast(&RootFlowOwner); - if (!IsValid(FlowComponent)) - { - return nullptr; - } - - AActor* ActorOwner = FlowComponent->GetOwner(); - if (!IsValid(ActorOwner)) - { - return nullptr; - } - - const UClass* ActorOwnerClass = ActorOwner->GetClass(); - if (!ActorOwnerClass->IsChildOf(&ExpectedOwnerClass)) - { - return nullptr; - } - - return CastChecked(ActorOwner); -} - EFlowAddOnAcceptResult UFlowNodeBase::AcceptFlowNodeAddOnChild_Implementation( const UFlowNodeAddOn* AddOnTemplate, const TArray& AdditionalAddOnsToAssumeAreChildren) const @@ -697,61 +627,6 @@ bool UFlowNodeBase::GetDynamicTitleColor(FLinearColor& OutColor) const return false; } -FText UFlowNodeBase::GetNodeTitle() const -{ - if (GetClass()->ClassGeneratedBy) - { - const FString& BlueprintTitle = Cast(GetClass()->ClassGeneratedBy)->BlueprintDisplayName; - if (!BlueprintTitle.IsEmpty()) - { - return FText::FromString(BlueprintTitle); - } - } - - static const FName NAME_DisplayName(TEXT("DisplayName")); - if (bDisplayNodeTitleWithoutPrefix && !GetClass()->HasMetaData(NAME_DisplayName)) - { - return GetGeneratedDisplayName(); - } - - return GetClass()->GetDisplayNameText(); -} - -FText UFlowNodeBase::GetNodeToolTip() const -{ - if (GetClass()->ClassGeneratedBy) - { - const FString& BlueprintToolTip = Cast(GetClass()->ClassGeneratedBy)->BlueprintDescription; - if (!BlueprintToolTip.IsEmpty()) - { - return FText::FromString(BlueprintToolTip); - } - } - - static const FName NAME_Tooltip(TEXT("Tooltip")); - if (bDisplayNodeTitleWithoutPrefix && !GetClass()->HasMetaData(NAME_Tooltip)) - { - return GetGeneratedDisplayName(); - } - - // GetClass()->GetToolTipText() can return meta = (DisplayName = ... ), but ignore BlueprintDisplayName even if it is BP Node - if (GetClass()->ClassGeneratedBy) - { - const FString& BlueprintTitle = Cast(GetClass()->ClassGeneratedBy)->BlueprintDisplayName; - if (!BlueprintTitle.IsEmpty()) - { - return FText::FromString(BlueprintTitle); - } - } - - return GetClass()->GetToolTipText(); -} - -FText UFlowNodeBase::GetNodeConfigText() const -{ - return DevNodeConfigText; -} - FText UFlowNodeBase::GetGeneratedDisplayName() const { static const FName NAME_GeneratedDisplayName(TEXT("GeneratedDisplayName")); @@ -825,15 +700,116 @@ FString UFlowNodeBase::GetNodeDescription() const { return K2_GetNodeDescription(); } + +bool UFlowNodeBase::CanModifyFlowDataPinType() const +{ + return !IsPlacedInFlowAsset() || IsFlowNamedPropertiesSupplier(); +} + +bool UFlowNodeBase::ShowFlowDataPinValueInputPinCheckbox() const +{ + const bool bIsPlacedInFlowAsset = IsPlacedInFlowAsset(); + return !bIsPlacedInFlowAsset; +} + +bool UFlowNodeBase::ShowFlowDataPinValueClassFilter(const FFlowDataPinValue* Value) const +{ + const bool bIsPlacedInFlowAsset = IsPlacedInFlowAsset(); + const bool bIsFlowNamedPropertiesSupplier = IsFlowNamedPropertiesSupplier(); + return !bIsPlacedInFlowAsset || bIsFlowNamedPropertiesSupplier; +} + +bool UFlowNodeBase::CanEditFlowDataPinValueClassFilter(const FFlowDataPinValue* Value) const +{ + const bool bIsPlacedInFlowAsset = IsPlacedInFlowAsset(); + const bool bIsFlowNamedPropertiesSupplier = IsFlowNamedPropertiesSupplier(); + return !bIsPlacedInFlowAsset || bIsFlowNamedPropertiesSupplier; +} + +bool UFlowNodeBase::IsPlacedInFlowAsset() const +{ + return GetFlowAsset() != nullptr; +} + +bool UFlowNodeBase::IsFlowNamedPropertiesSupplier() const +{ + return Implements(); +} + +#endif + +FText UFlowNodeBase::K2_GetNodeTitle_Implementation() const +{ +#if WITH_EDITOR + if (GetClass()->ClassGeneratedBy) + { + const FString& BlueprintTitle = Cast(GetClass()->ClassGeneratedBy)->BlueprintDisplayName; + if (!BlueprintTitle.IsEmpty()) + { + return FText::FromString(BlueprintTitle); + } + } + + static const FName NAME_DisplayName(TEXT("DisplayName")); + if (bDisplayNodeTitleWithoutPrefix && !GetClass()->HasMetaData(NAME_DisplayName)) + { + return GetGeneratedDisplayName(); + } + + return GetClass()->GetDisplayNameText(); +#else + return FText::GetEmpty(); +#endif +} + +FText UFlowNodeBase::K2_GetNodeToolTip_Implementation() const +{ +#if WITH_EDITOR + if (GetClass()->ClassGeneratedBy) + { + const FString& BlueprintToolTip = Cast(GetClass()->ClassGeneratedBy)->BlueprintDescription; + if (!BlueprintToolTip.IsEmpty()) + { + return FText::FromString(BlueprintToolTip); + } + } + + static const FName NAME_Tooltip(TEXT("Tooltip")); + if (bDisplayNodeTitleWithoutPrefix && !GetClass()->HasMetaData(NAME_Tooltip)) + { + return GetGeneratedDisplayName(); + } + + // GetClass()->GetToolTipText() can return meta = (DisplayName = ... ), but ignore BlueprintDisplayName even if it is BP Node + if (GetClass()->ClassGeneratedBy) + { + const FString& BlueprintTitle = Cast(GetClass()->ClassGeneratedBy)->BlueprintDisplayName; + if (!BlueprintTitle.IsEmpty()) + { + return FText::FromString(BlueprintTitle); + } + } + + return GetClass()->GetToolTipText(); +#else + return FText::GetEmpty(); #endif +} + +FText UFlowNodeBase::GetNodeConfigText() const +{ +#if WITH_EDITORONLY_DATA + return DevNodeConfigText; +#else + return FText::GetEmpty(); +#endif // WITH_EDITORONLY_DATA +} void UFlowNodeBase::SetNodeConfigText(const FText& NodeConfigText) { #if WITH_EDITOR if (!NodeConfigText.EqualTo(DevNodeConfigText)) { - Modify(); - DevNodeConfigText = NodeConfigText; } #endif // WITH_EDITOR @@ -956,569 +932,186 @@ bool UFlowNodeBase::BuildMessage(FString& Message) const bool UFlowNodeBase::TryAddValueToFormatNamedArguments(const FFlowNamedDataPinProperty& NamedDataPinProperty, FFormatNamedArguments& InOutArguments) const { - const FFlowDataPinProperty* FlowDataPinProperty = NamedDataPinProperty.DataPinProperty.GetPtr(); - if (!FlowDataPinProperty) - { - return false; - } - - const EFlowPinType FlowPinType = FlowDataPinProperty->GetFlowPinType(); + const FFlowDataPinValue& DataPinValue = NamedDataPinProperty.DataPinValue.Get(); - FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); - switch (FlowPinType) + const FFlowPinTypeName PinTypeName = DataPinValue.GetPinTypeName(); + if (PinTypeName.IsNone()) { - case EFlowPinType::Exec: - { - LogError(TEXT("Cannot add Exec pin value to FFormatNamedArguments")); - } - break; - - case EFlowPinType::InstancedStruct: - { - LogError(TEXT("Cannot add InstancedStruct pin value to FFormatNamedArguments")); - } - break; - - case EFlowPinType::Bool: - { - const FFlowDataPinResult_Bool ResolvedResult = TryResolveDataPinAsBool(NamedDataPinProperty.Name); - if (ResolvedResult.Result == EFlowDataPinResolveResult::Success) - { - InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(ResolvedResult.Value)); - - return true; - } - } - break; - - case EFlowPinType::Int: - { - const FFlowDataPinResult_Int ResolvedResult = TryResolveDataPinAsInt(NamedDataPinProperty.Name); - if (ResolvedResult.Result == EFlowDataPinResolveResult::Success) - { - InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(ResolvedResult.Value)); - - return true; - } - } - break; - - case EFlowPinType::Float: - { - const FFlowDataPinResult_Float ResolvedResult = TryResolveDataPinAsFloat(NamedDataPinProperty.Name); - if (ResolvedResult.Result == EFlowDataPinResolveResult::Success) - { - InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(ResolvedResult.Value)); - - return true; - } - } - break; - - case EFlowPinType::Name: - { - const FFlowDataPinResult_Name ResolvedResult = TryResolveDataPinAsName(NamedDataPinProperty.Name); - if (ResolvedResult.Result == EFlowDataPinResolveResult::Success) - { - InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(FText::FromString(ResolvedResult.Value.ToString()))); - - return true; - } - } - break; - - case EFlowPinType::String: - { - const FFlowDataPinResult_String ResolvedResult = TryResolveDataPinAsString(NamedDataPinProperty.Name); - if (ResolvedResult.Result == EFlowDataPinResolveResult::Success) - { - InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(FText::FromString(ResolvedResult.Value))); - - return true; - } - } - break; - - case EFlowPinType::Text: - { - const FFlowDataPinResult_Text ResolvedResult = TryResolveDataPinAsText(NamedDataPinProperty.Name); - if (ResolvedResult.Result == EFlowDataPinResolveResult::Success) - { - InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(ResolvedResult.Value)); - - return true; - } - } - break; - - case EFlowPinType::Enum: - { - const FFlowDataPinResult_Enum ResolvedResult = TryResolveDataPinAsEnum(NamedDataPinProperty.Name); - if (ResolvedResult.Result == EFlowDataPinResolveResult::Success) - { - InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(FText::FromString(ResolvedResult.Value.ToString()))); - - return true; - } - } - break; - - case EFlowPinType::Vector: - { - const FFlowDataPinResult_Vector ResolvedResult = TryResolveDataPinAsVector(NamedDataPinProperty.Name); - if (ResolvedResult.Result == EFlowDataPinResolveResult::Success) - { - InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(FText::FromString(ResolvedResult.Value.ToString()))); - - return true; - } - } - break; - - case EFlowPinType::Rotator: - { - const FFlowDataPinResult_Rotator ResolvedResult = TryResolveDataPinAsRotator(NamedDataPinProperty.Name); - if (ResolvedResult.Result == EFlowDataPinResolveResult::Success) - { - InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(FText::FromString(ResolvedResult.Value.ToString()))); - - return true; - } - } - break; - - case EFlowPinType::Transform: - { - const FFlowDataPinResult_Transform ResolvedResult = TryResolveDataPinAsTransform(NamedDataPinProperty.Name); - if (ResolvedResult.Result == EFlowDataPinResolveResult::Success) - { - InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(FText::FromString(ResolvedResult.Value.ToString()))); - - return true; - } - } - break; - - case EFlowPinType::GameplayTag: - { - const FFlowDataPinResult_GameplayTag ResolvedResult = TryResolveDataPinAsGameplayTag(NamedDataPinProperty.Name); - if (ResolvedResult.Result == EFlowDataPinResolveResult::Success) - { - InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(FText::FromString(ResolvedResult.Value.ToString()))); - - return true; - } - } - break; - - case EFlowPinType::GameplayTagContainer: - { - const FFlowDataPinResult_GameplayTagContainer ResolvedResult = TryResolveDataPinAsGameplayTagContainer(NamedDataPinProperty.Name); - if (ResolvedResult.Result == EFlowDataPinResolveResult::Success) - { - InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(FText::FromString(ResolvedResult.Value.ToString()))); - - return true; - } - } - break; - - case EFlowPinType::Object: - { - const FFlowDataPinResult_Object ResolvedResult = TryResolveDataPinAsObject(NamedDataPinProperty.Name); - if (ResolvedResult.Result == EFlowDataPinResolveResult::Success) - { - if (IsValid(ResolvedResult.Value)) - { - InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(FText::FromString(ResolvedResult.Value->GetName()))); - } - else - { - InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(FText::FromString(TEXT("null")))); - } - - return true; - } - } - break; - - case EFlowPinType::Class: - { - const FFlowDataPinResult_Class ResolvedResult = TryResolveDataPinAsClass(NamedDataPinProperty.Name); - if (ResolvedResult.Result == EFlowDataPinResolveResult::Success) - { - InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FFormatArgumentValue(FText::FromString(ResolvedResult.GetAsSoftClass().ToString()))); - - return true; - } - } - break; - - default: break; - } - - return false; -} - -EFlowDataPinResolveResult UFlowNodeBase::TryResolveDataPinPrerequisites(const FName& PinName, const UFlowNode*& FlowNode, const FFlowPin*& FlowPin, EFlowPinType PinType) const -{ - FlowNode = GetFlowNodeSelfOrOwner(); - - if (!IsValid(FlowNode)) - { - LogError(FString::Printf(TEXT("Unexpected for %s to not have an associated FlowNode"), *GetName()), EFlowOnScreenMessageType::Temporary); - - return EFlowDataPinResolveResult::FailedWithError; + return false; } - FlowPin = FindFlowPinByName(PinName, FlowNode->GetInputPins()); - if (!FlowPin) + const FFlowPinType* PinType = FFlowPinType::LookupPinType(PinTypeName); + if (!PinType) { - return EFlowDataPinResolveResult::FailedMissingPin; + return false; } - if (FlowPin->GetPinType() != PinType) + FFormatArgumentValue FormatValue; + if (PinType->ResolveAndFormatPinValue(*this, NamedDataPinProperty.Name, FormatValue)) { - return EFlowDataPinResolveResult::FailedMismatchedType; + InOutArguments.Add(NamedDataPinProperty.Name.ToString(), FormatValue); + return true; } - return EFlowDataPinResolveResult::Success; + return false; } -// Must implement TryResolveDataPinAs...() for every EFlowPinType -FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); - -template -bool TResolveDataPinWorkingData::TrySetupWorkingData(const FName& PinName, const UFlowNodeBase& FlowNodeBase) +FFlowDataPinResult UFlowNodeBase::TryResolveDataPin(FName PinName) const { - DataPinResult.Result = FlowNodeBase.TryResolveDataPinPrerequisites(PinName, FlowNode, FlowPin, PinType); - if (DataPinResult.Result != EFlowDataPinResolveResult::Success) - { - return false; - } + FFlowDataPinResult DataPinResult(EFlowDataPinResolveResult::Success); - if (!FlowNode->TryGetFlowDataPinSupplierDatasForPinName(FlowPin->PinName, PinValueSupplierDatas)) + const UFlowNode* FlowNode = GetFlowNodeSelfOrOwner(); + UFlowNode::TFlowPinValueSupplierDataArray PinValueSupplierDatas; + if (!FlowNode->TryGetFlowDataPinSupplierDatasForPinName(PinName, PinValueSupplierDatas)) { // If we could not build the PinValueDataSuppliers array, // then the pin must be disconnected and have no default value available. - DataPinResult.Result = EFlowDataPinResolveResult::FailedUnconnected; - return false; - } + DataPinResult.Result = EFlowDataPinResolveResult::FailedWithError; - return true; -} + LogError(FString::Printf(TEXT("DataPin named '%s' could not be supplied with a value."), *PinName.ToString()), EFlowOnScreenMessageType::Temporary); -FFlowDataPinResult_Bool UFlowNodeBase::TryResolveDataPinAsBool(const FName& PinName) const -{ - TResolveDataPinWorkingData WorkData; - if (!WorkData.TrySetupWorkingData(PinName, *this)) - { - return WorkData.DataPinResult; + return DataPinResult; } - for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) + // Iterate over the suppliers in inverse order + for (int32 Index = PinValueSupplierDatas.Num() - 1; Index >= 0; --Index) { - WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsBool(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); + const FFlowPinValueSupplierData& SupplierData = PinValueSupplierDatas[Index]; + + DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPin(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); - if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) + if (FlowPinType::IsSuccess(DataPinResult.Result)) { - return WorkData.DataPinResult; + return DataPinResult; } } - return WorkData.DataPinResult; + return DataPinResult; } -FFlowDataPinResult_Int UFlowNodeBase::TryResolveDataPinAsInt(const FName& PinName) const +// #FlowDataPinLegacy +FFlowDataPinResult_Bool UFlowNodeBase::TryResolveDataPinAsBool(const FName& PinName) const { - TResolveDataPinWorkingData WorkData; - if (!WorkData.TrySetupWorkingData(PinName, *this)) - { - return WorkData.DataPinResult; - } - - for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) - { - WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsInt(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); - - if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) - { - return WorkData.DataPinResult; - } - } + FFlowDataPinResult_Bool BoolResolveResult; + BoolResolveResult.Result = TryResolveDataPinValue(PinName, BoolResolveResult.Value); + return BoolResolveResult; +} - return WorkData.DataPinResult; +FFlowDataPinResult_Int UFlowNodeBase::TryResolveDataPinAsInt(const FName& PinName) const +{ + FFlowDataPinResult_Int ResolveResult; + int32 Value = 0; + ResolveResult.Result = TryResolveDataPinValue(PinName, Value); + ResolveResult.Value = Value; + return ResolveResult; } FFlowDataPinResult_Float UFlowNodeBase::TryResolveDataPinAsFloat(const FName& PinName) const { - TResolveDataPinWorkingData WorkData; - if (!WorkData.TrySetupWorkingData(PinName, *this)) - { - return WorkData.DataPinResult; - } - - for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) - { - WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsFloat(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); - - if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) - { - return WorkData.DataPinResult; - } - } - - return WorkData.DataPinResult; + FFlowDataPinResult_Float ResolveResult; + float Value = 0.0f; + ResolveResult.Result = TryResolveDataPinValue(PinName, Value); + ResolveResult.Value = Value; + return ResolveResult; } FFlowDataPinResult_Name UFlowNodeBase::TryResolveDataPinAsName(const FName& PinName) const { - TResolveDataPinWorkingData WorkData; - if (!WorkData.TrySetupWorkingData(PinName, *this)) - { - return WorkData.DataPinResult; - } - - for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) - { - WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsName(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); - - if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) - { - return WorkData.DataPinResult; - } - } - - return WorkData.DataPinResult; + FFlowDataPinResult_Name ResolveResult; + ResolveResult.Result = TryResolveDataPinValue(PinName, ResolveResult.Value); + return ResolveResult; } FFlowDataPinResult_String UFlowNodeBase::TryResolveDataPinAsString(const FName& PinName) const { - TResolveDataPinWorkingData WorkData; - if (!WorkData.TrySetupWorkingData(PinName, *this)) - { - return WorkData.DataPinResult; - } - - for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) - { - WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsString(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); - - if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) - { - return WorkData.DataPinResult; - } - } - - return WorkData.DataPinResult; + FFlowDataPinResult_String ResolveResult; + ResolveResult.Result = TryResolveDataPinValue(PinName, ResolveResult.Value); + return ResolveResult; } FFlowDataPinResult_Text UFlowNodeBase::TryResolveDataPinAsText(const FName& PinName) const { - TResolveDataPinWorkingData WorkData; - if (!WorkData.TrySetupWorkingData(PinName, *this)) - { - return WorkData.DataPinResult; - } - - for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) - { - WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsText(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); - - if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) - { - return WorkData.DataPinResult; - } - } - - return WorkData.DataPinResult; + FFlowDataPinResult_Text ResolveResult; + ResolveResult.Result = TryResolveDataPinValue(PinName, ResolveResult.Value); + return ResolveResult; } FFlowDataPinResult_Enum UFlowNodeBase::TryResolveDataPinAsEnum(const FName& PinName) const { - TResolveDataPinWorkingData WorkData; - if (!WorkData.TrySetupWorkingData(PinName, *this)) + const FFlowDataPinResult DataPinResult = TryResolveDataPin(PinName); + if (!FlowPinType::IsSuccess(DataPinResult.Result)) { - return WorkData.DataPinResult; + return FFlowDataPinResult_Enum(DataPinResult.Result); } - for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) - { - WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsEnum(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); + const FFlowDataPinValue_Enum& Wrapper = DataPinResult.ResultValue.Get(); - if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) - { - return WorkData.DataPinResult; - } + if (Wrapper.Values.IsEmpty()) + { + return FFlowDataPinResult_Enum(EFlowDataPinResolveResult::FailedInsufficientValues); } - return WorkData.DataPinResult; + const FFlowDataPinResult_Enum ResolveResult(Wrapper.Values[0], Wrapper.EnumClass.LoadSynchronous()); + return ResolveResult; } FFlowDataPinResult_Vector UFlowNodeBase::TryResolveDataPinAsVector(const FName& PinName) const { - TResolveDataPinWorkingData WorkData; - if (!WorkData.TrySetupWorkingData(PinName, *this)) - { - return WorkData.DataPinResult; - } - - for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) - { - WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsVector(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); - - if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) - { - return WorkData.DataPinResult; - } - } - - return WorkData.DataPinResult; + FFlowDataPinResult_Vector ResolveResult; + ResolveResult.Result = TryResolveDataPinValue(PinName, ResolveResult.Value); + return ResolveResult; } FFlowDataPinResult_Rotator UFlowNodeBase::TryResolveDataPinAsRotator(const FName& PinName) const { - TResolveDataPinWorkingData WorkData; - if (!WorkData.TrySetupWorkingData(PinName, *this)) - { - return WorkData.DataPinResult; - } - - for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) - { - WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsRotator(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); - - if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) - { - return WorkData.DataPinResult; - } - } - - return WorkData.DataPinResult; + FFlowDataPinResult_Rotator ResolveResult; + ResolveResult.Result = TryResolveDataPinValue(PinName, ResolveResult.Value); + return ResolveResult; } FFlowDataPinResult_Transform UFlowNodeBase::TryResolveDataPinAsTransform(const FName& PinName) const { - TResolveDataPinWorkingData WorkData; - if (!WorkData.TrySetupWorkingData(PinName, *this)) - { - return WorkData.DataPinResult; - } - - for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) - { - WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsTransform(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); - - if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) - { - return WorkData.DataPinResult; - } - } - - return WorkData.DataPinResult; + FFlowDataPinResult_Transform ResolveResult; + ResolveResult.Result = TryResolveDataPinValue(PinName, ResolveResult.Value); + return ResolveResult; } FFlowDataPinResult_GameplayTag UFlowNodeBase::TryResolveDataPinAsGameplayTag(const FName& PinName) const { - TResolveDataPinWorkingData WorkData; - if (!WorkData.TrySetupWorkingData(PinName, *this)) - { - return WorkData.DataPinResult; - } - - for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) - { - WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsGameplayTag(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); - - if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) - { - return WorkData.DataPinResult; - } - } - - return WorkData.DataPinResult; + FFlowDataPinResult_GameplayTag ResolveResult; + ResolveResult.Result = TryResolveDataPinValue(PinName, ResolveResult.Value); + return ResolveResult; } FFlowDataPinResult_GameplayTagContainer UFlowNodeBase::TryResolveDataPinAsGameplayTagContainer(const FName& PinName) const { - TResolveDataPinWorkingData WorkData; - if (!WorkData.TrySetupWorkingData(PinName, *this)) - { - return WorkData.DataPinResult; - } - - for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) - { - WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsGameplayTagContainer(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); - - if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) - { - return WorkData.DataPinResult; - } - } - - return WorkData.DataPinResult; + FFlowDataPinResult_GameplayTagContainer ResolveResult; + ResolveResult.Result = TryResolveDataPinValue(PinName, ResolveResult.Value); + return ResolveResult; } FFlowDataPinResult_InstancedStruct UFlowNodeBase::TryResolveDataPinAsInstancedStruct(const FName& PinName) const { - TResolveDataPinWorkingData WorkData; - if (!WorkData.TrySetupWorkingData(PinName, *this)) - { - return WorkData.DataPinResult; - } - - for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) - { - WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsInstancedStruct(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); - - if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) - { - return WorkData.DataPinResult; - } - } - - return WorkData.DataPinResult; + FFlowDataPinResult_InstancedStruct ResolveResult; + ResolveResult.Result = TryResolveDataPinValue(PinName, ResolveResult.Value); + return ResolveResult; } FFlowDataPinResult_Object UFlowNodeBase::TryResolveDataPinAsObject(const FName& PinName) const { - TResolveDataPinWorkingData WorkData; - if (!WorkData.TrySetupWorkingData(PinName, *this)) - { - return WorkData.DataPinResult; - } - - for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) - { - WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsObject(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); - - if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) - { - return WorkData.DataPinResult; - } - } - - return WorkData.DataPinResult; + FFlowDataPinResult_Object ResolveResult; + TObjectPtr Value = nullptr; + ResolveResult.Result = TryResolveDataPinValue(PinName, Value); + ResolveResult.Value = Value; + return ResolveResult; } FFlowDataPinResult_Class UFlowNodeBase::TryResolveDataPinAsClass(const FName& PinName) const { - TResolveDataPinWorkingData WorkData; - if (!WorkData.TrySetupWorkingData(PinName, *this)) - { - return WorkData.DataPinResult; - } - - for (const FFlowPinValueSupplierData& SupplierData : WorkData.PinValueSupplierDatas) - { - WorkData.DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsClass(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); - - if (WorkData.DataPinResult.Result == EFlowDataPinResolveResult::Success) - { - return WorkData.DataPinResult; - } - } - - return WorkData.DataPinResult; + FFlowDataPinResult_Class ResolveResult; + TObjectPtr Value = nullptr; + ResolveResult.Result = TryResolveDataPinValue(PinName, Value); + ResolveResult.SetValueFromObjectPtr(Value); + return ResolveResult; } +// -- \ No newline at end of file diff --git a/Source/Flow/Private/Nodes/FlowPin.cpp b/Source/Flow/Private/Nodes/FlowPin.cpp index 69de7d244..84a3351ad 100644 --- a/Source/Flow/Private/Nodes/FlowPin.cpp +++ b/Source/Flow/Private/Nodes/FlowPin.cpp @@ -7,6 +7,8 @@ #include "Misc/DateTime.h" #include "Misc/MessageDialog.h" #include "StructUtils/InstancedStruct.h" +#include "Types/FlowPinType.h" +#include "Types/FlowPinTypesStandard.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowPin) @@ -48,202 +50,154 @@ FORCEINLINE FString FPinRecord::DoubleDigit(const int32 Number) ////////////////////////////////////////////////////////////////////////// // Flow Pin -TArray FFlowPin::FlowPinTypeEnumValuesWithoutSpaces; +bool FFlowPin::IsExecPin() const +{ + return PinTypeName == FFlowPinType_Exec::GetPinTypeNameStatic(); +} + +bool FFlowPin::IsExecPinCategory(const FName& PC) +{ + return PC == FFlowPinType_Exec::GetPinTypeNameStatic().Name; +} const FName FFlowPin::MetadataKey_SourceForOutputFlowPin = "SourceForOutputFlowPin"; const FName FFlowPin::MetadataKey_DefaultForInputFlowPin = "DefaultForInputFlowPin"; const FName FFlowPin::MetadataKey_FlowPinType = "FlowPinType"; -const TArray& FFlowPin::GetFlowPinTypeEnumValuesWithoutSpaces() +void FFlowPin::SetPinTypeName(const FFlowPinTypeName& InTypeName) { - if (FlowPinTypeEnumValuesWithoutSpaces.IsEmpty()) + if (PinTypeName == InTypeName) { - FlowPinTypeEnumValuesWithoutSpaces.Reserve(static_cast(EFlowPinType::Max)); - - // Do a one-time caching of the string-names for this enum, - // since we need to de-spacify it and everything.... - - for (const EFlowPinType PinType : TEnumRange()) - { - FString StringValue = UEnum::GetDisplayValueAsText(PinType).ToString(); - StringValue.RemoveSpacesInline(); - - FlowPinTypeEnumValuesWithoutSpaces.Add(FName(StringValue)); - } + return; } - return FlowPinTypeEnumValuesWithoutSpaces; + PinTypeName = InTypeName; } -bool FFlowPin::ArePinArraysMatchingNamesAndTypes(const TArray& Left, const TArray& Right) +void FFlowPin::TrySetStructSubCategoryObjectFromPinType() { - if (Left.Num() != Right.Num()) + if (PinTypeName == FFlowPinType_Vector::GetPinTypeNameStatic()) { - return false; + PinSubCategoryObject = TBaseStructure::Get(); } - - for (int32 Index = 0; Index < Left.Num(); ++Index) + else if (PinTypeName == FFlowPinType_Rotator::GetPinTypeNameStatic()) { - const FFlowPin& LeftPin = Left[Index]; - const FFlowPin& RightPin = Right[Index]; - - if (!DoPinsMatchNamesAndTypes(LeftPin, RightPin)) + PinSubCategoryObject = TBaseStructure::Get(); + } + else if (PinTypeName == FFlowPinType_Transform::GetPinTypeNameStatic()) + { + PinSubCategoryObject = TBaseStructure::Get(); + } + else if (PinTypeName == FFlowPinType_GameplayTag::GetPinTypeNameStatic()) + { + PinSubCategoryObject = TBaseStructure::Get(); + } + else if (PinTypeName == FFlowPinType_GameplayTagContainer::GetPinTypeNameStatic()) + { + PinSubCategoryObject = TBaseStructure::Get(); + } + else if (PinTypeName == FFlowPinType_InstancedStruct::GetPinTypeNameStatic()) + { + PinSubCategoryObject = TBaseStructure::Get(); + } + else if (PinTypeName == FFlowPinType_Enum::GetPinTypeNameStatic()) + { + // Clear the PinSubCategoryObject if it is not an Enum + const UObject* PinSubCategoryObjectPtr = PinSubCategoryObject.Get(); + if (PinSubCategoryObjectPtr && !PinSubCategoryObjectPtr->IsA()) { - return false; + PinSubCategoryObject = nullptr; } } - - return true; -} - -void FFlowPin::SetPinType(const EFlowPinType InFlowPinType, UObject* SubCategoryObject) -{ - if (PinType == InFlowPinType) + else if (PinTypeName == FFlowPinType_Object::GetPinTypeNameStatic()) { - return; + // Clear the PinSubCategoryObject if it is not an Object + const UObject* PinSubCategoryObjectPtr = PinSubCategoryObject.Get(); + if (PinSubCategoryObjectPtr && !PinSubCategoryObjectPtr->IsA()) + { + PinSubCategoryObject = nullptr; + } + } + else if (PinTypeName == FFlowPinType_Class::GetPinTypeNameStatic()) + { + // Clear the PinSubCategoryObject if it is not a Class + const UObject* PinSubCategoryObjectPtr = PinSubCategoryObject.Get(); + if (PinSubCategoryObjectPtr && !PinSubCategoryObjectPtr->IsA()) + { + PinSubCategoryObject = nullptr; + } + } + else + { + // Clear the PinSubCategoryObject for all PinTypes that do not use it. + PinSubCategoryObject = nullptr; } - - PinType = InFlowPinType; - - PinSubCategoryObject = SubCategoryObject; - - TrySetStructSubCategoryObjectFromPinType(); } -void FFlowPin::TrySetStructSubCategoryObjectFromPinType() +#if WITH_EDITOR +FEdGraphPinType FFlowPin::BuildEdGraphPinType() const { - FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); - - // Set the PinSubCategoryObject based on the PinType (if appropriate) - switch (PinType) - { - case EFlowPinType::Vector: - { - PinSubCategoryObject = TBaseStructure::Get(); - } - break; - - case EFlowPinType::Rotator: - { - PinSubCategoryObject = TBaseStructure::Get(); - } - break; + check(!PinTypeName.Name.IsNone()); - case EFlowPinType::Transform: - { - PinSubCategoryObject = TBaseStructure::Get(); - } - break; + FEdGraphPinType EdGraphPinType; + EdGraphPinType.PinCategory = PinTypeName.Name; - case EFlowPinType::GameplayTag: - { - PinSubCategoryObject = TBaseStructure::Get(); - } - break; - - case EFlowPinType::GameplayTagContainer: - { - PinSubCategoryObject = TBaseStructure::Get(); - } - break; - - case EFlowPinType::InstancedStruct: - { - PinSubCategoryObject = TBaseStructure::Get(); - } - break; - - case EFlowPinType::Enum: - { - // Clear the PinSubCategoryObject if it is not an Enum - const UObject* PinSubCategoryObjectPtr = PinSubCategoryObject.Get(); - if (PinSubCategoryObjectPtr && !PinSubCategoryObjectPtr->IsA()) - { - PinSubCategoryObject = nullptr; - } - } - break; + // TODO (gtaylor) possible future extension for types, to allow sub categories + EdGraphPinType.PinSubCategory = NAME_None; - case EFlowPinType::Object: - { - // Clear the PinSubCategoryObject if it is not an Object - const UObject* PinSubCategoryObjectPtr = PinSubCategoryObject.Get(); - if (PinSubCategoryObjectPtr && !PinSubCategoryObjectPtr->IsA()) - { - PinSubCategoryObject = nullptr; - } - } - break; + EdGraphPinType.PinSubCategoryObject = PinSubCategoryObject; + EdGraphPinType.ContainerType = ContainerType; - case EFlowPinType::Class: - { - // Clear the PinSubCategoryObject if it is not a Class - const UObject* PinSubCategoryObjectPtr = PinSubCategoryObject.Get(); - if (PinSubCategoryObjectPtr && !PinSubCategoryObjectPtr->IsA()) - { - PinSubCategoryObject = nullptr; - } - } - break; + return EdGraphPinType; +} +#endif - default: - { - // Clear the PinSubCategoryObject for all PinTypes that do not use it. - PinSubCategoryObject = nullptr; - } - break; - } +const FFlowPinType* FFlowPin::ResolveFlowPinType() const +{ + // TODO (gtaylor) consider caching this in a mutable? + return FFlowPinType::LookupPinType(PinTypeName); } -const FName& FFlowPin::GetPinCategoryFromPinType(EFlowPinType FlowPinType) +// #FlowDataPinLegacy +FFlowPinTypeName FFlowPin::GetPinTypeNameForLegacyPinType(EFlowPinType PinType) { FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); - - switch (FlowPinType) + switch (PinType) { - case EFlowPinType::Exec: - return FFlowPin::PC_Exec; - - case EFlowPinType::Bool: - return FFlowPin::PC_Boolean; - - case EFlowPinType::Int: - return FFlowPin::PC_Int; - - case EFlowPinType::Float: - return FFlowPin::PC_Float; - - case EFlowPinType::Name: - return FFlowPin::PC_Name; - - case EFlowPinType::String: - return FFlowPin::PC_String; - - case EFlowPinType::Text: - return FFlowPin::PC_Text; - - case EFlowPinType::Enum: - return FFlowPin::PC_Enum; - - case EFlowPinType::Vector: - case EFlowPinType::Rotator: - case EFlowPinType::Transform: - case EFlowPinType::GameplayTag: - case EFlowPinType::GameplayTagContainer: - case EFlowPinType::InstancedStruct: - return FFlowPin::PC_Struct; - - case EFlowPinType::Object: - return FFlowPin::PC_Object; - - case EFlowPinType::Class: - return FFlowPin::PC_Class; - - default: - { - static const FName NameNone = NAME_None; - return NameNone; - } + case EFlowPinType::Exec: + return FFlowPinType_Exec::GetPinTypeNameStatic(); + case EFlowPinType::Bool: + return FFlowPinType_Bool::GetPinTypeNameStatic(); + case EFlowPinType::Int: + return FFlowPinType_Int::GetPinTypeNameStatic(); + case EFlowPinType::Float: + return FFlowPinType_Float::GetPinTypeNameStatic(); + case EFlowPinType::Name: + return FFlowPinType_Name::GetPinTypeNameStatic(); + case EFlowPinType::String: + return FFlowPinType_String::GetPinTypeNameStatic(); + case EFlowPinType::Text: + return FFlowPinType_Text::GetPinTypeNameStatic(); + case EFlowPinType::Enum: + return FFlowPinType_Enum::GetPinTypeNameStatic(); + case EFlowPinType::Vector: + return FFlowPinType_Vector::GetPinTypeNameStatic(); + case EFlowPinType::Rotator: + return FFlowPinType_Rotator::GetPinTypeNameStatic(); + case EFlowPinType::Transform: + return FFlowPinType_Transform::GetPinTypeNameStatic(); + case EFlowPinType::GameplayTag: + return FFlowPinType_GameplayTag::GetPinTypeNameStatic(); + case EFlowPinType::GameplayTagContainer: + return FFlowPinType_GameplayTagContainer::GetPinTypeNameStatic(); + case EFlowPinType::InstancedStruct: + return FFlowPinType_InstancedStruct::GetPinTypeNameStatic(); + case EFlowPinType::Object: + return FFlowPinType_Object::GetPinTypeNameStatic(); + case EFlowPinType::Class: + return FFlowPinType_Class::GetPinTypeNameStatic(); + default: + return FFlowPinTypeName(); } } @@ -251,52 +205,49 @@ const FName& FFlowPin::GetPinCategoryFromPinType(EFlowPinType FlowPinType) void FFlowPin::PostEditChangedPinTypeOrSubCategorySource() { // PinTypes with PinSubCategoryObjects will need to update this function - FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); // Must be called from PostEditChangeProperty() by an owning UObject - switch (PinType) + if (PinTypeName == FFlowPinType_Class::GetPinTypeNameStatic()) { - case EFlowPinType::Class: - PinSubCategoryObject = SubCategoryClassFilter; - break; - - case EFlowPinType::Object: - PinSubCategoryObject = SubCategoryObjectFilter; - break; - - case EFlowPinType::Enum: + PinSubCategoryObject = SubCategoryClassFilter; + } + else if (PinTypeName == FFlowPinType_Object::GetPinTypeNameStatic()) + { + PinSubCategoryObject = SubCategoryObjectFilter; + } + else if (PinTypeName == FFlowPinType_Enum::GetPinTypeNameStatic()) + { + if (!SubCategoryEnumName.IsEmpty()) + { + SubCategoryEnumClass = UClass::TryFindTypeSlow(SubCategoryEnumName, EFindFirstObjectOptions::ExactClass); + if (SubCategoryEnumClass != nullptr && !FFlowPin::ValidateEnum(*SubCategoryEnumClass)) { - if (!SubCategoryEnumName.IsEmpty()) - { - SubCategoryEnumClass = UClass::TryFindTypeSlow(SubCategoryEnumName, EFindFirstObjectOptions::ExactClass); - if (SubCategoryEnumClass != nullptr && !FFlowPin::ValidateEnum(*SubCategoryEnumClass)) - { - SubCategoryEnumClass = nullptr; - } - } - - PinSubCategoryObject = SubCategoryEnumClass; + SubCategoryEnumClass = nullptr; } - break; + } - default: - TrySetStructSubCategoryObjectFromPinType(); - break; + PinSubCategoryObject = SubCategoryEnumClass; + } + else + { + TrySetStructSubCategoryObjectFromPinType(); } } +// -- + FText FFlowPin::BuildHeaderText() const { const FText PinNameToUse = !PinFriendlyName.IsEmpty() ? PinFriendlyName : FText::FromName(PinName); - if (PinType == EFlowPinType::Exec) + if (IsExecPin()) { return PinNameToUse; } else { - return FText::Format(LOCTEXT("FlowPinNameAndType", "{0} ({1})"), {PinNameToUse, UEnum::GetDisplayValueAsText(PinType)}); + return FText::Format(LOCTEXT("FlowPinNameAndType", "{0} ({1})"), {PinNameToUse, FText::FromString(PinTypeName.ToString())}); } } diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_CustomInput.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_CustomInput.cpp index f189ce4d4..94e4410f8 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_CustomInput.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_CustomInput.cpp @@ -25,14 +25,14 @@ void UFlowNode_CustomInput::PostEditImport() } #if WITH_EDITOR -FText UFlowNode_CustomInput::GetNodeTitle() const +FText UFlowNode_CustomInput::K2_GetNodeTitle_Implementation() const { if (!EventName.IsNone() && UFlowSettings::Get()->bUseAdaptiveNodeTitles) { return FText::Format(LOCTEXT("CustomInputTitle", "{0} Input"), {FText::FromString(EventName.ToString())}); } - return Super::GetNodeTitle(); + return Super::K2_GetNodeTitle_Implementation(); } #endif diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_CustomOutput.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_CustomOutput.cpp index b19912f47..ee5263ee0 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_CustomOutput.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_CustomOutput.cpp @@ -53,14 +53,14 @@ void UFlowNode_CustomOutput::ExecuteInput(const FName& PinName) } #if WITH_EDITOR -FText UFlowNode_CustomOutput::GetNodeTitle() const +FText UFlowNode_CustomOutput::K2_GetNodeTitle_Implementation() const { if (!EventName.IsNone() && UFlowSettings::Get()->bUseAdaptiveNodeTitles) { return FText::Format(LOCTEXT("CustomOutputTitle", "{0} Output"), {FText::FromString(EventName.ToString())}); } - return Super::GetNodeTitle(); + return Super::K2_GetNodeTitle_Implementation(); } #endif diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_DefineProperties.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_DefineProperties.cpp index fd835b546..6d453eaf0 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_DefineProperties.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_DefineProperties.cpp @@ -1,6 +1,9 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "Nodes/Graph/FlowNode_DefineProperties.h" +#include "Types/FlowAutoDataPinsWorkingData.h" +#include "Types/FlowPinTypesStandard.h" +#include "Types/FlowDataPinValuesStandard.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_DefineProperties) @@ -18,47 +21,60 @@ UFlowNode_DefineProperties::UFlowNode_DefineProperties(const FObjectInitializer& AllowedSignalModes = {EFlowSignalMode::Enabled, EFlowSignalMode::Disabled}; } -bool UFlowNode_DefineProperties::TryFindPropertyByRemappedPinName( - const FName& RemappedPinName, +void UFlowNode_DefineProperties::PostLoad() +{ + Super::PostLoad(); + + if (!HasAnyFlags(RF_ArchetypeObject | RF_ClassDefaultObject)) + { + // Migrate the named properties over to the new structs + + for (FFlowNamedDataPinProperty& NamedProperty : NamedProperties) + { + NamedProperty.FixupDataPinProperty(); + } + } +} + +bool UFlowNode_DefineProperties::TryFindPropertyByPinName( + const UObject& PropertyOwnerObject, + const FName& PinName, const FProperty*& OutFoundProperty, - TInstancedStruct& OutFoundInstancedStruct, - EFlowDataPinResolveResult& InOutResult) const + TInstancedStruct& OutFoundInstancedStruct) const { // The start node stores its properties in instanced structs in an array, so look there first for (const FFlowNamedDataPinProperty& NamedProperty : NamedProperties) { - if (NamedProperty.Name == RemappedPinName && NamedProperty.IsValid()) + if (NamedProperty.Name == PinName && NamedProperty.IsValid()) { - OutFoundInstancedStruct = NamedProperty.DataPinProperty; + OutFoundInstancedStruct = NamedProperty.DataPinValue; return true; } } - return Super::TryFindPropertyByRemappedPinName(RemappedPinName, OutFoundProperty, OutFoundInstancedStruct, InOutResult); + return Super::TryFindPropertyByPinName(PropertyOwnerObject, PinName, OutFoundProperty, OutFoundInstancedStruct); } #if WITH_EDITOR -void UFlowNode_DefineProperties::AutoGenerateDataPins(TMap& PinNameToBoundPropertyMap, TArray& InputDataPins, TArray& OutputDataPins) const +void UFlowNode_DefineProperties::AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const { + Super::AutoGenerateDataPins(InOutWorkingData); + for (const FFlowNamedDataPinProperty& DataPinProperty : NamedProperties) { if (DataPinProperty.IsValid()) { - PinNameToBoundPropertyMap.Add(DataPinProperty.Name, DataPinProperty.Name); + const FFlowDataPinValue& DataPinValue = DataPinProperty.DataPinValue.Get(); - if (DataPinProperty.IsInputProperty()) + if (DataPinValue.IsInputPin()) { - InputDataPins.AddUnique(DataPinProperty.CreateFlowPin()); - } - else if (DataPinProperty.IsOutputProperty()) - { - OutputDataPins.AddUnique(DataPinProperty.CreateFlowPin()); + InOutWorkingData.AutoInputDataPinsNext.AddUnique(DataPinProperty.CreateFlowPin()); } else { - LogError(TEXT("DataPin must be either an Input or Output property!")); + InOutWorkingData.AutoOutputDataPinsNext.AddUnique(DataPinProperty.CreateFlowPin()); } } } @@ -75,8 +91,8 @@ void UFlowNode_DefineProperties::PostEditChangeChainProperty(FPropertyChangedCha auto& Property = PropertyChainEvent.PropertyChain.GetActiveMemberNode()->GetValue(); - // The DetailsCustomization for FFlowDataPinProperties isn't being called when using an InstancedStruct - // so we need to call this refresh by hand... + // The DetailsCustomization for FFlowDataPinValue_Enum isn't being called when using an InstancedStruct + // so we need to call OnEnumNameChanged refresh by hand... if (PropertyChainEvent.ChangeType == EPropertyChangeType::ValueSet && Property->GetFName() == GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Enum, EnumName)) { @@ -87,16 +103,13 @@ void UFlowNode_DefineProperties::PostEditChangeChainProperty(FPropertyChangedCha continue; } - const FFlowDataPinProperty& FlowDataPinProperty = NamedProperty.DataPinProperty.Get(); + const FFlowDataPinValue& FlowDataPinProperty = NamedProperty.DataPinValue.Get(); - if (FlowDataPinProperty.GetFlowPinType() == EFlowPinType::Enum) + if (FlowDataPinProperty.GetPinTypeName() == FFlowPinType_Enum::GetPinTypeNameStatic()) { - FFlowDataPinOutputProperty_Enum& EnumProperty = NamedProperty.DataPinProperty.GetMutable(); + FFlowDataPinValue_Enum& EnumProperty = NamedProperty.DataPinValue.GetMutable(); EnumProperty.OnEnumNameChanged(); } - - // We may need to manually call any PostEdit linked property updates here for future EFlowPinType values - FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); } } diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_FormatText.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_FormatText.cpp index 83adf38c0..e6564105d 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_FormatText.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_FormatText.cpp @@ -1,13 +1,12 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "Nodes/Graph/FlowNode_FormatText.h" +#include "Types/FlowPinTypesStandard.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_FormatText) #define LOCTEXT_NAMESPACE "FlowNode_FormatText" -const FName UFlowNode_FormatText::OUTPIN_TextOutput("Formatted Text"); - UFlowNode_FormatText::UFlowNode_FormatText(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { @@ -16,81 +15,41 @@ UFlowNode_FormatText::UFlowNode_FormatText(const FObjectInitializer& ObjectIniti NodeDisplayStyle = FlowNodeStyle::Terminal; #endif - OutputPins.Add(FFlowPin(OUTPIN_TextOutput, EFlowPinType::Text)); + OutputPins.Add(FFlowPin(TEXT("Formatted Text"), FFlowPinType_Text::GetPinTypeNameStatic())); } -FFlowDataPinResult_Name UFlowNode_FormatText::TrySupplyDataPinAsName_Implementation(const FName& PinName) const +FFlowDataPinResult UFlowNode_FormatText::TrySupplyDataPin_Implementation(FName PinName) const { - FText FormattedText; - const EFlowDataPinResolveResult FormatResult = TryResolveFormatText(PinName, FormattedText); - if (FormatResult != EFlowDataPinResolveResult::Invalid) + if (PinName == TEXT("Formatted Text")) { - if (FormatResult == EFlowDataPinResolveResult::Success) + FText FormattedText; + const EFlowDataPinResolveResult FormatResult = TryResolveFormatText(PinName, FormattedText); + + if (FlowPinType::IsSuccess(FormatResult)) { - return FFlowDataPinResult_Name(FName(FormattedText.ToString())); + return FFlowDataPinResult(FFlowDataPinValue_Text(FormattedText)); } else { - return FFlowDataPinResult_Name(FormatResult); + return FFlowDataPinResult(FormatResult); } } - return Super::TrySupplyDataPinAsName_Implementation(PinName); + return Super::TrySupplyDataPin_Implementation(PinName); } -FFlowDataPinResult_String UFlowNode_FormatText::TrySupplyDataPinAsString_Implementation(const FName& PinName) const +EFlowDataPinResolveResult UFlowNode_FormatText::TryResolveFormatText(const FName& PinName, FText& OutFormattedText) const { - FText FormattedText; - const EFlowDataPinResolveResult FormatResult = TryResolveFormatText(PinName, FormattedText); - if (FormatResult != EFlowDataPinResolveResult::Invalid) + if (TryFormatTextWithNamedPropertiesAsParameters(FormatText, OutFormattedText)) { - if (FormatResult == EFlowDataPinResolveResult::Success) - { - return FFlowDataPinResult_String(FormattedText.ToString()); - } - else - { - return FFlowDataPinResult_String(FormatResult); - } + return EFlowDataPinResolveResult::Success; } - - return Super::TrySupplyDataPinAsString_Implementation(PinName); -} - -FFlowDataPinResult_Text UFlowNode_FormatText::TrySupplyDataPinAsText_Implementation(const FName& PinName) const -{ - FText FormattedText; - const EFlowDataPinResolveResult FormatResult = TryResolveFormatText(PinName, FormattedText); - if (FormatResult != EFlowDataPinResolveResult::Invalid) + else { - if (FormatResult == EFlowDataPinResolveResult::Success) - { - return FFlowDataPinResult_Text(FormattedText); - } - else - { - return FFlowDataPinResult_Text(FormatResult); - } - } - - return Super::TrySupplyDataPinAsText_Implementation(PinName); -} + LogError(FString::Printf(TEXT("Could not format text '%s' with properties as parameters"), *FormatText.ToString()), EFlowOnScreenMessageType::Temporary); -EFlowDataPinResolveResult UFlowNode_FormatText::TryResolveFormatText(const FName& PinName, FText& OutFormattedText) const -{ - if (PinName == OUTPIN_TextOutput) - { - if (TryFormatTextWithNamedPropertiesAsParameters(FormatText, OutFormattedText)) - { - return EFlowDataPinResolveResult::Success; - } - else - { - return EFlowDataPinResolveResult::FailedWithError; - } + return EFlowDataPinResolveResult::FailedWithError; } - - return EFlowDataPinResolveResult::Invalid; } #if WITH_EDITOR @@ -98,9 +57,7 @@ EFlowDataPinResolveResult UFlowNode_FormatText::TryResolveFormatText(const FName void UFlowNode_FormatText::UpdateNodeConfigText_Implementation() { constexpr bool bErrorIfInputPinNotFound = false; - const bool bIsInputConnected = IsInputConnected(GET_MEMBER_NAME_CHECKED(ThisClass, FormatText), bErrorIfInputPinNotFound); - - if (bIsInputConnected) + if (IsInputConnected(GET_MEMBER_NAME_CHECKED(ThisClass, FormatText), bErrorIfInputPinNotFound)) { SetNodeConfigText(FText()); } diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_Start.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_Start.cpp index 3e30befa8..b8f399b78 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_Start.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_Start.cpp @@ -44,230 +44,18 @@ bool UFlowNode_Start::TryAppendExternalInputPins(TArray& InOutPins) co #endif // WITH_EDITOR -// Must implement TrySupplyDataPinAs... for every EFlowPinType -FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); - -FFlowDataPinResult_Bool UFlowNode_Start::TrySupplyDataPinAsBool_Implementation(const FName& PinName) const -{ - if (FlowDataPinValueSupplierInterface) - { - FFlowDataPinResult_Bool SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsBool(FlowDataPinValueSupplierInterface.GetObject(), PinName); - - if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) - { - return SuppliedResult; - } - } - - return Super::TrySupplyDataPinAsBool_Implementation(PinName); -} - -FFlowDataPinResult_Int UFlowNode_Start::TrySupplyDataPinAsInt_Implementation(const FName& PinName) const -{ - if (FlowDataPinValueSupplierInterface) - { - FFlowDataPinResult_Int SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsInt(FlowDataPinValueSupplierInterface.GetObject(), PinName); - - if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) - { - return SuppliedResult; - } - } - - return Super::TrySupplyDataPinAsInt_Implementation(PinName); -} - -FFlowDataPinResult_Float UFlowNode_Start::TrySupplyDataPinAsFloat_Implementation(const FName& PinName) const -{ - if (FlowDataPinValueSupplierInterface) - { - FFlowDataPinResult_Float SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsFloat(FlowDataPinValueSupplierInterface.GetObject(), PinName); - - if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) - { - return SuppliedResult; - } - } - - return Super::TrySupplyDataPinAsFloat_Implementation(PinName); -} - -FFlowDataPinResult_Name UFlowNode_Start::TrySupplyDataPinAsName_Implementation(const FName& PinName) const -{ - if (FlowDataPinValueSupplierInterface) - { - FFlowDataPinResult_Name SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsName(FlowDataPinValueSupplierInterface.GetObject(), PinName); - - if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) - { - return SuppliedResult; - } - } - - return Super::TrySupplyDataPinAsName_Implementation(PinName); -} - -FFlowDataPinResult_String UFlowNode_Start::TrySupplyDataPinAsString_Implementation(const FName& PinName) const -{ - if (FlowDataPinValueSupplierInterface) - { - FFlowDataPinResult_String SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsString(FlowDataPinValueSupplierInterface.GetObject(), PinName); - - if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) - { - return SuppliedResult; - } - } - - return Super::TrySupplyDataPinAsString_Implementation(PinName); -} - -FFlowDataPinResult_Text UFlowNode_Start::TrySupplyDataPinAsText_Implementation(const FName& PinName) const -{ - if (FlowDataPinValueSupplierInterface) - { - FFlowDataPinResult_Text SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsText(FlowDataPinValueSupplierInterface.GetObject(), PinName); - - if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) - { - return SuppliedResult; - } - } - - return Super::TrySupplyDataPinAsText_Implementation(PinName); -} - -FFlowDataPinResult_Enum UFlowNode_Start::TrySupplyDataPinAsEnum_Implementation(const FName& PinName) const -{ - if (FlowDataPinValueSupplierInterface) - { - FFlowDataPinResult_Enum SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsEnum(FlowDataPinValueSupplierInterface.GetObject(), PinName); - - if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) - { - return SuppliedResult; - } - } - - return Super::TrySupplyDataPinAsEnum_Implementation(PinName); -} - -FFlowDataPinResult_Vector UFlowNode_Start::TrySupplyDataPinAsVector_Implementation(const FName& PinName) const -{ - if (FlowDataPinValueSupplierInterface) - { - FFlowDataPinResult_Vector SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsVector(FlowDataPinValueSupplierInterface.GetObject(), PinName); - - if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) - { - return SuppliedResult; - } - } - - return Super::TrySupplyDataPinAsVector_Implementation(PinName); -} - -FFlowDataPinResult_Rotator UFlowNode_Start::TrySupplyDataPinAsRotator_Implementation(const FName& PinName) const +FFlowDataPinResult UFlowNode_Start::TrySupplyDataPin_Implementation(FName PinName) const { if (FlowDataPinValueSupplierInterface) { - FFlowDataPinResult_Rotator SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsRotator(FlowDataPinValueSupplierInterface.GetObject(), PinName); + FFlowDataPinResult SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPin(FlowDataPinValueSupplierInterface.GetObject(), PinName); - if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) + if (FlowPinType::IsSuccess(SuppliedResult.Result)) { return SuppliedResult; } } - return Super::TrySupplyDataPinAsRotator_Implementation(PinName); + return Super::TrySupplyDataPin_Implementation(PinName); } -FFlowDataPinResult_Transform UFlowNode_Start::TrySupplyDataPinAsTransform_Implementation(const FName& PinName) const -{ - if (FlowDataPinValueSupplierInterface) - { - FFlowDataPinResult_Transform SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsTransform(FlowDataPinValueSupplierInterface.GetObject(), PinName); - - if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) - { - return SuppliedResult; - } - } - - return Super::TrySupplyDataPinAsTransform_Implementation(PinName); -} - -FFlowDataPinResult_GameplayTag UFlowNode_Start::TrySupplyDataPinAsGameplayTag_Implementation(const FName& PinName) const -{ - if (FlowDataPinValueSupplierInterface) - { - FFlowDataPinResult_GameplayTag SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsGameplayTag(FlowDataPinValueSupplierInterface.GetObject(), PinName); - - if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) - { - return SuppliedResult; - } - } - - return Super::TrySupplyDataPinAsGameplayTag_Implementation(PinName); -} - -FFlowDataPinResult_GameplayTagContainer UFlowNode_Start::TrySupplyDataPinAsGameplayTagContainer_Implementation(const FName& PinName) const -{ - if (FlowDataPinValueSupplierInterface) - { - FFlowDataPinResult_GameplayTagContainer SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsGameplayTagContainer(FlowDataPinValueSupplierInterface.GetObject(), PinName); - - if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) - { - return SuppliedResult; - } - } - - return Super::TrySupplyDataPinAsGameplayTagContainer_Implementation(PinName); -} - -FFlowDataPinResult_InstancedStruct UFlowNode_Start::TrySupplyDataPinAsInstancedStruct_Implementation(const FName& PinName) const -{ - if (FlowDataPinValueSupplierInterface) - { - FFlowDataPinResult_InstancedStruct SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsInstancedStruct(FlowDataPinValueSupplierInterface.GetObject(), PinName); - - if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) - { - return SuppliedResult; - } - } - - return Super::TrySupplyDataPinAsInstancedStruct_Implementation(PinName); -} - -FFlowDataPinResult_Object UFlowNode_Start::TrySupplyDataPinAsObject_Implementation(const FName& PinName) const -{ - if (FlowDataPinValueSupplierInterface) - { - FFlowDataPinResult_Object SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsObject(FlowDataPinValueSupplierInterface.GetObject(), PinName); - - if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) - { - return SuppliedResult; - } - } - - return Super::TrySupplyDataPinAsObject_Implementation(PinName); -} - -FFlowDataPinResult_Class UFlowNode_Start::TrySupplyDataPinAsClass_Implementation(const FName& PinName) const -{ - if (FlowDataPinValueSupplierInterface) - { - FFlowDataPinResult_Class SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPinAsClass(FlowDataPinValueSupplierInterface.GetObject(), PinName); - - if (SuppliedResult.Result == EFlowDataPinResolveResult::Success) - { - return SuppliedResult; - } - } - - return Super::TrySupplyDataPinAsClass_Implementation(PinName); -} diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp index 98d42b3e2..9e74acc08 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp @@ -6,6 +6,7 @@ #include "FlowSettings.h" #include "FlowSubsystem.h" #include "Interfaces/FlowNodeWithExternalDataPinSupplierInterface.h" +#include "Types/FlowAutoDataPinsWorkingData.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_SubGraph) @@ -106,14 +107,14 @@ void UFlowNode_SubGraph::OnLoad_Implementation() #if WITH_EDITOR -FText UFlowNode_SubGraph::GetNodeTitle() const +FText UFlowNode_SubGraph::K2_GetNodeTitle_Implementation() const { if (UFlowSettings::Get()->bUseAdaptiveNodeTitles && !Asset.IsNull()) { - return FText::Format(LOCTEXT("SubGraphTitle", "{0}\n{1}"), {Super::GetNodeTitle(), FText::FromString(Asset.ToSoftObjectPath().GetAssetName())}); + return FText::Format(LOCTEXT("SubGraphTitle", "{0}\n{1}"), {Super::K2_GetNodeTitle_Implementation(), FText::FromString(Asset.ToSoftObjectPath().GetAssetName())}); } - return Super::GetNodeTitle(); + return Super::K2_GetNodeTitle_Implementation(); } FString UFlowNode_SubGraph::GetNodeDescription() const @@ -186,8 +187,10 @@ TArray UFlowNode_SubGraph::GetContextOutputs() const return ContextOutputPins; } -void UFlowNode_SubGraph::AutoGenerateDataPins(TMap& PinNameToBoundPropertyMap, TArray& InputDataPins, TArray& OutputDataPins) const +void UFlowNode_SubGraph::AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const { + Super::AutoGenerateDataPins(InOutWorkingData); + if (Asset.IsNull()) { return; @@ -209,12 +212,7 @@ void UFlowNode_SubGraph::AutoGenerateDataPins(TMap& PinNameToBound TArray ExternalInputPins; if (ExternalPinSuppliedNode->TryAppendExternalInputPins(ExternalInputPins)) { - for (const FFlowPin& FlowPin : ExternalInputPins) - { - PinNameToBoundPropertyMap.Add(FlowPin.PinName, FlowPin.PinName); - } - - InputDataPins.Append(ExternalInputPins); + InOutWorkingData.AutoInputDataPinsNext.Append(ExternalInputPins); } } } diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp index 2e842dd9c..92a742f73 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Timer.cpp @@ -34,7 +34,7 @@ UFlowNode_Timer::UFlowNode_Timer(const FObjectInitializer& ObjectInitializer) OutputPins.Add(FFlowPin(TEXT("Step"))); OutputPins.Add(FFlowPin(TEXT("Skipped"))); - INPIN_CompletionTime = GET_MEMBER_NAME_CHECKED(UFlowNode_Timer, CompletionTime); + INPIN_CompletionTime = GET_MEMBER_NAME_CHECKED(ThisClass, CompletionTime); } void UFlowNode_Timer::InitializeInstance() @@ -107,18 +107,10 @@ void UFlowNode_Timer::Restart() float UFlowNode_Timer::ResolveCompletionTime() const { // Get the CompletionTime from either the default (property) or the data pin (if connected) - FFlowDataPinResult_Float CompletionTimeResult = TryResolveDataPinAsFloat(INPIN_CompletionTime); + float ResolvedTime = CompletionTime; + const EFlowDataPinResolveResult TimeResult = TryResolveDataPinValue(INPIN_CompletionTime, ResolvedTime); - if (CompletionTimeResult.Result == EFlowDataPinResolveResult::FailedMissingPin) - { - // Handle lookup of a UFlowNode_Timer that predated DataPins - CompletionTimeResult.Result = EFlowDataPinResolveResult::Success; - CompletionTimeResult.Value = CompletionTime; - } - - check(CompletionTimeResult.Result == EFlowDataPinResolveResult::Success); - - return static_cast(CompletionTimeResult.Value); + return ResolvedTime; } void UFlowNode_Timer::OnStep() diff --git a/Source/Flow/Private/Types/FlowAutoDataPinsWorkingData.cpp b/Source/Flow/Private/Types/FlowAutoDataPinsWorkingData.cpp new file mode 100644 index 000000000..78107aa17 --- /dev/null +++ b/Source/Flow/Private/Types/FlowAutoDataPinsWorkingData.cpp @@ -0,0 +1,129 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Types/FlowAutoDataPinsWorkingData.h" +#include "FlowLogChannels.h" +#include "Types/FlowDataPinValue.h" +#include "Types/FlowStructUtils.h" + +#if WITH_EDITOR +bool FFlowAutoDataPinsWorkingData::DidAutoInputDataPinsChange() const +{ + return !FFlowPin::DeepArePinArraysMatching(AutoInputDataPinsPrev, AutoInputDataPinsNext); +} + +bool FFlowAutoDataPinsWorkingData::DidAutoOutputDataPinsChange() const +{ + return !FFlowPin::DeepArePinArraysMatching(AutoOutputDataPinsPrev, AutoOutputDataPinsNext); +} + +void FFlowAutoDataPinsWorkingData::AddFlowDataPinsForClassProperties(const UObject& ObjectContainer) +{ + // Try to harvest pins to auto-generate and/or bind to for each property in the flow node + UClass* Class = ObjectContainer.GetClass(); + for (TFieldIterator PropertyIt(Class); PropertyIt; ++PropertyIt) + { + AddFlowDataPinForProperty(*PropertyIt, ObjectContainer); + } +} + +void FFlowAutoDataPinsWorkingData::AddFlowDataPinForProperty(const FProperty* Property, const UObject& ObjectContainer) +{ + bool bIsInputPin = false; + + const FString* AutoPinType = nullptr; + const FString* SourceForOutputFlowPinName = nullptr; + const FString* DefaultForInputFlowPinName = nullptr; + + const void* Container = &ObjectContainer; + + const FStructProperty* StructProperty = CastField(Property); + const FFlowDataPinValue* DataPinValue = nullptr; + if (StructProperty && StructProperty->Struct) + { + const UScriptStruct* ScriptStruct = StructProperty->Struct; + + AutoPinType = ScriptStruct->FindMetaData(FFlowPin::MetadataKey_FlowPinType); + SourceForOutputFlowPinName = ScriptStruct->FindMetaData(FFlowPin::MetadataKey_SourceForOutputFlowPin); + DefaultForInputFlowPinName = ScriptStruct->FindMetaData(FFlowPin::MetadataKey_DefaultForInputFlowPin); + + // For blueprint use, we allow the Value structs to set input pins via editor-only data + DataPinValue = FlowStructUtils::CastStructValue(StructProperty, Container); + if (DataPinValue) + { + bIsInputPin = DataPinValue->IsInputPin(); + } + } + + if (!AutoPinType) + { + AutoPinType = Property->FindMetaData(FFlowPin::MetadataKey_FlowPinType); + + if (!AutoPinType) + { + return; + } + } + + const FFlowPinType* FlowPinType = FFlowPinType::LookupPinType(FFlowPinTypeName(*AutoPinType)); + if (!FlowPinType) + { + UE_LOG(LogFlow, Error, TEXT("Unknown pin type %s for property %s"), **AutoPinType, *Property->GetName()); + + return; + } + + if (!SourceForOutputFlowPinName) + { + SourceForOutputFlowPinName = Property->FindMetaData(FFlowPin::MetadataKey_SourceForOutputFlowPin); + } + + if (!DefaultForInputFlowPinName) + { + DefaultForInputFlowPinName = Property->FindMetaData(FFlowPin::MetadataKey_DefaultForInputFlowPin); + } + + if (SourceForOutputFlowPinName && DefaultForInputFlowPinName) + { + UE_LOG(LogFlow, Error, TEXT("Error. A property cannot be both a %s and %s"), + *FFlowPin::MetadataKey_SourceForOutputFlowPin.ToString(), + *FFlowPin::MetadataKey_DefaultForInputFlowPin.ToString()); + + return; + } + + bIsInputPin = bIsInputPin || DefaultForInputFlowPinName != nullptr; + + // Default assumption is the pin will be an output pin, unless metadata specifies otherwise + TArray* FlowPinArray = bIsInputPin ? &AutoInputDataPinsNext : &AutoOutputDataPinsNext; + + // Create the new FlowPin + FFlowPin NewFlowPin = FlowPinType->CreateFlowPinFromProperty(*Property, Container); + + // Potentially override the PinFriendlyName if the metadata specified an alternative + if (DefaultForInputFlowPinName) + { + const FString SpecifyInputPinNameString = *DefaultForInputFlowPinName; + if (SpecifyInputPinNameString.Len() > 0) + { + NewFlowPin.PinFriendlyName = FText::FromString(SpecifyInputPinNameString); + } + } + else if (SourceForOutputFlowPinName) + { + const FString SpecifyOutputPinNameString = *SourceForOutputFlowPinName; + if (SpecifyOutputPinNameString.Len() > 0) + { + NewFlowPin.PinFriendlyName = FText::FromString(SpecifyOutputPinNameString); + } + } + + FlowPinArray->Add(NewFlowPin); + + if (DataPinValue) + { + // Store the PropertyPinName in the property, for blueprint lookup functions. + DataPinValue->PropertyPinName = NewFlowPin.PinName; + } +} + +#endif diff --git a/Source/Flow/Private/Types/FlowDataPinBlueprintLibrary.cpp b/Source/Flow/Private/Types/FlowDataPinBlueprintLibrary.cpp index 13b17024a..373bf0ae2 100644 --- a/Source/Flow/Private/Types/FlowDataPinBlueprintLibrary.cpp +++ b/Source/Flow/Private/Types/FlowDataPinBlueprintLibrary.cpp @@ -1,36 +1,2364 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "Types/FlowDataPinBlueprintLibrary.h" -#include "FlowLogChannels.h" +#include "Types/FlowDataPinValue.h" +#include "Nodes/FlowNodeBase.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowDataPinBlueprintLibrary) -uint8 UFlowDataPinBlueprintLibrary::AutoConvert_FlowDataPinPropertyEnumToEnum(const FFlowDataPinOutputProperty_Enum& EnumProperty) +void UFlowDataPinBlueprintLibrary::ResolveAndExtract_Impl( + UFlowNodeBase* Target, + FName PinName, + EFlowDataPinResolveSimpleResult& SimpleResult, + EFlowDataPinResolveResult& ResultEnum, + auto&& ExtractLambda) { - if (IsValid(EnumProperty.EnumClass)) + using namespace FlowPinType; + + if (!IsValid(Target)) + { + ResultEnum = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + SimpleResult = ConvertToSimpleResult(ResultEnum); + return; + } + + ResultEnum = ExtractLambda(); + SimpleResult = ConvertToSimpleResult(ResultEnum); +} + +void UFlowDataPinBlueprintLibrary::ResolveAsBool(UFlowNodeBase* Target, const FFlowDataPinValue_Bool& BoolValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, bool& Value, EFlowSingleFromArray SingleMode) +{ + Value = false; + ResolveAndExtract_Impl(Target, BoolValue.PropertyPinName, Result, ResultEnum, [&]() { + return Target->TryResolveDataPinValue(BoolValue.PropertyPinName, Value, SingleMode); + }); +} + +void UFlowDataPinBlueprintLibrary::ResolveAsBoolArray(UFlowNodeBase* Target, const FFlowDataPinValue_Bool& BoolValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values) +{ + Values.Reset(); + ResolveAndExtract_Impl(Target, BoolValue.PropertyPinName, Result, ResultEnum, [&]() { + return Target->TryResolveDataPinValues(BoolValue.PropertyPinName, Values); + }); +} + +// Int +void UFlowDataPinBlueprintLibrary::ResolveAsInt(UFlowNodeBase* Target, const FFlowDataPinValue_Int& IntValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, int32& Value, EFlowSingleFromArray SingleMode) +{ + Value = 0; + ResolveAndExtract_Impl(Target, IntValue.PropertyPinName, Result, ResultEnum, [&]() { + return Target->TryResolveDataPinValue(IntValue.PropertyPinName, Value, SingleMode); + }); +} + +void UFlowDataPinBlueprintLibrary::ResolveAsIntArray(UFlowNodeBase* Target, const FFlowDataPinValue_Int& IntValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values) +{ + Values.Reset(); + ResolveAndExtract_Impl(Target, IntValue.PropertyPinName, Result, ResultEnum, [&]() { + return Target->TryResolveDataPinValues(IntValue.PropertyPinName, Values); + }); +} + +// Int64 +void UFlowDataPinBlueprintLibrary::ResolveAsInt64(UFlowNodeBase* Target, const FFlowDataPinValue_Int64& Int64Value, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, int64& Value, EFlowSingleFromArray SingleMode) +{ + Value = 0; + ResolveAndExtract_Impl(Target, Int64Value.PropertyPinName, Result, ResultEnum, [&]() { + return Target->TryResolveDataPinValue(Int64Value.PropertyPinName, Value, SingleMode); + }); +} + +void UFlowDataPinBlueprintLibrary::ResolveAsInt64Array(UFlowNodeBase* Target, const FFlowDataPinValue_Int64& Int64Value, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values) +{ + Values.Reset(); + ResolveAndExtract_Impl(Target, Int64Value.PropertyPinName, Result, ResultEnum, [&]() { + return Target->TryResolveDataPinValues(Int64Value.PropertyPinName, Values); + }); +} + +// Float +void UFlowDataPinBlueprintLibrary::ResolveAsFloat(UFlowNodeBase* Target, const FFlowDataPinValue_Float& FloatValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, float& Value, EFlowSingleFromArray SingleMode) +{ + Value = 0.0f; + ResolveAndExtract_Impl(Target, FloatValue.PropertyPinName, Result, ResultEnum, [&]() { + return Target->TryResolveDataPinValue(FloatValue.PropertyPinName, Value, SingleMode); + }); +} + +void UFlowDataPinBlueprintLibrary::ResolveAsFloatArray(UFlowNodeBase* Target, const FFlowDataPinValue_Float& FloatValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values) +{ + Values.Reset(); + ResolveAndExtract_Impl(Target, FloatValue.PropertyPinName, Result, ResultEnum, [&]() { + return Target->TryResolveDataPinValues(FloatValue.PropertyPinName, Values); + }); +} + +// Double +void UFlowDataPinBlueprintLibrary::ResolveAsDouble(UFlowNodeBase* Target, const FFlowDataPinValue_Double& DoubleValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, double& Value, EFlowSingleFromArray SingleMode) +{ + Value = 0.0; + ResolveAndExtract_Impl(Target, DoubleValue.PropertyPinName, Result, ResultEnum, [&]() { + return Target->TryResolveDataPinValue(DoubleValue.PropertyPinName, Value, SingleMode); + }); +} + +void UFlowDataPinBlueprintLibrary::ResolveAsDoubleArray(UFlowNodeBase* Target, const FFlowDataPinValue_Double& DoubleValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values) +{ + Values.Reset(); + ResolveAndExtract_Impl(Target, DoubleValue.PropertyPinName, Result, ResultEnum, [&]() { + return Target->TryResolveDataPinValues(DoubleValue.PropertyPinName, Values); + }); +} + +// Name +void UFlowDataPinBlueprintLibrary::ResolveAsName(UFlowNodeBase* Target, const FFlowDataPinValue_Name& NameValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, FName& Value, EFlowSingleFromArray SingleMode) +{ + Value = NAME_None; + ResolveAndExtract_Impl(Target, NameValue.PropertyPinName, Result, ResultEnum, [&]() { + return Target->TryResolveDataPinValue(NameValue.PropertyPinName, Value, SingleMode); + }); +} + +void UFlowDataPinBlueprintLibrary::ResolveAsNameArray(UFlowNodeBase* Target, const FFlowDataPinValue_Name& NameValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values) +{ + Values.Reset(); + ResolveAndExtract_Impl(Target, NameValue.PropertyPinName, Result, ResultEnum, [&]() { + return Target->TryResolveDataPinValues(NameValue.PropertyPinName, Values); + }); +} + +// String +void UFlowDataPinBlueprintLibrary::ResolveAsString(UFlowNodeBase* Target, const FFlowDataPinValue_String& StringValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, FString& Value, EFlowSingleFromArray SingleMode) +{ + Value = FString(); + ResolveAndExtract_Impl(Target, StringValue.PropertyPinName, Result, ResultEnum, [&]() { + return Target->TryResolveDataPinValue(StringValue.PropertyPinName, Value, SingleMode); + }); +} + +void UFlowDataPinBlueprintLibrary::ResolveAsStringArray(UFlowNodeBase* Target, const FFlowDataPinValue_String& StringValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values) +{ + Values.Reset(); + ResolveAndExtract_Impl(Target, StringValue.PropertyPinName, Result, ResultEnum, [&]() { + return Target->TryResolveDataPinValues(StringValue.PropertyPinName, Values); + }); +} + +// Text +void UFlowDataPinBlueprintLibrary::ResolveAsText(UFlowNodeBase* Target, const FFlowDataPinValue_Text& TextValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, FText& Value, EFlowSingleFromArray SingleMode) +{ + Value = FText::GetEmpty(); + ResolveAndExtract_Impl(Target, TextValue.PropertyPinName, Result, ResultEnum, [&]() { + return Target->TryResolveDataPinValue(TextValue.PropertyPinName, Value, SingleMode); + }); +} + +void UFlowDataPinBlueprintLibrary::ResolveAsTextArray(UFlowNodeBase* Target, const FFlowDataPinValue_Text& TextValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values) +{ + Values.Reset(); + ResolveAndExtract_Impl(Target, TextValue.PropertyPinName, Result, ResultEnum, [&]() { + return Target->TryResolveDataPinValues(TextValue.PropertyPinName, Values); + }); +} + +// Enum +void UFlowDataPinBlueprintLibrary::ResolveAsEnum(UFlowNodeBase* Target, const FFlowDataPinValue_Enum& EnumValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, uint8& Value, EFlowSingleFromArray SingleMode) +{ + Value = 0; + ResolveAndExtract_Impl(Target, EnumValue.PropertyPinName, Result, ResultEnum, [&]() { + FName ExtractedName; + UEnum* EnumClass = nullptr; + const EFlowDataPinResolveResult ResolveResult = Target->TryResolveDataPinValue(EnumValue.PropertyPinName, ExtractedName, EnumClass, SingleMode); + if (FlowPinType::IsSuccess(ResolveResult) && ensure(IsValid(EnumClass))) + { + const int64 IntValue = EnumClass->GetValueByName(ExtractedName); + Value = static_cast(IntValue); + } + return ResolveResult; + }); +} + +void UFlowDataPinBlueprintLibrary::ResolveAsEnumArray(UFlowNodeBase* Target, const FFlowDataPinValue_Enum& EnumValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values) +{ + Values.Reset(); + ResolveAndExtract_Impl(Target, EnumValue.PropertyPinName, Result, ResultEnum, [&]() { + TArray Names; + UEnum* EnumClass = nullptr; + const EFlowDataPinResolveResult ResolveResult = Target->TryResolveDataPinValues(EnumValue.PropertyPinName, Names, EnumClass); + if (FlowPinType::IsSuccess(ResolveResult) && ensure(IsValid(EnumClass))) + { + Values.Reserve(Names.Num()); + for (const FName& Name : Names) + { + const int64 IntValue = EnumClass->GetValueByName(Name); + Values.Add(static_cast(IntValue)); + } + } + return ResolveResult; + }); +} + +// Vector +void UFlowDataPinBlueprintLibrary::ResolveAsVector(UFlowNodeBase* Target, const FFlowDataPinValue_Vector& VectorValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, FVector& Value, EFlowSingleFromArray SingleMode) +{ + Value = FVector::ZeroVector; + ResolveAndExtract_Impl(Target, VectorValue.PropertyPinName, Result, ResultEnum, [&]() { + return Target->TryResolveDataPinValue(VectorValue.PropertyPinName, Value, SingleMode); + }); +} + +void UFlowDataPinBlueprintLibrary::ResolveAsVectorArray(UFlowNodeBase* Target, const FFlowDataPinValue_Vector& VectorValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values) +{ + Values.Reset(); + ResolveAndExtract_Impl(Target, VectorValue.PropertyPinName, Result, ResultEnum, [&]() { + return Target->TryResolveDataPinValues(VectorValue.PropertyPinName, Values); + }); +} + +// Rotator +void UFlowDataPinBlueprintLibrary::ResolveAsRotator(UFlowNodeBase* Target, const FFlowDataPinValue_Rotator& RotatorValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, FRotator& Value, EFlowSingleFromArray SingleMode) +{ + Value = FRotator::ZeroRotator; + ResolveAndExtract_Impl(Target, RotatorValue.PropertyPinName, Result, ResultEnum, [&]() { + return Target->TryResolveDataPinValue(RotatorValue.PropertyPinName, Value, SingleMode); + }); +} + +void UFlowDataPinBlueprintLibrary::ResolveAsRotatorArray(UFlowNodeBase* Target, const FFlowDataPinValue_Rotator& RotatorValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values) +{ + Values.Reset(); + ResolveAndExtract_Impl(Target, RotatorValue.PropertyPinName, Result, ResultEnum, [&]() { + return Target->TryResolveDataPinValues(RotatorValue.PropertyPinName, Values); + }); +} + +// Transform +void UFlowDataPinBlueprintLibrary::ResolveAsTransform(UFlowNodeBase* Target, const FFlowDataPinValue_Transform& TransformValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, FTransform& Value, EFlowSingleFromArray SingleMode) +{ + Value = FTransform::Identity; + ResolveAndExtract_Impl(Target, TransformValue.PropertyPinName, Result, ResultEnum, [&]() { + return Target->TryResolveDataPinValue(TransformValue.PropertyPinName, Value, SingleMode); + }); +} + +void UFlowDataPinBlueprintLibrary::ResolveAsTransformArray(UFlowNodeBase* Target, const FFlowDataPinValue_Transform& TransformValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values) +{ + Values.Reset(); + ResolveAndExtract_Impl(Target, TransformValue.PropertyPinName, Result, ResultEnum, [&]() { + return Target->TryResolveDataPinValues(TransformValue.PropertyPinName, Values); + }); +} + +// GameplayTag +void UFlowDataPinBlueprintLibrary::ResolveAsGameplayTag(UFlowNodeBase* Target, const FFlowDataPinValue_GameplayTag& GameplayTagValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, FGameplayTag& Value, EFlowSingleFromArray SingleMode) +{ + Value = FGameplayTag(); + ResolveAndExtract_Impl(Target, GameplayTagValue.PropertyPinName, Result, ResultEnum, [&]() { + return Target->TryResolveDataPinValue(GameplayTagValue.PropertyPinName, Value, SingleMode); + }); +} + +void UFlowDataPinBlueprintLibrary::ResolveAsGameplayTagArray(UFlowNodeBase* Target, const FFlowDataPinValue_GameplayTag& GameplayTagValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values) +{ + Values.Reset(); + ResolveAndExtract_Impl(Target, GameplayTagValue.PropertyPinName, Result, ResultEnum, [&]() { + return Target->TryResolveDataPinValues(GameplayTagValue.PropertyPinName, Values); + }); +} + +// GameplayTagContainer +void UFlowDataPinBlueprintLibrary::ResolveAsGameplayTagContainer(UFlowNodeBase* Target, const FFlowDataPinValue_GameplayTagContainer& GameplayTagContainerValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, FGameplayTagContainer& Value) +{ + Value = FGameplayTagContainer(); + ResolveAndExtract_Impl(Target, GameplayTagContainerValue.PropertyPinName, Result, ResultEnum, [&]() { + return Target->TryResolveDataPinValue(GameplayTagContainerValue.PropertyPinName, Value, EFlowSingleFromArray::FirstValue); + }); +} + +// InstancedStruct +void UFlowDataPinBlueprintLibrary::ResolveAsInstancedStruct(UFlowNodeBase* Target, const FFlowDataPinValue_InstancedStruct& InstancedStructValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, FInstancedStruct& Value, EFlowSingleFromArray SingleMode) +{ + Value = FInstancedStruct(); + ResolveAndExtract_Impl(Target, InstancedStructValue.PropertyPinName, Result, ResultEnum, [&]() { + return Target->TryResolveDataPinValue(InstancedStructValue.PropertyPinName, Value, SingleMode); + }); +} + +void UFlowDataPinBlueprintLibrary::ResolveAsInstancedStructArray(UFlowNodeBase* Target, const FFlowDataPinValue_InstancedStruct& InstancedStructValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values) +{ + Values.Reset(); + ResolveAndExtract_Impl(Target, InstancedStructValue.PropertyPinName, Result, ResultEnum, [&]() { + return Target->TryResolveDataPinValues(InstancedStructValue.PropertyPinName, Values); + }); +} + +// Object +void UFlowDataPinBlueprintLibrary::ResolveAsObject(UFlowNodeBase* Target, const FFlowDataPinValue_Object& ObjectValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, UObject*& Value, EFlowSingleFromArray SingleMode) +{ + Value = nullptr; + ResolveAndExtract_Impl(Target, ObjectValue.PropertyPinName, Result, ResultEnum, [&]() { + TObjectPtr Obj; + const EFlowDataPinResolveResult ResolveResult = Target->TryResolveDataPinValue(ObjectValue.PropertyPinName, Obj, SingleMode); + Value = Obj; + return ResolveResult; + }); +} + +void UFlowDataPinBlueprintLibrary::ResolveAsObjectArray(UFlowNodeBase* Target, const FFlowDataPinValue_Object& ObjectValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values) +{ + Values.Reset(); + ResolveAndExtract_Impl(Target, ObjectValue.PropertyPinName, Result, ResultEnum, [&]() { + TArray> ObjArray; + const EFlowDataPinResolveResult ResolveResult = Target->TryResolveDataPinValues(ObjectValue.PropertyPinName, ObjArray); + Values = ObjArray; + return ResolveResult; + }); +} + +// Class +void UFlowDataPinBlueprintLibrary::ResolveAsClass(UFlowNodeBase* Target, const FFlowDataPinValue_Class& ClassValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, UClass*& Value, EFlowSingleFromArray SingleMode) +{ + Value = nullptr; + ResolveAndExtract_Impl(Target, ClassValue.PropertyPinName, Result, ResultEnum, [&]() { + TObjectPtr ClassObj; + const EFlowDataPinResolveResult ResolveResult = Target->TryResolveDataPinValue(ClassValue.PropertyPinName, ClassObj, SingleMode); + Value = ClassObj; + return ResolveResult; + }); +} + +void UFlowDataPinBlueprintLibrary::ResolveAsClassArray(UFlowNodeBase* Target, const FFlowDataPinValue_Class& ClassValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values) +{ + Values.Reset(); + ResolveAndExtract_Impl(Target, ClassValue.PropertyPinName, Result, ResultEnum, [&]() { + TArray> ClassArray; + const EFlowDataPinResolveResult ResolveResult = Target->TryResolveDataPinValues(ClassValue.PropertyPinName, ClassArray); + Values = ClassArray; + return ResolveResult; + }); +} + +// Bool +bool UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsBool(const FFlowDataPinValue_Bool& BoolValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + bool Extracted = false; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValue(BoolValue.PropertyPinName, Extracted, SingleMode); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to Bool failed on pin '%s': %s"), + *BoolValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + + return Extracted; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsBoolArray(const FFlowDataPinValue_Bool& BoolValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + TArray Extracted; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValues(BoolValue.PropertyPinName, Extracted); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to Bool Array failed on pin '%s': %s"), + *BoolValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + return TArray(); + } + + return Extracted; +} + +// Int +int32 UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsInt(const FFlowDataPinValue_Int& IntValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + int32 Extracted = 0; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValue(IntValue.PropertyPinName, Extracted, SingleMode); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to Int failed on pin '%s': %s"), + *IntValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + + return Extracted; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsIntArray(const FFlowDataPinValue_Int& IntValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + TArray Extracted; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValues(IntValue.PropertyPinName, Extracted); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to Int Array failed on pin '%s': %s"), + *IntValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + return TArray(); + } + + return Extracted; +} + +// Int64 +int64 UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsInt64(const FFlowDataPinValue_Int64& Int64Value, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + int64 Extracted = 0; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValue(Int64Value.PropertyPinName, Extracted, SingleMode); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to Int64 failed on pin '%s': %s"), + *Int64Value.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + + return Extracted; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsInt64Array(const FFlowDataPinValue_Int64& Int64Value, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + TArray Extracted; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValues(Int64Value.PropertyPinName, Extracted); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to Int64 Array failed on pin '%s': %s"), + *Int64Value.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + return TArray(); + } + + return Extracted; +} + +// Float +float UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsFloat(const FFlowDataPinValue_Float& FloatValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + float Extracted = 0.0f; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValue(FloatValue.PropertyPinName, Extracted, SingleMode); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to Float failed on pin '%s': %s"), + *FloatValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + + return Extracted; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsFloatArray(const FFlowDataPinValue_Float& FloatValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + TArray Extracted; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValues(FloatValue.PropertyPinName, Extracted); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to Float Array failed on pin '%s': %s"), + *FloatValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + return TArray(); + } + + return Extracted; +} + +// Double +double UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsDouble(const FFlowDataPinValue_Double& DoubleValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + double Extracted = 0.0; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValue(DoubleValue.PropertyPinName, Extracted, SingleMode); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to Double failed on pin '%s': %s"), + *DoubleValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + + return Extracted; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsDoubleArray(const FFlowDataPinValue_Double& DoubleValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + TArray Extracted; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValues(DoubleValue.PropertyPinName, Extracted); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to Double Array failed on pin '%s': %s"), + *DoubleValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + return TArray(); + } + + return Extracted; +} + +// Name +FName UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsName(const FFlowDataPinValue_Name& NameValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + FName Extracted = NAME_None; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValue(NameValue.PropertyPinName, Extracted, SingleMode); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to Name failed on pin '%s': %s"), + *NameValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + + return Extracted; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsNameArray(const FFlowDataPinValue_Name& NameValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + TArray Extracted; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValues(NameValue.PropertyPinName, Extracted); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to Name Array failed on pin '%s': %s"), + *NameValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + return TArray(); + } + + return Extracted; +} + +// String +FString UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsString(const FFlowDataPinValue_String& StringValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + FString Extracted; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValue(StringValue.PropertyPinName, Extracted, SingleMode); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to String failed on pin '%s': %s"), + *StringValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + + return Extracted; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsStringArray(const FFlowDataPinValue_String& StringValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + TArray Extracted; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValues(StringValue.PropertyPinName, Extracted); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to String Array failed on pin '%s': %s"), + *StringValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + return TArray(); + } + + return Extracted; +} + +// Text +FText UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsText(const FFlowDataPinValue_Text& TextValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + FText Extracted; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValue(TextValue.PropertyPinName, Extracted, SingleMode); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to Text failed on pin '%s': %s"), + *TextValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + + return Extracted; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsTextArray(const FFlowDataPinValue_Text& TextValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + TArray Extracted; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValues(TextValue.PropertyPinName, Extracted); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to Text Array failed on pin '%s': %s"), + *TextValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + return TArray(); + } + + return Extracted; +} + +// Enum +uint8 UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsEnum(const FFlowDataPinValue_Enum& EnumValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + FName ExtractedName; + UEnum* EnumClass = nullptr; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValue(EnumValue.PropertyPinName, ExtractedName, EnumClass, SingleMode); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to Enum failed on pin '%s': %s"), + *EnumValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + return static_cast(INDEX_NONE); + } + + if (ensure(IsValid(EnumClass))) + { + const uint64 ValueInt = EnumClass->GetValueByName(ExtractedName); + return static_cast(ValueInt); + } + + return static_cast(INDEX_NONE); +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsEnumArray(const FFlowDataPinValue_Enum& EnumValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + TArray ExtractedNames; + UEnum* EnumClass = nullptr; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValues(EnumValue.PropertyPinName, ExtractedNames, EnumClass); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to Enum Array failed on pin '%s': %s"), + *EnumValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + return TArray(); + } + + if (!ensure(IsValid(EnumClass))) + { + return TArray(); + } + + TArray Result; + Result.Reserve(ExtractedNames.Num()); + for (const FName& Name : ExtractedNames) + { + const uint64 ValueInt = EnumClass->GetValueByName(Name); + Result.Add(static_cast(ValueInt)); + } + + return Result; +} + +// Vector +FVector UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsVector(const FFlowDataPinValue_Vector& VectorValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + FVector Extracted = FVector::ZeroVector; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValue(VectorValue.PropertyPinName, Extracted, SingleMode); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to Vector failed on pin '%s': %s"), + *VectorValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + + return Extracted; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsVectorArray(const FFlowDataPinValue_Vector& VectorValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + TArray Extracted; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValues(VectorValue.PropertyPinName, Extracted); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to Vector Array failed on pin '%s': %s"), + *VectorValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + return TArray(); + } + + return Extracted; +} + +// Rotator +FRotator UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsRotator(const FFlowDataPinValue_Rotator& RotatorValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + FRotator Extracted = FRotator::ZeroRotator; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValue(RotatorValue.PropertyPinName, Extracted, SingleMode); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to Rotator failed on pin '%s': %s"), + *RotatorValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + + return Extracted; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsRotatorArray(const FFlowDataPinValue_Rotator& RotatorValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + TArray Extracted; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValues(RotatorValue.PropertyPinName, Extracted); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to Rotator Array failed on pin '%s': %s"), + *RotatorValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + return TArray(); + } + + return Extracted; +} + +// Transform +FTransform UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsTransform(const FFlowDataPinValue_Transform& TransformValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + FTransform Extracted = FTransform::Identity; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValue(TransformValue.PropertyPinName, Extracted, SingleMode); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to Transform failed on pin '%s': %s"), + *TransformValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + + return Extracted; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsTransformArray(const FFlowDataPinValue_Transform& TransformValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + TArray Extracted; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValues(TransformValue.PropertyPinName, Extracted); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to Transform Array failed on pin '%s': %s"), + *TransformValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + return TArray(); + } + + return Extracted; +} + +// GameplayTag +FGameplayTag UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsGameplayTag(const FFlowDataPinValue_GameplayTag& GameplayTagValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + FGameplayTag Extracted; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValue(GameplayTagValue.PropertyPinName, Extracted, SingleMode); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to GameplayTag failed on pin '%s': %s"), + *GameplayTagValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + + return Extracted; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsGameplayTagArray(const FFlowDataPinValue_GameplayTag& GameplayTagValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + TArray Extracted; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValues(GameplayTagValue.PropertyPinName, Extracted); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to GameplayTag Array failed on pin '%s': %s"), + *GameplayTagValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + return TArray(); + } + + return Extracted; +} + +// GameplayTagContainer +FGameplayTagContainer UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsGameplayTagContainer(const FFlowDataPinValue_GameplayTagContainer& GameplayTagContainerValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + FGameplayTagContainer Extracted; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValue(GameplayTagContainerValue.PropertyPinName, Extracted); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to GameplayTagContainer failed on pin '%s': %s"), + *GameplayTagContainerValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + + return Extracted; +} + +// InstancedStruct +FInstancedStruct UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsInstancedStruct(const FFlowDataPinValue_InstancedStruct& InstancedStructValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + FInstancedStruct Extracted; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValue(InstancedStructValue.PropertyPinName, Extracted, SingleMode); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to InstancedStruct failed on pin '%s': %s"), + *InstancedStructValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + + return Extracted; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsInstancedStructArray(const FFlowDataPinValue_InstancedStruct& InstancedStructValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + TArray Extracted; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValues(InstancedStructValue.PropertyPinName, Extracted); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to InstancedStruct Array failed on pin '%s': %s"), + *InstancedStructValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + return TArray(); + } + + return Extracted; +} + +// Object +UObject* UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsObject(const FFlowDataPinValue_Object& ObjectValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + TObjectPtr Extracted = nullptr; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValue(ObjectValue.PropertyPinName, Extracted, SingleMode); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to Object failed on pin '%s': %s"), + *ObjectValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + + return Extracted; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsObjectArray(const FFlowDataPinValue_Object& ObjectValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + TArray> ExtractedTemp; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValues(ObjectValue.PropertyPinName, ExtractedTemp); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to Object Array failed on pin '%s': %s"), + *ObjectValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + return TArray(); + } + + TArray Result; + Result.Reserve(ExtractedTemp.Num()); + for (const TObjectPtr& Obj : ExtractedTemp) + { + Result.Add(Obj.Get()); + } + return Result; +} + +// Class +UClass* UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsClass(const FFlowDataPinValue_Class& ClassValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + TObjectPtr Extracted = nullptr; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValue(ClassValue.PropertyPinName, Extracted, SingleMode); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to Class failed on pin '%s': %s"), + *ClassValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + + return Extracted; +} + +// Class +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryResolveAsClassArray(const FFlowDataPinValue_Class& ClassValue, const UFlowNodeBase* Target) +{ + using namespace FlowPinType; + + TArray> ExtractedTemp; + EFlowDataPinResolveResult ResolveResult = EFlowDataPinResolveResult::FailedNullFlowNodeBase; + + if (IsValid(Target)) + { + ResolveResult = Target->TryResolveDataPinValues(ClassValue.PropertyPinName, ExtractedTemp); + } + + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("Auto-Resolve to Class Array failed on pin '%s': %s"), + *ClassValue.PropertyPinName.ToString(), + *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + return TArray(); + } + + TArray Result; + Result.Reserve(ExtractedTemp.Num()); + for (const TObjectPtr& ClassPtr : ExtractedTemp) + { + Result.Add(ClassPtr.Get()); + } + + return Result; +} + +bool UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractBool(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + bool ExtractedValue = false; + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + const EFlowDataPinResolveResult ResolveResult = TryExtractValue(DataPinResult, ExtractedValue, SingleMode); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractBool Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValue; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractBoolArray(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + TArray ExtractedValues; + const EFlowDataPinResolveResult ResolveResult = TryExtractValues(DataPinResult, ExtractedValues); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractBoolArray Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValues; +} + +int32 UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractInt(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + int32 ExtractedValue = 0; + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + const EFlowDataPinResolveResult ResolveResult = TryExtractValue(DataPinResult, ExtractedValue, SingleMode); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractInt Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValue; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractIntArray(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + TArray ExtractedValues; + const EFlowDataPinResolveResult ResolveResult = TryExtractValues(DataPinResult, ExtractedValues); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractIntArray Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValues; +} + +int64 UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractInt64(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + int64 ExtractedValue = 0; + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + const EFlowDataPinResolveResult ResolveResult = TryExtractValue(DataPinResult, ExtractedValue, SingleMode); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractInt64 Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValue; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractInt64Array(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + TArray ExtractedValues; + const EFlowDataPinResolveResult ResolveResult = TryExtractValues(DataPinResult, ExtractedValues); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractInt64Array Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValues; +} + +float UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractFloat(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + float ExtractedValue = 0.0f; + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + const EFlowDataPinResolveResult ResolveResult = TryExtractValue(DataPinResult, ExtractedValue, SingleMode); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractFloat Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValue; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractFloatArray(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + TArray ExtractedValues; + const EFlowDataPinResolveResult ResolveResult = TryExtractValues(DataPinResult, ExtractedValues); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractFloatArray Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValues; +} + +double UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractDouble(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + double ExtractedValue = 0.0; + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + const EFlowDataPinResolveResult ResolveResult = TryExtractValue(DataPinResult, ExtractedValue, SingleMode); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractDouble Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValue; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractDoubleArray(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + TArray ExtractedValues; + const EFlowDataPinResolveResult ResolveResult = TryExtractValues(DataPinResult, ExtractedValues); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractDoubleArray Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValues; +} + +FName UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractName(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + FName ExtractedValue = NAME_None; + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + const EFlowDataPinResolveResult ResolveResult = TryExtractValue(DataPinResult, ExtractedValue, SingleMode); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractName Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValue; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractNameArray(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + TArray ExtractedValues; + const EFlowDataPinResolveResult ResolveResult = TryExtractValues(DataPinResult, ExtractedValues); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractNameArray Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValues; +} + +FString UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractString(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + FString ExtractedValue; + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + const EFlowDataPinResolveResult ResolveResult = TryExtractValue(DataPinResult, ExtractedValue, SingleMode); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractString Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValue; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractStringArray(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + TArray ExtractedValues; + const EFlowDataPinResolveResult ResolveResult = TryExtractValues(DataPinResult, ExtractedValues); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractStringArray Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValues; +} + +FText UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractText(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + FText ExtractedValue; + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + const EFlowDataPinResolveResult ResolveResult = TryExtractValue(DataPinResult, ExtractedValue, SingleMode); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractText Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValue; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractTextArray(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + TArray ExtractedValues; + const EFlowDataPinResolveResult ResolveResult = TryExtractValues(DataPinResult, ExtractedValues); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractTextArray Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValues; +} + +uint8 UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractEnum(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + FName ExtractedValueAsName; + UEnum* EnumClass = nullptr; + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + const EFlowDataPinResolveResult ResolveResult = TryExtractValue(DataPinResult, ExtractedValueAsName, EnumClass, SingleMode); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractEnum Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + + if (ensure(IsValid(EnumClass))) + { + const uint64 EnumValueAsInt = EnumClass->GetValueByName(ExtractedValueAsName); + return static_cast(EnumValueAsInt); + } + + return static_cast(INDEX_NONE); +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractEnumArray(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + + TArray ExtractedNames; + UEnum* EnumClass = nullptr; + + const EFlowDataPinResolveResult ResolveResult = TryExtractValues(DataPinResult, ExtractedNames, EnumClass); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractEnumArray Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + return TArray(); + } + + if (!ensure(IsValid(EnumClass))) + { + return TArray(); + } + + TArray ExtractedValues; + ExtractedValues.Reserve(ExtractedNames.Num()); + + for (const FName& Name : ExtractedNames) + { + const uint64 EnumValueAsInt = EnumClass->GetValueByName(Name); + ExtractedValues.Add(static_cast(EnumValueAsInt)); + } + + return ExtractedValues; +} + +FVector UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractVector(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + FVector ExtractedValue = FVector::ZeroVector; + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + const EFlowDataPinResolveResult ResolveResult = TryExtractValue(DataPinResult, ExtractedValue, SingleMode); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractVector Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValue; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractVectorArray(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + TArray ExtractedValues; + const EFlowDataPinResolveResult ResolveResult = TryExtractValues(DataPinResult, ExtractedValues); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractVectorArray Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValues; +} + +FRotator UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractRotator(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + FRotator ExtractedValue = FRotator::ZeroRotator; + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + const EFlowDataPinResolveResult ResolveResult = TryExtractValue(DataPinResult, ExtractedValue, SingleMode); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractRotator Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValue; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractRotatorArray(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + TArray ExtractedValues; + const EFlowDataPinResolveResult ResolveResult = TryExtractValues(DataPinResult, ExtractedValues); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractRotatorArray Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValues; +} + +FTransform UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractTransform(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + FTransform ExtractedValue = FTransform::Identity; + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + const EFlowDataPinResolveResult ResolveResult = TryExtractValue(DataPinResult, ExtractedValue, SingleMode); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractTransform Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValue; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractTransformArray(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + TArray ExtractedValues; + const EFlowDataPinResolveResult ResolveResult = TryExtractValues(DataPinResult, ExtractedValues); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractTransformArray Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValues; +} + +FGameplayTag UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractGameplayTag(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + FGameplayTag ExtractedValue; + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + const EFlowDataPinResolveResult ResolveResult = TryExtractValue(DataPinResult, ExtractedValue, SingleMode); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractGameplayTag Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValue; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractGameplayTagArray(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + TArray ExtractedValues; + const EFlowDataPinResolveResult ResolveResult = TryExtractValues(DataPinResult, ExtractedValues); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractGameplayTagArray Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValues; +} + +FGameplayTagContainer UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractGameplayTagContainer(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + FGameplayTagContainer ExtractedValue; + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + const EFlowDataPinResolveResult ResolveResult = TryExtractValue(DataPinResult, ExtractedValue, SingleMode); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractGameplayTagContainer Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValue; +} + +FInstancedStruct UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractInstancedStruct(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + FInstancedStruct ExtractedValue; + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + const EFlowDataPinResolveResult ResolveResult = TryExtractValue(DataPinResult, ExtractedValue, SingleMode); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractInstancedStruct Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValue; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractInstancedStructArray(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + TArray ExtractedValues; + const EFlowDataPinResolveResult ResolveResult = TryExtractValues(DataPinResult, ExtractedValues); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractInstancedStructArray Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValues; +} + +UObject* UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractObject(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + TObjectPtr ExtractedValue = nullptr; + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + const EFlowDataPinResolveResult ResolveResult = TryExtractValue(DataPinResult, ExtractedValue, SingleMode); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractObject Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValue; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractObjectArray(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + TArray> ExtractedValues; + const EFlowDataPinResolveResult ResolveResult = TryExtractValues(DataPinResult, ExtractedValues); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractObjectArray Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValues; +} + +UClass* UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractClass(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + TObjectPtr ExtractedValue = nullptr; + constexpr EFlowSingleFromArray SingleMode = EFlowSingleFromArray::LastValue; + const EFlowDataPinResolveResult ResolveResult = TryExtractValue(DataPinResult, ExtractedValue, SingleMode); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractClass Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValue; +} + +TArray UFlowDataPinBlueprintLibrary::AutoConvert_TryExtractClassArray(const FFlowDataPinResult& DataPinResult) +{ + using namespace FlowPinType; + TArray> ExtractedValues; + const EFlowDataPinResolveResult ResolveResult = TryExtractValues(DataPinResult, ExtractedValues); + if (!IsSuccess(ResolveResult)) + { + UE_LOG(LogFlow, Error, TEXT("TryExtractClassArray Error: %s"), *UEnum::GetDisplayValueAsText(ResolveResult).ToString()); + } + return ExtractedValues; +} + +bool UFlowDataPinBlueprintLibrary::GetBoolValue(const FFlowDataPinValue_Bool& BoolDataPinValue, EFlowSingleFromArray SingleFromArray) +{ +#if WITH_EDITOR + if (BoolDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetBoolValue on an input pin, use ResolveAsBool instead.")); + } +#endif + const int32 Index = EFlowSingleFromArray_Classifiers::ConvertToIndex(SingleFromArray, BoolDataPinValue.Values.Num()); + if (BoolDataPinValue.Values.IsValidIndex(Index)) + { + return BoolDataPinValue.Values[Index]; + } + UE_LOG(LogFlow, Error, TEXT("Insufficient values in Bool Data Pin Value.")); + return false; +} + +TArray UFlowDataPinBlueprintLibrary::GetBoolValues(FFlowDataPinValue_Bool& BoolDataPinValue) +{ +#if WITH_EDITOR + if (BoolDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetBoolValues on an input pin, use ResolveAsBoolArray instead.")); + } +#endif + return BoolDataPinValue.Values; +} + +// Int +int32 UFlowDataPinBlueprintLibrary::GetIntValue(const FFlowDataPinValue_Int& IntDataPinValue, EFlowSingleFromArray SingleFromArray) +{ +#if WITH_EDITOR + if (IntDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetIntValue on an input pin, use ResolveAsInt instead.")); + } +#endif + const int32 Index = EFlowSingleFromArray_Classifiers::ConvertToIndex(SingleFromArray, IntDataPinValue.Values.Num()); + if (IntDataPinValue.Values.IsValidIndex(Index)) + { + return IntDataPinValue.Values[Index]; + } + UE_LOG(LogFlow, Error, TEXT("Insufficient values in Int Data Pin Value.")); + return 0; +} + +TArray UFlowDataPinBlueprintLibrary::GetIntValues(FFlowDataPinValue_Int& IntDataPinValue) +{ +#if WITH_EDITOR + if (IntDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetIntValues on an input pin, use ResolveAsIntArray instead.")); + } +#endif + return IntDataPinValue.Values; +} + +// Int64 +int64 UFlowDataPinBlueprintLibrary::GetInt64Value(const FFlowDataPinValue_Int64& Int64DataPinValue, EFlowSingleFromArray SingleFromArray) +{ +#if WITH_EDITOR + if (Int64DataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetInt64Value on an input pin, use ResolveAsInt64 instead.")); + } +#endif + const int32 Index = EFlowSingleFromArray_Classifiers::ConvertToIndex(SingleFromArray, Int64DataPinValue.Values.Num()); + if (Int64DataPinValue.Values.IsValidIndex(Index)) + { + return Int64DataPinValue.Values[Index]; + } + UE_LOG(LogFlow, Error, TEXT("Insufficient values in Int64 Data Pin Value.")); + return 0; +} + +TArray UFlowDataPinBlueprintLibrary::GetInt64Values(FFlowDataPinValue_Int64& Int64DataPinValue) +{ +#if WITH_EDITOR + if (Int64DataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetInt64Values on an input pin, use ResolveAsInt64Array instead.")); + } +#endif + return Int64DataPinValue.Values; +} + +// Float +float UFlowDataPinBlueprintLibrary::GetFloatValue(const FFlowDataPinValue_Float& FloatDataPinValue, EFlowSingleFromArray SingleFromArray) +{ +#if WITH_EDITOR + if (FloatDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetFloatValue on an input pin, use ResolveAsFloat instead.")); + } +#endif + const int32 Index = EFlowSingleFromArray_Classifiers::ConvertToIndex(SingleFromArray, FloatDataPinValue.Values.Num()); + if (FloatDataPinValue.Values.IsValidIndex(Index)) + { + return FloatDataPinValue.Values[Index]; + } + UE_LOG(LogFlow, Error, TEXT("Insufficient values in Float Data Pin Value.")); + return 0.f; +} + +TArray UFlowDataPinBlueprintLibrary::GetFloatValues(FFlowDataPinValue_Float& FloatDataPinValue) +{ +#if WITH_EDITOR + if (FloatDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetFloatValues on an input pin, use ResolveAsFloatArray instead.")); + } +#endif + return FloatDataPinValue.Values; +} + +// Double +double UFlowDataPinBlueprintLibrary::GetDoubleValue(const FFlowDataPinValue_Double& DoubleDataPinValue, EFlowSingleFromArray SingleFromArray) +{ +#if WITH_EDITOR + if (DoubleDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetDoubleValue on an input pin, use ResolveAsDouble instead.")); + } +#endif + const int32 Index = EFlowSingleFromArray_Classifiers::ConvertToIndex(SingleFromArray, DoubleDataPinValue.Values.Num()); + if (DoubleDataPinValue.Values.IsValidIndex(Index)) + { + return DoubleDataPinValue.Values[Index]; + } + UE_LOG(LogFlow, Error, TEXT("Insufficient values in Double Data Pin Value.")); + return 0.0; +} + +TArray UFlowDataPinBlueprintLibrary::GetDoubleValues(FFlowDataPinValue_Double& DoubleDataPinValue) +{ +#if WITH_EDITOR + if (DoubleDataPinValue.bIsInputPin) { - const uint64 EnumValueAsInt = EnumProperty.EnumClass->GetValueByName(EnumProperty.Value); + UE_LOG(LogFlow, Error, TEXT("Should not call GetDoubleValues on an input pin, use ResolveAsDoubleArray instead.")); + } +#endif + return DoubleDataPinValue.Values; +} - // At least For Now(tm) Blueprint Enums want to be uint8's - return static_cast(EnumValueAsInt); +// Name +FName UFlowDataPinBlueprintLibrary::GetNameValue(const FFlowDataPinValue_Name& NameDataPinValue, EFlowSingleFromArray SingleFromArray) +{ +#if WITH_EDITOR + if (NameDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetNameValue on an input pin, use ResolveAsName instead.")); + } +#endif + const int32 Index = EFlowSingleFromArray_Classifiers::ConvertToIndex(SingleFromArray, NameDataPinValue.Values.Num()); + if (NameDataPinValue.Values.IsValidIndex(Index)) + { + return NameDataPinValue.Values[Index]; } + UE_LOG(LogFlow, Error, TEXT("Insufficient values in Name Data Pin Value.")); + return FName(); +} - UE_LOG(LogFlow, Error, TEXT("Could not cast enum value %s, because missing enum class"), *EnumProperty.Value.ToString()); +TArray UFlowDataPinBlueprintLibrary::GetNameValues(FFlowDataPinValue_Name& NameDataPinValue) +{ +#if WITH_EDITOR + if (NameDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetNameValues on an input pin, use ResolveAsNameArray instead.")); + } +#endif + return NameDataPinValue.Values; +} - return static_cast(INDEX_NONE); +// String +FString UFlowDataPinBlueprintLibrary::GetStringValue(const FFlowDataPinValue_String& StringDataPinValue, EFlowSingleFromArray SingleFromArray) +{ +#if WITH_EDITOR + if (StringDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetStringValue on an input pin, use ResolveAsString instead.")); + } +#endif + const int32 Index = EFlowSingleFromArray_Classifiers::ConvertToIndex(SingleFromArray, StringDataPinValue.Values.Num()); + if (StringDataPinValue.Values.IsValidIndex(Index)) + { + return StringDataPinValue.Values[Index]; + } + UE_LOG(LogFlow, Error, TEXT("Insufficient values in String Data Pin Value.")); + return FString(); } -uint8 UFlowDataPinBlueprintLibrary::AutoConvert_FlowDataPinResultEnumToEnum(const FFlowDataPinResult_Enum& EnumProperty) +TArray UFlowDataPinBlueprintLibrary::GetStringValues(FFlowDataPinValue_String& StringDataPinValue) { - if (IsValid(EnumProperty.EnumClass)) +#if WITH_EDITOR + if (StringDataPinValue.bIsInputPin) { - const uint64 EnumValueAsInt = EnumProperty.EnumClass->GetValueByName(EnumProperty.Value); + UE_LOG(LogFlow, Error, TEXT("Should not call GetStringValues on an input pin, use ResolveAsStringArray instead.")); + } +#endif + return StringDataPinValue.Values; +} - // At least For Now(tm) Blueprint Enums want to be uint8's - return static_cast(EnumValueAsInt); +// Text +FText UFlowDataPinBlueprintLibrary::GetTextValue(const FFlowDataPinValue_Text& TextDataPinValue, EFlowSingleFromArray SingleFromArray) +{ +#if WITH_EDITOR + if (TextDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetTextValue on an input pin, use ResolveAsText instead.")); + } +#endif + const int32 Index = EFlowSingleFromArray_Classifiers::ConvertToIndex(SingleFromArray, TextDataPinValue.Values.Num()); + if (TextDataPinValue.Values.IsValidIndex(Index)) + { + return TextDataPinValue.Values[Index]; + } + UE_LOG(LogFlow, Error, TEXT("Insufficient values in Text Data Pin Value.")); + return FText::GetEmpty(); +} + +TArray UFlowDataPinBlueprintLibrary::GetTextValues(FFlowDataPinValue_Text& TextDataPinValue) +{ +#if WITH_EDITOR + if (TextDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetTextValues on an input pin, use ResolveAsTextArray instead.")); } +#endif + return TextDataPinValue.Values; +} - UE_LOG(LogFlow, Error, TEXT("Could not cast enum value %s, because missing enum class"), *EnumProperty.Value.ToString()); +// ----- Enum Setters ----- - return static_cast(INDEX_NONE); +void UFlowDataPinBlueprintLibrary::SetEnumValue(uint8 InValue, FFlowDataPinValue_Enum& EnumDataPinValue) +{ + UEnum* EnumClass = EnumDataPinValue.EnumClass.LoadSynchronous(); + if (!IsValid(EnumClass)) + { + UE_LOG(LogFlow, Error, TEXT("SetEnumValue: Null EnumClass")); + return; + } + + const int64 ValueInt64 = static_cast(InValue); + const FName EnumValueName = EnumClass->GetNameByValue(ValueInt64); + if (EnumValueName.IsNone()) + { + UE_LOG(LogFlow, Error, TEXT("SetEnumValue: Could not find enum name for value %d in %s"), InValue, *EnumClass->GetPathName()); + return; + } + + EnumDataPinValue.EnumClass = EnumClass; + EnumDataPinValue.Values = { EnumValueName }; +#if WITH_EDITOR + EnumDataPinValue.MultiType = EFlowDataMultiType::Single; +#endif +} + +void UFlowDataPinBlueprintLibrary::SetEnumValues(const TArray& InValues, FFlowDataPinValue_Enum& EnumDataPinValue) +{ + UEnum* EnumClass = EnumDataPinValue.EnumClass.LoadSynchronous(); + if (!IsValid(EnumClass)) + { + UE_LOG(LogFlow, Error, TEXT("SetEnumValues: Null EnumClass")); + return; + } + + TArray Names; + Names.Reserve(InValues.Num()); + + for (uint8 RawValue : InValues) + { + const int64 ValueInt64 = static_cast(RawValue); + const FName EnumValueName = EnumClass->GetNameByValue(ValueInt64); + if (EnumValueName.IsNone()) + { + UE_LOG(LogFlow, Error, TEXT("SetEnumValues: Could not find enum name for value %d in %s"), RawValue, *EnumClass->GetPathName()); + + // Abort entire set to avoid partial data + return; + } + + Names.Add(EnumValueName); + } + + EnumDataPinValue.EnumClass = EnumClass; + EnumDataPinValue.Values = MoveTemp(Names); +#if WITH_EDITOR + EnumDataPinValue.MultiType = EFlowDataMultiType::Array; +#endif +} + +uint8 UFlowDataPinBlueprintLibrary::GetEnumValue(const FFlowDataPinValue_Enum& EnumDataPinValue, EFlowSingleFromArray SingleFromArray) +{ +#if WITH_EDITOR + if (EnumDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetEnumValue on an input pin, use ResolveAsEnum instead.")); + } +#endif + + UEnum* EnumClass = EnumDataPinValue.EnumClass.LoadSynchronous(); + if (!IsValid(EnumClass)) + { + UE_LOG(LogFlow, Error, TEXT("GetEnumValue: Null EnumClass")); + return 0; + } + + const int32 Index = EFlowSingleFromArray_Classifiers::ConvertToIndex(SingleFromArray, EnumDataPinValue.Values.Num()); + if (!EnumDataPinValue.Values.IsValidIndex(Index)) + { + UE_LOG(LogFlow, Error, TEXT("GetEnumValue: Insufficient values.")); + return 0; + } + + const FName& EnumName = EnumDataPinValue.Values[Index]; + const int32 EnumIndex = EnumClass->GetIndexByName(EnumName); + if (EnumIndex == INDEX_NONE) + { + UE_LOG(LogFlow, Error, TEXT("GetEnumValue: Name '%s' not found in enum %s"), *EnumName.ToString(), *EnumClass->GetPathName()); + return 0; + } + + const int64 RawValue = EnumClass->GetValueByIndex(EnumIndex); + return static_cast(RawValue); +} + +TArray UFlowDataPinBlueprintLibrary::GetEnumValues(const FFlowDataPinValue_Enum& EnumDataPinValue) +{ +#if WITH_EDITOR + if (EnumDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetEnumValues on an input pin, use ResolveAsEnumArray instead.")); + } +#endif + TArray Values; + + UEnum* EnumClass = EnumDataPinValue.EnumClass.LoadSynchronous(); + if (!IsValid(EnumClass)) + { + UE_LOG(LogFlow, Error, TEXT("GetEnumValues: Null EnumClass")); + return Values; + } + + Values.Reserve(EnumDataPinValue.Values.Num()); + for (const FName& EnumName : EnumDataPinValue.Values) + { + const int32 EnumIndex = EnumClass->GetIndexByName(EnumName); + if (EnumIndex == INDEX_NONE) + { + UE_LOG(LogFlow, Error, TEXT("GetEnumValues: Name '%s' not found in enum %s"), *EnumName.ToString(), *EnumClass->GetPathName()); + + // Abort entire set to avoid partial data + return TArray(); + } + const int64 RawValue = EnumClass->GetValueByIndex(EnumIndex); + Values.Add(static_cast(RawValue)); + } + + return Values; +} + +// Vector +FVector UFlowDataPinBlueprintLibrary::GetVectorValue(const FFlowDataPinValue_Vector& VectorDataPinValue, EFlowSingleFromArray SingleFromArray) +{ +#if WITH_EDITOR + if (VectorDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetVectorValue on an input pin, use ResolveAsVector instead.")); + } +#endif + const int32 Index = EFlowSingleFromArray_Classifiers::ConvertToIndex(SingleFromArray, VectorDataPinValue.Values.Num()); + if (VectorDataPinValue.Values.IsValidIndex(Index)) + { + return VectorDataPinValue.Values[Index]; + } + UE_LOG(LogFlow, Error, TEXT("Insufficient values in Vector Data Pin Value.")); + return FVector::ZeroVector; +} + +TArray UFlowDataPinBlueprintLibrary::GetVectorValues(FFlowDataPinValue_Vector& VectorDataPinValue) +{ +#if WITH_EDITOR + if (VectorDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetVectorValues on an input pin, use ResolveAsVectorArray instead.")); + } +#endif + return VectorDataPinValue.Values; +} + +// Rotator +FRotator UFlowDataPinBlueprintLibrary::GetRotatorValue(const FFlowDataPinValue_Rotator& RotatorDataPinValue, EFlowSingleFromArray SingleFromArray) +{ +#if WITH_EDITOR + if (RotatorDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetRotatorValue on an input pin, use ResolveAsRotator instead.")); + } +#endif + const int32 Index = EFlowSingleFromArray_Classifiers::ConvertToIndex(SingleFromArray, RotatorDataPinValue.Values.Num()); + if (RotatorDataPinValue.Values.IsValidIndex(Index)) + { + return RotatorDataPinValue.Values[Index]; + } + UE_LOG(LogFlow, Error, TEXT("Insufficient values in Rotator Data Pin Value.")); + return FRotator::ZeroRotator; +} + +TArray UFlowDataPinBlueprintLibrary::GetRotatorValues(FFlowDataPinValue_Rotator& RotatorDataPinValue) +{ +#if WITH_EDITOR + if (RotatorDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetRotatorValues on an input pin, use ResolveAsRotatorArray instead.")); + } +#endif + return RotatorDataPinValue.Values; +} + +// Transform +FTransform UFlowDataPinBlueprintLibrary::GetTransformValue(const FFlowDataPinValue_Transform& TransformDataPinValue, EFlowSingleFromArray SingleFromArray) +{ +#if WITH_EDITOR + if (TransformDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetTransformValue on an input pin, use ResolveAsTransform instead.")); + } +#endif + const int32 Index = EFlowSingleFromArray_Classifiers::ConvertToIndex(SingleFromArray, TransformDataPinValue.Values.Num()); + if (TransformDataPinValue.Values.IsValidIndex(Index)) + { + return TransformDataPinValue.Values[Index]; + } + UE_LOG(LogFlow, Error, TEXT("Insufficient values in Transform Data Pin Value.")); + return FTransform::Identity; +} + +TArray UFlowDataPinBlueprintLibrary::GetTransformValues(FFlowDataPinValue_Transform& TransformDataPinValue) +{ +#if WITH_EDITOR + if (TransformDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetTransformValues on an input pin, use ResolveAsTransformArray instead.")); + } +#endif + return TransformDataPinValue.Values; +} + +// GameplayTag +FGameplayTag UFlowDataPinBlueprintLibrary::GetGameplayTagValue(const FFlowDataPinValue_GameplayTag& GameplayTagDataPinValue, EFlowSingleFromArray SingleFromArray) +{ +#if WITH_EDITOR + if (GameplayTagDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetGameplayTagValue on an input pin, use ResolveAsGameplayTag instead.")); + } +#endif + const int32 Index = EFlowSingleFromArray_Classifiers::ConvertToIndex(SingleFromArray, GameplayTagDataPinValue.Values.Num()); + if (GameplayTagDataPinValue.Values.IsValidIndex(Index)) + { + return GameplayTagDataPinValue.Values[Index]; + } + UE_LOG(LogFlow, Error, TEXT("Insufficient values in GameplayTag Data Pin Value.")); + return FGameplayTag(); +} + +TArray UFlowDataPinBlueprintLibrary::GetGameplayTagValues(FFlowDataPinValue_GameplayTag& GameplayTagDataPinValue) +{ +#if WITH_EDITOR + if (GameplayTagDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetGameplayTagValues on an input pin, use ResolveAsGameplayTagArray instead.")); + } +#endif + return GameplayTagDataPinValue.Values; +} + +// GameplayTagContainer (scalar only) +FGameplayTagContainer UFlowDataPinBlueprintLibrary::GetGameplayTagContainerValue(const FFlowDataPinValue_GameplayTagContainer& GameplayTagContainerDataPinValue) +{ +#if WITH_EDITOR + if (GameplayTagContainerDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetGameplayTagContainerValue on an input pin, use ResolveAsGameplayTagContainer instead.")); + } +#endif + return GameplayTagContainerDataPinValue.Values; +} + +// InstancedStruct +FInstancedStruct UFlowDataPinBlueprintLibrary::GetInstancedStructValue(const FFlowDataPinValue_InstancedStruct& InstancedStructDataPinValue, EFlowSingleFromArray SingleFromArray) +{ +#if WITH_EDITOR + if (InstancedStructDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetInstancedStructValue on an input pin, use ResolveAsInstancedStruct instead.")); + } +#endif + const int32 Index = EFlowSingleFromArray_Classifiers::ConvertToIndex(SingleFromArray, InstancedStructDataPinValue.Values.Num()); + if (InstancedStructDataPinValue.Values.IsValidIndex(Index)) + { + return InstancedStructDataPinValue.Values[Index]; + } + UE_LOG(LogFlow, Error, TEXT("Insufficient values in InstancedStruct Data Pin Value.")); + return FInstancedStruct(); +} + +TArray UFlowDataPinBlueprintLibrary::GetInstancedStructValues(FFlowDataPinValue_InstancedStruct& InstancedStructDataPinValue) +{ +#if WITH_EDITOR + if (InstancedStructDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetInstancedStructValues on an input pin, use ResolveAsInstancedStructArray instead.")); + } +#endif + return InstancedStructDataPinValue.Values; +} + +// Object +UObject* UFlowDataPinBlueprintLibrary::GetObjectValue(const FFlowDataPinValue_Object& ObjectDataPinValue, EFlowSingleFromArray SingleFromArray) +{ +#if WITH_EDITOR + if (ObjectDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetObjectValue on an input pin, use ResolveAsObject instead.")); + } +#endif + const int32 Index = EFlowSingleFromArray_Classifiers::ConvertToIndex(SingleFromArray, ObjectDataPinValue.Values.Num()); + if (ObjectDataPinValue.Values.IsValidIndex(Index)) + { + return ObjectDataPinValue.Values[Index]; + } + UE_LOG(LogFlow, Error, TEXT("Insufficient values in Object Data Pin Value.")); + return nullptr; +} + +TArray UFlowDataPinBlueprintLibrary::GetObjectValues(FFlowDataPinValue_Object& ObjectDataPinValue) +{ +#if WITH_EDITOR + if (ObjectDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetObjectValues on an input pin, use ResolveAsObjectArray instead.")); + } +#endif + return ObjectDataPinValue.Values; +} + +// Class +FSoftClassPath UFlowDataPinBlueprintLibrary::GetClassValue(const FFlowDataPinValue_Class& ClassDataPinValue, EFlowSingleFromArray SingleFromArray) +{ +#if WITH_EDITOR + if (ClassDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetClassValue on an input pin, use ResolveAsClass instead.")); + } +#endif + const int32 Index = EFlowSingleFromArray_Classifiers::ConvertToIndex(SingleFromArray, ClassDataPinValue.Values.Num()); + if (ClassDataPinValue.Values.IsValidIndex(Index)) + { + return ClassDataPinValue.Values[Index]; + } + UE_LOG(LogFlow, Error, TEXT("Insufficient values in Class Data Pin Value.")); + return nullptr; +} + +TArray UFlowDataPinBlueprintLibrary::GetClassValues(FFlowDataPinValue_Class& ClassDataPinValue) +{ +#if WITH_EDITOR + if (ClassDataPinValue.bIsInputPin) + { + UE_LOG(LogFlow, Error, TEXT("Should not call GetClassValues on an input pin, use ResolveAsClassArray instead.")); + } +#endif + return ClassDataPinValue.Values; +} + +FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_Bool(const TArray& BoolValues) +{ + FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); + + FFlowDataPinValue_Bool ValueStruct; + ValueStruct.Values = BoolValues; + Out.ResultValue = TInstancedStruct::Make(ValueStruct); + + return Out; +} + +FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_Int(const TArray& IntValues) +{ + FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); + + FFlowDataPinValue_Int ValueStruct; + ValueStruct.Values = IntValues; + Out.ResultValue = TInstancedStruct::Make(ValueStruct); + + return Out; +} + +FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_Int64(const TArray& Int64Values) +{ + FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); + + FFlowDataPinValue_Int64 ValueStruct; + ValueStruct.Values = Int64Values; + Out.ResultValue = TInstancedStruct::Make(ValueStruct); + + return Out; +} + +FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_Float(const TArray& FloatValues) +{ + FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); + + FFlowDataPinValue_Float ValueStruct; + ValueStruct.Values = FloatValues; + Out.ResultValue = TInstancedStruct::Make(ValueStruct); + + return Out; +} + +FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_Double(const TArray& DoubleValues) +{ + FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); + + FFlowDataPinValue_Double ValueStruct; + ValueStruct.Values = DoubleValues; + Out.ResultValue = TInstancedStruct::Make(ValueStruct); + + return Out; +} + +FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_Name(const TArray& NameValues) +{ + FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); + + FFlowDataPinValue_Name ValueStruct; + ValueStruct.Values = NameValues; + Out.ResultValue = TInstancedStruct::Make(ValueStruct); + + return Out; +} + +FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_String(const TArray& StringValues) +{ + FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); + + FFlowDataPinValue_String ValueStruct; + ValueStruct.Values = StringValues; + Out.ResultValue = TInstancedStruct::Make(ValueStruct); + + return Out; +} + +FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_Text(const TArray& TextValues) +{ + FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); + + FFlowDataPinValue_Text ValueStruct; + ValueStruct.Values = TextValues; + Out.ResultValue = TInstancedStruct::Make(ValueStruct); + + return Out; +} + +FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_Enum(const TArray& EnumValues, UEnum* EnumClass) +{ + FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); + + if (EnumClass) + { + FFlowDataPinValue_Enum ValueStruct; + ValueStruct.EnumClass = EnumClass; + ValueStruct.Values.Reserve(EnumValues.Num()); + + for (uint8 RawValue : EnumValues) + { + const FName EnumName = EnumClass->GetNameByValue(RawValue); + ValueStruct.Values.Add(EnumName); + } + + Out.ResultValue = TInstancedStruct::Make(ValueStruct); + } + + return Out; +} + +FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_Vector(const TArray& VectorValues) +{ + FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); + + FFlowDataPinValue_Vector ValueStruct; + ValueStruct.Values = VectorValues; + Out.ResultValue = TInstancedStruct::Make(ValueStruct); + + return Out; +} + +FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_Rotator(const TArray& RotatorValues) +{ + FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); + + FFlowDataPinValue_Rotator ValueStruct; + ValueStruct.Values = RotatorValues; + Out.ResultValue = TInstancedStruct::Make(ValueStruct); + + return Out; +} + +FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_Transform(const TArray& TransformValues) +{ + FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); + + FFlowDataPinValue_Transform ValueStruct; + ValueStruct.Values = TransformValues; + Out.ResultValue = TInstancedStruct::Make(ValueStruct); + + return Out; +} + +FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_GameplayTag(const TArray& GameplayTagValues) +{ + FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); + + FFlowDataPinValue_GameplayTag ValueStruct; + ValueStruct.Values = GameplayTagValues; + Out.ResultValue = TInstancedStruct::Make(ValueStruct); + + return Out; +} + +FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_GameplayTagContainer(FGameplayTagContainer GameplayTagContainerValue) +{ + FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); + + FFlowDataPinValue_GameplayTagContainer ValueStruct; + ValueStruct.Values = GameplayTagContainerValue; + Out.ResultValue = TInstancedStruct::Make(ValueStruct); + + return Out; +} + +FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_InstancedStruct(const TArray& InstancedStructValues) +{ + FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); + + FFlowDataPinValue_InstancedStruct ValueStruct; + ValueStruct.Values = InstancedStructValues; + Out.ResultValue = TInstancedStruct::Make(ValueStruct); + + return Out; +} + +FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_Object(const TArray& ObjectValues) +{ + FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); + + FFlowDataPinValue_Object ValueStruct; + ValueStruct.Values = ObjectValues; + Out.ResultValue = TInstancedStruct::Make(ValueStruct); + + return Out; } + +FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_Class(const TArray& ClassValues) +{ + FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); + + FFlowDataPinValue_Class ValueStruct; + ValueStruct.Values = ClassValues; + + Out.ResultValue = TInstancedStruct::Make(ValueStruct); + return Out; +} \ No newline at end of file diff --git a/Source/Flow/Private/Types/FlowDataPinProperties.cpp b/Source/Flow/Private/Types/FlowDataPinProperties.cpp index 1f11e190a..196672b9e 100644 --- a/Source/Flow/Private/Types/FlowDataPinProperties.cpp +++ b/Source/Flow/Private/Types/FlowDataPinProperties.cpp @@ -1,191 +1,11 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors +// #FlowDataPinLegacy #include "Types/FlowDataPinProperties.h" -#include "Types/FlowClassUtils.h" -#include "FlowLogChannels.h" #include "UObject/Class.h" #include "UObject/UObjectIterator.h" -#if WITH_EDITOR -#include "EditorClassUtils.h" -#endif - -#define LOCTEXT_NAMESPACE "FlowDataPinProperties" - -#if WITH_EDITOR - -FFlowPin FFlowDataPinProperty::CreateFlowPin(const FName& PinName, const TInstancedStruct& DataPinProperty) -{ - FFlowPin FlowPin; - - const FFlowDataPinProperty* Property = DataPinProperty.GetPtr(); - if (!Property) - { - return FlowPin; - } - - FlowPin.PinName = PinName; - - const EFlowPinType FlowPinType = Property->GetFlowPinType(); - - FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); - - switch (FlowPinType) - { - case EFlowPinType::Enum: - { - const FFlowDataPinOutputProperty_Enum& EnumDataPinProperty = DataPinProperty.Get(); - UEnum* EnumClass = EnumDataPinProperty.EnumClass; - - FlowPin.SetPinType(FlowPinType, EnumClass); - } - break; - - case EFlowPinType::Class: - { - const FFlowDataPinOutputProperty_Class& ClassDataPinProperty = DataPinProperty.Get(); - - FlowPin.SetPinType(FlowPinType, ClassDataPinProperty.ClassFilter); - } - break; - - case EFlowPinType::Object: - { - const FFlowDataPinOutputProperty_Object& ObjectDataPinProperty = DataPinProperty.Get(); - - FlowPin.SetPinType(FlowPinType, ObjectDataPinProperty.ClassFilter); - } - break; - - default: - { - FlowPin.SetPinType(FlowPinType); - } - break; - } - - return FlowPin; -} - -void FFlowDataPinOutputProperty_Enum::OnEnumNameChanged() -{ - if (!EnumName.IsEmpty()) - { - EnumClass = UClass::TryFindTypeSlow(EnumName, EFindFirstObjectOptions::ExactClass); - - if (EnumClass != nullptr && !FFlowPin::ValidateEnum(*EnumClass)) - { - EnumClass = nullptr; - } - } -} - -FText FFlowNamedDataPinProperty::BuildHeaderText() const -{ - EFlowPinType PinType = EFlowPinType::Invalid; - - if (const FFlowDataPinProperty* DataPinPropertyPtr = DataPinProperty.GetPtr()) - { - PinType = DataPinPropertyPtr->GetFlowPinType(); - } - - return FText::Format(LOCTEXT("FlowNamedDataPinPropertyHeader", "{0} ({1})"), { FText::FromName(Name), UEnum::GetDisplayValueAsText(PinType) }); -} - -UClass* FFlowDataPinOutputProperty_Class::DeriveMetaClass(const FProperty& MetaDataProperty) const -{ - if (UClass* MetaClass = TryGetMetaClassFromProperty(MetaDataProperty)) - { - return MetaClass; - } - - return ClassFilter; -} -UClass* FFlowDataPinOutputProperty_Class::TryGetMetaClassFromProperty(const FProperty& MetaDataProperty) -{ - const FString& MetaClassName = MetaDataProperty.GetMetaData("MetaClass"); - - if (!MetaClassName.IsEmpty()) - { - if (UClass* FoundClass = FEditorClassUtils::GetClassFromString(MetaClassName)) - { - return FoundClass; - } - else - { - UE_LOG(LogFlow, Error, TEXT("Could not resolve MetaClass named %s for property %s"), *MetaClassName, *MetaDataProperty.GetName()); - } - } - - return nullptr; -} - -UClass* FFlowDataPinOutputProperty_Object::DeriveObjectClass(const FProperty& MetaDataProperty) const -{ - if (UClass* MetaClass = FFlowDataPinOutputProperty_Object::TryGetObjectClassFromProperty(MetaDataProperty)) - { - return MetaClass; - } - - return ClassFilter; -} - -UClass* FFlowDataPinOutputProperty_Object::TryGetObjectClassFromProperty(const FProperty& MetaDataProperty) -{ - if (UClass* MetaClass = FFlowDataPinOutputProperty_Class::TryGetMetaClassFromProperty(MetaDataProperty)) - { - return MetaClass; - } - - // FSoftObjectPath can use the "AllowedClasses" to define what classes are allowed for the object. - // Using the "AllowedClasses" metadata tag, but we only support a single class, due to singular return value for this function. - const FString AllowedClassesString = MetaDataProperty.GetMetaData("AllowedClasses"); - const TArray AllowedClasses = FlowClassUtils::GetClassesFromMetadataString(AllowedClassesString); - - if (AllowedClasses.Num() > 1) - { - UE_LOG(LogFlow, Error, TEXT("Only a single AllowedClasses entry is allowed for flow data pin properties (multiple found: %s) for property %s"), *AllowedClassesString, *MetaDataProperty.GetName()); - - return nullptr; - } - - if (AllowedClasses.IsEmpty()) - { - return nullptr; - } - - if (UClass* AllowedClass = AllowedClasses[0]) - { - return AllowedClass; - } - else - { - UE_LOG(LogFlow, Error, TEXT("Could not resolve AllowedClasses '%s' for property %s"), *AllowedClassesString, *MetaDataProperty.GetName()); - } - - return nullptr; -} -#endif - -bool FFlowNamedDataPinProperty::IsInputProperty() const -{ - if (const FFlowDataPinProperty* DataPinPropertyPtr = DataPinProperty.GetPtr()) - { - return DataPinPropertyPtr->IsInputProperty(); - } - - return false; -} - -bool FFlowNamedDataPinProperty::IsOutputProperty() const -{ - if (const FFlowDataPinProperty* DataPinPropertyPtr = DataPinProperty.GetPtr()) - { - return !DataPinPropertyPtr->IsInputProperty(); - } - - return false; -} +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowDataPinProperties) FFlowDataPinOutputProperty_Object::FFlowDataPinOutputProperty_Object(UObject* InValue, UClass* InClassFilter) : Super() @@ -193,12 +13,7 @@ FFlowDataPinOutputProperty_Object::FFlowDataPinOutputProperty_Object(UObject* In , ClassFilter(InClassFilter) #endif { - SetObjectValue(InValue); -} - -void FFlowDataPinOutputProperty_Object::SetObjectValue(UObject* InValue) -{ - UClass* ObjectClass = IsValid(InValue) ? InValue->GetClass() : nullptr; + const UClass* ObjectClass = IsValid(InValue) ? InValue->GetClass() : nullptr; if (IsValid(ObjectClass)) { const bool bIsInstanced = (ObjectClass->ClassFlags & CLASS_EditInlineNew) != 0; @@ -221,4 +36,4 @@ void FFlowDataPinOutputProperty_Object::SetObjectValue(UObject* InValue) } } -#undef LOCTEXT_NAMESPACE +// -- diff --git a/Source/Flow/Private/Types/FlowDataPinResults.cpp b/Source/Flow/Private/Types/FlowDataPinResults.cpp index f8e5f440c..7f92c5df6 100644 --- a/Source/Flow/Private/Types/FlowDataPinResults.cpp +++ b/Source/Flow/Private/Types/FlowDataPinResults.cpp @@ -1,9 +1,9 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "Types/FlowDataPinResults.h" -#include "Types/FlowDataPinProperties.h" +#include "Types/FlowDataPinValuesStandard.h" -// FFlowDataPinResult_Object +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowDataPinResults) FFlowDataPinResult_Object::FFlowDataPinResult_Object(UObject* InValue) : Super(EFlowDataPinResolveResult::Success) @@ -11,11 +11,6 @@ FFlowDataPinResult_Object::FFlowDataPinResult_Object(UObject* InValue) SetValueFromObjectPtr(InValue); } -void FFlowDataPinResult_Object::SetValueFromPropertyWrapper(const FFlowDataPinOutputProperty_Object& InPropertyWrapper) -{ - Value = InPropertyWrapper.GetObjectValue(); -} - // FFlowDataPinResult_Class FFlowDataPinResult_Class::FFlowDataPinResult_Class(const FSoftClassPath& InValuePath) @@ -30,11 +25,6 @@ FFlowDataPinResult_Class::FFlowDataPinResult_Class(UClass* InValueClass) SetValueFromObjectPtr(InValueClass); } -void FFlowDataPinResult_Class::SetValueFromPropertyWrapper(const FFlowDataPinOutputProperty_Class& PropertyWrapper) -{ - SetValueFromSoftPath(PropertyWrapper.GetAsSoftClass()); -} - void FFlowDataPinResult_Class::SetValueFromSoftPath(const FSoftObjectPath& SoftObjectPath) { const FSoftClassPath SoftClassPath(SoftObjectPath.ToString()); diff --git a/Source/Flow/Private/Types/FlowDataPinValue.cpp b/Source/Flow/Private/Types/FlowDataPinValue.cpp new file mode 100644 index 000000000..b10c3e206 --- /dev/null +++ b/Source/Flow/Private/Types/FlowDataPinValue.cpp @@ -0,0 +1,16 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Types/FlowDataPinValue.h" +#include "Types/FlowDataPinResults.h" +#include "Types/FlowDataPinValuesStandard.h" +#include "Types/FlowPinType.h" +#include "Nodes/FlowPin.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowDataPinValue) + +const FString FFlowDataPinValue::StringArraySeparator = TEXT(","); + +const FFlowPinType* FFlowDataPinValue::LookupPinType() const +{ + return FFlowPinType::LookupPinType(GetPinTypeName()); +} diff --git a/Source/Flow/Private/Types/FlowDataPinValuesStandard.cpp b/Source/Flow/Private/Types/FlowDataPinValuesStandard.cpp new file mode 100644 index 000000000..2d8b4db79 --- /dev/null +++ b/Source/Flow/Private/Types/FlowDataPinValuesStandard.cpp @@ -0,0 +1,608 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Types/FlowDataPinValuesStandard.h" +#include "Nodes/FlowPin.h" +#include "Types/FlowArray.h" + +#include "GameFramework/Actor.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowDataPinValuesStandard) + +//====================================================================== +// Bool +//====================================================================== +FFlowDataPinValue_Bool::FFlowDataPinValue_Bool(ValueType InValue) + : Values({ InValue }) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Single; +#endif +} + +FFlowDataPinValue_Bool::FFlowDataPinValue_Bool(const TArray& InValues) + : Values(InValues) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Array; +#endif +} + +bool FFlowDataPinValue_Bool::TryConvertValuesToString(FString& OutString) const +{ + OutString = FlowArray::FormatArrayString(Values, + [](ValueType b) + { + return b ? TEXT("true") : TEXT("false"); + }, + StringArraySeparator); + return true; +} + +//====================================================================== +// Int (int32) +//====================================================================== +FFlowDataPinValue_Int::FFlowDataPinValue_Int(ValueType InValue) + : Values({ InValue }) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Single; +#endif +} + +FFlowDataPinValue_Int::FFlowDataPinValue_Int(const TArray& InValues) + : Values(InValues) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Array; +#endif +} + +bool FFlowDataPinValue_Int::TryConvertValuesToString(FString& OutString) const +{ + OutString = FlowArray::FormatArrayString(Values, + [](ValueType Val) + { + return FString::FromInt(Val); + }, + StringArraySeparator); + return true; +} + +//====================================================================== +// Int64 +//====================================================================== +FFlowDataPinValue_Int64::FFlowDataPinValue_Int64(ValueType InValue) + : Values({ InValue }) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Single; +#endif +} + +FFlowDataPinValue_Int64::FFlowDataPinValue_Int64(const TArray& InValues) + : Values(InValues) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Array; +#endif +} + +bool FFlowDataPinValue_Int64::TryConvertValuesToString(FString& OutString) const +{ + OutString = FlowArray::FormatArrayString(Values, + [](ValueType Val) + { + return FString::Printf(TEXT("%lld"), Val); + }, + StringArraySeparator); + return true; +} + +//====================================================================== +// Float +//====================================================================== +FFlowDataPinValue_Float::FFlowDataPinValue_Float(ValueType InValue) + : Values({ InValue }) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Single; +#endif +} + +FFlowDataPinValue_Float::FFlowDataPinValue_Float(const TArray& InValues) + : Values(InValues) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Array; +#endif +} + +bool FFlowDataPinValue_Float::TryConvertValuesToString(FString& OutString) const +{ + OutString = FlowArray::FormatArrayString(Values, + [](ValueType Val) + { + return FString::SanitizeFloat(Val); + }, + StringArraySeparator); + return true; +} + +//====================================================================== +// Double +//====================================================================== +FFlowDataPinValue_Double::FFlowDataPinValue_Double(ValueType InValue) + : Values({ InValue }) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Single; +#endif +} + +FFlowDataPinValue_Double::FFlowDataPinValue_Double(const TArray& InValues) + : Values(InValues) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Array; +#endif +} + +bool FFlowDataPinValue_Double::TryConvertValuesToString(FString& OutString) const +{ + OutString = FlowArray::FormatArrayString(Values, + [](ValueType Val) + { + return FString::SanitizeFloat(Val); + }, + StringArraySeparator); + return true; +} + +//====================================================================== +// Name +//====================================================================== +FFlowDataPinValue_Name::FFlowDataPinValue_Name(const ValueType& InValue) + : Values({ InValue }) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Single; +#endif +} + +FFlowDataPinValue_Name::FFlowDataPinValue_Name(const TArray& InValues) + : Values(InValues) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Array; +#endif +} + +bool FFlowDataPinValue_Name::TryConvertValuesToString(FString& OutString) const +{ + OutString = FlowArray::FormatArrayString(Values, + [](const ValueType& Val) + { + return Val.ToString(); + }, + StringArraySeparator); + return true; +} + +//====================================================================== +// String +//====================================================================== +FFlowDataPinValue_String::FFlowDataPinValue_String(const ValueType& InValue) + : Values({ InValue }) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Single; +#endif +} + +FFlowDataPinValue_String::FFlowDataPinValue_String(const TArray& InValues) + : Values(InValues) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Array; +#endif +} + +bool FFlowDataPinValue_String::TryConvertValuesToString(FString& OutString) const +{ + OutString = FlowArray::FormatArrayString(Values, + [](const ValueType& Val) + { + return Val; + }, + StringArraySeparator); + return true; +} + +//====================================================================== +// Text +//====================================================================== +FFlowDataPinValue_Text::FFlowDataPinValue_Text(const ValueType& InValue) + : Values({ InValue }) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Single; +#endif +} + +FFlowDataPinValue_Text::FFlowDataPinValue_Text(const TArray& InValues) + : Values(InValues) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Array; +#endif +} + +bool FFlowDataPinValue_Text::TryConvertValuesToString(FString& OutString) const +{ + OutString = FlowArray::FormatArrayString(Values, + [](const ValueType& Val) + { + return Val.ToString(); + }, + StringArraySeparator); + return true; +} + +//====================================================================== +// Enum +//====================================================================== +FFlowDataPinValue_Enum::FFlowDataPinValue_Enum(const TSoftObjectPtr& InEnumClass, const ValueType& InValue) + : Values({ InValue }), EnumClass(InEnumClass) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Single; +#endif +} + +FFlowDataPinValue_Enum::FFlowDataPinValue_Enum(const TSoftObjectPtr& InEnumClass, const TArray& InValues) + : Values(InValues), EnumClass(InEnumClass) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Array; +#endif +} + +#if WITH_EDITOR +void FFlowDataPinValue_Enum::OnEnumNameChanged() +{ + if (!EnumName.IsEmpty()) + { + EnumClass = UClass::TryFindTypeSlow(EnumName, EFindFirstObjectOptions::ExactClass); + + if (EnumClass != nullptr && !FFlowPin::ValidateEnum(*EnumClass)) + { + EnumClass = nullptr; + } + } +} +#endif + +UField* FFlowDataPinValue_Enum::GetFieldType() const +{ + return EnumClass.LoadSynchronous(); +} + +bool FFlowDataPinValue_Enum::TryConvertValuesToString(FString& OutString) const +{ + OutString = FlowArray::FormatArrayString(Values, + [](const ValueType& Val) { return Val.ToString(); }, + StringArraySeparator); + return true; +} + +//====================================================================== +// Vector +//====================================================================== +FFlowDataPinValue_Vector::FFlowDataPinValue_Vector(const ValueType& InValue) + : Values({ InValue }) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Single; +#endif +} + +FFlowDataPinValue_Vector::FFlowDataPinValue_Vector(const TArray& InValues) + : Values(InValues) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Array; +#endif +} + +bool FFlowDataPinValue_Vector::TryConvertValuesToString(FString& OutString) const +{ + OutString = FlowArray::FormatArrayString(Values, + [](const ValueType& V) + { + return V.ToCompactString(); + }, + StringArraySeparator); + return true; +} + +//====================================================================== +// Rotator +//====================================================================== +FFlowDataPinValue_Rotator::FFlowDataPinValue_Rotator(const ValueType& InValue) + : Values({ InValue }) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Single; +#endif +} + +FFlowDataPinValue_Rotator::FFlowDataPinValue_Rotator(const TArray& InValues) + : Values(InValues) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Array; +#endif +} + +bool FFlowDataPinValue_Rotator::TryConvertValuesToString(FString& OutString) const +{ + OutString = FlowArray::FormatArrayString(Values, + [](const ValueType& R) + { + return R.ToCompactString(); + }, + StringArraySeparator); + return true; +} + +//====================================================================== +// Transform +//====================================================================== +FFlowDataPinValue_Transform::FFlowDataPinValue_Transform(const ValueType& InValue) + : Values({ InValue }) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Single; +#endif +} + +FFlowDataPinValue_Transform::FFlowDataPinValue_Transform(const TArray& InValues) + : Values(InValues) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Array; +#endif +} + +bool FFlowDataPinValue_Transform::TryConvertValuesToString(FString& OutString) const +{ + OutString = FlowArray::FormatArrayString(Values, + [](const ValueType& T) + { + return T.ToString(); + }, + StringArraySeparator); + return true; +} + +//====================================================================== +// GameplayTag +//====================================================================== +FFlowDataPinValue_GameplayTag::FFlowDataPinValue_GameplayTag(const ValueType& InValue) + : Values({ InValue }) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Single; +#endif +} + +FFlowDataPinValue_GameplayTag::FFlowDataPinValue_GameplayTag(const TArray& InValues) + : Values(InValues) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Array; +#endif +} + +bool FFlowDataPinValue_GameplayTag::TryConvertValuesToString(FString& OutString) const +{ + OutString = FlowArray::FormatArrayString(Values, + [](const ValueType& Tag) + { + return Tag.ToString(); + }, + StringArraySeparator); + return true; +} + +//====================================================================== +// GameplayTagContainer +//====================================================================== +FFlowDataPinValue_GameplayTagContainer::FFlowDataPinValue_GameplayTagContainer(const FGameplayTag& InValue) + : Values({ InValue }) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Single; +#endif +} + +FFlowDataPinValue_GameplayTagContainer::FFlowDataPinValue_GameplayTagContainer(const FGameplayTagContainer& InValues) + : Values(InValues) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Single; +#endif +} + +FFlowDataPinValue_GameplayTagContainer::FFlowDataPinValue_GameplayTagContainer(const TArray& InValues) + : Values(FGameplayTagContainer::CreateFromArray(InValues)) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Array; +#endif +} + +FFlowDataPinValue_GameplayTagContainer::FFlowDataPinValue_GameplayTagContainer(const TArray& InValues) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Array; +#endif + + for (const FGameplayTagContainer& Container : InValues) + { + Values.AppendTags(Container); + } +} + +bool FFlowDataPinValue_GameplayTagContainer::TryConvertValuesToString(FString& OutString) const +{ + OutString = Values.ToStringSimple(); + return true; +} + +//====================================================================== +// InstancedStruct +//====================================================================== +FFlowDataPinValue_InstancedStruct::FFlowDataPinValue_InstancedStruct(const ValueType& InValue) + : Values({ InValue }) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Single; +#endif +} + +FFlowDataPinValue_InstancedStruct::FFlowDataPinValue_InstancedStruct(const TArray& InValues) + : Values(InValues) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Array; +#endif +} + +//====================================================================== +// Object +//====================================================================== +FFlowDataPinValue_Object::FFlowDataPinValue_Object(TObjectPtr InObject, UClass* InClassFilter) +#if WITH_EDITOR + : ClassFilter(InClassFilter) +#endif +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Single; +#endif + Values = { InObject }; +} + +FFlowDataPinValue_Object::FFlowDataPinValue_Object(const TArray>& InObjects, UClass* InClassFilter) +#if WITH_EDITOR + : ClassFilter(InClassFilter) +#endif +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Array; +#endif + Values = InObjects; +} + +FFlowDataPinValue_Object::FFlowDataPinValue_Object(AActor* InActor, UClass* InClassFilter) +#if WITH_EDITOR + : ClassFilter(InClassFilter ? InClassFilter : AActor::StaticClass()) +#endif +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Single; +#endif + Values = { InActor }; +} + +FFlowDataPinValue_Object::FFlowDataPinValue_Object(const TArray& InActors, UClass* InClassFilter) +#if WITH_EDITOR + : ClassFilter(InClassFilter ? InClassFilter : AActor::StaticClass()) +#endif +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Array; +#endif + + Values.Reset(); + Values.Reserve(InActors.Num()); + for (AActor* Actor : InActors) + { + Values.Add(Cast(Actor)); + } +} + +bool FFlowDataPinValue_Object::TryConvertValuesToString(FString& OutString) const +{ + OutString = FlowArray::FormatArrayString(Values, + [](UObject* Obj) + { + return Obj ? Obj->GetName() : TEXT("None"); + }, + StringArraySeparator); + return true; +} + +//====================================================================== +// Class +//====================================================================== +FFlowDataPinValue_Class::FFlowDataPinValue_Class(const FSoftClassPath& InPath, UClass* InClassFilter) + : Values({ InPath }) +#if WITH_EDITOR + , ClassFilter(InClassFilter) +#endif +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Single; +#endif +} + +FFlowDataPinValue_Class::FFlowDataPinValue_Class(const TArray& InPaths, UClass* InClassFilter) + : Values(InPaths) +#if WITH_EDITOR + , ClassFilter(InClassFilter) +#endif +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Array; +#endif +} + +FFlowDataPinValue_Class::FFlowDataPinValue_Class(const UClass* InClass, UClass* InClassFilter) + : Values({ FSoftClassPath(InClass) }) +#if WITH_EDITOR + , ClassFilter(InClassFilter) +#endif +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Single; +#endif +} + +FFlowDataPinValue_Class::FFlowDataPinValue_Class(const TArray& InClasses, UClass* InClassFilter) +#if WITH_EDITOR + : ClassFilter(InClassFilter) +#endif +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Array; +#endif + + Values.Reset(); + Values.Reserve(InClasses.Num()); + for (UClass* Class : InClasses) + { + Values.Add(FSoftClassPath(Class)); + } +} + +bool FFlowDataPinValue_Class::TryConvertValuesToString(FString& OutString) const +{ + OutString = FlowArray::FormatArrayString(Values, + [](const FSoftClassPath& Path) + { + return Path.IsValid() ? Path.GetAssetName() : TEXT("None"); + }, + StringArraySeparator); + return true; +} \ No newline at end of file diff --git a/Source/Flow/Private/Types/FlowNamedDataPinProperty.cpp b/Source/Flow/Private/Types/FlowNamedDataPinProperty.cpp new file mode 100644 index 000000000..b0d8a289f --- /dev/null +++ b/Source/Flow/Private/Types/FlowNamedDataPinProperty.cpp @@ -0,0 +1,37 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Types/FlowNamedDataPinProperty.h" +#include "Types/FlowDataPinPropertyToValueMigration.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNamedDataPinProperty) + +#define LOCTEXT_NAMESPACE "FlowNamedDataPinProperty" + +#if WITH_EDITOR +FFlowPin FFlowNamedDataPinProperty::CreateFlowPin() const +{ + if (const FFlowDataPinValue* FlowDataPinValuePtr = DataPinValue.GetPtr()) + { + if (const FFlowPinType* FlowPinType = FFlowPinType::LookupPinType(FlowDataPinValuePtr->GetPinTypeName())) + { + return FlowPinType->CreateFlowPinFromValueWrapper(Name, *FlowDataPinValuePtr); + } + } + + return FFlowPin(); +} + +FText FFlowNamedDataPinProperty::BuildHeaderText() const +{ + FFlowPinTypeName PinTypeName; + + if (const FFlowDataPinValue* FlowDataPinValuePtr = DataPinValue.GetPtr()) + { + PinTypeName = FlowDataPinValuePtr->GetPinTypeName(); + } + + return FText::Format(LOCTEXT("FlowNamedFFlowDataPinValueHeader", "{0} ({1})"), { FText::FromName(Name), FText::FromString(PinTypeName.ToString()) }); +} +#endif + +#undef LOCTEXT_NAMESPACE diff --git a/Source/Flow/Private/Types/FlowPinType.cpp b/Source/Flow/Private/Types/FlowPinType.cpp new file mode 100644 index 000000000..c73ffd183 --- /dev/null +++ b/Source/Flow/Private/Types/FlowPinType.cpp @@ -0,0 +1,100 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Types/FlowPinType.h" +#include "Types/FlowDataPinValue.h" +#include "Types/FlowStructUtils.h" +#include "Nodes/FlowNode.h" +#include "FlowPinSubsystem.h" +#include "FlowLogChannels.h" + +#if WITH_EDITOR +#include "PropertyHandle.h" +#endif + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowPinType) + +const FFlowPinTypeName FFlowPinType::PinTypeNameUnknown = FFlowPinTypeName(FFlowPinTypeNamesStandard::PinTypeNameUnknown); + +const FFlowPinType* FFlowPinType::LookupPinType(const FFlowPinTypeName& PinTypeName) +{ + const FFlowPinType* PinType = UFlowPinSubsystem::Get()->FindPinType(PinTypeName); + + if (!PinType) + { + UE_LOG(LogFlow, Error, TEXT("Could not find pin type %s in FlowPinSubsystem"), *PinTypeName.ToString()); + return nullptr; + } + + return PinType; +} + +bool FFlowPinType::ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const +{ + return false; +} + +bool FFlowPinType::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +{ + OutResult.Result = EFlowDataPinResolveResult::FailedMismatchedType; + return false; +} + +#if WITH_EDITOR +FFlowPin FFlowPinType::CreateFlowPinFromProperty(const FProperty& Property, void const* InContainer) const +{ + FFlowPin NewFlowPin; + + if (const FFlowDataPinValue* DataPinValue = FlowStructUtils::CastStructValue(&Property, InContainer)) + { + // Create the pin from a FFlowDataPinValue property wrapper + NewFlowPin = CreateFlowPinFromValueWrapper(Property.GetFName(), *DataPinValue); + } + else + { + // Create the pin from a native property + NewFlowPin.PinName = Property.GetFName(); + NewFlowPin.SetPinTypeName(GetPinTypeName()); + + FLOW_ASSERT_ENUM_MAX(EFlowDataMultiType, 2); + if (CastField(&Property)) + { + NewFlowPin.ContainerType = EPinContainerType::Array; + } + + UObject* SubCategoryObject = GetPinSubCategoryObjectFromProperty(&Property, InContainer, DataPinValue); + NewFlowPin.SetPinSubCategoryObject(SubCategoryObject); + } + + // Common property settings for both versions + NewFlowPin.PinFriendlyName = Property.GetDisplayNameText(); + NewFlowPin.PinToolTip = Property.GetToolTipText().ToString(); + + return NewFlowPin; +} + +FFlowPin FFlowPinType::CreateFlowPinFromValueWrapper(const FName& PinName, const FFlowDataPinValue& Wrapper) const +{ + FFlowPin NewFlowPin(PinName); + + FLOW_ASSERT_ENUM_MAX(EFlowDataMultiType, 2); + if (Wrapper.IsArray()) + { + NewFlowPin.ContainerType = EPinContainerType::Array; + } + + constexpr const FProperty* Property = nullptr; + constexpr void const* InContainer = nullptr; + UObject* SubCategoryObject = GetPinSubCategoryObjectFromProperty(Property, InContainer, &Wrapper); + NewFlowPin.SetPinSubCategoryObject(SubCategoryObject); + + // Common property settings for both versions + NewFlowPin.SetPinTypeName(GetPinTypeName()); + + return NewFlowPin; +} + +TSharedPtr FFlowPinType::GetValuesHandle(const TSharedRef& FlowDataPinValuePropertyHandle) const +{ + return FlowDataPinValuePropertyHandle.Get().GetChildHandle(TEXT("Values")); +} +#endif \ No newline at end of file diff --git a/Source/Flow/Private/Types/FlowPinTypeNamesStandard.cpp b/Source/Flow/Private/Types/FlowPinTypeNamesStandard.cpp new file mode 100644 index 000000000..28d1b0c94 --- /dev/null +++ b/Source/Flow/Private/Types/FlowPinTypeNamesStandard.cpp @@ -0,0 +1,162 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Types/FlowPinTypeNamesStandard.h" + +#if WITH_EDITOR + +// Cross-conversion rules: +// - Most* types → String (one-way) (*except InstancedStruct) +// - Numeric: full bidirectional conversion +// - Name/String/Text: full bidirectional +// - GameplayTag ↔ Container: bidirectional + +const TMap FFlowPinTypeNamesStandard::PinTypeMatchPolicies = +{ + { FFlowPinTypeNamesStandard::PinTypeNameBool, + { + EFlowPinTypeMatchRules::StandardPinTypeMatchRulesMask, + { }, + } + }, + { FFlowPinTypeNamesStandard::PinTypeNameInt, + { + EFlowPinTypeMatchRules::StandardPinTypeMatchRulesMask, + { + FFlowPinTypeNamesStandard::PinTypeNameInt64, + FFlowPinTypeNamesStandard::PinTypeNameFloat, + FFlowPinTypeNamesStandard::PinTypeNameDouble + }, + } + }, + { FFlowPinTypeNamesStandard::PinTypeNameInt64, + { + EFlowPinTypeMatchRules::StandardPinTypeMatchRulesMask, + { + FFlowPinTypeNamesStandard::PinTypeNameInt, + FFlowPinTypeNamesStandard::PinTypeNameFloat, + FFlowPinTypeNamesStandard::PinTypeNameDouble + }, + } + }, + { FFlowPinTypeNamesStandard::PinTypeNameFloat, + { + EFlowPinTypeMatchRules::StandardPinTypeMatchRulesMask, + { + FFlowPinTypeNamesStandard::PinTypeNameInt, + FFlowPinTypeNamesStandard::PinTypeNameInt64, + FFlowPinTypeNamesStandard::PinTypeNameDouble + }, + } + }, + { FFlowPinTypeNamesStandard::PinTypeNameDouble, + { + EFlowPinTypeMatchRules::StandardPinTypeMatchRulesMask, + { + FFlowPinTypeNamesStandard::PinTypeNameInt, + FFlowPinTypeNamesStandard::PinTypeNameInt64, + FFlowPinTypeNamesStandard::PinTypeNameFloat + }, + } + }, + { FFlowPinTypeNamesStandard::PinTypeNameEnum, + { + EFlowPinTypeMatchRules::StandardPinTypeMatchRulesMask, + { }, + } + }, + { FFlowPinTypeNamesStandard::PinTypeNameName, + { + EFlowPinTypeMatchRules::StandardPinTypeMatchRulesMask, + { + FFlowPinTypeNamesStandard::PinTypeNameString, + FFlowPinTypeNamesStandard::PinTypeNameText + }, + } + }, + { FFlowPinTypeNamesStandard::PinTypeNameString, + { + EFlowPinTypeMatchRules::StandardPinTypeMatchRulesMask, + { + FFlowPinTypeNamesStandard::PinTypeNameName, + FFlowPinTypeNamesStandard::PinTypeNameText, + + // All other types (except InstancedStruct) can cross-convert to string + FFlowPinTypeNamesStandard::PinTypeNameBool, + FFlowPinTypeNamesStandard::PinTypeNameInt, + FFlowPinTypeNamesStandard::PinTypeNameInt64, + FFlowPinTypeNamesStandard::PinTypeNameFloat, + FFlowPinTypeNamesStandard::PinTypeNameDouble, + FFlowPinTypeNamesStandard::PinTypeNameEnum, + FFlowPinTypeNamesStandard::PinTypeNameVector, + FFlowPinTypeNamesStandard::PinTypeNameRotator, + FFlowPinTypeNamesStandard::PinTypeNameTransform, + FFlowPinTypeNamesStandard::PinTypeNameGameplayTag, + FFlowPinTypeNamesStandard::PinTypeNameGameplayTagContainer, + FFlowPinTypeNamesStandard::PinTypeNameObject, + FFlowPinTypeNamesStandard::PinTypeNameClass + }, + } + }, + { FFlowPinTypeNamesStandard::PinTypeNameText, + { + EFlowPinTypeMatchRules::StandardPinTypeMatchRulesMask, + { + FFlowPinTypeNamesStandard::PinTypeNameString, + FFlowPinTypeNamesStandard::PinTypeNameName + }, + } + }, + { FFlowPinTypeNamesStandard::PinTypeNameVector, + { + EFlowPinTypeMatchRules::StandardPinTypeMatchRulesMask, + { }, + } + }, + { FFlowPinTypeNamesStandard::PinTypeNameRotator, + { + EFlowPinTypeMatchRules::StandardPinTypeMatchRulesMask, + { }, + } + }, + { FFlowPinTypeNamesStandard::PinTypeNameTransform, + { + EFlowPinTypeMatchRules::StandardPinTypeMatchRulesMask, + { }, + } + }, + { FFlowPinTypeNamesStandard::PinTypeNameGameplayTag, + { + EFlowPinTypeMatchRules::StandardPinTypeMatchRulesMask, + { + FFlowPinTypeNamesStandard::PinTypeNameGameplayTagContainer + }, + } + }, + { FFlowPinTypeNamesStandard::PinTypeNameGameplayTagContainer, + { + EFlowPinTypeMatchRules::StandardPinTypeMatchRulesMask, + { + FFlowPinTypeNamesStandard::PinTypeNameGameplayTag + }, + } + }, + { FFlowPinTypeNamesStandard::PinTypeNameInstancedStruct, + { + EFlowPinTypeMatchRules::SubCategoryObjectPinTypeMatchRulesMask, + { }, + } + }, + { FFlowPinTypeNamesStandard::PinTypeNameObject, + { + EFlowPinTypeMatchRules::SubCategoryObjectPinTypeMatchRulesMask, + { }, + } + }, + { FFlowPinTypeNamesStandard::PinTypeNameClass, + { + EFlowPinTypeMatchRules::SubCategoryObjectPinTypeMatchRulesMask, + { }, + } + }, +}; +#endif \ No newline at end of file diff --git a/Source/Flow/Private/Types/FlowPinTypesStandard.cpp b/Source/Flow/Private/Types/FlowPinTypesStandard.cpp new file mode 100644 index 000000000..b959fe6d9 --- /dev/null +++ b/Source/Flow/Private/Types/FlowPinTypesStandard.cpp @@ -0,0 +1,503 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Types/FlowPinTypesStandard.h" +#include "Types/FlowClassUtils.h" +#include "Nodes/FlowNode.h" +#include "Types/FlowDataPinValuesStandard.h" +#include "Types/FlowDataPinResults.h" +#include "Types/FlowPinTypeTemplates.h" +#include "Types/FlowPinTypeNodeTemplates.h" +#include "FlowLogChannels.h" + +#if WITH_EDITOR +#include "EditorClassUtils.h" +#endif + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowPinTypesStandard) + +// NOTE (gtaylor) Beware static initialization order if attempting to use these in static initialization. +// Instead, consider sourcing directly from the FFlowPinTypeNamesStandard's TCHAR form in those cases. +const FFlowPinTypeName FFlowPinType_Exec::PinTypeNameExec = FFlowPinTypeName(FFlowPinTypeNamesStandard::PinTypeNameExec); +const FFlowPinTypeName FFlowPinType_Bool::PinTypeNameBool = FFlowPinTypeName(FFlowPinTypeNamesStandard::PinTypeNameBool); +const FFlowPinTypeName FFlowPinType_Int::PinTypeNameInt = FFlowPinTypeName(FFlowPinTypeNamesStandard::PinTypeNameInt); +const FFlowPinTypeName FFlowPinType_Int64::PinTypeNameInt64 = FFlowPinTypeName(FFlowPinTypeNamesStandard::PinTypeNameInt64); +const FFlowPinTypeName FFlowPinType_Float::PinTypeNameFloat = FFlowPinTypeName(FFlowPinTypeNamesStandard::PinTypeNameFloat); +const FFlowPinTypeName FFlowPinType_Double::PinTypeNameDouble = FFlowPinTypeName(FFlowPinTypeNamesStandard::PinTypeNameDouble); +const FFlowPinTypeName FFlowPinType_Enum::PinTypeNameEnum = FFlowPinTypeName(FFlowPinTypeNamesStandard::PinTypeNameEnum); +const FFlowPinTypeName FFlowPinType_Name::PinTypeNameName = FFlowPinTypeName(FFlowPinTypeNamesStandard::PinTypeNameName); +const FFlowPinTypeName FFlowPinType_String::PinTypeNameString = FFlowPinTypeName(FFlowPinTypeNamesStandard::PinTypeNameString); +const FFlowPinTypeName FFlowPinType_Text::PinTypeNameText = FFlowPinTypeName(FFlowPinTypeNamesStandard::PinTypeNameText); +const FFlowPinTypeName FFlowPinType_Vector::PinTypeNameVector = FFlowPinTypeName(FFlowPinTypeNamesStandard::PinTypeNameVector); +const FFlowPinTypeName FFlowPinType_Rotator::PinTypeNameRotator = FFlowPinTypeName(FFlowPinTypeNamesStandard::PinTypeNameRotator); +const FFlowPinTypeName FFlowPinType_Transform::PinTypeNameTransform = FFlowPinTypeName(FFlowPinTypeNamesStandard::PinTypeNameTransform); +const FFlowPinTypeName FFlowPinType_GameplayTag::PinTypeNameGameplayTag = FFlowPinTypeName(FFlowPinTypeNamesStandard::PinTypeNameGameplayTag); +const FFlowPinTypeName FFlowPinType_GameplayTagContainer::PinTypeNameGameplayTagContainer = FFlowPinTypeName(FFlowPinTypeNamesStandard::PinTypeNameGameplayTagContainer); +const FFlowPinTypeName FFlowPinType_InstancedStruct::PinTypeNameInstancedStruct = FFlowPinTypeName(FFlowPinTypeNamesStandard::PinTypeNameInstancedStruct); +const FFlowPinTypeName FFlowPinType_Object::PinTypeNameObject = FFlowPinTypeName(FFlowPinTypeNamesStandard::PinTypeNameObject); +const FFlowPinTypeName FFlowPinType_Class::PinTypeNameClass = FFlowPinTypeName(FFlowPinTypeNamesStandard::PinTypeNameClass); + +bool FFlowPinType_Bool::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +{ + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); +} + +bool FFlowPinType_Int::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +{ + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); +} + +bool FFlowPinType_Int64::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +{ + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); +} + +bool FFlowPinType_Float::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +{ + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); +} + +bool FFlowPinType_Double::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +{ + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); +} + +bool FFlowPinType_Enum::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +{ + using TFlowPinType = FFlowPinType_Enum; + using TValue = TFlowPinType::ValueType; + using TWrapper = TFlowPinType::WrapperType; + using Traits = FlowPinType::FFlowDataPinValueTraits; + + TInstancedStruct ValueStruct; + const FProperty* FoundProperty = nullptr; + + if (!Node.TryFindPropertyByPinName(PropertyOwnerObject, Pin.PinName, FoundProperty, ValueStruct)) + { + OutResult.Result = EFlowDataPinResolveResult::FailedUnknownPin; + return false; + } + + if (ValueStruct.IsValid() && ValueStruct.Get().GetPinTypeName() == TFlowPinType::GetPinTypeNameStatic()) + { + OutResult.ResultValue = ValueStruct; + OutResult.Result = EFlowDataPinResolveResult::Success; + return true; + } + + TArray Values; + TSoftObjectPtr EnumClass; + if (FlowPinType::IsSuccess(Traits::ExtractFromProperty(FoundProperty, &PropertyOwnerObject, Values, EnumClass))) + { + OutResult.ResultValue = TInstancedStruct::Make(EnumClass, Values); + OutResult.Result = EFlowDataPinResolveResult::Success; + return true; + } + + OutResult.Result = EFlowDataPinResolveResult::FailedMismatchedType; + return false; +} + +bool FFlowPinType_Name::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +{ + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); +} + +bool FFlowPinType_String::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +{ + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); +} + +bool FFlowPinType_Text::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +{ + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); +} + +bool FFlowPinType_Vector::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +{ + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); +} + +bool FFlowPinType_Rotator::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +{ + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); +} + +bool FFlowPinType_Transform::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +{ + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); +} + +bool FFlowPinType_GameplayTag::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +{ + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); +} + +bool FFlowPinType_GameplayTagContainer::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +{ + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); +} + +bool FFlowPinType_InstancedStruct::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +{ + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); +} + +bool FFlowPinType_Object::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +{ + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); +} + +bool FFlowPinType_Class::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +{ + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); +} + +#if WITH_EDITOR + +UObject* FFlowPinType_Vector::GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const +{ + static UObject* PinSubCategoryObject = TBaseStructure::Get(); + return PinSubCategoryObject; +} + +UObject* FFlowPinType_Rotator::GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const +{ + static UObject* PinSubCategoryObject = TBaseStructure::Get(); + return PinSubCategoryObject; +} + +UObject* FFlowPinType_Transform::GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const +{ + static UObject* PinSubCategoryObject = TBaseStructure::Get(); + return PinSubCategoryObject; +} + +UObject* FFlowPinType_GameplayTag::GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const +{ + static UObject* PinSubCategoryObject = TBaseStructure::Get(); + return PinSubCategoryObject; +} + +UObject* FFlowPinType_GameplayTagContainer::GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const +{ + static UObject* PinSubCategoryObject = TBaseStructure::Get(); + return PinSubCategoryObject; +} + +UObject* FFlowPinType_InstancedStruct::GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const +{ + static UObject* PinSubCategoryObject = TBaseStructure::Get(); + return PinSubCategoryObject; +} + +UObject* FFlowPinType_Enum::GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const +{ + UEnum* EnumClass = nullptr; + if (Wrapper && Wrapper->GetPinTypeName() == FFlowPinType_Enum::GetPinTypeNameStatic()) + { + const FFlowDataPinValue_Enum* EnumWrapper = static_cast(Wrapper); + TSoftObjectPtr EnumClassPtr = EnumWrapper->EnumClass; + EnumClass = EnumClassPtr.LoadSynchronous(); + } + else if (Property) + { + if (const FStructProperty* StructProperty = CastField(Property)) + { + const UStruct* ScriptStruct = FFlowDataPinValue_Enum::StaticStruct(); + if (StructProperty->Struct == ScriptStruct) + { + FFlowDataPinValue_Enum ValueStruct; + StructProperty->GetValue_InContainer(InContainer, &ValueStruct); + EnumClass = ValueStruct.EnumClass.LoadSynchronous(); + } + } + else if (const FEnumProperty* EnumProperty = CastField(Property)) + { + EnumClass = EnumProperty->GetEnum(); + } + else if (const FByteProperty* ByteProperty = CastField(Property)) + { + if (ByteProperty->Enum) + { + EnumClass = ByteProperty->Enum; + } + } + else if (const FArrayProperty* ArrayProperty = CastField(Property)) + { + if (const FEnumProperty* InnerEnumProperty = CastField(ArrayProperty->Inner)) + { + EnumClass = InnerEnumProperty->GetEnum(); + } + else if (const FByteProperty* InnerByteProperty = CastField(ArrayProperty->Inner)) + { + if (InnerByteProperty->Enum) + { + EnumClass = InnerByteProperty->Enum; + } + } + } + } + + return EnumClass; +} + +UClass* FFlowPinType_Object::TryGetObjectClassFromProperty(const FProperty& MetaDataProperty) +{ + if (UClass* MetaClass = FFlowPinType_Object::TryGetMetaClassFromProperty(MetaDataProperty)) + { + return MetaClass; + } + + // FSoftObjectPath can use the "AllowedClasses" to define what classes are allowed for the object. + // Using the "AllowedClasses" metadata tag, but we only support a single class, due to singular return value for this function. + const FString AllowedClassesString = MetaDataProperty.GetMetaData("AllowedClasses"); + const TArray AllowedClasses = FlowClassUtils::GetClassesFromMetadataString(AllowedClassesString); + + if (AllowedClasses.Num() > 1) + { + UE_LOG(LogFlow, Error, TEXT("Only a single AllowedClasses entry is allowed for flow data pin properties (multiple found: %s) for property %s"), *AllowedClassesString, *MetaDataProperty.GetName()); + + return nullptr; + } + + if (AllowedClasses.IsEmpty()) + { + return nullptr; + } + + if (UClass* AllowedClass = AllowedClasses[0]) + { + return AllowedClass; + } + else + { + UE_LOG(LogFlow, Error, TEXT("Could not resolve AllowedClasses '%s' for property %s"), *AllowedClassesString, *MetaDataProperty.GetName()); + } + + return nullptr; +} + +UClass* FFlowPinType_Object::TryGetMetaClassFromProperty(const FProperty& MetaDataProperty) +{ + const FString& MetaClassName = MetaDataProperty.GetMetaData("MetaClass"); + + if (!MetaClassName.IsEmpty()) + { + if (UClass* FoundClass = FEditorClassUtils::GetClassFromString(MetaClassName)) + { + return FoundClass; + } + else + { + UE_LOG(LogFlow, Error, TEXT("Could not resolve MetaClass named %s for property %s"), *MetaClassName, *MetaDataProperty.GetName()); + } + } + + return nullptr; +} + +UObject* FFlowPinType_Object::GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const +{ + UClass* Class = nullptr; + if (Wrapper && Wrapper->GetPinTypeName() == FFlowPinType_Object::GetPinTypeNameStatic()) + { + const FFlowDataPinValue_Object* ObjectWrapper = static_cast(Wrapper); + Class = ObjectWrapper->ClassFilter; + } + else if (Property) + { + if (const FStructProperty* StructProperty = CastField(Property)) + { + const UStruct* ScriptStruct = FFlowDataPinValue_Object::StaticStruct(); + static const UStruct* SoftObjectPathStruct = TBaseStructure::Get(); + if (StructProperty->Struct == ScriptStruct) + { + FFlowDataPinValue_Object ValueStruct; + StructProperty->GetValue_InContainer(InContainer, &ValueStruct); + Class = ValueStruct.ClassFilter; + } + else if (StructProperty->Struct == SoftObjectPathStruct) + { + Class = FFlowPinType_Object::TryGetObjectClassFromProperty(*StructProperty); + } + } + else if (const FObjectProperty* ObjectProperty = CastField(Property)) + { + Class = ObjectProperty->PropertyClass; + } + else if (const FSoftObjectProperty* SoftObjectProperty = CastField(Property)) + { + Class = SoftObjectProperty->PropertyClass; + } + else if (const FWeakObjectProperty* WeakObjectProperty = CastField(Property)) + { + Class = WeakObjectProperty->PropertyClass; + } + else if (const FLazyObjectProperty* LazyObjectProperty = CastField(Property)) + { + Class = LazyObjectProperty->PropertyClass; + } + else if (const FArrayProperty* ArrayProperty = CastField(Property)) + { + if (const FObjectProperty* InnerObjectProperty = CastField(ArrayProperty->Inner)) + { + Class = InnerObjectProperty->PropertyClass; + } + else if (const FSoftObjectProperty* InnerSoftObjectProperty = CastField(ArrayProperty->Inner)) + { + Class = InnerSoftObjectProperty->PropertyClass; + } + else if (const FWeakObjectProperty* InnerWeakObjectProperty = CastField(ArrayProperty->Inner)) + { + Class = InnerWeakObjectProperty->PropertyClass; + } + else if (const FLazyObjectProperty* InnerLazyObjectProperty = CastField(ArrayProperty->Inner)) + { + Class = InnerLazyObjectProperty->PropertyClass; + } + } + } + + return Class; +} + +UObject* FFlowPinType_Class::GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const +{ + UClass* Class = nullptr; + if (Wrapper && Wrapper->GetPinTypeName() == FFlowPinType_Class::GetPinTypeNameStatic()) + { + const FFlowDataPinValue_Class* ClassWrapper = static_cast(Wrapper); + Class = ClassWrapper->ClassFilter; + } + else if (Property) + { + if (const FStructProperty* StructProperty = CastField(Property)) + { + const UStruct* ScriptStruct = FFlowDataPinValue_Class::StaticStruct(); + static const UStruct* SoftClassPathStruct = TBaseStructure::Get(); + if (StructProperty->Struct == ScriptStruct) + { + FFlowDataPinValue_Class ValueStruct; + StructProperty->GetValue_InContainer(InContainer, &ValueStruct); + Class = ValueStruct.ClassFilter; + } + else if (StructProperty->Struct == SoftClassPathStruct) + { + Class = FFlowPinType_Object::TryGetMetaClassFromProperty(*StructProperty); + } + } + else if (const FClassProperty* ClassProperty = CastField(Property)) + { + Class = ClassProperty->MetaClass; + } + else if (const FSoftClassProperty* SoftClassProperty = CastField(Property)) + { + Class = SoftClassProperty->MetaClass; + } + else if (const FArrayProperty* ArrayProperty = CastField(Property)) + { + if (const FClassProperty* InnerClassProperty = CastField(ArrayProperty->Inner)) + { + Class = InnerClassProperty->MetaClass; + } + else if (const FSoftClassProperty* InnerSoftClassProperty = CastField(ArrayProperty->Inner)) + { + Class = InnerSoftClassProperty->MetaClass; + } + } + } + + return Class; +} + +bool FFlowPinType_Exec::ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const +{ + OutValue = FFormatArgumentValue(FText::FromString(TEXT("Exec"))); + return true; +} + +bool FFlowPinType_Bool::ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const +{ + return FlowPinType::ResolveAndFormatArray(Node, PinName, OutValue, [](const bool& Value) { return Value ? TEXT("true") : TEXT("false"); }); +} + +bool FFlowPinType_Int::ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const +{ + return FlowPinType::ResolveAndFormatArray(Node, PinName, OutValue, [](const int32& Value) { return FString::FromInt(Value); }); +} + +bool FFlowPinType_Int64::ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const +{ + return FlowPinType::ResolveAndFormatArray(Node, PinName, OutValue, [](const int64& Value) { return FString::Printf(TEXT("%lld"), Value); }); +} + +bool FFlowPinType_Float::ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const +{ + return FlowPinType::ResolveAndFormatArray(Node, PinName, OutValue, [](const float& Value) { return FString::SanitizeFloat(Value); }); +} + +bool FFlowPinType_Double::ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const +{ + return FlowPinType::ResolveAndFormatArray(Node, PinName, OutValue, [](const double& Value) { return FString::SanitizeFloat(Value); }); +} + +bool FFlowPinType_Enum::ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const +{ + return FlowPinType::ResolveAndFormatArray(Node, PinName, OutValue, [](const FName& Value) { return Value.ToString(); }); +} + +bool FFlowPinType_Name::ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const +{ + return FlowPinType::ResolveAndFormatArray(Node, PinName, OutValue, [](const FName& Value) { return Value.ToString(); }); +} + +bool FFlowPinType_String::ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const +{ + return FlowPinType::ResolveAndFormatArray(Node, PinName, OutValue, [](const FString& Value) { return Value; }); +} + +bool FFlowPinType_Text::ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const +{ + return FlowPinType::ResolveAndFormatArray(Node, PinName, OutValue, [](const FText& Value) { return Value.ToString(); }); +} + +bool FFlowPinType_Vector::ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const +{ + return FlowPinType::ResolveAndFormatArray(Node, PinName, OutValue, [](const FVector& Value) { return Value.ToString(); }); +} + +bool FFlowPinType_Rotator::ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const +{ + return FlowPinType::ResolveAndFormatArray(Node, PinName, OutValue, [](const FRotator& Value) { return Value.ToString(); }); +} + +bool FFlowPinType_Transform::ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const +{ + return FlowPinType::ResolveAndFormatArray(Node, PinName, OutValue, [](const FTransform& Value) { return Value.ToString(); }); +} + +bool FFlowPinType_GameplayTag::ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const +{ + return FlowPinType::ResolveAndFormatArray(Node, PinName, OutValue, [](const FGameplayTag& Value) { return Value.ToString(); }); +} + +bool FFlowPinType_GameplayTagContainer::ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const +{ + return FlowPinType::ResolveAndFormatArray(Node, PinName, OutValue, [](const FGameplayTagContainer& Value) { return Value.ToString(); }); +} + +bool FFlowPinType_InstancedStruct::ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const +{ + return FlowPinType::ResolveAndFormatArray(Node, PinName, OutValue, [](const FInstancedStruct& Value) { return Value.GetScriptStruct() ? Value.GetScriptStruct()->GetName() : TEXT("None"); }); +} + +bool FFlowPinType_Object::ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const +{ + return FlowPinType::ResolveAndFormatArray(Node, PinName, OutValue, [](const UObject* Value) { return Value ? Value->GetName() : TEXT("None"); }); +} + +bool FFlowPinType_Class::ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const +{ + return FlowPinType::ResolveAndFormatArray(Node, PinName, OutValue, [](const UClass* Value) { return Value ? Value->GetName() : TEXT("None"); }); +} +#endif \ No newline at end of file diff --git a/Source/Flow/Public/Asset/FlowAssetParams.h b/Source/Flow/Public/Asset/FlowAssetParams.h new file mode 100644 index 000000000..43b9f23eb --- /dev/null +++ b/Source/Flow/Public/Asset/FlowAssetParams.h @@ -0,0 +1,111 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Engine/DataAsset.h" +#include "Types/FlowNamedDataPinProperty.h" +#include "Interfaces/FlowDataPinValueOwnerInterface.h" +#include "Interfaces/FlowDataPinValueSupplierInterface.h" +#include "Interfaces/FlowAssetProviderInterface.h" +#include "Asset/FlowAssetParamsTypes.h" + +#include "FlowAssetParams.generated.h" + +class UFlowAsset; + +/** +* Data asset for storing Flow Graph Start node parameters, supporting external configuration. +* This is considered experimental at the moment. +*/ +UCLASS(BlueprintType) +class FLOW_API UFlowAssetParams + : public UDataAsset + , public IFlowAssetProviderInterface + , public IFlowDataPinValueOwnerInterface + , public IFlowDataPinValueSupplierInterface +{ + GENERATED_BODY() + +public: +#if WITH_EDITORONLY_DATA + // Reference to the associated Flow Asset. + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = FlowAssetParams) + TSoftObjectPtr OwnerFlowAsset; + + // Reference to the "Parent" params object to inherit from (if any). + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = FlowAssetParams) + FFlowAssetParamsPtr ParentParams; + + // Array of properties synchronized with the Start node (local adds/overrides; effective flattened via ReconcilePropertiesWithParentParams). + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = FlowAssetParams, meta = (EditFixedSize)) + TArray Properties; +#endif + + UPROPERTY() + TMap> PropertyMap; + +public: + // UObject interface +#if WITH_EDITOR + virtual void PostLoad() override; + virtual void PreSaveRoot(FObjectPreSaveRootContext ObjectSaveContext) override; +#endif + virtual void Serialize(FArchive& Ar) override; + // -- + + // IFlowDataPinValueSupplierInterface + virtual bool CanSupplyDataPinValues_Implementation() const override; + virtual FFlowDataPinResult TrySupplyDataPin_Implementation(FName PinName) const override; + // -- + + // IFlowAssetProviderInterface + virtual UFlowAsset* ProvideFlowAsset() const override; + // -- + +#if WITH_EDITOR + // UObject interface + virtual EDataValidationResult IsDataValid(FDataValidationContext& Context) const override; + // -- + + // Generates properties from the associated Start node or updates Start node from params. + EFlowReconcilePropertiesResult ReconcilePropertiesWithStartNode( + const FDateTime& FlowAssetLastSaveTimeStamp, + const TSoftObjectPtr& InOwnerFlowAsset, + TArray& MutablePropertiesFromStartNode); + + void ConfigureFlowAssetParams(TSoftObjectPtr OwnerAsset, TSoftObjectPtr InParentParams, const TArray& InProperties); + + // IFlowDataPinValueOwnerInterface + virtual bool CanModifyFlowDataPinType() const override; + virtual bool ShowFlowDataPinValueInputPinCheckbox() const override; + virtual bool ShowFlowDataPinValueClassFilter(const FFlowDataPinValue* Value) const override; + virtual bool CanEditFlowDataPinValueClassFilter(const FFlowDataPinValue* Value) const override; + virtual void SetFlowDataPinValuesRebuildDelegate(FSimpleDelegate InDelegate) override + { + FlowDataPinValuesRebuildDelegate = InDelegate; + } + + virtual void RequestFlowDataPinValuesDetailsRebuild() override + { + if (FlowDataPinValuesRebuildDelegate.IsBound()) + { + FlowDataPinValuesRebuildDelegate.Execute(); + } + } + +private: + FSimpleDelegate FlowDataPinValuesRebuildDelegate; + // -- + +protected: + + // Updates properties from ParentParams, handling inheritance and name enforcement. + EFlowReconcilePropertiesResult ReconcilePropertiesWithParentParams(); + + EFlowReconcilePropertiesResult CheckForParentCycle() const; + + void ModifyAndRebuildPropertiesMap(); + + void RebuildPropertiesMap(); +#endif +}; \ No newline at end of file diff --git a/Source/Flow/Public/Asset/FlowAssetParamsTypes.h b/Source/Flow/Public/Asset/FlowAssetParamsTypes.h new file mode 100644 index 000000000..95e50c7e0 --- /dev/null +++ b/Source/Flow/Public/Asset/FlowAssetParamsTypes.h @@ -0,0 +1,67 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Types/FlowEnumUtils.h" +#include "UObject/SoftObjectPtr.h" + +#include "FlowAssetParamsTypes.generated.h" + +class UFlowAssetParams; + +// Result of reconciling Flow Asset Params with Start node or SuperParams. +UENUM(BlueprintType) +enum class EFlowReconcilePropertiesResult : uint8 +{ + NoChanges, + + ParamsPropertiesUpdated, + AssetPropertyValuesUpdated, + + Error_InvalidAsset, + Error_PropertyCountMismatch, + Error_PropertyTypeMismatch, + Error_CyclicInheritance, + Error_UnloadableParent, + + Max UMETA(Hidden), + Invalid UMETA(Hidden), + Min = 0 UMETA(Hidden), + + SuccessFirst = NoChanges UMETA(Hidden), + SuccessLast = AssetPropertyValuesUpdated UMETA(Hidden), + + ModifiedFirst = ParamsPropertiesUpdated UMETA(Hidden), + ModifiedLast = AssetPropertyValuesUpdated UMETA(Hidden), + + ErrorFirst = Error_InvalidAsset UMETA(Hidden), + ErrorLast = Error_UnloadableParent UMETA(Hidden), +}; +FLOW_ENUM_RANGE_VALUES(EFlowReconcilePropertiesResult) + +namespace EFlowReconcilePropertiesResult_Classifiers +{ + FORCEINLINE bool IsSuccessResult(EFlowReconcilePropertiesResult Result) { return FLOW_IS_ENUM_IN_SUBRANGE(Result, EFlowReconcilePropertiesResult::Success); } + FORCEINLINE bool IsModifiedResult(EFlowReconcilePropertiesResult Result) { return FLOW_IS_ENUM_IN_SUBRANGE(Result, EFlowReconcilePropertiesResult::Modified); } + FORCEINLINE bool IsErrorResult(EFlowReconcilePropertiesResult Result) { return FLOW_IS_ENUM_IN_SUBRANGE(Result, EFlowReconcilePropertiesResult::Error); } +} + +// Wrapper for TSoftObjectPtr to enable editor customization. +// +// Supported metadata tags: +// - ShowCreateNew - Should we show the "Create New" button? +// - HideChildParams - When showing a chooser, should we hide "Child" params or not? (Child params have a non-null ParentParams) +USTRUCT(BlueprintType) +struct FLOW_API FFlowAssetParamsPtr +{ + GENERATED_BODY() + + FFlowAssetParamsPtr() = default; + FFlowAssetParamsPtr(TSoftObjectPtr InAssetParamsPtr) : AssetPtr(InAssetParamsPtr) { } + + UFlowAssetParams* ResolveFlowAssetParams() const; + + // Reference to the Flow Asset Params. + UPROPERTY(EditAnywhere, Category = FlowAssetParams, meta = (EditAssetInline)) + TSoftObjectPtr AssetPtr; +}; diff --git a/Source/Flow/Public/Asset/FlowAssetParamsUtils.h b/Source/Flow/Public/Asset/FlowAssetParamsUtils.h new file mode 100644 index 000000000..9c1f966a1 --- /dev/null +++ b/Source/Flow/Public/Asset/FlowAssetParamsUtils.h @@ -0,0 +1,44 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Misc/DateTime.h" +#include "Asset/FlowAssetParamsTypes.h" + +#include "FlowAssetParamsUtils.generated.h" + +class UObject; +struct FFlowNamedDataPinProperty; + +/** +* Utility functions for Flow Asset Params reconciliation and validation. +*/ +USTRUCT() +struct FLOW_API FFlowAssetParamsUtils +{ + GENERATED_BODY() + +#if WITH_EDITOR + static FDateTime GetLastSavedTimestampForObject(const UObject* Object); + + static EFlowReconcilePropertiesResult CheckPropertiesMatch( + const TArray& PropertiesA, + const TArray& PropertiesB); + + static const FFlowNamedDataPinProperty* FindPropertyByGuid( + const TArray& Props, + const FGuid& Guid); + + static FFlowNamedDataPinProperty* FindPropertyByGuid( + TArray& Props, + const FGuid& Guid); + + static bool ArePropertyArraysEqual( + const TArray& A, + const TArray& B); + + static bool ArePropertiesEqual( + const FFlowNamedDataPinProperty& A, + const FFlowNamedDataPinProperty& B); +#endif +}; \ No newline at end of file diff --git a/Source/Flow/Public/Asset/FlowPinTypeMatchPolicy.h b/Source/Flow/Public/Asset/FlowPinTypeMatchPolicy.h new file mode 100644 index 000000000..ee2d8a32c --- /dev/null +++ b/Source/Flow/Public/Asset/FlowPinTypeMatchPolicy.h @@ -0,0 +1,43 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "FlowPinTypeMatchPolicy.generated.h" + +UENUM(meta = (BitFlags)) +enum class EFlowPinTypeMatchRules : uint32 +{ + None = 0, + + RequirePinCategoryMatch = 1 << 0, + RequirePinCategoryMemberReferenceMatch = 1 << 1, + RequireContainerTypeMatch = 1 << 2, + RequirePinSubCategoryObjectMatch = 1 << 3, + AllowSubCategoryObjectSubclasses = 1 << 4, + AllowSubCategoryObjectSameLayout = 1 << 5, + SameLayoutMustMatchPropertyNames = 1 << 6, + + // Masks for convenience + StandardPinTypeMatchRulesMask = + RequirePinCategoryMatch | + RequirePinCategoryMemberReferenceMatch | + AllowSubCategoryObjectSubclasses | + AllowSubCategoryObjectSameLayout UMETA(Hidden), + + SubCategoryObjectPinTypeMatchRulesMask = + StandardPinTypeMatchRulesMask | + RequirePinSubCategoryObjectMatch UMETA(Hidden), +}; + +USTRUCT() +struct FFlowPinTypeMatchPolicy +{ + GENERATED_BODY() + + UPROPERTY() + EFlowPinTypeMatchRules PinTypeMatchRules = EFlowPinTypeMatchRules::StandardPinTypeMatchRulesMask; + + // Pin categories to allow beyond an exact match + UPROPERTY() + TSet PinCategories; +}; diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 38b9e206d..d562b8d8b 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -4,6 +4,7 @@ #include "FlowSave.h" #include "FlowTypes.h" +#include "Asset/FlowAssetParamsTypes.h" #include "Nodes/FlowNode.h" #if WITH_EDITOR @@ -21,42 +22,13 @@ class UFlowSubsystem; class UEdGraph; class UEdGraphNode; class UFlowAsset; +class UFlowAssetParams; #if !UE_BUILD_SHIPPING DECLARE_DELEGATE(FFlowGraphEvent); DECLARE_DELEGATE_TwoParams(FFlowSignalEvent, const FGuid& /*NodeGuid*/, const FName& /*PinName*/); #endif -// Working Data struct for the Harvest Data Pins operation -// (passed between functions involved in the harvesting operation to simplify the function signatures) -struct FFlowHarvestDataPinsWorkingData -{ - FFlowHarvestDataPinsWorkingData(UFlowNode& InFlowNode, const TMap& PinNameMapPrev, const TArray& InputPinsPrev, const TArray& OutputPinsPrev) - : FlowNode(&InFlowNode) - , PinNameToBoundPropertyNameMapPrev(PinNameMapPrev) - , AutoInputDataPinsPrev(InputPinsPrev) - , AutoOutputDataPinsPrev(OutputPinsPrev) - { } - -#if WITH_EDITOR - bool DidPinNameToBoundPropertyNameMapChange() const; - bool DidAutoInputDataPinsChange() const; - bool DidAutoOutputDataPinsChange() const; -#endif - - UFlowNode* FlowNode = nullptr; - - const TMap& PinNameToBoundPropertyNameMapPrev; - const TArray& AutoInputDataPinsPrev; - const TArray& AutoOutputDataPinsPrev; - - TMap PinNameToBoundPropertyNameMapNext; - TArray AutoInputDataPinsNext; - TArray AutoOutputDataPinsNext; - - bool bPinNameMapChanged = false; -}; - /** * Single asset containing flow nodes. */ @@ -119,6 +91,8 @@ class FLOW_API UFlowAsset : public UObject // Returns whether the node class is allowed in this flow asset bool IsNodeOrAddOnClassAllowed(const UClass* FlowNodeClass, FText* OutOptionalFailureReason = nullptr) const; + virtual TSubclassOf GetDefaultFlowAssetForSubgraphs() const { return GetClass(); } + protected: bool CanFlowNodeClassBeUsedByFlowAsset(const UClass& FlowNodeClass) const; bool CanFlowAssetUseFlowNodeClass(const UClass& FlowNodeClass) const; @@ -154,7 +128,7 @@ class FLOW_API UFlowAsset : public UObject TArray CustomInputs; /** - * Custom Outputs define custom graph outputs, this allow to send signals to the parent graph while executing this graph + * Custom Outputs define custom graph outputs, this allows to send signals to the parent graph while executing this graph * Sub Graph node using this Flow Asset will generate context Output Pin for every valid Event name on this list */ UPROPERTY(EditAnywhere, Category = "Sub Graph") @@ -173,24 +147,10 @@ class FLOW_API UFlowAsset : public UObject // Processes nodes and updates pin connections from the graph to the UFlowNode (processes all nodes in the graph if passed nullptr) void HarvestNodeConnections(UFlowNode* TargetNode = nullptr); - // Updates the auto-generated pins and bindings for a given FlowNode, - // returns true if any changes were made. - bool TryUpdateManagedFlowPinsForNode(UFlowNode& FlowNode); + static bool TryGetDefaultForInputPinName(const FStructProperty& StructProperty, const void* Container, FString& OutString); -protected: - void AddDataPinPropertyBindingToMap( - const FName& PinAuthoredName, - const FName& PropertyAuthoredName, - FFlowHarvestDataPinsWorkingData& InOutData); - virtual bool TryCreateFlowDataPinFromMetadataValue( - const FString& MetadataValue, - UFlowNode& FlowNode, - const FProperty& Property, - const FText& PinDisplayName, - const bool bIsInputPin, - TArray* InOutDataPinsNext) const; - - void HarvestFlowPinMetadataForProperty(const FProperty* Property, FFlowHarvestDataPinsWorkingData& InOutData); + // Updates the auto-generated pins and bindings for a given FlowNode, returns true if any changes were made. + static bool TryUpdateManagedFlowPinsForNode(UFlowNode& FlowNode); #endif public: @@ -209,6 +169,8 @@ class FLOW_API UFlowAsset : public UObject return nullptr; } + + TArray GetAllNodes() const; UFUNCTION(BlueprintPure, Category = "FlowAsset") virtual UFlowNode* GetDefaultEntryNode() const; @@ -216,6 +178,9 @@ class FLOW_API UFlowAsset : public UObject // Gathers all of the nodes that are connected to the Start & Custom Inputs of the flow graph TArray GatherNodesConnectedToAllInputs() const; + // Return all other Pins connected to the passed Pin. + TArray GatherPinsConnectedToPin(const FConnectedPin& Pin) const; + UFUNCTION(BlueprintPure, Category = "FlowAsset", meta = (DeterminesOutputType = "FlowNodeClass")) TArray GetNodesInExecutionOrder(UFlowNode* FirstIteratedNode, const TSubclassOf FlowNodeClass); @@ -392,9 +357,10 @@ class FLOW_API UFlowAsset : public UObject void TriggerCustomInput_FromSubGraph(UFlowNode_SubGraph* Node, const FName& EventName) const; void TriggerCustomOutput(const FName& EventName); - void TriggerInput(const FGuid& NodeGuid, const FName& PinName); + // TODO: Extend FromPin through to Node level Trigger functions + virtual void TriggerInput(const FGuid& NodeGuid, const FName& PinName, const FConnectedPin& FromPin); - void FinishNode(UFlowNode* Node); + virtual void FinishNode(UFlowNode* Node); void ResetNodes(); #if !UE_BUILD_SHIPPING @@ -431,7 +397,7 @@ class FLOW_API UFlowAsset : public UObject // Expects to be owned (at runtime) by an object with this class (or one of its subclasses) // NOTE - If the class is an AActor, and the flow asset is owned by a component, // it will consider the component's owner for the AActor - UPROPERTY(EditAnywhere, Category = "Flow", meta = (MustImplement = "/Script/Flow.FlowOwnerInterface")) + UPROPERTY(EditAnywhere, Category = "Flow") TSubclassOf ExpectedOwnerClass; ////////////////////////////////////////////////////////////////////////// @@ -457,6 +423,28 @@ class FLOW_API UFlowAsset : public UObject UFUNCTION(BlueprintNativeEvent, Category = "SaveGame") bool IsBoundToWorld(); +////////////////////////////////////////////////////////////////////////// +// FlowAssetParams support (Start node params for a flow graph) + + // Default parameters asset for this Flow Asset (optional) + UPROPERTY(EditAnywhere, Category = FlowAssetParams, meta = (ShowCreateNew, HideChildParams)) + FFlowAssetParamsPtr BaseAssetParams; + +#if WITH_EDITOR + // Called before saving the asset. + virtual void PreSaveRoot(FObjectPreSaveRootContext ObjectSaveContext) override; + + // Generates a new params asset from the Start node. + UFlowAssetParams* GenerateParamsFromStartNode(); + + // Generates the FlowAssetParams name for the 'base' (root) asset, used when creating the params asset + virtual FString GenerateParamsAssetName() const; + +protected: + + void ReconcileBaseAssetParams(const FDateTime& AssetLastSavedTimestamp); +#endif + ////////////////////////////////////////////////////////////////////////// // Utils diff --git a/Source/Flow/Public/FlowComponent.h b/Source/Flow/Public/FlowComponent.h index d579e2568..333779911 100644 --- a/Source/Flow/Public/FlowComponent.h +++ b/Source/Flow/Public/FlowComponent.h @@ -7,7 +7,8 @@ #include "FlowSave.h" #include "FlowTypes.h" -#include "Interfaces/FlowOwnerInterface.h" +#include "Interfaces/FlowAssetProviderInterface.h" +#include "Asset/FlowAssetParamsTypes.h" #include "FlowComponent.generated.h" class UFlowAsset; @@ -42,7 +43,7 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FFlowComponentDynamicNotify, class * Base component of Flow System - makes possible to communicate between Actor, Flow Subsystem and Flow Graphs */ UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent)) -class FLOW_API UFlowComponent : public UActorComponent, public IFlowOwnerInterface +class FLOW_API UFlowComponent : public UActorComponent, public IFlowAssetProviderInterface { GENERATED_UCLASS_BODY() @@ -167,6 +168,10 @@ class FLOW_API UFlowComponent : public UActorComponent, public IFlowOwnerInterfa UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "RootFlow") TObjectPtr RootFlow; + // Flow Asset Params to use as the data pin value supplier for the Root Flow + UPROPERTY(EditAnywhere, Category = "RootFlow") + FFlowAssetParamsPtr RootFlowParams; + // If true, component will start Root Flow on Begin Play UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "RootFlow") bool bAutoStartRootFlow; @@ -197,6 +202,10 @@ class FLOW_API UFlowComponent : public UActorComponent, public IFlowOwnerInterfa UFUNCTION(BlueprintPure, Category = "RootFlow", meta = (DeprecatedFunction, DeprecationMessage="Use GetRootInstances() instead.")) UFlowAsset* GetRootFlowInstance() const; + // IFlowAssetProviderInterface + virtual UFlowAsset* ProvideFlowAsset() const override { return RootFlow; } + // -- + ////////////////////////////////////////////////////////////////////////// // Custom Input and Output events @@ -253,4 +262,4 @@ class FLOW_API UFlowComponent : public UActorComponent, public IFlowOwnerInterfa public: UFlowSubsystem* GetFlowSubsystem() const; bool IsFlowNetMode(const EFlowNetMode NetMode) const; -}; +}; \ No newline at end of file diff --git a/Source/Flow/Public/FlowExecutableActorComponent.h b/Source/Flow/Public/FlowExecutableActorComponent.h new file mode 100644 index 000000000..960820b36 --- /dev/null +++ b/Source/Flow/Public/FlowExecutableActorComponent.h @@ -0,0 +1,61 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Components/ActorComponent.h" +#include "Interfaces/FlowContextPinSupplierInterface.h" +#include "Interfaces/FlowCoreExecutableInterface.h" +#include "Interfaces/FlowDataPinValueOwnerInterface.h" +#include "Interfaces/FlowExternalExecutableInterface.h" + +#include "FlowExecutableActorComponent.generated.h" + +/** + * A base class for blueprint components that are expected to be executed from an ExecuteComponent flow node. + * Provides the support for FFlowDataPinValue subclasses, so that blueprint components (that derive from this) + * can have their pins be automatically discovered and supplied. + */ +UCLASS(Abstract, Blueprintable, EditInlineNew, DisplayName = "Flow Executable Actor Component", hidecategories = (Tags, Activation, Cooking, AssetUserData, Navigation)) +class FLOW_API UFlowExecutableActorComponent + : public UActorComponent + , public IFlowContextPinSupplierInterface + , public IFlowCoreExecutableInterface + , public IFlowDataPinValueOwnerInterface + , public IFlowExternalExecutableInterface +{ + GENERATED_BODY() + +private: + FSimpleDelegate FlowDataPinValuesRebuildDelegate; + +protected: + // FlowNodeBase that will execute this component in the FlowGraph on our behalf + UPROPERTY(Transient, BlueprintReadOnly, Category = DataPins) + TObjectPtr FlowNodeProxy; + +public: + + // IFlowContextPinSupplierInterface + virtual bool K2_SupportsContextPins_Implementation() const override { return true; } + // -- + +#if WITH_EDITOR + + // IFlowDataPinValueOwnerInterface + virtual bool CanModifyFlowDataPinType() const override; + virtual bool ShowFlowDataPinValueInputPinCheckbox() const override; + virtual bool ShowFlowDataPinValueClassFilter(const FFlowDataPinValue* Value) const override; + virtual bool CanEditFlowDataPinValueClassFilter(const FFlowDataPinValue* Value) const override; + virtual void SetFlowDataPinValuesRebuildDelegate(FSimpleDelegate InDelegate) override; + virtual void RequestFlowDataPinValuesDetailsRebuild() override; + // -- +#endif //WITH_EDITOR + + // IFlowExternalExecutableInterface + virtual void PreActivateExternalFlowExecutable(UFlowNodeBase& FlowNodeBase) override; + // -- + +protected: + + FORCEINLINE bool IsDefaultObject() const { return HasAnyFlags(RF_ClassDefaultObject); } +}; \ No newline at end of file diff --git a/Source/Flow/Public/FlowPinSubsystem.h b/Source/Flow/Public/FlowPinSubsystem.h new file mode 100644 index 000000000..66fff9a38 --- /dev/null +++ b/Source/Flow/Public/FlowPinSubsystem.h @@ -0,0 +1,63 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Subsystems/EngineSubsystem.h" +#include "Types/FlowPinType.h" +#include "StructUtils/InstancedStruct.h" +#include "Templates/UnrealTypeTraits.h" + +#include "FlowPinSubsystem.generated.h" + +struct FFlowPinTypeName; + +UCLASS(MinimalApi) +class UFlowPinSubsystem : public UEngineSubsystem +{ + GENERATED_BODY() + +protected: + UPROPERTY(Transient) + TMap> PinTypes; + +public: + FLOW_API static UFlowPinSubsystem* Get(); + + virtual bool ShouldCreateSubsystem(UObject* Outer) const override; + virtual void Initialize(FSubsystemCollectionBase& Collection) override; + virtual void Deinitialize() override; + + template + void RegisterPinType() + { + TInstancedStruct PinType; + PinType.InitializeAs(); + RegisterPinType(TPinType::GetPinTypeNameStatic(), PinType); + } + FLOW_API void RegisterPinType(const FFlowPinTypeName& TypeName, const TInstancedStruct& PinType); + + template + void UnregisterPinType() + { + UnregisterPinType(TPinType::GetPinTypeNameStatic()); + } + FLOW_API void UnregisterPinType(const FFlowPinTypeName& TypeName); + + template + const TPinType* FindPinType(const FFlowPinTypeName& TypeName) const + { + static_assert(TIsDerivedFrom::IsDerived, "TPinType must be derived from FFlowPinType"); + + if (const TInstancedStruct* Found = PinTypes.Find(TypeName)) + { + return Found->GetPtr(); + } + + return nullptr; + } + + FLOW_API TArray GetPinTypeNames() const; + +protected: + void UnregisterAllPinTypes(); +}; \ No newline at end of file diff --git a/Source/Flow/Public/FlowSubsystem.h b/Source/Flow/Public/FlowSubsystem.h index 4852cb519..16bd02571 100644 --- a/Source/Flow/Public/FlowSubsystem.h +++ b/Source/Flow/Public/FlowSubsystem.h @@ -11,6 +11,7 @@ class UFlowAsset; class UFlowNode_SubGraph; +class IFlowDataPinValueSupplierInterface; DECLARE_DYNAMIC_MULTICAST_DELEGATE(FSimpleFlowEvent); DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSimpleFlowComponentEvent, UFlowComponent*, Component); @@ -73,7 +74,7 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem /* Start the root Flow, graph that will eventually instantiate next Flow Graphs through the SubGraph node */ UFUNCTION(BlueprintCallable, Category = "FlowSubsystem", meta = (DefaultToSelf = "Owner")) - virtual void StartRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const bool bAllowMultipleInstances = true); + virtual void StartRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const TScriptInterface DataPinValueSupplier, const bool bAllowMultipleInstances = true); virtual UFlowAsset* CreateRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const bool bAllowMultipleInstances = true, const FString& NewInstanceName = FString()); @@ -431,4 +432,4 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem private: void FindComponents(const FGameplayTag& Tag, const bool bExactMatch, TArray>& OutComponents) const; void FindComponents(const FGameplayTagContainer& Tags, const EGameplayContainerMatchType MatchType, const bool bExactMatch, TSet>& OutComponents) const; -}; +}; \ No newline at end of file diff --git a/Source/Flow/Public/Interfaces/FlowAssetProviderInterface.h b/Source/Flow/Public/Interfaces/FlowAssetProviderInterface.h new file mode 100644 index 000000000..29d9bc95a --- /dev/null +++ b/Source/Flow/Public/Interfaces/FlowAssetProviderInterface.h @@ -0,0 +1,29 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "UObject/Interface.h" + +#include "FlowAssetProviderInterface.generated.h" + +class UFlowAsset; + +// Interface to define a UFlowAsset provider. +// This is used for filtering in FFlowAssetParamsPtrCustomization +UINTERFACE(MinimalAPI, Blueprintable, DisplayName = "Flow Asset Provider Interface") +class UFlowAssetProviderInterface : public UInterface +{ + GENERATED_BODY() +}; + +class FLOW_API IFlowAssetProviderInterface +{ + GENERATED_BODY() + +public: + + // Provide a FlowAsset for use in FFlowAssetParamsPtr resolution + UFUNCTION(BlueprintImplementableEvent, Category = FlowAssetParams, DisplayName = "ProvideFlowAsset") + UFlowAsset* K2_ProvideFlowAsset() const; + virtual UFlowAsset* ProvideFlowAsset() const; +}; diff --git a/Source/Flow/Public/Interfaces/FlowContextPinSupplierInterface.h b/Source/Flow/Public/Interfaces/FlowContextPinSupplierInterface.h index 6e14ed37d..ad2f59a29 100644 --- a/Source/Flow/Public/Interfaces/FlowContextPinSupplierInterface.h +++ b/Source/Flow/Public/Interfaces/FlowContextPinSupplierInterface.h @@ -30,7 +30,10 @@ class FLOW_API IFlowContextPinSupplierInterface UFUNCTION(BlueprintNativeEvent, Category = "FlowNode In-Editor Functions", DisplayName = "SupportsContextPins", meta = (DevelopmentOnly)) bool K2_SupportsContextPins() const; virtual bool K2_SupportsContextPins_Implementation() const; + #if WITH_EDITOR + // Note: This method can only be called by native implementors of the interface, so we still have to manually handle and check + // classes that only implement the interface in Blueprint. virtual bool SupportsContextPins() const { return Execute_K2_SupportsContextPins(Cast(this)); } #endif // WITH_EDITOR diff --git a/Source/Flow/Public/Interfaces/FlowDataPinGeneratorInterface.h b/Source/Flow/Public/Interfaces/FlowDataPinGeneratorInterface.h new file mode 100644 index 000000000..fea685c08 --- /dev/null +++ b/Source/Flow/Public/Interfaces/FlowDataPinGeneratorInterface.h @@ -0,0 +1,25 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "UObject/Interface.h" +#include "FlowDataPinGeneratorInterface.generated.h" + +struct FFlowAutoDataPinsWorkingData; + +// Interface for Classes that can auto-generate DataPins +UINTERFACE(MinimalAPI, NotBlueprintable, DisplayName = "Flow Data Pin Generator Interface", meta = (CannotImplementInterfaceInBlueprint)) +class UFlowDataPinGeneratorInterface : public UInterface +{ + GENERATED_BODY() +}; + +class FLOW_API IFlowDataPinGeneratorInterface +{ + GENERATED_BODY() + +public: +#if WITH_EDITOR + virtual void AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const = 0; +#endif +}; diff --git a/Source/Flow/Public/Interfaces/FlowDataPinGeneratorNodeInterface.h b/Source/Flow/Public/Interfaces/FlowDataPinGeneratorNodeInterface.h deleted file mode 100644 index 2ebe5f3a5..000000000 --- a/Source/Flow/Public/Interfaces/FlowDataPinGeneratorNodeInterface.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#pragma once - -#include "UObject/Interface.h" -#include "Nodes/FlowPin.h" - -#include "FlowDataPinGeneratorNodeInterface.generated.h" - -// Interface for special UFlowNodes that can auto-generate pins directly -UINTERFACE(MinimalAPI, NotBlueprintable, DisplayName = "Flow Data Pin Generator Node Interface") -class UFlowDataPinGeneratorNodeInterface : public UInterface -{ - GENERATED_BODY() -}; - -class FLOW_API IFlowDataPinGeneratorNodeInterface -{ - GENERATED_BODY() - -public: -#if WITH_EDITOR - virtual void AutoGenerateDataPins(TMap& PinNameToBoundPropertyMap, TArray& InputDataPins, TArray& OutputDataPins) const = 0; -#endif -}; diff --git a/Source/Flow/Public/Interfaces/FlowDataPinPropertyProviderInterface.h b/Source/Flow/Public/Interfaces/FlowDataPinPropertyProviderInterface.h index e91924248..21b964bcf 100644 --- a/Source/Flow/Public/Interfaces/FlowDataPinPropertyProviderInterface.h +++ b/Source/Flow/Public/Interfaces/FlowDataPinPropertyProviderInterface.h @@ -6,9 +6,9 @@ #include "UObject/Interface.h" #include "FlowDataPinPropertyProviderInterface.generated.h" -struct FFlowDataPinProperty; +struct FFlowDataPinValue; -// Interface to define a FFlowDataPinProperty provider. +// Interface to define a FFlowDataPinValue provider. // This is used in plumbing data in the AI Flow extension plugin into the Flow Data Pins framework. UINTERFACE(MinimalAPI, NotBlueprintable) class UFlowDataPinPropertyProviderInterface : public UInterface @@ -22,6 +22,6 @@ class FLOW_API IFlowDataPinPropertyProviderInterface public: - // Provide a FFlowDataPinProperty (instancedStruct) for the creation of data pins and supplying their values. - virtual bool TryProvideFlowDataPinProperty(const bool bIsInputPin, TInstancedStruct& OutFlowDataPinProperty) const = 0; + // Provide a FFlowDataPinValue (instancedStruct) for the creation of data pins and supplying their values. + virtual bool TryProvideFlowDataPinProperty(TInstancedStruct& OutFlowDataPinProperty) const = 0; }; diff --git a/Source/Flow/Public/Interfaces/FlowDataPinValueOwnerInterface.h b/Source/Flow/Public/Interfaces/FlowDataPinValueOwnerInterface.h new file mode 100644 index 000000000..3ae3136cc --- /dev/null +++ b/Source/Flow/Public/Interfaces/FlowDataPinValueOwnerInterface.h @@ -0,0 +1,43 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "UObject/Interface.h" +#include "Delegates/Delegate.h" + +#include "FlowDataPinValueOwnerInterface.generated.h" + +struct FFlowDataPinValue; + +UINTERFACE(NotBlueprintable) +class FLOW_API UFlowDataPinValueOwnerInterface : public UInterface +{ + GENERATED_BODY() +}; + +class FLOW_API IFlowDataPinValueOwnerInterface +{ + GENERATED_BODY() + +public: +#if WITH_EDITOR + + // Determines if the pin's type properties (bIsInputPin, MultiType) can be modified + virtual bool CanModifyFlowDataPinType() const { return true; } + + // Determines if the bIsInputPin checkbox should be visible in the Details panel + virtual bool ShowFlowDataPinValueInputPinCheckbox() const { return true; } + + // Should the ClassFilter or EnumClass row be visible? + virtual bool ShowFlowDataPinValueClassFilter(const FFlowDataPinValue* Value) const { return true; } + + // Base policy for whether the ClassFilter / Enum source can be edited + virtual bool CanEditFlowDataPinValueClassFilter(const FFlowDataPinValue* Value) const { return true; } + + // Set the delegate that forces a layout rebuild (provided by owner detail customization). + virtual void SetFlowDataPinValuesRebuildDelegate(FSimpleDelegate InDelegate) {} + + // Request a details rebuild (executes delegate if bound). + virtual void RequestFlowDataPinValuesDetailsRebuild() {} +#endif +}; diff --git a/Source/Flow/Public/Interfaces/FlowDataPinValueSupplierInterface.h b/Source/Flow/Public/Interfaces/FlowDataPinValueSupplierInterface.h index 950bd1c5c..7a6266f23 100644 --- a/Source/Flow/Public/Interfaces/FlowDataPinValueSupplierInterface.h +++ b/Source/Flow/Public/Interfaces/FlowDataPinValueSupplierInterface.h @@ -2,10 +2,9 @@ #pragma once -#include "Types/FlowDataPinResults.h" - #include "UObject/Interface.h" +#include "Types/FlowDataPinResults.h" #include "FlowDataPinValueSupplierInterface.generated.h" // Interface to define a Flow Data Pin value supplier. This is generally a UFlowNode subclass, @@ -28,81 +27,7 @@ class FLOW_API IFlowDataPinValueSupplierInterface bool CanSupplyDataPinValues() const; virtual bool CanSupplyDataPinValues_Implementation() const { return true; } - // Must implement TrySupplyDataAs... for every EFlowPinType - FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); - - // Try to supply the value for a data Bool pin - UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As Bool") - FFlowDataPinResult_Bool TrySupplyDataPinAsBool(const FName& PinName) const; - virtual FFlowDataPinResult_Bool TrySupplyDataPinAsBool_Implementation(const FName& PinName) const { return FFlowDataPinResult_Bool(); } - - // Try to supply the value for a data Int pin - UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As Int") - FFlowDataPinResult_Int TrySupplyDataPinAsInt(const FName& PinName) const; - virtual FFlowDataPinResult_Int TrySupplyDataPinAsInt_Implementation(const FName& PinName) const { return FFlowDataPinResult_Int(); } - - // Try to supply the value for a data Float pin - UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As Float") - FFlowDataPinResult_Float TrySupplyDataPinAsFloat(const FName& PinName) const; - virtual FFlowDataPinResult_Float TrySupplyDataPinAsFloat_Implementation(const FName& PinName) const { return FFlowDataPinResult_Float(); } - - // Try to supply the value for a data Name pin - UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As Name") - FFlowDataPinResult_Name TrySupplyDataPinAsName(const FName& PinName) const; - virtual FFlowDataPinResult_Name TrySupplyDataPinAsName_Implementation(const FName& PinName) const { return FFlowDataPinResult_Name(); } - - // Try to supply the value for a data String pin - UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As String") - FFlowDataPinResult_String TrySupplyDataPinAsString(const FName& PinName) const; - virtual FFlowDataPinResult_String TrySupplyDataPinAsString_Implementation(const FName& PinName) const { return FFlowDataPinResult_String(); } - - // Try to supply the value for a data Text pin - UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As Text") - FFlowDataPinResult_Text TrySupplyDataPinAsText(const FName& PinName) const; - virtual FFlowDataPinResult_Text TrySupplyDataPinAsText_Implementation(const FName& PinName) const { return FFlowDataPinResult_Text(); } - - // Try to supply the value for a data Enum pin - UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As Enum") - FFlowDataPinResult_Enum TrySupplyDataPinAsEnum(const FName& PinName) const; - virtual FFlowDataPinResult_Enum TrySupplyDataPinAsEnum_Implementation(const FName& PinName) const { return FFlowDataPinResult_Enum(); } - - // Try to supply the value for a data Vector pin - UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As Vector") - FFlowDataPinResult_Vector TrySupplyDataPinAsVector(const FName& PinName) const; - virtual FFlowDataPinResult_Vector TrySupplyDataPinAsVector_Implementation(const FName& PinName) const { return FFlowDataPinResult_Vector(); } - - // Try to supply the value for a data Rotator pin - UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As Rotator") - FFlowDataPinResult_Rotator TrySupplyDataPinAsRotator(const FName& PinName) const; - virtual FFlowDataPinResult_Rotator TrySupplyDataPinAsRotator_Implementation(const FName& PinName) const { return FFlowDataPinResult_Rotator(); } - - // Try to supply the value for a data Transform pin - UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As Transform") - FFlowDataPinResult_Transform TrySupplyDataPinAsTransform(const FName& PinName) const; - virtual FFlowDataPinResult_Transform TrySupplyDataPinAsTransform_Implementation(const FName& PinName) const { return FFlowDataPinResult_Transform(); } - - // Try to supply the value for a data GameplayTag pin - UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As GameplayTag") - FFlowDataPinResult_GameplayTag TrySupplyDataPinAsGameplayTag(const FName& PinName) const; - virtual FFlowDataPinResult_GameplayTag TrySupplyDataPinAsGameplayTag_Implementation(const FName& PinName) const { return FFlowDataPinResult_GameplayTag(); } - - // Try to supply the value for a data GameplayTagContainer pin - UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As GameplayTagContainer") - FFlowDataPinResult_GameplayTagContainer TrySupplyDataPinAsGameplayTagContainer(const FName& PinName) const; - virtual FFlowDataPinResult_GameplayTagContainer TrySupplyDataPinAsGameplayTagContainer_Implementation(const FName& PinName) const { return FFlowDataPinResult_GameplayTagContainer(); } - - // Try to supply the value for a data InstancedStruct pin - UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As InstancedStruct") - FFlowDataPinResult_InstancedStruct TrySupplyDataPinAsInstancedStruct(const FName& PinName) const; - virtual FFlowDataPinResult_InstancedStruct TrySupplyDataPinAsInstancedStruct_Implementation(const FName& PinName) const { return FFlowDataPinResult_InstancedStruct(); } - - // Try to supply the value for a data Object pin - UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As Object") - FFlowDataPinResult_Object TrySupplyDataPinAsObject(const FName& PinName) const; - virtual FFlowDataPinResult_Object TrySupplyDataPinAsObject_Implementation(const FName& PinName) const { return FFlowDataPinResult_Object(); } - - // Try to supply the value for a data Class pin - UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin As Class") - FFlowDataPinResult_Class TrySupplyDataPinAsClass(const FName& PinName) const; - virtual FFlowDataPinResult_Class TrySupplyDataPinAsClass_Implementation(const FName& PinName) const { return FFlowDataPinResult_Class(); } + UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin") + FFlowDataPinResult TrySupplyDataPin(FName PinName) const; + virtual FFlowDataPinResult TrySupplyDataPin_Implementation(FName PinName) const { return FFlowDataPinResult(); } }; diff --git a/Source/Flow/Public/Interfaces/FlowNamedPropertiesSupplierInterface.h b/Source/Flow/Public/Interfaces/FlowNamedPropertiesSupplierInterface.h new file mode 100644 index 000000000..b41bd87ac --- /dev/null +++ b/Source/Flow/Public/Interfaces/FlowNamedPropertiesSupplierInterface.h @@ -0,0 +1,30 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "UObject/Interface.h" + +#include "FlowNamedPropertiesSupplierInterface.generated.h" + +struct FFlowNamedDataPinProperty; + +UINTERFACE(Blueprintable) +class FLOW_API UFlowNamedPropertiesSupplierInterface : public UInterface +{ + GENERATED_BODY() +}; + +/** + * Interface for Flow nodes that supply named properties, such as Start or DefineProperties nodes. + */ +class FLOW_API IFlowNamedPropertiesSupplierInterface +{ + GENERATED_BODY() + +public: + +#if WITH_EDITOR + // Returns the array of named properties defined by this node. + virtual TArray& GetMutableNamedProperties() = 0; +#endif +}; diff --git a/Source/Flow/Public/Interfaces/FlowOwnerInterface.h b/Source/Flow/Public/Interfaces/FlowOwnerInterface.h deleted file mode 100644 index 28a00f5e0..000000000 --- a/Source/Flow/Public/Interfaces/FlowOwnerInterface.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#pragma once - -#include "UObject/Interface.h" - -#include "FlowOwnerInterface.generated.h" - -// (optional) interface to enable a Flow owner object to execute CallOwnerFunction nodes -UINTERFACE(MinimalAPI, Blueprintable, BlueprintType) -class UFlowOwnerInterface : public UInterface -{ - GENERATED_BODY() -}; - -class FLOW_API IFlowOwnerInterface -{ - GENERATED_BODY() -}; diff --git a/Source/Flow/Public/Nodes/Actor/FlowNode_ExecuteComponent.h b/Source/Flow/Public/Nodes/Actor/FlowNode_ExecuteComponent.h index c4d8a4c58..78f85f7e5 100644 --- a/Source/Flow/Public/Nodes/Actor/FlowNode_ExecuteComponent.h +++ b/Source/Flow/Public/Nodes/Actor/FlowNode_ExecuteComponent.h @@ -2,16 +2,13 @@ #pragma once -#include "Types/FlowActorOwnerComponentRef.h" - #include "Nodes/FlowNode.h" -#include "Types/FlowInjectComponentsHelper.h" +#include "Types/FlowActorOwnerComponentRef.h" #include "Types/FlowEnumUtils.h" #include "FlowNode_ExecuteComponent.generated.h" // Forward Declarations -class IFlowOwnerInterface; class UFlowInjectComponentsManager; UENUM() @@ -64,23 +61,8 @@ class FLOW_API UFlowNode_ExecuteComponent : public UFlowNode virtual void UpdateNodeConfigText_Implementation() override; // -- - // IFlowDataPinValueSupplierInterface - virtual bool CanSupplyDataPinValues_Implementation() const override; - virtual FFlowDataPinResult_Bool TrySupplyDataPinAsBool_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Int TrySupplyDataPinAsInt_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Float TrySupplyDataPinAsFloat_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Name TrySupplyDataPinAsName_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_String TrySupplyDataPinAsString_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Text TrySupplyDataPinAsText_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Enum TrySupplyDataPinAsEnum_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Vector TrySupplyDataPinAsVector_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Rotator TrySupplyDataPinAsRotator_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Transform TrySupplyDataPinAsTransform_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_GameplayTag TrySupplyDataPinAsGameplayTag_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_GameplayTagContainer TrySupplyDataPinAsGameplayTagContainer_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_InstancedStruct TrySupplyDataPinAsInstancedStruct_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Object TrySupplyDataPinAsObject_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Class TrySupplyDataPinAsClass_Implementation(const FName& PinName) const override; + // UFlowNode + virtual void GatherPotentialPropertyOwnersForDataPins(TArray& InOutOwners) const override; // -- #if WITH_EDITOR @@ -96,7 +78,7 @@ class FLOW_API UFlowNode_ExecuteComponent : public UFlowNode // -- // UFlowNode - virtual FText GetNodeTitle() const override; + virtual FText K2_GetNodeTitle_Implementation() const override; virtual EDataValidationResult ValidateNode() override; virtual FString GetStatusString() const override; @@ -114,6 +96,8 @@ class FLOW_API UFlowNode_ExecuteComponent : public UFlowNode bool TryInjectComponent(); + const UActorComponent* GetResolvedOrExpectedComponent() const; + UActorComponent* TryResolveComponent(); UActorComponent* GetResolvedComponent() const; TSubclassOf TryGetExpectedActorOwnerClass() const; diff --git a/Source/Flow/Public/Nodes/Developer/FlowNode_Log.h b/Source/Flow/Public/Nodes/Developer/FlowNode_Log.h index 0a88727ed..0fdf3fa77 100644 --- a/Source/Flow/Public/Nodes/Developer/FlowNode_Log.h +++ b/Source/Flow/Public/Nodes/Developer/FlowNode_Log.h @@ -51,6 +51,10 @@ class FLOW_API UFlowNode_Log : public UFlowNode_DefineProperties #if WITH_EDITOR public: + // UObject + virtual void PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent) override; + // -- + virtual void UpdateNodeConfigText_Implementation() override; #endif }; diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index b9da34695..9e2aec140 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -9,9 +9,10 @@ #include "FlowNodeBase.h" #include "FlowTypes.h" +#include "Interfaces/FlowDataPinGeneratorInterface.h" #include "Interfaces/FlowDataPinValueSupplierInterface.h" #include "Nodes/FlowPin.h" -#include "Types/FlowDataPinProperties.h" +#include "Types/FlowArray.h" #include "FlowNode.generated.h" @@ -21,6 +22,7 @@ UCLASS(Abstract, Blueprintable, HideCategories = Object) class FLOW_API UFlowNode : public UFlowNodeBase + , public IFlowDataPinGeneratorInterface , public IFlowDataPinValueSupplierInterface , public IVisualLoggerDebugSnapshotInterface { @@ -53,14 +55,17 @@ class FLOW_API UFlowNode // -- public: -#if WITH_EDITOR // UObject - virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; virtual void PostLoad() override; // -- - virtual EDataValidationResult ValidateNode() { return EDataValidationResult::NotValidated; } +#if WITH_EDITOR + // UObject + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; + // -- + virtual EDataValidationResult ValidateNode(); + void ValidateFlowPinArrayIsUnique(const TArray& FlowPins, TSet& InOutUniquePinNames, EDataValidationResult& InOutResult); #endif // Inherits Guid after graph node @@ -146,11 +151,6 @@ class FLOW_API UFlowNode void RemoveUserInput(const FName& PinName); void RemoveUserOutput(const FName& PinName); - - // Functions to determine acceptance for 'wildcard' data pin types (eg., singular, array, set, map) - // TODO (gtaylor) The data pins feature is under construction - bool DoesInputWildcardPinAcceptArray(const UEdGraphPin* Pin) const { return true; } - bool DoesOutputWildcardPinAcceptContainer(const UEdGraphPin* Pin) const { return true; } #endif protected: @@ -169,7 +169,6 @@ class FLOW_API UFlowNode TMap Connections; public: - void SetConnections(const TMap& InConnections) { Connections = InConnections; } FConnectedPin GetConnection(const FName OutputName) const { return Connections.FindRef(OutputName); } UE_DEPRECATED(5.5, "Please use GatherConnectedNodes instead.") @@ -191,6 +190,8 @@ class FLOW_API UFlowNode FFlowPin* FindInputPinByName(const FName& PinName); FFlowPin* FindOutputPinByName(const FName& PinName); + const FFlowPin* FindInputPinByName(const FName& PinName) const { return const_cast(this)->FindInputPinByName(PinName); } + const FFlowPin* FindOutputPinByName(const FName& PinName) const { return const_cast(this)->FindOutputPinByName(PinName); } static void RecursiveFindNodesByClass(UFlowNode* Node, const TSubclassOf Class, uint8 Depth, TArray& OutNodes); @@ -201,18 +202,17 @@ class FLOW_API UFlowNode bool FindConnectedNodeForPinFast(const FName& FlowPinName, FGuid* FoundGuid = nullptr, FName* OutConnectedPinName = nullptr) const; bool FindConnectedNodeForPinSlow(const FName& FlowPinName, FGuid* FoundGuid = nullptr, FName* OutConnectedPinName = nullptr) const; + // Return all connections to a Pin this Node knows about. + // Connections are only stored on one of the Nodes they connect depending on pin type. + // As such, this function may not return anything even if the Node is connected to the Pin. + // Use UFlowAsset::GetAllPinsConnectedToPin() to do a guaranteed find of all Connections. + TArray GetKnownConnectionsToPin(const FConnectedPin& Pin) const; + ////////////////////////////////////////////////////////////////////////// // Data Pins public: - // Map of DataPin Name to its Bound Property, - // when using metadata tag 'BindOutputFlowDataPin' to bind properties to data pins for automatic supplier support - UPROPERTY(VisibleDefaultsOnly, AdvancedDisplay, Category = "FlowNode", meta = (GetByRef)) - TMap PinNameToBoundPropertyNameMap; - - const TMap& GetPinNameToBoundPropertyNameMap() const { return PinNameToBoundPropertyNameMap; } - #if WITH_EDITORONLY_DATA UPROPERTY(VisibleDefaultsOnly, AdvancedDisplay, Category = "FlowNode", meta = (GetByRef)) TArray AutoInputDataPins; @@ -222,9 +222,6 @@ class FLOW_API UFlowNode #endif // WITH_EDITORONLY_DATA #if WITH_EDITOR - void SetPinNameToBoundPropertyNameMap(const TMap& Map); - TMap& GetMutablePinNameToBoundPropertyNameMap() { return PinNameToBoundPropertyNameMap; } - void SetAutoInputDataPins(const TArray& AutoInputPins); void SetAutoOutputDataPins(const TArray& AutoOutputPins); const TArray& GetAutoInputDataPins() const { return AutoInputDataPins; } @@ -232,72 +229,64 @@ class FLOW_API UFlowNode TArray& GetMutableAutoInputDataPins() { return AutoInputDataPins; } TArray& GetMutableAutoOutputDataPins() { return AutoOutputDataPins; } + + void SetConnections(const TMap& InConnections); + virtual void OnConnectionsChanged(const TMap& OldConnections) { } #endif // WITH_EDITOR // IFlowDataPinValueSupplierInterface - virtual bool CanSupplyDataPinValues_Implementation() const override; - virtual FFlowDataPinResult_Bool TrySupplyDataPinAsBool_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Int TrySupplyDataPinAsInt_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Float TrySupplyDataPinAsFloat_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Name TrySupplyDataPinAsName_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_String TrySupplyDataPinAsString_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Text TrySupplyDataPinAsText_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Enum TrySupplyDataPinAsEnum_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Vector TrySupplyDataPinAsVector_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Rotator TrySupplyDataPinAsRotator_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Transform TrySupplyDataPinAsTransform_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_GameplayTag TrySupplyDataPinAsGameplayTag_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_GameplayTagContainer TrySupplyDataPinAsGameplayTagContainer_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_InstancedStruct TrySupplyDataPinAsInstancedStruct_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Object TrySupplyDataPinAsObject_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Class TrySupplyDataPinAsClass_Implementation(const FName& PinName) const override; - - bool TryGetFlowDataPinSupplierDatasForPinName( - const FName& PinName, - TArray& InOutPinValueSupplierDatas) const; - // -- - -protected: +public: + virtual FFlowDataPinResult TrySupplyDataPin_Implementation(FName PinName) const override; - // Helper functions for the TrySupplyDataPin...() functions - bool TryFindPropertyByPinName( + // Advanced helper for TrySupplyDataPin, which can be overridden in subclasses to provide alternate sourcing for properties. + // If returns true, either OutFoundProperty or OutFoundInstancedStruct is expected to carry the property value. + // (this function is used for cases like DefineProperties, Start, and blackboard lookup nodes) + virtual bool TryFindPropertyByPinName( + const UObject& PropertyOwnerObject, const FName& PinName, const FProperty*& OutFoundProperty, - TInstancedStruct& OutFoundInstancedStruct, - EFlowDataPinResolveResult& InOutResult) const; - virtual bool TryFindPropertyByRemappedPinName( - const FName& RemappedPinName, - const FProperty*& OutFoundProperty, - TInstancedStruct& OutFoundInstancedStruct, - EFlowDataPinResolveResult& InOutResult) const; + TInstancedStruct& OutFoundInstancedStruct) const; - // Functions to supply the pin data value from a variety of supported property types - template - TFlowDataPinResultType TrySupplyDataPinAsType(const FName& PinName) const; + // Advanced helper for TrySupplyDataPin, which can be overridden in subclasses to provide additional or replacement object(s) + // for sourcing the properties for the given pin name. These objects will have PopulateResult called on them. + // (this function is used for cases like ExecuteComponent) + virtual void GatherPotentialPropertyOwnersForDataPins(TArray& InOutOwners) const; - template - TFlowDataPinResultType TrySupplyDataPinAsNumericType(const FName& PinName) const; + bool TryGatherPropertyOwnersAndPopulateResult( + const FName& PinName, + const FFlowPinType& DataPinType, + const FFlowPin& FlowPin, + FFlowDataPinResult& OutSuppliedResult) const; - template - TFlowDataPinResultType TrySupplyDataPinAsAnyTextType(const FName& PinName) const; +protected: + // Static implementation of the default TryFindPropertyByPinName (which subclasses can incorperate into overrides) + static bool TryFindPropertyByPinName_Static( + const UObject& PropertyOwnerObject, + const FName& PinName, + const FProperty*& OutFoundProperty, + TInstancedStruct& OutFoundInstancedStruct); - FORCEINLINE_DEBUGGABLE FFlowDataPinResult_Enum TrySupplyDataPinAsEnumType(const FName& PinName) const; + // #FlowDataPinLegacy +public: + void FixupDataPinTypes(); - template - TFlowDataPinResultType TrySupplyDataPinAsStructType(const FName& PinName) const; +protected: + static void FixupDataPinTypesForArray(TArray& MutableDataPinArray); + static void FixupDataPinTypesForPin(FFlowPin& MutableDataPin); + // -- - template - TFlowDataPinResultType TrySupplyDataPinAsUObjectTypeCommon(const FName& PinName, const FProperty*& OutFoundProperty) const; +public: - template - TFlowDataPinResultType TrySupplyDataPinAsUObjectType(const FName& PinName) const; + using TFlowPinValueSupplierDataArray = FlowArray::TInlineArray; + bool TryGetFlowDataPinSupplierDatasForPinName( + const FName& PinName, + TFlowPinValueSupplierDataArray& InOutPinValueSupplierDatas) const; - template - TFlowDataPinResultType TrySupplyDataPinAsUClassType(const FName& PinName) const; + // IFlowDataPinGeneratorInterface +#if WITH_EDITOR + virtual void AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const override; +#endif + // -- ////////////////////////////////////////////////////////////////////////// // Debugger @@ -324,7 +313,7 @@ class FLOW_API UFlowNode #if !UE_BUILD_SHIPPING -private: +protected: TMap> InputRecords; TMap> OutputRecords; #endif @@ -417,493 +406,3 @@ class FLOW_API UFlowNode UFUNCTION(BlueprintPure, Category = "FlowNode") static FString GetProgressAsString(float Value); }; - -// Templates & inline implementations: - -template -TFlowDataPinResultType UFlowNode::TrySupplyDataPinAsType(const FName& PinName) const -{ - TFlowDataPinResultType SuppliedResult; - - const FProperty* FoundProperty = nullptr; - TInstancedStruct InstancedStruct; - if (!TryFindPropertyByPinName(PinName, FoundProperty, InstancedStruct, SuppliedResult.Result)) - { - return SuppliedResult; - } - - if (const TFlowDataPinProperty* FlowDataPinProp = InstancedStruct.GetPtr()) - { - // In some cases, TryFindPropertyByPinName can find an instanced struct for the wrapper, - // so get the value from it and return straight away - - SuppliedResult.Value = FlowDataPinProp->Value; - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - - return SuppliedResult; - } - - // Check for struct-based wrapper for the property and get the value out of it - if (const FStructProperty* StructProperty = CastField(FoundProperty)) - { - const UScriptStruct* FlowDataPinPropertyStruct = TFlowDataPinProperty::StaticStruct(); - - if (StructProperty->Struct == FlowDataPinPropertyStruct) - { - TFlowDataPinProperty ValueStruct; - StructProperty->GetValue_InContainer(this, &ValueStruct); - - SuppliedResult.Value = ValueStruct.Value; - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - } - - return SuppliedResult; - } - - // Get the value from a UE simple property type - if (const TFieldPropertyType* UnrealProperty = CastField(FoundProperty)) - { - SuppliedResult.Value = UnrealProperty->GetPropertyValue_InContainer(this); - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - - return SuppliedResult; - } - - SuppliedResult.Result = EFlowDataPinResolveResult::FailedMismatchedType; - - return SuppliedResult; -} - -template -TFlowDataPinResultType UFlowNode::TrySupplyDataPinAsNumericType(const FName& PinName) const -{ - TFlowDataPinResultType SuppliedResult; - - const FProperty* FoundProperty = nullptr; - TInstancedStruct InstancedStruct; - if (!TryFindPropertyByPinName(PinName, FoundProperty, InstancedStruct, SuppliedResult.Result)) - { - return SuppliedResult; - } - - if (const FFlowDataPinProperty* FlowDataPinProp = InstancedStruct.GetPtr()) - { - // In some cases, TryFindPropertyByPinName can find an instanced struct for the wrapper, - // so get the value from it and return straight away - - if (const TFlowLargeDataPinProperty* FlowDataPinPropLarge = InstancedStruct.GetPtr()) - { - SuppliedResult.Value = FlowDataPinPropLarge->Value; - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - - return SuppliedResult; - } - else if (const TFlowMediumDataPinProperty* FlowDataPinPropMedium = InstancedStruct.GetPtr()) - { - SuppliedResult.Value = FlowDataPinPropMedium->Value; - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - - return SuppliedResult; - } - } - - // Check for struct-based wrapper for the property and get the value out of it - if (const FStructProperty* StructProperty = CastField(FoundProperty)) - { - const UScriptStruct* FlowLargeDataPinPropertyStruct = TFlowLargeDataPinProperty::StaticStruct(); - const UScriptStruct* FlowMediumDataPinPropertyStruct = TFlowMediumDataPinProperty::StaticStruct(); - - // Supporting both a 64 and 32 bit wrapper for ints/floats, given the ubiquity of int32/float. - if (StructProperty->Struct == FlowLargeDataPinPropertyStruct) - { - TFlowLargeDataPinProperty ValueStruct; - StructProperty->GetValue_InContainer(this, &ValueStruct); - - SuppliedResult.Value = ValueStruct.Value; - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - } - else if (StructProperty->Struct == FlowMediumDataPinPropertyStruct) - { - TFlowMediumDataPinProperty ValueStruct; - StructProperty->GetValue_InContainer(this, &ValueStruct); - - SuppliedResult.Value = ValueStruct.Value; - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - } - - return SuppliedResult; - } - - // Get the value from a UE simple property type - if (const FNumericProperty* NumericProperty = CastField(FoundProperty)) - { - if (const FFloatProperty* FloatProperty = CastField(NumericProperty)) - { - float FloatValue; - FloatProperty->GetValue_InContainer(this, &FloatValue); - SuppliedResult.Value = FloatValue; - } - else if (const FDoubleProperty* DoubleProperty = CastField(NumericProperty)) - { - double DoubleValue; - DoubleProperty->GetValue_InContainer(this, &DoubleValue); - SuppliedResult.Value = DoubleValue; - } - else - { - SuppliedResult.Value = NumericProperty->GetSignedIntPropertyValue_InContainer(this); - } - - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - - return SuppliedResult; - } - - SuppliedResult.Result = EFlowDataPinResolveResult::FailedMismatchedType; - - return SuppliedResult; -} - -template -TFlowDataPinResultType UFlowNode::TrySupplyDataPinAsAnyTextType(const FName& PinName) const -{ - TFlowDataPinResultType SuppliedResult; - - const FProperty* FoundProperty = nullptr; - TInstancedStruct InstancedStruct; - if (!TryFindPropertyByPinName(PinName, FoundProperty, InstancedStruct, SuppliedResult.Result)) - { - return SuppliedResult; - } - - if (const FFlowDataPinProperty* FlowDataPinProp = InstancedStruct.GetPtr()) - { - // In some cases, TryFindPropertyByPinName can find an instanced struct for the wrapper, - // so get the value from it and return straight away - - if (const FFlowDataPinOutputProperty_Name* FlowDataPinPropName = InstancedStruct.GetPtr()) - { - SuppliedResult.SetValue(FlowDataPinPropName->Value); - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - - return SuppliedResult; - } - else if (const FFlowDataPinOutputProperty_String* FlowDataPinPropString = InstancedStruct.GetPtr()) - { - SuppliedResult.SetValue(FlowDataPinPropString->Value); - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - - return SuppliedResult; - } - else if (const FFlowDataPinOutputProperty_Text* FlowDataPinPropText = InstancedStruct.GetPtr()) - { - SuppliedResult.SetValue(FlowDataPinPropText->Value); - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - - return SuppliedResult; - } - } - - // Check for struct-based wrapper for the property and get the value out of it - if (const FStructProperty* StructProperty = CastField(FoundProperty)) - { - const UScriptStruct* FlowDataPinPropertyStruct_Name = FFlowDataPinOutputProperty_Name::StaticStruct(); - const UScriptStruct* FlowDataPinPropertyStruct_String = FFlowDataPinOutputProperty_String::StaticStruct(); - const UScriptStruct* FlowDataPinPropertyStruct_Text = FFlowDataPinOutputProperty_Text::StaticStruct(); - - if (StructProperty->Struct == FlowDataPinPropertyStruct_Name) - { - FFlowDataPinOutputProperty_Name ValueStruct; - StructProperty->GetValue_InContainer(this, &ValueStruct); - - SuppliedResult.SetValue(ValueStruct.Value); - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - } - else if (StructProperty->Struct == FlowDataPinPropertyStruct_String) - { - FFlowDataPinOutputProperty_String ValueStruct; - StructProperty->GetValue_InContainer(this, &ValueStruct); - - SuppliedResult.SetValue(ValueStruct.Value); - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - } - else if (StructProperty->Struct == FlowDataPinPropertyStruct_Text) - { - FFlowDataPinOutputProperty_Text ValueStruct; - StructProperty->GetValue_InContainer(this, &ValueStruct); - - SuppliedResult.SetValue(ValueStruct.Value); - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - } - - return SuppliedResult; - } - - // Get the value from a UE simple property type - if (const FNameProperty* NameProperty = CastField(FoundProperty)) - { - SuppliedResult.SetValue(NameProperty->GetPropertyValue_InContainer(this)); - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - - return SuppliedResult; - } - else if (const FStrProperty* StrProperty = CastField(FoundProperty)) - { - SuppliedResult.SetValue(StrProperty->GetPropertyValue_InContainer(this)); - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - - return SuppliedResult; - } - else if (const FTextProperty* TextProperty = CastField(FoundProperty)) - { - SuppliedResult.SetValue(TextProperty->GetPropertyValue_InContainer(this)); - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - - return SuppliedResult; - } - else - { - SuppliedResult.Result = EFlowDataPinResolveResult::FailedMismatchedType; - - return SuppliedResult; - } -} - -FFlowDataPinResult_Enum UFlowNode::TrySupplyDataPinAsEnumType(const FName& PinName) const -{ - FFlowDataPinResult_Enum SuppliedResult; - - const FProperty* FoundProperty = nullptr; - TInstancedStruct InstancedStruct; - if (!TryFindPropertyByPinName(PinName, FoundProperty, InstancedStruct, SuppliedResult.Result)) - { - return SuppliedResult; - } - - if (const FFlowDataPinOutputProperty_Enum* FlowDataPinProp = InstancedStruct.GetPtr()) - { - // In some cases, TryFindPropertyByPinName can find an instanced struct for the wrapper, - // so get the value from it and return straight away - - SuppliedResult.Value = FlowDataPinProp->Value; - SuppliedResult.EnumClass = FlowDataPinProp->EnumClass; - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - - return SuppliedResult; - } - - // Check for struct-based wrapper for the property and get the value out of it - if (const FStructProperty* StructProperty = CastField(FoundProperty)) - { - const UScriptStruct* FlowDataPinPropertyStruct_Enum = FFlowDataPinOutputProperty_Enum::StaticStruct(); - - if (StructProperty->Struct == FlowDataPinPropertyStruct_Enum) - { - FFlowDataPinOutputProperty_Enum ValueStruct; - StructProperty->GetValue_InContainer(this, &ValueStruct); - - SuppliedResult.Value = ValueStruct.Value; - SuppliedResult.EnumClass = ValueStruct.EnumClass; - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - } - - return SuppliedResult; - } - - // Get the value from a UE enum property type - if (const FEnumProperty* EnumProperty = CastField(FoundProperty)) - { - UEnum* EnumClass = EnumProperty->GetEnum(); - - const FNumericProperty* UnderlyingProperty = EnumProperty->GetUnderlyingProperty(); - const int64 SignedIntValue = UnderlyingProperty->GetSignedIntPropertyValue_InContainer(this); - const FString StringValue = EnumClass->GetAuthoredNameStringByValue(SignedIntValue); - - SuppliedResult.Value = FName(StringValue); - SuppliedResult.EnumClass = EnumClass; - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - - return SuppliedResult; - } - else - { - SuppliedResult.Result = EFlowDataPinResolveResult::FailedMismatchedType; - - return SuppliedResult; - } -} - -template -TFlowDataPinResultType UFlowNode::TrySupplyDataPinAsStructType(const FName& PinName) const -{ - TFlowDataPinResultType SuppliedResult; - - const FProperty* FoundProperty = nullptr; - TInstancedStruct InstancedStruct; - if (!TryFindPropertyByPinName(PinName, FoundProperty, InstancedStruct, SuppliedResult.Result)) - { - return SuppliedResult; - } - - if (const TFlowDataPinProperty* FlowDataPinProp = InstancedStruct.GetPtr()) - { - // In some cases, TryFindPropertyByPinName can find an instanced struct for the wrapper, - // so get the value from it and return straight away - - SuppliedResult.Value = FlowDataPinProp->Value; - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - - return SuppliedResult; - } - - const FStructProperty* StructProperty = CastField(FoundProperty); - if (!StructProperty) - { - SuppliedResult.Result = EFlowDataPinResolveResult::FailedMismatchedType; - - return SuppliedResult; - } - - const UScriptStruct* FlowDataPinPropertyStruct = TFlowDataPinProperty::StaticStruct(); - static const UScriptStruct* TargetPropertyStruct = TBaseStructure::Get(); - - if (StructProperty->Struct == FlowDataPinPropertyStruct) - { - // Check for struct-based wrapper for the property and get the value out of it - - TFlowDataPinProperty ValueStruct; - StructProperty->GetValue_InContainer(this, &ValueStruct); - - SuppliedResult.Value = ValueStruct.Value; - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - - return SuppliedResult; - } - else if (StructProperty->Struct == TargetPropertyStruct) - { - // Get the value from a UE struct (non-wrapper) property type - - TTargetStruct TargetStruct; - StructProperty->GetValue_InContainer(this, &TargetStruct); - - SuppliedResult.Value = TargetStruct; - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - - return SuppliedResult; - } - else - { - SuppliedResult.Result = EFlowDataPinResolveResult::FailedMismatchedType; - - return SuppliedResult; - } -} - -template -TFlowDataPinResultType UFlowNode::TrySupplyDataPinAsUObjectTypeCommon(const FName& PinName, const FProperty*& OutFoundProperty) const -{ - TFlowDataPinResultType SuppliedResult; - - TInstancedStruct InstancedStruct; - if (!TryFindPropertyByPinName(PinName, OutFoundProperty, InstancedStruct, SuppliedResult.Result)) - { - return SuppliedResult; - } - - if (const TFlowDataPinProperty* FlowDataPinProp = InstancedStruct.GetPtr()) - { - // In some cases, TryFindPropertyByPinName can find an instanced struct for the wrapper, - // so get the value from it and return straight away - - SuppliedResult.SetValueFromPropertyWrapper(*FlowDataPinProp); - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - - return SuppliedResult; - } - - // Check for struct-based wrapper for the property and get the value out of it - if (const FStructProperty* StructProperty = CastField(OutFoundProperty)) - { - const UScriptStruct* FlowDataPinPropertyStruct = TFlowDataPinProperty::StaticStruct(); - - if (StructProperty->Struct == FlowDataPinPropertyStruct) - { - TFlowDataPinProperty ValueStruct; - StructProperty->GetValue_InContainer(this, &ValueStruct); - - SuppliedResult.SetValueFromPropertyWrapper(ValueStruct); - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - } - - return SuppliedResult; - } - - // Get the value from one of the UE simple property types - if (const TFieldPropertyObjectType0* UnrealProperty0 = CastField(OutFoundProperty)) - { - // TObjectPtr / UObject* - TUObjectType* Object = Cast(UnrealProperty0->GetPropertyValue_InContainer(this)); - SuppliedResult.SetValueFromObjectPtr(Object); - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - - return SuppliedResult; - } - - if (const TFieldPropertySoftObjectType1* UnrealProperty1 = CastField(OutFoundProperty)) - { - // FSoftObjectPath / TSoftObjectPtr (or their Class variants) - const FSoftObjectPath SoftObjectPath = UnrealProperty1->GetPropertyValue_InContainer(this).ToSoftObjectPath(); - SuppliedResult.SetValueFromSoftPath(SoftObjectPath); - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - - return SuppliedResult; - } - - SuppliedResult.Result = EFlowDataPinResolveResult::FailedMismatchedType; - - return SuppliedResult; -} - -template -TFlowDataPinResultType UFlowNode::TrySupplyDataPinAsUObjectType(const FName& PinName) const -{ - // First execute TrySupplyDataPinAsUObjectTypeCommon to handle all of the shared cases between UObject and UClass properties: - const FProperty* FoundProperty = nullptr; - TFlowDataPinResultType SuppliedResult = - TrySupplyDataPinAsUObjectTypeCommon(PinName, FoundProperty); - - if (SuppliedResult.Result == EFlowDataPinResolveResult::FailedMismatchedType) - { - if (const TFieldPropertyWeakType2* UnrealProperty2 = CastField(FoundProperty)) - { - // TWeakObjectPtr - TUObjectType* Object = Cast(UnrealProperty2->GetPropertyValue_InContainer(this).Get()); - SuppliedResult.SetValueFromObjectPtr(Object); - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - - return SuppliedResult; - } - - if (const TFieldPropertyLazyType3* UnrealProperty3 = CastField(FoundProperty)) - { - // FLazyObjectPtr - TUObjectType* Object = Cast(UnrealProperty3->GetPropertyValue_InContainer(this).Get()); - SuppliedResult.SetValueFromObjectPtr(Object); - SuppliedResult.Result = EFlowDataPinResolveResult::Success; - - return SuppliedResult; - } - } - - return SuppliedResult; -} - -template -TFlowDataPinResultType UFlowNode::TrySupplyDataPinAsUClassType(const FName& PinName) const -{ - const FProperty* FoundProperty = nullptr; - return TrySupplyDataPinAsUObjectTypeCommon(PinName, FoundProperty); -} diff --git a/Source/Flow/Public/Nodes/FlowNodeBase.h b/Source/Flow/Public/Nodes/FlowNodeBase.h index b9f9d5169..e5441eff0 100644 --- a/Source/Flow/Public/Nodes/FlowNodeBase.h +++ b/Source/Flow/Public/Nodes/FlowNodeBase.h @@ -6,10 +6,12 @@ #include "Interfaces/FlowCoreExecutableInterface.h" #include "Interfaces/FlowContextPinSupplierInterface.h" +#include "Interfaces/FlowDataPinValueOwnerInterface.h" #include "FlowMessageLog.h" #include "FlowTags.h" // used by subclasses #include "FlowTypes.h" #include "Types/FlowDataPinResults.h" +#include "Types/FlowPinTypeTemplates.h" #include "FlowNodeBase.generated.h" @@ -18,10 +20,10 @@ class UFlowNode; class UFlowNodeAddOn; class UFlowSubsystem; class UEdGraphNode; -class IFlowOwnerInterface; class IFlowDataPinValueSupplierInterface; struct FFlowPin; struct FFlowNamedDataPinProperty; +struct FFlowPinType; #if WITH_EDITORONLY_DATA DECLARE_DELEGATE(FFlowNodeEvent); @@ -37,21 +39,6 @@ struct FFlowPinValueSupplierData const IFlowDataPinValueSupplierInterface* PinValueSupplier = nullptr; }; -// Helper template to reduce (some) of the boilerplate in TryResolveDataPinAs...() functions -template -struct TResolveDataPinWorkingData -{ - bool TrySetupWorkingData(const FName& PinName, const UFlowNodeBase& FlowNodeBase); - - TFlowDataPinResultType DataPinResult; - const UFlowNode* FlowNode = nullptr; - const FFlowPin* FlowPin = nullptr; - - TArray PinValueSupplierDatas; - - static constexpr bool bCheckDefaultProperties = true; -}; - /** * The base class for UFlowNode and UFlowNodeAddOn, with their shared functionality */ @@ -60,6 +47,7 @@ class FLOW_API UFlowNodeBase : public UObject , public IFlowCoreExecutableInterface , public IFlowContextPinSupplierInterface + , public IFlowDataPinValueOwnerInterface { GENERATED_UCLASS_BODY() @@ -157,17 +145,8 @@ class FLOW_API UFlowNodeBase UFUNCTION(BlueprintCallable, Category = "FlowNode") UObject* TryGetRootFlowObjectOwner() const; - // Returns the IFlowOwnerInterface for the owner object (if implemented) - // NOTE - will consider a UActorComponent owner's owning actor if appropriate - IFlowOwnerInterface* GetFlowOwnerInterface() const; - static TArray BuildFlowNodeBaseAncestorChain(UFlowNodeBase& FromFlowNodeBase, bool bIncludeFromFlowNodeBase); -protected: - // Helper functions for GetFlowOwnerInterface() - static IFlowOwnerInterface* TryGetFlowOwnerInterfaceFromRootFlowOwner(UObject& RootFlowOwner, const UClass& ExpectedOwnerClass); - static IFlowOwnerInterface* TryGetFlowOwnerInterfaceActor(UObject& RootFlowOwner, const UClass& ExpectedOwnerClass); - ////////////////////////////////////////////////////////////////////////// // AddOn support @@ -233,63 +212,114 @@ class FLOW_API UFlowNodeBase ////////////////////////////////////////////////////////////////////////// // Data Pins - // Must implement TryResolveDataAs... for every EFlowPinType - FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); + // IFlowDataPinValueOwnerInterface +#if WITH_EDITOR +public: + virtual bool CanModifyFlowDataPinType() const override; + virtual bool ShowFlowDataPinValueInputPinCheckbox() const override; + virtual bool ShowFlowDataPinValueClassFilter(const FFlowDataPinValue* Value) const override; + virtual bool CanEditFlowDataPinValueClassFilter(const FFlowDataPinValue* Value) const override; + virtual void SetFlowDataPinValuesRebuildDelegate(FSimpleDelegate InDelegate) override + { + FlowDataPinValuesRebuildDelegate = InDelegate; + } + virtual void RequestFlowDataPinValuesDetailsRebuild() override + { + if (FlowDataPinValuesRebuildDelegate.IsBound()) + { + FlowDataPinValuesRebuildDelegate.Execute(); + } + } +private: + FSimpleDelegate FlowDataPinValuesRebuildDelegate; +protected: + // Helpers for IFlowDataPinValueOwnerInterface + bool IsPlacedInFlowAsset() const; + bool IsFlowNamedPropertiesSupplier() const; +#endif + // -- + +private: + UFUNCTION(BlueprintPure, Category = DataPins, DisplayName = "Resolve DataPin By Name") + FFlowDataPinResult TryResolveDataPin(FName PinName) const; + +public: + // Generic single-value resolve & extractor + template + EFlowDataPinResolveResult TryResolveDataPinValue(const FName& PinName, typename TFlowPinType::ValueType& OutValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue) const; + + // Generic array-value resolve & extractor + template + EFlowDataPinResolveResult TryResolveDataPinValues(const FName& PinName, TArray& OutValues) const; + + // Special-case single-value resolve & extractor for native enums + template requires std::is_enum_v + EFlowDataPinResolveResult TryResolveDataPinValue(const FName& PinName, TEnumType& OutValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue) const; + + // Special-case array-value resolve & extractor for native enums + template requires std::is_enum_v + EFlowDataPinResolveResult TryResolveDataPinValues(const FName& PinName, TArray& OutValues) const; + + // Special-case single-value resolve & extractor for enums (as FName values) + template + EFlowDataPinResolveResult TryResolveDataPinValue(const FName& PinName, FName& OutEnumValue, UEnum*& OutEnumClass, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue) const; - UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Bool") + // Special-case array-value resolve & extractor for enums (as FName values) + template + EFlowDataPinResolveResult TryResolveDataPinValues(const FName& PinName, TArray& OutEnumValues, UEnum*& OutEnumClass) const; + +public: + + // #FlowDataPinLegacy + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Bool", meta = (DeprecatedFunction, DeprecationMessage = "Use TryResolveDataPin (in blueprint) or TryResolveDataPinValue(s) (in code) instead")) FFlowDataPinResult_Bool TryResolveDataPinAsBool(const FName& PinName) const; - UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Int") + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Int", meta = (DeprecatedFunction, DeprecationMessage = "Use TryResolveDataPin (in blueprint) or TryResolveDataPinValue(s) (in code) instead")) FFlowDataPinResult_Int TryResolveDataPinAsInt(const FName& PinName) const; - UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Float") + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Float", meta = (DeprecatedFunction, DeprecationMessage = "Use TryResolveDataPin (in blueprint) or TryResolveDataPinValue(s) (in code) instead")) FFlowDataPinResult_Float TryResolveDataPinAsFloat(const FName& PinName) const; - UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Name") + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Name", meta = (DeprecatedFunction, DeprecationMessage = "Use TryResolveDataPin (in blueprint) or TryResolveDataPinValue(s) (in code) instead")) FFlowDataPinResult_Name TryResolveDataPinAsName(const FName& PinName) const; - UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As String") + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As String", meta = (DeprecatedFunction, DeprecationMessage = "Use TryResolveDataPin (in blueprint) or TryResolveDataPinValue(s) (in code) instead")) FFlowDataPinResult_String TryResolveDataPinAsString(const FName& PinName) const; - UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Text") + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Text", meta = (DeprecatedFunction, DeprecationMessage = "Use TryResolveDataPin (in blueprint) or TryResolveDataPinValue(s) (in code) instead")) FFlowDataPinResult_Text TryResolveDataPinAsText(const FName& PinName) const; - UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Enum") + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Enum", meta = (DeprecatedFunction, DeprecationMessage = "Use TryResolveDataPin (in blueprint) or TryResolveDataPinValue(s) (in code) instead")) FFlowDataPinResult_Enum TryResolveDataPinAsEnum(const FName& PinName) const; - UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Vector") + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Vector", meta = (DeprecatedFunction, DeprecationMessage = "Use TryResolveDataPin (in blueprint) or TryResolveDataPinValue(s) (in code) instead")) FFlowDataPinResult_Vector TryResolveDataPinAsVector(const FName& PinName) const; - UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Rotator") + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Rotator", meta = (DeprecatedFunction, DeprecationMessage = "Use TryResolveDataPin (in blueprint) or TryResolveDataPinValue(s) (in code) instead")) FFlowDataPinResult_Rotator TryResolveDataPinAsRotator(const FName& PinName) const; - UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Transform") + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Transform", meta = (DeprecatedFunction, DeprecationMessage = "Use TryResolveDataPin (in blueprint) or TryResolveDataPinValue(s) (in code) instead")) FFlowDataPinResult_Transform TryResolveDataPinAsTransform(const FName& PinName) const; - UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As GameplayTag") + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As GameplayTag", meta = (DeprecatedFunction, DeprecationMessage = "Use TryResolveDataPin (in blueprint) or TryResolveDataPinValue(s) (in code) instead")) FFlowDataPinResult_GameplayTag TryResolveDataPinAsGameplayTag(const FName& PinName) const; - UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As GameplayTagContainer") + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As GameplayTagContainer", meta = (DeprecatedFunction, DeprecationMessage = "Use TryResolveDataPin (in blueprint) or TryResolveDataPinValue(s) (in code) instead")) FFlowDataPinResult_GameplayTagContainer TryResolveDataPinAsGameplayTagContainer(const FName& PinName) const; - UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As InstancedStruct") + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As InstancedStruct", meta = (DeprecatedFunction, DeprecationMessage = "Use TryResolveDataPin (in blueprint) or TryResolveDataPinValue(s) (in code) instead")) FFlowDataPinResult_InstancedStruct TryResolveDataPinAsInstancedStruct(const FName& PinName) const; - UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Object") + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Object", meta = (DeprecatedFunction, DeprecationMessage = "Use TryResolveDataPin (in blueprint) or TryResolveDataPinValue(s) (in code) instead")) FFlowDataPinResult_Object TryResolveDataPinAsObject(const FName& PinName) const; - UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Class") + UFUNCTION(BlueprintCallable, Category = DataPins, DisplayName = "Try Resolve DataPin As Class", meta = (DeprecatedFunction, DeprecationMessage = "Use TryResolveDataPin (in blueprint) or TryResolveDataPinValue(s) (in code) instead")) FFlowDataPinResult_Class TryResolveDataPinAsClass(const FName& PinName) const; - - // Public only for TResolveDataPinWorkingData's use - EFlowDataPinResolveResult TryResolveDataPinPrerequisites(const FName& PinName, const UFlowNode*& FlowNode, const FFlowPin*& FlowPin, EFlowPinType PinType) const; + // -- protected: - bool TryAddValueToFormatNamedArguments(const FFlowNamedDataPinProperty& NamedDataPinProperty, FFormatNamedArguments& InOutArguments) const; -public: - ////////////////////////////////////////////////////////////////////////// // Editor @@ -342,6 +372,8 @@ class FLOW_API UFlowNodeBase void RequestReconstruction() const { (void) OnReconstructionRequested.ExecuteIfBound(); }; + void SetCanDelete(bool CanDelete) { bCanDelete = CanDelete;} + #endif protected: @@ -373,22 +405,33 @@ class FLOW_API UFlowNodeBase #if WITH_EDITOR public: + // WARNING! Call UFlowGraphSettings::GetNodeCategoryForNode() instead! virtual FString GetNodeCategory() const; const FGameplayTag& GetNodeDisplayStyle() const { return NodeDisplayStyle; } // This method allows to have different for every node instance, i.e. Red if node represents enemy, Green if node represents a friend virtual bool GetDynamicTitleColor(FLinearColor& OutColor) const; - - virtual FText GetNodeTitle() const; - virtual FText GetNodeToolTip() const; - virtual FText GetNodeConfigText() const; + + virtual FText GetNodeTitle() const { return K2_GetNodeTitle(); } + virtual FText GetNodeToolTip() const { return K2_GetNodeToolTip(); } + FText GetGeneratedDisplayName() const; protected: void EnsureNodeDisplayStyle(); #endif // WITH_EDITOR +public: + UFUNCTION(BlueprintNativeEvent, Category = "FlowNode") + FText K2_GetNodeTitle() const; + + UFUNCTION(BlueprintNativeEvent, Category = "FlowNode") + FText K2_GetNodeToolTip() const; + + UFUNCTION(BlueprintPure, Category = "FlowNode") + virtual FText GetNodeConfigText() const; + protected: // Set the editor-only Config Text // (for displaying config info on the Node in the flow graph, ignored in non-editor builds) @@ -414,6 +457,7 @@ class FLOW_API UFlowNodeBase UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Get Node Description")) FString K2_GetNodeDescription() const; +public: UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (DevelopmentOnly)) void LogError(FString Message, const EFlowOnScreenMessageType OnScreenMessageType = EFlowOnScreenMessageType::Permanent) const; @@ -431,3 +475,57 @@ class FLOW_API UFlowNodeBase bool BuildMessage(FString& Message) const; #endif }; + +// Templates & inline implementations: + +template +EFlowDataPinResolveResult UFlowNodeBase::TryResolveDataPinValue(const FName& PinName, typename TFlowPinType::ValueType& OutValue, EFlowSingleFromArray SingleFromArray /*= EFlowSingleFromArray::LastValue*/) const +{ + const FFlowDataPinResult DataPinResult = TryResolveDataPin(PinName); + return FlowPinType::TryExtractValue(DataPinResult, OutValue, SingleFromArray); +} + +template +EFlowDataPinResolveResult UFlowNodeBase::TryResolveDataPinValues(const FName& PinName, TArray& OutValues) const +{ + const FFlowDataPinResult DataPinResult = TryResolveDataPin(PinName); + return FlowPinType::TryExtractValues(DataPinResult, OutValues); +} + +template +EFlowDataPinResolveResult UFlowNodeBase::TryResolveDataPinValue(const FName& PinName, FName& OutEnumValue, UEnum*& OutEnumClass, EFlowSingleFromArray SingleFromArray /*= EFlowSingleFromArray::LastValue*/) const +{ + const FFlowDataPinResult DataPinResult = TryResolveDataPin(PinName); + if (!FlowPinType::IsSuccess(DataPinResult.Result)) + { + return DataPinResult.Result; + } + + return FlowPinType::TryExtractValue(DataPinResult, OutEnumValue, OutEnumClass, SingleFromArray); +} + +template +EFlowDataPinResolveResult UFlowNodeBase::TryResolveDataPinValues(const FName& PinName, TArray& OutEnumValues, UEnum*& OutEnumClass) const +{ + const FFlowDataPinResult DataPinResult = TryResolveDataPin(PinName); + if (!FlowPinType::IsSuccess(DataPinResult.Result)) + { + return DataPinResult.Result; + } + + return FlowPinType::TryExtractValues(DataPinResult, OutEnumValues, OutEnumClass); +} + +template requires std::is_enum_v +EFlowDataPinResolveResult UFlowNodeBase::TryResolveDataPinValue(const FName& PinName, TEnumType& OutValue, EFlowSingleFromArray SingleFromArray /*= EFlowSingleFromArray::LastValue*/) const +{ + const FFlowDataPinResult DataPinResult = TryResolveDataPin(PinName); + return FlowPinType::TryExtractValue(DataPinResult, OutValue, SingleFromArray); +} + +template requires std::is_enum_v +EFlowDataPinResolveResult UFlowNodeBase::TryResolveDataPinValues(const FName& PinName, TArray& OutValues) const +{ + const FFlowDataPinResult DataPinResult = TryResolveDataPin(PinName); + return FlowPinType::TryExtractValues(DataPinResult, OutValues); +} diff --git a/Source/Flow/Public/Nodes/FlowPin.h b/Source/Flow/Public/Nodes/FlowPin.h index 94d733b02..465a2cf14 100644 --- a/Source/Flow/Public/Nodes/FlowPin.h +++ b/Source/Flow/Public/Nodes/FlowPin.h @@ -3,9 +3,12 @@ #pragma once #include "Types/FlowPinEnums.h" +#include "Types/FlowPinTypeName.h" #include "Templates/SubclassOf.h" #include "UObject/ObjectMacros.h" +#include "Types/FlowPinTypeNamesStandard.h" +#include "EdGraph/EdGraphPin.h" #include "FlowPin.generated.h" @@ -13,6 +16,7 @@ class UEnum; class UClass; class UObject; class IPropertyHandle; +struct FFlowPinType; USTRUCT(BlueprintType, meta = (HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStruct", HasNativeBreak = "/Script/Flow.FlowDataPinBlueprintLibrary.BreakStruct")) struct FLOW_API FFlowPin @@ -20,149 +24,140 @@ struct FLOW_API FFlowPin GENERATED_BODY() // A logical name, used during execution of pin - UPROPERTY(EditDefaultsOnly, Category = FlowPin) + UPROPERTY(EditDefaultsOnly, Category = DataPins) FName PinName; // An optional Display Name, you can use it to override PinName without the need to update graph connections - UPROPERTY(EditDefaultsOnly, Category = FlowPin) + UPROPERTY(EditDefaultsOnly, Category = DataPins) FText PinFriendlyName; - UPROPERTY(EditDefaultsOnly, Category = FlowPin) + UPROPERTY(EditDefaultsOnly, Category = DataPins) FString PinToolTip; -protected: // PinType (implies PinCategory) - UPROPERTY(EditAnywhere, Category = FlowPin) - EFlowPinType PinType = EFlowPinType::Exec; + UPROPERTY(Meta = (DeprecatedProperty, DeprecationMessage = "Use PinTypeName instead")) + EFlowPinType PinType = EFlowPinType::Invalid; - // Sub-category object - // (used to identify the struct or class type for some PinCategories, see IsSubtypeSupportedPinCategory) + // Only supporting None (Single) or Array for now(tm) for data pins via EFlowMultiType UPROPERTY() + EPinContainerType ContainerType = EPinContainerType::None; + +protected: + UPROPERTY(EditDefaultsOnly, Category = DataPins) + FFlowPinTypeName PinTypeName = FFlowPinTypeName(FFlowPinTypeNamesStandard::PinTypeNameExec); + + // Sub-category object + // (used to identify the struct or class type for some PinCategories) + UPROPERTY(VisibleAnywhere, Category = DataPins) TWeakObjectPtr PinSubCategoryObject; #if WITH_EDITORONLY_DATA // Filter for limiting the compatible classes for this data pin. - // This property is editor-only, but it is automatically copied into PinSubCategoryObject if the PinType matches (for runtime use). - UPROPERTY(EditAnywhere, Category = FlowPin, meta = (EditCondition = "PinType == EFlowPinType::Class", EditConditionHides)) + // This property is editor-only, but it is automatically copied into PinSubCategoryObject if the PinTypeName matches (for runtime use). + UPROPERTY(EditAnywhere, Category = DataPins, meta = (EditCondition = "PinTypeName == Class", EditConditionHides)) TSubclassOf SubCategoryClassFilter = UClass::StaticClass(); // Filter for limiting the compatible object types for this data pin. - // This property is editor-only, but it is automatically copied into PinSubCategoryObject if the PinType matches (for runtime use). - UPROPERTY(EditAnywhere, Category = FlowPin, meta = (EditCondition = "PinType == EFlowPinType::Object", EditConditionHides)) + // This property is editor-only, but it is automatically copied into PinSubCategoryObject if the PinTypeName matches (for runtime use). + UPROPERTY(EditAnywhere, Category = DataPins, meta = (EditCondition = "PinTypeName == Object", EditConditionHides)) TSubclassOf SubCategoryObjectFilter = UObject::StaticClass(); - // Configuration option for setting the EnumClass to a Blueprint Enum + // Configuration option for setting the EnumClass to a Blueprint Enum // (C++ enums must bind by name using SubCategoryEnumName, due to a limitation with UE's UEnum discovery). // This property is editor-only, but it is automatically copied into PinSubCategoryObject if the PinType matches (for runtime use). - UPROPERTY(EditAnywhere, Category = FlowPin, meta = (EditCondition = "PinType == EFlowPinType::Enum", EditConditionHides)) + UPROPERTY(EditAnywhere, Category = DataPins, meta = (EditCondition = "PinTypeName == Enum", EditConditionHides)) TObjectPtr SubCategoryEnumClass = nullptr; // name of enum defined in c++ code, will take priority over asset from EnumType property - // (this is a work-around because EnumClass cannot find C++ Enums, - // so you need to type the name of the enum in here, manually) + // (this is a work-around because EnumClass cannot find C++ Enums, + // so you need to type the name of the enum in here, manually) // See also: FFlowPin::PostEditChangedEnumName() - UPROPERTY(EditAnywhere, Category = FlowPin, meta = (EditCondition = "PinType == EFlowPinType::Enum", EditConditionHides)) + UPROPERTY(EditAnywhere, Category = DataPins, meta = (EditCondition = "PinTypeName == Enum", EditConditionHides)) FString SubCategoryEnumName; -#endif // WITH_EDITORONLY_DATA +#endif public: - // PinCategory aliases for (a subset of) those defined in UEdGraphSchema_K2 - static inline FName PC_Exec = TEXT("exec"); - static inline FName PC_Boolean = TEXT("bool"); - static inline FName PC_Byte = TEXT("byte"); - static inline FName PC_Class = TEXT("class"); - static inline FName PC_Int = TEXT("int"); - static inline FName PC_Int64 = TEXT("int64"); - static inline FName PC_Float = TEXT("float"); - static inline FName PC_Double = TEXT("double"); - static inline FName PC_Name = TEXT("name"); - static inline FName PC_Object = TEXT("object"); - static inline FName PC_String = TEXT("string"); - static inline FName PC_Text = TEXT("text"); - static inline FName PC_Struct = TEXT("struct"); - static inline FName PC_Enum = TEXT("enum"); - - static inline FName AnyPinName = TEXT("AnyPinName"); - FFlowPin() : PinName(NAME_None) { } - FFlowPin(const FName& InPinName) + explicit FFlowPin(const FName& InPinName) : PinName(InPinName) { } - FFlowPin(const FString& InPinName) + explicit FFlowPin(const FString& InPinName) : PinName(*InPinName) { } - FFlowPin(const FText& InPinName) + explicit FFlowPin(const FText& InPinName) : PinName(*InPinName.ToString()) { } - FFlowPin(const TCHAR* InPinName) + explicit FFlowPin(const TCHAR* InPinName) : PinName(FName(InPinName)) { } - FFlowPin(const uint8& InPinName) + explicit FFlowPin(const uint8& InPinName) : PinName(FName(*FString::FromInt(InPinName))) { } - FFlowPin(const int32& InPinName) + explicit FFlowPin(const int32& InPinName) : PinName(FName(*FString::FromInt(InPinName))) { } - FFlowPin(const FStringView InPinName, const FText& InPinFriendlyName) + explicit FFlowPin(const FStringView InPinName, const FText& InPinFriendlyName) : PinName(InPinName) , PinFriendlyName(InPinFriendlyName) { } - FFlowPin(const FStringView InPinName, const FString& InPinTooltip) + explicit FFlowPin(const FStringView InPinName, const FString& InPinTooltip) : PinName(InPinName) , PinToolTip(InPinTooltip) { } - FFlowPin(const FStringView InPinName, const FText& InPinFriendlyName, const FString& InPinTooltip) + explicit FFlowPin(const FStringView InPinName, const FText& InPinFriendlyName, const FString& InPinTooltip) : PinName(InPinName) , PinFriendlyName(InPinFriendlyName) , PinToolTip(InPinTooltip) { } - FFlowPin(const FName& InPinName, const FText& InPinFriendlyName) + explicit FFlowPin(const FName& InPinName, const FText& InPinFriendlyName) : PinName(InPinName) , PinFriendlyName(InPinFriendlyName) { } - FFlowPin(const FName& InPinName, const FText& InPinFriendlyName, const FString& InPinTooltip) + explicit FFlowPin(const FName& InPinName, const FText& InPinFriendlyName, const FString& InPinTooltip) : PinName(InPinName) , PinFriendlyName(InPinFriendlyName) , PinToolTip(InPinTooltip) { } - FFlowPin(const FName& InPinName, const FText& InPinFriendlyName, EFlowPinType InFlowPinType, UObject* SubCategoryObject = nullptr) + explicit FFlowPin(const FName& InPinName, const FText& InPinFriendlyName, const FFlowPinTypeName& InTypeName, UObject* OptionalSubCategoryObject = nullptr) : PinName(InPinName) , PinFriendlyName(InPinFriendlyName) { - SetPinType(InFlowPinType, SubCategoryObject); + SetPinTypeName(InTypeName); + SetPinSubCategoryObject(OptionalSubCategoryObject); } - FFlowPin(const FName& InPinName, EFlowPinType InFlowPinType, UObject* SubCategoryObject = nullptr) + explicit FFlowPin(const FName& InPinName, const FFlowPinTypeName& InTypeName, UObject* OptionalSubCategoryObject = nullptr) : PinName(InPinName) { - SetPinType(InFlowPinType, SubCategoryObject); + SetPinTypeName(InTypeName); + SetPinSubCategoryObject(OptionalSubCategoryObject); } FORCEINLINE bool IsValid() const @@ -190,6 +185,18 @@ struct FLOW_API FFlowPin return PinName != Other; } + bool DeepIsEqual(const FFlowPin& Other) const + { + // Do a deep pin match (not a simple name-only match), to check if the pins are exactly equal + return + PinName == Other.PinName && + PinFriendlyName.EqualTo(Other.PinFriendlyName) && + PinToolTip == Other.PinToolTip && + ContainerType == Other.ContainerType && + PinTypeName == Other.PinTypeName && + PinSubCategoryObject == Other.PinSubCategoryObject; + } + friend uint32 GetTypeHash(const FFlowPin& FlowPin) { return GetTypeHash(FlowPin.PinName); @@ -206,46 +213,23 @@ struct FLOW_API FFlowPin static bool ValidateEnum(const UEnum& EnumType); #endif // WITH_EDITOR - void SetPinType(const EFlowPinType InFlowPinType, UObject* SubCategoryObject = nullptr); - EFlowPinType GetPinType() const { return PinType; } - static const FName& GetPinCategoryFromPinType(EFlowPinType FlowPinType); - static const TArray& GetFlowPinTypeEnumValuesWithoutSpaces(); + void SetPinTypeName(const FFlowPinTypeName& InTypeName); + const FFlowPinTypeName& GetPinTypeName() const { return PinTypeName; } + const FFlowPinType* ResolveFlowPinType() const; + void SetPinSubCategoryObject(UObject* Object) { PinSubCategoryObject = Object; } + static FFlowPinTypeName GetPinTypeNameForLegacyPinType(EFlowPinType PinType); + +#if WITH_EDITOR + FEdGraphPinType BuildEdGraphPinType() const; +#endif const TWeakObjectPtr& GetPinSubCategoryObject() const { return PinSubCategoryObject; } - static bool ArePinArraysMatchingNamesAndTypes(const TArray& Left, const TArray& Right); - static bool DoPinsMatchNamesAndTypes(const FFlowPin& LeftPin, const FFlowPin& RightPin) - { - return (LeftPin.PinName == RightPin.PinName && LeftPin.PinType == RightPin.PinType && LeftPin.PinSubCategoryObject == RightPin.PinSubCategoryObject); - } + FORCEINLINE_DEBUGGABLE static bool DeepArePinArraysMatching(const TArray& Left, const TArray& Right); // FFlowPin instance signatures for "trait" functions - FORCEINLINE bool IsExecPin() const { return PinType == EFlowPinType::Exec; } - FORCEINLINE bool IsDataPin() const { return PinType != EFlowPinType::Exec; } - // -- - - // PinCategory "trait" functions: - FORCEINLINE static bool IsExecPinCategory(const FName& PC) { return PC == PC_Exec; } - FORCEINLINE static bool IsDataPinCategory(const FName& PC) { return PC != PC_Exec; } - FORCEINLINE static bool IsBoolPinCategory(const FName& PC) { return PC == PC_Boolean; } - FORCEINLINE static bool IsIntPinCategory(const FName& PC) { return PC == PC_Byte || PC == PC_Int || PC == PC_Int64; } - FORCEINLINE static bool IsFloatPinCategory(const FName& PC) { return PC == PC_Double || PC == PC_Float; } - FORCEINLINE static bool IsEnumPinCategory(const FName& PC) { return PC == PC_Enum; } - FORCEINLINE static bool IsTextPinCategory(const FName& PC) { return PC == PC_Name || PC == PC_String || PC == PC_Text; } - FORCEINLINE static bool IsObjectPinCategory(const FName& PC) { return PC == PC_Object; } - FORCEINLINE static bool IsClassPinCategory(const FName& PC) { return PC == PC_Class; } - FORCEINLINE static bool IsStructPinCategory(const FName& PC) { return PC == PC_Struct; } - // -- - - // IsConvertable trait functions: - FORCEINLINE static bool IsConvertableToBoolPinCategory(const FName& PC) { return IsBoolPinCategory(PC); } - FORCEINLINE static bool IsConvertableToIntPinCategory(const FName& PC) { return IsIntPinCategory(PC); } - FORCEINLINE static bool IsConvertableToFloatPinCategory(const FName& PC) { return IsFloatPinCategory(PC); } - FORCEINLINE static bool IsConvertableToEnumPinCategory(const FName& PC) { return IsEnumPinCategory(PC); } - FORCEINLINE static bool IsConvertableToTextPinCategory(const FName& PC) { return IsTextPinCategory(PC); } - FORCEINLINE static bool IsConvertableToObjectPinCategory(const FName& PC) { return IsObjectPinCategory(PC); } - FORCEINLINE static bool IsConvertableToClassPinCategory(const FName& PC) { return IsClassPinCategory(PC); } - FORCEINLINE static bool IsConvertableToStructPinCategory(const FName& PC) { return IsStructPinCategory(PC); } + bool IsExecPin() const; + static bool IsExecPinCategory(const FName& PC); // -- // Metadata keys for properties that bind and auto-generate Data Pins: @@ -292,12 +276,26 @@ struct FLOW_API FFlowPin protected: void TrySetStructSubCategoryObjectFromPinType(); +}; -private: +// Inline implementations +bool FFlowPin::DeepArePinArraysMatching(const TArray& Left, const TArray& Right) +{ + if (Left.Num() != Right.Num()) + { + return false; + } - // Cached EFlowPinType values as FName, de-spaced, so they can be compared with FlowPinType metadata strings - static TArray FlowPinTypeEnumValuesWithoutSpaces; -}; + for (int32 Index = 0; Index < Left.Num(); ++Index) + { + if (!Left[Index].DeepIsEqual(Right[Index])) + { + return false; + } + } + + return true; +} USTRUCT() struct FLOW_API FFlowPinHandle diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_CustomInput.h b/Source/Flow/Public/Nodes/Graph/FlowNode_CustomInput.h index 549a0a0d8..38bc168e4 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_CustomInput.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_CustomInput.h @@ -23,6 +23,6 @@ class FLOW_API UFlowNode_CustomInput : public UFlowNode_CustomEventBase #if WITH_EDITOR public: - virtual FText GetNodeTitle() const override; + virtual FText K2_GetNodeTitle_Implementation() const override; #endif }; diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_CustomOutput.h b/Source/Flow/Public/Nodes/Graph/FlowNode_CustomOutput.h index b0e93d422..4d283fb31 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_CustomOutput.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_CustomOutput.h @@ -18,6 +18,6 @@ class FLOW_API UFlowNode_CustomOutput final : public UFlowNode_CustomEventBase virtual void ExecuteInput(const FName& PinName) override; #if WITH_EDITOR - virtual FText GetNodeTitle() const override; + virtual FText K2_GetNodeTitle_Implementation() const override; #endif }; diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h b/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h index 0573362bc..2d5d21bb8 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h @@ -2,9 +2,9 @@ #pragma once -#include "Interfaces/FlowDataPinGeneratorNodeInterface.h" +#include "Interfaces/FlowNamedPropertiesSupplierInterface.h" #include "Nodes/FlowNode.h" -#include "Types/FlowDataPinProperties.h" +#include "Types/FlowNamedDataPinProperty.h" #include "FlowNode_DefineProperties.generated.h" @@ -12,7 +12,9 @@ * FlowNode to define data pin property literals for use connecting to data pin inputs in a flow graph */ UCLASS(Blueprintable, meta = (DisplayName = "Define Properties")) -class FLOW_API UFlowNode_DefineProperties : public UFlowNode, public IFlowDataPinGeneratorNodeInterface +class FLOW_API UFlowNode_DefineProperties + : public UFlowNode + , public IFlowNamedPropertiesSupplierInterface { GENERATED_UCLASS_BODY() @@ -23,6 +25,8 @@ class FLOW_API UFlowNode_DefineProperties : public UFlowNode, public IFlowDataPi TArray NamedProperties; public: + virtual void PostLoad() override; + #if WITH_EDITOR // IFlowContextPinSupplierInterface virtual bool SupportsContextPins() const override { return Super::SupportsContextPins() || !NamedProperties.IsEmpty(); } @@ -32,17 +36,21 @@ class FLOW_API UFlowNode_DefineProperties : public UFlowNode, public IFlowDataPi virtual void PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent) override; // -- - // IFlowDataPinGeneratorNodeInterface - virtual void AutoGenerateDataPins(TMap& PinNameToBoundPropertyMap, TArray& InputDataPins, TArray& OutputDataPins) const override; + // IFlowDataPinGeneratorInterface + virtual void AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const override; + // -- + + // IFlowNamedPropertiesSupplierInterface + virtual TArray& GetMutableNamedProperties() override { return NamedProperties; } // -- #endif bool TryFormatTextWithNamedPropertiesAsParameters(const FText& FormatText, FText& OutFormattedText) const; protected: - virtual bool TryFindPropertyByRemappedPinName( - const FName& RemappedPinName, + virtual bool TryFindPropertyByPinName( + const UObject& PropertyOwnerObject, + const FName& PinName, const FProperty*& OutFoundProperty, - TInstancedStruct& OutFoundInstancedStruct, - EFlowDataPinResolveResult& InOutResult) const override; + TInstancedStruct& OutFoundInstancedStruct) const override; }; diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_FormatText.h b/Source/Flow/Public/Nodes/Graph/FlowNode_FormatText.h index 02d5addea..bc699ddc6 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_FormatText.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_FormatText.h @@ -33,9 +33,7 @@ class FLOW_API UFlowNode_FormatText : public UFlowNode_DefineProperties public: // IFlowDataPinValueSupplierInterface - virtual FFlowDataPinResult_Name TrySupplyDataPinAsName_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_String TrySupplyDataPinAsString_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Text TrySupplyDataPinAsText_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult TrySupplyDataPin_Implementation(FName PinName) const override; // -- static const FName OUTPIN_TextOutput; diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_Start.h b/Source/Flow/Public/Nodes/Graph/FlowNode_Start.h index d47fd6343..8b31a392b 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_Start.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_Start.h @@ -39,24 +39,7 @@ class FLOW_API UFlowNode_Start #endif // -- - // Must implement TrySupplyDataAs... for every EFlowPinType - FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); - // IFlowDataPinValueSupplierInterface - virtual FFlowDataPinResult_Bool TrySupplyDataPinAsBool_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Int TrySupplyDataPinAsInt_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Float TrySupplyDataPinAsFloat_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Name TrySupplyDataPinAsName_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_String TrySupplyDataPinAsString_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Text TrySupplyDataPinAsText_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Enum TrySupplyDataPinAsEnum_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Vector TrySupplyDataPinAsVector_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Rotator TrySupplyDataPinAsRotator_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Transform TrySupplyDataPinAsTransform_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_GameplayTag TrySupplyDataPinAsGameplayTag_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_GameplayTagContainer TrySupplyDataPinAsGameplayTagContainer_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_InstancedStruct TrySupplyDataPinAsInstancedStruct_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Object TrySupplyDataPinAsObject_Implementation(const FName& PinName) const override; - virtual FFlowDataPinResult_Class TrySupplyDataPinAsClass_Implementation(const FName& PinName) const override; + virtual FFlowDataPinResult TrySupplyDataPin_Implementation(FName PinName) const override; // -- }; diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h b/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h index 5f7f89f0d..572a42aa3 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h @@ -3,7 +3,6 @@ #pragma once #include "Nodes/FlowNode.h" -#include "Interfaces/FlowDataPinGeneratorNodeInterface.h" #include "FlowNode_SubGraph.generated.h" @@ -11,11 +10,11 @@ * Creates instance of provided Flow Asset and starts its execution */ UCLASS(NotBlueprintable, meta = (DisplayName = "Sub Graph")) -class FLOW_API UFlowNode_SubGraph : public UFlowNode, public IFlowDataPinGeneratorNodeInterface +class FLOW_API UFlowNode_SubGraph : public UFlowNode { GENERATED_UCLASS_BODY() -public: +public: friend class UFlowAsset; friend class FFlowNode_SubGraphDetails; friend class UFlowSubsystem; @@ -27,6 +26,8 @@ class FLOW_API UFlowNode_SubGraph : public UFlowNode, public IFlowDataPinGenerat UPROPERTY(EditAnywhere, Category = "Graph") TSoftObjectPtr Asset; + // TODO (gtaylor) Create FlowAssetParams option for the Subgraph & reconcile with connected input pins' values + /* * Allow to create instance of the same Flow Asset as the asset containing this node * Enabling it may cause an infinite loop, if graph would keep creating copies of itself @@ -74,7 +75,7 @@ class FLOW_API UFlowNode_SubGraph : public UFlowNode, public IFlowDataPinGenerat virtual TArray GetContextOutputs() const override; // -- - virtual FText GetNodeTitle() const override; + virtual FText K2_GetNodeTitle_Implementation() const override; virtual FString GetNodeDescription() const override; virtual UObject* GetAssetToEdit() override; virtual EDataValidationResult ValidateNode() override; @@ -89,8 +90,8 @@ class FLOW_API UFlowNode_SubGraph : public UFlowNode, public IFlowDataPinGenerat virtual bool CanSupplyDataPinValues_Implementation() const override; // -- - // IFlowDataPinGeneratorNodeInterface - virtual void AutoGenerateDataPins(TMap& PinNameToBoundPropertyMap, TArray& InputDataPins, TArray& OutputDataPins) const override; + // IFlowDataPinGeneratorInterface + virtual void AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const override; // -- private: diff --git a/Source/Flow/Public/Types/FlowArray.h b/Source/Flow/Public/Types/FlowArray.h index 7940a6db8..b5817ca53 100644 --- a/Source/Flow/Public/Types/FlowArray.h +++ b/Source/Flow/Public/Types/FlowArray.h @@ -70,4 +70,21 @@ namespace FlowArray return false; } + + template + FString FormatArrayString(const TArray& Values, TFunctionRef Formatter, const FString& Separator = TEXT(",")) + { + FString ValueString; + for (const T& Value : Values) + { + if (!ValueString.IsEmpty()) + { + ValueString += Separator; + } + + ValueString += Formatter(Value); + } + + return ValueString; + } } \ No newline at end of file diff --git a/Source/Flow/Public/Types/FlowAutoDataPinsWorkingData.h b/Source/Flow/Public/Types/FlowAutoDataPinsWorkingData.h new file mode 100644 index 000000000..1a5f41bd7 --- /dev/null +++ b/Source/Flow/Public/Types/FlowAutoDataPinsWorkingData.h @@ -0,0 +1,28 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Nodes/FlowPin.h" + +// Working Data struct for the UFlowDataPinGeneratorNodeInterface::AutoGenerateDataPins function +struct FFlowAutoDataPinsWorkingData +{ + FFlowAutoDataPinsWorkingData(const TArray& InputPinsPrev, const TArray& OutputPinsPrev) + : AutoInputDataPinsPrev(InputPinsPrev) + , AutoOutputDataPinsPrev(OutputPinsPrev) + { } + +#if WITH_EDITOR + FLOW_API void AddFlowDataPinsForClassProperties(const UObject& ObjectContainer); + FLOW_API void AddFlowDataPinForProperty(const FProperty* Property, const UObject& ObjectContainer); + + FLOW_API bool DidAutoInputDataPinsChange() const; + FLOW_API bool DidAutoOutputDataPinsChange() const; +#endif + + const TArray& AutoInputDataPinsPrev; + const TArray& AutoOutputDataPinsPrev; + + TArray AutoInputDataPinsNext; + TArray AutoOutputDataPinsNext; +}; diff --git a/Source/Flow/Public/Types/FlowDataPinBlueprintLibrary.h b/Source/Flow/Public/Types/FlowDataPinBlueprintLibrary.h index 7183d9146..3e25e8572 100644 --- a/Source/Flow/Public/Types/FlowDataPinBlueprintLibrary.h +++ b/Source/Flow/Public/Types/FlowDataPinBlueprintLibrary.h @@ -4,246 +4,819 @@ #include "Kismet/BlueprintFunctionLibrary.h" -#include "FlowDataPinProperties.h" +#include "FlowDataPinValuesStandard.h" #include "FlowDataPinResults.h" +#include "FlowPinTypeTemplates.h" #include "FlowDataPinBlueprintLibrary.generated.h" -// Auto-cast operators for blueprint to their inner types +struct FFlowDataPinValue; + +// Auto‑cast operators for blueprint to their inner types UCLASS() class UFlowDataPinBlueprintLibrary : public UBlueprintFunctionLibrary { GENERATED_BODY() +private: + static void ResolveAndExtract_Impl( + UFlowNodeBase* Target, + FName PinName, + EFlowDataPinResolveSimpleResult& SimpleResult, + EFlowDataPinResolveResult& ResultEnum, + auto&& ExtractLambda); + public: + // ---------- Pin construction helpers ---------- + UFUNCTION(BlueprintPure, Category = FlowPin, Meta = (BlueprintThreadSafe, DisplayName = "Make Flow Pin")) - static - UPARAM(DisplayName = "Flow Pin") FFlowPin - MakeStruct( - FName PinName, - FText PinFriendlyName, - FString PinToolTip) + static UPARAM(DisplayName = "Flow Pin") FFlowPin MakeStruct(FName PinName, FText PinFriendlyName, FString PinToolTip) { return FFlowPin(PinName, PinFriendlyName, PinToolTip); } UFUNCTION(BlueprintPure, Category = FlowPin, Meta = (BlueprintThreadSafe, DisplayName = "Break Flow Pin")) - static void - BreakStruct( - UPARAM(DisplayName = "Flow Pin") FFlowPin Ref, - FName& OutPinName, - FText& OutPinFriendlyName, - FString& OutPinToolTip) + static void BreakStruct(UPARAM(DisplayName = "Flow Pin") FFlowPin Ref, FName& OutPinName, FText& OutPinFriendlyName, FString& OutPinToolTip) { OutPinName = Ref.PinName; OutPinFriendlyName = Ref.PinFriendlyName; OutPinToolTip = Ref.PinToolTip; } - // Recommend implementing AutoConvert_FlowDataPinProperty... for every EFlowPinType - FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); + // ---------- Resolve As ... functions ---------- + // Full-featured resolve nodes with execution pins for detailed error handling. + // Use these when you need to branch on Success/Failure/Coercion or inspect the exact resolve result. + + // Resolve a Bool DataPin Value to a single bool. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Bool", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsBool(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Bool& BoolValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, bool& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); + + // Resolve a Bool DataPin Value to a bool array. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Bool Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsBoolArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Bool& BoolValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); + + // Resolve an Int DataPin Value to a single int32. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Int", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsInt(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Int& IntValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, int32& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); + + // Resolve an Int DataPin Value to an int32 array. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Int Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsIntArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Int& IntValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); + + // Resolve an Int64 DataPin Value to a single int64. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Int64", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsInt64(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Int64& Int64Value, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, int64& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); + + // Resolve an Int64 DataPin Value to an int64 array. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Int64 Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsInt64Array(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Int64& Int64Value, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); + + // Resolve a Float DataPin Value to a single float. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Float", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsFloat(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Float& FloatValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, float& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); + + // Resolve a Float DataPin Value to a float array. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Float Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsFloatArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Float& FloatValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); + + // Resolve a Double DataPin Value to a single double. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Double", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsDouble(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Double& DoubleValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, double& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); + + // Resolve a Double DataPin Value to a double array. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Double Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsDoubleArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Double& DoubleValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); + + // Resolve a Name DataPin Value to a single FName. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Name", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsName(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Name& NameValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, FName& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); + + // Resolve a Name DataPin Value to an FName array. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Name Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsNameArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Name& NameValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); + + // Resolve a String DataPin Value to a single FString. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As String", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsString(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_String& StringValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, FString& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); + + // Resolve a String DataPin Value to an FString array. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As String Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsStringArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_String& StringValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); + + // Resolve a Text DataPin Value to a single FText. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Text", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsText(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Text& TextValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, FText& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); + + // Resolve a Text DataPin Value to an FText array. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Text Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsTextArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Text& TextValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); + + // Resolve an Enum DataPin Value to a single uint8. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Enum", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsEnum(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Enum& EnumValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, uint8& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); + + // Resolve an Enum DataPin Value to a uint8 array. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Enum Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsEnumArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Enum& EnumValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); + + // Resolve a Vector DataPin Value to a single FVector. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Vector", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsVector(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Vector& VectorValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, FVector& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); + + // Resolve a Vector DataPin Value to an FVector array. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Vector Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsVectorArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Vector& VectorValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); + + // Resolve a Rotator DataPin Value to a single FRotator. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Rotator", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsRotator(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Rotator& RotatorValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, FRotator& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); + + // Resolve a Rotator DataPin Value to an FRotator array. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Rotator Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsRotatorArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Rotator& RotatorValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); + + // Resolve a Transform DataPin Value to a single FTransform. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Transform", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsTransform(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Transform& TransformValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, FTransform& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); + + // Resolve a Transform DataPin Value to an FTransform array. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Transform Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsTransformArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Transform& TransformValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); + + // Resolve a GameplayTag DataPin Value to a single FGameplayTag. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As GameplayTag", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsGameplayTag(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_GameplayTag& GameplayTagValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, FGameplayTag& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); + + // Resolve a GameplayTag DataPin Value to an FGameplayTag array. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As GameplayTag Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsGameplayTagArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_GameplayTag& GameplayTagValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); + + // Resolve a GameplayTagContainer DataPin Value (scalar only). + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As GameplayTagContainer", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsGameplayTagContainer(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_GameplayTagContainer& GameplayTagContainerValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, FGameplayTagContainer& Value); + + // Resolve an InstancedStruct DataPin Value to a single FInstancedStruct. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As InstancedStruct", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsInstancedStruct(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_InstancedStruct& InstancedStructValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, FInstancedStruct& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); + + // Resolve an InstancedStruct DataPin Value to an FInstancedStruct array. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As InstancedStruct Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsInstancedStructArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_InstancedStruct& InstancedStructValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); + + // Resolve an Object DataPin Value to a single UObject*. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Object", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsObject(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Object& ObjectValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, UObject*& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); + + // Resolve an Object DataPin Value to a UObject* array. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Object Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsObjectArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Object& ObjectValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); + + // Resolve a Class DataPin Value to a single UClass*. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Class", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsClass(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Class& ClassValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, UClass*& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); + + // Resolve a Class DataPin Value to a UClass* array. + UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Class Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) + static void ResolveAsClassArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Class& ClassValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); + + // ---------- Auto-Resolve As ... functions ---------- + // Easy-resolve convenience nodes. On failure, logs an error and returns a safe default (false/empty/null). + // Use these for fast, fire-and-forget resolving when you don't expect failures. + + // Easy Resolve a Bool DataPin Value to a single bool (last value if array). Logs error on failure. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Bool", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static bool AutoConvert_TryResolveAsBool(UPARAM(Ref) const FFlowDataPinValue_Bool& BoolValue, const UFlowNodeBase* Target); + + // Easy Resolve a Bool DataPin Value to a bool array. Logs error on failure and returns empty array. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Bool Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static TArray AutoConvert_TryResolveAsBoolArray(UPARAM(Ref) const FFlowDataPinValue_Bool& BoolValue, const UFlowNodeBase* Target); + + // Easy Resolve an Int DataPin Value to a single int32. Logs error on failure. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Int", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static int32 AutoConvert_TryResolveAsInt(UPARAM(Ref) const FFlowDataPinValue_Int& IntValue, const UFlowNodeBase* Target); + + // Easy Resolve an Int DataPin Value to an int32 array. Logs error on failure and returns empty array. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Int Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static TArray AutoConvert_TryResolveAsIntArray(UPARAM(Ref) const FFlowDataPinValue_Int& IntValue, const UFlowNodeBase* Target); + + // Easy Resolve an Int64 DataPin Value to a single int64. Logs error on failure. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Int64", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static int64 AutoConvert_TryResolveAsInt64(UPARAM(Ref) const FFlowDataPinValue_Int64& Int64Value, const UFlowNodeBase* Target); + + // Easy Resolve an Int64 DataPin Value to an int64 array. Logs error on failure and returns empty array. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Int64 Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static TArray AutoConvert_TryResolveAsInt64Array(UPARAM(Ref) const FFlowDataPinValue_Int64& Int64Value, const UFlowNodeBase* Target); + + // Easy Resolve a Float DataPin Value to a single float. Logs error on failure. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Float", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static float AutoConvert_TryResolveAsFloat(UPARAM(Ref) const FFlowDataPinValue_Float& FloatValue, const UFlowNodeBase* Target); + + // Easy Resolve a Float DataPin Value to a float array. Logs error on failure and returns empty array. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Float Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static TArray AutoConvert_TryResolveAsFloatArray(UPARAM(Ref) const FFlowDataPinValue_Float& FloatValue, const UFlowNodeBase* Target); + + // Easy Resolve a Double DataPin Value to a single double. Logs error on failure. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Double", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static double AutoConvert_TryResolveAsDouble(UPARAM(Ref) const FFlowDataPinValue_Double& DoubleValue, const UFlowNodeBase* Target); + + // Easy Resolve a Double DataPin Value to a double array. Logs error on failure and returns empty array. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Double Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static TArray AutoConvert_TryResolveAsDoubleArray(UPARAM(Ref) const FFlowDataPinValue_Double& DoubleValue, const UFlowNodeBase* Target); + + // Easy Resolve a Name DataPin Value to a single FName. Logs error on failure. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Name", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static FName AutoConvert_TryResolveAsName(UPARAM(Ref) const FFlowDataPinValue_Name& NameValue, const UFlowNodeBase* Target); + + // Easy Resolve a Name DataPin Value to an FName array. Logs error on failure and returns empty array. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Name Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static TArray AutoConvert_TryResolveAsNameArray(UPARAM(Ref) const FFlowDataPinValue_Name& NameValue, const UFlowNodeBase* Target); + + // Easy Resolve a String DataPin Value to a single FString. Logs error on failure. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to String", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static FString AutoConvert_TryResolveAsString(UPARAM(Ref) const FFlowDataPinValue_String& StringValue, const UFlowNodeBase* Target); + + // Easy Resolve a String DataPin Value to an FString array. Logs error on failure and returns empty array. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to String Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static TArray AutoConvert_TryResolveAsStringArray(UPARAM(Ref) const FFlowDataPinValue_String& StringValue, const UFlowNodeBase* Target); + + // Easy Resolve a Text DataPin Value to a single FText. Logs error on failure. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Text", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static FText AutoConvert_TryResolveAsText(UPARAM(Ref) const FFlowDataPinValue_Text& TextValue, const UFlowNodeBase* Target); + + // Easy Resolve a Text DataPin Value to an FText array. Logs error on failure and returns empty array. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Text Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static TArray AutoConvert_TryResolveAsTextArray(UPARAM(Ref) const FFlowDataPinValue_Text& TextValue, const UFlowNodeBase* Target); + + // Easy Resolve an Enum DataPin Value to a single uint8. Logs error on failure. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Enum", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static uint8 AutoConvert_TryResolveAsEnum(UPARAM(Ref) const FFlowDataPinValue_Enum& EnumValue, const UFlowNodeBase* Target); + + // Easy Resolve an Enum DataPin Value to a uint8 array. Logs error on failure and returns empty array. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Enum Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static TArray AutoConvert_TryResolveAsEnumArray(UPARAM(Ref) const FFlowDataPinValue_Enum& EnumValue, const UFlowNodeBase* Target); + + // Easy Resolve a Vector DataPin Value to a single FVector. Logs error on failure. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Vector", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static FVector AutoConvert_TryResolveAsVector(UPARAM(Ref) const FFlowDataPinValue_Vector& VectorValue, const UFlowNodeBase* Target); + + // Easy Resolve a Vector DataPin Value to an FVector array. Logs error on failure and returns empty array. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Vector Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static TArray AutoConvert_TryResolveAsVectorArray(UPARAM(Ref) const FFlowDataPinValue_Vector& VectorValue, const UFlowNodeBase* Target); + + // Easy Resolve a Rotator DataPin Value to a single FRotator. Logs error on failure. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Rotator", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static FRotator AutoConvert_TryResolveAsRotator(UPARAM(Ref) const FFlowDataPinValue_Rotator& RotatorValue, const UFlowNodeBase* Target); + + // Easy Resolve a Rotator DataPin Value to an FRotator array. Logs error on failure and returns empty array. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Rotator Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static TArray AutoConvert_TryResolveAsRotatorArray(UPARAM(Ref) const FFlowDataPinValue_Rotator& RotatorValue, const UFlowNodeBase* Target); + + // Easy Resolve a Transform DataPin Value to a single FTransform. Logs error on failure. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Transform", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static FTransform AutoConvert_TryResolveAsTransform(UPARAM(Ref) const FFlowDataPinValue_Transform& TransformValue, const UFlowNodeBase* Target); + + // Easy Resolve a Transform DataPin Value to an FTransform array. Logs error on failure and returns empty array. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Transform Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static TArray AutoConvert_TryResolveAsTransformArray(UPARAM(Ref) const FFlowDataPinValue_Transform& TransformValue, const UFlowNodeBase* Target); + + // Easy Resolve a GameplayTag DataPin Value to a single FGameplayTag. Logs error on failure. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to GameplayTag", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static FGameplayTag AutoConvert_TryResolveAsGameplayTag(UPARAM(Ref) const FFlowDataPinValue_GameplayTag& GameplayTagValue, const UFlowNodeBase* Target); + + // Easy Resolve a GameplayTag DataPin Value to an FGameplayTag array. Logs error on failure and returns empty array. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to GameplayTag Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static TArray AutoConvert_TryResolveAsGameplayTagArray(UPARAM(Ref) const FFlowDataPinValue_GameplayTag& GameplayTagValue, const UFlowNodeBase* Target); + + // Easy Resolve a GameplayTagContainer DataPin Value (scalar only). Logs error on failure. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to GameplayTagContainer", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static FGameplayTagContainer AutoConvert_TryResolveAsGameplayTagContainer(UPARAM(Ref) const FFlowDataPinValue_GameplayTagContainer& GameplayTagContainerValue, const UFlowNodeBase* Target); + + // Easy Resolve an InstancedStruct DataPin Value to a single FInstancedStruct. Logs error on failure. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to InstancedStruct", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static FInstancedStruct AutoConvert_TryResolveAsInstancedStruct(UPARAM(Ref) const FFlowDataPinValue_InstancedStruct& InstancedStructValue, const UFlowNodeBase* Target); + + // Easy Resolve an InstancedStruct DataPin Value to an FInstancedStruct array. Logs error on failure and returns empty array. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to InstancedStruct Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static TArray AutoConvert_TryResolveAsInstancedStructArray(UPARAM(Ref) const FFlowDataPinValue_InstancedStruct& InstancedStructValue, const UFlowNodeBase* Target); + + // Easy Resolve an Object DataPin Value to a single UObject*. Logs error on failure. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Object", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static UObject* AutoConvert_TryResolveAsObject(UPARAM(Ref) const FFlowDataPinValue_Object& ObjectValue, const UFlowNodeBase* Target); + + // Easy Resolve an Object DataPin Value to a UObject* array. Logs error on failure and returns empty array. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Object Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static TArray AutoConvert_TryResolveAsObjectArray(UPARAM(Ref) const FFlowDataPinValue_Object& ObjectValue, const UFlowNodeBase* Target); + + // Easy Resolve a Class DataPin Value to a single UClass*. Logs error on failure. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Class", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static UClass* AutoConvert_TryResolveAsClass(UPARAM(Ref) const FFlowDataPinValue_Class& ClassValue, const UFlowNodeBase* Target); + + // Easy Resolve a Class DataPin Value to a UClass* array. Logs error on failure and returns empty array. + UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Class Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) + static TArray AutoConvert_TryResolveAsClassArray(UPARAM(Ref) const FFlowDataPinValue_Class& ClassValue, const UFlowNodeBase* Target); + + // ---------- Result → result enum converter ---------- + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Result", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static EFlowDataPinResolveResult AutoConvert_TryExtractResultEnum(const FFlowDataPinResult& DataPinResult) + { + return DataPinResult.Result; + } + + // ---------- Result → native value extractors ---------- + + // Bool + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to Bool", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static bool AutoConvert_TryExtractBool(const FFlowDataPinResult& DataPinResult); + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to Bool Array", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static TArray AutoConvert_TryExtractBoolArray(const FFlowDataPinResult& DataPinResult); + + // Int (int32) + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to Int", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static int32 AutoConvert_TryExtractInt(const FFlowDataPinResult& DataPinResult); + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to Int Array", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static TArray AutoConvert_TryExtractIntArray(const FFlowDataPinResult& DataPinResult); + + // Int64 + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to Int64", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static int64 AutoConvert_TryExtractInt64(const FFlowDataPinResult& DataPinResult); + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to Int64 Array", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static TArray AutoConvert_TryExtractInt64Array(const FFlowDataPinResult& DataPinResult); + + // Float + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to Float", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static float AutoConvert_TryExtractFloat(const FFlowDataPinResult& DataPinResult); + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to Float Array", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static TArray AutoConvert_TryExtractFloatArray(const FFlowDataPinResult& DataPinResult); + + // Double + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to Double", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static double AutoConvert_TryExtractDouble(const FFlowDataPinResult& DataPinResult); + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to Double Array", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static TArray AutoConvert_TryExtractDoubleArray(const FFlowDataPinResult& DataPinResult); + + // Name + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to Name", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FName AutoConvert_TryExtractName(const FFlowDataPinResult& DataPinResult); + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to Name Array", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static TArray AutoConvert_TryExtractNameArray(const FFlowDataPinResult& DataPinResult); + + // String + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to String", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FString AutoConvert_TryExtractString(const FFlowDataPinResult& DataPinResult); + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to String Array", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static TArray AutoConvert_TryExtractStringArray(const FFlowDataPinResult& DataPinResult); + + // Text + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to Text", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FText AutoConvert_TryExtractText(const FFlowDataPinResult& DataPinResult); + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to Text Array", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static TArray AutoConvert_TryExtractTextArray(const FFlowDataPinResult& DataPinResult); + + // Enum + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to Enum", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static uint8 AutoConvert_TryExtractEnum(const FFlowDataPinResult& DataPinResult); + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to Enum Array", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static TArray AutoConvert_TryExtractEnumArray(const FFlowDataPinResult& DataPinResult); + + // Vector + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to Vector", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FVector AutoConvert_TryExtractVector(const FFlowDataPinResult& DataPinResult); + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to Vector Array", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static TArray AutoConvert_TryExtractVectorArray(const FFlowDataPinResult& DataPinResult); + + // Rotator + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to Rotator", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FRotator AutoConvert_TryExtractRotator(const FFlowDataPinResult& DataPinResult); + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to Rotator Array", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static TArray AutoConvert_TryExtractRotatorArray(const FFlowDataPinResult& DataPinResult); + + // Transform + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to Transform", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FTransform AutoConvert_TryExtractTransform(const FFlowDataPinResult& DataPinResult); + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to Transform Array", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static TArray AutoConvert_TryExtractTransformArray(const FFlowDataPinResult& DataPinResult); + + // GameplayTag + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to GameplayTag", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FGameplayTag AutoConvert_TryExtractGameplayTag(const FFlowDataPinResult& DataPinResult); + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to GameplayTag Array", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static TArray AutoConvert_TryExtractGameplayTagArray(const FFlowDataPinResult& DataPinResult); + + // GameplayTagContainer + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to GameplayTagContainer", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FGameplayTagContainer AutoConvert_TryExtractGameplayTagContainer(const FFlowDataPinResult& DataPinResult); + + // InstancedStruct + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to InstancedStruct", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static FInstancedStruct AutoConvert_TryExtractInstancedStruct(const FFlowDataPinResult& DataPinResult); + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to InstancedStruct Array", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static TArray AutoConvert_TryExtractInstancedStructArray(const FFlowDataPinResult& DataPinResult); + + // Object + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to Object", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static UObject* AutoConvert_TryExtractObject(const FFlowDataPinResult& DataPinResult); + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to Object Array", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static TArray AutoConvert_TryExtractObjectArray(const FFlowDataPinResult& DataPinResult); + + // Class + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to Class", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static UClass* AutoConvert_TryExtractClass(const FFlowDataPinResult& DataPinResult); + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Extract to Class Array", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) + static TArray AutoConvert_TryExtractClassArray(const FFlowDataPinResult& DataPinResult); + + // ---------- Get & Set Value functions ---------- + // Direct access to the stored payload on a DataPin Value struct. + // Set functions: Safe for both input and output pins. + // Get functions: ONLY safe on output pins. Using on an input pin triggers a runtime error in editor builds. + + // Set a single bool on a Bool DataPin Value (input or output pin). Replaces any existing values. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Bool Value")) + static void SetBoolValue(bool bInValue, UPARAM(Ref) FFlowDataPinValue_Bool& BoolValue) { BoolValue.Values = { bInValue }; } + + // Set a bool array on a Bool DataPin Value (input or output pin). Replaces the entire array. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Bool Values")) + static void SetBoolValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_Bool& BoolValue) { BoolValue.Values = InValues; } + + // Get a single bool from an output Bool DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Bool Value")) + static bool GetBoolValue(UPARAM(Ref) const FFlowDataPinValue_Bool& BoolValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); + + // Get the full bool array from an output Bool DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Bool Values")) + static TArray GetBoolValues(UPARAM(Ref) FFlowDataPinValue_Bool& BoolValue); + + // Set a single int32 on an Int DataPin Value (input or output pin). Replaces any existing values. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Int Value")) + static void SetIntValue(int32 InValue, UPARAM(Ref) FFlowDataPinValue_Int& IntValue) { IntValue.Values = { InValue }; } + + // Set an int32 array on an Int DataPin Value (input or output pin). Replaces the entire array. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Int Values")) + static void SetIntValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_Int& IntValue) { IntValue.Values = InValues; } + + // Get a single int32 from an output Int DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Int Value")) + static int32 GetIntValue(UPARAM(Ref) const FFlowDataPinValue_Int& IntValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); + + // Get the full int32 array from an output Int DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Int Values")) + static TArray GetIntValues(UPARAM(Ref) FFlowDataPinValue_Int& IntValue); + + // Set a single int64 on an Int64 DataPin Value (input or output pin). Replaces any existing values. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Int64 Value")) + static void SetInt64Value(int64 InValue, UPARAM(Ref) FFlowDataPinValue_Int64& Int64Value) { Int64Value.Values = { InValue }; } + + // Set an int64 array on an Int64 DataPin Value (input or output pin). Replaces the entire array. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Int64 Values")) + static void SetInt64Values(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_Int64& Int64Value) { Int64Value.Values = InValues; } + + // Get a single int64 from an output Int64 DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Int64 Value")) + static int64 GetInt64Value(UPARAM(Ref) const FFlowDataPinValue_Int64& Int64Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); + + // Get the full int64 array from an output Int64 DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Int64 Values")) + static TArray GetInt64Values(UPARAM(Ref) FFlowDataPinValue_Int64& Int64Value); + + // Set a single float on a Float DataPin Value (input or output pin). Replaces any existing values. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Float Value")) + static void SetFloatValue(float InValue, UPARAM(Ref) FFlowDataPinValue_Float& FloatValue) { FloatValue.Values = { InValue }; } + + // Set a float array on a Float DataPin Value (input or output pin). Replaces the entire array. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Float Values")) + static void SetFloatValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_Float& FloatValue) { FloatValue.Values = InValues; } + + // Get a single float from an output Float DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Float Value")) + static float GetFloatValue(UPARAM(Ref) const FFlowDataPinValue_Float& FloatValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); + + // Get the full float array from an output Float DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Float Values")) + static TArray GetFloatValues(UPARAM(Ref) FFlowDataPinValue_Float& FloatValue); + + // Set a single double on a Double DataPin Value (input or output pin). Replaces any existing values. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Double Value")) + static void SetDoubleValue(double InValue, UPARAM(Ref) FFlowDataPinValue_Double& DoubleValue) { DoubleValue.Values = { InValue }; } + + // Set a double array on a Double DataPin Value (input or output pin). Replaces the entire array. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Double Values")) + static void SetDoubleValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_Double& DoubleValue) { DoubleValue.Values = InValues; } + + // Get a single double from an output Double DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Double Value")) + static double GetDoubleValue(UPARAM(Ref) const FFlowDataPinValue_Double& DoubleValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); + + // Get the full double array from an output Double DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Double Values")) + static TArray GetDoubleValues(UPARAM(Ref) FFlowDataPinValue_Double& DoubleValue); + + // Set a single FName on a Name DataPin Value (input or output pin). Replaces any existing values. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Name Value")) + static void SetNameValue(FName InValue, UPARAM(Ref) FFlowDataPinValue_Name& NameValue) { NameValue.Values = { InValue }; } + + // Set an FName array on a Name DataPin Value (input or output pin). Replaces the entire array. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Name Values")) + static void SetNameValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_Name& NameValue) { NameValue.Values = InValues; } + + // Get a single FName from an output Name DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Name Value")) + static FName GetNameValue(UPARAM(Ref) const FFlowDataPinValue_Name& NameValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); + + // Get the full FName array from an output Name DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Name Values")) + static TArray GetNameValues(UPARAM(Ref) FFlowDataPinValue_Name& NameValue); + + // Set a single FString on a String DataPin Value (input or output pin). Replaces any existing values. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set String Value")) + static void SetStringValue(const FString& InValue, UPARAM(Ref) FFlowDataPinValue_String& StringValue) { StringValue.Values = { InValue }; } + + // Set an FString array on a String DataPin Value (input or output pin). Replaces the entire array. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set String Values")) + static void SetStringValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_String& StringValue) { StringValue.Values = InValues; } + + // Get a single FString from an output String DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get String Value")) + static FString GetStringValue(UPARAM(Ref) const FFlowDataPinValue_String& StringValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); + + // Get the full FString array from an output String DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get String Values")) + static TArray GetStringValues(UPARAM(Ref) FFlowDataPinValue_String& StringValue); + + // Set a single FText on a Text DataPin Value (input or output pin). Replaces any existing values. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Text Value")) + static void SetTextValue(const FText& InValue, UPARAM(Ref) FFlowDataPinValue_Text& TextValue) { TextValue.Values = { InValue }; } + + // Set an FText array on a Text DataPin Value (input or output pin). Replaces the entire array. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Text Values")) + static void SetTextValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_Text& TextValue) { TextValue.Values = InValues; } + + // Get a single FText from an output Text DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Text Value")) + static FText GetTextValue(UPARAM(Ref) const FFlowDataPinValue_Text& TextValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); + + // Get the full FText array from an output Text DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Text Values")) + static TArray GetTextValues(UPARAM(Ref) FFlowDataPinValue_Text& TextValue); + + // Set a single enum value (as uint8) on an Enum DataPin Value (input or output pin). Requires EnumClass to be set on the struct. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Enum Value")) + static void SetEnumValue(uint8 InValue, UPARAM(Ref) FFlowDataPinValue_Enum& EnumValue); + + // Set an enum value array (as uint8) on an Enum DataPin Value (input or output pin). Requires EnumClass to be set on the struct. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Enum Values")) + static void SetEnumValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_Enum& EnumValue); + + // Get a single enum value (as uint8) from an output Enum DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Enum Value")) + static uint8 GetEnumValue(UPARAM(Ref) const FFlowDataPinValue_Enum& EnumValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); + + // Get the full enum value array (as uint8) from an output Enum DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Enum Values")) + static TArray GetEnumValues(UPARAM(Ref) const FFlowDataPinValue_Enum& EnumValue); + + // Set a single FVector on a Vector DataPin Value (input or output pin). Replaces any existing values. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Vector Value")) + static void SetVectorValue(const FVector& InValue, UPARAM(Ref) FFlowDataPinValue_Vector& VectorValue) { VectorValue.Values = { InValue }; } + + // Set an FVector array on a Vector DataPin Value (input or output pin). Replaces the entire array. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Vector Values")) + static void SetVectorValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_Vector& VectorValue) { VectorValue.Values = InValues; } + + // Get a single FVector from an output Vector DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Vector Value")) + static FVector GetVectorValue(UPARAM(Ref) const FFlowDataPinValue_Vector& VectorValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); + + // Get the full FVector array from an output Vector DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Vector Values")) + static TArray GetVectorValues(UPARAM(Ref) FFlowDataPinValue_Vector& VectorValue); + + // Set a single FRotator on a Rotator DataPin Value (input or output pin). Replaces any existing values. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Rotator Value")) + static void SetRotatorValue(const FRotator& InValue, UPARAM(Ref) FFlowDataPinValue_Rotator& RotatorValue) { RotatorValue.Values = { InValue }; } + + // Set an FRotator array on a Rotator DataPin Value (input or output pin). Replaces the entire array. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Rotator Values")) + static void SetRotatorValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_Rotator& RotatorValue) { RotatorValue.Values = InValues; } + + // Get a single FRotator from an output Rotator DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Rotator Value")) + static FRotator GetRotatorValue(UPARAM(Ref) const FFlowDataPinValue_Rotator& RotatorValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); + + // Get the full FRotator array from an output Rotator DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Rotator Values")) + static TArray GetRotatorValues(UPARAM(Ref) FFlowDataPinValue_Rotator& RotatorValue); - // FFlowDataPinProperty auto-cast functions + // Set a single FTransform on a Transform DataPin Value (input or output pin). Replaces any existing values. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Transform Value")) + static void SetTransformValue(const FTransform& InValue, UPARAM(Ref) FFlowDataPinValue_Transform& TransformValue) { TransformValue.Values = { InValue }; } - // Convert bool property values to their inner blue values - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Bool", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static bool AutoConvert_FlowDataPinPropertyBoolToBool(const FFlowDataPinOutputProperty_Bool& BoolProperty) { return BoolProperty.Value; } + // Set an FTransform array on a Transform DataPin Value (input or output pin). Replaces the entire array. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Transform Values")) + static void SetTransformValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_Transform& TransformValue) { TransformValue.Values = InValues; } - // to Int variants for all int types - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Int", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static int32 AutoConvert_FlowDataPinPropertyInt32ToInt32(const FFlowDataPinOutputProperty_Int32& IntProperty) { return IntProperty.Value; } + // Get a single FTransform from an output Transform DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Transform Value")) + static FTransform GetTransformValue(UPARAM(Ref) const FFlowDataPinValue_Transform& TransformValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Int64", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static int64 AutoConvert_FlowDataPinPropertyInt32ToInt64(const FFlowDataPinOutputProperty_Int32& IntProperty) { return static_cast(IntProperty.Value); } + // Get the full FTransform array from an output Transform DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Transform Values")) + static TArray GetTransformValues(UPARAM(Ref) FFlowDataPinValue_Transform& TransformValue); - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Int", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static int32 AutoConvert_FlowDataPinPropertyInt64ToInt32(const FFlowDataPinOutputProperty_Int64& IntProperty) { /* possible loss of precision */ return static_cast(IntProperty.Value); } + // Set a single FGameplayTag on a GameplayTag DataPin Value (input or output pin). Replaces any existing values. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set GameplayTag Value")) + static void SetGameplayTagValue(FGameplayTag InValue, UPARAM(Ref) FFlowDataPinValue_GameplayTag& GameplayTagValue) { GameplayTagValue.Values = { InValue }; } - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Int64", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static int64 AutoConvert_FlowDataPinPropertyInt64ToInt64(const FFlowDataPinOutputProperty_Int64& IntProperty) { return IntProperty.Value; } + // Set an FGameplayTag array on a GameplayTag DataPin Value (input or output pin). Replaces the entire array. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set GameplayTag Values")) + static void SetGameplayTagValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_GameplayTag& GameplayTagValue) { GameplayTagValue.Values = InValues; } - // to Float variants for all float types - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Float", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static float AutoConvert_FlowDataPinPropertyFloat32ToFloat32(const FFlowDataPinOutputProperty_Float& FloatProperty) { return FloatProperty.Value; } + // Get a single FGameplayTag from an output GameplayTag DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get GameplayTag Value")) + static FGameplayTag GetGameplayTagValue(UPARAM(Ref) const FFlowDataPinValue_GameplayTag& GameplayTagValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Double", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static double AutoConvert_FlowDataPinPropertyFloat32ToFloat64(const FFlowDataPinOutputProperty_Float& FloatProperty) { return static_cast(FloatProperty.Value); } + // Get the full FGameplayTag array from an output GameplayTag DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get GameplayTag Values")) + static TArray GetGameplayTagValues(UPARAM(Ref) FFlowDataPinValue_GameplayTag& GameplayTagValue); - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Float", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static float AutoConvert_FlowDataPinPropertyFloat64ToFloat32(const FFlowDataPinOutputProperty_Double& FloatProperty) { /* possible loss of precision */ return static_cast(FloatProperty.Value); } + // Set a GameplayTagContainer on a GameplayTagContainer DataPin Value (input or output pin, scalar only). + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set GameplayTagContainer Value")) + static void SetGameplayTagContainerValue(const FGameplayTagContainer& InValue, UPARAM(Ref) FFlowDataPinValue_GameplayTagContainer& GameplayTagContainerValue) { GameplayTagContainerValue.Values = InValue; } - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Double", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static double AutoConvert_FlowDataPinPropertyFloat64ToFloat64(const FFlowDataPinOutputProperty_Double& FloatProperty) { return FloatProperty.Value; } + // Get the GameplayTagContainer from an output GameplayTagContainer DataPin Value (scalar only). DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get GameplayTagContainer Value")) + static FGameplayTagContainer GetGameplayTagContainerValue(UPARAM(Ref) const FFlowDataPinValue_GameplayTagContainer& GameplayTagContainerValue); - // to Name variants for all text-based types - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Name", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FName AutoConvert_FlowDataPinPropertyNameToName(const FFlowDataPinOutputProperty_Name& NameProperty) { return NameProperty.Value; } + // Set a single FInstancedStruct on an InstancedStruct DataPin Value (input or output pin). Replaces any existing values. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set InstancedStruct Value")) + static void SetInstancedStructValue(const FInstancedStruct& InValue, UPARAM(Ref) FFlowDataPinValue_InstancedStruct& InstancedStructValue) { InstancedStructValue.Values = { InValue }; } - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Name", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FName AutoConvert_FlowDataPinPropertyStringToName(const FFlowDataPinOutputProperty_String& StringProperty) { return FName(StringProperty.Value); } + // Set an FInstancedStruct array on an InstancedStruct DataPin Value (input or output pin). Replaces the entire array. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set InstancedStruct Values")) + static void SetInstancedStructValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_InstancedStruct& InstancedStructValue) { InstancedStructValue.Values = InValues; } - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Name", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FName AutoConvert_FlowDataPinPropertyTextToName(const FFlowDataPinOutputProperty_Text& TextProperty) { return FName(TextProperty.Value.ToString()); } + // Get a single FInstancedStruct from an output InstancedStruct DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get InstancedStruct Value")) + static FInstancedStruct GetInstancedStructValue(UPARAM(Ref) const FFlowDataPinValue_InstancedStruct& InstancedStructValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // to String variants for all text-based types - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to String", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FString AutoConvert_FlowDataPinPropertyNameToString(const FFlowDataPinOutputProperty_Name& NameProperty) { return NameProperty.Value.ToString(); } + // Get the full FInstancedStruct array from an output InstancedStruct DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get InstancedStruct Values")) + static TArray GetInstancedStructValues(UPARAM(Ref) FFlowDataPinValue_InstancedStruct& InstancedStructValue); - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to String", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FString AutoConvert_FlowDataPinPropertyStringToString(const FFlowDataPinOutputProperty_String& StringProperty) { return StringProperty.Value; } + // Set a single UObject* on an Object DataPin Value (input or output pin). Replaces any existing values. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Object Value")) + static void SetObjectValue(UObject* InValue, UPARAM(Ref) FFlowDataPinValue_Object& ObjectValue) { ObjectValue.Values = { InValue }; } - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to String", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FString AutoConvert_FlowDataPinPropertyTextToString(const FFlowDataPinOutputProperty_Text& TextProperty) { return TextProperty.Value.ToString(); } + // Set a UObject* array on an Object DataPin Value (input or output pin). Replaces the entire array. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Object Values")) + static void SetObjectValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_Object& ObjectValue) { ObjectValue.Values = InValues; } - // to Text variants for all text-based types - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Text", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FText AutoConvert_FlowDataPinPropertyNameToText(const FFlowDataPinOutputProperty_Name& NameProperty) { return FText::FromName(NameProperty.Value); } + // Get a single UObject* from an output Object DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Object Value")) + static UObject* GetObjectValue(UPARAM(Ref) const FFlowDataPinValue_Object& ObjectValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Text", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FText AutoConvert_FlowDataPinPropertyStringToText(const FFlowDataPinOutputProperty_String& StringProperty) { return FText::FromString(StringProperty.Value); } + // Get the full UObject* array from an output Object DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Object Values")) + static TArray GetObjectValues(UPARAM(Ref) FFlowDataPinValue_Object& ObjectValue); - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Text", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FText AutoConvert_FlowDataPinPropertyTextToText(const FFlowDataPinOutputProperty_Text& TextProperty) { return TextProperty.Value; } + // Set a single FSoftClassPath on a Class DataPin Value (input or output pin). Replaces any existing values. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Class Value")) + static void SetClassValue(const FSoftClassPath& InValue, UPARAM(Ref) FFlowDataPinValue_Class& ClassValue) { ClassValue.Values = { InValue }; } - // Convert enum property values to their inner enum values - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Enum", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static uint8 AutoConvert_FlowDataPinPropertyEnumToEnum(const FFlowDataPinOutputProperty_Enum& EnumProperty); + // Set an FSoftClassPath array on a Class DataPin Value (input or output pin). Replaces the entire array. + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Class Values")) + static void SetClassValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_Class& ClassValue) { ClassValue.Values = InValues; } - // Convert vector property values to their inner Vector - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Vector", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FVector AutoConvert_FlowDataPinPropertyVectorToVector(const FFlowDataPinOutputProperty_Vector& VectorProperty) { return VectorProperty.Value; } + // Get a single FSoftClassPath from an output Class DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Class Value")) + static FSoftClassPath GetClassValue(UPARAM(Ref) const FFlowDataPinValue_Class& ClassValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Convert Rotator property values to their inner Rotator - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Rotator", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FRotator AutoConvert_FlowDataPinPropertyRotatorToRotator(const FFlowDataPinOutputProperty_Rotator& RotatorProperty) { return RotatorProperty.Value; } + // Get the full FSoftClassPath array from an output Class DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Class Values")) + static TArray GetClassValues(UPARAM(Ref) FFlowDataPinValue_Class& ClassValue); - // Convert Transform property values to their inner Transform - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Transform", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FTransform AutoConvert_FlowDataPinPropertyTransformToTransform(const FFlowDataPinOutputProperty_Transform& TransformProperty) { return TransformProperty.Value; } + // --------- Make Flow DataPin Result - for use in blueprint TrySupplyDataPin implementations ---------- + + UFUNCTION(BlueprintPure, Category = DataPins, meta = (DisplayName = "Make Flow DataPin Result")) + static FFlowDataPinResult MakeFlowDataPinResult_Empty(EFlowDataPinResolveResult Result) { return FFlowDataPinResult(Result); } - // Convert GameplayTag property values to their inner GameplayTag - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to GameplayTag", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FGameplayTag AutoConvert_FlowDataPinPropertyGameplayTagToGameplayTag(const FFlowDataPinOutputProperty_GameplayTag& GameplayTagProperty) { return GameplayTagProperty.Value; } + UFUNCTION(BlueprintPure, Category = DataPins, meta = (DisplayName = "Make Bool Flow DataPin Result")) + static FFlowDataPinResult MakeFlowDataPinResult_Bool(const TArray& BoolValues); - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to GameplayTagContainer", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FGameplayTagContainer AutoConvert_FlowDataPinPropertyGameplayTagToGameplayTagContainer(const FFlowDataPinOutputProperty_GameplayTag& GameplayTagProperty) { return FGameplayTagContainer(GameplayTagProperty.Value); } + UFUNCTION(BlueprintPure, Category = DataPins, meta = (DisplayName = "Make Int Flow DataPin Result")) + static FFlowDataPinResult MakeFlowDataPinResult_Int(const TArray& IntValues); - // Convert GameplayTagContainer property values to their inner GameplayTagContainer - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to GameplayTagContainer", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FGameplayTagContainer AutoConvert_FlowDataPinPropertyGameplayTagContainerToGameplayTagContainer(const FFlowDataPinOutputProperty_GameplayTagContainer& GameplayTagContainerProperty) { return GameplayTagContainerProperty.Value; } + UFUNCTION(BlueprintPure, Category = DataPins, meta = (DisplayName = "Make Int64 Flow DataPin Result")) + static FFlowDataPinResult MakeFlowDataPinResult_Int64(const TArray& Int64Values); - // Convert InstancedStruct property values to their inner InstancedStruct - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to InstancedStruct", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FInstancedStruct AutoConvert_FlowDataPinPropertyInstancedStructToInstancedStruct(const FFlowDataPinOutputProperty_InstancedStruct& InstancedStructProperty) { return InstancedStructProperty.Value; } + UFUNCTION(BlueprintPure, Category = DataPins, meta = (DisplayName = "Make Float Flow DataPin Result")) + static FFlowDataPinResult MakeFlowDataPinResult_Float(const TArray& FloatValues); - // Convert Object property values to their inner Object - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Object", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static UObject* AutoConvert_FlowDataPinPropertyObjectToObject(const FFlowDataPinOutputProperty_Object& ObjectProperty) { return ObjectProperty.GetObjectValue(); } + UFUNCTION(BlueprintPure, Category = DataPins, meta = (DisplayName = "Make Double Flow DataPin Result")) + static FFlowDataPinResult MakeFlowDataPinResult_Double(const TArray& DoubleValues); - // Convert Class property values to their inner Class - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Class", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static UClass* AutoConvert_FlowDataPinPropertyClassToClass(const FFlowDataPinOutputProperty_Class& ClassProperty) { return ClassProperty.GetResolvedClass(); } + UFUNCTION(BlueprintPure, Category = DataPins, meta = (DisplayName = "Make Name Flow DataPin Result")) + static FFlowDataPinResult MakeFlowDataPinResult_Name(const TArray& NameValues); - // Convert Class property values to their FSoftClassPath - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to SoftClass", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FSoftClassPath AutoConvert_FlowDataPinPropertyClassToSoftClass(const FFlowDataPinOutputProperty_Class& ClassProperty) { return ClassProperty.GetAsSoftClass(); } + UFUNCTION(BlueprintPure, Category = DataPins, meta = (DisplayName = "Make String Flow DataPin Result")) + static FFlowDataPinResult MakeFlowDataPinResult_String(const TArray& StringValues); - // Recommend implementing AutoConvert_FlowDataPinResult... for every EFlowPinType - FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); + UFUNCTION(BlueprintPure, Category = DataPins, meta = (DisplayName = "Make Text Flow DataPin Result")) + static FFlowDataPinResult MakeFlowDataPinResult_Text(const TArray& TextValues); - // FFlowDataPinResults auto-cast functions + UFUNCTION(BlueprintPure, Category = DataPins, meta = (DisplayName = "Make Enum Flow DataPin Result")) + static FFlowDataPinResult MakeFlowDataPinResult_Enum(const TArray& EnumValues, UEnum* EnumClass); - // Convert bool property values to their inner blueprint values - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Bool", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static bool AutoConvert_FlowDataPinResultBoolToBool(const FFlowDataPinResult_Bool& BoolProperty) { return BoolProperty.Value; } + UFUNCTION(BlueprintPure, Category = DataPins, meta = (DisplayName = "Make Vector Flow DataPin Result")) + static FFlowDataPinResult MakeFlowDataPinResult_Vector(const TArray& VectorValues); - // to Int variants for all int types (that blueprint supports - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Int", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static int32 AutoConvert_FlowDataPinResultInt64ToInt32(const FFlowDataPinResult_Int& IntProperty) { /* possible loss of precision */ return static_cast(IntProperty.Value); } + UFUNCTION(BlueprintPure, Category = DataPins, meta = (DisplayName = "Make Rotator Flow DataPin Result")) + static FFlowDataPinResult MakeFlowDataPinResult_Rotator(const TArray& RotatorValues); - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Int64", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static int64 AutoConvert_FlowDataPinResultInt64ToInt64(const FFlowDataPinResult_Int& IntProperty) { return IntProperty.Value; } + UFUNCTION(BlueprintPure, Category = DataPins, meta = (DisplayName = "Make Transform Flow DataPin Result")) + static FFlowDataPinResult MakeFlowDataPinResult_Transform(const TArray& TransformValues); - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Int", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static uint8 AutoConvert_FlowDataPinResultInt64ToUint8(const FFlowDataPinResult_Int& IntProperty) { /* possible loss of precision */ return static_cast(IntProperty.Value); } + UFUNCTION(BlueprintPure, Category = DataPins, meta = (DisplayName = "Make GameplayTag Flow DataPin Result")) + static FFlowDataPinResult MakeFlowDataPinResult_GameplayTag(const TArray& GameplayTagValues); - // to Float variants for all float types - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Float", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static float AutoConvert_FlowDataPinResultFloat64ToFloat32(const FFlowDataPinResult_Float& FloatProperty) { /* possible loss of precision */ return static_cast(FloatProperty.Value); } + UFUNCTION(BlueprintPure, Category = DataPins, meta = (DisplayName = "Make GameplayTagContainer Flow DataPin Result")) + static FFlowDataPinResult MakeFlowDataPinResult_GameplayTagContainer(FGameplayTagContainer GameplayTagContainerValue); - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Double", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static double AutoConvert_FlowDataPinResultFloat64ToFloat64(const FFlowDataPinResult_Float& FloatProperty) { return FloatProperty.Value; } + UFUNCTION(BlueprintPure, Category = DataPins, meta = (DisplayName = "Make InstancedStruct Flow DataPin Result")) + static FFlowDataPinResult MakeFlowDataPinResult_InstancedStruct(const TArray& InstancedStructValues); - // to Name variants for all text-based types - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Name", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FName AutoConvert_FlowDataPinResultNameToName(const FFlowDataPinResult_Name& NameProperty) { return NameProperty.Value; } + UFUNCTION(BlueprintPure, Category = DataPins, meta = (DisplayName = "Make Object Flow DataPin Result")) + static FFlowDataPinResult MakeFlowDataPinResult_Object(const TArray& ObjectValues); - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Name", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FName AutoConvert_FlowDataPinResultStringToName(const FFlowDataPinResult_String& StringProperty) { return FName(StringProperty.Value); } + UFUNCTION(BlueprintPure, Category = DataPins, meta = (DisplayName = "Make Class Flow DataPin Result")) + static FFlowDataPinResult MakeFlowDataPinResult_Class(const TArray& ClassValues); - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Name", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FName AutoConvert_FlowDataPinResultTextToName(const FFlowDataPinResult_Text& TextProperty) { return FName(TextProperty.Value.ToString()); } + // ---------- Override the Make functions to discourage use ---------- + // Ideally, we would forbid Make altogether, but this is the best work-around I have found. - // to String variants for all text-based types - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to String", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FString AutoConvert_FlowDataPinResultNameToString(const FFlowDataPinResult_Name& NameProperty) { return NameProperty.Value.ToString(); } + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Make Bool Flow DataPin Value", DeprecatedFunction, DeprecationMessage = "use SetBoolValue(s) instead")) + static FFlowDataPinValue_Bool MakeStructBool(const FFlowDataPinValue_Bool& OtherValueStruct) { return OtherValueStruct; } - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to String", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FString AutoConvert_FlowDataPinResultStringToString(const FFlowDataPinResult_String& StringProperty) { return StringProperty.Value; } + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Make Int Flow DataPin Value", DeprecatedFunction, DeprecationMessage = "use SetIntValue(s) instead")) + static FFlowDataPinValue_Int MakeStructInt(const FFlowDataPinValue_Int& OtherValueStruct) { return OtherValueStruct; } - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to String", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FString AutoConvert_FlowDataPinResultTextToString(const FFlowDataPinResult_Text& TextProperty) { return TextProperty.Value.ToString(); } + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Make Int64 Flow DataPin Value", DeprecatedFunction, DeprecationMessage = "use SetInt64Value(s) instead")) + static FFlowDataPinValue_Int64 MakeStructInt64(const FFlowDataPinValue_Int64& OtherValueStruct) { return OtherValueStruct; } - // to Text variants for all text-based types - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Text", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FText AutoConvert_FlowDataPinResultNameToText(const FFlowDataPinResult_Name& NameProperty) { return FText::FromName(NameProperty.Value); } + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Make Float Flow DataPin Value", DeprecatedFunction, DeprecationMessage = "use SetFloatValue(s) instead")) + static FFlowDataPinValue_Float MakeStructFloat(const FFlowDataPinValue_Float& OtherValueStruct) { return OtherValueStruct; } - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Text", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FText AutoConvert_FlowDataPinResultStringToText(const FFlowDataPinResult_String& StringProperty) { return FText::FromString(StringProperty.Value); } + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Make Double Flow DataPin Value", DeprecatedFunction, DeprecationMessage = "use SetDoubleValue(s) instead")) + static FFlowDataPinValue_Double MakeStructDouble(const FFlowDataPinValue_Double& OtherValueStruct) { return OtherValueStruct; } - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Text", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FText AutoConvert_FlowDataPinResultTextToText(const FFlowDataPinResult_Text& TextProperty) { return TextProperty.Value; } + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Make Name Flow DataPin Value", DeprecatedFunction, DeprecationMessage = "use SetNameValue(s) instead")) + static FFlowDataPinValue_Name MakeStructName(const FFlowDataPinValue_Name& OtherValueStruct) { return OtherValueStruct; } - // Convert enum property values to their inner enum values - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Enum", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static uint8 AutoConvert_FlowDataPinResultEnumToEnum(const FFlowDataPinResult_Enum& EnumProperty); + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Make String Flow DataPin Value", DeprecatedFunction, DeprecationMessage = "use SetStringValue(s) instead")) + static FFlowDataPinValue_String MakeStructString(const FFlowDataPinValue_String& OtherValueStruct) { return OtherValueStruct; } - // Convert vector property values to their inner Vector - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Vector", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FVector AutoConvert_FlowDataPinResultVectorToVector(const FFlowDataPinResult_Vector& VectorProperty) { return VectorProperty.Value; } + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Make Text Flow DataPin Value", DeprecatedFunction, DeprecationMessage = "use SetTextValue(s) instead")) + static FFlowDataPinValue_Text MakeStructText(const FFlowDataPinValue_Text& OtherValueStruct) { return OtherValueStruct; } - // Convert Rotator property values to their inner Rotator - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Rotator", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FRotator AutoConvert_FlowDataPinResultRotatorToRotator(const FFlowDataPinResult_Rotator& RotatorProperty) { return RotatorProperty.Value; } + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Make Enum Flow DataPin Value", DeprecatedFunction, DeprecationMessage = "use SetEnumValue(s) instead")) + static FFlowDataPinValue_Enum MakeStructEnum(const FFlowDataPinValue_Enum& OtherValueStruct) { return OtherValueStruct; } - // Convert Transform property values to their inner Transform - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Transform", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FTransform AutoConvert_FlowDataPinResultTransformToTransform(const FFlowDataPinResult_Transform& TransformProperty) { return TransformProperty.Value; } + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Make Vector Flow DataPin Value", DeprecatedFunction, DeprecationMessage = "use SetVectorValue(s) instead")) + static FFlowDataPinValue_Vector MakeStructVector(const FFlowDataPinValue_Vector& OtherValueStruct) { return OtherValueStruct; } - // Convert GameplayTag property values to their inner GameplayTag - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to GameplayTag", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FGameplayTag AutoConvert_FlowDataPinResultGameplayTagToGameplayTag(const FFlowDataPinResult_GameplayTag& GameplayTagProperty) { return GameplayTagProperty.Value; } + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Make Rotator Flow DataPin Value", DeprecatedFunction, DeprecationMessage = "use SetRotatorValue(s) instead")) + static FFlowDataPinValue_Rotator MakeStructRotator(const FFlowDataPinValue_Rotator& OtherValueStruct) { return OtherValueStruct; } - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to GameplayTagContainer", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FGameplayTagContainer AutoConvert_FlowDataPinResultGameplayTagToGameplayTagContainer(const FFlowDataPinResult_GameplayTag& GameplayTagProperty) { return FGameplayTagContainer(GameplayTagProperty.Value); } + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Make Transform Flow DataPin Value", DeprecatedFunction, DeprecationMessage = "use SetTransformValue(s) instead")) + static FFlowDataPinValue_Transform MakeStructTransform(const FFlowDataPinValue_Transform& OtherValueStruct) { return OtherValueStruct; } - // Convert GameplayTagContainer property values to their inner GameplayTagContainer - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to GameplayTagContainer", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FGameplayTagContainer AutoConvert_FlowDataPinResultGameplayTagContainerToGameplayTagContainer(const FFlowDataPinResult_GameplayTagContainer& GameplayTagContainerProperty) { return GameplayTagContainerProperty.Value; } + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Make GameplayTag Flow DataPin Value", DeprecatedFunction, DeprecationMessage = "use SetGameplayTagValue(s) instead")) + static FFlowDataPinValue_GameplayTag MakeStructGameplayTag(const FFlowDataPinValue_GameplayTag& OtherValueStruct) { return OtherValueStruct; } - // Convert InstancedStruct property values to their inner InstancedStruct - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to InstancedStruct", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FInstancedStruct AutoConvert_FlowDataPinResultInstancedStructToInstancedStruct(const FFlowDataPinResult_InstancedStruct& InstancedStructProperty) { return InstancedStructProperty.Value; } + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Make GameplayTagContainer Flow DataPin Value", DeprecatedFunction, DeprecationMessage = "use SetGameplayTagContainerValue(s) instead")) + static FFlowDataPinValue_GameplayTagContainer MakeStructGameplayTagContainer(const FFlowDataPinValue_GameplayTagContainer& OtherValueStruct) { return OtherValueStruct; } - // Convert Object property values to their inner Object - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Object", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static UObject* AutoConvert_FlowDataPinResultObjectToObject(const FFlowDataPinResult_Object& ObjectProperty) { return ObjectProperty.Value; } + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Make InstancedStruct Flow DataPin Value", DeprecatedFunction, DeprecationMessage = "use SetInstancedStructValue(s) instead")) + static FFlowDataPinValue_InstancedStruct MakeStructInstancedStruct(const FFlowDataPinValue_InstancedStruct& OtherValueStruct) { return OtherValueStruct; } - // Convert Class property values to their inner Class - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to Class", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static UClass* AutoConvert_FlowDataPinResultClassToClass(const FFlowDataPinResult_Class& ClassProperty) { return ClassProperty.GetOrResolveClass(); } + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Make Object Flow DataPin Value", DeprecatedFunction, DeprecationMessage = "use SetObjectValue(s) instead")) + static FFlowDataPinValue_Object MakeStructObject(const FFlowDataPinValue_Object& OtherValueStruct) { return OtherValueStruct; } - // Convert Class property values to their the FSoftClassPath - UFUNCTION(BlueprintPure, meta = (DisplayName = "Convert to SoftClass", CompactNodeTitle = "->", BlueprintAutocast), Category = DataPins) - static FSoftClassPath AutoConvert_FlowDataPinResultClassToSoftClass(const FFlowDataPinResult_Class& ClassProperty) { return ClassProperty.GetAsSoftClass(); } -}; \ No newline at end of file + UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Make Class Flow DataPin Value", DeprecatedFunction, DeprecationMessage = "use SetClassValue(s) instead")) + static FFlowDataPinValue_Class MakeStructClass(const FFlowDataPinValue_Class& OtherValueStruct) { return OtherValueStruct; } +}; diff --git a/Source/Flow/Public/Types/FlowDataPinProperties.h b/Source/Flow/Public/Types/FlowDataPinProperties.h index a40a0e24f..fb3811bfd 100644 --- a/Source/Flow/Public/Types/FlowDataPinProperties.h +++ b/Source/Flow/Public/Types/FlowDataPinProperties.h @@ -3,60 +3,25 @@ #pragma once #include "GameplayTagContainer.h" +#include "Kismet/BlueprintFunctionLibrary.h" #include "StructUtils/InstancedStruct.h" #include "UObject/Class.h" -#include "Nodes/FlowPin.h" #include "FlowDataPinProperties.generated.h" -class FStructProperty; -class UScriptStruct; - -USTRUCT(BlueprintType, DisplayName = "Base - Flow DataPin Property") +// #FlowDataPinLegacy +USTRUCT(DisplayName = "Base - Flow DataPin Property", meta = (Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinProperty { GENERATED_BODY() - FFlowDataPinProperty() { } + FFlowDataPinProperty() = default; virtual ~FFlowDataPinProperty() { } - - FLOW_API virtual EFlowPinType GetFlowPinType() const { return EFlowPinType::Invalid; } - FLOW_API virtual bool IsInputProperty() const { return false; } - -#if WITH_EDITOR - FLOW_API static FFlowPin CreateFlowPin(const FName& PinName, const TInstancedStruct& DataPinProperty); - - template - static UScriptStruct* FindScriptStructForFlowDataPinProperty(const FProperty& Property) - { - // Find the ScriptStruct of the wrapped struct in a wrapper (eg, FFlowDataPinOutputProperty_Vector) or the struct itself (eg, FVector) - const FStructProperty* StructProperty = CastField(&Property); - if (!StructProperty) - { - return nullptr; - } - - UScriptStruct* ScriptStruct = TFlowDataPinPropertyType::StaticStruct(); - if (StructProperty->Struct == ScriptStruct) - { - static UScriptStruct* UnrealType = TBaseStructure::Get(); - - return UnrealType; - } - else - { - return StructProperty->Struct; - } - } -#endif }; -// Recommend implementing FFlowDataPinProperty... for every EFlowPinType -FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); - // Wrapper struct for a bool that will generate and link to a Data Pin with its same name -USTRUCT(BlueprintType, DisplayName = "Bool - Output Flow Data Pin Property", meta = (FlowPinType = "Bool")) +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Bool - Output Flow Data Pin Property", meta = (FlowPinType = "Bool", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_Bool : public FFlowDataPinProperty { GENERATED_BODY() @@ -70,12 +35,10 @@ struct FFlowDataPinOutputProperty_Bool : public FFlowDataPinProperty FFlowDataPinOutputProperty_Bool() { } FFlowDataPinOutputProperty_Bool(bool InValue) : Value(InValue) { } - - FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Bool; } }; -// Wrapper struct for a int64 that will generate and link to a Data Pin with its same name -USTRUCT(BlueprintType, DisplayName = "Int64 - Output Flow Data Pin Property", meta = (FlowPinType = "Int")) +// Wrapper struct for an int64 that will generate and link to a Data Pin with its same name +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Int64 - Output Flow Data Pin Property", meta = (FlowPinType = "Int64", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_Int64 : public FFlowDataPinProperty { GENERATED_BODY() @@ -89,12 +52,10 @@ struct FFlowDataPinOutputProperty_Int64 : public FFlowDataPinProperty FFlowDataPinOutputProperty_Int64() { } FFlowDataPinOutputProperty_Int64(int64 InValue) : Value(InValue) { } - - FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Int; } }; -// Wrapper struct for a int32 that will generate and link to a Data Pin with its same name -USTRUCT(BlueprintType, DisplayName = "Int - Output Flow Data Pin Property", meta = (FlowPinType = "Int")) +// Wrapper struct for an int32 that will generate and link to a Data Pin with its same name +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Int - Output Flow Data Pin Property", meta = (FlowPinType = "Int", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_Int32 : public FFlowDataPinProperty { GENERATED_BODY() @@ -108,12 +69,10 @@ struct FFlowDataPinOutputProperty_Int32 : public FFlowDataPinProperty FFlowDataPinOutputProperty_Int32() { } FFlowDataPinOutputProperty_Int32(int32 InValue) : Value(InValue) { } - - FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Int; } }; // Wrapper struct for a Double (64bit float) that will generate and link to a Data Pin with its same name -USTRUCT(BlueprintType, DisplayName = "Double (float64) - Output Flow Data Pin Property", meta = (FlowPinType = "Float")) +USTRUCT(BlueprintType, DisplayName = "Double (float64) - Output Flow Data Pin Property", meta = (FlowPinType = "Double", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_Double : public FFlowDataPinProperty { GENERATED_BODY() @@ -127,12 +86,10 @@ struct FFlowDataPinOutputProperty_Double : public FFlowDataPinProperty FFlowDataPinOutputProperty_Double() { } FFlowDataPinOutputProperty_Double(double InValue) : Value(InValue) { } - - FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Float; } }; // Wrapper struct for a Float (32bit) that will generate and link to a Data Pin with its same name -USTRUCT(BlueprintType, DisplayName = "Float - Output Flow Data Pin Property", meta = (FlowPinType = "Float")) +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Float - Output Flow Data Pin Property", meta = (FlowPinType = "Float", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_Float : public FFlowDataPinProperty { GENERATED_BODY() @@ -146,12 +103,10 @@ struct FFlowDataPinOutputProperty_Float : public FFlowDataPinProperty FFlowDataPinOutputProperty_Float() { } FFlowDataPinOutputProperty_Float(float InValue) : Value(InValue) { } - - FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Float; } }; // Wrapper struct for a FName that will generate and link to a Data Pin with its same name -USTRUCT(BlueprintType, DisplayName = "Name - Output Flow Data Pin Property", meta = (FlowPinType = "Name")) +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Name - Output Flow Data Pin Property", meta = (FlowPinType = "Name", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_Name : public FFlowDataPinProperty { GENERATED_BODY() @@ -165,12 +120,10 @@ struct FFlowDataPinOutputProperty_Name : public FFlowDataPinProperty FFlowDataPinOutputProperty_Name() { } FFlowDataPinOutputProperty_Name(const FName& InValue) : Value(InValue) { } - - FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Name; } }; // Wrapper struct for a FString that will generate and link to a Data Pin with its same name -USTRUCT(BlueprintType, DisplayName = "String - Output Flow Data Pin Property", meta = (FlowPinType = "String")) +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] String - Output Flow Data Pin Property", meta = (FlowPinType = "String", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_String : public FFlowDataPinProperty { GENERATED_BODY() @@ -184,12 +137,10 @@ struct FFlowDataPinOutputProperty_String : public FFlowDataPinProperty FFlowDataPinOutputProperty_String() { } FFlowDataPinOutputProperty_String(const FString& InValue) : Value(InValue) { } - - FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::String; } }; // Wrapper struct for a FText that will generate and link to a Data Pin with its same name -USTRUCT(BlueprintType, DisplayName = "Text - Output Flow Data Pin Property", meta = (FlowPinType = "Text")) +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Text - Output Flow Data Pin Property", meta = (FlowPinType = "Text", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_Text : public FFlowDataPinProperty { GENERATED_BODY() @@ -203,12 +154,10 @@ struct FFlowDataPinOutputProperty_Text : public FFlowDataPinProperty FFlowDataPinOutputProperty_Text() { } FFlowDataPinOutputProperty_Text(const FText& InValue) : Value(InValue) { } - - FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Text; } }; // Wrapper struct for an enum that will generate and link to a Data Pin with its same name -USTRUCT(BlueprintType, DisplayName = "Enum - Output Flow Data Pin Property", meta = (FlowPinType = "Enum")) +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Enum - Output Flow Data Pin Property", meta = (FlowPinType = "Enum", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_Enum : public FFlowDataPinProperty { GENERATED_BODY() @@ -238,17 +187,12 @@ struct FFlowDataPinOutputProperty_Enum : public FFlowDataPinProperty FFlowDataPinOutputProperty_Enum(const FName& InValue, UEnum* InEnumClass) : Value(InValue) , EnumClass(InEnumClass) - { } - - FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Enum; } - -#if WITH_EDITOR - FLOW_API void OnEnumNameChanged(); -#endif // WITH_EDITOR + { + } }; // Wrapper struct for a FVector that will generate and link to a Data Pin with its same name -USTRUCT(BlueprintType, DisplayName = "Vector - Output Flow Data Pin Property", meta = (FlowPinType = "Vector")) +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Vector - Output Flow Data Pin Property", meta = (FlowPinType = "Vector", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_Vector : public FFlowDataPinProperty { GENERATED_BODY() @@ -262,12 +206,10 @@ struct FFlowDataPinOutputProperty_Vector : public FFlowDataPinProperty FFlowDataPinOutputProperty_Vector() {} FFlowDataPinOutputProperty_Vector(const FVector& InValue) : Value(InValue) { } - - FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Vector; } }; // Wrapper struct for a FRotator that will generate and link to a Data Pin with its same name -USTRUCT(BlueprintType, DisplayName = "Rotator - Output Flow Data Pin Property", meta = (FlowPinType = "Rotator")) +USTRUCT(BlueprintType, DisplayName = "Rotator - Output Flow Data Pin Property", meta = (FlowPinType = "Rotator", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_Rotator : public FFlowDataPinProperty { GENERATED_BODY() @@ -281,12 +223,10 @@ struct FFlowDataPinOutputProperty_Rotator : public FFlowDataPinProperty FFlowDataPinOutputProperty_Rotator() {} FFlowDataPinOutputProperty_Rotator(const FRotator& InValue) : Value(InValue) { } - - FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Rotator; } }; // Wrapper struct for a FTransform that will generate and link to a Data Pin with its same name -USTRUCT(BlueprintType, DisplayName = "Transform - Output Flow Data Pin Property", meta = (FlowPinType = "Transform")) +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Transform - Output Flow Data Pin Property", meta = (FlowPinType = "Transform", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_Transform : public FFlowDataPinProperty { GENERATED_BODY() @@ -300,12 +240,10 @@ struct FFlowDataPinOutputProperty_Transform : public FFlowDataPinProperty FFlowDataPinOutputProperty_Transform() {} FFlowDataPinOutputProperty_Transform(const FTransform& InValue) : Value(InValue) { } - - FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Transform; } }; // Wrapper struct for a FGameplayTag that will generate and link to a Data Pin with its same name -USTRUCT(BlueprintType, DisplayName = "GameplayTag - Output Flow Data Pin Property", meta = (FlowPinType = "GameplayTag")) +USTRUCT(BlueprintType, DisplayName = "GameplayTag - Output Flow Data Pin Property", meta = (FlowPinType = "GameplayTag", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_GameplayTag : public FFlowDataPinProperty { GENERATED_BODY() @@ -319,12 +257,10 @@ struct FFlowDataPinOutputProperty_GameplayTag : public FFlowDataPinProperty FFlowDataPinOutputProperty_GameplayTag() {} FFlowDataPinOutputProperty_GameplayTag(const FGameplayTag& InValue) : Value(InValue) { } - - FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::GameplayTag; } }; // Wrapper struct for a FGameplayTagContainer that will generate and link to a Data Pin with its same name -USTRUCT(BlueprintType, DisplayName = "GameplayTagContainer - Output Flow DataPin Property", meta = (FlowPinType = "GameplayTagContainer")) +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] GameplayTagContainer - Output Flow DataPin Property", meta = (FlowPinType = "GameplayTagContainer", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_GameplayTagContainer : public FFlowDataPinProperty { GENERATED_BODY() @@ -338,12 +274,10 @@ struct FFlowDataPinOutputProperty_GameplayTagContainer : public FFlowDataPinProp FFlowDataPinOutputProperty_GameplayTagContainer() {} FFlowDataPinOutputProperty_GameplayTagContainer(const FGameplayTagContainer& InValue) : Value(InValue) { } - - FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::GameplayTagContainer; } }; // Wrapper struct for a FInstancedStruct that will generate and link to a Data Pin with its same name -USTRUCT(BlueprintType, DisplayName = "InstancedStruct - Output Flow DataPin Property", meta = (FlowPinType = "InstancedStruct")) +USTRUCT(BlueprintType, DisplayName = "InstancedStruct - Output Flow DataPin Property", meta = (FlowPinType = "InstancedStruct", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_InstancedStruct : public FFlowDataPinProperty { GENERATED_BODY() @@ -357,19 +291,17 @@ struct FFlowDataPinOutputProperty_InstancedStruct : public FFlowDataPinProperty FFlowDataPinOutputProperty_InstancedStruct() {} FFlowDataPinOutputProperty_InstancedStruct(const FInstancedStruct& InValue) : Value(InValue) { } - - FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::InstancedStruct; } }; // Wrapper struct for a UObject that will generate and link to a Data Pin with its same name -USTRUCT(BlueprintType, DisplayName = "Object - Output Flow DataPin Property", meta = (FlowPinType = "Object")) +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Object - Output Flow DataPin Property", meta = (FlowPinType = "Object", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_Object : public FFlowDataPinProperty { GENERATED_BODY() friend class FFlowDataPinProperty_ObjectCustomizationBase; -protected: +public: // These pointers are separate so that the default value for the object can be configured // in the editor according to the type of object that it is (instanced or not). @@ -382,8 +314,6 @@ struct FFlowDataPinOutputProperty_Object : public FFlowDataPinProperty UPROPERTY(EditAnywhere, Instanced, BlueprintReadWrite, Category = DataPins, DisplayName = "Value (inline)", meta = (EditCondition = "ReferenceValue == nullptr")) TObjectPtr InlineValue = nullptr; -public: - #if WITH_EDITORONLY_DATA UPROPERTY(EditAnywhere, Category = DataPins, meta = (AllowAbstract)) TObjectPtr ClassFilter = UObject::StaticClass(); @@ -394,32 +324,22 @@ struct FFlowDataPinOutputProperty_Object : public FFlowDataPinProperty FFlowDataPinOutputProperty_Object() {} FLOW_API FFlowDataPinOutputProperty_Object(UObject* InValue, UClass* InClassFilter = nullptr); - FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Object; } - UObject* GetObjectValue() const { return ReferenceValue ? ReferenceValue : InlineValue; } - void SetObjectValue(UObject* InValue); - -#if WITH_EDITOR - UClass* DeriveObjectClass(const FProperty& MetaDataProperty) const; - FLOW_API static UClass* TryGetObjectClassFromProperty(const FProperty& MetaDataProperty); -#endif }; // Wrapper struct for a UClass that will generate and link to a Data Pin with its same name -USTRUCT(BlueprintType, DisplayName = "Class - Output Flow DataPin Property", meta = (FlowPinType = "Class")) +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Class - Output Flow DataPin Property", meta = (FlowPinType = "Class", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_Class : public FFlowDataPinProperty { GENERATED_BODY() friend class FFlowDataPinProperty_ClassCustomizationBase; -protected: +public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) FSoftClassPath Value; -public: - #if WITH_EDITORONLY_DATA UPROPERTY(EditAnywhere, Category = DataPins, meta = (AllowAbstract)) TObjectPtr ClassFilter = UObject::StaticClass(); @@ -433,236 +353,160 @@ struct FFlowDataPinOutputProperty_Class : public FFlowDataPinProperty #if WITH_EDITOR , ClassFilter(InClassFilter) #endif - { } - - FLOW_API virtual EFlowPinType GetFlowPinType() const override { return EFlowPinType::Class; } - -#if WITH_EDITOR - UClass* DeriveMetaClass(const FProperty& MetaDataProperty) const; - FLOW_API static UClass* TryGetMetaClassFromProperty(const FProperty& MetaDataProperty); -#endif - - const FSoftClassPath& GetAsSoftClass() const { return Value; } - UClass* GetResolvedClass() const { return Value.ResolveClass(); } -}; - -// Wrapper for FFlowDataPinProperty that is used for flow nodes that add -// dynamic properties, with associated data pins, on the flow node instance -// (as opposed to C++ or blueprint compile-time). -USTRUCT(BlueprintType, DisplayName = "Flow Named DataPin Property") -struct FFlowNamedDataPinProperty -{ - GENERATED_BODY() - -public: - - // Name of this instanced property - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) - FName Name; - - // DataPinProperty payload - UPROPERTY(EditAnywhere, Category = DataPins, meta = (ExcludeBaseStruct, NoClear)) - TInstancedStruct DataPinProperty; - -public: - - FFlowNamedDataPinProperty() { } - - bool IsValid() const { return Name != NAME_None && DataPinProperty.GetPtr() != nullptr; } - - bool IsInputProperty() const; - bool IsOutputProperty() const; - -#if WITH_EDITOR - FFlowPin CreateFlowPin() const { return FFlowDataPinProperty::CreateFlowPin(Name, DataPinProperty); } + { + } - FLOW_API FText BuildHeaderText() const; -#endif // WITH_EDITOR + UClass* GetObjectValue() const { return Value.ResolveClass(); } }; // Wrapper-structs for a blueprint defaulted input pin types -// "Hidden" to keep them out of the TInstancedStruct selection list (but they can still be authored as properties in blueprint) -// "DefaultForInputFlowPin" to change them to an Defaulted-Input property (rather than an output property) +// "Hidden" to keep them out of the TInstancedStruct selection list (but they can still be authored as properties in blueprint) +// "DefaultForInputFlowPin" to change them to a Defaulted-Input property (rather than an output property) -USTRUCT(BlueprintType, DisplayName = "Bool - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Bool")) +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Bool - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Bool", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinInputProperty_Bool : public FFlowDataPinOutputProperty_Bool { GENERATED_BODY() FFlowDataPinInputProperty_Bool(bool InValue = false) : Super(InValue) { } - - FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "Int64 - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Int")) +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Int64 - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Int64", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinInputProperty_Int64 : public FFlowDataPinOutputProperty_Int64 { GENERATED_BODY() FFlowDataPinInputProperty_Int64(int64 InValue = 0) : Super(InValue) { } - - FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "Int - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Int")) +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Int - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Int", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinInputProperty_Int32 : public FFlowDataPinOutputProperty_Int32 { GENERATED_BODY() FFlowDataPinInputProperty_Int32(int32 InValue = 0) : Super(InValue) { } - - FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "Double (float64) - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Float")) +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Double (float64) - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Double", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinInputProperty_Double : public FFlowDataPinOutputProperty_Double { GENERATED_BODY() FFlowDataPinInputProperty_Double(double InValue = 0.0) : Super(InValue) { } - - FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "Float - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Float")) +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Float - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Float", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinInputProperty_Float : public FFlowDataPinOutputProperty_Float { GENERATED_BODY() FFlowDataPinInputProperty_Float(float InValue = 0.0f) : Super(InValue) { } - - FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "Name - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Name")) +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Name - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Name", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinInputProperty_Name : public FFlowDataPinOutputProperty_Name { GENERATED_BODY() FFlowDataPinInputProperty_Name() : Super() { } FFlowDataPinInputProperty_Name(const FName& InValue) : Super(InValue) { } - - FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "String - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "String")) +USTRUCT(BlueprintType, DisplayName = "String - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "String", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinInputProperty_String : public FFlowDataPinOutputProperty_String { GENERATED_BODY() FFlowDataPinInputProperty_String() : Super() { } FFlowDataPinInputProperty_String(const FString& InValue) : Super(InValue) { } - - FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "Text - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Text")) +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Text - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Text", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinInputProperty_Text : public FFlowDataPinOutputProperty_Text { GENERATED_BODY() FFlowDataPinInputProperty_Text() : Super() { } FFlowDataPinInputProperty_Text(const FText& InValue) : Super(InValue) { } - - FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "Enum - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Enum")) +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Enum - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Enum", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinInputProperty_Enum : public FFlowDataPinOutputProperty_Enum { GENERATED_BODY() FFlowDataPinInputProperty_Enum() : Super() { } FFlowDataPinInputProperty_Enum(const FName& InValue, UEnum* InEnumClass) : Super(InValue, InEnumClass) { } - - FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "Vector - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Vector")) +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Vector - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Vector", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinInputProperty_Vector : public FFlowDataPinOutputProperty_Vector { GENERATED_BODY() FFlowDataPinInputProperty_Vector() : Super() { } FFlowDataPinInputProperty_Vector(const FVector& InValue) : Super(InValue) { } - - FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "Rotator - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Rotator")) +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Rotator - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Rotator", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinInputProperty_Rotator : public FFlowDataPinOutputProperty_Rotator { GENERATED_BODY() FFlowDataPinInputProperty_Rotator() : Super() { } FFlowDataPinInputProperty_Rotator(const FRotator& InValue) : Super(InValue) { } - - FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "Transform - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Transform")) +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Transform - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Transform", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinInputProperty_Transform : public FFlowDataPinOutputProperty_Transform { GENERATED_BODY() FFlowDataPinInputProperty_Transform() : Super() { } FFlowDataPinInputProperty_Transform(const FTransform& InValue) : Super(InValue) { } - - FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "GameplayTag - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "GameplayTag")) +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] GameplayTag - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "GameplayTag", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinInputProperty_GameplayTag : public FFlowDataPinOutputProperty_GameplayTag { GENERATED_BODY() FFlowDataPinInputProperty_GameplayTag() : Super() { } FFlowDataPinInputProperty_GameplayTag(const FGameplayTag& InValue) : Super(InValue) { } - - FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "GameplayTagContainer - Input Flow DataPin Property", meta = (DefaultForInputFlowPin, FlowPinType = "GameplayTagContainer")) +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] GameplayTagContainer - Input Flow DataPin Property", meta = (DefaultForInputFlowPin, FlowPinType = "GameplayTagContainer", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinInputProperty_GameplayTagContainer : public FFlowDataPinOutputProperty_GameplayTagContainer { GENERATED_BODY() FFlowDataPinInputProperty_GameplayTagContainer() : Super() { } FFlowDataPinInputProperty_GameplayTagContainer(const FGameplayTagContainer& InValue) : Super(InValue) { } - - FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "InstancedStruct - Input Flow DataPin Property", meta = (DefaultForInputFlowPin, FlowPinType = "InstancedStruct")) +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] InstancedStruct - Input Flow DataPin Property", meta = (DefaultForInputFlowPin, FlowPinType = "InstancedStruct", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinInputProperty_InstancedStruct : public FFlowDataPinOutputProperty_InstancedStruct { GENERATED_BODY() FFlowDataPinInputProperty_InstancedStruct() : Super() { } FFlowDataPinInputProperty_InstancedStruct(const FInstancedStruct& InValue) : Super(InValue) { } - - FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "Object - Input Flow DataPin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Object")) +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Object - Input Flow DataPin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Object", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinInputProperty_Object : public FFlowDataPinOutputProperty_Object { GENERATED_BODY() FFlowDataPinInputProperty_Object() : Super() { } FFlowDataPinInputProperty_Object(UObject* InValue, UClass* InClassFilter) : Super(InValue, InClassFilter) { } - - FLOW_API virtual bool IsInputProperty() const override { return true; } }; -USTRUCT(BlueprintType, DisplayName = "Class - Input Flow DataPin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Class")) +USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Class - Input Flow DataPin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Class", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinInputProperty_Class : public FFlowDataPinOutputProperty_Class { GENERATED_BODY() FFlowDataPinInputProperty_Class() : Super() { } FFlowDataPinInputProperty_Class(const FSoftClassPath& InValue, UClass* InClassFilter) : Super(InValue, InClassFilter) { } - - FLOW_API virtual bool IsInputProperty() const override { return true; } }; - diff --git a/Source/Flow/Public/Types/FlowDataPinPropertyToValueMigration.h b/Source/Flow/Public/Types/FlowDataPinPropertyToValueMigration.h new file mode 100644 index 000000000..4233d2d83 --- /dev/null +++ b/Source/Flow/Public/Types/FlowDataPinPropertyToValueMigration.h @@ -0,0 +1,409 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Types/FlowDataPinValuesStandard.h" + +// #FlowDataPinLegacy +#include "Types/FlowDataPinProperties.h" +// -- + +// #FlowDataPinLegacy + +// Templated helper to migrate simple types (scalar Value to TArray Values) +template +static bool MigrateSimpleType(const TInstancedStruct& Source, TInstancedStruct& Target) +{ + if (!Source.IsValid() || !Source.GetPtr()) + { + return false; + } + + const SourceType* SourceData = Source.GetPtr(); + Target.InitializeAsScriptStruct(TargetType::StaticStruct()); + TargetType* TargetData = Target.GetMutablePtr(); + if (TargetData) + { + TargetData->Values.Add(SourceData->Value); + return true; + } + return false; +} + +// Specialization for FGameplayTagContainer +template <> +bool MigrateSimpleType(const TInstancedStruct& Source, TInstancedStruct& Target) +{ + if (!Source.IsValid() || !Source.GetPtr()) + { + return false; + } + + const FFlowDataPinOutputProperty_GameplayTagContainer* SourceData = Source.GetPtr(); + Target.InitializeAsScriptStruct(FFlowDataPinValue_GameplayTagContainer::StaticStruct()); + FFlowDataPinValue_GameplayTagContainer* TargetData = Target.GetMutablePtr(); + if (TargetData) + { + TargetData->Values.AppendTags(SourceData->Value); + return true; + } + return false; +} + +// Specialization for FGameplayTagContainer (Input) +template <> +bool MigrateSimpleType< + FFlowDataPinInputProperty_GameplayTagContainer, + FFlowDataPinValue_GameplayTagContainer, + FGameplayTagContainer>( + const TInstancedStruct& Source, + TInstancedStruct& Target) +{ + if (!Source.IsValid() || !Source.GetPtr()) + { + return false; + } + + const FFlowDataPinInputProperty_GameplayTagContainer* SourceData = + Source.GetPtr(); + + Target.InitializeAsScriptStruct(FFlowDataPinValue_GameplayTagContainer::StaticStruct()); + FFlowDataPinValue_GameplayTagContainer* TargetData = + Target.GetMutablePtr(); + + if (TargetData) + { + TargetData->Values.AppendTags(SourceData->Value); + return true; + } + return false; +} + +// Specialization for Enum (handles Value and EnumClass) +template <> +bool MigrateSimpleType(const TInstancedStruct& Source, TInstancedStruct& Target) +{ + if (!Source.IsValid() || !Source.GetPtr()) + { + return false; + } + + const FFlowDataPinOutputProperty_Enum* SourceData = Source.GetPtr(); + Target.InitializeAsScriptStruct(FFlowDataPinValue_Enum::StaticStruct()); + FFlowDataPinValue_Enum* TargetData = Target.GetMutablePtr(); + if (TargetData) + { + TargetData->Values.Add(SourceData->Value); + TargetData->EnumClass = SourceData->EnumClass; +#if WITH_EDITORONLY_DATA + TargetData->EnumName = SourceData->EnumName; +#endif + return true; + } + return false; +} + +// Specialization for Enum (Input) +template <> +bool MigrateSimpleType(const TInstancedStruct& Source, TInstancedStruct& Target) +{ + if (!Source.IsValid() || !Source.GetPtr()) + { + return false; + } + + const FFlowDataPinInputProperty_Enum* SourceData = Source.GetPtr(); + Target.InitializeAsScriptStruct(FFlowDataPinValue_Enum::StaticStruct()); + FFlowDataPinValue_Enum* TargetData = Target.GetMutablePtr(); + if (TargetData) + { + TargetData->Values.Add(SourceData->Value); + TargetData->EnumClass = SourceData->EnumClass; +#if WITH_EDITORONLY_DATA + TargetData->EnumName = SourceData->EnumName; +#endif + return true; + } + return false; +} + +// Specialization for Object (handles ReferenceValue/InlineValue and ClassFilter) +template <> +bool MigrateSimpleType(const TInstancedStruct& Source, TInstancedStruct& Target) +{ + if (!Source.IsValid() || !Source.GetPtr()) + { + return false; + } + + const FFlowDataPinOutputProperty_Object* SourceData = Source.GetPtr(); + UScriptStruct* TargetStruct = FFlowDataPinValue_Object::StaticStruct(); + Target.InitializeAsScriptStruct(TargetStruct); + + { + FFlowDataPinValue_Object* TargetData = Target.GetMutablePtr(); + if (TargetData && SourceData->ReferenceValue) + { + TargetData->Values.Add(SourceData->ReferenceValue); +#if WITH_EDITORONLY_DATA + TargetData->ClassFilter = SourceData->ClassFilter; +#endif + return true; + } + } + return false; +} + +// Specialization for Object (Input) +template <> +bool MigrateSimpleType(const TInstancedStruct& Source, TInstancedStruct& Target) +{ + if (!Source.IsValid() || !Source.GetPtr()) + { + return false; + } + + const FFlowDataPinInputProperty_Object* SourceData = Source.GetPtr(); + UScriptStruct* TargetStruct = FFlowDataPinValue_Object::StaticStruct(); + Target.InitializeAsScriptStruct(TargetStruct); + + { + FFlowDataPinValue_Object* TargetData = Target.GetMutablePtr(); + if (TargetData && SourceData->ReferenceValue) + { + TargetData->Values.Add(SourceData->ReferenceValue); +#if WITH_EDITORONLY_DATA + TargetData->ClassFilter = SourceData->ClassFilter; +#endif + return true; + } + } + return false; +} + +// Specialization for Class (handles Value and ClassFilter) +template <> +bool MigrateSimpleType(const TInstancedStruct& Source, TInstancedStruct& Target) +{ + if (!Source.IsValid() || !Source.GetPtr()) + { + return false; + } + + const FFlowDataPinOutputProperty_Class* SourceData = Source.GetPtr(); + Target.InitializeAsScriptStruct(FFlowDataPinValue_Class::StaticStruct()); + FFlowDataPinValue_Class* TargetData = Target.GetMutablePtr(); + if (TargetData) + { + TargetData->Values.Add(SourceData->Value); +#if WITH_EDITORONLY_DATA + TargetData->ClassFilter = SourceData->ClassFilter; +#endif + return true; + } + return false; +} + +// Specialization for Class (Input) +template <> +bool MigrateSimpleType(const TInstancedStruct& Source, TInstancedStruct& Target) +{ + if (!Source.IsValid() || !Source.GetPtr()) + { + return false; + } + + const FFlowDataPinInputProperty_Class* SourceData = Source.GetPtr(); + Target.InitializeAsScriptStruct(FFlowDataPinValue_Class::StaticStruct()); + FFlowDataPinValue_Class* TargetData = Target.GetMutablePtr(); + if (TargetData) + { + TargetData->Values.Add(SourceData->Value); +#if WITH_EDITORONLY_DATA + TargetData->ClassFilter = SourceData->ClassFilter; +#endif + return true; + } + return false; +} + +bool FFlowNamedDataPinProperty::FixupDataPinProperty() +{ + // Skip if no data to migrate or target already has data + if (!DataPinProperty.IsValid() || DataPinValue.IsValid()) + { + DataPinProperty.Reset(); + return false; + } + + // Get source struct type + const UScriptStruct* SourceStruct = DataPinProperty.GetScriptStruct(); + if (!SourceStruct || !SourceStruct->IsChildOf(FFlowDataPinProperty::StaticStruct())) + { + DataPinProperty.Reset(); + return false; + } + + // Map source struct to target struct and migrate data + bool bSuccess = false; + + // Bool (Output and Input) + if (SourceStruct == FFlowDataPinOutputProperty_Bool::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + else if (SourceStruct == FFlowDataPinInputProperty_Bool::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + // Int32 (Output and Input) + else if (SourceStruct == FFlowDataPinOutputProperty_Int32::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + else if (SourceStruct == FFlowDataPinInputProperty_Int32::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + // Int64 (Output and Input) + else if (SourceStruct == FFlowDataPinOutputProperty_Int64::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + else if (SourceStruct == FFlowDataPinInputProperty_Int64::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + // Float (Output and Input) + else if (SourceStruct == FFlowDataPinOutputProperty_Float::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + else if (SourceStruct == FFlowDataPinInputProperty_Float::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + // Double (Output and Input) + else if (SourceStruct == FFlowDataPinOutputProperty_Double::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + else if (SourceStruct == FFlowDataPinInputProperty_Double::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + // Name (Output and Input) + else if (SourceStruct == FFlowDataPinOutputProperty_Name::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + else if (SourceStruct == FFlowDataPinInputProperty_Name::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + // String (Output and Input) + else if (SourceStruct == FFlowDataPinOutputProperty_String::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + else if (SourceStruct == FFlowDataPinInputProperty_String::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + // Text (Output and Input) + else if (SourceStruct == FFlowDataPinOutputProperty_Text::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + else if (SourceStruct == FFlowDataPinInputProperty_Text::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + // Enum (Output and Input) + else if (SourceStruct == FFlowDataPinOutputProperty_Enum::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + else if (SourceStruct == FFlowDataPinInputProperty_Enum::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + // Vector (Output and Input) + else if (SourceStruct == FFlowDataPinOutputProperty_Vector::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + else if (SourceStruct == FFlowDataPinInputProperty_Vector::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + // Rotator (Output and Input) + else if (SourceStruct == FFlowDataPinOutputProperty_Rotator::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + else if (SourceStruct == FFlowDataPinInputProperty_Rotator::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + // Transform (Output and Input) + else if (SourceStruct == FFlowDataPinOutputProperty_Transform::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + else if (SourceStruct == FFlowDataPinInputProperty_Transform::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + // GameplayTag (Output and Input) + else if (SourceStruct == FFlowDataPinOutputProperty_GameplayTag::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + else if (SourceStruct == FFlowDataPinInputProperty_GameplayTag::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + // GameplayTagContainer (Output and Input) + else if (SourceStruct == FFlowDataPinOutputProperty_GameplayTagContainer::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + else if (SourceStruct == FFlowDataPinInputProperty_GameplayTagContainer::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + // InstancedStruct (Output and Input) + else if (SourceStruct == FFlowDataPinOutputProperty_InstancedStruct::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + else if (SourceStruct == FFlowDataPinInputProperty_InstancedStruct::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + // Object (Output and Input) + else if (SourceStruct == FFlowDataPinOutputProperty_Object::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + else if (SourceStruct == FFlowDataPinInputProperty_Object::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + // Class (Output and Input) + else if (SourceStruct == FFlowDataPinOutputProperty_Class::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + else if (SourceStruct == FFlowDataPinInputProperty_Class::StaticStruct()) + { + bSuccess = MigrateSimpleType(DataPinProperty, DataPinValue); + } + + // Clear the deprecated property + DataPinProperty.Reset(); + + return bSuccess; +} +// -- diff --git a/Source/Flow/Public/Types/FlowDataPinResults.h b/Source/Flow/Public/Types/FlowDataPinResults.h index d186d6ea9..9b442604d 100644 --- a/Source/Flow/Public/Types/FlowDataPinResults.h +++ b/Source/Flow/Public/Types/FlowDataPinResults.h @@ -9,8 +9,12 @@ #include "FlowDataPinResults.generated.h" struct FInstancedStruct; +struct FFlowDataPinValue; + +// #FlowDataPinLegacy struct FFlowDataPinOutputProperty_Object; struct FFlowDataPinOutputProperty_Class; +// -- USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result") struct FFlowDataPinResult @@ -20,25 +24,31 @@ struct FFlowDataPinResult public: // Result for the DataPin resolve attempt - UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + UPROPERTY(BlueprintReadWrite, Category = DataPins) EFlowDataPinResolveResult Result = EFlowDataPinResolveResult::FailedUnimplemented; public: - FLOW_API FFlowDataPinResult() { } + FLOW_API explicit FFlowDataPinResult() = default; FLOW_API explicit FFlowDataPinResult(EFlowDataPinResolveResult InResult) : Result(InResult) { } + + template + explicit FFlowDataPinResult(const TFlowDataPinValueSubclass& InValue) : Result(EFlowDataPinResolveResult::Success), ResultValue(TInstancedStruct::Make(InValue)) {} + +public: + UPROPERTY() + TInstancedStruct ResultValue; }; -// Recommend implementing FFlowDataPinResult... for every EFlowPinType -FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); +// #FlowDataPinLegacy -USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Bool)") +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Bool)", meta = (DeprecatedClass)) struct FFlowDataPinResult_Bool : public FFlowDataPinResult { GENERATED_BODY() public: - UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + UPROPERTY(BlueprintReadWrite, Category = DataPins) bool Value = false; public: @@ -51,14 +61,14 @@ struct FFlowDataPinResult_Bool : public FFlowDataPinResult { } }; -USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Int)") +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Int)", meta = (DeprecatedClass)) struct FFlowDataPinResult_Int : public FFlowDataPinResult { GENERATED_BODY() public: - UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + UPROPERTY(BlueprintReadWrite, Category = DataPins) int64 Value = 0; public: @@ -71,14 +81,14 @@ struct FFlowDataPinResult_Int : public FFlowDataPinResult { } }; -USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Float)") +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Float)", meta = (DeprecatedClass)) struct FFlowDataPinResult_Float : public FFlowDataPinResult { GENERATED_BODY() public: - UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + UPROPERTY(BlueprintReadWrite, Category = DataPins) double Value = 0; public: @@ -91,14 +101,14 @@ struct FFlowDataPinResult_Float : public FFlowDataPinResult { } }; -USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Name)") +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Name)", meta = (DeprecatedClass)) struct FFlowDataPinResult_Name : public FFlowDataPinResult { GENERATED_BODY() public: - UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + UPROPERTY(BlueprintReadWrite, Category = DataPins) FName Value = NAME_None; public: @@ -115,14 +125,14 @@ struct FFlowDataPinResult_Name : public FFlowDataPinResult FLOW_API void SetValue(const FText& FromText) { Value = FName(FromText.ToString()); } }; -USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (String)") +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (String)", meta = (DeprecatedClass)) struct FFlowDataPinResult_String : public FFlowDataPinResult { GENERATED_BODY() public: - UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + UPROPERTY(BlueprintReadWrite, Category = DataPins) FString Value; public: @@ -139,14 +149,14 @@ struct FFlowDataPinResult_String : public FFlowDataPinResult FLOW_API void SetValue(const FText& FromText) { Value = FromText.ToString(); } }; -USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Text)") +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Text)", meta = (DeprecatedClass)) struct FFlowDataPinResult_Text : public FFlowDataPinResult { GENERATED_BODY() public: - UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + UPROPERTY(BlueprintReadWrite, Category = DataPins) FText Value; public: @@ -163,7 +173,7 @@ struct FFlowDataPinResult_Text : public FFlowDataPinResult FLOW_API void SetValue(const FText& FromText) { Value = FromText; } }; -USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Enum)") +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Enum)", meta = (DeprecatedClass)) struct FFlowDataPinResult_Enum : public FFlowDataPinResult { GENERATED_BODY() @@ -171,11 +181,11 @@ struct FFlowDataPinResult_Enum : public FFlowDataPinResult public: // The selected enum Value - UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + UPROPERTY(BlueprintReadWrite, Category = DataPins) FName Value = NAME_None; // Class for this enum - UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + UPROPERTY(BlueprintReadWrite, Category = DataPins) TObjectPtr EnumClass = nullptr; public: @@ -238,14 +248,14 @@ struct FFlowDataPinResult_Enum : public FFlowDataPinResult } }; -USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Vector)") +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Vector)", meta = (DeprecatedClass)) struct FFlowDataPinResult_Vector : public FFlowDataPinResult { GENERATED_BODY() public: - UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + UPROPERTY(BlueprintReadWrite, Category = DataPins) FVector Value = FVector::ZeroVector; public: @@ -258,14 +268,14 @@ struct FFlowDataPinResult_Vector : public FFlowDataPinResult { } }; -USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Rotator)") +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Rotator)", meta = (DeprecatedClass)) struct FFlowDataPinResult_Rotator : public FFlowDataPinResult { GENERATED_BODY() public: - UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + UPROPERTY(BlueprintReadWrite, Category = DataPins) FRotator Value = FRotator::ZeroRotator; public: @@ -278,14 +288,14 @@ struct FFlowDataPinResult_Rotator : public FFlowDataPinResult { } }; -USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Transform)") +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Transform)", meta = (DeprecatedClass)) struct FFlowDataPinResult_Transform : public FFlowDataPinResult { GENERATED_BODY() public: - UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + UPROPERTY(BlueprintReadWrite, Category = DataPins) FTransform Value; public: @@ -298,14 +308,14 @@ struct FFlowDataPinResult_Transform : public FFlowDataPinResult { } }; -USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (GameplayTag)") +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (GameplayTag)", meta = (DeprecatedClass)) struct FFlowDataPinResult_GameplayTag : public FFlowDataPinResult { GENERATED_BODY() public: - UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + UPROPERTY(BlueprintReadWrite, Category = DataPins) FGameplayTag Value; public: @@ -318,14 +328,14 @@ struct FFlowDataPinResult_GameplayTag : public FFlowDataPinResult { } }; -USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (GameplayTagContainer)") +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (GameplayTagContainer)", meta = (DeprecatedClass)) struct FFlowDataPinResult_GameplayTagContainer : public FFlowDataPinResult { GENERATED_BODY() public: - UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + UPROPERTY(BlueprintReadWrite, Category = DataPins) FGameplayTagContainer Value; public: @@ -338,14 +348,14 @@ struct FFlowDataPinResult_GameplayTagContainer : public FFlowDataPinResult { } }; -USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (InstancedStruct)") +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (InstancedStruct)", meta = (DeprecatedClass)) struct FFlowDataPinResult_InstancedStruct : public FFlowDataPinResult { GENERATED_BODY() public: - UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + UPROPERTY(BlueprintReadWrite, Category = DataPins) FInstancedStruct Value; public: @@ -358,14 +368,14 @@ struct FFlowDataPinResult_InstancedStruct : public FFlowDataPinResult { } }; -USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Object)") +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Object)", meta = (DeprecatedClass)) struct FFlowDataPinResult_Object : public FFlowDataPinResult { GENERATED_BODY() public: - UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + UPROPERTY(BlueprintReadWrite, Category = DataPins) TObjectPtr Value; public: @@ -374,12 +384,11 @@ struct FFlowDataPinResult_Object : public FFlowDataPinResult FLOW_API FFlowDataPinResult_Object(EFlowDataPinResolveResult InResult) : Super(InResult) { } FLOW_API FFlowDataPinResult_Object(UObject* InValue); - FLOW_API void SetValueFromPropertyWrapper(const FFlowDataPinOutputProperty_Object& InPropertyWrapper); FLOW_API FORCEINLINE void SetValueFromSoftPath(const FSoftObjectPath& SoftPath) { Value = SoftPath.ResolveObject(); } FLOW_API FORCEINLINE void SetValueFromObjectPtr(UObject* ObjectPtr) { Value = ObjectPtr; } }; -USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Class)") +USTRUCT(BlueprintType, DisplayName = "Flow DataPin Result (Class)", meta = (DeprecatedClass)) struct FFlowDataPinResult_Class : public FFlowDataPinResult { GENERATED_BODY() @@ -388,12 +397,12 @@ struct FFlowDataPinResult_Class : public FFlowDataPinResult // SoftClassPath version of the result // (both the SoftClassPath and the UClass (if available) will be set for the result) - UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + UPROPERTY(BlueprintReadWrite, Category = DataPins) FSoftClassPath ValuePath; // UClass version of the result // (both the SoftClassPath and the UClass (if available) will be set for the result) - UPROPERTY(Transient, BlueprintReadWrite, Category = DataPins) + UPROPERTY(BlueprintReadWrite, Category = DataPins) TObjectPtr ValueClass = nullptr; public: @@ -403,7 +412,6 @@ struct FFlowDataPinResult_Class : public FFlowDataPinResult FLOW_API FFlowDataPinResult_Class(const FSoftClassPath& InValuePath); FLOW_API FFlowDataPinResult_Class(UClass* InValueClass); - FLOW_API void SetValueFromPropertyWrapper(const FFlowDataPinOutputProperty_Class& PropertyWrapper); FLOW_API void SetValueSoftClassAndClassPtr(const FSoftClassPath& SoftPath, UClass* ObjectPtr); FLOW_API void SetValueFromSoftPath(const FSoftObjectPath& SoftObjectPath); FLOW_API FORCEINLINE void SetValueFromObjectPtr(UClass* ClassPtr) { SetValueSoftClassAndClassPtr(FSoftClassPath(ClassPtr), ClassPtr); } @@ -411,3 +419,4 @@ struct FFlowDataPinResult_Class : public FFlowDataPinResult FLOW_API UClass* GetOrResolveClass() const { return IsValid(ValueClass) ? ValueClass.Get() : ValuePath.ResolveClass(); } FLOW_API FSoftClassPath GetAsSoftClass() const; }; +// -- \ No newline at end of file diff --git a/Source/Flow/Public/Types/FlowDataPinValue.h b/Source/Flow/Public/Types/FlowDataPinValue.h new file mode 100644 index 000000000..983310c7c --- /dev/null +++ b/Source/Flow/Public/Types/FlowDataPinValue.h @@ -0,0 +1,63 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "UObject/NameTypes.h" +#include "UObject/ObjectPtr.h" + +#include "FlowPinEnums.h" +#include "FlowPinType.h" +#include "FlowDataPinValue.generated.h" + +struct FFlowDataPinResult; +class FProperty; +class UObject; +class IPropertyHandle; +class UScriptStruct; + +USTRUCT() +struct FFlowDataPinValue +{ + GENERATED_BODY() + + friend class FFlowDataPinValueCustomization; + +public: + // IF a pin was created from this property, this is the cached pin name that was used + // (which can be used in UFlowDataPinBlueprintLibrary::ResolveAs... functions to lookup the correct pin by name) + UPROPERTY(VisibleAnywhere, Category = DataPins) + mutable FName PropertyPinName; + +#if WITH_EDITORONLY_DATA + UPROPERTY(EditAnywhere, Category = DataPins) + bool bIsInputPin = false; + + UPROPERTY(EditAnywhere, Category = DataPins) + EFlowDataMultiType MultiType = EFlowDataMultiType::Single; +#endif + + FFlowDataPinValue() {} + virtual ~FFlowDataPinValue() {} + +#if WITH_EDITOR + FLOW_API bool IsInputPin() const { return bIsInputPin; } + FLOW_API bool IsArray() const { FLOW_ASSERT_ENUM_MAX(EFlowDataMultiType, 2); return MultiType == EFlowDataMultiType::Array; } + + // Helper to get the Values property handle (implemented by subclasses or via type system) + FLOW_API virtual TSharedPtr GetValuesPropertyHandle() const PURE_VIRTUAL(GetValuesPropertyHandle, return nullptr;); +#endif + + // Pin Type Name (identity) + FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const PURE_VIRTUAL(GetPinTypeName, return FFlowPinType::PinTypeNameUnknown;); + + // (optional) Get the field type if one exists (only used for UEnum For Now) + FLOW_API virtual UField* GetFieldType() const { return nullptr; } + + // (optional) + FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const { return false; } + + // Resolve the registered data pin type + FLOW_API const FFlowPinType* LookupPinType() const; + + FLOW_API static const FString StringArraySeparator; +}; \ No newline at end of file diff --git a/Source/Flow/Public/Types/FlowDataPinValuesStandard.h b/Source/Flow/Public/Types/FlowDataPinValuesStandard.h new file mode 100644 index 000000000..462c62739 --- /dev/null +++ b/Source/Flow/Public/Types/FlowDataPinValuesStandard.h @@ -0,0 +1,496 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Types/FlowDataPinValue.h" +#include "Types/FlowPinTypesStandard.h" + +#include "StructUtils/InstancedStruct.h" +#include "GameplayTagContainer.h" +#include "UObject/SoftObjectPtr.h" +#include "UObject/SoftObjectPath.h" +#include "UObject/Class.h" +#include "Math/Vector.h" +#include "Math/Rotator.h" +#include "Math/Transform.h" + +#include "FlowDataPinValuesStandard.generated.h" + +//====================================================================== +// Bool +//====================================================================== +USTRUCT(MinimalApi, BlueprintType, DisplayName = "Bool - Flow DataPin Value", meta = (FlowPinType = "Bool", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructBool")) +struct FFlowDataPinValue_Bool : public FFlowDataPinValue +{ + GENERATED_BODY() + +public: + using PinType = FFlowPinType_Bool; + using ValueType = PinType::ValueType; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + TArray Values{ false }; + + FLOW_API FFlowDataPinValue_Bool() = default; + FLOW_API FFlowDataPinValue_Bool(ValueType InValue); + FLOW_API FFlowDataPinValue_Bool(const TArray& InValues); + + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + virtual bool TryConvertValuesToString(FString& OutString) const override; +}; + +//====================================================================== +// Int (int32) +//====================================================================== +USTRUCT(MinimalApi, BlueprintType, DisplayName = "Int - Flow DataPin Value", meta = (FlowPinType = "Int", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructInt")) +struct FFlowDataPinValue_Int : public FFlowDataPinValue +{ + GENERATED_BODY() + +public: + using PinType = FFlowPinType_Int; + using ValueType = PinType::ValueType; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + TArray Values{ 0 }; + + FLOW_API FFlowDataPinValue_Int() = default; + FLOW_API FFlowDataPinValue_Int(ValueType InValue); + FLOW_API FFlowDataPinValue_Int(const TArray& InValues); + + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + virtual bool TryConvertValuesToString(FString& OutString) const override; +}; + +//====================================================================== +// Int64 +//====================================================================== +USTRUCT(MinimalApi, BlueprintType, DisplayName = "Int64 - Flow DataPin Value", meta = (FlowPinType = "Int64", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructInt64")) +struct FFlowDataPinValue_Int64 : public FFlowDataPinValue +{ + GENERATED_BODY() + +public: + using PinType = FFlowPinType_Int64; + using ValueType = PinType::ValueType; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + TArray Values{ 0 }; + + FLOW_API FFlowDataPinValue_Int64() = default; + FLOW_API FFlowDataPinValue_Int64(ValueType InValue); + FLOW_API FFlowDataPinValue_Int64(const TArray& InValues); + + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + virtual bool TryConvertValuesToString(FString& OutString) const override; +}; + +//====================================================================== +// Float +//====================================================================== +USTRUCT(MinimalApi, BlueprintType, DisplayName = "Float - Flow DataPin Value", meta = (FlowPinType = "Float", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructFloat")) +struct FFlowDataPinValue_Float : public FFlowDataPinValue +{ + GENERATED_BODY() + +public: + using PinType = FFlowPinType_Float; + using ValueType = PinType::ValueType; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + TArray Values{ 0.0f }; + + FLOW_API FFlowDataPinValue_Float() = default; + FLOW_API FFlowDataPinValue_Float(ValueType InValue); + FLOW_API FFlowDataPinValue_Float(const TArray& InValues); + + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + virtual bool TryConvertValuesToString(FString& OutString) const override; +}; + +//====================================================================== +// Double +//====================================================================== +USTRUCT(MinimalApi, BlueprintType, DisplayName = "Double - Flow DataPin Value", meta = (FlowPinType = "Double", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructDouble")) +struct FFlowDataPinValue_Double : public FFlowDataPinValue +{ + GENERATED_BODY() + +public: + using PinType = FFlowPinType_Double; + using ValueType = PinType::ValueType; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + TArray Values{ 0.0 }; + + FLOW_API FFlowDataPinValue_Double() = default; + FLOW_API FFlowDataPinValue_Double(ValueType InValue); + FLOW_API FFlowDataPinValue_Double(const TArray& InValues); + + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + virtual bool TryConvertValuesToString(FString& OutString) const override; +}; + +//====================================================================== +// Name +//====================================================================== +USTRUCT(MinimalApi, BlueprintType, DisplayName = "Name - Flow DataPin Value", meta = (FlowPinType = "Name", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructName")) +struct FFlowDataPinValue_Name : public FFlowDataPinValue +{ + GENERATED_BODY() + +public: + using PinType = FFlowPinType_Name; + using ValueType = PinType::ValueType; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + TArray Values{ NAME_None }; + + FLOW_API FFlowDataPinValue_Name() = default; + FLOW_API FFlowDataPinValue_Name(const ValueType& InValue); + FLOW_API FFlowDataPinValue_Name(const TArray& InValues); + + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + virtual bool TryConvertValuesToString(FString& OutString) const override; +}; + +//====================================================================== +// String +//====================================================================== +USTRUCT(MinimalApi, BlueprintType, DisplayName = "String - Flow DataPin Value", meta = (FlowPinType = "String", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructString")) +struct FFlowDataPinValue_String : public FFlowDataPinValue +{ + GENERATED_BODY() + +public: + using PinType = FFlowPinType_String; + using ValueType = PinType::ValueType; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + TArray Values; + + FLOW_API FFlowDataPinValue_String() = default; + FLOW_API FFlowDataPinValue_String(const ValueType& InValue); + FLOW_API FFlowDataPinValue_String(const TArray& InValues); + + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + virtual bool TryConvertValuesToString(FString& OutString) const override; +}; + +//====================================================================== +// Text +//====================================================================== +USTRUCT(MinimalApi, BlueprintType, DisplayName = "Text - Flow DataPin Value", meta = (FlowPinType = "Text", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructText")) +struct FFlowDataPinValue_Text : public FFlowDataPinValue +{ + GENERATED_BODY() + +public: + using PinType = FFlowPinType_Text; + using ValueType = PinType::ValueType; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + TArray Values; + + FLOW_API FFlowDataPinValue_Text() = default; + FLOW_API FFlowDataPinValue_Text(const ValueType& InValue); + FLOW_API FFlowDataPinValue_Text(const TArray& InValues); + + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + virtual bool TryConvertValuesToString(FString& OutString) const override; +}; + +//====================================================================== +// Enum +//====================================================================== +USTRUCT(MinimalApi, BlueprintType, DisplayName = "Enum - Flow DataPin Value", meta = (FlowPinType = "Enum", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructEnum")) +struct FFlowDataPinValue_Enum : public FFlowDataPinValue +{ + GENERATED_BODY() + +public: + using PinType = FFlowPinType_Enum; + using ValueType = PinType::ValueType; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + TArray Values; + + // Enum asset reference (advanced) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins, meta = (NoClear, AdvancedDisplay)) + TSoftObjectPtr EnumClass; + +#if WITH_EDITORONLY_DATA + // Native C++ enum name (advanced) + UPROPERTY(EditAnywhere, Category = DataPins, meta = (AdvancedDisplay)) + FString EnumName; +#endif + + FLOW_API FFlowDataPinValue_Enum() = default; + FLOW_API FFlowDataPinValue_Enum(const TSoftObjectPtr& InEnumClass, const ValueType& InValue); + FLOW_API FFlowDataPinValue_Enum(const TSoftObjectPtr& InEnumClass, const TArray& InValues); + +#if WITH_EDITOR + FLOW_API void OnEnumNameChanged(); +#endif + + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + virtual UField* GetFieldType() const override; + virtual bool TryConvertValuesToString(FString& OutString) const override; + + // Helper templates + template + static bool TryGetEnumValueByName(const UEnum* EnumClass, const FName& EnumValueName, TUnrealNativeEnumType& OutValue, EGetByNameFlags GetByNameFlags = EGetByNameFlags::ErrorIfNotFound) + { + if (!IsValid(EnumClass)) + { + return false; + } + + const int32 EnumIndex = EnumClass->GetIndexByName(EnumValueName, GetByNameFlags); + if (EnumIndex != INDEX_NONE) + { + OutValue = static_cast(EnumClass->GetValueByIndex(EnumIndex)); + return true; + } + return false; + } + + template + EFlowDataPinResolveResult TryGetSingleEnumValue(TUnrealNativeEnumType& OutEnumValue, EFlowSingleFromArray SingleFromArray, EGetByNameFlags GetByNameFlags = EGetByNameFlags::ErrorIfNotFound) const + { + const int32 Index = EFlowSingleFromArray_Classifiers::ConvertToIndex(SingleFromArray, Values.Num()); + if (!Values.IsValidIndex(Index)) + { + return EFlowDataPinResolveResult::FailedInsufficientValues; + } + + UEnum* EnumClassPtr = EnumClass.LoadSynchronous(); + if (!TryGetEnumValueByName(EnumClassPtr, Values[Index], OutEnumValue, GetByNameFlags)) + { + return EFlowDataPinResolveResult::FailedUnknownEnumValue; + } + return EFlowDataPinResolveResult::Success; + } + + template + EFlowDataPinResolveResult TryGetAllNativeEnumValues(TArray& OutEnumValues, EGetByNameFlags GetByNameFlags = EGetByNameFlags::ErrorIfNotFound) const + { + if (Values.IsEmpty()) + { + return EFlowDataPinResolveResult::FailedInsufficientValues; + } + + UEnum* EnumClassPtr = EnumClass.LoadSynchronous(); + OutEnumValues.Reserve(Values.Num()); + + for (const ValueType& ValueName : Values) + { + TUnrealNativeEnumType EnumValue; + if (!TryGetEnumValueByName(EnumClassPtr, ValueName, EnumValue, GetByNameFlags)) + { + return EFlowDataPinResolveResult::FailedUnknownEnumValue; + } + OutEnumValues.Add(EnumValue); + } + return EFlowDataPinResolveResult::Success; + } +}; + +//====================================================================== +// Vector +//====================================================================== +USTRUCT(MinimalApi, BlueprintType, DisplayName = "Vector - Flow DataPin Value", meta = (FlowPinType = "Vector", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructVector")) +struct FFlowDataPinValue_Vector : public FFlowDataPinValue +{ + GENERATED_BODY() + +public: + using PinType = FFlowPinType_Vector; + using ValueType = PinType::ValueType; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + TArray Values{ FVector::ZeroVector }; + + FLOW_API FFlowDataPinValue_Vector() = default; + FLOW_API FFlowDataPinValue_Vector(const ValueType& InValue); + FLOW_API FFlowDataPinValue_Vector(const TArray& InValues); + + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + virtual bool TryConvertValuesToString(FString& OutString) const override; +}; + +//====================================================================== +// Rotator +//====================================================================== +USTRUCT(MinimalApi, BlueprintType, DisplayName = "Rotator - Flow DataPin Value", meta = (FlowPinType = "Rotator", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructRotator")) +struct FFlowDataPinValue_Rotator : public FFlowDataPinValue +{ + GENERATED_BODY() + +public: + using PinType = FFlowPinType_Rotator; + using ValueType = PinType::ValueType; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + TArray Values{ FRotator::ZeroRotator }; + + FLOW_API FFlowDataPinValue_Rotator() = default; + FLOW_API FFlowDataPinValue_Rotator(const ValueType& InValue); + FLOW_API FFlowDataPinValue_Rotator(const TArray& InValues); + + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + virtual bool TryConvertValuesToString(FString& OutString) const override; +}; + +//====================================================================== +// Transform +//====================================================================== +USTRUCT(MinimalApi, BlueprintType, DisplayName = "Transform - Flow DataPin Value", meta = (FlowPinType = "Transform", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructTransform")) +struct FFlowDataPinValue_Transform : public FFlowDataPinValue +{ + GENERATED_BODY() + +public: + using PinType = FFlowPinType_Transform; + using ValueType = PinType::ValueType; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + TArray Values{ FTransform::Identity }; + + FLOW_API FFlowDataPinValue_Transform() = default; + FLOW_API FFlowDataPinValue_Transform(const ValueType& InValue); + FLOW_API FFlowDataPinValue_Transform(const TArray& InValues); + + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + virtual bool TryConvertValuesToString(FString& OutString) const override; +}; + +//====================================================================== +// GameplayTag +//====================================================================== +USTRUCT(MinimalApi, BlueprintType, DisplayName = "GameplayTag - Flow DataPin Value", meta = (FlowPinType = "GameplayTag", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructGameplayTag")) +struct FFlowDataPinValue_GameplayTag : public FFlowDataPinValue +{ + GENERATED_BODY() + +public: + using PinType = FFlowPinType_GameplayTag; + using ValueType = PinType::ValueType; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + TArray Values; + + FLOW_API FFlowDataPinValue_GameplayTag() = default; + FLOW_API FFlowDataPinValue_GameplayTag(const ValueType& InValue); + FLOW_API FFlowDataPinValue_GameplayTag(const TArray& InValues); + + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + virtual bool TryConvertValuesToString(FString& OutString) const override; +}; + +//====================================================================== +// GameplayTagContainer +//====================================================================== +USTRUCT(MinimalApi, BlueprintType, DisplayName = "GameplayTagContainer - Flow DataPin Value", meta = (FlowPinType = "GameplayTagContainer", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructGameplayTagContainer")) +struct FFlowDataPinValue_GameplayTagContainer : public FFlowDataPinValue +{ + GENERATED_BODY() + +public: + using PinType = FFlowPinType_GameplayTagContainer; + using ValueType = PinType::ValueType; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + FGameplayTagContainer Values; + + FLOW_API FFlowDataPinValue_GameplayTagContainer() = default; + FLOW_API FFlowDataPinValue_GameplayTagContainer(const FGameplayTag& InValue); + FLOW_API FFlowDataPinValue_GameplayTagContainer(const FGameplayTagContainer& InValues); + FLOW_API FFlowDataPinValue_GameplayTagContainer(const TArray& InValues); + FLOW_API FFlowDataPinValue_GameplayTagContainer(const TArray& InValues); + + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + virtual bool TryConvertValuesToString(FString& OutString) const override; +}; + +//====================================================================== +// InstancedStruct +//====================================================================== +USTRUCT(MinimalApi, BlueprintType, DisplayName = "InstancedStruct - Flow DataPin Value", meta = (FlowPinType = "InstancedStruct", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructInstancedStruct")) +struct FFlowDataPinValue_InstancedStruct : public FFlowDataPinValue +{ + GENERATED_BODY() + +public: + using PinType = FFlowPinType_InstancedStruct; + using ValueType = PinType::ValueType; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + TArray Values; + + FLOW_API FFlowDataPinValue_InstancedStruct() = default; + FLOW_API FFlowDataPinValue_InstancedStruct(const ValueType& InValue); + FLOW_API FFlowDataPinValue_InstancedStruct(const TArray& InValues); + + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } +}; + +//====================================================================== +// Object +//====================================================================== +USTRUCT(MinimalApi, BlueprintType, DisplayName = "Object - Flow DataPin Value", meta = (FlowPinType = "Object", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructObject")) +struct FFlowDataPinValue_Object : public FFlowDataPinValue +{ + GENERATED_BODY() + +public: + using PinType = FFlowPinType_Object; + using ValueType = PinType::ValueType; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + TArray> Values; + +#if WITH_EDITORONLY_DATA + UPROPERTY(EditAnywhere, Category = DataPins, meta = (AllowAbstract, AdvancedDisplay)) + TObjectPtr ClassFilter = UObject::StaticClass(); +#endif + + FLOW_API FFlowDataPinValue_Object() = default; + FLOW_API FFlowDataPinValue_Object(TObjectPtr InObject, UClass* InClassFilter = UObject::StaticClass()); + FLOW_API FFlowDataPinValue_Object(const TArray>& InObjects, UClass* InClassFilter = UObject::StaticClass()); + FLOW_API FFlowDataPinValue_Object(AActor* InActor, UClass* InClassFilter = nullptr /* nullptr here defaults to AActor::StaticClass() */ ); + FLOW_API FFlowDataPinValue_Object(const TArray& InActors, UClass* InClassFilter = nullptr /* nullptr here defaults to AActor::StaticClass() */); + + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + virtual bool TryConvertValuesToString(FString& OutString) const override; +}; + +//====================================================================== +// Class +//====================================================================== +USTRUCT(MinimalApi, BlueprintType, DisplayName = "Class - Flow DataPin Value", meta = (FlowPinType = "Class", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructClass")) +struct FFlowDataPinValue_Class : public FFlowDataPinValue +{ + GENERATED_BODY() + +public: + using PinType = FFlowPinType_Class; + using ValueType = PinType::ValueType; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) + TArray Values; + +#if WITH_EDITORONLY_DATA + UPROPERTY(EditAnywhere, Category = DataPins, meta = (AllowAbstract, AdvancedDisplay)) + TObjectPtr ClassFilter = UObject::StaticClass(); +#endif + + FLOW_API FFlowDataPinValue_Class() = default; + FLOW_API FFlowDataPinValue_Class(const FSoftClassPath& InPath, UClass* InClassFilter = UObject::StaticClass()); + FLOW_API FFlowDataPinValue_Class(const TArray& InPaths, UClass* InClassFilter = UObject::StaticClass()); + FLOW_API FFlowDataPinValue_Class(const UClass* InClass, UClass* InClassFilter = UObject::StaticClass()); + FLOW_API FFlowDataPinValue_Class(const TArray& InClasses, UClass* InClassFilter = UObject::StaticClass()); + + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + virtual bool TryConvertValuesToString(FString& OutString) const override; +}; \ No newline at end of file diff --git a/Source/Flow/Public/Types/FlowNamedDataPinProperty.h b/Source/Flow/Public/Types/FlowNamedDataPinProperty.h new file mode 100644 index 000000000..939b67fa1 --- /dev/null +++ b/Source/Flow/Public/Types/FlowNamedDataPinProperty.h @@ -0,0 +1,98 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "StructUtils/InstancedStruct.h" +#include "UObject/Class.h" +#include "Nodes/FlowPin.h" +#include "Types/FlowDataPinValue.h" + +#include "FlowNamedDataPinProperty.generated.h" + +struct FFlowDataPinProperty; +struct FFlowDataPinValue; + +// Wrapper for FFlowDataPinProperty that is used for flow nodes that add +// dynamic properties, with associated data pins, on the flow node instance +// (as opposed to C++ or blueprint compile-time). +USTRUCT(BlueprintType, DisplayName = "Flow Named DataPin Property") +struct FFlowNamedDataPinProperty +{ + GENERATED_BODY() + +public: + // Name of this instanced property + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins, meta = (EditCondition = "bMayChangeNameAndType", HideEditConditionToggle)) + FName Name = NAME_None; + +private: + // DataPinProperty payload + UPROPERTY(VisibleAnywhere, Category = DataPins, meta = (DeprecatedProperty)) + TInstancedStruct DataPinProperty; + +public: + // DataPinProperty payload + UPROPERTY(EditAnywhere, Category = DataPins, meta = (ExcludeBaseStruct, NoClear)) + TInstancedStruct DataPinValue; + +#if WITH_EDITORONLY_DATA + // Unique identifier for property tracking + UPROPERTY() + FGuid Guid = FGuid::NewGuid(); + + // Tracks if this property overrides its super (auto-clears if matches super) + UPROPERTY() + bool bIsOverride = false; + + // TODO (gtaylor) Does not currently police the type, + // because that prevents the instanced struct contents being edited as well, + // which is not what we want from this feature. + // Will try to fix next pass on the details customization. + UPROPERTY() + bool bMayChangeNameAndType = true; +#endif + +public: + FFlowNamedDataPinProperty() = default; + + bool IsValid() const { return Name != NAME_None && DataPinValue.GetPtr() != nullptr; } + + // #FlowDataPinLegacy + bool FixupDataPinProperty(); + // -- + +#if WITH_EDITOR + FLOW_API FFlowPin CreateFlowPin() const; + + FLOW_API FText BuildHeaderText() const; + + void ConfigureForFlowAssetParams() + { + bIsOverride = false; + bMayChangeNameAndType = false; + } + + void ConfigureForFlowAssetStartNode() + { + bIsOverride = false; + bMayChangeNameAndType = true; + } + + static void ConfigurePropertiesForFlowAssetParams(TArray& MutableProperties) + { + for (FFlowNamedDataPinProperty& Property : MutableProperties) + { + Property.ConfigureForFlowAssetParams(); + } + } + static void ConfigurePropertiesForFlowAssetStartNode(TArray& MutableProperties) + { + for (FFlowNamedDataPinProperty& Property : MutableProperties) + { + Property.ConfigureForFlowAssetStartNode(); + } + } + +#endif +}; + diff --git a/Source/Flow/Public/Types/FlowPinEnums.h b/Source/Flow/Public/Types/FlowPinEnums.h index 5035013cd..53bbecfb4 100644 --- a/Source/Flow/Public/Types/FlowPinEnums.h +++ b/Source/Flow/Public/Types/FlowPinEnums.h @@ -6,7 +6,7 @@ #include "FlowPinEnums.generated.h" -UENUM(BlueprintType) +UENUM(BlueprintType, meta = (ScriptName = "LegacyFlowPinNameEnum")) enum class EFlowPinType : uint8 { // Execution pin @@ -63,16 +63,13 @@ enum class EFlowPinType : uint8 }; FLOW_ENUM_RANGE_VALUES(EFlowPinType) -// Result enum for TryResolveDataPinAs...() functions +// Result enum for TryResolveDataPin() UENUM(BlueprintType) enum class EFlowDataPinResolveResult : uint8 { // Pin resolved successfully Success, - // The pin is not connected to another pin - FailedUnconnected, - // The pin name is unknown FailedUnknownPin, @@ -82,8 +79,14 @@ enum class EFlowDataPinResolveResult : uint8 // The Flow Node or AddOn did not implement the necessary function to provide this value FailedUnimplemented, - // Failed due to missing pin (may just need re-save for the asset) - FailedMissingPin, + // Failed due to insufficient values (eg. resolving a single value with an empty array) + FailedInsufficientValues, + + // Could not resolve an enum value + FailedUnknownEnumValue, + + // Tried to extract with a null FlowNodeBase + FailedNullFlowNodeBase, // Failed with an error message (see the error log) FailedWithError, @@ -93,3 +96,108 @@ enum class EFlowDataPinResolveResult : uint8 Min = 0 UMETA(Hidden), }; FLOW_ENUM_RANGE_VALUES(EFlowDataPinResolveResult) + +UENUM(BlueprintType) +enum class EFlowDataPinResolveSimpleResult : uint8 +{ + Succeeded = 1, + Failed = 0, + + Max UMETA(Hidden), + Invalid UMETA(Hidden), + Min = 0 UMETA(Hidden), +}; +FLOW_ENUM_RANGE_VALUES(EFlowDataPinResolveSimpleResult) + +namespace EFlowDataPinResolveResult_Classifiers +{ + FORCEINLINE bool IsSuccess(EFlowDataPinResolveResult Result) { return Result == EFlowDataPinResolveResult::Success; } + FORCEINLINE EFlowDataPinResolveSimpleResult ConvertToSimpleResult(EFlowDataPinResolveResult ResultEnum) + { return IsSuccess(ResultEnum) ? EFlowDataPinResolveSimpleResult::Succeeded : EFlowDataPinResolveSimpleResult::Failed; } +}; + +UENUM(BlueprintType) +enum class EFlowDataMultiType : uint8 +{ + Single, + Array, + + // TODO (gtaylor) Consider future types like Set, Map + + Max UMETA(Hidden), + Invalid UMETA(Hidden), + Min = 0 UMETA(Hidden), +}; +FLOW_ENUM_RANGE_VALUES(EFlowDataMultiType) + +UENUM(BlueprintType) +enum class EFlowSingleFromArray : uint8 +{ + // For the Single value, use the [0]th value (First) + FirstValue, + + // For the Single value, use the [N-1]th value (Last) + LastValue, + + // Expect a single value only, log an error if not (and return [0]th) + ExpectSingleValueOnly, + + // Used in the FlowPinType templates for entire array extraction + EntireArray UMETA(Hidden), + + Max UMETA(Hidden), + Invalid UMETA(Hidden), + Min = 0 UMETA(Hidden), +}; +FLOW_ENUM_RANGE_VALUES(EFlowSingleFromArray) + +namespace EFlowSingleFromArray_Classifiers +{ + FORCEINLINE int32 ConvertToIndex(EFlowSingleFromArray SingleFromArray, int32 ArrayMax) + { + FLOW_ASSERT_ENUM_MAX(EFlowSingleFromArray, 4); + switch (SingleFromArray) + { + case EFlowSingleFromArray::FirstValue: + { + if (ArrayMax > 0) + { + return 0; + } + else + { + return INDEX_NONE; + } + } + + case EFlowSingleFromArray::LastValue: + { + if (ArrayMax > 0) + { + return ArrayMax - 1; + } + else + { + return INDEX_NONE; + } + } + + case EFlowSingleFromArray::EntireArray: + check(SingleFromArray != EFlowSingleFromArray::EntireArray); + return INDEX_NONE; + + default: + case EFlowSingleFromArray::ExpectSingleValueOnly: + { + if (ArrayMax == 1) + { + return 0; + } + else + { + return INDEX_NONE; + } + } + } + } +}; \ No newline at end of file diff --git a/Source/Flow/Public/Types/FlowPinType.h b/Source/Flow/Public/Types/FlowPinType.h new file mode 100644 index 000000000..798b93d5d --- /dev/null +++ b/Source/Flow/Public/Types/FlowPinType.h @@ -0,0 +1,55 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "FlowPinEnums.h" + +#include "Math/Color.h" +#include "UObject/NameTypes.h" + +#if WITH_EDITOR +#include "GraphEditorSettings.h" +#endif + +#include "FlowPinType.generated.h" + +class FFormatArgumentValue; +class IPropertyHandle; +class UFlowNodeBase; +class UFlowNode; +struct FFlowDataPinResult; +struct FFlowPin; +struct FFlowDataPinValue; + +USTRUCT(BlueprintType) +struct FFlowPinType +{ + GENERATED_BODY() + +public: + virtual ~FFlowPinType() {} + + // Lookup a registered type by name + FLOW_API static const FFlowPinType* LookupPinType(const FFlowPinTypeName& FlowPinTypeName); + + // Identity + FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const PURE_VIRTUAL(GetPinTypeName, return PinTypeNameUnknown;); + + // Value resolution + FLOW_API virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const; + FLOW_API virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const; + +#if WITH_EDITOR + // Editor visualization + FLOW_API virtual FLinearColor GetPinColor() const { return GetDefault()->DefaultPinTypeColor; } + FLOW_API virtual TSharedPtr GetValuesHandle(const TSharedRef& FlowDataPinValuePropertyHandle) const; + FLOW_API virtual bool SupportsMultiType(EFlowDataMultiType Mode) const { return true; } + FLOW_API virtual UObject* GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const { return nullptr; } + + // Pin creation + FLOW_API FFlowPin CreateFlowPinFromProperty(const FProperty& Property, void const* InContainer) const; + FLOW_API FFlowPin CreateFlowPinFromValueWrapper(const FName& PinName, const FFlowDataPinValue& Wrapper) const; +#endif + + static const FFlowPinTypeName PinTypeNameUnknown; +}; \ No newline at end of file diff --git a/Source/Flow/Public/Types/FlowPinTypeName.h b/Source/Flow/Public/Types/FlowPinTypeName.h new file mode 100644 index 000000000..1531c289c --- /dev/null +++ b/Source/Flow/Public/Types/FlowPinTypeName.h @@ -0,0 +1,33 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "UObject/NameTypes.h" + +#include "FlowPinTypeName.generated.h" + +USTRUCT(BlueprintType) +struct FFlowPinTypeName +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, Category = FlowPin) + FName Name = NAME_None; + + FFlowPinTypeName() = default; + explicit FFlowPinTypeName(const TCHAR* InPinName) : Name(FName(InPinName)) {} + explicit FFlowPinTypeName(const FName& InName) : Name(InName) {} + explicit FFlowPinTypeName(const FString& InString) : Name(FName(InString)) {} + + friend inline uint32 GetTypeHash(const FFlowPinTypeName& PinTypeName) + { + return GetTypeHash(PinTypeName.Name); + } + + FORCEINLINE bool operator==(const FFlowPinTypeName& Other) const { return Name == Other.Name; } + FORCEINLINE bool operator==(const FName& OtherName) const { return Name == OtherName; } + + FString ToString() const { return Name.ToString(); } + bool IsNone() const { return Name.IsNone(); } +}; \ No newline at end of file diff --git a/Source/Flow/Public/Types/FlowPinTypeNamesStandard.h b/Source/Flow/Public/Types/FlowPinTypeNamesStandard.h new file mode 100644 index 000000000..b3f824d23 --- /dev/null +++ b/Source/Flow/Public/Types/FlowPinTypeNamesStandard.h @@ -0,0 +1,38 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "UObject/NameTypes.h" +#include "Asset/FlowPinTypeMatchPolicy.h" + +struct FFlowPinTypeNamesStandard +{ + // Other Standard Pin Types + FLOW_API static constexpr const TCHAR* PinTypeNameUnknown = TEXT("Unknown"); + FLOW_API static constexpr const TCHAR* PinTypeNameExec = TEXT("Exec"); + + // "Standard" Data Pin Types + FLOW_API static constexpr const TCHAR* PinTypeNameBool = TEXT("Bool"); + FLOW_API static constexpr const TCHAR* PinTypeNameInt = TEXT("Int"); + FLOW_API static constexpr const TCHAR* PinTypeNameInt64 = TEXT("Int64"); + FLOW_API static constexpr const TCHAR* PinTypeNameFloat = TEXT("Float"); + FLOW_API static constexpr const TCHAR* PinTypeNameDouble = TEXT("Double"); + FLOW_API static constexpr const TCHAR* PinTypeNameEnum = TEXT("Enum"); + FLOW_API static constexpr const TCHAR* PinTypeNameName = TEXT("Name"); + FLOW_API static constexpr const TCHAR* PinTypeNameString = TEXT("String"); + FLOW_API static constexpr const TCHAR* PinTypeNameText = TEXT("Text"); + FLOW_API static constexpr const TCHAR* PinTypeNameVector = TEXT("Vector"); + FLOW_API static constexpr const TCHAR* PinTypeNameRotator = TEXT("Rotator"); + FLOW_API static constexpr const TCHAR* PinTypeNameTransform = TEXT("Transform"); + FLOW_API static constexpr const TCHAR* PinTypeNameGameplayTag = TEXT("GameplayTag"); + FLOW_API static constexpr const TCHAR* PinTypeNameGameplayTagContainer = TEXT("GameplayTagContainer"); + FLOW_API static constexpr const TCHAR* PinTypeNameInstancedStruct = TEXT("InstancedStruct"); + FLOW_API static constexpr const TCHAR* PinTypeNameObject = TEXT("Object"); + FLOW_API static constexpr const TCHAR* PinTypeNameClass = TEXT("Class"); + +#if WITH_EDITOR + // These are the default pin match policies for input pin connections + // in the UFlowGraphSchema. Schema subclasses can modify this map + FLOW_API static const TMap PinTypeMatchPolicies; +#endif +}; \ No newline at end of file diff --git a/Source/Flow/Public/Types/FlowPinTypeNodeTemplates.h b/Source/Flow/Public/Types/FlowPinTypeNodeTemplates.h new file mode 100644 index 000000000..a59cc3c24 --- /dev/null +++ b/Source/Flow/Public/Types/FlowPinTypeNodeTemplates.h @@ -0,0 +1,70 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Types/FlowDataPinResults.h" +#include "Types/FlowDataPinValuesStandard.h" +#include "Types/FlowPinTypeTemplates.h" +#include "Types/FlowArray.h" +#include "Nodes/FlowNode.h" + +// Additional FlowPinType templates that require FlowNode.h include +namespace FlowPinType +{ + template + static bool PopulateResultTemplate(const UObject& PropertyOwnerObject, const UFlowNode& FlowNode, const FFlowPin& Pin, FFlowDataPinResult& OutResult) + { + using TValue = typename TPinType::ValueType; + using TWrapper = typename TPinType::WrapperType; + using Traits = FlowPinType::FFlowDataPinValueTraits; + + TInstancedStruct ValueStruct; + const FProperty* FoundProperty = nullptr; + + if (!FlowNode.TryFindPropertyByPinName(PropertyOwnerObject, Pin.PinName, FoundProperty, ValueStruct)) + { + OutResult.Result = EFlowDataPinResolveResult::FailedUnknownPin; + return false; + } + + if (ValueStruct.IsValid() && ValueStruct.Get().GetPinTypeName() == TPinType::GetPinTypeNameStatic()) + { + OutResult.ResultValue = ValueStruct; + OutResult.Result = EFlowDataPinResolveResult::Success; + return true; + } + + TArray Values; + if (FlowPinType::IsSuccess(Traits::ExtractFromProperty(FoundProperty, &PropertyOwnerObject, Values))) + { + OutResult.ResultValue = TInstancedStruct::Make(Values); + OutResult.Result = EFlowDataPinResolveResult::Success; + return true; + } + + OutResult.Result = EFlowDataPinResolveResult::FailedMismatchedType; + return false; + } + + // ResolveAndFormatArray + template + bool ResolveAndFormatArray( + const UFlowNodeBase& Node, + const FName& PinName, + FFormatArgumentValue& OutValue, + TFunctionRef Formatter) + { + using TValue = typename TPinType::ValueType; + + TArray Values; + const EFlowDataPinResolveResult ResolveResult = Node.TryResolveDataPinValues(PinName, Values); + if (FlowPinType::IsSuccess(ResolveResult)) + { + const FString ValueString = FlowArray::FormatArrayString(Values, Formatter); + OutValue = FFormatArgumentValue(FText::FromString(ValueString)); + return true; + } + + return false; + } +}; \ No newline at end of file diff --git a/Source/Flow/Public/Types/FlowPinTypeTemplates.h b/Source/Flow/Public/Types/FlowPinTypeTemplates.h new file mode 100644 index 000000000..6bad39388 --- /dev/null +++ b/Source/Flow/Public/Types/FlowPinTypeTemplates.h @@ -0,0 +1,1025 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "UObject/NameTypes.h" +#include "UObject/TextProperty.h" +#include "Math/Vector.h" +#include "Math/Rotator.h" +#include "Math/Transform.h" +#include "GameplayTagContainer.h" +#include "StructUtils/InstancedStruct.h" +#include "UObject/Class.h" +#include "UObject/UnrealType.h" +#include "Types/FlowDataPinValuesStandard.h" +#include "Types/FlowDataPinResults.h" +#include "FlowLogChannels.h" +#include +#include + +// #FlowDataPinLegacy +#include "Types/FlowDataPinProperties.h" +// -- + +namespace FlowPinType +{ + // Success check helper + FORCEINLINE bool IsSuccess(EFlowDataPinResolveResult ResultEnum) + { + return EFlowDataPinResolveResult_Classifiers::IsSuccess(ResultEnum); + } + + FORCEINLINE EFlowDataPinResolveSimpleResult ConvertToSimpleResult(EFlowDataPinResolveResult ResultEnum) + { + return EFlowDataPinResolveResult_Classifiers::ConvertToSimpleResult(ResultEnum); + } + + // ----------------------------------------------------------------------- + // Value Conversion System + // ----------------------------------------------------------------------- + + // Numeric conversion dispatcher + template + struct TValueConverter; + + // int32 + template <> struct TValueConverter { static int32 Convert(int64 Val); }; + template <> struct TValueConverter { static int32 Convert(float Val); }; + template <> struct TValueConverter { static int32 Convert(double Val); }; + + // int64 + template <> struct TValueConverter { static int64 Convert(int32 Val); }; + template <> struct TValueConverter { static int64 Convert(float Val); }; + template <> struct TValueConverter { static int64 Convert(double Val); }; + + // float + template <> struct TValueConverter { static float Convert(int32 Val); }; + template <> struct TValueConverter { static float Convert(int64 Val); }; + template <> struct TValueConverter { static float Convert(double Val); }; + + // double + template <> struct TValueConverter { static double Convert(int32 Val); }; + template <> struct TValueConverter { static double Convert(int64 Val); }; + template <> struct TValueConverter { static double Convert(float Val); }; + + // String types + template <> struct TValueConverter { static FName Convert(const FString& Val); }; + template <> struct TValueConverter { static FName Convert(const FText& Val); }; + template <> struct TValueConverter { static FString Convert(const FName& Val); }; + template <> struct TValueConverter { static FString Convert(const FText& Val); }; + template <> struct TValueConverter { static FText Convert(const FName& Val); }; + template <> struct TValueConverter { static FText Convert(const FString& Val); }; + + // To string for logging + template <> struct TValueConverter { static FString Convert(int32 Val); }; + template <> struct TValueConverter { static FString Convert(int64 Val); }; + template <> struct TValueConverter { static FString Convert(float Val); }; + template <> struct TValueConverter { static FString Convert(double Val); }; + template <> struct TValueConverter { static FString Convert(bool Val); }; + + // GameplayTag + template <> struct TValueConverter + { + static FGameplayTag Convert(const FGameplayTagContainer& Container); + }; + + template <> struct TValueConverter + { + static FGameplayTagContainer Convert(const FGameplayTag& Tag); + }; + + // ----------------------------------------------------------------------- + // Array Conversion Helper + // ----------------------------------------------------------------------- + + // Converts array with logging and clamping + template + void ConvertArray(const TArray& Source, TArray& OutValues, TConverter Converter) + { + OutValues.Reserve(Source.Num()); + for (const TSource& Val : Source) + { +#if !UE_BUILD_SHIPPING + // Lossy conversion warnings + if constexpr (std::is_integral_v && std::is_floating_point_v) + { + int64 iv = FMath::FloorToInt64(Val); + if (iv < std::numeric_limits::min() || iv > std::numeric_limits::max()) + { + UE_LOG(LogFlow, Warning, TEXT("Converting %s to %s (out of range, clamping)"), + *TValueConverter::Convert(Val), TEXT("int")); + } + } + else if constexpr (std::is_same_v && std::is_same_v) + { + if (Val < std::numeric_limits::lowest() || Val > std::numeric_limits::max()) + { + UE_LOG(LogFlow, Warning, TEXT("Converting %s to float (out of range, clamping)"), + *TValueConverter::Convert(Val)); + } + } + else if constexpr (std::is_same_v && (std::is_same_v || std::is_same_v)) + { + FString SourceStr; + if constexpr (std::is_same_v) + { + SourceStr = Val; + } + else + { + SourceStr = TValueConverter::Convert(Val); + } + if (SourceStr.Len() > NAME_SIZE) + { + UE_LOG(LogFlow, Warning, TEXT("Converting '%s' to FName (possible truncation)"), *SourceStr); + } + } +#endif + OutValues.Add(Converter(Val)); + } + } + + // ----------------------------------------------------------------------- + // Internal helper – applies the single-from-array policy after extraction + // ----------------------------------------------------------------------- + template + FORCEINLINE EFlowDataPinResolveResult ApplySinglePolicy( + const TArray& Source, + TArray& OutValues, + EFlowSingleFromArray Policy) + { + if (Policy == EFlowSingleFromArray::EntireArray) + { + OutValues = Source; + return EFlowDataPinResolveResult::Success; + } + + const int32 Num = Source.Num(); + const int32 Index = EFlowSingleFromArray_Classifiers::ConvertToIndex(Policy, Num); + + if (!Source.IsValidIndex(Index)) + { + return EFlowDataPinResolveResult::FailedInsufficientValues; + } + + OutValues.Add(Source[Index]); + return EFlowDataPinResolveResult::Success; + } + + template + FORCEINLINE EFlowDataPinResolveResult ConvertWithPolicy( + const TArray& Source, + TArray& OutValues, + TConverter Converter, + EFlowSingleFromArray Policy) + { + if (Policy == EFlowSingleFromArray::EntireArray) + { + ConvertArray(Source, OutValues, Converter); + return EFlowDataPinResolveResult::Success; + } + + const int32 Index = EFlowSingleFromArray_Classifiers::ConvertToIndex(Policy, Source.Num()); + if (!Source.IsValidIndex(Index)) + { + return EFlowDataPinResolveResult::FailedInsufficientValues; + } + + OutValues.Add(Converter(Source[Index])); + return EFlowDataPinResolveResult::Success; + } + + // ----------------------------------------------------------------------- + // Numeric Validation & Clamping + // ----------------------------------------------------------------------- + + template + TValue ValidateAndClampNumericValue(TInput Val, TValue MinValue, TValue MaxValue) + { + if constexpr (std::is_floating_point::value) + { +#if !UE_BUILD_SHIPPING + if (!FMath::IsFinite(Val)) + { + UE_LOG(LogFlow, Warning, TEXT("Non-finite value %s encountered during conversion to %s"), + *TValueConverter::Convert(Val), TEXT("numeric")); + return TValue(0); + } +#endif + } + + if constexpr (std::is_floating_point::value) + { + if constexpr (std::is_same::value && std::is_same::value) + { +#if !UE_BUILD_SHIPPING + if (Val < MinValue || Val > MaxValue) + { + UE_LOG(LogFlow, Warning, TEXT("Double value %s out of range for float, clamping"), + *TValueConverter::Convert(Val)); + } +#endif + } + return FMath::Clamp(static_cast(Val), MinValue, MaxValue); + } + else + { + int64 iv = std::is_floating_point::value ? FMath::FloorToInt64(Val) : static_cast(Val); +#if !UE_BUILD_SHIPPING + if (iv < MinValue || iv > MaxValue) + { + UE_LOG(LogFlow, Warning, TEXT("Value %lld out of range for %s, clamping"), iv, TEXT("int")); + } +#endif + return static_cast(FMath::Clamp(iv, static_cast(MinValue), static_cast(MaxValue))); + } + } + + // ----------------------------------------------------------------------- + // ValueConverter Implementations + // ----------------------------------------------------------------------- + + // int32 + inline int32 TValueConverter::Convert(int64 Val) { return ValidateAndClampNumericValue(Val, std::numeric_limits::min(), std::numeric_limits::max()); } + inline int32 TValueConverter::Convert(float Val) { return ValidateAndClampNumericValue(Val, std::numeric_limits::min(), std::numeric_limits::max()); } + inline int32 TValueConverter::Convert(double Val) { return ValidateAndClampNumericValue(Val, std::numeric_limits::min(), std::numeric_limits::max()); } + + // int64 + inline int64 TValueConverter::Convert(int32 Val) { return static_cast(Val); } + inline int64 TValueConverter::Convert(float Val) { return ValidateAndClampNumericValue(Val, MIN_int64, MAX_int64); } + inline int64 TValueConverter::Convert(double Val) { return ValidateAndClampNumericValue(Val, MIN_int64, MAX_int64); } + + // float + inline float TValueConverter::Convert(int32 Val) { return static_cast(Val); } + inline float TValueConverter::Convert(int64 Val) { return ValidateAndClampNumericValue(Val, std::numeric_limits::lowest(), std::numeric_limits::max()); } + inline float TValueConverter::Convert(double Val) { return ValidateAndClampNumericValue(Val, std::numeric_limits::lowest(), std::numeric_limits::max()); } + + // double + inline double TValueConverter::Convert(int32 Val) { return static_cast(Val); } + inline double TValueConverter::Convert(int64 Val) { return static_cast(Val); } + inline double TValueConverter::Convert(float Val) { return static_cast(Val); } + + // String types + inline FName TValueConverter::Convert(const FString& Val) { return FName(*Val); } + inline FName TValueConverter::Convert(const FText& Val) { return FName(*Val.ToString()); } + inline FString TValueConverter::Convert(const FName& Val) { return Val.ToString(); } + inline FString TValueConverter::Convert(const FText& Val) { return Val.ToString(); } + inline FText TValueConverter::Convert(const FName& Val) { return FText::FromName(Val); } + inline FText TValueConverter::Convert(const FString& Val) { return FText::FromString(Val); } + + // String converters for other types + inline FString TValueConverter::Convert(int32 Val) { return FString::Printf(TEXT("%d"), Val); } + inline FString TValueConverter::Convert(int64 Val) { return FString::Printf(TEXT("%lld"), Val); } + inline FString TValueConverter::Convert(float Val) { return FString::Printf(TEXT("%f"), Val); } + inline FString TValueConverter::Convert(double Val) { return FString::Printf(TEXT("%f"), Val); } + inline FString TValueConverter::Convert(bool Val) { return Val ? TEXT("true") : TEXT("false"); } + + // GameplayTag + inline FGameplayTag TValueConverter::Convert(const FGameplayTagContainer& Container) + { +#if !UE_BUILD_SHIPPING + if (Container.Num() > 1) + { + UE_LOG(LogFlow, Warning, TEXT("Multiple tags in container; using first: %s"), *Container.ToStringSimple()); + } +#endif + return Container.Num() > 0 ? Container.GetByIndex(0) : FGameplayTag(); + } + + inline FGameplayTagContainer TValueConverter::Convert(const FGameplayTag& Tag) + { + return FGameplayTagContainer(Tag); + } + + // ----------------------------------------------------------------------- + // Property Traits + // ----------------------------------------------------------------------- + + // Base for simple scalar types + template + struct FFlowSimplePropertyTraitsBase + { + using ValueType = TPinType::ValueType; + using WrapperType = TPinType::WrapperType; + using PropertyType = TPinType::MainPropertyType; + using LegacyWrapperType = TPinType::LegacyWrapperType; + + static EFlowDataPinResolveResult ExtractFromProperty(const FProperty* Property, const void* Container, TArray& OutValues) + { + // 1. Wrapper struct + if (const FStructProperty* StructProp = CastField(Property)) + { + if (StructProp->Struct == WrapperType::StaticStruct()) + { + const WrapperType* Wrapper = StructProp->ContainerPtrToValuePtr(Container); + OutValues = Wrapper->Values; + return EFlowDataPinResolveResult::Success; + } + + // #FlowDataPinLegacy - support sourcing from old property wrappers For Now(tm) + static const UScriptStruct* OldPropStruct = LegacyWrapperType::StaticStruct(); + if (StructProp->Struct->IsChildOf(OldPropStruct)) + { + const LegacyWrapperType* Wrapper = StructProp->ContainerPtrToValuePtr(Container); + OutValues = { Wrapper->Value }; + return EFlowDataPinResolveResult::Success; + } + // -- + } + + // 2. Direct property + if (const PropertyType* Prop = CastField(Property)) + { + OutValues = { *Prop->template ContainerPtrToValuePtr(Container) }; + return EFlowDataPinResolveResult::Success; + } + + // 3. Array of property + if (const FArrayProperty* ArrProp = CastField(Property)) + { + if (const PropertyType* Inner = CastField(ArrProp->Inner)) + { + FScriptArrayHelper ArrHelper(ArrProp, ArrProp->ContainerPtrToValuePtr(Container)); + OutValues.Reserve(ArrHelper.Num()); + for (int32 i = 0; i < ArrHelper.Num(); ++i) + { + OutValues.Add(*Inner->template ContainerPtrToValuePtr(ArrHelper.GetRawPtr(i))); + } + return EFlowDataPinResolveResult::Success; + } + } + + return EFlowDataPinResolveResult::FailedMismatchedType; + } + + static EFlowDataPinResolveResult ExtractValues(const FFlowDataPinResult& DataPinResult, TArray& OutValues, EFlowSingleFromArray SingleFromArray) + { + if (!IsSuccess(DataPinResult.Result)) + { + return DataPinResult.Result; + } + + if (DataPinResult.ResultValue.GetScriptStruct() == WrapperType::StaticStruct()) + { + const WrapperType& Wrapper = DataPinResult.ResultValue.Get(); + return ApplySinglePolicy(Wrapper.Values, OutValues, SingleFromArray); + } + + return EFlowDataPinResolveResult::FailedMismatchedType; + } + }; + + // Numeric cross-conversion + template + struct FFlowNumericTraitsBase : public FFlowSimplePropertyTraitsBase + { + using Super = FFlowSimplePropertyTraitsBase; + using ValueType = TPinType::ValueType; + using WrapperType = TPinType::WrapperType; + + static EFlowDataPinResolveResult ExtractValues(const FFlowDataPinResult& DataPinResult, TArray& OutValues, EFlowSingleFromArray SingleFromArray) + { + if (!IsSuccess(DataPinResult.Result)) + { + return DataPinResult.Result; + } + + const UScriptStruct* ScriptStruct = DataPinResult.ResultValue.GetScriptStruct(); + + if (ScriptStruct == WrapperType::StaticStruct()) + { + const WrapperType& Wrapper = DataPinResult.ResultValue.Get(); + return ApplySinglePolicy(Wrapper.Values, OutValues, SingleFromArray); + } + + // Cross-convert from other numeric types + if constexpr (!std::is_same_v) + { + if (ScriptStruct == FFlowDataPinValue_Int::StaticStruct()) + { + const FFlowDataPinValue_Int& Wrapper = DataPinResult.ResultValue.Get(); + return ConvertWithPolicy(Wrapper.Values, OutValues, TValueConverter::Convert, SingleFromArray); + } + } + + if constexpr (!std::is_same_v) + { + if (ScriptStruct == FFlowDataPinValue_Int64::StaticStruct()) + { + const FFlowDataPinValue_Int64& Wrapper = DataPinResult.ResultValue.Get(); + return ConvertWithPolicy(Wrapper.Values, OutValues, TValueConverter::Convert, SingleFromArray); + } + } + + if constexpr (!std::is_same_v) + { + if (ScriptStruct == FFlowDataPinValue_Float::StaticStruct()) + { + const FFlowDataPinValue_Float& Wrapper = DataPinResult.ResultValue.Get(); + return ConvertWithPolicy(Wrapper.Values, OutValues, TValueConverter::Convert, SingleFromArray); + } + } + + if constexpr (!std::is_same_v) + { + if (ScriptStruct == FFlowDataPinValue_Double::StaticStruct()) + { + const FFlowDataPinValue_Double& Wrapper = DataPinResult.ResultValue.Get(); + return ConvertWithPolicy(Wrapper.Values, OutValues, TValueConverter::Convert, SingleFromArray); + } + } + + return EFlowDataPinResolveResult::FailedMismatchedType; + } + }; + + // String cross-conversion + template + struct FFlowStringTraitsBase : public FFlowSimplePropertyTraitsBase + { + using Super = FFlowSimplePropertyTraitsBase; + using ValueType = TPinType::ValueType; + using WrapperType = TPinType::WrapperType; + + static EFlowDataPinResolveResult ExtractValues(const FFlowDataPinResult& DataPinResult, TArray& OutValues, EFlowSingleFromArray SingleFromArray) + { + if (!IsSuccess(DataPinResult.Result)) + { + return DataPinResult.Result; + } + + const UScriptStruct* ScriptStruct = DataPinResult.ResultValue.GetScriptStruct(); + + if (ScriptStruct == WrapperType::StaticStruct()) + { + const WrapperType& Wrapper = DataPinResult.ResultValue.Get(); + return ApplySinglePolicy(Wrapper.Values, OutValues, SingleFromArray); + } + + // Cross-convert from other string types + if constexpr (!std::is_same_v) + { + if (ScriptStruct == FFlowDataPinValue_Name::StaticStruct()) + { + const FFlowDataPinValue_Name& Wrapper = DataPinResult.ResultValue.Get(); + return ConvertWithPolicy(Wrapper.Values, OutValues, TValueConverter::Convert, SingleFromArray); + } + } + + if constexpr (!std::is_same_v) + { + if (ScriptStruct == FFlowDataPinValue_String::StaticStruct()) + { + const FFlowDataPinValue_String& Wrapper = DataPinResult.ResultValue.Get(); + return ConvertWithPolicy(Wrapper.Values, OutValues, TValueConverter::Convert, SingleFromArray); + } + } + + if constexpr (!std::is_same_v) + { + if (ScriptStruct == FFlowDataPinValue_Text::StaticStruct()) + { + const FFlowDataPinValue_Text& Wrapper = DataPinResult.ResultValue.Get(); + return ConvertWithPolicy(Wrapper.Values, OutValues, TValueConverter::Convert, SingleFromArray); + } + } + + // Fallback to string conversion from any pin value + if (ScriptStruct->IsChildOf(FFlowDataPinValue::StaticStruct())) + { + const FFlowDataPinValue& BaseWrapper = DataPinResult.ResultValue.Get(); + FString StrValue; + if (BaseWrapper.TryConvertValuesToString(StrValue)) + { + if constexpr (std::is_same_v) + { + OutValues = { StrValue }; + } + else + { + OutValues = { TValueConverter::Convert(StrValue) }; + } + return EFlowDataPinResolveResult::Success; + } + } + + return EFlowDataPinResolveResult::FailedMismatchedType; + } + }; + + // Struct types (Vector, Rotator, etc.) + template + struct FFlowStructTraitsBase : public FFlowSimplePropertyTraitsBase + { + using ValueType = TPinType::ValueType; + using WrapperType = TPinType::WrapperType; + using LegacyWrapperType = TPinType::LegacyWrapperType; + + static EFlowDataPinResolveResult ExtractFromProperty(const FProperty* Property, const void* Container, TArray& OutValues) + { + static const UScriptStruct* ValueStruct = TBaseStructure::Get(); + + if (const FStructProperty* StructProp = CastField(Property)) + { + static const UScriptStruct* WrapperStruct = TBaseStructure::Get(); + if (StructProp->Struct == WrapperStruct) + { + const WrapperType* Wrapper = StructProp->ContainerPtrToValuePtr(Container); + OutValues = Wrapper->Values; + return EFlowDataPinResolveResult::Success; + } + + if (StructProp->Struct == ValueStruct) + { + OutValues = { *StructProp->ContainerPtrToValuePtr(Container) }; + return EFlowDataPinResolveResult::Success; + } + + // #FlowDataPinLegacy - support sourcing from old property wrappers For Now(tm) + static const UScriptStruct* OldPropStruct = LegacyWrapperType::StaticStruct(); + if (StructProp->Struct->IsChildOf(OldPropStruct)) + { + const LegacyWrapperType* Wrapper = StructProp->ContainerPtrToValuePtr(Container); + OutValues = { Wrapper->Value }; + return EFlowDataPinResolveResult::Success; + } + // -- + } + else if (const FArrayProperty* ArrayProp = CastField(Property)) + { + const FStructProperty* InnerStruct = CastField(ArrayProp->Inner); + if (InnerStruct && InnerStruct->Struct == ValueStruct) + { + FScriptArrayHelper Helper(ArrayProp, ArrayProp->ContainerPtrToValuePtr(Container)); + OutValues.Reserve(Helper.Num()); + for (int32 i = 0; i < Helper.Num(); ++i) + { + OutValues.Add(*reinterpret_cast(Helper.GetRawPtr(i))); + } + return EFlowDataPinResolveResult::Success; + } + } + + return EFlowDataPinResolveResult::FailedMismatchedType; + } + }; + + // ----------------------------------------------------------------------- + // Pin Type Traits + // ----------------------------------------------------------------------- + + template struct FFlowDataPinValueTraits; + + // Scalars + template <> struct FFlowDataPinValueTraits : public FFlowSimplePropertyTraitsBase {}; + template <> struct FFlowDataPinValueTraits : public FFlowNumericTraitsBase {}; + template <> struct FFlowDataPinValueTraits : public FFlowNumericTraitsBase {}; + template <> struct FFlowDataPinValueTraits : public FFlowNumericTraitsBase {}; + template <> struct FFlowDataPinValueTraits : public FFlowNumericTraitsBase {}; + template <> struct FFlowDataPinValueTraits : public FFlowStringTraitsBase {}; + template <> struct FFlowDataPinValueTraits : public FFlowStringTraitsBase {}; + template <> struct FFlowDataPinValueTraits : public FFlowStringTraitsBase {}; + + // Structs + template <> struct FFlowDataPinValueTraits : public FFlowStructTraitsBase {}; + template <> struct FFlowDataPinValueTraits : public FFlowStructTraitsBase {}; + template <> struct FFlowDataPinValueTraits : public FFlowStructTraitsBase {}; + template <> struct FFlowDataPinValueTraits : public FFlowStructTraitsBase {}; + + // Enum + template <> + struct FFlowDataPinValueTraits : public FFlowSimplePropertyTraitsBase + { + using TPinType = FFlowPinType_Enum; + using WrapperType = TPinType::WrapperType; + using ValueType = TPinType::ValueType; + using LegacyWrapperType = TPinType::LegacyWrapperType; + + static EFlowDataPinResolveResult ExtractFromProperty(const FProperty* Property, const void* Container, TArray& OutValues, TSoftObjectPtr& OutEnumClass) + { + const FStructProperty* StructProp = CastField(Property); + if (StructProp && StructProp->Struct == WrapperType::StaticStruct()) + { + const WrapperType* Wrapper = StructProp->ContainerPtrToValuePtr(Container); + OutValues = Wrapper->Values; + OutEnumClass = Wrapper->EnumClass; + return EFlowDataPinResolveResult::Success; + } + + // #FlowDataPinLegacy - support sourcing from old property wrappers For Now(tm) + static const UScriptStruct* OldPropStruct = LegacyWrapperType::StaticStruct(); + if (StructProp && StructProp->Struct->IsChildOf(OldPropStruct)) + { + const LegacyWrapperType* Wrapper = StructProp->ContainerPtrToValuePtr(Container); + OutValues = { Wrapper->Value }; + OutEnumClass = Wrapper->EnumClass; + return EFlowDataPinResolveResult::Success; + } + // -- + + if (const FEnumProperty* EnumProp = CastField(Property)) + { + const void* ContainerPtr = EnumProp->ContainerPtrToValuePtr(Container); + UEnum* EnumClass = EnumProp->GetEnum(); + const FNumericProperty* Underlying = EnumProp->GetUnderlyingProperty(); + int64 RawValue = Underlying->GetSignedIntPropertyValue_InContainer(ContainerPtr); + FString AuthoredName = EnumClass->GetAuthoredNameStringByValue(RawValue); + + OutValues = { FName(AuthoredName) }; + OutEnumClass = EnumClass; + return EFlowDataPinResolveResult::Success; + } + + if (const FArrayProperty* ArrayProp = CastField(Property)) + { + if (const FEnumProperty* Inner = CastField(ArrayProp->Inner)) + { + FScriptArrayHelper Helper(ArrayProp, ArrayProp->ContainerPtrToValuePtr(Container)); + UEnum* EnumClass = Inner->GetEnum(); + const FNumericProperty* Underlying = Inner->GetUnderlyingProperty(); + OutValues.Reserve(Helper.Num()); + for (int32 i = 0; i < Helper.Num(); ++i) + { + int64 RawValue = Underlying->GetSignedIntPropertyValue(Helper.GetRawPtr(i)); + FString Name = EnumClass->GetAuthoredNameStringByValue(RawValue); + OutValues.Add(FName(Name)); + } + OutEnumClass = EnumClass; + return EFlowDataPinResolveResult::Success; + } + } + + return EFlowDataPinResolveResult::FailedMismatchedType; + } + }; + + // GameplayTag + template <> + struct FFlowDataPinValueTraits : public FFlowStructTraitsBase + { + using PinType = FFlowPinType_GameplayTag; + using ValueType = PinType::ValueType; + using WrapperType = FFlowDataPinValue_GameplayTag; + using ContainerWrapper = FFlowDataPinValue_GameplayTagContainer; + + static EFlowDataPinResolveResult ExtractValues(const FFlowDataPinResult& DataPinResult, TArray& OutValues, EFlowSingleFromArray SingleFromArray) + { + if (!IsSuccess(DataPinResult.Result)) + { + return DataPinResult.Result; + } + + const UScriptStruct* ScriptStruct = DataPinResult.ResultValue.GetScriptStruct(); + + if (ScriptStruct == WrapperType::StaticStruct()) + { + const WrapperType& Wrapper = DataPinResult.ResultValue.Get(); + return ApplySinglePolicy(Wrapper.Values, OutValues, SingleFromArray); + } + + if (ScriptStruct == ContainerWrapper::StaticStruct()) + { + const ContainerWrapper& Wrapper = DataPinResult.ResultValue.Get(); + TArray Temp = Wrapper.Values.GetGameplayTagArray(); + return ApplySinglePolicy(Temp, OutValues, SingleFromArray); + } + + return EFlowDataPinResolveResult::FailedMismatchedType; + } + }; + + // GameplayTagContainer + template <> + struct FFlowDataPinValueTraits : public FFlowStructTraitsBase + { + using PinType = FFlowPinType_GameplayTagContainer; + using ValueType = PinType::ValueType; + using WrapperType = FFlowDataPinValue_GameplayTagContainer; + + static EFlowDataPinResolveResult ExtractFromProperty(const FProperty* Property, const void* Container, TArray& OutValues) + { + static const UScriptStruct* ValueStruct = TBaseStructure::Get(); + + if (const FStructProperty* StructProp = CastField(Property)) + { + static const UScriptStruct* WrapperStruct = TBaseStructure::Get(); + if (StructProp->Struct == WrapperStruct) + { + const WrapperType* Wrapper = StructProp->ContainerPtrToValuePtr(Container); + OutValues = { Wrapper->Values }; + return EFlowDataPinResolveResult::Success; + } + + if (StructProp->Struct == ValueStruct) + { + OutValues = { *StructProp->ContainerPtrToValuePtr(Container) }; + return EFlowDataPinResolveResult::Success; + } + + // #FlowDataPinLegacy - support sourcing from old property wrappers For Now(tm) + static const UScriptStruct* OldPropStruct = LegacyWrapperType::StaticStruct(); + if (StructProp->Struct->IsChildOf(OldPropStruct)) + { + const LegacyWrapperType* Wrapper = StructProp->ContainerPtrToValuePtr(Container); + OutValues = { Wrapper->Value }; + return EFlowDataPinResolveResult::Success; + } + // -- + } + else if (const FArrayProperty* ArrayProp = CastField(Property)) + { + const FStructProperty* Inner = CastField(ArrayProp->Inner); + if (Inner && Inner->Struct == ValueStruct) + { + FScriptArrayHelper Helper(ArrayProp, ArrayProp->ContainerPtrToValuePtr(Container)); + ValueType Consolidated; + for (int32 i = 0; i < Helper.Num(); ++i) + { + Consolidated.AppendTags(*reinterpret_cast(Helper.GetRawPtr(i))); + } + OutValues = { Consolidated }; + return EFlowDataPinResolveResult::Success; + } + } + + return EFlowDataPinResolveResult::FailedMismatchedType; + } + + static EFlowDataPinResolveResult ExtractValues(const FFlowDataPinResult& DataPinResult, TArray& OutValues, EFlowSingleFromArray SingleFromArray) + { + if (!IsSuccess(DataPinResult.Result)) + { + return DataPinResult.Result; + } + + const UScriptStruct* ScriptStruct = DataPinResult.ResultValue.GetScriptStruct(); + + if (ScriptStruct == WrapperType::StaticStruct()) + { + const WrapperType& Wrapper = DataPinResult.ResultValue.Get(); + OutValues = { Wrapper.Values }; + return EFlowDataPinResolveResult::Success; + } + + if (ScriptStruct == FFlowDataPinValue_GameplayTag::StaticStruct()) + { + const FFlowDataPinValue_GameplayTag& Wrapper = DataPinResult.ResultValue.Get(); + OutValues = { FGameplayTagContainer::CreateFromArray(Wrapper.Values) }; + return EFlowDataPinResolveResult::Success; + } + + return EFlowDataPinResolveResult::FailedMismatchedType; + } + }; + + // Base for Object, Class + template + struct FFlowObjectTraitsBase + { + using ValueType = TPinType::ValueType; + using WrapperType = TPinType::WrapperType; + using LegacyWrapperType = TPinType::LegacyWrapperType; + + static EFlowDataPinResolveResult ExtractFromProperty(const FProperty* Property, const void* Container, TArray& OutValues) + { + if (const FStructProperty* StructProp = CastField(Property)) + { + if (StructProp->Struct == WrapperType::StaticStruct()) + { + const WrapperType* Wrapper = StructProp->ContainerPtrToValuePtr(Container); + for (const auto& Path : Wrapper->Values) + { + if constexpr (std::is_same_v, FSoftObjectPath> || + std::is_same_v, FSoftClassPath>) + { + OutValues.Add(Cast(Path.ResolveObject())); + } + else + { + OutValues.Add(Cast(Path)); + } + } + return EFlowDataPinResolveResult::Success; + } + + // #FlowDataPinLegacy - support sourcing from old property wrappers For Now(tm) + static const UScriptStruct* OldPropStruct = LegacyWrapperType::StaticStruct(); + if (StructProp->Struct->IsChildOf(OldPropStruct)) + { + const LegacyWrapperType* Wrapper = StructProp->ContainerPtrToValuePtr(Container); + OutValues = { Cast(Wrapper->GetObjectValue()) }; + return EFlowDataPinResolveResult::Success; + } + // -- + } + + if (const FArrayProperty* ArrProp = CastField(Property)) + { + if (const TProperty* InnerObjProp = CastField(ArrProp->Inner)) + { + FScriptArrayHelper ArrHelper(ArrProp, ArrProp->ContainerPtrToValuePtr(Container)); + const int32 Num = ArrHelper.Num(); + OutValues.Reserve(Num); + for (int32 i = 0; i < Num; ++i) + { + OutValues.Add(Cast(InnerObjProp->GetObjectPropertyValue(ArrHelper.GetRawPtr(i)))); + } + return EFlowDataPinResolveResult::Success; + } + else if (const TSoftProperty* InnerSoftProp = CastField(ArrProp->Inner)) + { + FScriptArrayHelper ArrHelper(ArrProp, ArrProp->ContainerPtrToValuePtr(Container)); + const int32 Num = ArrHelper.Num(); + OutValues.Reserve(Num); + for (int32 i = 0; i < Num; ++i) + { + const FSoftObjectPath Path = InnerSoftProp->GetPropertyValue(ArrHelper.GetRawPtr(i)).ToSoftObjectPath(); + OutValues.Add(Cast(Path.ResolveObject())); + } + return EFlowDataPinResolveResult::Success; + } + else if (const FWeakObjectProperty* InnerWeakProp = CastField(ArrProp->Inner)) + { + FScriptArrayHelper ArrHelper(ArrProp, ArrProp->ContainerPtrToValuePtr(Container)); + const int32 Num = ArrHelper.Num(); + OutValues.Reserve(Num); + for (int32 i = 0; i < Num; ++i) + { + OutValues.Add(Cast(InnerWeakProp->GetPropertyValue_InContainer(Container).Get())); + } + return EFlowDataPinResolveResult::Success; + } + } + + if (const TProperty* ObjProp = CastField(Property)) + { + OutValues = { Cast(ObjProp->GetObjectPropertyValue_InContainer(Container)) }; + return EFlowDataPinResolveResult::Success; + } + else if (const TSoftProperty* SoftObjProp = CastField(Property)) + { + const FSoftObjectPath Path = SoftObjProp->GetPropertyValue_InContainer(Container).ToSoftObjectPath(); + OutValues = { Cast(Path.ResolveObject()) }; + return EFlowDataPinResolveResult::Success; + } + else if (const FWeakObjectProperty* WeakProp = CastField(Property)) + { + OutValues = { Cast(WeakProp->GetPropertyValue_InContainer(Container).Get()) }; + return EFlowDataPinResolveResult::Success; + } + + return EFlowDataPinResolveResult::FailedMismatchedType; + } + + static EFlowDataPinResolveResult ExtractValues(const FFlowDataPinResult& DataPinResult, TArray& OutValues, EFlowSingleFromArray SingleFromArray) + { + if (!IsSuccess(DataPinResult.Result)) + { + return DataPinResult.Result; + } + + if (DataPinResult.ResultValue.GetScriptStruct() == WrapperType::StaticStruct()) + { + const WrapperType& Wrapper = DataPinResult.ResultValue.Get(); + const auto& Source = Wrapper.Values; // this is TArray or TArray + + if (SingleFromArray == EFlowSingleFromArray::EntireArray) + { + OutValues.Reserve(Source.Num()); + for (const auto& Path : Source) + { + if constexpr (std::is_same_v, FSoftObjectPath> || + std::is_same_v, FSoftClassPath>) + { + OutValues.Add(Cast(Path.ResolveObject())); + } + else + { + OutValues.Add(Cast(Path)); + } + } + } + else + { + const int32 Index = EFlowSingleFromArray_Classifiers::ConvertToIndex(SingleFromArray, Source.Num()); + if (!Source.IsValidIndex(Index)) + { + return EFlowDataPinResolveResult::FailedInsufficientValues; + } + + const auto& Path = Source[Index]; + if constexpr (std::is_same_v, FSoftObjectPath> || + std::is_same_v, FSoftClassPath>) + { + OutValues.Add(Cast(Path.ResolveObject())); + } + else + { + OutValues.Add(Cast(Path)); + } + } + + return EFlowDataPinResolveResult::Success; + } + + return EFlowDataPinResolveResult::FailedMismatchedType; + } + }; + + template <> struct FFlowDataPinValueTraits : public FFlowObjectTraitsBase {}; + template <> struct FFlowDataPinValueTraits : public FFlowObjectTraitsBase {}; + + // ----------------------------------------------------------------------- + // Value Extractors + // ----------------------------------------------------------------------- + + template + static EFlowDataPinResolveResult TryExtractValue(const FFlowDataPinResult& DataPinResult, typename TPinType::ValueType& OutValue, EFlowSingleFromArray SingleFromArray) + { + if (!IsSuccess(DataPinResult.Result)) + { + return DataPinResult.Result; + } + + TArray Values; + const EFlowDataPinResolveResult Result = FFlowDataPinValueTraits::ExtractValues(DataPinResult, Values, SingleFromArray); + + if (!IsSuccess(Result)) + { + return Result; + } + + if (Values.IsEmpty()) + { + return EFlowDataPinResolveResult::FailedInsufficientValues; + } + + OutValue = Values[0]; + return EFlowDataPinResolveResult::Success; + } + + template + static EFlowDataPinResolveResult TryExtractValues(const FFlowDataPinResult& DataPinResult, TArray& OutValues) + { + if (!IsSuccess(DataPinResult.Result)) + { + return DataPinResult.Result; + } + + return FFlowDataPinValueTraits::ExtractValues(DataPinResult, OutValues, EFlowSingleFromArray::EntireArray); + } + + // Special-case single-value extractor for enums (FName + EnumClass) + template + static EFlowDataPinResolveResult TryExtractValue(const FFlowDataPinResult& DataPinResult, typename TPinType::ValueType& OutValue, typename TPinType::FieldType*& OutField, EFlowSingleFromArray SingleFromArray) + { + if (!IsSuccess(DataPinResult.Result)) + { + return DataPinResult.Result; + } + + const typename TPinType::WrapperType& Wrapper = DataPinResult.ResultValue.Get(); + OutField = Cast(Wrapper.GetFieldType()); + return TryExtractValue(DataPinResult, OutValue, SingleFromArray); + } + + // Special-case array-value extractor for enums (TArray + EnumClass) + template + static EFlowDataPinResolveResult TryExtractValues(const FFlowDataPinResult& DataPinResult, TArray& OutValues, typename TPinType::FieldType*& OutField) + { + if (!IsSuccess(DataPinResult.Result)) + { + return DataPinResult.Result; + } + + const typename TPinType::WrapperType& Wrapper = DataPinResult.ResultValue.Get(); + OutField = Cast(Wrapper.GetFieldType()); + return TryExtractValues(DataPinResult, OutValues); + } + + // Special-case single-value extractor for enums (Native enum value) + template requires std::is_enum_v + static EFlowDataPinResolveResult TryExtractValue(const FFlowDataPinResult& DataPinResult, TEnumType& OutValue, EFlowSingleFromArray SingleFromArray) + { + if (!IsSuccess(DataPinResult.Result)) + { + return DataPinResult.Result; + } + + const FFlowDataPinValue_Enum& Wrapper = DataPinResult.ResultValue.Get(); + return Wrapper.TryGetSingleEnumValue(OutValue, SingleFromArray); + } + + // Special-case array-value extractor for enums (Native enum values) + template requires std::is_enum_v + static EFlowDataPinResolveResult TryExtractValues(const FFlowDataPinResult& DataPinResult, TArray& OutValues) + { + if (!IsSuccess(DataPinResult.Result)) + { + return DataPinResult.Result; + } + + const FFlowDataPinValue_Enum& Wrapper = DataPinResult.ResultValue.Get(); + return Wrapper.TryGetAllNativeEnumValues(OutValues); + } +} \ No newline at end of file diff --git a/Source/Flow/Public/Types/FlowPinTypesStandard.h b/Source/Flow/Public/Types/FlowPinTypesStandard.h new file mode 100644 index 000000000..e8bca110d --- /dev/null +++ b/Source/Flow/Public/Types/FlowPinTypesStandard.h @@ -0,0 +1,519 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Types/FlowPinType.h" +#include "Nodes/FlowPin.h" +#include "Math/Vector.h" +#include "Math/Rotator.h" +#include "Math/Transform.h" +#include "GameplayTagContainer.h" +#include "StructUtils/InstancedStruct.h" +#include "UObject/Class.h" + +#if WITH_EDITOR +#include "GraphEditorSettings.h" +#endif + +#include "FlowPinTypesStandard.generated.h" + +// Forward declarations +struct FFlowDataPinValue_Bool; +struct FFlowDataPinValue_Int; +struct FFlowDataPinValue_Int64; +struct FFlowDataPinValue_Float; +struct FFlowDataPinValue_Double; +struct FFlowDataPinValue_Name; +struct FFlowDataPinValue_String; +struct FFlowDataPinValue_Text; +struct FFlowDataPinValue_Enum; +struct FFlowDataPinValue_Vector; +struct FFlowDataPinValue_Rotator; +struct FFlowDataPinValue_Transform; +struct FFlowDataPinValue_GameplayTag; +struct FFlowDataPinValue_GameplayTagContainer; +struct FFlowDataPinValue_InstancedStruct; +struct FFlowDataPinValue_Object; +struct FFlowDataPinValue_Class; + +// #FlowDataPinLegacy +struct FFlowDataPinOutputProperty_Bool; +struct FFlowDataPinOutputProperty_Int32; +struct FFlowDataPinOutputProperty_Int64; +struct FFlowDataPinOutputProperty_Float; +struct FFlowDataPinOutputProperty_Double; +struct FFlowDataPinOutputProperty_Name; +struct FFlowDataPinOutputProperty_String; +struct FFlowDataPinOutputProperty_Text; +struct FFlowDataPinOutputProperty_Enum; +struct FFlowDataPinOutputProperty_Vector; +struct FFlowDataPinOutputProperty_Rotator; +struct FFlowDataPinOutputProperty_Transform; +struct FFlowDataPinOutputProperty_GameplayTag; +struct FFlowDataPinOutputProperty_GameplayTagContainer; +struct FFlowDataPinOutputProperty_InstancedStruct; +struct FFlowDataPinOutputProperty_Object; +struct FFlowDataPinOutputProperty_Class; +// -- + +// Exec +USTRUCT(BlueprintType) +struct FLOW_API FFlowPinType_Exec : public FFlowPinType +{ + GENERATED_BODY() + + using ValueType = void; + using WrapperType = void; + using MainPropertyType = void; + using LegacyWrapperType = void; + +private: + static const FFlowPinTypeName PinTypeNameExec; +public: + static const FFlowPinTypeName& GetPinTypeNameStatic() { return PinTypeNameExec; } + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinTypeNameExec; } + +#if WITH_EDITOR + virtual FLinearColor GetPinColor() const override { return GetDefault()->ExecutionPinTypeColor; } + virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; +#endif +}; + +// Bool +USTRUCT(BlueprintType) +struct FLOW_API FFlowPinType_Bool : public FFlowPinType +{ + GENERATED_BODY() + + using ValueType = bool; + using WrapperType = FFlowDataPinValue_Bool; + using MainPropertyType = FBoolProperty; + using LegacyWrapperType = FFlowDataPinOutputProperty_Bool; + +private: + static const FFlowPinTypeName PinTypeNameBool; +public: + static const FFlowPinTypeName& GetPinTypeNameStatic() { return PinTypeNameBool; } + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinTypeNameBool; } + +#if WITH_EDITOR + virtual FLinearColor GetPinColor() const override { return GetDefault()->BooleanPinTypeColor; } + virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; +#endif + + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; +}; + +// Int +USTRUCT(BlueprintType) +struct FLOW_API FFlowPinType_Int : public FFlowPinType +{ + GENERATED_BODY() + + using ValueType = int32; + using WrapperType = FFlowDataPinValue_Int; + using MainPropertyType = FIntProperty; + using LegacyWrapperType = FFlowDataPinOutputProperty_Int32; + +private: + static const FFlowPinTypeName PinTypeNameInt; +public: + static const FFlowPinTypeName& GetPinTypeNameStatic() { return PinTypeNameInt; } + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinTypeNameInt; } + +#if WITH_EDITOR + virtual FLinearColor GetPinColor() const override { return GetDefault()->IntPinTypeColor; } + virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; +#endif + + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; +}; + +// Int64 +USTRUCT(BlueprintType) +struct FLOW_API FFlowPinType_Int64 : public FFlowPinType +{ + GENERATED_BODY() + + using ValueType = int64; + using WrapperType = FFlowDataPinValue_Int64; + using MainPropertyType = FInt64Property; + using LegacyWrapperType = FFlowDataPinOutputProperty_Int64; + +private: + static const FFlowPinTypeName PinTypeNameInt64; +public: + static const FFlowPinTypeName& GetPinTypeNameStatic() { return PinTypeNameInt64; } + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinTypeNameInt64; } + +#if WITH_EDITOR + virtual FLinearColor GetPinColor() const override { return GetDefault()->IntPinTypeColor; } + virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; +#endif + + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; +}; + +// Float +USTRUCT(BlueprintType) +struct FLOW_API FFlowPinType_Float : public FFlowPinType +{ + GENERATED_BODY() + + using ValueType = float; + using WrapperType = FFlowDataPinValue_Float; + using MainPropertyType = FFloatProperty; + using LegacyWrapperType = FFlowDataPinOutputProperty_Float; + +private: + static const FFlowPinTypeName PinTypeNameFloat; +public: + static const FFlowPinTypeName& GetPinTypeNameStatic() { return PinTypeNameFloat; } + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinTypeNameFloat; } + +#if WITH_EDITOR + virtual FLinearColor GetPinColor() const override { return GetDefault()->FloatPinTypeColor; } + virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; +#endif + + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; +}; + +// Double +USTRUCT(BlueprintType) +struct FLOW_API FFlowPinType_Double : public FFlowPinType +{ + GENERATED_BODY() + + using ValueType = double; + using WrapperType = FFlowDataPinValue_Double; + using MainPropertyType = FDoubleProperty; + using LegacyWrapperType = FFlowDataPinOutputProperty_Double; + +private: + static const FFlowPinTypeName PinTypeNameDouble; +public: + static const FFlowPinTypeName& GetPinTypeNameStatic() { return PinTypeNameDouble; } + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinTypeNameDouble; } + +#if WITH_EDITOR + virtual FLinearColor GetPinColor() const override { return GetDefault()->FloatPinTypeColor; } + virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; +#endif + + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; +}; + +// Name +USTRUCT(BlueprintType) +struct FLOW_API FFlowPinType_Name : public FFlowPinType +{ + GENERATED_BODY() + + using ValueType = FName; + using WrapperType = FFlowDataPinValue_Name; + using MainPropertyType = FNameProperty; + using LegacyWrapperType = FFlowDataPinOutputProperty_Name; + +private: + static const FFlowPinTypeName PinTypeNameName; +public: + static const FFlowPinTypeName& GetPinTypeNameStatic() { return PinTypeNameName; } + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinTypeNameName; } + +#if WITH_EDITOR + virtual FLinearColor GetPinColor() const override { return GetDefault()->NamePinTypeColor; } + virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; +#endif + + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; +}; + +// String +USTRUCT(BlueprintType) +struct FLOW_API FFlowPinType_String : public FFlowPinType +{ + GENERATED_BODY() + + using ValueType = FString; + using WrapperType = FFlowDataPinValue_String; + using MainPropertyType = FStrProperty; + using LegacyWrapperType = FFlowDataPinOutputProperty_String; + +private: + static const FFlowPinTypeName PinTypeNameString; +public: + static const FFlowPinTypeName& GetPinTypeNameStatic() { return PinTypeNameString; } + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinTypeNameString; } + +#if WITH_EDITOR + virtual FLinearColor GetPinColor() const override { return GetDefault()->StringPinTypeColor; } + virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; +#endif + + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; +}; + +// Text +USTRUCT(BlueprintType) +struct FLOW_API FFlowPinType_Text : public FFlowPinType +{ + GENERATED_BODY() + + using ValueType = FText; + using WrapperType = FFlowDataPinValue_Text; + using MainPropertyType = FTextProperty; + using LegacyWrapperType = FFlowDataPinOutputProperty_Text; + +private: + static const FFlowPinTypeName PinTypeNameText; +public: + static const FFlowPinTypeName& GetPinTypeNameStatic() { return PinTypeNameText; } + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinTypeNameText; } + +#if WITH_EDITOR + virtual FLinearColor GetPinColor() const override { return GetDefault()->TextPinTypeColor; } + virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; +#endif + + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; +}; + +// Enum +USTRUCT(BlueprintType) +struct FLOW_API FFlowPinType_Enum : public FFlowPinType +{ + GENERATED_BODY() + + using ValueType = FName; + using WrapperType = FFlowDataPinValue_Enum; + using MainPropertyType = FEnumProperty; + using FieldType = UEnum; + using LegacyWrapperType = FFlowDataPinOutputProperty_Enum; + +private: + static const FFlowPinTypeName PinTypeNameEnum; +public: + static const FFlowPinTypeName& GetPinTypeNameStatic() { return PinTypeNameEnum; } + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinTypeNameEnum; } + +#if WITH_EDITOR + virtual FLinearColor GetPinColor() const override { return GetDefault()->DefaultPinTypeColor; } + virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; + virtual UObject* GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const override; +#endif + + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; +}; + +// Vector +USTRUCT(BlueprintType) +struct FLOW_API FFlowPinType_Vector : public FFlowPinType +{ + GENERATED_BODY() + + using ValueType = FVector; + using WrapperType = FFlowDataPinValue_Vector; + using MainPropertyType = FStructProperty; + using LegacyWrapperType = FFlowDataPinOutputProperty_Vector; + +private: + static const FFlowPinTypeName PinTypeNameVector; +public: + static const FFlowPinTypeName& GetPinTypeNameStatic() { return PinTypeNameVector; } + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinTypeNameVector; } + +#if WITH_EDITOR + virtual FLinearColor GetPinColor() const override { return GetDefault()->StructPinTypeColor; } + virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; + virtual UObject* GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const override; +#endif + + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; +}; + +// Rotator +USTRUCT(BlueprintType) +struct FLOW_API FFlowPinType_Rotator : public FFlowPinType +{ + GENERATED_BODY() + + using ValueType = FRotator; + using WrapperType = FFlowDataPinValue_Rotator; + using MainPropertyType = FStructProperty; + using LegacyWrapperType = FFlowDataPinOutputProperty_Rotator; + +private: + static const FFlowPinTypeName PinTypeNameRotator; +public: + static const FFlowPinTypeName& GetPinTypeNameStatic() { return PinTypeNameRotator; } + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinTypeNameRotator; } + +#if WITH_EDITOR + virtual FLinearColor GetPinColor() const override { return GetDefault()->StructPinTypeColor; } + virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; + virtual UObject* GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const override; +#endif + + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; +}; + +// Transform +USTRUCT(BlueprintType) +struct FLOW_API FFlowPinType_Transform : public FFlowPinType +{ + GENERATED_BODY() + + using ValueType = FTransform; + using WrapperType = FFlowDataPinValue_Transform; + using MainPropertyType = FStructProperty; + using LegacyWrapperType = FFlowDataPinOutputProperty_Transform; + +private: + static const FFlowPinTypeName PinTypeNameTransform; +public: + static const FFlowPinTypeName& GetPinTypeNameStatic() { return PinTypeNameTransform; } + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinTypeNameTransform; } + +#if WITH_EDITOR + virtual FLinearColor GetPinColor() const override { return GetDefault()->StructPinTypeColor; } + virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; + virtual UObject* GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const override; +#endif + + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; +}; + +// GameplayTag +USTRUCT(BlueprintType) +struct FLOW_API FFlowPinType_GameplayTag : public FFlowPinType +{ + GENERATED_BODY() + + using ValueType = FGameplayTag; + using WrapperType = FFlowDataPinValue_GameplayTag; + using MainPropertyType = FStructProperty; + using LegacyWrapperType = FFlowDataPinOutputProperty_GameplayTag; + +private: + static const FFlowPinTypeName PinTypeNameGameplayTag; +public: + static const FFlowPinTypeName& GetPinTypeNameStatic() { return PinTypeNameGameplayTag; } + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinTypeNameGameplayTag; } + +#if WITH_EDITOR + virtual FLinearColor GetPinColor() const override { return GetDefault()->DefaultPinTypeColor; } + virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; + virtual UObject* GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const override; +#endif + + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; +}; + +// GameplayTagContainer +USTRUCT(BlueprintType) +struct FLOW_API FFlowPinType_GameplayTagContainer : public FFlowPinType +{ + GENERATED_BODY() + + using ValueType = FGameplayTagContainer; + using WrapperType = FFlowDataPinValue_GameplayTagContainer; + using MainPropertyType = FStructProperty; + using LegacyWrapperType = FFlowDataPinOutputProperty_GameplayTagContainer; + +private: + static const FFlowPinTypeName PinTypeNameGameplayTagContainer; +public: + static const FFlowPinTypeName& GetPinTypeNameStatic() { return PinTypeNameGameplayTagContainer; } + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinTypeNameGameplayTagContainer; } + +#if WITH_EDITOR + virtual FLinearColor GetPinColor() const override { return GetDefault()->DefaultPinTypeColor; } + virtual bool SupportsMultiType(EFlowDataMultiType Mode) const { FLOW_ASSERT_ENUM_MAX(EFlowDataMultiType, 2); return (Mode == EFlowDataMultiType::Single); } + virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; + virtual UObject* GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const override; +#endif + + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; +}; + +// InstancedStruct +USTRUCT(BlueprintType) +struct FLOW_API FFlowPinType_InstancedStruct : public FFlowPinType +{ + GENERATED_BODY() + + using ValueType = FInstancedStruct; + using WrapperType = FFlowDataPinValue_InstancedStruct; + using MainPropertyType = FStructProperty; + using LegacyWrapperType = FFlowDataPinOutputProperty_InstancedStruct; + +private: + static const FFlowPinTypeName PinTypeNameInstancedStruct; +public: + static const FFlowPinTypeName& GetPinTypeNameStatic() { return PinTypeNameInstancedStruct; } + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinTypeNameInstancedStruct; } + +#if WITH_EDITOR + virtual FLinearColor GetPinColor() const override { return GetDefault()->StructPinTypeColor; } + virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; + virtual UObject* GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const override; +#endif + + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; +}; + +// Object +USTRUCT(BlueprintType) +struct FLOW_API FFlowPinType_Object : public FFlowPinType +{ + GENERATED_BODY() + + using ValueType = TObjectPtr; + using WrapperType = FFlowDataPinValue_Object; + using MainPropertyType = FObjectProperty; + using LegacyWrapperType = FFlowDataPinOutputProperty_Object; + +private: + static const FFlowPinTypeName PinTypeNameObject; +public: + static const FFlowPinTypeName& GetPinTypeNameStatic() { return PinTypeNameObject; } + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinTypeNameObject; } + +#if WITH_EDITOR + virtual FLinearColor GetPinColor() const override { return GetDefault()->ObjectPinTypeColor; } + virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; + virtual UObject* GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const override; + + static UClass* TryGetObjectClassFromProperty(const FProperty& MetaDataProperty); + static UClass* TryGetMetaClassFromProperty(const FProperty& MetaDataProperty); +#endif + + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; +}; + +// Class +USTRUCT(BlueprintType) +struct FLOW_API FFlowPinType_Class : public FFlowPinType +{ + GENERATED_BODY() + + using ValueType = TObjectPtr; + using WrapperType = FFlowDataPinValue_Class; + using MainPropertyType = FClassProperty; + using LegacyWrapperType = FFlowDataPinOutputProperty_Class; + +private: + static const FFlowPinTypeName PinTypeNameClass; +public: + static const FFlowPinTypeName& GetPinTypeNameStatic() { return PinTypeNameClass; } + virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinTypeNameClass; } + +#if WITH_EDITOR + virtual FLinearColor GetPinColor() const override { return GetDefault()->ClassPinTypeColor; } + virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; + virtual UObject* GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const override; +#endif + + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; +}; \ No newline at end of file diff --git a/Source/Flow/Public/Types/FlowStructUtils.h b/Source/Flow/Public/Types/FlowStructUtils.h new file mode 100644 index 000000000..c6054fe73 --- /dev/null +++ b/Source/Flow/Public/Types/FlowStructUtils.h @@ -0,0 +1,102 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "UObject/Field.h" + +#if WITH_EDITOR +namespace FlowStructUtils +{ + template + static UScriptStruct* FindScriptStructForProperty(const FProperty& Property) + { + const FStructProperty* StructProperty = CastField(&Property); + if (!StructProperty) + { + return nullptr; + } + + UScriptStruct* ScriptStruct = TPropertyType::StaticStruct(); + + if (StructProperty->Struct == ScriptStruct) + { + static UScriptStruct* UnrealType = TBaseStructure::Get(); + return UnrealType; + } + + return StructProperty->Struct; + } + + template + TStruct* GetTypedStructValue(FProperty& Prop, void* Container) + { + static_assert(TIsDerivedFrom::IsDerived, "Must be a USTRUCT type"); + if (auto* StructProp = CastField(&Prop)) + { + if (StructProp->Struct->IsChildOf(TStruct::StaticStruct())) + { + return reinterpret_cast(StructProp + ->ContainerPtrToValuePtr(Container)); + } + } + return nullptr; + } + + // Internal SFINAE probe: will fail to compile if TStruct has no StaticStruct(). + template + struct THasStaticStruct + { + private: + template + static auto Test(int) -> decltype(U::StaticStruct(), std::true_type{}); + template + static std::false_type Test(...); + public: + static constexpr bool Value = decltype(Test(0))::value; + }; + + template + FORCEINLINE TStruct* CastStructValue(FProperty* Prop, void* Container) + { + static_assert(THasStaticStruct::Value, + "TStruct must be a USTRUCT type providing StaticStruct()."); + + if (!Prop || !Container) + return nullptr; + + FStructProperty* StructProp = CastField(Prop); + if (!StructProp) + return nullptr; + + // Check exact or derived type. + if (!StructProp->Struct->IsChildOf(TStruct::StaticStruct())) + return nullptr; + + // Retrieve the memory for this property within the container and cast. + void* ValueMem = StructProp->ContainerPtrToValuePtr(Container); + return static_cast(ValueMem); + } + + // Pointer overload (const) + template + FORCEINLINE const TStruct* CastStructValue(const FProperty* Prop, const void* Container) + { + return CastStructValue( + const_cast(Prop), + const_cast(Container)); + } + + // Reference overloads for convenience + template + FORCEINLINE TStruct* CastStructValue(FProperty& Prop, void* Container) + { + return CastStructValue(&Prop, Container); + } + + template + FORCEINLINE const TStruct* CastStructValue(const FProperty& Prop, const void* Container) + { + return CastStructValue(&Prop, Container); + } +} +#endif \ No newline at end of file diff --git a/Source/FlowEditor/Private/Asset/AssetDefinition_FlowAsset.cpp b/Source/FlowEditor/Private/Asset/AssetDefinition_FlowAsset.cpp index 03a513c6d..ce1ef302e 100644 --- a/Source/FlowEditor/Private/Asset/AssetDefinition_FlowAsset.cpp +++ b/Source/FlowEditor/Private/Asset/AssetDefinition_FlowAsset.cpp @@ -30,7 +30,7 @@ TConstArrayView UAssetDefinition_FlowAsset::GetAssetCategori { if (UFlowGraphSettings::Get()->bExposeFlowAssetCreation) { - static const auto Categories = {FFLowAssetCategoryPaths::Flow}; + static const auto Categories = {FFlowAssetCategoryPaths::Flow}; return Categories; } @@ -60,15 +60,15 @@ EAssetCommandResult UAssetDefinition_FlowAsset::PerformAssetDiff(const FAssetDif return EAssetCommandResult::Unhandled; } - const UFlowAsset* OldFlow = CastChecked(DiffArgs.OldAsset); - const UFlowAsset* NewFlow = CastChecked(DiffArgs.NewAsset); + const UFlowAsset* OldFlow = Cast(DiffArgs.OldAsset); + const UFlowAsset* NewFlow = Cast(DiffArgs.NewAsset); // sometimes we're comparing different revisions of one single asset (other // times we're comparing two completely separate assets altogether) - const bool bIsSingleAsset = (OldFlow->GetName() == NewFlow->GetName()); - + const bool bIsSingleAsset = !IsValid(OldFlow) || !IsValid(NewFlow) || (OldFlow->GetName() == NewFlow->GetName()); + static const FText BasicWindowTitle = LOCTEXT("FlowAssetDiff", "FlowAsset Diff"); - const FText WindowTitle = !bIsSingleAsset ? BasicWindowTitle : FText::Format(LOCTEXT("FlowAsset Diff", "{0} - FlowAsset Diff"), FText::FromString(NewFlow->GetName())); + const FText WindowTitle = !bIsSingleAsset ? BasicWindowTitle : FText::Format(LOCTEXT("FlowAsset Diff", "{0} - FlowAsset Diff"), FText::FromString(IsValid(NewFlow) ? NewFlow->GetName() : OldFlow->GetName())); SFlowDiff::CreateDiffWindow(WindowTitle, OldFlow, NewFlow, DiffArgs.OldRevision, DiffArgs.NewRevision); return EAssetCommandResult::Handled; diff --git a/Source/FlowEditor/Private/Asset/AssetDefinition_FlowAssetParams.cpp b/Source/FlowEditor/Private/Asset/AssetDefinition_FlowAssetParams.cpp new file mode 100644 index 000000000..1c9748aaa --- /dev/null +++ b/Source/FlowEditor/Private/Asset/AssetDefinition_FlowAssetParams.cpp @@ -0,0 +1,152 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Asset/AssetDefinition_FlowAssetParams.h" +#include "Asset/FlowAssetParams.h" +#include "FlowAsset.h" +#include "FlowEditorLogChannels.h" +#include "FlowEditorModule.h" +#include "Types/FlowDataPinValuesStandard.h" +#include "AssetRegistry/AssetRegistryModule.h" +#include "AssetToolsModule.h" +#include "ContentBrowserMenuContexts.h" +#include "ContentBrowserModule.h" +#include "FileHelpers.h" +#include "IContentBrowserSingleton.h" +#include "SourceControlHelpers.h" +#include "ToolMenus.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(AssetDefinition_FlowAssetParams) + +#define LOCTEXT_NAMESPACE "AssetDefinition_FlowAssetParams" + +FText UAssetDefinition_FlowAssetParams::GetAssetDisplayName() const +{ + return LOCTEXT("GetAssetDisplayName", "Flow Asset Params"); +} + +FLinearColor UAssetDefinition_FlowAssetParams::GetAssetColor() const +{ + return FLinearColor(255, 196, 128); +} + +TSoftClassPtr UAssetDefinition_FlowAssetParams::GetAssetClass() const +{ + return UFlowAssetParams::StaticClass(); +} + +TConstArrayView UAssetDefinition_FlowAssetParams::GetAssetCategories() const +{ + static const auto Categories = { FFlowAssetCategoryPaths::Flow }; + return Categories; +} + +FAssetSupportResponse UAssetDefinition_FlowAssetParams::CanLocalize(const FAssetData& InAsset) const +{ + return FAssetSupportResponse::Supported(); +} + +namespace MenuExtension_FlowAssetParams +{ + static void ExecuteCreateChildParams(const FToolMenuContext& InContext) + { + const UContentBrowserAssetContextMenuContext* Context = UContentBrowserAssetContextMenuContext::FindContextWithAssets(InContext); + if (!Context) + { + UE_LOG(LogFlowEditor, Warning, TEXT("No valid context for Create Child Params action")); + return; + } + + const TArray& SelectedParams = Context->LoadSelectedObjects(); + if (SelectedParams.Num() != 1) + { + UE_LOG(LogFlowEditor, Warning, TEXT("Create Child Params requires exactly one selected Flow Asset Params")); + return; + } + + UFlowAssetParams* ParentParams = SelectedParams[0]; + if (!IsValid(ParentParams)) + { + UE_LOG(LogFlowEditor, Error, TEXT("Invalid Flow Asset Params selected for Create Child Params")); + return; + } + + FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked("AssetTools"); + const FString PackagePath = FPackageName::GetLongPackagePath(ParentParams->GetPackage()->GetPathName()); + const FString BaseAssetName = ParentParams->GetName(); + + FString UniquePackageName, UniqueAssetName; + AssetToolsModule.Get().CreateUniqueAssetName(PackagePath + TEXT("/") + BaseAssetName, TEXT(""), UniquePackageName, UniqueAssetName); + if (UniqueAssetName.IsEmpty()) + { + UE_LOG(LogFlowEditor, Error, TEXT("Failed to generate unique asset name for child params of %s"), *BaseAssetName); + return; + } + + UFlowAssetParams* NewParams = Cast( + AssetToolsModule.Get().CreateAsset(UniqueAssetName, PackagePath, ParentParams->GetClass(), nullptr)); + if (!IsValid(NewParams)) + { + UE_LOG(LogFlowEditor, Error, TEXT("Failed to create child Flow Asset Params: %s"), *UniqueAssetName); + return; + } + + if (USourceControlHelpers::IsAvailable()) + { + const FString FileName = USourceControlHelpers::PackageFilename(NewParams->GetPathName()); + if (!USourceControlHelpers::CheckOutOrAddFile(FileName)) + { + UE_LOG(LogFlowEditor, Warning, TEXT("Failed to check out/add %s; saved in-memory only"), *NewParams->GetPathName()); + } + } + + NewParams->ConfigureFlowAssetParams(ParentParams->OwnerFlowAsset, ParentParams, ParentParams->Properties); + + // Save the package (force save even if not prompted) + UPackage* Package = NewParams->GetPackage(); + TArray PackagesToSave = { Package }; + + // Saves without dialog/prompt + const bool bForceSave = true; + if (!UEditorLoadingAndSavingUtils::SavePackages(PackagesToSave, bForceSave)) + { + UE_LOG(LogFlowEditor, Error, TEXT("Failed to save child Flow Asset Params: %s"), *NewParams->GetPathName()); + return; + } + + FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked("ContentBrowser"); + FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked("AssetRegistry"); + AssetRegistryModule.Get().AssetCreated(NewParams); + TArray AssetsToSync = { NewParams }; + ContentBrowserModule.Get().SyncBrowserToAssets(AssetsToSync, true); + } + + static void RegisterContextMenu() + { + UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateLambda([]() + { + FToolMenuOwnerScoped OwnerScoped(UE_MODULE_NAME); + UToolMenu* Menu = UE::ContentBrowser::ExtendToolMenu_AssetContextMenu(UFlowAssetParams::StaticClass()); + + FToolMenuSection& Section = Menu->FindOrAddSection("GetAssetActions"); + Section.AddDynamicEntry("Flow Asset Params Commands", FNewToolMenuSectionDelegate::CreateLambda([](FToolMenuSection& InSection) + { + const TAttribute Label = LOCTEXT("FlowAssetParams_CreateChildParams", "Create Child Params"); + const TAttribute ToolTip = LOCTEXT("FlowAssetParams_CreateChildParamsTooltip", "Creates a new Flow Asset Params inheriting from the selected params."); + const FSlateIcon Icon = FSlateIcon(); + + FToolUIAction UIAction; + UIAction.ExecuteAction = FToolMenuExecuteAction::CreateStatic(&ExecuteCreateChildParams); + UIAction.CanExecuteAction = FToolMenuCanExecuteAction::CreateLambda([](const FToolMenuContext& InContext) + { + const UContentBrowserAssetContextMenuContext* Context = UContentBrowserAssetContextMenuContext::FindContextWithAssets(InContext); + return Context && Context->SelectedAssets.Num() == 1; + }); + InSection.AddMenuEntry("FlowAssetParams_CreateChildParams", Label, ToolTip, Icon, UIAction); + })); + })); + } + + static FDelayedAutoRegisterHelper DelayedAutoRegister(EDelayedRegisterRunPhase::EndOfEngineInit, &RegisterContextMenu); +} + +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp b/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp index 370ff0bd9..270eab9ac 100644 --- a/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp +++ b/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp @@ -1,10 +1,10 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "Asset/FlowObjectDiff.h" #include "Asset/FlowDiffControl.h" -#include "Graph/Nodes/FlowGraphNode.h" #include "Nodes/FlowNodeBase.h" +#include "Graph/Nodes/FlowGraphNode.h" #include "DiffResults.h" #include "EdGraph/EdGraph.h" @@ -72,7 +72,7 @@ void FFlowObjectDiff::DiffProperties(TArray& OutProperty if (OldDetailsView.IsValid() && NewDetailsView.IsValid()) { static constexpr bool bSortByDisplayOrder = true; - OldDetailsView->DiffAgainst(*NewDetailsView.Get(), OutPropertyDiffsArray, bSortByDisplayOrder); + //OldDetailsView->DiffAgainst(*NewDetailsView.Get(), OutPropertyDiffsArray, bSortByDisplayOrder); } } diff --git a/Source/FlowEditor/Private/Asset/SFlowDiff.cpp b/Source/FlowEditor/Private/Asset/SFlowDiff.cpp index 3ec288cb0..96540b6dc 100644 --- a/Source/FlowEditor/Private/Asset/SFlowDiff.cpp +++ b/Source/FlowEditor/Private/Asset/SFlowDiff.cpp @@ -11,6 +11,7 @@ #include "Framework/Commands/GenericCommands.h" #include "Framework/MultiBox/MultiBoxBuilder.h" #include "Framework/MultiBox/MultiBoxDefs.h" +#include "Graph/Nodes/FlowGraphNode.h" #include "GraphDiffControl.h" #include "HAL/PlatformApplicationMisc.h" #include "Internationalization/Text.h" @@ -86,11 +87,13 @@ BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION void SFlowDiff::Construct(const FArguments& InArgs) { - check(InArgs._OldFlow && InArgs._NewFlow); + check(InArgs._OldFlow || InArgs._NewFlow); PanelOld.FlowAsset = InArgs._OldFlow; PanelNew.FlowAsset = InArgs._NewFlow; PanelOld.RevisionInfo = InArgs._OldRevision; PanelNew.RevisionInfo = InArgs._NewRevision; + PanelOld.bIsOldPanel = true; + PanelNew.bIsOldPanel = false; // sometimes we want to clearly identify the assets being diffed (when it's // not the same asset in each panel) @@ -330,7 +333,7 @@ TSharedPtr SFlowDiff::CreateDiffWindow(const FText WindowTitle, const U { // sometimes we're comparing different revisions of one single asset (other // times we're comparing two completely separate assets altogether) - const bool bIsSingleAsset = (NewFlow->GetName() == OldFlow->GetName()); + const bool bIsSingleAsset = !IsValid(OldFlow) || !IsValid(NewFlow) || (OldFlow->GetName() == NewFlow->GetName()); TSharedPtr Window = SNew(SWindow) .Title(WindowTitle) @@ -425,12 +428,16 @@ void SFlowDiff::OnDiffListSelectionChanged(TSharedPtr FlowO SafeClearSelection(PanelNew.GraphEditor); SafeClearSelection(PanelOld.GraphEditor); + // PanelDefaultDetailsView can be used for displaying nodes on click. Clear out it's content before potentially trying to show an empty panel. + PanelOld.PanelDefaultDetailsView->SetObject(nullptr); + PanelNew.PanelDefaultDetailsView->SetObject(nullptr); + //Select the details panel to display below the graphs. //Show an empty details panel if there is no generated details panel. const TSharedPtr OldDetailsPanel = FlowObjectDiff->OldDetailsView.IsValid() ? - FlowObjectDiff->OldDetailsView->DetailsWidget() : PanelOld.EmptyDetailsView.ToSharedRef(); + FlowObjectDiff->OldDetailsView->DetailsWidget() : PanelOld.PanelDefaultDetailsView.ToSharedRef(); const TSharedPtr NewDetailsPanel = FlowObjectDiff->NewDetailsView.IsValid() ? - FlowObjectDiff->NewDetailsView->DetailsWidget() : PanelNew.EmptyDetailsView.ToSharedRef(); + FlowObjectDiff->NewDetailsView->DetailsWidget() : PanelNew.PanelDefaultDetailsView.ToSharedRef(); GraphDiffSplitter->SetBottomLeftContent(OldDetailsPanel.ToSharedRef()); GraphDiffSplitter->SetBottomRightContent(NewDetailsPanel.ToSharedRef()); @@ -531,6 +538,8 @@ void FFlowDiffPanel::GeneratePanel(UEdGraph* Graph, TSharedPtrSetContent(Widget.ToSharedRef()); } +void FFlowDiffPanel::OnNodeClicked(UObject* ClickedNode) +{ + UFlowGraphNode* ClickedFlowGraphNode = Cast(ClickedNode); + if (IsValid(ClickedFlowGraphNode)) + { + PanelDefaultDetailsView->SetObject(ClickedFlowGraphNode->GetFlowNodeBase()); + } + else + { + PanelDefaultDetailsView->SetObject(nullptr); + } + + if (GraphDiffSplitter.IsValid()) + { + if (bIsOldPanel) + { + GraphDiffSplitter.Pin()->SetBottomLeftContent(PanelDefaultDetailsView.ToSharedRef()); + } + else + { + GraphDiffSplitter.Pin()->SetBottomRightContent(PanelDefaultDetailsView.ToSharedRef()); + } + } +} + FGraphPanelSelectionSet FFlowDiffPanel::GetSelectedNodes() const { FGraphPanelSelectionSet CurrentSelection; @@ -660,13 +694,13 @@ void SFlowDiff::HandleGraphChanged(const FString& GraphPath) }); // only regenerate PanelOld if the old graph has changed - if (!PanelOld.GraphEditor.IsValid() || GraphOld != PanelOld.GraphEditor.Pin()->GetCurrentGraph()) + if (PanelOld.FlowAsset && (!PanelOld.GraphEditor.IsValid() || GraphOld != PanelOld.GraphEditor.Pin()->GetCurrentGraph())) { PanelOld.GeneratePanel(GraphOld, DiffResults, FocusedDiffResult); } // only regenerate PanelNew if the old graph has changed - if (!PanelNew.GraphEditor.IsValid() || GraphNew != PanelNew.GraphEditor.Pin()->GetCurrentGraph()) + if (PanelNew.FlowAsset && (!PanelNew.GraphEditor.IsValid() || GraphNew != PanelNew.GraphEditor.Pin()->GetCurrentGraph())) { PanelNew.GeneratePanel(GraphNew, DiffResults, FocusedDiffResult); } @@ -695,8 +729,8 @@ void SFlowDiff::GenerateDifferencesList() return DetailsView; }; - PanelOld.EmptyDetailsView = CreateInspector(nullptr); - PanelNew.EmptyDetailsView = CreateInspector(nullptr); + PanelOld.PanelDefaultDetailsView = CreateInspector(nullptr); + PanelNew.PanelDefaultDetailsView = CreateInspector(nullptr); // Now that we have done the diffs, create the panel widgets ModePanels.Add(DetailsMode, GenerateDetailsPanel()); @@ -715,23 +749,63 @@ SFlowDiff::FDiffControl SFlowDiff::GenerateDetailsPanel() const TSharedRef Splitter = SNew(SDetailsSplitter); if (PanelOld.FlowAsset) + { + if (PanelNew.FlowAsset) + { + Splitter->AddSlot( + SDetailsSplitter::Slot() + .Value(0.5f) + .DetailsView(NewDiffControl->GetDetailsWidget(PanelOld.FlowAsset)) + .DifferencesWithRightPanel(NewDiffControl.ToSharedRef(), &FFlowAssetDiffControl::GetDifferencesWithRight, Cast(PanelOld.FlowAsset)) + ); + } + else + { + Splitter->AddSlot( + SDetailsSplitter::Slot() + .Value(0.5f) + .DetailsView(NewDiffControl->GetDetailsWidget(PanelOld.FlowAsset)) + ); + } + } + else { Splitter->AddSlot( SDetailsSplitter::Slot() .Value(0.5f) - .DetailsView(NewDiffControl->GetDetailsWidget(PanelOld.FlowAsset)) - .DifferencesWithRightPanel(NewDiffControl.ToSharedRef(), &FFlowAssetDiffControl::GetDifferencesWithRight, Cast(PanelOld.FlowAsset)) + .DetailsView(PanelOld.PanelDefaultDetailsView) ); } - if (PanelNew.FlowAsset) + + if ( PanelNew.FlowAsset) + { + if (PanelOld.FlowAsset) + { + Splitter->AddSlot( + SDetailsSplitter::Slot() + .Value(0.5f) + .DetailsView(NewDiffControl->GetDetailsWidget(PanelNew.FlowAsset)) + .DifferencesWithRightPanel(NewDiffControl.ToSharedRef(), &FFlowAssetDiffControl::GetDifferencesWithRight, Cast(PanelOld.FlowAsset)) + ); + } + else + { + Splitter->AddSlot( + SDetailsSplitter::Slot() + .Value(0.5f) + .DetailsView(NewDiffControl->GetDetailsWidget(PanelNew.FlowAsset)) + ); + } + } + else { Splitter->AddSlot( SDetailsSplitter::Slot() .Value(0.5f) - .DetailsView(NewDiffControl->GetDetailsWidget(PanelNew.FlowAsset)) - .DifferencesWithRightPanel(NewDiffControl.ToSharedRef(), &FFlowAssetDiffControl::GetDifferencesWithRight, Cast(PanelOld.FlowAsset)) + .DetailsView(PanelNew.PanelDefaultDetailsView) ); } + Ret.Widget = Splitter; return Ret; @@ -740,15 +814,24 @@ SFlowDiff::FDiffControl SFlowDiff::GenerateDetailsPanel() SFlowDiff::FDiffControl SFlowDiff::GenerateGraphPanel() { // We only have a single permanent graph in Flow Asset - GraphToDiff = MakeShared(this, PanelOld.FlowAsset->GetGraph(), PanelNew.FlowAsset->GetGraph(), PanelOld.RevisionInfo, PanelNew.RevisionInfo); + GraphToDiff = MakeShared( + this, + IsValid(PanelOld.FlowAsset) ? PanelOld.FlowAsset->GetGraph() : nullptr, + IsValid(PanelNew.FlowAsset) ? PanelNew.FlowAsset->GetGraph() : nullptr, + PanelOld.RevisionInfo, + PanelNew.RevisionInfo); GraphToDiff->GenerateTreeEntries(PrimaryDifferencesList, RealDifferences); SAssignNew(GraphDiffSplitter,SSplitter2x2) .TopLeft()[ GenerateGraphWidgetForPanel(PanelOld) ] .TopRight()[ GenerateGraphWidgetForPanel(PanelNew) ] - .BottomLeft()[ PanelOld.EmptyDetailsView.ToSharedRef() ] - .BottomRight()[ PanelNew.EmptyDetailsView.ToSharedRef() ]; + .BottomLeft()[ PanelOld.PanelDefaultDetailsView.ToSharedRef() ] + .BottomRight()[ PanelNew.PanelDefaultDetailsView.ToSharedRef() ]; + + //the panels need a pointer to GraphDiffSplitter to update DetailsViews on click of a node. + PanelOld.GraphDiffSplitter = GraphDiffSplitter; + PanelNew.GraphDiffSplitter = GraphDiffSplitter; static const FVector2D GraphPercentage = {.5f, .7f}; static const FVector2D DetailsViewPercentage = {.5f, .3f}; @@ -761,8 +844,13 @@ SFlowDiff::FDiffControl SFlowDiff::GenerateGraphPanel() return Ret; } -TSharedRef SFlowDiff::GenerateGraphWidgetForPanel(FFlowDiffPanel& OutDiffPanel) const +TSharedRef SFlowDiff::GenerateGraphWidgetForPanel(FFlowDiffPanel& OutDiffPanel) const { + if (!IsValid(OutDiffPanel.FlowAsset)) + { + return SNullWidget::NullWidget; + } + return SNew(SOverlay) + SOverlay::Slot() // Graph slot [ diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowAssetParamsPtrCustomization.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowAssetParamsPtrCustomization.cpp new file mode 100644 index 000000000..dc3406b04 --- /dev/null +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowAssetParamsPtrCustomization.cpp @@ -0,0 +1,192 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "DetailCustomizations/FlowAssetParamsPtrCustomization.h" +#include "Asset/FlowAssetParams.h" +#include "FlowAsset.h" +#include "FlowComponent.h" +#include "FlowEditorLogChannels.h" +#include "Interfaces/FlowAssetProviderInterface.h" +#include "AssetRegistry/AssetRegistryModule.h" +#include "ContentBrowserModule.h" +#include "DetailLayoutBuilder.h" +#include "DetailWidgetRow.h" +#include "IContentBrowserSingleton.h" +#include "PropertyCustomizationHelpers.h" +#include "Widgets/Input/SButton.h" + +#define LOCTEXT_NAMESPACE "FlowAssetParamsPtrCustomization" + +TSharedRef FFlowAssetParamsPtrCustomization::MakeInstance() +{ + return MakeShared(); +} + +void FFlowAssetParamsPtrCustomization::CustomizeHeader( + TSharedRef PropertyHandle, + FDetailWidgetRow& HeaderRow, + IPropertyTypeCustomizationUtils& CustomizationUtils) +{ + StructPropertyHandle = PropertyHandle; + + const TSharedRef ObjectPicker = SNew(SObjectPropertyEntryBox) + .PropertyHandle(PropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowAssetParamsPtr, AssetPtr))) + .AllowedClass(UFlowAssetParams::StaticClass()) + .AllowClear(true) + .DisplayUseSelected(false) + .DisplayBrowse(false) + .DisplayCompactSize(true) + .OnShouldFilterAsset(this, &FFlowAssetParamsPtrCustomization::ShouldFilterAsset); + + // Show create button if ShowCreateNew metadata is specified + const bool bShowCreateButton = PropertyHandle->HasMetaData(TEXT("ShowCreateNew")); + + HeaderRow + .NameContent()[PropertyHandle->CreatePropertyNameWidget()] + .ValueContent() + .MinDesiredWidth(200.f) + .MaxDesiredWidth(800.f) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot().FillWidth(1.0f) + [ + ObjectPicker + ] + + SHorizontalBox::Slot().AutoWidth().Padding(2, 0) + .VAlign(VAlign_Center) + [ + bShowCreateButton ? + PropertyCustomizationHelpers::MakeAddButton( + FSimpleDelegate::CreateSP(this, &FFlowAssetParamsPtrCustomization::HandleCreateNew), + LOCTEXT("CreateNewAsset", "Create New") + ) : + SNullWidget::NullWidget + ] + ]; +} + +void FFlowAssetParamsPtrCustomization::CustomizeChildren( + TSharedRef PropertyHandle, + IDetailChildrenBuilder& ChildBuilder, + IPropertyTypeCustomizationUtils& CustomizationUtils) +{ +} + +void FFlowAssetParamsPtrCustomization::HandleCreateNew() +{ + if (!StructPropertyHandle.IsValid()) + { + UE_LOG(LogFlowEditor, Error, TEXT("Invalid property handle for FFlowAssetParamsPtr customization")); + + return; + } + + TArray OuterObjects; + StructPropertyHandle->GetOuterObjects(OuterObjects); + if (OuterObjects.Num() == 0) + { + UE_LOG(LogFlowEditor, Error, TEXT("No outer objects found for BaseAssetParams")); + + return; + } + + const FName PropertyName = StructPropertyHandle->GetProperty()->GetFName(); + FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked("AssetRegistry"); + + TArray AssetsToSync; + + for (UObject* OuterObject : OuterObjects) + { + UFlowAsset* FlowAsset = CastChecked(OuterObject, ECastCheckedType::NullAllowed); + if (!IsValid(FlowAsset)) + { + UE_LOG(LogFlowEditor, Error, TEXT("Outer object is not a valid UFlowAsset: %s"), *OuterObject->GetPathName()); + + continue; + } + + if (PropertyName != GET_MEMBER_NAME_CHECKED(UFlowAsset, BaseAssetParams)) + { + UE_LOG(LogFlowEditor, Error, TEXT("Property %s is not BaseAssetParams for %s"), *PropertyName.ToString(), *FlowAsset->GetPathName()); + + continue; + } + + UFlowAssetParams* NewParams = FlowAsset->GenerateParamsFromStartNode(); + if (IsValid(NewParams)) + { + StructPropertyHandle->NotifyPreChange(); + StructPropertyHandle->SetValueFromFormattedString(NewParams->GetPathName()); + StructPropertyHandle->NotifyPostChange(EPropertyChangeType::ValueSet); + + AssetRegistryModule.Get().AssetCreated(NewParams); + AssetsToSync.Add(NewParams); + } + } + + if (!AssetsToSync.IsEmpty()) + { + FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked("ContentBrowser"); + ContentBrowserModule.Get().SyncBrowserToAssets(AssetsToSync, true); + } +} + +bool FFlowAssetParamsPtrCustomization::ShouldFilterAsset(const FAssetData& AssetData) const +{ + UFlowAssetParams* Params = Cast(AssetData.GetAsset()); + if (!Params) + { + // Filter out invalid assets + return true; + } + + // Ensure Params->OwnerFlowAsset is valid + if (Params->OwnerFlowAsset.IsNull()) + { + UE_LOG(LogFlowEditor, Warning, TEXT("OwnerFlowAsset is null for %s"), *AssetData.GetFullName()); + + // Filter out if OwnerFlowAsset is invalid + return true; + } + + // Check if child params are allowed + const bool bHideChildParams = StructPropertyHandle->HasMetaData(TEXT("HideChildParams")); + if (bHideChildParams && !Params->ParentParams.AssetPtr.IsNull()) + { + // Filter out params with non-null ParentParams unless allowed + return true; + } + + TArray OuterObjects; + StructPropertyHandle->GetOuterObjects(OuterObjects); + if (OuterObjects.IsEmpty()) + { + UE_LOG(LogFlowEditor, Warning, TEXT("No outer objects found for FFlowAssetParamsPtr customization")); + + // Filter out if no outer objects + return true; + } + + // All OwnerAssets must match Params->OwnerFlowAsset + for (UObject* OuterObject : OuterObjects) + { + UFlowAsset* OwnerAssetCur = Cast(OuterObject); + if (!OwnerAssetCur) + { + if (IFlowAssetProviderInterface* AssetProvider = Cast(OuterObject)) + { + OwnerAssetCur = AssetProvider->ProvideFlowAsset(); + } + } + + if (!IsValid(OwnerAssetCur) || Params->OwnerFlowAsset != OwnerAssetCur) + { + // Filter out if any OwnerAsset doesn't match + return true; + } + } + + // Allow the asset + return false; +} + +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinPropertyCustomizationBase.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinPropertyCustomizationBase.cpp deleted file mode 100644 index e9e998a66..000000000 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinPropertyCustomizationBase.cpp +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#include "DetailCustomizations/FlowDataPinPropertyCustomizationBase.h" -#include "DetailWidgetRow.h" - -void FFlowDataPinPropertyCustomizationBase::CustomizeHeader(TSharedRef InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) -{ - uint32 NumChildren = 0; - InStructPropertyHandle->GetNumChildren(NumChildren); - - for (uint32 ChildNum = 0; ChildNum < NumChildren; ++ChildNum) - { - TSharedPtr ChildPtr = InStructPropertyHandle->GetChildHandle(ChildNum); - - HeaderRow.NameContent() - [ - InStructPropertyHandle->CreatePropertyNameWidget() - ]; - HeaderRow.ValueContent() - [ - ChildPtr->CreatePropertyValueWidget() - ]; - - // Use the 0th child's Value Widget to replace the Header row's Value Widget - break; - } -} - -void FFlowDataPinPropertyCustomizationBase::CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) -{ - // Do not display any children -} diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinProperty_ClassCustomization.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinProperty_ClassCustomization.cpp deleted file mode 100644 index 30f1f7756..000000000 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinProperty_ClassCustomization.cpp +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#include "DetailCustomizations/FlowDataPinProperty_ClassCustomization.h" -#include "DetailWidgetRow.h" -#include "Types/FlowDataPinProperties.h" -#include "EditorClassUtils.h" -#include "PropertyCustomizationHelpers.h" -#include "IDetailChildrenBuilder.h" - -void FFlowDataPinProperty_ClassCustomizationBase::CustomizeHeader(TSharedRef InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) -{ - StructPropertyHandle = InStructPropertyHandle; - - // Based on SoftClassPtr Customization - - const FString& MustImplementName = StructPropertyHandle->GetMetaData("MustImplement"); - const bool bAllowAbstract = StructPropertyHandle->HasMetaData("AllowAbstract"); - const bool bIsBlueprintBaseOnly = StructPropertyHandle->HasMetaData("IsBlueprintBaseOnly") || StructPropertyHandle->HasMetaData("BlueprintBaseOnly"); - const bool bAllowNone = !(StructPropertyHandle->GetMetaDataProperty()->PropertyFlags & CPF_NoClear); - const bool bShowTreeView = StructPropertyHandle->HasMetaData("ShowTreeView"); - const bool bHideViewOptions = StructPropertyHandle->HasMetaData("HideViewOptions"); - const bool bShowDisplayNames = StructPropertyHandle->HasMetaData("ShowDisplayNames"); - - CachedMetaClassPtr = DeriveBestClassFilter(); - - TrySetClassFilterFromMetaData(); - - const UClass* const RequiredInterface = FEditorClassUtils::GetClassFromString(MustImplementName); - - HeaderRow - .NameContent() - [ - InStructPropertyHandle->CreatePropertyNameWidget() - ] - .ValueContent() - .MinDesiredWidth(250.0f) - .MaxDesiredWidth(0.0f) - [ - // Add a class entry box. Even though this isn't an class entry, we will simulate one - SNew(SClassPropertyEntryBox) - .MetaClass(BuildMetaClass()) - .RequiredInterface(RequiredInterface) - .AllowAbstract(bAllowAbstract) - .IsBlueprintBaseOnly(bIsBlueprintBaseOnly) - .AllowNone(bAllowNone) - .ShowTreeView(bShowTreeView) - .HideViewOptions(bHideViewOptions) - .ShowDisplayNames(bShowDisplayNames) - .SelectedClass(this, &FFlowDataPinProperty_ClassCustomizationBase::OnGetClass) - .OnSetClass(this, &FFlowDataPinProperty_ClassCustomizationBase::OnSetClass) - ]; -} - -void FFlowDataPinProperty_ClassCustomizationBase::CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) -{ - if (TSharedPtr ClassFilterHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Class, ClassFilter))) - { - StructBuilder.AddProperty(ClassFilterHandle.ToSharedRef()); - - ClassFilterHandle->SetOnPropertyValueChanged(FSimpleDelegate::CreateSP(this, &FFlowDataPinProperty_ClassCustomizationBase::OnClassFilterChanged)); - } -} - -void FFlowDataPinProperty_ClassCustomizationBase::OnClassFilterChanged() -{ - // We don't allow changing away from the class filter specified in property metadata. - // So potentially undo the change (would be better to make it non-editable if the metadata was set, but I'm not sure how to do that) - TrySetClassFilterFromMetaData(); - - UClass* MetaClass = DeriveBestClassFilter(); - - TSharedPtr ClassValueHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Class, Value)); - UObject* ClassValueAsObject = nullptr; - ClassValueHandle->GetValue(ClassValueAsObject); - UClass* ClassValue = Cast(ClassValueAsObject); - - if (MetaClass && ClassValue && !ClassValue->IsChildOf(MetaClass)) - { - // Clear the class value if it is not compatible with the new ClassFilter value - const UClass* NullClassPtr = nullptr; - ClassValueHandle->SetValue(NullClassPtr, EPropertyValueSetFlags::DefaultFlags); - } - - CachedMetaClassPtr = MetaClass; - - IFlowExtendedPropertyTypeCustomization::OnAnyChildPropertyChanged(); -} - -UClass* FFlowDataPinProperty_ClassCustomizationBase::DeriveBestClassFilter() const -{ - const FProperty* StructProperty = StructPropertyHandle->GetProperty(); - - if (!StructProperty) - { - return nullptr; - } - - if (UClass* MetaClass = FFlowDataPinOutputProperty_Class::TryGetMetaClassFromProperty(*StructProperty)) - { - return MetaClass; - } - - // Allow the Instance to edit the ClassFilter to override the MetaClass - if (TSharedPtr ClassFilterHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Class, ClassFilter))) - { - UObject* ExistingMetaClass = nullptr; - ClassFilterHandle->GetValue(ExistingMetaClass); - - if (ExistingMetaClass) - { - return Cast(ExistingMetaClass); - } - } - - return nullptr; -} - -void FFlowDataPinProperty_ClassCustomizationBase::TrySetClassFilterFromMetaData() -{ - const FString& MetaClassName = StructPropertyHandle->GetMetaData("MetaClass"); - - if (MetaClassName.IsEmpty()) - { - return; - } - - UClass* MetaClass = FEditorClassUtils::GetClassFromString(MetaClassName); - if (!MetaClass) - { - return; - } - - // If the class filter was set in meta data, force that value to the ClassFilter property - if (TSharedPtr ClassFilterHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Class, ClassFilter))) - { - UObject* ExistingMetaClass = nullptr; - ClassFilterHandle->GetValue(ExistingMetaClass); - - if (ExistingMetaClass != MetaClass) - { - ClassFilterHandle->SetValue(MetaClass, EPropertyValueSetFlags::DefaultFlags); - } - } -} - -const UClass* FFlowDataPinProperty_ClassCustomizationBase::OnGetClass() const -{ - FString ClassName; - - if (TSharedPtr ClassValueHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Class, Value))) - { - ClassValueHandle->GetValueAsFormattedString(ClassName); - } - - // Do we have a valid cached class pointer? - const UClass* Class = CachedClassPtr.Get(); - if (!Class || Class->GetPathName() != ClassName) - { - Class = FEditorClassUtils::GetClassFromString(ClassName); - CachedClassPtr = MakeWeakObjectPtr(const_cast(Class)); - } - return Class; -} - -UClass* FFlowDataPinProperty_ClassCustomizationBase::BuildMetaClass() const -{ - UClass* MetaClass = CachedMetaClassPtr.Get(); - return MetaClass ? MetaClass : UObject::StaticClass(); -} - -void FFlowDataPinProperty_ClassCustomizationBase::OnSetClass(const UClass* NewClass) -{ - if (TSharedPtr ClassValueHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Class, Value))) - { - if (ClassValueHandle->SetValueFromFormattedString((NewClass) ? NewClass->GetPathName() : "None") == FPropertyAccess::Result::Success) - { - CachedClassPtr = MakeWeakObjectPtr(const_cast(NewClass)); - } - } -} diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinProperty_EnumCustomization.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinProperty_EnumCustomization.cpp deleted file mode 100644 index b1b5027c5..000000000 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinProperty_EnumCustomization.cpp +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#include "DetailCustomizations/FlowDataPinProperty_EnumCustomization.h" -#include "Types/FlowDataPinProperties.h" -#include "Nodes/FlowPin.h" - -#include "IDetailChildrenBuilder.h" -#include "UObject/UnrealType.h" - -void FFlowDataPinProperty_EnumCustomizationBase::CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) -{ - if (TSharedPtr EnumClassHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Enum, EnumClass))) - { - StructBuilder.AddProperty(EnumClassHandle.ToSharedRef()); - } - - if (TSharedPtr EnumNameHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Enum, EnumName))) - { - StructBuilder.AddProperty(EnumNameHandle.ToSharedRef()); - - EnumNameHandle->SetOnPropertyValueChanged(FSimpleDelegate::CreateSP(this, &FFlowDataPinProperty_EnumCustomizationBase::OnEnumNameChanged)); - } -} - -TSharedPtr FFlowDataPinProperty_EnumCustomizationBase::GetCuratedNamePropertyHandle() const -{ - check(StructPropertyHandle->IsValidHandle()); - - TSharedPtr FoundHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Enum, Value)); - check(FoundHandle); - - return FoundHandle; -} - -TArray FFlowDataPinProperty_EnumCustomizationBase::GetCuratedNameOptions() const -{ - TArray Results; - - const UEnum* Enum = GetEnumClass(); - - if (IsValid(Enum)) - { - Results = GetEnumValues(*Enum); - } - - return Results; -} - -TArray FFlowDataPinProperty_EnumCustomizationBase::GetEnumValues(const UEnum& Enum) -{ - TArray EnumValues; - - for (int Index = 0; Index < Enum.GetMaxEnumValue(); Index++) - { - if (!Enum.IsValidEnumValue(Index)) - { - continue; - } - - static const TCHAR* MetaDataKey_Hidden = TEXT("Hidden"); - if (!Enum.HasMetaData(MetaDataKey_Hidden, Index)) - { - EnumValues.Add(*Enum.GetDisplayNameTextByIndex(Index).ToString()); - } - } - - return EnumValues; -} - -void FFlowDataPinProperty_EnumCustomizationBase::SetCuratedName(const FName& NewValue) -{ - TSharedPtr ValueHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Enum, Value)); - - check(ValueHandle); - - ValueHandle->SetPerObjectValue(0, NewValue.ToString()); -} - -bool FFlowDataPinProperty_EnumCustomizationBase::TryGetCuratedName(FName& OutName) const -{ - if (const FFlowDataPinOutputProperty_Enum* ConfigurableEnumProperty = GetFlowDataPinEnumProperty()) - { - OutName = ConfigurableEnumProperty->Value; - - return true; - } - else - { - return false; - } -} - -void FFlowDataPinProperty_EnumCustomizationBase::OnEnumNameChanged() -{ - if (FFlowDataPinOutputProperty_Enum* FlowDataPinEnumProperty = GetFlowDataPinEnumProperty()) - { - FlowDataPinEnumProperty->OnEnumNameChanged(); - } -} - -const UEnum* FFlowDataPinProperty_EnumCustomizationBase::GetEnumClass() const -{ - if (const FFlowDataPinOutputProperty_Enum* FlowDataPinEnumProperty = GetFlowDataPinEnumProperty()) - { - return FlowDataPinEnumProperty->EnumClass; - } - - return nullptr; -} diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinProperty_ObjectCustomization.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinProperty_ObjectCustomization.cpp deleted file mode 100644 index a5f6a236a..000000000 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinProperty_ObjectCustomization.cpp +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#include "DetailCustomizations/FlowDataPinProperty_ObjectCustomization.h" -#include "DetailWidgetRow.h" -#include "Types/FlowDataPinProperties.h" -#include "EditorClassUtils.h" -#include "IDetailChildrenBuilder.h" - -void FFlowDataPinProperty_ObjectCustomizationBase::CustomizeHeader(TSharedRef InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) -{ - StructPropertyHandle = InStructPropertyHandle; - - CachedMetaClassPtr = DeriveBestClassFilter(); - - TrySetClassFilterFromMetaData(); - - // NOTE (gtaylor) Unfortunately, I wasn't able to get the customization filtering the object options using the ClassFilter - // (like FFlowDataPinProperty_ClassCustomizationBase does), because the object selection widget is less customizable (compared to the Class selection widget). - // Longer-term, this property customization could be improved to do this object filtering using the ClassFilter, - // but I don't have time to do that right now. - - HeaderRow - .NameContent() - [ - InStructPropertyHandle->CreatePropertyNameWidget() - ]; - - // This avoids making duplicate reset boxes - StructPropertyHandle->MarkResetToDefaultCustomized(); -} - -void FFlowDataPinProperty_ObjectCustomizationBase::CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) -{ - if (TSharedPtr ClassFilterHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Object, ClassFilter))) - { - StructBuilder.AddProperty(ClassFilterHandle.ToSharedRef()); - - ClassFilterHandle->SetOnPropertyValueChanged(FSimpleDelegate::CreateSP(this, &FFlowDataPinProperty_ObjectCustomizationBase::OnClassFilterChanged)); - } - - if (TSharedPtr ReferenceValueHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Object, ReferenceValue))) - { - StructBuilder.AddProperty(ReferenceValueHandle.ToSharedRef()); - - ReferenceValueHandle->SetOnPropertyValueChanged(FSimpleDelegate::CreateSP(this, &FFlowDataPinProperty_ObjectCustomizationBase::OnObjectValueChanged)); - } - - if (TSharedPtr InlineValueHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Object, InlineValue))) - { - StructBuilder.AddProperty(InlineValueHandle.ToSharedRef()); - - InlineValueHandle->SetOnPropertyValueChanged(FSimpleDelegate::CreateSP(this, &FFlowDataPinProperty_ObjectCustomizationBase::OnObjectValueChanged)); - } -} - -void FFlowDataPinProperty_ObjectCustomizationBase::OnClassFilterChanged() -{ - // We don't allow changing away from the Object filter specified in property metadata. - // So potentially undo the change (would be better to make it non-editable if the metadata was set, but I'm not sure how to do that) - TrySetClassFilterFromMetaData(); - - UClass* MetaClass = DeriveBestClassFilter(); - void* ObjectValuePropertyAsVoid = nullptr; - StructPropertyHandle->GetValueData(ObjectValuePropertyAsVoid); - FFlowDataPinOutputProperty_Object* ObjectValueProperty = static_cast(ObjectValuePropertyAsVoid); - - UObject* ObjectValue = ObjectValueProperty ? ObjectValueProperty->GetObjectValue() : nullptr; - - if (MetaClass && ObjectValue && !ObjectValue->IsA(MetaClass)) - { - TSharedPtr ReferenceObjectValueHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Object, ReferenceValue)); - TSharedPtr InlineObjectValueHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Object, InlineValue)); - - // Clear the Object value if it is not compatible with the new ClassFilter value - const UObject* NullObjectPtr = nullptr; - ReferenceObjectValueHandle->SetValue(NullObjectPtr, EPropertyValueSetFlags::DefaultFlags); - InlineObjectValueHandle->SetValue(NullObjectPtr, EPropertyValueSetFlags::DefaultFlags); - } - - CachedMetaClassPtr = MetaClass; - - IFlowExtendedPropertyTypeCustomization::OnAnyChildPropertyChanged(); -} - -void FFlowDataPinProperty_ObjectCustomizationBase::OnObjectValueChanged() -{ - OnClassFilterChanged(); -} - -UClass* FFlowDataPinProperty_ObjectCustomizationBase::DeriveBestClassFilter() const -{ - const FProperty* StructProperty = StructPropertyHandle->GetProperty(); - - if (!StructProperty) - { - return nullptr; - } - - if (UClass* MetaClass = FFlowDataPinOutputProperty_Class::TryGetMetaClassFromProperty(*StructProperty)) - { - return MetaClass; - } - - // Allow the Instance to edit the ClassFilter to override the MetaClass - if (TSharedPtr ClassFilterHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Object, ClassFilter))) - { - UObject* ExistingMetaClass = nullptr; - ClassFilterHandle->GetValue(ExistingMetaClass); - - if (ExistingMetaClass) - { - return Cast(ExistingMetaClass); - } - } - - return nullptr; -} - -void FFlowDataPinProperty_ObjectCustomizationBase::TrySetClassFilterFromMetaData() -{ - const FString& MetaClassName = StructPropertyHandle->GetMetaData("MetaClass"); - - if (MetaClassName.IsEmpty()) - { - return; - } - - UClass* MetaClass = FEditorClassUtils::GetClassFromString(MetaClassName); - if (!MetaClass) - { - return; - } - - // If the Object filter was set in meta data, force that value to the ClassFilter property - if (TSharedPtr ClassFilterHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinOutputProperty_Object, ClassFilter))) - { - UObject* ExistingMetaClass = nullptr; - ClassFilterHandle->GetValue(ExistingMetaClass); - - if (ExistingMetaClass != MetaClass) - { - ClassFilterHandle->SetValue(MetaClass, EPropertyValueSetFlags::DefaultFlags); - } - } -} - -UClass* FFlowDataPinProperty_ObjectCustomizationBase::BuildMetaClass() const -{ - return CachedMetaClassPtr.Get(); -} diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinValueCustomization.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinValueCustomization.cpp new file mode 100644 index 000000000..731a7394d --- /dev/null +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinValueCustomization.cpp @@ -0,0 +1,548 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "DetailCustomizations/FlowDataPinValueCustomization.h" +#include "Interfaces/FlowDataPinValueOwnerInterface.h" +#include "Types/FlowDataPinValuesStandard.h" +#include "UnrealExtensions/VisibilityArrayBuilder.h" + +#include "DetailLayoutBuilder.h" +#include "DetailWidgetRow.h" +#include "IDetailChildrenBuilder.h" +#include "IDetailPropertyRow.h" +#include "IPropertyUtilities.h" +#include "ScopedTransaction.h" +#include "Widgets/Input/SCheckBox.h" +#include "Widgets/Input/SComboBox.h" +#include "Widgets/Text/STextBlock.h" +#include "UObject/EnumProperty.h" + +#define LOCTEXT_NAMESPACE "FlowDataPinValueCustomization" + +static const TCHAR HiddenMeta[] = TEXT("Hidden"); + +FText FFlowDataPinValueCustomization::GetMultiTypeTooltip() +{ + return LOCTEXT("MultiTypeTooltip", + "Select whether this Data Pin holds a Single value or an Array of values.\n" + "Changing from Array to Single trims the array to the first element."); +} +FText FFlowDataPinValueCustomization::GetInputPinTooltip() +{ + return LOCTEXT("InputPinTooltip", + "Marks this Data Pin as an Input.\nChecked = Input Pin, Unchecked = Output Pin."); +} + +TSharedRef FFlowDataPinValueCustomization::MakeInstance() +{ + return MakeShareable(new FFlowDataPinValueCustomization()); +} + +void FFlowDataPinValueCustomization::CustomizeHeader(TSharedRef InStructPropertyHandle, + FDetailWidgetRow& HeaderRow, + IPropertyTypeCustomizationUtils& StructCustomizationUtils) +{ + Super::CustomizeHeader(InStructPropertyHandle, HeaderRow, StructCustomizationUtils); + + CacheHandles(InStructPropertyHandle, StructCustomizationUtils); + CacheOwnerInterface(); + CacheArraySupported(); + + // Populate MultiTypeOptions from enum (respect bArraySupported) + MultiTypeOptions.Reset(); + if (const UEnum* MultiTypeEnum = StaticEnum()) + { + const int32 NumEnums = FMath::Min(static_cast(FlowEnum::MaxOf()), MultiTypeEnum->NumEnums()); + for (int32 i = 0; i < NumEnums; ++i) + { + if (MultiTypeEnum->HasMetaData(HiddenMeta, i)) + { + continue; + } + const int64 Value = MultiTypeEnum->GetValueByIndex(i); + EFlowDataMultiType MT = static_cast(Value); + if (!bArraySupported && MT == EFlowDataMultiType::Array) + { + continue; + } + MultiTypeOptions.Add(MakeShareable(new int32(static_cast(Value)))); + } + } + + // If current mode is Array but unsupported, force Single (non-transactable) + if (!bArraySupported && MultiTypeHandle.IsValid()) + { + uint8 CurrentValue = 0; + if (MultiTypeHandle->GetValue(CurrentValue) == FPropertyAccess::Success && + static_cast(CurrentValue) == EFlowDataMultiType::Array) + { + MultiTypeHandle->SetValue(static_cast(EFlowDataMultiType::Single), + EPropertyValueSetFlags::NotTransactable); + } + + if (MultiTypeComboBox.IsValid()) + { + MultiTypeComboBox->SetEnabled(false); + } + } + + // Select current + const EFlowDataMultiType CurrentType = GetCurrentMultiType(); + for (auto& Opt : MultiTypeOptions) + { + if (Opt.IsValid() && static_cast(*Opt) == CurrentType) + { + SelectedMultiType = Opt; + break; + } + } + + TSharedRef HeaderBox = SNew(SHorizontalBox); + + // MultiType control (combo or static label if array unsupported) + if (bArraySupported) + { + HeaderBox->AddSlot() + .FillWidth(1.0f) + .VAlign(VAlign_Center) + [ + SAssignNew(MultiTypeComboBox, SComboBox>) + .OptionsSource(&MultiTypeOptions) + .OnGenerateWidget(this, &FFlowDataPinValueCustomization::GenerateMultiTypeWidget) + .OnSelectionChanged(this, &FFlowDataPinValueCustomization::OnMultiTypeChanged) + .IsEnabled(this, &FFlowDataPinValueCustomization::GetInputPinCheckboxEnabled) + .ToolTipText(GetMultiTypeTooltip()) + .Content() + [ + SNew(STextBlock) + .Text(this, &FFlowDataPinValueCustomization::GetSelectedMultiTypeText) + .Font(IDetailLayoutBuilder::GetDetailFont()) + ] + ]; + } + else + { + HeaderBox->AddSlot() + .FillWidth(1.0f) + .VAlign(VAlign_Center) + [ + SNew(STextBlock) + .Text(LOCTEXT("MultiTypeForcedSingle", "Single")) + .Font(IDetailLayoutBuilder::GetDetailFont()) + .ToolTipText(LOCTEXT("MultiTypeForcedSingleTooltip", "This pin type does not support Array mode.")) + ]; + } + + // Input Pin checkbox + HeaderBox->AddSlot() + .AutoWidth() + .VAlign(VAlign_Center) + .Padding(4.f, 0.f) + [ + SNew(SCheckBox) + .IsChecked(this, &FFlowDataPinValueCustomization::GetCurrentIsInputPin) + .OnCheckStateChanged(this, &FFlowDataPinValueCustomization::OnInputPinChanged) + .IsEnabled(this, &FFlowDataPinValueCustomization::GetInputPinCheckboxEnabled) + .Visibility(this, &FFlowDataPinValueCustomization::GetInputPinCheckboxVisibility) + .ToolTipText(GetInputPinTooltip()) + [ + SNew(STextBlock) + .Text(LOCTEXT("InputPin", "Input Pin")) + .Font(IDetailLayoutBuilder::GetDetailFont()) + ] + ]; + + HeaderRow + .NameContent() + [ + SNew(STextBlock) + .Text(StructPropertyHandle->GetPropertyDisplayName()) + .Font(IDetailLayoutBuilder::GetDetailFont()) + ] + .ValueContent() + .MinDesiredWidth(250.f) + [ + HeaderBox + ]; +} + +void FFlowDataPinValueCustomization::CustomizeChildren(TSharedRef InStructPropertyHandle, + IDetailChildrenBuilder& StructBuilder, + IPropertyTypeCustomizationUtils& StructCustomizationUtils) +{ + BuildValueRows(InStructPropertyHandle, StructBuilder, StructCustomizationUtils); +} + +void FFlowDataPinValueCustomization::BuildValueRows(TSharedRef InStructPropertyHandle, + IDetailChildrenBuilder& StructBuilder, + IPropertyTypeCustomizationUtils& StructCustomizationUtils) +{ + CacheHandles(InStructPropertyHandle, StructCustomizationUtils); + CacheArraySupported(); + + if (!ValuesHandle.IsValid()) + { + return; + } + + if (bArraySupported) + { + EnsureSingleElementExists(); + } + + BuildSingleBranch(StructBuilder); + if (bArraySupported) + { + BuildArrayBranch(StructBuilder); + } +} + +void FFlowDataPinValueCustomization::BuildSingleBranch(IDetailChildrenBuilder& StructBuilder) +{ + if (GetSingleModeVisibility() == EVisibility::Collapsed) + { + return; + } + + if (!ValuesHandle.IsValid()) + { + return; + } + + TSharedPtr ValueToShow = bArraySupported + ? ValuesHandle->GetChildHandle(0) + : ValuesHandle; + + if (!ValueToShow.IsValid()) + { + return; + } + + IDetailPropertyRow& Row = StructBuilder.AddProperty(ValueToShow.ToSharedRef()); + Row.ShouldAutoExpand(true); +} + +void FFlowDataPinValueCustomization::BuildArrayBranch(IDetailChildrenBuilder& StructBuilder) +{ + if (GetArrayModeVisibility() == EVisibility::Collapsed) + { + return; + } + + if (bArraySupported && ValuesHandle.IsValid() && ValuesHandle->AsArray()) + { + IDetailPropertyRow& Row = StructBuilder.AddProperty(ValuesHandle.ToSharedRef()); + Row.ShouldAutoExpand(true); + } +} + +void FFlowDataPinValueCustomization::RequestRefresh() +{ + if (PropertyUtilities.IsValid()) + { + PropertyUtilities->RequestRefresh(); + } +} + +void FFlowDataPinValueCustomization::EnsureSingleElementExists() +{ + if (!ValuesHandle.IsValid()) + { + return; + } + + if (bArraySupported) + { + if (auto AsArray = ValuesHandle->AsArray()) + { + uint32 Num = 0; + AsArray->GetNumElements(Num); + if (Num == 0) + { + AsArray->AddItem(); + } + } + } +} + +void FFlowDataPinValueCustomization::CacheHandles(const TSharedRef& PropertyHandle, + IPropertyTypeCustomizationUtils& StructCustomizationUtils) +{ + CustomizationUtils = &StructCustomizationUtils; + MultiTypeHandle = PropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinValue, MultiType)); + IsInputPinHandle = PropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinValue, bIsInputPin)); + PropertyUtilities = StructCustomizationUtils.GetPropertyUtilities(); + + if (auto* Value = GetFlowDataPinValueBeingEdited()) + { + DataPinType = Value->LookupPinType(); + if (DataPinType) + { + ValuesHandle = DataPinType->GetValuesHandle(PropertyHandle); + } + } +} + +void FFlowDataPinValueCustomization::CacheOwnerInterface() +{ + OwnerInterface = nullptr; + TArray Outers; + StructPropertyHandle->GetOuterObjects(Outers); + + if (Outers.Num() == 1) + { + OwnerInterface = Cast(Outers[0]); + } +} + +void FFlowDataPinValueCustomization::CacheArraySupported() +{ + bArraySupported = DataPinType ? DataPinType->SupportsMultiType(EFlowDataMultiType::Array) : true; +} + +void FFlowDataPinValueCustomization::OnMultiTypeChanged(TSharedPtr NewSelection, ESelectInfo::Type) +{ + if (!NewSelection.IsValid() || !MultiTypeHandle.IsValid()) + { + return; + } + + if (!bArraySupported) + { + return; + } + + const EFlowDataMultiType NewType = static_cast(*NewSelection); + + bool bNeedsTrim = (NewType == EFlowDataMultiType::Single); + if (bNeedsTrim && ValuesHandle.IsValid()) + { + if (auto AsArray = ValuesHandle->AsArray()) + { + uint32 NumElements = 0; + AsArray->GetNumElements(NumElements); + bNeedsTrim = NumElements > 1; + } + } + + FScopedTransaction Transaction(LOCTEXT("ChangePinMultiType", "Change Pin MultiType")); + MultiTypeHandle->NotifyPreChange(); + MultiTypeHandle->SetValue(static_cast(NewType)); + if (bNeedsTrim) + { + TrimArrayToSingle(); + } + MultiTypeHandle->NotifyPostChange(EPropertyChangeType::ValueSet); + + SelectedMultiType = NewSelection; + + // Preferred: trigger owner rebuild +#if WITH_EDITOR + if (OwnerInterface) + { + OwnerInterface->RequestFlowDataPinValuesDetailsRebuild(); + } + else + { + RequestRefresh(); + } +#endif +} + +void FFlowDataPinValueCustomization::OnInputPinChanged(ECheckBoxState NewState) +{ + if (!IsInputPinHandle.IsValid()) + { + return; + } + + bool Existing = false; + IsInputPinHandle->GetValue(Existing); + const bool bNewValue = NewState == ECheckBoxState::Checked; + + if (Existing == bNewValue) + { + return; + } + + FScopedTransaction Transaction(LOCTEXT("ChangeInputPin", "Change Input Pin")); + IsInputPinHandle->NotifyPreChange(); + IsInputPinHandle->SetValue(bNewValue); + IsInputPinHandle->NotifyPostChange(EPropertyChangeType::ValueSet); + + RequestRefresh(); +} + +void FFlowDataPinValueCustomization::TrimArrayToSingle() +{ + if (!ValuesHandle.IsValid()) + { + return; + } + + if (auto AsArray = ValuesHandle->AsArray()) + { + uint32 NumElements = 0; + AsArray->GetNumElements(NumElements); + + if (NumElements == 0) + { + AsArray->AddItem(); + } + else + { + while (NumElements > 1) + { + AsArray->DeleteItem(NumElements - 1); + AsArray->GetNumElements(NumElements); + } + } + + RequestRefresh(); + } +} + +EFlowDataMultiType FFlowDataPinValueCustomization::GetCurrentMultiType() const +{ + if (MultiTypeHandle.IsValid()) + { + uint8 Value = 0; + if (MultiTypeHandle->GetValue(Value) == FPropertyAccess::Success) + { + return static_cast(Value); + } + } + return EFlowDataMultiType::Single; +} + +ECheckBoxState FFlowDataPinValueCustomization::GetCurrentIsInputPin() const +{ + if (IsInputPinHandle.IsValid()) + { + bool Value = false; + IsInputPinHandle->GetValue(Value); + return Value ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; + } + return ECheckBoxState::Unchecked; +} + +EVisibility FFlowDataPinValueCustomization::GetSingleModeVisibility() const +{ + return GetCurrentMultiType() == EFlowDataMultiType::Single ? EVisibility::Visible : EVisibility::Collapsed; +} + +EVisibility FFlowDataPinValueCustomization::GetArrayModeVisibility() const +{ + if (!bArraySupported) + { + return EVisibility::Collapsed; + } + return GetCurrentMultiType() == EFlowDataMultiType::Array ? EVisibility::Visible : EVisibility::Collapsed; +} + +EVisibility FFlowDataPinValueCustomization::GetInputPinCheckboxVisibility() const +{ + return OwnerInterface && OwnerInterface->ShowFlowDataPinValueInputPinCheckbox() + ? EVisibility::Visible + : EVisibility::Collapsed; +} + +bool FFlowDataPinValueCustomization::GetInputPinCheckboxEnabled() const +{ + return OwnerInterface ? OwnerInterface->CanModifyFlowDataPinType() : true; +} + +TSharedRef FFlowDataPinValueCustomization::GenerateMultiTypeWidget(TSharedPtr Item) const +{ + const UEnum* MultiTypeEnum = StaticEnum(); + return SNew(STextBlock) + .Text(Item.IsValid() && MultiTypeEnum + ? MultiTypeEnum->GetDisplayNameTextByValue(*Item) + : FText::GetEmpty()) + .Font(IDetailLayoutBuilder::GetDetailFont()); +} + +FText FFlowDataPinValueCustomization::GetSelectedMultiTypeText() const +{ + const UEnum* MultiTypeEnum = StaticEnum(); + return (SelectedMultiType.IsValid() && MultiTypeEnum) + ? MultiTypeEnum->GetDisplayNameTextByValue(*SelectedMultiType) + : FText::GetEmpty(); +} + +void FFlowDataPinValueCustomization::BuildVisibilityAwareArray( + IDetailChildrenBuilder& StructBuilder, + TSharedPtr ArrayHandle, + TFunction, int32, IDetailChildrenBuilder&, const TAttribute&)> Generator, + TAttribute VisibilityAttribute) +{ + if (!ArrayHandle.IsValid() || !bArraySupported) + { + return; + } + + TSharedRef ArrayBuilder = + MakeShareable(new FVisibilityArrayBuilder(ArrayHandle.ToSharedRef(), true, true, true)); + + ArrayBuilder->SetVisibilityGetter([VisibilityAttribute]() + { + return VisibilityAttribute.Get(); + }); + + ArrayBuilder->OnGenerateArrayElementWidget( + FOnGenerateArrayElementWidgetVisible::CreateLambda( + [Generator](TSharedRef Elem, int32 Index, IDetailChildrenBuilder& Child, const TAttribute& RowVis) + { + Generator(Elem, Index, Child, RowVis); + })); + + StructBuilder.AddCustomBuilder(ArrayBuilder); +} + +void FFlowDataPinValueCustomization::ValidateArrayElements(TSharedPtr ArrayHandle, + TFunction)> IsValidPredicate, + TFunction)> InvalidateAction) +{ + if (!ArrayHandle.IsValid()) + { + return; + } + + auto AsArray = ArrayHandle->AsArray(); + if (!AsArray.IsValid()) + { + return; + } + + uint32 Num = 0; + AsArray->GetNumElements(Num); + + TArray> ToInvalidate; + ToInvalidate.Reserve(Num); + + for (uint32 i = 0; i < Num; ++i) + { + TSharedPtr Elem = ArrayHandle->GetChildHandle(i); + if (!Elem.IsValid()) + { + continue; + } + if (!IsValidPredicate(Elem)) + { + ToInvalidate.Add(Elem); + } + } + + if (ToInvalidate.Num() > 0) + { + const FScopedTransaction Tx(LOCTEXT("InvalidateArrayElements", "Clear Invalid Data Pin Values")); + for (auto& H : ToInvalidate) + { + if (H.IsValid()) + { + InvalidateAction(H); + } + } + } +} + +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinValueCustomization_Class.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinValueCustomization_Class.cpp new file mode 100644 index 000000000..0b68de1d2 --- /dev/null +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinValueCustomization_Class.cpp @@ -0,0 +1,386 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "DetailCustomizations/FlowDataPinValueCustomization_Class.h" +#include "Interfaces/FlowDataPinValueOwnerInterface.h" +#include "Types/FlowDataPinValuesStandard.h" +#include "UnrealExtensions/VisibilityArrayBuilder.h" + +#include "DetailLayoutBuilder.h" +#include "DetailWidgetRow.h" +#include "IDetailChildrenBuilder.h" +#include "PropertyHandle.h" +#include "EditorClassUtils.h" +#include "UObject/SoftObjectPath.h" +#include "IPropertyUtilities.h" +#include "ScopedTransaction.h" + +#define LOCTEXT_NAMESPACE "FlowDataPinValueCustomization_Class" + +FFlowDataPinValue_Class* FFlowDataPinValueCustomization_Class::GetValueStruct() const +{ + return IFlowExtendedPropertyTypeCustomization::TryGetTypedStructValue(StructPropertyHandle); +} + +bool FFlowDataPinValueCustomization_Class::ShouldShowSourceRow() const +{ + return OwnerInterface ? OwnerInterface->ShowFlowDataPinValueClassFilter(GetValueStruct()) : true; +} + +bool FFlowDataPinValueCustomization_Class::IsSourceEditable() const +{ + if (bHasMetaClass) + { + return false; // forced meta class: show disabled + } + return OwnerInterface ? OwnerInterface->CanEditFlowDataPinValueClassFilter(GetValueStruct()) : true; +} + +void FFlowDataPinValueCustomization_Class::BuildValueRows( + TSharedRef InStructPropertyHandle, + IDetailChildrenBuilder& StructBuilder, + IPropertyTypeCustomizationUtils& StructCustomizationUtils) +{ + CacheHandles(InStructPropertyHandle, StructCustomizationUtils); + CacheArraySupported(); // from base + if (!ValuesHandle.IsValid()) + { + return; + } + + ClassFilterHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinValue_Class, ClassFilter)); + + TrySetClassFilterFromMetaData(); + ExtractMetadata(); + RefreshEffectiveFilter(); + + const bool bShowSource = ShouldShowSourceRow(); + if (bShowSource) + { + BuildClassFilterRow(StructBuilder, IsSourceEditable()); + } + + EnsureSingleElementExists(); + BuildSingleBranch(StructBuilder); + if (bArraySupported) + { + BuildArrayBranch(StructBuilder); + } + + BindDelegates(); + ValidateAllElements(); +} + +void FFlowDataPinValueCustomization_Class::ExtractMetadata() +{ + if (!StructPropertyHandle.IsValid()) + { + return; + } + + const FString& MustImplement = StructPropertyHandle->GetMetaData(TEXT("MustImplement")); + RequiredInterface = FEditorClassUtils::GetClassFromString(MustImplement); + + bAllowAbstract = StructPropertyHandle->HasMetaData(TEXT("AllowAbstract")); + bIsBlueprintBaseOnly = StructPropertyHandle->HasMetaData(TEXT("IsBlueprintBaseOnly")) || + StructPropertyHandle->HasMetaData(TEXT("BlueprintBaseOnly")); + bShowTreeView = StructPropertyHandle->HasMetaData(TEXT("ShowTreeView")); + bHideViewOptions = StructPropertyHandle->HasMetaData(TEXT("HideViewOptions")); + bShowDisplayNames = StructPropertyHandle->HasMetaData(TEXT("ShowDisplayNames")); + + bHasMetaClass = !StructPropertyHandle->GetMetaData(TEXT("MetaClass")).IsEmpty(); + + if (const FProperty* MetaProp = StructPropertyHandle->GetMetaDataProperty()) + { + bAllowNone = !(MetaProp->PropertyFlags & CPF_NoClear); + } + else + { + bAllowNone = true; + } +} + +void FFlowDataPinValueCustomization_Class::BuildClassFilterRow(IDetailChildrenBuilder& StructBuilder, bool bSourceEditable) const +{ + if (!ClassFilterHandle.IsValid()) + { + return; + } + + IDetailPropertyRow& Row = StructBuilder.AddProperty(ClassFilterHandle.ToSharedRef()); + Row.DisplayName(LOCTEXT("ClassFilterLabel", "Class Filter")); + Row.IsEnabled(bSourceEditable); + Row.ToolTip(bHasMetaClass + ? LOCTEXT("ClassFilterMetaTooltip", "Class Filter is fixed by MetaClass metadata and cannot be edited.") + : LOCTEXT("ClassFilterTooltip", "Class Filter constrains which classes can be selected.")); +} + +void FFlowDataPinValueCustomization_Class::BuildSingleBranch(IDetailChildrenBuilder& StructBuilder) +{ + auto First = ValuesHandle->GetChildHandle(0); + if (!First.IsValid()) + { + return; + } + + StructBuilder.AddCustomRow(LOCTEXT("ClassSingleSearch", "Class")) + .Visibility(TAttribute::CreateSP(this, &FFlowDataPinValueCustomization_Class::GetSingleModeVisibility)) + .NameContent() + [ + SNew(STextBlock) + .Text(LOCTEXT("ClassValueLabel", "Class")) + .Font(IDetailLayoutBuilder::GetDetailFont()) + ] + .ValueContent() + .MinDesiredWidth(250.f) + [ + SNew(SClassPropertyEntryBox) + .MetaClass(CachedEffectiveFilter.Get() ? CachedEffectiveFilter.Get() : UObject::StaticClass()) + .RequiredInterface(RequiredInterface) + .AllowAbstract(bAllowAbstract) + .IsBlueprintBaseOnly(bIsBlueprintBaseOnly) + .AllowNone(bAllowNone) + .ShowTreeView(bShowTreeView) + .HideViewOptions(bHideViewOptions) + .ShowDisplayNames(bShowDisplayNames) + .IsEnabled(AreValuesEditable()) + .SelectedClass_Lambda([this, First]() -> const UClass* + { + return GetSelectedClassForHandle(First); + }) + .OnSetClass_Lambda([this, First](const UClass* NewClass) + { + OnSetClassForHandle(NewClass, First); + }) + ]; +} + +void FFlowDataPinValueCustomization_Class::BuildArrayBranch(IDetailChildrenBuilder& StructBuilder) +{ + BuildVisibilityAwareArray(StructBuilder, + ValuesHandle, + [this](TSharedRef ElementHandle, int32 Index, IDetailChildrenBuilder& ChildBuilder, const TAttribute& RowVis) + { + IDetailPropertyRow& Row = ChildBuilder.AddProperty(ElementHandle); + Row.Visibility(RowVis); + + Row.CustomWidget() + .NameContent() + [ + SNew(STextBlock) + .Text(FText::Format(LOCTEXT("ClassArrayElemLabelFmt", "Class {0}"), FText::AsNumber(Index))) + .Font(IDetailLayoutBuilder::GetDetailFont()) + ] + .ValueContent() + .MinDesiredWidth(250.f) + [ + SNew(SClassPropertyEntryBox) + .MetaClass(CachedEffectiveFilter.Get() ? CachedEffectiveFilter.Get() : UObject::StaticClass()) + .RequiredInterface(RequiredInterface) + .AllowAbstract(bAllowAbstract) + .IsBlueprintBaseOnly(bIsBlueprintBaseOnly) + .AllowNone(bAllowNone) + .ShowTreeView(bShowTreeView) + .HideViewOptions(bHideViewOptions) + .ShowDisplayNames(bShowDisplayNames) + .IsEnabled(AreValuesEditable()) + .SelectedClass_Lambda([this, ElementHandle]() -> const UClass* + { + return GetSelectedClassForHandle(ElementHandle); + }) + .OnSetClass_Lambda([this, ElementHandle](const UClass* NewClass) + { + OnSetClassForHandle(NewClass, ElementHandle); + }) + ]; + }, + TAttribute::CreateSP(this, &FFlowDataPinValueCustomization_Class::GetArrayModeVisibility)); +} + +void FFlowDataPinValueCustomization_Class::BindDelegates() +{ + if (ClassFilterHandle.IsValid()) + { + ClassFilterHandle->SetOnPropertyValueChanged( + FSimpleDelegate::CreateSP(this, &FFlowDataPinValueCustomization_Class::OnClassFilterChanged)); + } + if (ValuesHandle.IsValid()) + { + ValuesHandle->SetOnPropertyValueChanged( + FSimpleDelegate::CreateSP(this, &FFlowDataPinValueCustomization_Class::OnValuesChanged)); + } +} + +void FFlowDataPinValueCustomization_Class::OnClassFilterChanged() +{ + RefreshEffectiveFilter(); + ValidateAllElements(); + + if (CustomizationUtils) + { + if (auto Utils = CustomizationUtils->GetPropertyUtilities()) + { + Utils->RequestRefresh(); + } + } +} + +void FFlowDataPinValueCustomization_Class::OnValuesChanged() +{ + ValidateAllElements(); +} + +void FFlowDataPinValueCustomization_Class::TrySetClassFilterFromMetaData() const +{ + if (!StructPropertyHandle.IsValid() || !ClassFilterHandle.IsValid()) + { + return; + } + + const FString& MetaClassName = StructPropertyHandle->GetMetaData(TEXT("MetaClass")); + if (MetaClassName.IsEmpty()) + { + return; + } + + if (UClass* MetaClass = FEditorClassUtils::GetClassFromString(MetaClassName)) + { + UObject* Existing = nullptr; + ClassFilterHandle->GetValue(Existing); + if (Existing != MetaClass) + { + ClassFilterHandle->SetValue(MetaClass, EPropertyValueSetFlags::DefaultFlags); + } + } +} + +UClass* FFlowDataPinValueCustomization_Class::DeriveBestClassFilter() const +{ + if (!StructPropertyHandle.IsValid()) + { + return nullptr; + } + + const FString& MetaClassName = StructPropertyHandle->GetMetaData(TEXT("MetaClass")); + if (!MetaClassName.IsEmpty()) + { + if (UClass* MetaClass = FEditorClassUtils::GetClassFromString(MetaClassName)) + { + return MetaClass; + } + } + + if (ClassFilterHandle.IsValid()) + { + UObject* Raw = nullptr; + if (ClassFilterHandle->GetValue(Raw) == FPropertyAccess::Success && Raw) + { + return Cast(Raw); + } + } + + return nullptr; +} + +void FFlowDataPinValueCustomization_Class::RefreshEffectiveFilter() +{ + CachedEffectiveFilter = DeriveBestClassFilter(); +} + +void FFlowDataPinValueCustomization_Class::ValidateAllElements() +{ + ValidateArrayElements(ValuesHandle, + [this](TSharedPtr Elem) + { + return IsElementValid(Elem); + }, + [](TSharedPtr Elem) + { + if (Elem.IsValid()) + { + Elem->SetValueFromFormattedString(TEXT("None")); + } + }); +} + +bool FFlowDataPinValueCustomization_Class::IsElementValid(TSharedPtr ElementHandle) const +{ + if (!ElementHandle.IsValid()) + { + return true; + } + + UClass* FilterClass = CachedEffectiveFilter.Get(); + if (!FilterClass) + { + return true; + } + + FString Path; + if (!GetElementPathString(ElementHandle, Path) || IsNoneString(Path)) + { + return true; + } + + FSoftClassPath SCP(Path); + if (UClass* Loaded = SCP.TryLoadClass()) + { + return Loaded->IsChildOf(FilterClass); + } + return false; +} + +const UClass* FFlowDataPinValueCustomization_Class::GetSelectedClassForHandle(TSharedPtr ElementHandle) +{ + if (!ElementHandle.IsValid()) + { + return nullptr; + } + + FString Path; + if (ElementHandle->GetValueAsFormattedString(Path) != FPropertyAccess::Success || IsNoneString(Path)) + { + return nullptr; + } + return FEditorClassUtils::GetClassFromString(Path); +} + +void FFlowDataPinValueCustomization_Class::OnSetClassForHandle(const UClass* NewClass, TSharedPtr ElementHandle) const +{ + if (!ElementHandle.IsValid()) + { + return; + } + + const UClass* Filter = CachedEffectiveFilter.Get(); + if (Filter && NewClass && !NewClass->IsChildOf(Filter)) + { + NewClass = nullptr; + } + + FString Current; + ElementHandle->GetValueAsFormattedString(Current); + const FString NewValue = NewClass ? NewClass->GetPathName() : TEXT("None"); + if (Current == NewValue) + { + return; + } + + FScopedTransaction Tx(LOCTEXT("SetClassArrayElement", "Set Class Value")); + ElementHandle->SetValueFromFormattedString(NewValue); +} + +bool FFlowDataPinValueCustomization_Class::GetElementPathString(const TSharedPtr& ElementHandle, FString& OutPath) +{ + if (!ElementHandle.IsValid()) + { + return false; + } + return ElementHandle->GetValueAsFormattedString(OutPath) == FPropertyAccess::Success; +} + +bool FFlowDataPinValueCustomization_Class::IsNoneString(const FString& Str) +{ + return Str.IsEmpty() || Str.Equals(TEXT("None"), ESearchCase::IgnoreCase); +} + +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinValueCustomization_Enum.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinValueCustomization_Enum.cpp new file mode 100644 index 000000000..b63079521 --- /dev/null +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinValueCustomization_Enum.cpp @@ -0,0 +1,433 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "DetailCustomizations/FlowDataPinValueCustomization_Enum.h" +#include "Interfaces/FlowDataPinValueOwnerInterface.h" +#include "Types/FlowDataPinValuesStandard.h" + +#include "DetailLayoutBuilder.h" +#include "DetailWidgetRow.h" +#include "IDetailChildrenBuilder.h" +#include "IPropertyUtilities.h" +#include "PropertyHandle.h" +#include "ScopedTransaction.h" + +#include "Widgets/Input/SComboBox.h" +#include "Widgets/Text/STextBlock.h" + +#define LOCTEXT_NAMESPACE "FlowDataPinValueCustomization_Enum" + +FFlowDataPinValue_Enum* FFlowDataPinValueCustomization_Enum::GetEnumValueStruct() const +{ + return IFlowExtendedPropertyTypeCustomization::TryGetTypedStructValue(StructPropertyHandle); +} + +bool FFlowDataPinValueCustomization_Enum::ShouldShowSourceRow() const +{ + return OwnerInterface ? OwnerInterface->ShowFlowDataPinValueClassFilter(GetEnumValueStruct()) : true; +} + +bool FFlowDataPinValueCustomization_Enum::IsSourceEditable() const +{ + return OwnerInterface ? OwnerInterface->CanEditFlowDataPinValueClassFilter(GetEnumValueStruct()) : true; +} + +void FFlowDataPinValueCustomization_Enum::BuildValueRows( + TSharedRef InStructPropertyHandle, + IDetailChildrenBuilder& StructBuilder, + IPropertyTypeCustomizationUtils& StructCustomizationUtils) +{ + CacheHandles(InStructPropertyHandle, StructCustomizationUtils); + CacheArraySupported(); // base + CacheEnumHandles(InStructPropertyHandle); + + if (!bMultiTypeDelegateBound && MultiTypeHandle.IsValid()) + { + MultiTypeHandle->SetOnPropertyValueChanged( + FSimpleDelegate::CreateSP(this, &FFlowDataPinValueCustomization_Enum::OnMultiTypeChanged)); + bMultiTypeDelegateBound = true; + } + + const bool bShowSource = ShouldShowSourceRow(); + const bool bSourceEditable = IsSourceEditable(); + + if (bShowSource && EnumClassHandle.IsValid()) + { + IDetailPropertyRow& RowEnumClass = StructBuilder.AddProperty(EnumClassHandle.ToSharedRef()); + RowEnumClass.IsEnabled(bSourceEditable); + RowEnumClass.ToolTip(GetEnumSourceTooltip()); + EnumClassHandle->SetOnPropertyValueChanged( + FSimpleDelegate::CreateSP(this, &FFlowDataPinValueCustomization_Enum::OnEnumSourceChanged)); + } + + if (bShowSource && EnumNameHandle.IsValid()) + { + IDetailPropertyRow& RowEnumClassName = StructBuilder.AddProperty(EnumNameHandle.ToSharedRef()); + RowEnumClassName.IsEnabled(bSourceEditable); + RowEnumClassName.ToolTip(LOCTEXT("EnumNameTooltip", "Name of native C++ enum type to derive EnumClass.")); + EnumNameHandle->SetOnPropertyValueChanged( + FSimpleDelegate::CreateSP(this, &FFlowDataPinValueCustomization_Enum::OnEnumSourceChanged)); + } + + RebuildEnumData(); + EnsureSingleElementExists(); + BuildSingle(StructBuilder); + if (bArraySupported) + { + BuildArray(StructBuilder); + } +} + +void FFlowDataPinValueCustomization_Enum::CacheEnumHandles(const TSharedRef& StructHandle) +{ + EnumClassHandle = StructHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinValue_Enum, EnumClass)); + EnumNameHandle = StructHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFlowDataPinValue_Enum, EnumName)); +} + +void FFlowDataPinValueCustomization_Enum::OnEnumSourceChanged() +{ + RebuildEnumData(); + + if (CustomizationUtils) + { + if (auto Utils = CustomizationUtils->GetPropertyUtilities()) + { + Utils->RequestRefresh(); + } + } +} + +void FFlowDataPinValueCustomization_Enum::RebuildEnumData() +{ + EnumeratorOptions.Reset(); + bEnumResolved = false; + + if (FFlowDataPinValue_Enum* EnumStruct = GetEnumValueStruct()) + { + EnumStruct->OnEnumNameChanged(); + } + + if (UEnum* EnumObj = ResolveEnum()) + { + CollectEnumerators(*EnumObj); + bEnumResolved = EnumeratorOptions.Num() > 0; + } + + ValidateStoredValues(); +} + +UEnum* FFlowDataPinValueCustomization_Enum::ResolveEnum() const +{ + const FFlowDataPinValue_Enum* Data = GetEnumValueStruct(); + return Data ? Data->EnumClass.LoadSynchronous() : nullptr; +} + +void FFlowDataPinValueCustomization_Enum::CollectEnumerators(UEnum& EnumObj) +{ + const int32 Max = EnumObj.GetMaxEnumValue(); + static const TCHAR* HiddenKey = TEXT("Hidden"); + + for (int32 Index = 0; Index < Max; ++Index) + { + if (!EnumObj.IsValidEnumValue(Index)) + { + continue; + } + if (EnumObj.HasMetaData(HiddenKey, Index)) + { + continue; + } + + const FText Display = EnumObj.GetDisplayNameTextByIndex(Index); + EnumeratorOptions.Add(MakeShared(*Display.ToString())); + } +} + +void FFlowDataPinValueCustomization_Enum::ValidateStoredValues() +{ + if (!ValuesHandle.IsValid()) + { + return; + } + + TArray ValidNames; + for (auto& Opt : EnumeratorOptions) + { + if (Opt.IsValid()) + { + ValidNames.Add(*Opt); + } + } + + if (auto AsArray = ValuesHandle->AsArray()) + { + uint32 Count = 0; + AsArray->GetNumElements(Count); + + if (GetSingleModeVisibility() == EVisibility::Visible && Count == 0) + { + AsArray->AddItem(); + AsArray->GetNumElements(Count); + } + + for (uint32 i = 0; i < Count; ++i) + { + auto Elem = ValuesHandle->GetChildHandle(i); + if (!Elem.IsValid()) + { + continue; + } + + FName Current; + if (Elem->GetValue(Current) == FPropertyAccess::Success) + { + if (!IsValueValid(Current)) + { + Elem->SetValue(ValidNames.Num() > 0 ? ValidNames[0] : FName(NAME_None)); + } + } + } + } +} + +bool FFlowDataPinValueCustomization_Enum::IsValueValid(const FName& Candidate) const +{ + if (Candidate.IsNone()) + { + return EnumeratorOptions.Num() == 0; + } + for (auto& Opt : EnumeratorOptions) + { + if (Opt.IsValid() && *Opt == Candidate) + { + return true; + } + } + return false; +} + +TSharedPtr FFlowDataPinValueCustomization_Enum::FindEnumeratorMatch(const FName& Current) const +{ + for (auto& Opt : EnumeratorOptions) + { + if (Opt.IsValid() && *Opt == Current) + { + return Opt; + } + } + return nullptr; +} + +void FFlowDataPinValueCustomization_Enum::BuildSingle(IDetailChildrenBuilder& StructBuilder) +{ + if (!ValuesHandle.IsValid()) + { + return; + } + + auto First = ValuesHandle->GetChildHandle(0); + if (!First.IsValid()) + { + if (auto AsArray = ValuesHandle->AsArray()) + { + AsArray->AddItem(); + First = ValuesHandle->GetChildHandle(0); + } + } + + if (!First.IsValid()) + { + return; + } + + StructBuilder.AddCustomRow(LOCTEXT("EnumSingleSearch", "Value")) + .Visibility(TAttribute::CreateSP(this, &FFlowDataPinValueCustomization_Enum::GetSingleModeVisibility)) + .NameContent() + [ + SNew(STextBlock) + .Text(LOCTEXT("EnumValueLabel", "Value")) + .Font(IDetailLayoutBuilder::GetDetailFont()) + ] + .ValueContent() + .MinDesiredWidth(200.f) + [ + SNew(SComboBox>) + .OptionsSource(&EnumeratorOptions) + .OnGenerateWidget(this, &FFlowDataPinValueCustomization_Enum::GenerateEnumeratorWidget) + .OnSelectionChanged_Static(&FFlowDataPinValueCustomization_Enum::OnSingleValueChanged, First) + .IsEnabled(this, &FFlowDataPinValueCustomization_Enum::IsValueEditingEnabled) + .InitiallySelectedItem([this, First]() + { + FName Current; + if (First->GetValue(Current) == FPropertyAccess::Success) + { + return FindEnumeratorMatch(Current); + } + return EnumeratorOptions.Num() > 0 ? EnumeratorOptions[0] : nullptr; + }()) + .Content() + [ + SNew(STextBlock) + .Text_Lambda([this, First]() + { + FName Current; + if (First->GetValue(Current) == FPropertyAccess::Success && !Current.IsNone()) + { + return GetEnumeratorDisplayText(Current); + } + return LOCTEXT("EnumNonePlaceholder", ""); + }) + .Font(IDetailLayoutBuilder::GetDetailFont()) + .ToolTipText(GetEnumSourceTooltip()) + ] + ]; +} + +void FFlowDataPinValueCustomization_Enum::BuildArray(IDetailChildrenBuilder& StructBuilder) +{ + BuildVisibilityAwareArray(StructBuilder, + ValuesHandle, + [this](TSharedRef ElementHandle, int32 Index, IDetailChildrenBuilder& ChildBuilder, const TAttribute& RowVis) + { + IDetailPropertyRow& Row = ChildBuilder.AddProperty(ElementHandle); + Row.Visibility(RowVis); + + Row.CustomWidget() + .NameContent() + [ + SNew(STextBlock) + .Text(FText::AsNumber(Index)) + .Font(IDetailLayoutBuilder::GetDetailFont()) + ] + .ValueContent() + .MinDesiredWidth(200.f) + [ + SNew(SComboBox>) + .OptionsSource(&EnumeratorOptions) + .OnGenerateWidget(this, &FFlowDataPinValueCustomization_Enum::GenerateEnumeratorWidget) + .OnSelectionChanged_Static(&FFlowDataPinValueCustomization_Enum::OnArrayElementChanged, TSharedPtr(ElementHandle)) + .IsEnabled(this, &FFlowDataPinValueCustomization_Enum::IsValueEditingEnabled) + .InitiallySelectedItem([this, ElementHandle]() + { + FName Current; + if (ElementHandle->GetValue(Current) == FPropertyAccess::Success) + { + return FindEnumeratorMatch(Current); + } + return EnumeratorOptions.Num() > 0 ? EnumeratorOptions[0] : nullptr; + }()) + .Content() + [ + SNew(STextBlock) + .Text_Lambda([this, ElementHandle]() + { + FName Current; + if (ElementHandle->GetValue(Current) == FPropertyAccess::Success && !Current.IsNone()) + { + return GetEnumeratorDisplayText(Current); + } + return LOCTEXT("EnumNonePlaceholder", ""); + }) + .Font(IDetailLayoutBuilder::GetDetailFont()) + .ToolTipText(GetEnumSourceTooltip()) + ] + ]; + }, + TAttribute::CreateSP(this, &FFlowDataPinValueCustomization_Enum::GetArrayModeVisibility)); +} + +TSharedRef FFlowDataPinValueCustomization_Enum::GenerateEnumeratorWidget(TSharedPtr Item) const +{ + const FName Name = Item.IsValid() ? *Item : NAME_None; + return SNew(STextBlock) + .Text(GetEnumeratorDisplayText(Name)) + .Font(IDetailLayoutBuilder::GetDetailFont()); +} + +FText FFlowDataPinValueCustomization_Enum::GetEnumeratorDisplayText(const FName& Value) +{ + return Value.IsNone() ? LOCTEXT("EnumNoneDisplay", "") : FText::FromName(Value); +} + +FText FFlowDataPinValueCustomization_Enum::GetEnumSourceTooltip() const +{ + const FFlowDataPinValue_Enum* Data = GetEnumValueStruct(); + if (!Data) + { + return LOCTEXT("EnumTooltipMissing", "Enum value struct not available."); + } + + FString Source; + if (!Data->EnumName.IsEmpty()) + { + Source = FString::Printf(TEXT("Native Enum: %s"), *Data->EnumName); + } + if (Source.IsEmpty() && Data->EnumClass.IsValid()) + { + Source = FString::Printf(TEXT("Enum Asset: %s"), *Data->EnumClass.ToString()); + } + if (Source.IsEmpty()) + { + Source = TEXT("No enum source selected"); + } + return FText::FromString(Source); +} + +void FFlowDataPinValueCustomization_Enum::OnSingleValueChanged(TSharedPtr NewSelection, ESelectInfo::Type SelectInfo, TSharedPtr ElementHandle) +{ + if (!ElementHandle.IsValid() || !NewSelection.IsValid()) + { + return; + } + + FName Current; + ElementHandle->GetValue(Current); + if (Current == *NewSelection) + { + return; + } + + FScopedTransaction Tx(LOCTEXT("SetEnumSingleValue", "Set Enum Value")); + ElementHandle->SetValue(*NewSelection); +} + +void FFlowDataPinValueCustomization_Enum::OnArrayElementChanged(TSharedPtr NewSelection, ESelectInfo::Type SelectInfo,TSharedPtr ElementHandle) +{ + if (!ElementHandle.IsValid() || !NewSelection.IsValid()) + { + return; + } + + FName Current; + ElementHandle->GetValue(Current); + if (Current == *NewSelection) + { + return; + } + + FScopedTransaction Tx(LOCTEXT("SetEnumArrayElement", "Set Enum Array Element")); + ElementHandle->SetValue(*NewSelection); +} + +void FFlowDataPinValueCustomization_Enum::OnMultiTypeChanged() +{ + // If array not supported, ignore switching + if (!bArraySupported) + { + return; + } + + if (GetArrayModeVisibility() == EVisibility::Collapsed) + { + EnsureSingleElementExists(); + } + + if (CustomizationUtils) + { + if (auto Utils = CustomizationUtils->GetPropertyUtilities()) + { + Utils->RequestRefresh(); + } + } +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinValueCustomization_Object.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinValueCustomization_Object.cpp new file mode 100644 index 000000000..b4db1500f --- /dev/null +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowDataPinValueCustomization_Object.cpp @@ -0,0 +1,297 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "DetailCustomizations/FlowDataPinValueCustomization_Object.h" +#include "Interfaces/FlowDataPinValueOwnerInterface.h" +#include "Types/FlowDataPinValuesStandard.h" + +#include "DetailLayoutBuilder.h" +#include "DetailWidgetRow.h" +#include "IDetailChildrenBuilder.h" +#include "PropertyHandle.h" +#include "IPropertyUtilities.h" +#include "PropertyCustomizationHelpers.h" +#include "ScopedTransaction.h" +#include "EditorClassUtils.h" + +#define LOCTEXT_NAMESPACE "FlowDataPinValueCustomization_Object" + +FFlowDataPinValue_Object* FFlowDataPinValueCustomization_Object::GetValueStruct() const +{ + return IFlowExtendedPropertyTypeCustomization::TryGetTypedStructValue(StructPropertyHandle); +} + +bool FFlowDataPinValueCustomization_Object::ShouldShowSourceRow() const +{ + return OwnerInterface ? OwnerInterface->ShowFlowDataPinValueClassFilter(GetValueStruct()) : true; +} + +bool FFlowDataPinValueCustomization_Object::IsSourceEditable() const +{ + if (bMetaClassForced) + { + return false; + } + return OwnerInterface ? OwnerInterface->CanEditFlowDataPinValueClassFilter(GetValueStruct()) : true; +} + +void FFlowDataPinValueCustomization_Object::BuildValueRows( + TSharedRef InStructPropertyHandle, + IDetailChildrenBuilder& StructBuilder, + IPropertyTypeCustomizationUtils& StructCustomizationUtils) +{ + CacheHandles(InStructPropertyHandle, StructCustomizationUtils); + CacheArraySupported(); // base + if (!ValuesHandle.IsValid()) + { + return; + } + + ClassFilterHandle = StructPropertyHandle->GetChildHandle(TEXT("ClassFilter")); + + TryApplyMetaClass(); + ResolveEffectiveFilter(); + + const bool bShowSource = ShouldShowSourceRow(); + if (bShowSource) + { + BuildClassFilterRow(StructBuilder, IsSourceEditable()); + } + + EnsureSingleElementExists(); + BuildSingleBranch(StructBuilder); + if (bArraySupported) + { + BuildArrayBranch(StructBuilder); + } + + BindDelegates(); + ValidateAll(); +} + +void FFlowDataPinValueCustomization_Object::TryApplyMetaClass() +{ + if (!StructPropertyHandle.IsValid() || !ClassFilterHandle.IsValid()) + { + return; + } + + const FString& MetaClassName = StructPropertyHandle->GetMetaData(TEXT("MetaClass")); + if (MetaClassName.IsEmpty()) + { + bMetaClassForced = false; + return; + } + + if (UClass* Meta = FEditorClassUtils::GetClassFromString(MetaClassName)) + { + UObject* Existing = nullptr; + ClassFilterHandle->GetValue(Existing); + if (Existing != Meta) + { + ClassFilterHandle->SetValue(Meta, EPropertyValueSetFlags::DefaultFlags); + } + bMetaClassForced = true; + } + else + { + bMetaClassForced = false; + } +} + +void FFlowDataPinValueCustomization_Object::ResolveEffectiveFilter() +{ + if (bMetaClassForced) + { + const FString& MetaClassName = StructPropertyHandle->GetMetaData(TEXT("MetaClass")); + EffectiveFilterClass = FEditorClassUtils::GetClassFromString(MetaClassName); + return; + } + + if (ClassFilterHandle.IsValid()) + { + UObject* Obj = nullptr; + if (ClassFilterHandle->GetValue(Obj) == FPropertyAccess::Success) + { + EffectiveFilterClass = Cast(Obj); + return; + } + } + + EffectiveFilterClass = nullptr; +} + +void FFlowDataPinValueCustomization_Object::BuildClassFilterRow(IDetailChildrenBuilder& StructBuilder, bool bSourceEditable) const +{ + if (!ClassFilterHandle.IsValid()) + { + return; + } + + IDetailPropertyRow& Row = StructBuilder.AddProperty(ClassFilterHandle.ToSharedRef()); + Row.DisplayName(LOCTEXT("ObjClassFilter", "Class Filter")); + Row.IsEnabled(bSourceEditable); + Row.ToolTip(bMetaClassForced + ? LOCTEXT("ObjClassFilterMetaTooltip", "Class Filter is fixed by MetaClass metadata and cannot be edited.") + : LOCTEXT("ObjClassFilterTooltip", "Class Filter constrains which object classes are selectable.")); +} + +void FFlowDataPinValueCustomization_Object::BuildSingleBranch(IDetailChildrenBuilder& StructBuilder) +{ + auto First = ValuesHandle->GetChildHandle(0); + if (!First.IsValid()) + { + return; + } + + StructBuilder.AddCustomRow(LOCTEXT("ObjectSingleSearch", "Object")) + .Visibility(TAttribute::CreateSP(this, &FFlowDataPinValueCustomization_Object::GetSingleModeVisibility)) + .NameContent() + [ + SNew(STextBlock) + .Text(LOCTEXT("ObjectValueLabel", "Object")) + .Font(IDetailLayoutBuilder::GetDetailFont()) + ] + .ValueContent() + .MinDesiredWidth(250.f) + [ + BuildObjectValueWidgetForElement(First) + ]; +} + +void FFlowDataPinValueCustomization_Object::BuildArrayBranch(IDetailChildrenBuilder& StructBuilder) +{ + BuildVisibilityAwareArray(StructBuilder, + ValuesHandle, + [this](TSharedRef ElementHandle, int32 Index, IDetailChildrenBuilder& ChildBuilder, const TAttribute& RowVis) + { + IDetailPropertyRow& Row = ChildBuilder.AddProperty(ElementHandle); + Row.Visibility(RowVis); + + Row.CustomWidget() + .NameContent() + [ + SNew(STextBlock) + .Text(FText::Format(LOCTEXT("ObjectArrayElemFmt", "Object {0}"), FText::AsNumber(Index))) + .Font(IDetailLayoutBuilder::GetDetailFont()) + ] + .ValueContent() + .MinDesiredWidth(250.f) + [ + BuildObjectValueWidgetForElement(ElementHandle) + ]; + }, + TAttribute::CreateSP(this, &FFlowDataPinValueCustomization_Object::GetArrayModeVisibility)); +} + +TSharedRef FFlowDataPinValueCustomization_Object::BuildObjectValueWidgetForElement(TSharedPtr ElementHandle) const +{ + return SNew(SObjectPropertyEntryBox) + .PropertyHandle(ElementHandle) + .AllowedClass(EffectiveFilterClass.Get() ? EffectiveFilterClass.Get() : UObject::StaticClass()) + .AllowClear(AreValuesEditable()) + .IsEnabled(AreValuesEditable()) + .ToolTipText(AreValuesEditable() + ? LOCTEXT("ObjectPickerTooltip", "Select an object reference.") + : LOCTEXT("ObjectPickerLockedTooltip", "Object references are not editable.")); +} + +void FFlowDataPinValueCustomization_Object::BindDelegates() +{ + if (ClassFilterHandle.IsValid()) + { + ClassFilterHandle->SetOnPropertyValueChanged( + FSimpleDelegate::CreateSP(this, &FFlowDataPinValueCustomization_Object::OnClassFilterChanged)); + } + if (ValuesHandle.IsValid()) + { + ValuesHandle->SetOnPropertyValueChanged( + FSimpleDelegate::CreateSP(this, &FFlowDataPinValueCustomization_Object::OnValuesChanged)); + } +} + +void FFlowDataPinValueCustomization_Object::OnClassFilterChanged() +{ + ResolveEffectiveFilter(); + ValidateAll(); + + if (CustomizationUtils) + { + if (auto Utils = CustomizationUtils->GetPropertyUtilities()) + { + Utils->RequestRefresh(); + } + } +} + +void FFlowDataPinValueCustomization_Object::OnValuesChanged() +{ + ValidateAll(); +} + +void FFlowDataPinValueCustomization_Object::ValidateAll() +{ + ValidateArrayElements(ValuesHandle, + [this](TSharedPtr Elem) + { + return IsElementValid(Elem); + }, + [this](TSharedPtr Elem) + { + InvalidateElement(Elem); + }); +} + +bool FFlowDataPinValueCustomization_Object::IsElementValid(TSharedPtr ElementHandle) const +{ + if (!ElementHandle.IsValid()) + { + return true; + } + + UClass* Filter = EffectiveFilterClass.Get(); + if (!Filter) + { + return true; + } + + UObject* Obj = GetObjectValue(ElementHandle); + return !Obj || Obj->IsA(Filter); +} + +void FFlowDataPinValueCustomization_Object::InvalidateElement(TSharedPtr ElementHandle) +{ + if (ElementHandle.IsValid()) + { + SetObjectValue(ElementHandle, nullptr); + } +} + +UObject* FFlowDataPinValueCustomization_Object::GetObjectValue(TSharedPtr ElementHandle) +{ + UObject* Obj = nullptr; + if (ElementHandle.IsValid()) + { + ElementHandle->GetValue(Obj); + } + return Obj; +} + +void FFlowDataPinValueCustomization_Object::SetObjectValue(TSharedPtr ElementHandle, UObject* NewObj) +{ + if (!ElementHandle.IsValid()) + { + return; + } + + UObject* Current = nullptr; + ElementHandle->GetValue(Current); + if (Current == NewObj) + { + return; + } + + FScopedTransaction Tx(LOCTEXT("SetObjectValue", "Set Object Reference")); + ElementHandle->SetValue(NewObj); +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowNamedDataPinOutputPropertyCustomization.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowNamedDataPinPropertyCustomization.cpp similarity index 78% rename from Source/FlowEditor/Private/DetailCustomizations/FlowNamedDataPinOutputPropertyCustomization.cpp rename to Source/FlowEditor/Private/DetailCustomizations/FlowNamedDataPinPropertyCustomization.cpp index 194def98c..3db153a12 100644 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowNamedDataPinOutputPropertyCustomization.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowNamedDataPinPropertyCustomization.cpp @@ -1,6 +1,7 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#include "DetailCustomizations/FlowNamedDataPinOutputPropertyCustomization.h" +#include "DetailCustomizations/FlowNamedDataPinPropertyCustomization.h" +#include "Types/FlowNamedDataPinProperty.h" FText FFlowNamedDataPinPropertyCustomization::BuildHeaderText() const { diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowNodeAddOn_Details.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowNodeAddOn_Details.cpp index d57a94587..3ce051f45 100644 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowNodeAddOn_Details.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowNodeAddOn_Details.cpp @@ -1,6 +1,8 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "DetailCustomizations/FlowNodeAddOn_Details.h" +#include "AddOns/FlowNodeAddOn.h" + #include "DetailLayoutBuilder.h" void FFlowNodeAddOn_Details::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) @@ -11,4 +13,7 @@ void FFlowNodeAddOn_Details::CustomizeDetails(IDetailLayoutBuilder& DetailLayout DetailLayout.HideCategory(TEXT("FlowNode")); DetailLayout.HideCategory(TEXT("FlowNodeAddOn")); } + + // Call base template to set up rebuild delegate wiring + TFlowDataPinValueOwnerCustomization::CustomizeDetails(DetailLayout); } diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowNode_Details.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_Details.cpp index 8570fc0f1..41c09eb66 100644 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowNode_Details.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowNode_Details.cpp @@ -1,13 +1,18 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "DetailCustomizations/FlowNode_Details.h" +#include "Nodes/FlowNode.h" + #include "DetailLayoutBuilder.h" void FFlowNode_Details::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) { - // hide class properties while editing node instance placed in the graph - if (DetailLayout.HasClassDefaultObject() == false) + // Hide class-level category when editing an instance (not the CDO) + if (!DetailLayout.HasClassDefaultObject()) { DetailLayout.HideCategory(TEXT("FlowNode")); } + + // Call base template to set up rebuild delegate wiring + TFlowDataPinValueOwnerCustomization::CustomizeDetails(DetailLayout); } diff --git a/Source/FlowEditor/Private/FlowEditorModule.cpp b/Source/FlowEditor/Private/FlowEditorModule.cpp index 94582f2f7..282fab0d2 100644 --- a/Source/FlowEditor/Private/FlowEditorModule.cpp +++ b/Source/FlowEditor/Private/FlowEditorModule.cpp @@ -26,20 +26,21 @@ #include "DetailCustomizations/FlowNode_SubGraphDetails.h" #include "DetailCustomizations/FlowNodeAddOn_Details.h" #include "DetailCustomizations/FlowActorOwnerComponentRefCustomization.h" -#include "DetailCustomizations/FlowDataPinPropertyCustomizations.h" -#include "DetailCustomizations/FlowDataPinProperty_ClassCustomization.h" -#include "DetailCustomizations/FlowDataPinProperty_EnumCustomization.h" -#include "DetailCustomizations/FlowDataPinProperty_ObjectCustomization.h" #include "DetailCustomizations/FlowPinCustomization.h" -#include "DetailCustomizations/FlowNamedDataPinOutputPropertyCustomization.h" +#include "DetailCustomizations/FlowNamedDataPinPropertyCustomization.h" +#include "DetailCustomizations/FlowAssetParamsPtrCustomization.h" +#include "DetailCustomizations/FlowDataPinValueOwnerCustomizations.h" +#include "DetailCustomizations/FlowDataPinValueStandardCustomizations.h" #include "FlowAsset.h" #include "AddOns/FlowNodeAddOn.h" +#include "Asset/FlowAssetParamsTypes.h" #include "Nodes/Actor/FlowNode_ComponentObserver.h" #include "Nodes/Actor/FlowNode_PlayLevelSequence.h" #include "Nodes/Graph/FlowNode_CustomInput.h" #include "Nodes/Graph/FlowNode_CustomOutput.h" #include "Nodes/Graph/FlowNode_SubGraph.h" +#include "Types/FlowNamedDataPinProperty.h" #include "AssetToolsModule.h" #include "EdGraphUtilities.h" @@ -55,7 +56,7 @@ static FName AssetSearchModuleName = TEXT("AssetSearch"); #define LOCTEXT_NAMESPACE "FlowEditorModule" EAssetTypeCategories::Type FFlowEditorModule::FlowAssetCategory = static_cast(0); -FAssetCategoryPath FFLowAssetCategoryPaths::Flow(LOCTEXT("Flow", "Flow")); +FAssetCategoryPath FFlowAssetCategoryPaths::Flow(LOCTEXT("Flow", "Flow")); void FFlowEditorModule::StartupModule() { @@ -97,9 +98,6 @@ void FFlowEditorModule::StartupModule() RegisterAssetIndexers(); } ModulesChangedHandle = FModuleManager::Get().OnModulesChanged().AddRaw(this, &FFlowEditorModule::ModulesChangesCallback); - - // run one-time asserts that cannot be asserted statically - UFlowK2SchemaSubclassForAccess::AssertPinCategoryNames(); } void FFlowEditorModule::ShutdownModule() @@ -224,6 +222,9 @@ void FFlowEditorModule::RegisterDetailCustomizations() FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked("PropertyEditor"); RegisterCustomClassLayout(UFlowAsset::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowAssetDetails::MakeInstance)); + RegisterCustomClassLayout(UFlowAssetParams::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowAssetParamsCustomization::MakeInstance)); + RegisterCustomClassLayout(UFlowExecutableActorComponent::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowExecutableActorComponentCustomization::MakeInstance)); + RegisterCustomClassLayout(UFlowNodeBase::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNodeBaseCustomization::MakeInstance)); RegisterCustomClassLayout(UFlowNode::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_Details::MakeInstance)); RegisterCustomClassLayout(UFlowNodeAddOn::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNodeAddOn_Details::MakeInstance)); RegisterCustomClassLayout(UFlowNode_ComponentObserver::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFlowNode_ComponentObserverDetails::MakeInstance)); @@ -234,33 +235,24 @@ void FFlowEditorModule::RegisterDetailCustomizations() RegisterCustomStructLayout(*FFlowActorOwnerComponentRef::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowActorOwnerComponentRefCustomization::MakeInstance)); RegisterCustomStructLayout(*FFlowPin::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowPinCustomization::MakeInstance)); RegisterCustomStructLayout(*FFlowNamedDataPinProperty::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowNamedDataPinPropertyCustomization::MakeInstance)); - - RegisterCustomStructLayout(*FFlowDataPinOutputProperty_Bool::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinOutputProperty_BoolCustomization::MakeInstance)); - RegisterCustomStructLayout(*FFlowDataPinOutputProperty_Int64::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinOutputProperty_Int64Customization::MakeInstance)); - RegisterCustomStructLayout(*FFlowDataPinOutputProperty_Int32::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinOutputProperty_Int32Customization::MakeInstance)); - RegisterCustomStructLayout(*FFlowDataPinOutputProperty_Double::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinOutputProperty_DoubleCustomization::MakeInstance)); - RegisterCustomStructLayout(*FFlowDataPinOutputProperty_Float::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinOutputProperty_FloatCustomization::MakeInstance)); - RegisterCustomStructLayout(*FFlowDataPinOutputProperty_Name::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinOutputProperty_NameCustomization::MakeInstance)); - RegisterCustomStructLayout(*FFlowDataPinOutputProperty_String::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinOutputProperty_StringCustomization::MakeInstance)); - RegisterCustomStructLayout(*FFlowDataPinOutputProperty_Text::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinOutputProperty_TextCustomization::MakeInstance)); - RegisterCustomStructLayout(*FFlowDataPinOutputProperty_Enum::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinOutputProperty_EnumCustomization::MakeInstance)); - RegisterCustomStructLayout(*FFlowDataPinOutputProperty_Class::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinOutputProperty_ClassCustomization::MakeInstance)); - RegisterCustomStructLayout(*FFlowDataPinOutputProperty_Object::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinOutputProperty_ObjectCustomization::MakeInstance)); - - RegisterCustomStructLayout(*FFlowDataPinInputProperty_Bool::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinInputProperty_BoolCustomization::MakeInstance)); - RegisterCustomStructLayout(*FFlowDataPinInputProperty_Int64::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinInputProperty_Int64Customization::MakeInstance)); - RegisterCustomStructLayout(*FFlowDataPinInputProperty_Int32::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinInputProperty_Int32Customization::MakeInstance)); - RegisterCustomStructLayout(*FFlowDataPinInputProperty_Double::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinInputProperty_DoubleCustomization::MakeInstance)); - RegisterCustomStructLayout(*FFlowDataPinInputProperty_Float::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinInputProperty_FloatCustomization::MakeInstance)); - RegisterCustomStructLayout(*FFlowDataPinInputProperty_Name::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinInputProperty_NameCustomization::MakeInstance)); - RegisterCustomStructLayout(*FFlowDataPinInputProperty_String::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinInputProperty_StringCustomization::MakeInstance)); - RegisterCustomStructLayout(*FFlowDataPinInputProperty_Text::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinInputProperty_TextCustomization::MakeInstance)); - RegisterCustomStructLayout(*FFlowDataPinInputProperty_Enum::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinInputProperty_EnumCustomization::MakeInstance)); - RegisterCustomStructLayout(*FFlowDataPinInputProperty_Class::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinInputProperty_ClassCustomization::MakeInstance)); - RegisterCustomStructLayout(*FFlowDataPinInputProperty_Object::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinInputProperty_ObjectCustomization::MakeInstance)); - - // Consider implementing details customizations... for every EFlowPinType - FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); + RegisterCustomStructLayout(*FFlowAssetParamsPtr::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowAssetParamsPtrCustomization::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinValue_Bool::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinValueCustomization_Bool::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinValue_Int::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinValueCustomization_Int::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinValue_Int64::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinValueCustomization_Int64::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinValue_Float::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinValueCustomization_Float::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinValue_Double::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinValueCustomization_Double::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinValue_Name::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinValueCustomization_Name::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinValue_String::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinValueCustomization_String::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinValue_Text::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinValueCustomization_Text::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinValue_Enum::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinValueCustomization_Enum::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinValue_Vector::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinValueCustomization_Vector::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinValue_Rotator::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinValueCustomization_Rotator::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinValue_Transform::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinValueCustomization_Transform::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinValue_GameplayTag::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinValueCustomization_GameplayTag::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinValue_GameplayTagContainer::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinValueCustomization_GameplayTagContainer::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinValue_InstancedStruct::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinValueCustomization_InstancedStruct::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinValue_Class::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinValueCustomization_Class::MakeInstance)); + RegisterCustomStructLayout(*FFlowDataPinValue_Object::StaticStruct(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFlowDataPinValueCustomization_Object::MakeInstance)); PropertyModule.NotifyCustomizationModuleChanged(); } @@ -324,4 +316,4 @@ TSharedRef FFlowEditorModule::CreateFlowAssetEditor(const EToo #undef LOCTEXT_NAMESPACE -IMPLEMENT_MODULE(FFlowEditorModule, FlowEditor) +IMPLEMENT_MODULE(FFlowEditorModule, FlowEditor) \ No newline at end of file diff --git a/Source/FlowEditor/Private/Graph/FlowGraph.cpp b/Source/FlowEditor/Private/Graph/FlowGraph.cpp index 14bfa5881..e039011db 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraph.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraph.cpp @@ -176,6 +176,8 @@ void UFlowGraph::OnLoaded() bIsLoadingGraph = true; + UpdateVersion(); + // Setup all the Nodes in the graph for editing for (UEdGraphNode* Node : Nodes) { @@ -214,20 +216,52 @@ void UFlowGraph::Initialize() void UFlowGraph::UpdateVersion() { - if (GraphVersion == 1) + if (GraphVersion == CurrentGraphVersion) { return; } + const int32 PrevGraphVersion = GraphVersion; MarkVersion(); Modify(); // Insert any Version updating code here + + if (PrevGraphVersion < 2) + { + UpgradeAllFlowNodePins(); + } +} + +void UFlowGraph::UpgradeAllFlowNodePins() +{ + if (UFlowAsset* FlowAsset = GetFlowAsset()) + { + for (TPair>& Node : FlowAsset->Nodes) + { + UFlowNode* FlowNode = Node.Value; + if (IsValid(FlowNode)) + { + FlowNode->FixupDataPinTypes(); + + FlowAsset->TryUpdateManagedFlowPinsForNode(*FlowNode); + } + } + } + + for (UEdGraphNode* Node : Nodes) + { + if (UFlowGraphNode* FlowGraphNode = Cast(Node)) + { + FlowGraphNode->MarkNeedsFullReconstruction(); + FlowGraphNode->ReconstructNode(); + } + } } void UFlowGraph::MarkVersion() { - GraphVersion = 1; + GraphVersion = CurrentGraphVersion; } void UFlowGraph::UpdateClassData() diff --git a/Source/FlowEditor/Private/Graph/FlowGraphNodesPolicy.cpp b/Source/FlowEditor/Private/Graph/FlowGraphNodesPolicy.cpp new file mode 100644 index 000000000..45c038dff --- /dev/null +++ b/Source/FlowEditor/Private/Graph/FlowGraphNodesPolicy.cpp @@ -0,0 +1,54 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Graph/FlowGraphNodesPolicy.h" +#include "Nodes/FlowNodeBase.h" +#include "Graph/FlowGraphSettings.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraphNodesPolicy) + +#if WITH_EDITOR +bool FFlowGraphNodesPolicy::IsNodeAllowedByPolicy(const UFlowNodeBase* FlowNodeBase) const +{ + if (!IsValid(FlowNodeBase)) + { + return false; + } + + const FString NodeCategoryString = UFlowGraphSettings::GetNodeCategoryForNode(*FlowNodeBase); + + const bool bIsInAllowedCategory = !AllowedCategories.IsEmpty() && IsAnySubcategory(NodeCategoryString, AllowedCategories); + if (bIsInAllowedCategory) + { + return true; + } + + const bool bIsInDisallowedCategory = !DisallowedCategories.IsEmpty() && IsAnySubcategory(NodeCategoryString, DisallowedCategories); + if (bIsInDisallowedCategory) + { + return false; + } + + if (AllowedCategories.IsEmpty()) + { + // If the AllowedCategories is empty, then we consider any node that isn't disallowed, as allowed + return true; + } + else + { + return false; + } +} + +bool FFlowGraphNodesPolicy::IsAnySubcategory(const FString& CheckCategory, const TArray& Categories) +{ + for (const FString& Category : Categories) + { + if (CheckCategory.StartsWith(Category, ESearchCase::IgnoreCase)) + { + return true; + } + } + + return false; +} +#endif \ No newline at end of file diff --git a/Source/FlowEditor/Private/Graph/FlowGraphPinFactory.cpp b/Source/FlowEditor/Private/Graph/FlowGraphPinFactory.cpp index 83e891c1e..55b874378 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphPinFactory.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphPinFactory.cpp @@ -11,8 +11,6 @@ #include "NodeFactory.h" #include "SGraphPin.h" -#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowGraphPinFactory) - ////////////////////////////////////////////////////////////////////////// // FFlowGraphPinFactory @@ -79,26 +77,3 @@ int32 FFlowGraphPinFactory::GatherValidPinsCount(const TArray& Pins) return Count; } - -////////////////////////////////////////////////////////////////////////// -// UFlowK2SchemaSubclassForAccess - -void UFlowK2SchemaSubclassForAccess::AssertPinCategoryNames() -{ - // Assert that the FFlowPin aliases for the UEdGraphSchema_K2 PC_* FNames match exactly - // (since we will leverage some K2 functions that key off these names) - checkf(FFlowPin::PC_Exec == UEdGraphSchema_K2::PC_Exec, TEXT("Flow PC_* aliases should match Epic's K2 PC_* names exactly")); - checkf(FFlowPin::PC_Boolean == UEdGraphSchema_K2::PC_Boolean, TEXT("Flow PC_* aliases should match Epic's K2 PC_* names exactly")); - checkf(FFlowPin::PC_Byte == UEdGraphSchema_K2::PC_Byte, TEXT("Flow PC_* aliases should match Epic's K2 PC_* names exactly")); - checkf(FFlowPin::PC_Class == UEdGraphSchema_K2::PC_Class, TEXT("Flow PC_* aliases should match Epic's K2 PC_* names exactly")); - checkf(FFlowPin::PC_Int == UEdGraphSchema_K2::PC_Int, TEXT("Flow PC_* aliases should match Epic's K2 PC_* names exactly")); - checkf(FFlowPin::PC_Int64 == UEdGraphSchema_K2::PC_Int64, TEXT("Flow PC_* aliases should match Epic's K2 PC_* names exactly")); - checkf(FFlowPin::PC_Float == UEdGraphSchema_K2::PC_Float, TEXT("Flow PC_* aliases should match Epic's K2 PC_* names exactly")); - checkf(FFlowPin::PC_Double == UEdGraphSchema_K2::PC_Double, TEXT("Flow PC_* aliases should match Epic's K2 PC_* names exactly")); - checkf(FFlowPin::PC_Name == UEdGraphSchema_K2::PC_Name, TEXT("Flow PC_* aliases should match Epic's K2 PC_* names exactly")); - checkf(FFlowPin::PC_Object == UEdGraphSchema_K2::PC_Object, TEXT("Flow PC_* aliases should match Epic's K2 PC_* names exactly")); - checkf(FFlowPin::PC_String == UEdGraphSchema_K2::PC_String, TEXT("Flow PC_* aliases should match Epic's K2 PC_* names exactly")); - checkf(FFlowPin::PC_Text == UEdGraphSchema_K2::PC_Text, TEXT("Flow PC_* aliases should match Epic's K2 PC_* names exactly")); - checkf(FFlowPin::PC_Struct == UEdGraphSchema_K2::PC_Struct, TEXT("Flow PC_* aliases should match Epic's K2 PC_* names exactly")); - checkf(FFlowPin::PC_Enum == UEdGraphSchema_K2::PC_Enum, TEXT("Flow PC_* aliases should match Epic's K2 PC_* names exactly")); -} diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index 21004681a..68b6ce845 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -12,6 +12,7 @@ #include "FlowAsset.h" #include "FlowEditorLogChannels.h" +#include "FlowPinSubsystem.h" #include "FlowSettings.h" #include "AddOns/FlowNodeAddOn.h" #include "Nodes/FlowNode.h" @@ -20,6 +21,7 @@ #include "Nodes/Graph/FlowNode_CustomInput.h" #include "Nodes/Graph/FlowNode_Start.h" #include "Nodes/Route/FlowNode_Reroute.h" +#include "Types/FlowPinType.h" #include "AssetRegistry/AssetRegistryModule.h" #include "EdGraph/EdGraph.h" @@ -51,10 +53,6 @@ int32 UFlowGraphSchema::CurrentCacheRefreshID = 0; FFlowGraphSchemaRefresh UFlowGraphSchema::OnNodeListChanged; -const UScriptStruct* UFlowGraphSchema::VectorStruct = nullptr; -const UScriptStruct* UFlowGraphSchema::RotatorStruct = nullptr; -const UScriptStruct* UFlowGraphSchema::TransformStruct = nullptr; - namespace FlowGraphSchema::Private { // Adapted from UE::EdGraphSchemaK2::Private, because it's Private @@ -203,16 +201,21 @@ UFlowGraphSchema::UFlowGraphSchema(const FObjectInitializer& ObjectInitializer) GetDefault()->ForceVisualizationCacheClear(); }); } +} - // Initialize cached static references to well-known struct types - if (VectorStruct == nullptr) +void UFlowGraphSchema::EnsurePinTypesInitialized() +{ + if (PinTypeMatchPolicies.IsEmpty()) { - VectorStruct = TBaseStructure::Get(); - RotatorStruct = TBaseStructure::Get(); - TransformStruct = TBaseStructure::Get(); + InitializedPinTypes(); } } +void UFlowGraphSchema::InitializedPinTypes() +{ + PinTypeMatchPolicies = FFlowPinTypeNamesStandard::PinTypeMatchPolicies; +} + void UFlowGraphSchema::SubscribeToAssetChanges() { const FAssetRegistryModule& AssetRegistry = FModuleManager::LoadModuleChecked(AssetRegistryConstants::ModuleName); @@ -287,7 +290,7 @@ UFlowGraphNode* UFlowGraphSchema::CreateDefaultNode(UEdGraph& Graph, const TSubc return NewGraphNode; } -bool UFlowGraphSchema::ArePinsCompatible(const UEdGraphPin* PinA, const UEdGraphPin* PinB, const UClass* CallingContext, bool bIgnoreArray /*= false*/) const +bool UFlowGraphSchema::ArePinsCompatible(const UEdGraphPin* PinA, const UEdGraphPin* PinB, const UClass* CallingContext, bool bIgnoreArray) const { // Adapted from UEdGraphSchema_K2 if ((PinA->Direction == EGPD_Input) && (PinB->Direction == EGPD_Output)) @@ -304,126 +307,225 @@ bool UFlowGraphSchema::ArePinsCompatible(const UEdGraphPin* PinA, const UEdGraph } } -bool UFlowGraphSchema::ArePinCategoriesEffectivelyMatching(const FName& InputPinCategory, const FName& OutputPinCategory, bool bAllowImplicitCasts) +bool UFlowGraphSchema::ArePinTypesCompatible( + const FEdGraphPinType& OutputPinType, + const FEdGraphPinType& InputPinType, + const UClass* CallingContext, + bool bIgnoreArray) const { - if (InputPinCategory == OutputPinCategory) + FPinConnectionResponse ConnectionResponse; + + const bool bIsInputExecPin = FFlowPin::IsExecPinCategory(InputPinType.PinCategory); + const bool bIsOutputExecPin = FFlowPin::IsExecPinCategory(OutputPinType.PinCategory); + + if (bIsInputExecPin || bIsOutputExecPin) { - return true; + return (bIsInputExecPin && bIsOutputExecPin); } - if (!bAllowImplicitCasts) + UFlowGraphSchema* MutableThis = const_cast(this); + MutableThis->EnsurePinTypesInitialized(); + + const FFlowPinTypeMatchPolicy* FoundPinTypeMatchPolicy = PinTypeMatchPolicies.Find(InputPinType.PinCategory); + + // PinCategories must match exactly or be in the map of compatible PinCategories for the input pin type + if (!FoundPinTypeMatchPolicy) { + ConnectionResponse = + FPinConnectionResponse( + CONNECT_RESPONSE_DISALLOW, + FString::Printf( + TEXT("Could not find PinTypeMatchPolicy for %s"), + *InputPinType.PinCategory.ToString())); + return false; } - // Must handle pin connectivity for all added EFlowPinTypes - FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); + // PinCategories must match exactly or be in the map of compatible PinCategories for the input pin type + const bool bRequirePinCategoryMatch = EnumHasAnyFlags(FoundPinTypeMatchPolicy->PinTypeMatchRules, EFlowPinTypeMatchRules::RequirePinCategoryMatch); + if (bRequirePinCategoryMatch && + OutputPinType.PinCategory != InputPinType.PinCategory && + !FoundPinTypeMatchPolicy->PinCategories.Contains(OutputPinType.PinCategory)) + { + ConnectionResponse = + FPinConnectionResponse( + CONNECT_RESPONSE_DISALLOW, + FString::Printf( + TEXT("Pin type mismatch %s != %s"), + *OutputPinType.PinCategory.ToString(), + *InputPinType.PinCategory.ToString())); - // We could extend the compatibility here to accept more implicit conversions (eg, null objects convertible to bools) - // but we'd need to also add support the conversion in the Supply/Resolve side as well. + return false; + } - if (FFlowPin::IsBoolPinCategory(InputPinCategory) && FFlowPin::IsConvertableToBoolPinCategory(OutputPinCategory)) + // RequirePinCategoryMemberReference + const bool bRequirePinCategoryMemberReferenceMatch = EnumHasAnyFlags(FoundPinTypeMatchPolicy->PinTypeMatchRules, EFlowPinTypeMatchRules::RequirePinCategoryMemberReferenceMatch); + if (bRequirePinCategoryMemberReferenceMatch && + OutputPinType.PinSubCategoryMemberReference != InputPinType.PinSubCategoryMemberReference) { - return true; + ConnectionResponse = + FPinConnectionResponse( + CONNECT_RESPONSE_DISALLOW, + FString::Printf( + TEXT("Pin category member reference mismatch %s != %s"), + *OutputPinType.PinSubCategoryMemberReference.MemberName.ToString(), + *InputPinType.PinSubCategoryMemberReference.MemberName.ToString())); + + return false; } - if (FFlowPin::IsIntPinCategory(InputPinCategory) && FFlowPin::IsConvertableToIntPinCategory(OutputPinCategory)) - { - return true; + // TODO (gtaylor) We will want to revisit how we want to use the "PinSubCategory" field of FEdGraphPinType to best effect. + // So far, we don't have any use for it in the standard flow types, but if we can dream up a good way to use it + // in supporting other projects, we can add support for it here. +#if 0 + // PinSubCategories must match exactly or be in the map of compatible PinSubCategories for the input pin type + const bool bRequirePinSubCategoryMatch = EnumHasAnyFlags(FoundPinTypeMatchPolicy->PinTypeMatchRules, EFlowPinTypeMatchRules::RequirePinSubCategoryMatch); + if (bRequirePinSubCategoryMatch && + OutputPinType.PinSubCategory != InputPinType.PinSubCategory && + !FoundPinTypeMatchPolicy->PinSubCategories.Contains(OutputPinType.PinSubCategory)) + { + ConnectionResponse = + FPinConnectionResponse( + CONNECT_RESPONSE_DISALLOW, + FString::Printf( + TEXT("Pin sub-category mismatch %s != %s"), + *OutputPinType.PinSubCategory.ToString(), + *InputPinType.PinSubCategory.ToString())); + + return false; } +#endif - if (FFlowPin::IsFloatPinCategory(InputPinCategory) && FFlowPin::IsConvertableToFloatPinCategory(OutputPinCategory)) + // Container type (Single/Array, etc.) + const bool bRequireContainerTypeMatch = EnumHasAnyFlags(FoundPinTypeMatchPolicy->PinTypeMatchRules, EFlowPinTypeMatchRules::RequireContainerTypeMatch); + if (bRequireContainerTypeMatch && OutputPinType.ContainerType != InputPinType.ContainerType) { - return true; + const bool bIsAnyArray = + OutputPinType.ContainerType == EPinContainerType::Array || + InputPinType.ContainerType == EPinContainerType::Array; + + if (!bIgnoreArray || !bIsAnyArray) + { + ConnectionResponse = + FPinConnectionResponse( + CONNECT_RESPONSE_DISALLOW, + FString::Printf( + TEXT("Mismatched container type %s != %s"), + *UEnum::GetDisplayValueAsText(OutputPinType.ContainerType).ToString(), + *UEnum::GetDisplayValueAsText(InputPinType.ContainerType).ToString())); + + return false; + } } - if (FFlowPin::IsEnumPinCategory(InputPinCategory) && FFlowPin::IsConvertableToEnumPinCategory(OutputPinCategory)) + const bool bRequirePinSubCategoryObjectMatch = EnumHasAnyFlags(FoundPinTypeMatchPolicy->PinTypeMatchRules, EFlowPinTypeMatchRules::RequirePinSubCategoryObjectMatch); + if (bRequirePinSubCategoryObjectMatch) { - return true; + const UStruct* OutputStruct = Cast(OutputPinType.PinSubCategoryObject.Get()); + const UStruct* InputStruct = Cast(InputPinType.PinSubCategoryObject.Get()); + + if (!ArePinSubCategoryObjectsCompatible(OutputStruct, InputStruct, *FoundPinTypeMatchPolicy, ConnectionResponse)) + { + return false; + } } - if (FFlowPin::IsTextPinCategory(InputPinCategory) && FFlowPin::IsConvertableToTextPinCategory(OutputPinCategory)) + return true; +} + +bool UFlowGraphSchema::ArePinSubCategoryObjectsCompatible( + const UStruct* OutputStruct, + const UStruct* InputStruct, + const FFlowPinTypeMatchPolicy& PinTypeMatchPolicy, + FPinConnectionResponse& OutConnectionResponse) const +{ + if (!IsValid(InputStruct)) { + // Assume "InputStruct's SubCategoryObject == null", means any SubCategoryObject is acceptable return true; } - if (FFlowPin::IsObjectPinCategory(InputPinCategory) && FFlowPin::IsConvertableToObjectPinCategory(OutputPinCategory)) + if (!IsValid(OutputStruct)) { + // null objects are the norm for many PinCategories, so long as they match return true; } - if (FFlowPin::IsClassPinCategory(InputPinCategory) && FFlowPin::IsConvertableToClassPinCategory(OutputPinCategory)) + // Exact match + if (OutputStruct == InputStruct) { return true; } - if (FFlowPin::IsStructPinCategory(InputPinCategory) && FFlowPin::IsConvertableToStructPinCategory(OutputPinCategory)) + using namespace FlowGraphSchema::Private; + + // Only allow a match if the input is a superclass of the output + const bool bAllowSubCategoryObjectSubclasses = EnumHasAnyFlags(PinTypeMatchPolicy.PinTypeMatchRules, EFlowPinTypeMatchRules::AllowSubCategoryObjectSubclasses); + if (bAllowSubCategoryObjectSubclasses && IsAuthoritativeChildOf(OutputStruct, InputStruct)) { return true; } - return false; -} + UClass const* OutputClass = Cast(OutputStruct); + UClass const* InputClass = Cast(InputStruct); -bool UFlowGraphSchema::ArePinTypesCompatible(const FEdGraphPinType& Output, const FEdGraphPinType& Input, const UClass* CallingContext, bool bIgnoreArray /*= false*/) const -{ - // NOTE - Adapted from UEdGraphSchema_K2::ArePinTypesCompatible() - - using namespace FlowGraphSchema::Private; - using namespace UE::Kismet::BlueprintTypeConversions; - - if (ArePinCategoriesEffectivelyMatching(Input.PinCategory, Output.PinCategory)) + // Class specifics + if (IsValid(InputClass) && IsValid(OutputClass)) { - const UScriptStruct* OutputStruct = Cast(Output.PinSubCategoryObject.Get()); - const UScriptStruct* InputStruct = Cast(Input.PinSubCategoryObject.Get()); - if (OutputStruct != InputStruct) + // Only allow a match if the input is a superclass of the output + if (bAllowSubCategoryObjectSubclasses && ExtendedIsChildOf(OutputClass, InputClass)) { - const bool bAreConvertibleStructs = - FStructConversionTable::Get().GetConversionFunction(OutputStruct, InputStruct).IsSet(); - - if (bAreConvertibleStructs) - { - return true; - } + return true; } - if ((Output.PinSubCategory == Input.PinSubCategory) - && (Output.PinSubCategoryObject == Input.PinSubCategoryObject) - && (Output.PinSubCategoryMemberReference == Input.PinSubCategoryMemberReference)) + OutConnectionResponse = + FPinConnectionResponse( + CONNECT_RESPONSE_DISALLOW, + FString::Printf( + TEXT("Output %s must be subclass of input %s"), + *OutputClass->GetName(), + *InputClass->GetName())); + + return false; + } + + if (!IsValid(InputClass) && !IsValid(OutputClass)) + { + const bool bAllowSubCategoryObjectSameLayout = EnumHasAnyFlags(PinTypeMatchPolicy.PinTypeMatchRules, EFlowPinTypeMatchRules::AllowSubCategoryObjectSameLayout); + const bool bSameLayoutMustMatchPropertyNames = EnumHasAnyFlags(PinTypeMatchPolicy.PinTypeMatchRules, EFlowPinTypeMatchRules::SameLayoutMustMatchPropertyNames); + + // Allow structs with the same layout + if (bAllowSubCategoryObjectSameLayout + && FStructUtils::TheSameLayout(OutputStruct, InputStruct, bSameLayoutMustMatchPropertyNames)) { - // If the sub-category also matches exactly, then the pins are compatible return true; } - - if ((Output.PinCategory == FFlowPin::PC_Object) || (Output.PinCategory == FFlowPin::PC_Struct) || (Output.PinCategory == FFlowPin::PC_Class)) - { - // Subcategory mismatch, but the two could be castable - // Only allow a match if the input is a superclass of the output - - UStruct const* OutputObject = (Output.PinSubCategory == UEdGraphSchema_K2::PSC_Self) ? CallingContext : Cast(Output.PinSubCategoryObject.Get()); - UStruct const* InputObject = (Input.PinSubCategory == UEdGraphSchema_K2::PSC_Self) ? CallingContext : Cast(Input.PinSubCategoryObject.Get()); - if (OutputObject && InputObject) - { - if (Output.PinCategory == FFlowPin::PC_Struct) - { - return OutputObject->IsChildOf(InputObject) && FStructUtils::TheSameLayout(OutputObject, InputObject); - } + using namespace UE::Kismet::BlueprintTypeConversions; - UClass const* OutputClass = Cast(OutputObject); - UClass const* InputClass = Cast(InputObject); + // Allow convertable ScriptStructs + const UScriptStruct* InputScriptStruct = Cast(InputStruct); + const UScriptStruct* OutputScriptStruct = Cast(OutputStruct); + if (IsValid(InputScriptStruct) && IsValid(OutputScriptStruct)) + { + const bool bAreConvertibleStructs = + FStructConversionTable::Get().GetConversionFunction(OutputScriptStruct, InputScriptStruct).IsSet(); - return - (IsAuthoritativeChildOf(OutputObject, InputObject) || - (OutputClass && InputClass && ExtendedIsChildOf(OutputClass, InputClass))); + if (bAreConvertibleStructs) + { + return true; } - - return false; } - - return false; } + OutConnectionResponse = + FPinConnectionResponse( + CONNECT_RESPONSE_DISALLOW, + FString::Printf( + TEXT("Output %s is not compatible with input %s"), + *OutputStruct->GetName(), + *InputStruct->GetName())); + return false; } @@ -516,14 +618,16 @@ const FPinConnectionResponse UFlowGraphSchema::DetermineConnectionResponseOfComp checkf(!PinB->LinkedTo.Contains(PinA), TEXT("This should be caught with the bIsExistingConnection test above")); // Break existing connections on outputs for Exec Pins - if (FFlowPin::IsExecPinCategory(InputPin->PinType.PinCategory) && OutputPin->LinkedTo.Num() > 0) + const bool bIsExecPin = FFlowPin::IsExecPinCategory(InputPin->PinType.PinCategory); + if (bIsExecPin && OutputPin->LinkedTo.Num() > 0) { const ECanCreateConnectionResponse ReplyBreakInputs = (OutputPin == PinA ? CONNECT_RESPONSE_BREAK_OTHERS_A : CONNECT_RESPONSE_BREAK_OTHERS_B); return FPinConnectionResponse(ReplyBreakInputs, TEXT("Replace existing exec connection")); } // Break existing connections on inputs for Data Pins - if (FFlowPin::IsDataPinCategory(InputPin->PinType.PinCategory) && InputPin->LinkedTo.Num() > 0) + const bool bIsDataPin = !bIsExecPin; + if (bIsDataPin && InputPin->LinkedTo.Num() > 0) { const ECanCreateConnectionResponse ReplyBreakInputs = (InputPin == PinA ? CONNECT_RESPONSE_BREAK_OTHERS_A : CONNECT_RESPONSE_BREAK_OTHERS_B); return FPinConnectionResponse(ReplyBreakInputs, TEXT("Replace existing data connection")); @@ -597,77 +701,12 @@ bool UFlowGraphSchema::ShouldHidePinDefaultValue(UEdGraphPin* Pin) const FLinearColor UFlowGraphSchema::GetPinTypeColor(const FEdGraphPinType& PinType) const { - // NOTE - Adapted from UEdGraphSchema_K2::GetPinTypeColor() - // (because we cannot directly inherit from it, but want the same color language) - - const FName& PinCategory = PinType.PinCategory; - const UGraphEditorSettings* Settings = GetDefault(); - - if (FFlowPin::IsExecPinCategory(PinCategory)) - { - return Settings->ExecutionPinTypeColor; - } - else if (PinCategory == FFlowPin::PC_Object) - { - return Settings->ObjectPinTypeColor; - } - else if (PinCategory == FFlowPin::PC_Boolean) - { - return Settings->BooleanPinTypeColor; - } - else if (PinCategory == FFlowPin::PC_Byte) - { - return Settings->BytePinTypeColor; - } - else if (PinCategory == FFlowPin::PC_Int) - { - return Settings->IntPinTypeColor; - } - else if (PinCategory == FFlowPin::PC_Int64) - { - return Settings->Int64PinTypeColor; - } - else if (PinCategory == FFlowPin::PC_Struct) - { - if (PinType.PinSubCategoryObject == VectorStruct) - { - // vector - return Settings->VectorPinTypeColor; - } - else if (PinType.PinSubCategoryObject == RotatorStruct) - { - // rotator - return Settings->RotatorPinTypeColor; - } - else if (PinType.PinSubCategoryObject == TransformStruct) - { - // transform - return Settings->TransformPinTypeColor; - } - else - { - return Settings->StructPinTypeColor; - } - } - else if (PinCategory == FFlowPin::PC_String) - { - return Settings->StringPinTypeColor; - } - else if (PinCategory == FFlowPin::PC_Text) + if (const FFlowPinType* FlowPinType = LookupDataPinTypeForPinCategory(PinType.PinCategory)) { - return Settings->TextPinTypeColor; - } - else if (PinCategory == FFlowPin::PC_Name) - { - return Settings->NamePinTypeColor; - } - else if (PinCategory == FFlowPin::PC_Class) - { - return Settings->ClassPinTypeColor; + return FlowPinType->GetPinColor(); } - // Type does not have a defined color! - return Settings->DefaultPinTypeColor; + return FLinearColor(1.0f, 1.0f, 1.0f, 1.0f); } FText UFlowGraphSchema::GetPinDisplayName(const UEdGraphPin* Pin) const @@ -753,6 +792,22 @@ bool UFlowGraphSchema::CanShowDataTooltipForPin(const UEdGraphPin& Pin) const return !FFlowPin::IsExecPinCategory(Pin.PinType.PinCategory); } +const FFlowPinType* UFlowGraphSchema::LookupDataPinTypeForPinCategory(const FName& PinCategory) +{ + UFlowPinSubsystem* PinSubsystem = UFlowPinSubsystem::Get(); + if (!PinSubsystem) + { + UE_LOG(LogFlowEditor, Error, TEXT("Could not find the FlowPinSubsystem!")); + + return nullptr; + } + + // Flow uses the PinTypeName as the PinCategory for UEdGraphPin purposes + const FFlowPinTypeName PinTypeName(PinCategory); + const FFlowPinType* PinType = PinSubsystem->FindPinType(PinTypeName); + return PinType; +} + bool UFlowGraphSchema::IsTitleBarPin(const UEdGraphPin& Pin) const { return FFlowPin::IsExecPinCategory(Pin.PinType.PinCategory); @@ -793,15 +848,17 @@ void UFlowGraphSchema::BreakPinLinks(UEdGraphPin& TargetPin, bool bSendsNodeNoti for (UEdGraphPin* OtherPin : CachedLinkedTo) { UFlowGraphNode* OtherOwningFlowGraphNode = Cast(OtherPin->GetOwningNodeUnchecked()); - - if (OtherPin->bOrphanedPin) - { - // this calls NotifyNodeChanged() - OtherOwningFlowGraphNode->RemoveOrphanedPin(OtherPin); - } - else if (bSendsNodeNotification) + if (IsValid(OtherOwningFlowGraphNode)) { - EdGraph->NotifyNodeChanged(OtherOwningFlowGraphNode); + if (OtherPin->bOrphanedPin) + { + // this calls NotifyNodeChanged() + OtherOwningFlowGraphNode->RemoveOrphanedPin(OtherPin); + } + else if (bSendsNodeNotification) + { + EdGraph->NotifyNodeChanged(OtherOwningFlowGraphNode); + } } } } @@ -816,7 +873,6 @@ TSharedPtr UFlowGraphSchema::GetCreateCommentAction() cons return TSharedPtr(static_cast(new FFlowGraphSchemaAction_NewComment)); } - PRAGMA_DISABLE_DEPRECATION_WARNINGS void UFlowGraphSchema::OnPinConnectionDoubleCicked(UEdGraphPin* PinA, UEdGraphPin* PinB, const FVector2D& GraphPosition) const { @@ -934,7 +990,8 @@ TArray> UFlowGraphSchema::GetFlowNodeCategories() { if (const UFlowNode* DefaultObject = FlowNodeClass->GetDefaultObject()) { - UnsortedCategories.Emplace(DefaultObject->GetNodeCategory()); + const FString NodeCategoryString = UFlowGraphSettings::GetNodeCategoryForNode(*DefaultObject); + UnsortedCategories.Emplace(NodeCategoryString); } } @@ -942,7 +999,8 @@ TArray> UFlowGraphSchema::GetFlowNodeCategories() { if (const UFlowNodeAddOn* DefaultObject = FlowNodeAddOnClass->GetDefaultObject()) { - UnsortedCategories.Emplace(DefaultObject->GetNodeCategory()); + const FString NodeCategoryString = UFlowGraphSettings::GetNodeCategoryForNode(*DefaultObject); + UnsortedCategories.Emplace(NodeCategoryString); } } @@ -1045,23 +1103,46 @@ void UFlowGraphSchema::ApplyNodeOrAddOnFilter(const UFlowAsset* EditedFlowAsset, { return; } - - UFlowNodeBase* NodeDefaults = FlowNodeClass->GetDefaultObject(); - FilteredNodes.Emplace(NodeDefaults); + + const UFlowGraphSettings& FlowGraphSettings = *UFlowGraphSettings::Get(); + const bool bIsHiddenFromPaletteByNodeClass = FlowGraphSettings.NodesHiddenFromPalette.Contains(FlowNodeClass); + if (bIsHiddenFromPaletteByNodeClass) + { + return; + } + + UFlowNodeBase* FlowNodeBaseCDO = FlowNodeClass->GetDefaultObject(); + const FFlowGraphNodesPolicy* FlowAssetPolicy = FlowGraphSettings.PerAssetSubclassFlowNodePolicies.Find(FSoftClassPath(EditedFlowAsset->GetClass())); + if (FlowAssetPolicy) + { + const bool bIsAllowedByPolicy = FlowAssetPolicy->IsNodeAllowedByPolicy(FlowNodeBaseCDO); + if (!bIsAllowedByPolicy) + { + return; + } + } + + FilteredNodes.Emplace(FlowNodeBaseCDO); } void UFlowGraphSchema::GetFlowNodeActions(FGraphActionMenuBuilder& ActionMenuBuilder, const UFlowAsset* EditedFlowAsset, const FString& CategoryName) { - TArray FilteredNodes = GetFilteredPlaceableNodesOrAddOns(EditedFlowAsset, NativeFlowNodes, BlueprintFlowNodes); + const TArray FilteredNodes = GetFilteredPlaceableNodesOrAddOns(EditedFlowAsset, NativeFlowNodes, BlueprintFlowNodes); const UFlowGraphSettings& FlowGraphSettings = *UFlowGraphSettings::Get(); - for (const UFlowNodeBase* FlowNode : FilteredNodes) + for (const UFlowNodeBase* FlowNodeBase : FilteredNodes) { - if ((CategoryName.IsEmpty() || CategoryName.Equals(FlowNode->GetNodeCategory())) && !FlowGraphSettings.NodesHiddenFromPalette.Contains(FlowNode->GetClass())) + // TODO (gtaylor) This should really be integrated into GetFilteredPlaceableNodesOrAddOns, + // but it needs the schema instance, so we need to do a bit more refactoring + const FString NodeCategoryString = UFlowGraphSettings::GetNodeCategoryForNode(*FlowNodeBase); + const bool bAllowedForSchemaCategory = (CategoryName.IsEmpty() || CategoryName.Equals(NodeCategoryString)); + if (!bAllowedForSchemaCategory) { - TSharedPtr NewNodeAction(new FFlowGraphSchemaAction_NewNode(FlowNode, FlowGraphSettings)); - ActionMenuBuilder.AddAction(NewNodeAction); + continue; } + + TSharedPtr NewNodeAction(new FFlowGraphSchemaAction_NewNode(FlowNodeBase, FlowGraphSettings)); + ActionMenuBuilder.AddAction(NewNodeAction); } } @@ -1119,10 +1200,11 @@ void UFlowGraphSchema::GetGraphNodeContextActions(FGraphContextMenuBuilder& Cont UFlowGraphNode* OpNode = NewObject(Graph, GraphNodeClass); OpNode->NodeInstanceClass = FlowNodeAddOnTemplate->GetClass(); + const FString NodeCategoryString = UFlowGraphSettings::GetNodeCategoryForNode(*FlowNodeBase); TSharedPtr AddOpAction = FFlowSchemaAction_NewSubNode::AddNewSubNodeAction( ContextMenuBuilder, - FText::FromString(FlowNodeBase->GetNodeCategory()), + FText::FromString(NodeCategoryString), FlowNodeBase->GetNodeTitle(), FlowNodeBase->GetNodeToolTip()); diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp index abd8db3d2..42b404e38 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema_Actions.cpp @@ -203,13 +203,8 @@ UFlowGraphNode* FFlowGraphSchemaAction_NewNode::ImportNode(UEdGraph* ParentGraph FText FFlowGraphSchemaAction_NewNode::GetNodeCategory(const UFlowNodeBase* Node, const UFlowGraphSettings& GraphSettings) { - const FString* CategoryOverridenByUser = GraphSettings.OverridenNodeCategories.Find(Node->GetClass()); - if (CategoryOverridenByUser && !CategoryOverridenByUser->IsEmpty()) - { - return FText::FromString(*CategoryOverridenByUser); - } - - return FText::FromString(Node->GetNodeCategory()); + const FString NodeCategoryString = UFlowGraphSettings::GetNodeCategoryForNode(*Node); + return FText::FromString(NodeCategoryString); } ///////////////////////////////////////////////////// diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp index 02d054ac1..595efcdde 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp @@ -98,6 +98,18 @@ void UFlowGraphSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyC } } +FString UFlowGraphSettings::GetNodeCategoryForNode(const UFlowNodeBase& FlowNodeBase) +{ + const UFlowGraphSettings* GraphSettings = UFlowGraphSettings::Get(); + + if (const FString* CategoryOverridenByUser = GraphSettings->OverridenNodeCategories.Find(FlowNodeBase.GetClass())) + { + return *CategoryOverridenByUser; + } + + return FlowNodeBase.GetNodeCategory(); +} + const TMap& UFlowGraphSettings::EnsureNodeDisplayStylesMap() { if (NodeDisplayStylesAuthoredTags.Num() != NodeDisplayStyles.Num()) diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index f041c0280..420b5bdc7 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -28,6 +28,7 @@ #include "Kismet2/KismetEditorUtilities.h" #include "ScopedTransaction.h" #include "SourceCodeNavigation.h" +#include "Subsystems/AssetEditorSubsystem.h" #include "Textures/SlateIcon.h" #include "ToolMenuSection.h" #include "Editor/Transactor.h" @@ -196,6 +197,14 @@ void UFlowGraphNode::SubscribeToExternalChanges() if (NodeInstance) { NodeInstance->OnReconstructionRequested.BindUObject(this, &UFlowGraphNode::OnExternalChange); + + for (UFlowGraphNode* SubNode : SubNodes) + { + if (SubNode->NodeInstance) + { + SubNode->NodeInstance->OnAddOnRequestedParentReconstruction.BindUObject(this, &UFlowGraphNode::OnExternalChange); + } + } } } @@ -346,6 +355,11 @@ void UFlowGraphNode::ReconstructNode() bNeedsFullReconstruction = false; } + if (UFlowNode* FlowNode = Cast(NodeInstance)) + { + FlowNode->UpdateNodeConfigText(); + } + // This ensures the graph editor 'Refresh' button still rebuilds all the graph widgets even if the FlowGraphNode has nothing to update // Ideally we could get rid of the 'Refresh' button, but I think it will keep being useful, esp. for users making rough custom widgets (void)OnReconstructNodeCompleted.ExecuteIfBound(); @@ -886,13 +900,11 @@ void UFlowGraphNode::CreateInputPin(const FFlowPin& FlowPin, const int32 Index / return; } - const FName PinCategory = GetPinCategoryFromFlowPin(FlowPin); - const FName PinSubCategory = NAME_None; - UObject* PinSubCategoryObject = FlowPin.GetPinSubCategoryObject().Get(); - constexpr bool bIsReference = false; + const FEdGraphPinType EdGraphPinType = FlowPin.BuildEdGraphPinType(); + + check(!EdGraphPinType.PinCategory.IsNone()); - const FEdGraphPinType PinType = FEdGraphPinType(PinCategory, PinSubCategory, PinSubCategoryObject, EPinContainerType::None, bIsReference, FEdGraphTerminalType()); - UEdGraphPin* NewPin = CreatePin(EGPD_Input, PinType, FlowPin.PinName, Index); + UEdGraphPin* NewPin = CreatePin(EGPD_Input, EdGraphPinType, FlowPin.PinName, Index); check(NewPin); if (!FlowPin.PinFriendlyName.IsEmpty()) @@ -913,13 +925,10 @@ void UFlowGraphNode::CreateOutputPin(const FFlowPin& FlowPin, const int32 Index return; } - const FName PinCategory = GetPinCategoryFromFlowPin(FlowPin); - const FName PinSubCategory = NAME_None; - UObject* PinSubCategoryObject = FlowPin.GetPinSubCategoryObject().Get(); - constexpr bool bIsReference = false; + const FEdGraphPinType EdGraphPinType = FlowPin.BuildEdGraphPinType(); + check(!EdGraphPinType.PinCategory.IsNone()); - const FEdGraphPinType PinType = FEdGraphPinType(PinCategory, PinSubCategory, PinSubCategoryObject, EPinContainerType::None, bIsReference, FEdGraphTerminalType()); - UEdGraphPin* NewPin = CreatePin(EGPD_Output, PinType, FlowPin.PinName, Index); + UEdGraphPin* NewPin = CreatePin(EGPD_Output, EdGraphPinType, FlowPin.PinName, Index); check(NewPin); if (!FlowPin.PinFriendlyName.IsEmpty()) @@ -1114,10 +1123,6 @@ void UFlowGraphNode::GetPinHoverText(const UEdGraphPin& Pin, FString& HoverTextO } } -const FName& UFlowGraphNode::GetPinCategoryFromFlowPin(const FFlowPin& FlowPin) -{ - return FFlowPin::GetPinCategoryFromPinType(FlowPin.GetPinType()); -} void UFlowGraphNode::ForcePinActivation(const FEdGraphPinReference PinReference) const { @@ -1493,6 +1498,10 @@ void UFlowGraphNode::AddSubNode(UFlowGraphNode* SubNode, class UEdGraph* ParentG SubNode->NodePosY = 0; SubNodes.Add(SubNode); + if (SubNode->NodeInstance) + { + SubNode->NodeInstance->OnAddOnRequestedParentReconstruction.BindUObject(this, &UFlowGraphNode::OnExternalChange); + } OnSubNodeAdded(SubNode); ParentGraph->NotifyGraphChanged(); @@ -1509,6 +1518,12 @@ void UFlowGraphNode::OnSubNodeAdded(UFlowGraphNode* SubNode) void UFlowGraphNode::RemoveSubNode(UFlowGraphNode* SubNode) { Modify(); + + if (SubNode->NodeInstance) + { + SubNode->NodeInstance->OnAddOnRequestedParentReconstruction.Unbind(); + } + SubNodes.RemoveSingle(SubNode); RebuildRuntimeAddOnsFromEditorSubNodes(); @@ -1518,6 +1533,14 @@ void UFlowGraphNode::RemoveSubNode(UFlowGraphNode* SubNode) void UFlowGraphNode::RemoveAllSubNodes() { + for (UFlowGraphNode* SubNode : SubNodes) + { + if (SubNode->NodeInstance) + { + SubNode->NodeInstance->OnAddOnRequestedParentReconstruction.Unbind(); + } + } + SubNodes.Reset(); RebuildRuntimeAddOnsFromEditorSubNodes(); @@ -1621,7 +1644,7 @@ void UFlowGraphNode::ValidateGraphNode(FFlowMessageLog& MessageLog) const const UFlowGraphSchema* Schema = CastChecked(GetSchema()); for (const UEdGraphPin* EdGraphPin : InputPins) { - if (!FFlowPin::IsDataPinCategory(EdGraphPin->PinType.PinCategory)) + if (FFlowPin::IsExecPinCategory(EdGraphPin->PinType.PinCategory)) { continue; } @@ -1711,12 +1734,10 @@ bool CheckPinsMatch(const TArray& LeftPins, const TArray& Ri for (const FFlowPin& Left : LeftPins) { + // Do a deep pin match (not a simple name-only match) auto PinsAreEqualPredicate = [&Left](const FFlowPin& Right) { - const bool bNameMatch = Left.PinName == Right.PinName; - const bool bFriendlyNameMatch = Left.PinFriendlyName.EqualTo(Right.PinFriendlyName); - const bool bTypeMatch = Left.GetPinType() == Right.GetPinType(); - return bNameMatch && bFriendlyNameMatch && bTypeMatch; + return Left.DeepIsEqual(Right); }; // For each required pin, make sure the existing pins array contains a pin that matches by name and type @@ -1785,6 +1806,10 @@ bool UFlowGraphNode::TryUpdateNodePins() const const UFlowNode* FlowNodeCDO = FlowNodeInstance->GetClass()->GetDefaultObject(); check(IsValid(FlowNodeCDO)); + // Fix up old pins on the CDO + UFlowNode* MutableCDO = const_cast(FlowNodeCDO); + MutableCDO->FixupDataPinTypes(); + // We grab basic built-in input/output pins from the CDO // We grab extra required pins from the actual node as generated context pins (this includes both data pins and other context exec pins) TArray RequiredNodeInputPins = FlowNodeCDO->GetInputPins(); @@ -1805,10 +1830,12 @@ bool UFlowGraphNode::TryUpdateNodePins() const // ------------ // If required pins don't match existing pins, brute force replace them + // (unless the node allows user added inputs/outputs, in which case we cannot destroy them) bool bPinsChanged = false; - if (!CheckPinsMatch(RequiredNodeInputPins, ExistingNodeInputPins)) + if (!FlowNodeInstance->CanUserAddInput() && + !CheckPinsMatch(RequiredNodeInputPins, ExistingNodeInputPins)) { FlowNodeInstance->Modify(); @@ -1818,7 +1845,8 @@ bool UFlowGraphNode::TryUpdateNodePins() const bPinsChanged = true; } - if (!CheckPinsMatch(RequiredNodeOutputPins, ExistingNodeOutputPins)) + if (!FlowNodeInstance->CanUserAddOutput() && + !CheckPinsMatch(RequiredNodeOutputPins, ExistingNodeOutputPins)) { FlowNodeInstance->Modify(); diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp index 25d96645f..0f81baeaf 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp @@ -375,8 +375,8 @@ void SFlowGraphNode::UpdateGraphNode() .IsGraphNodeHovered(this, &SGraphNode::IsHovered); GetOrAddSlot(ENodeZone::TopCenter) - .SlotOffset(TAttribute(CommentBubble.Get(), &SCommentBubble::GetOffset)) - .SlotSize(TAttribute(CommentBubble.Get(), &SCommentBubble::GetSize)) + .SlotOffset2f(TAttribute(CommentBubble.Get(), &SCommentBubble::GetOffset2f)) + .SlotSize2f(TAttribute(CommentBubble.Get(), &SCommentBubble::GetSize2f)) .AllowScaling(TAttribute(CommentBubble.Get(), &SCommentBubble::IsScalingAllowed)) .VAlign(VAlign_Top) [ @@ -511,7 +511,7 @@ EVisibility SFlowGraphNode::GetNodeConfigTextVisibility() const { // Hide in lower LODs const TSharedPtr OwnerPanel = GetOwnerPanel(); - if (!OwnerPanel.IsValid() || OwnerPanel->GetCurrentLOD() > EGraphRenderingLOD::MediumDetail) + if (!OwnerPanel.IsValid() || OwnerPanel->GetCurrentLOD() >= EGraphRenderingLOD::MediumDetail) { if (ConfigTextBlock && !ConfigTextBlock->GetText().IsEmptyOrWhitespace()) { diff --git a/Source/FlowEditor/Private/UnrealExtensions/IFlowCuratedNamePropertyCustomization.cpp b/Source/FlowEditor/Private/UnrealExtensions/IFlowCuratedNamePropertyCustomization.cpp index 5ef3afb86..77e64d428 100644 --- a/Source/FlowEditor/Private/UnrealExtensions/IFlowCuratedNamePropertyCustomization.cpp +++ b/Source/FlowEditor/Private/UnrealExtensions/IFlowCuratedNamePropertyCustomization.cpp @@ -59,22 +59,22 @@ void IFlowCuratedNamePropertyCustomization::CreateHeaderRowWidget(FDetailWidgetR .NameContent() [ SAssignNew(HeaderTextBlock, STextBlock) - .Text(BuildHeaderText()) + .Text(BuildHeaderText()) ] .ValueContent() .MaxDesiredWidth(250.0f) [ SAssignNew(TextListWidget, SComboBox>) - .OptionsSource(&CachedTextList) - .OnGenerateWidget_Static(&IFlowCuratedNamePropertyCustomization::GenerateTextListWidget) - .OnComboBoxOpening(this, &IFlowCuratedNamePropertyCustomization::OnTextListComboBoxOpening) - .OnSelectionChanged(this, &IFlowCuratedNamePropertyCustomization::OnTextSelected) - [ - SNew(STextBlock) - .Text(this, &IFlowCuratedNamePropertyCustomization::GetCachedText) - .Font(IDetailLayoutBuilder::GetDetailFont()) - .ToolTipText(this, &IFlowCuratedNamePropertyCustomization::GetCachedText) - ] + .OptionsSource(&CachedTextList) + .OnGenerateWidget_Static(&IFlowCuratedNamePropertyCustomization::GenerateTextListWidget) + .OnComboBoxOpening(this, &IFlowCuratedNamePropertyCustomization::OnTextListComboBoxOpening) + .OnSelectionChanged(this, &IFlowCuratedNamePropertyCustomization::OnTextSelected) + [ + SNew(STextBlock) + .Text(this, &IFlowCuratedNamePropertyCustomization::GetCachedText) + .Font(IDetailLayoutBuilder::GetDetailFont()) + .ToolTipText(this, &IFlowCuratedNamePropertyCustomization::GetCachedText) + ] ]; // Hook-up the ResetToDefault overrides @@ -82,7 +82,7 @@ void IFlowCuratedNamePropertyCustomization::CreateHeaderRowWidget(FDetailWidgetR FIsResetToDefaultVisible::CreateSP( this, &IFlowCuratedNamePropertyCustomization::CustomIsResetToDefaultVisible); - FResetToDefaultHandler ResetHandler = + FResetToDefaultHandler ResetHandler = FResetToDefaultHandler::CreateSP( this, &IFlowCuratedNamePropertyCustomization::CustomResetToDefault); @@ -95,7 +95,7 @@ void IFlowCuratedNamePropertyCustomization::CreateHeaderRowWidget(FDetailWidgetR HeaderRow.IsEnabled(IsEnabledAttribute); } -bool IFlowCuratedNamePropertyCustomization::CustomIsResetToDefaultVisible(TSharedPtr Property) const +bool IFlowCuratedNamePropertyCustomization::CustomIsResetToDefaultVisibleImpl(TSharedPtr Property) const { FName CuratedName; if (!TryGetCuratedName(CuratedName)) @@ -106,7 +106,7 @@ bool IFlowCuratedNamePropertyCustomization::CustomIsResetToDefaultVisible(TShare return !CuratedName.IsNone(); } -void IFlowCuratedNamePropertyCustomization::CustomResetToDefault(TSharedPtr Property) +void IFlowCuratedNamePropertyCustomization::CustomResetToDefaultImpl(TSharedPtr Property) { if (TrySetCuratedNameWithSideEffects(NAME_None)) { @@ -114,7 +114,7 @@ void IFlowCuratedNamePropertyCustomization::CustomResetToDefault(TSharedPtrIsEditConst()) { @@ -132,7 +132,7 @@ bool IFlowCuratedNamePropertyCustomization::CustomIsEnabled() const bool IFlowCuratedNamePropertyCustomization::TrySetCuratedNameWithSideEffects(const FName& NewName) { FName ExistingName; - (void) TryGetCuratedName(ExistingName); + (void)TryGetCuratedName(ExistingName); if (ExistingName != NewName) { @@ -196,7 +196,7 @@ void IFlowCuratedNamePropertyCustomization::OnTextListComboBoxOpening() for (TSharedPtr& Text : CachedTextList) { - (void) MapNameToText.FindOrAdd(FName(Text.Get()->ToString()), Text); + (void)MapNameToText.FindOrAdd(FName(Text.Get()->ToString()), Text); } TArray CuratedNameOptions = GetCuratedNameOptions(); diff --git a/Source/FlowEditor/Private/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.cpp b/Source/FlowEditor/Private/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.cpp index d54ab8610..96d42a729 100644 --- a/Source/FlowEditor/Private/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.cpp +++ b/Source/FlowEditor/Private/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.cpp @@ -10,9 +10,6 @@ #include "IDetailPropertyRow.h" #include "Widgets/Text/STextBlock.h" - -// IFlowExtendedPropertyTypeCustomization Implementation - void IFlowExtendedPropertyTypeCustomization::CustomizeHeader(TSharedRef InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) { StructPropertyHandle = InStructPropertyHandle; diff --git a/Source/FlowEditor/Public/Asset/AssetDefinition_FlowAssetParams.h b/Source/FlowEditor/Public/Asset/AssetDefinition_FlowAssetParams.h new file mode 100644 index 000000000..99c8aa74c --- /dev/null +++ b/Source/FlowEditor/Public/Asset/AssetDefinition_FlowAssetParams.h @@ -0,0 +1,24 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "AssetDefinitionDefault.h" +#include "AssetDefinition_FlowAssetParams.generated.h" + +/** +* Asset Definition for Flow Asset Params, providing Content Browser integration. +*/ +UCLASS() +class FLOWEDITOR_API UAssetDefinition_FlowAssetParams : public UAssetDefinitionDefault +{ + GENERATED_BODY() + +public: + // UAssetDefinition interface + virtual FText GetAssetDisplayName() const override; + virtual FLinearColor GetAssetColor() const override; + virtual TSoftClassPtr GetAssetClass() const override; + virtual TConstArrayView GetAssetCategories() const override; + virtual FAssetSupportResponse CanLocalize(const FAssetData& InAsset) const override; + // -- +}; \ No newline at end of file diff --git a/Source/FlowEditor/Public/Asset/FlowDiffControl.h b/Source/FlowEditor/Public/Asset/FlowDiffControl.h index 5240636ff..770f3a996 100644 --- a/Source/FlowEditor/Public/Asset/FlowDiffControl.h +++ b/Source/FlowEditor/Public/Asset/FlowDiffControl.h @@ -5,6 +5,7 @@ #include "Asset/FlowObjectDiff.h" #include "DiffResults.h" +#include "Runtime/Launch/Resources/Version.h" #if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 7 #include "Editor/Kismet/Private/DiffControl.h" #else diff --git a/Source/FlowEditor/Public/Asset/FlowObjectDiff.h b/Source/FlowEditor/Public/Asset/FlowObjectDiff.h index d1896fabe..0bb17be07 100644 --- a/Source/FlowEditor/Public/Asset/FlowObjectDiff.h +++ b/Source/FlowEditor/Public/Asset/FlowObjectDiff.h @@ -2,6 +2,7 @@ #pragma once +#include "Runtime/Launch/Resources/Version.h" #if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 7 #include "Editor/Kismet/Private/DiffControl.h" #else diff --git a/Source/FlowEditor/Public/Asset/SFlowDiff.h b/Source/FlowEditor/Public/Asset/SFlowDiff.h index 5b4ea888e..bf23eacb8 100644 --- a/Source/FlowEditor/Public/Asset/SFlowDiff.h +++ b/Source/FlowEditor/Public/Asset/SFlowDiff.h @@ -48,6 +48,8 @@ struct FLOWEDITOR_API FFlowDiffPanel void FocusDiff(const UEdGraphPin& Pin) const; void FocusDiff(const UEdGraphNode& Node) const; + void OnNodeClicked(UObject* ClickedNode ); + /** The Flow Asset that owns the graph we are showing */ const UFlowAsset* FlowAsset; @@ -55,8 +57,9 @@ struct FLOWEDITOR_API FFlowDiffPanel TSharedPtr GraphEditorBox; /** using SNullWidget::NullNullWidget can only work for a single widget, since widget instances can only be - * used one at a time. EmptyDetailsView is used for displaying an empty details panel instead. */ - TSharedPtr EmptyDetailsView; + * used one at a time. PanelDefaultDetailsView is used for displaying an empty details panel instead, as well + * as if the user selects a node in the graph view. */ + TSharedPtr PanelDefaultDetailsView; /** The graph editor which does the work of displaying the graph */ TWeakPtr GraphEditor; @@ -69,6 +72,10 @@ struct FLOWEDITOR_API FFlowDiffPanel /** The widget that contains the revision info in graph mode */ TSharedPtr OverlayGraphRevisionInfo; + + TWeakPtr GraphDiffSplitter = nullptr; + bool bIsOldPanel = false; + private: /** Command list for this diff panel */ TSharedPtr GraphEditorCommands; @@ -172,7 +179,7 @@ class FLOWEDITOR_API SFlowDiff : public SCompoundWidget FDiffControl GenerateDetailsPanel(); FDiffControl GenerateGraphPanel(); - TSharedRef GenerateGraphWidgetForPanel(FFlowDiffPanel& OutDiffPanel) const; + TSharedRef GenerateGraphWidgetForPanel(FFlowDiffPanel& OutDiffPanel) const; TSharedRef GenerateRevisionInfoWidgetForPanel(TSharedPtr& OutGeneratedWidget, const FText& InRevisionText) const; /** Accessor and event handler for toggling between diff view modes (defaults, components, graph view, interface, macro): */ @@ -214,7 +221,7 @@ class FLOWEDITOR_API SFlowDiff : public SCompoundWidget /** Tree view that displays the differences, cached for the buttons that iterate the differences: */ TSharedPtr>> DifferencesTreeView; - /** Stored references to widgets used to display various parts of a asset, from the mode name */ + /** Stored references to widgets used to display various parts of asset, from the mode name */ TMap ModePanels; /** A pointer to the window holding this */ diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowAssetParamsPtrCustomization.h b/Source/FlowEditor/Public/DetailCustomizations/FlowAssetParamsPtrCustomization.h new file mode 100644 index 000000000..2c979633f --- /dev/null +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowAssetParamsPtrCustomization.h @@ -0,0 +1,24 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "IPropertyTypeCustomization.h" +#include "PropertyHandle.h" + +class IPropertyHandle; + +// Customizes the FFlowAssetParamsPtr property in the Details panel. +class FFlowAssetParamsPtrCustomization : public IPropertyTypeCustomization +{ +public: + static TSharedRef MakeInstance(); + + virtual void CustomizeHeader(TSharedRef PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) override; + virtual void CustomizeChildren(TSharedRef PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) override; + +private: + TSharedPtr StructPropertyHandle; + + void HandleCreateNew(); + bool ShouldFilterAsset(const FAssetData& AssetData) const; +}; \ No newline at end of file diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinPropertyCustomizationBase.h b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinPropertyCustomizationBase.h deleted file mode 100644 index f29ee10c4..000000000 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinPropertyCustomizationBase.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#pragma once - -#include "UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h" - -// Base class customization for most FFlow*DataPinProperty structs -class FFlowDataPinPropertyCustomizationBase : public IFlowExtendedPropertyTypeCustomization -{ - typedef IFlowExtendedPropertyTypeCustomization Super; - -protected: - - virtual void CustomizeHeader(TSharedRef InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; - virtual void CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; -}; - -// Template to create customization classes for FFlow*DataPinProperty structs -template -class TFlowDataPinPropertyCustomization : public FFlowDataPinPropertyCustomizationBase -{ -public: - static TSharedRef MakeInstance() { return MakeShareable(new TFlowDataPinPropertyCustomization()); } -}; diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinPropertyCustomizations.h b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinPropertyCustomizations.h deleted file mode 100644 index e696996b5..000000000 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinPropertyCustomizations.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#pragma once - -#include "FlowDataPinPropertyCustomizationBase.h" - -#include "Types/FlowDataPinProperties.h" - -// Consider implementing details customization... for every EFlowPinType -FLOW_ASSERT_ENUM_MAX(EFlowPinType, 16); - -typedef TFlowDataPinPropertyCustomization FFlowDataPinOutputProperty_BoolCustomization; -typedef TFlowDataPinPropertyCustomization FFlowDataPinOutputProperty_Int64Customization; -typedef TFlowDataPinPropertyCustomization FFlowDataPinOutputProperty_Int32Customization; -typedef TFlowDataPinPropertyCustomization FFlowDataPinOutputProperty_DoubleCustomization; -typedef TFlowDataPinPropertyCustomization FFlowDataPinOutputProperty_FloatCustomization; -typedef TFlowDataPinPropertyCustomization FFlowDataPinOutputProperty_NameCustomization; -typedef TFlowDataPinPropertyCustomization FFlowDataPinOutputProperty_StringCustomization; -typedef TFlowDataPinPropertyCustomization FFlowDataPinOutputProperty_TextCustomization; - -typedef TFlowDataPinPropertyCustomization FFlowDataPinInputProperty_BoolCustomization; -typedef TFlowDataPinPropertyCustomization FFlowDataPinInputProperty_Int64Customization; -typedef TFlowDataPinPropertyCustomization FFlowDataPinInputProperty_Int32Customization; -typedef TFlowDataPinPropertyCustomization FFlowDataPinInputProperty_DoubleCustomization; -typedef TFlowDataPinPropertyCustomization FFlowDataPinInputProperty_FloatCustomization; -typedef TFlowDataPinPropertyCustomization FFlowDataPinInputProperty_NameCustomization; -typedef TFlowDataPinPropertyCustomization FFlowDataPinInputProperty_StringCustomization; -typedef TFlowDataPinPropertyCustomization FFlowDataPinInputProperty_TextCustomization; - -// NOTE (gtaylor) Enum is defined separately, because it's quite a bit more complex -// NOTE (gtaylor) Class, Object are also defined separately as they are also more complex -// NOTE (gtaylor) BaseStruct types like FVector don't customize well using this technique, so I am leaving their default details handler in-place \ No newline at end of file diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinProperty_ClassCustomization.h b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinProperty_ClassCustomization.h deleted file mode 100644 index eb8a25a90..000000000 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinProperty_ClassCustomization.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#pragma once - -#include "UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h" - -#include "Types/FlowDataPinProperties.h" - -class SClassPropertyEntryBox; - -class FFlowDataPinProperty_ClassCustomizationBase : public IFlowExtendedPropertyTypeCustomization -{ -private: - typedef IFlowExtendedPropertyTypeCustomization Super; - -protected: - - //~Begin IPropertyTypeCustomization - virtual void CustomizeHeader(TSharedRef InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; - virtual void CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; - //~End IPropertyTypeCustomization - - // Accessor to return the actual struct being edited - FORCEINLINE FFlowDataPinOutputProperty_Class* GetFlowDataPinClassProperty() const - { - return IFlowExtendedPropertyTypeCustomization::TryGetTypedStructValue(StructPropertyHandle); - } - - UClass* DeriveBestClassFilter() const; - UClass* BuildMetaClass() const; - - void TrySetClassFilterFromMetaData(); - - void OnClassFilterChanged(); - const UClass* OnGetClass() const; - void OnSetClass(const UClass* NewClass); - - mutable TWeakObjectPtr CachedClassPtr; - mutable TWeakObjectPtr CachedMetaClassPtr; -}; - -// Details customization for FFlowDataPinOutputProperty_Class/FFlowDataPinInputProperty_Class -template -class TFlowDataPinProperty_ClassCustomization : public FFlowDataPinProperty_ClassCustomizationBase -{ -public: - static TSharedRef MakeInstance() { return MakeShareable(new TFlowDataPinProperty_ClassCustomization()); } -}; - -// Details customization for Class FFlowDataPinProperties -typedef TFlowDataPinProperty_ClassCustomization FFlowDataPinOutputProperty_ClassCustomization; -typedef TFlowDataPinProperty_ClassCustomization FFlowDataPinInputProperty_ClassCustomization; diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinProperty_EnumCustomization.h b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinProperty_EnumCustomization.h deleted file mode 100644 index 06ffcaa71..000000000 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinProperty_EnumCustomization.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#pragma once - -#include "UnrealExtensions/IFlowCuratedNamePropertyCustomization.h" - -#include "Types/FlowDataPinProperties.h" - -// NOTE (gtaylor) this is nearly identical to AI Flow - FConfigurableEnumPropertyCustomization, can we combine them? - -class FFlowDataPinProperty_EnumCustomizationBase : public IFlowCuratedNamePropertyCustomization -{ -private: - typedef IFlowCuratedNamePropertyCustomization Super; - -protected: - - //~Begin IPropertyTypeCustomization - virtual void CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; - //~End IPropertyTypeCustomization - - //~Begin ICuratedNamePropertyCustomization - virtual TSharedPtr GetCuratedNamePropertyHandle() const override; - virtual void SetCuratedName(const FName& NewName) override; - virtual bool TryGetCuratedName(FName& OutName) const override; - virtual TArray GetCuratedNameOptions() const override; - virtual bool AllowNameNoneIfOtherOptionsExist() const override { return false; } - //~End ICuratedNamePropertyCustomization - - // Accessor to return the actual struct being edited - FORCEINLINE FFlowDataPinOutputProperty_Enum* GetFlowDataPinEnumProperty() const - { - return IFlowExtendedPropertyTypeCustomization::TryGetTypedStructValue(StructPropertyHandle); - } - - void OnEnumNameChanged(); - - const UEnum* GetEnumClass() const; - - static TArray GetEnumValues(const UEnum& Enum); -}; - -// Details customization for FFlowDataPinOutputProperty_Enum -template -class TFlowDataPinProperty_EnumCustomization : public FFlowDataPinProperty_EnumCustomizationBase -{ -public: - static TSharedRef MakeInstance() { return MakeShareable(new TFlowDataPinProperty_EnumCustomization()); } -}; - -// Details customization for enum FFlowDataPinProperties -typedef TFlowDataPinProperty_EnumCustomization FFlowDataPinOutputProperty_EnumCustomization; -typedef TFlowDataPinProperty_EnumCustomization FFlowDataPinInputProperty_EnumCustomization; diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinProperty_ObjectCustomization.h b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinProperty_ObjectCustomization.h deleted file mode 100644 index 2a1fffdec..000000000 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinProperty_ObjectCustomization.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -#pragma once - -#include "UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h" - -#include "Types/FlowDataPinProperties.h" - -class FFlowDataPinProperty_ObjectCustomizationBase : public IFlowExtendedPropertyTypeCustomization -{ -private: - typedef IFlowExtendedPropertyTypeCustomization Super; - -protected: - - //~Begin IPropertyTypeCustomization - virtual void CustomizeHeader(TSharedRef InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; - virtual void CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; - //~End IPropertyTypeCustomization - - // Accessor to return the actual struct being edited - FORCEINLINE FFlowDataPinOutputProperty_Object* GetFlowDataPinObjectProperty() const - { - return IFlowExtendedPropertyTypeCustomization::TryGetTypedStructValue(StructPropertyHandle); - } - - UClass* DeriveBestClassFilter() const; - UClass* BuildMetaClass() const; - - void TrySetClassFilterFromMetaData(); - - void OnClassFilterChanged(); - void OnObjectValueChanged(); - - mutable TWeakObjectPtr CachedMetaClassPtr; -}; - -// Details customization for FFlowDataPinOutputProperty_Object/FFlowDataPinInputProperty_Object -template -class TFlowDataPinProperty_ObjectCustomization : public FFlowDataPinProperty_ObjectCustomizationBase -{ -public: - static TSharedRef MakeInstance() { return MakeShareable(new TFlowDataPinProperty_ObjectCustomization()); } -}; - -// Details customization for Object FFlowDataPinProperties -typedef TFlowDataPinProperty_ObjectCustomization FFlowDataPinOutputProperty_ObjectCustomization; -typedef TFlowDataPinProperty_ObjectCustomization FFlowDataPinInputProperty_ObjectCustomization; diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization.h b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization.h new file mode 100644 index 000000000..7e0fb1404 --- /dev/null +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization.h @@ -0,0 +1,138 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Types/FlowPinType.h" +#include "Types/FlowDataPinValue.h" +#include "Types/FlowPinEnums.h" +#include "UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h" +#include "IPropertyTypeCustomization.h" +#include "Widgets/Input/SComboBox.h" +#include "Templates/Function.h" + +class IFlowDataPinValueOwnerInterface; + +/* +* Flow Data Pin Value Customization +* +* Responsibilities: +* - Header with MultiType selector + Input Pin checkbox. +* - Child rows for single vs array modes (built conditionally now). +* - Base for specialized enum/class/object customizations. +* - Exposes array validation helpers. +* +* Dynamic Mode Switching: +* - After MultiType changes, invokes OwnerInterface->RequestFlowDataPinValuesDetailsRebuild() +* (implemented via owner-level detail customization) to force a full rebuild. +*/ +class FLOWEDITOR_API FFlowDataPinValueCustomization : public IFlowExtendedPropertyTypeCustomization +{ + using Super = IFlowExtendedPropertyTypeCustomization; + +protected: + // Property handles + TSharedPtr MultiTypeHandle; + TSharedPtr ValuesHandle; + TSharedPtr IsInputPinHandle; + TSharedPtr PropertyUtilities; + + // Cached context + const FFlowPinType* DataPinType = nullptr; + IPropertyTypeCustomizationUtils* CustomizationUtils = nullptr; + IFlowDataPinValueOwnerInterface* OwnerInterface = nullptr; + + // MultiType UI state (enum values) + TArray> MultiTypeOptions; + TSharedPtr SelectedMultiType; + TSharedPtr>> MultiTypeComboBox; + + // Cached flag whether this pin type supports Array mode + bool bArraySupported = true; + +public: + FFlowDataPinValueCustomization() = default; + static TSharedRef MakeInstance(); + + // Non-copyable / non-movable + FFlowDataPinValueCustomization(const FFlowDataPinValueCustomization&) = delete; + FFlowDataPinValueCustomization& operator=(const FFlowDataPinValueCustomization&) = delete; + FFlowDataPinValueCustomization(FFlowDataPinValueCustomization&&) = delete; + FFlowDataPinValueCustomization& operator=(FFlowDataPinValueCustomization&&) = delete; + + // IPropertyTypeCustomization Interface + virtual void CustomizeHeader(TSharedRef InStructPropertyHandle, + FDetailWidgetRow& HeaderRow, + IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; + + virtual void CustomizeChildren(TSharedRef InStructPropertyHandle, + IDetailChildrenBuilder& StructBuilder, + IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; + +protected: + // Build flow + virtual void BuildValueRows(TSharedRef InStructPropertyHandle, + IDetailChildrenBuilder& StructBuilder, + IPropertyTypeCustomizationUtils& StructCustomizationUtils); + + virtual void BuildSingleBranch(IDetailChildrenBuilder& StructBuilder); + virtual void BuildArrayBranch(IDetailChildrenBuilder& StructBuilder); // Skips if !bArraySupported + + void EnsureSingleElementExists(); + void RequestRefresh(); + + // Mode / State + EFlowDataMultiType GetCurrentMultiType() const; + EVisibility GetSingleModeVisibility() const; + EVisibility GetArrayModeVisibility() const; + void TrimArrayToSingle(); + + // Appearance + FFlowDataPinValue* GetFlowDataPinValueBeingEdited() const + { + return IFlowExtendedPropertyTypeCustomization::TryGetTypedStructValue(StructPropertyHandle); + } + + // Input Pin + ECheckBoxState GetCurrentIsInputPin() const; + EVisibility GetInputPinCheckboxVisibility() const; + bool GetInputPinCheckboxEnabled() const; + + // MultiType UI Helpers + TSharedRef GenerateMultiTypeWidget(TSharedPtr Item) const; + FText GetSelectedMultiTypeText() const; + + // Change Handlers + void OnMultiTypeChanged(TSharedPtr NewSelection, ESelectInfo::Type SelectInfo); + void OnInputPinChanged(ECheckBoxState NewState); + + // Caching + void CacheHandles(const TSharedRef& PropertyHandle, + IPropertyTypeCustomizationUtils& StructCustomizationUtils); + void CacheOwnerInterface(); + void CacheArraySupported(); + + // Shared Helpers ------------------------------------------------------ + void BuildVisibilityAwareArray(IDetailChildrenBuilder& StructBuilder, + TSharedPtr ArrayHandle, + TFunction, int32, IDetailChildrenBuilder&, const TAttribute&)> Generator, + TAttribute VisibilityAttribute); + + void ValidateArrayElements(TSharedPtr ArrayHandle, + TFunction)> IsValidPredicate, + TFunction)> InvalidateAction); + + // Tooltips (centralized) + static FText GetMultiTypeTooltip(); + static FText GetInputPinTooltip(); +}; + +// Template customization for simple scalar value structs +template +class TFlowDataPinValueCustomization : public FFlowDataPinValueCustomization +{ +public: + static TSharedRef MakeInstance() + { + return MakeShareable(new TFlowDataPinValueCustomization()); + } +}; \ No newline at end of file diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization_Class.h b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization_Class.h new file mode 100644 index 000000000..32728efb8 --- /dev/null +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization_Class.h @@ -0,0 +1,81 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "DetailCustomizations/FlowDataPinValueCustomization.h" + +class SClassPropertyEntryBox; + +/* +* Class value customization: +* - Conditionally shows ClassFilter (OwnerInterface->ShowFlowDataPinValueClassFilter). +* - If MetaClass metadata present: show row but disabled. +* - Enabled state otherwise: OwnerInterface->CanEditFlowDataPinValueClassFilter. +* - Validates stored FSoftClassPath values against effective filter. +*/ +class FLOWEDITOR_API FFlowDataPinValueCustomization_Class : public FFlowDataPinValueCustomization +{ + using Super = FFlowDataPinValueCustomization; + +public: + static TSharedRef MakeInstance() + { + return MakeShareable(new FFlowDataPinValueCustomization_Class()); + } + +protected: + virtual void BuildValueRows(TSharedRef InStructPropertyHandle, + IDetailChildrenBuilder& StructBuilder, + IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; + +private: + // Property handles + TSharedPtr ClassFilterHandle; + + // Metadata-derived flags + const UClass* RequiredInterface = nullptr; + bool bAllowAbstract = true; + bool bIsBlueprintBaseOnly = false; + bool bAllowNone = true; + bool bShowTreeView = false; + bool bHideViewOptions = false; + bool bShowDisplayNames = false; + bool bHasMetaClass = false; + + // Effective filter + TWeakObjectPtr CachedEffectiveFilter; + + // Helpers + void ExtractMetadata(); + void TrySetClassFilterFromMetaData() const; + UClass* DeriveBestClassFilter() const; + void RefreshEffectiveFilter(); + + // UI + void BuildClassFilterRow(IDetailChildrenBuilder& StructBuilder, bool bSourceEditable) const; + void BuildSingleBranch(IDetailChildrenBuilder& StructBuilder); + void BuildArrayBranch(IDetailChildrenBuilder& StructBuilder); + + // Delegates / validation + void BindDelegates(); + void OnClassFilterChanged(); + void OnValuesChanged(); + + void ValidateAllElements(); + bool IsElementValid(TSharedPtr ElementHandle) const; + + // Access / modification + static const UClass* GetSelectedClassForHandle(TSharedPtr ElementHandle); + void OnSetClassForHandle(const UClass* NewClass, TSharedPtr ElementHandle) const; + + static bool GetElementPathString(const TSharedPtr& ElementHandle, FString& OutPath); + static bool IsNoneString(const FString& Str); + + // Permissions (inline owner queries) + bool ShouldShowSourceRow() const; + bool IsSourceEditable() const; + bool AreValuesEditable() const { return true; } + + // Value struct access + struct FFlowDataPinValue_Class* GetValueStruct() const; +}; \ No newline at end of file diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization_Enum.h b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization_Enum.h new file mode 100644 index 000000000..c6bdecaac --- /dev/null +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization_Enum.h @@ -0,0 +1,79 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "DetailCustomizations/FlowDataPinValueCustomization.h" + +class UEnum; + +/* +* Enum customization: +* - Conditionally shows EnumClass / EnumName (OwnerInterface->ShowFlowDataPinValueClassFilter). +* - Enabled if OwnerInterface->CanEditFlowDataPinValueClassFilter (MetaClass concept not applied here). +* - Enumerator selection via combo boxes (single / array). +* - Validates stored names. +* - Uses base single/array visibility helpers. +*/ +class FLOWEDITOR_API FFlowDataPinValueCustomization_Enum : public FFlowDataPinValueCustomization +{ + using Super = FFlowDataPinValueCustomization; + +public: + static TSharedRef MakeInstance() + { + return MakeShareable(new FFlowDataPinValueCustomization_Enum()); + } + +protected: + virtual void BuildValueRows(TSharedRef InStructPropertyHandle, + IDetailChildrenBuilder& StructBuilder, + IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; + +private: + // Source handles + TSharedPtr EnumClassHandle; + TSharedPtr EnumNameHandle; + + // Enumerator state + TArray> EnumeratorOptions; + bool bEnumResolved = false; + bool bMultiTypeDelegateBound = false; + + // Build helpers + void BuildSingle(IDetailChildrenBuilder& StructBuilder); + void BuildArray(IDetailChildrenBuilder& StructBuilder); + + // Enum resolution + void CacheEnumHandles(const TSharedRef& StructHandle); + void OnEnumSourceChanged(); + void RebuildEnumData(); + UEnum* ResolveEnum() const; + void CollectEnumerators(UEnum& EnumObj); + + // Validation + void ValidateStoredValues(); + bool IsValueValid(const FName& Candidate) const; + TSharedPtr FindEnumeratorMatch(const FName& Current) const; + + // Multi-type reaction + void OnMultiTypeChanged(); + + // Widgets + TSharedRef GenerateEnumeratorWidget(TSharedPtr Item) const; + static FText GetEnumeratorDisplayText(const FName& Value); + FText GetEnumSourceTooltip() const; + + // Selection handlers + static void OnSingleValueChanged(TSharedPtr NewSelection, ESelectInfo::Type SelectInfo, TSharedPtr ElementHandle); + static void OnArrayElementChanged(TSharedPtr NewSelection, ESelectInfo::Type SelectInfo, TSharedPtr ElementHandle); + + // Convenience + struct FFlowDataPinValue_Enum* GetEnumValueStruct() const; + bool HasEnumeratorOptions() const { return bEnumResolved && EnumeratorOptions.Num() > 0; } + bool IsValueEditingEnabled() const { return HasEnumeratorOptions(); } + + // Permissions (inline owner queries) + bool ShouldShowSourceRow() const; + bool IsSourceEditable() const; + bool AreValuesEditable() const { return true; } +}; diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization_Object.h b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization_Object.h new file mode 100644 index 000000000..a680d2e9a --- /dev/null +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization_Object.h @@ -0,0 +1,67 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "DetailCustomizations/FlowDataPinValueCustomization.h" + +/* +* Object value customization: +* - Conditionally shows ClassFilter (OwnerInterface->ShowFlowDataPinValueClassFilter). +* - MetaClass metadata forces filter (row shown but disabled). +* - Validates object references against effective filter. +*/ +class FLOWEDITOR_API FFlowDataPinValueCustomization_Object : public FFlowDataPinValueCustomization +{ + using Super = FFlowDataPinValueCustomization; + +public: + static TSharedRef MakeInstance() + { + return MakeShareable(new FFlowDataPinValueCustomization_Object()); + } + +protected: + virtual void BuildValueRows(TSharedRef InStructPropertyHandle, + IDetailChildrenBuilder& StructBuilder, + IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; + +private: + // Property handles + TSharedPtr ClassFilterHandle; + + // MetaClass state + bool bMetaClassForced = false; + TWeakObjectPtr EffectiveFilterClass; + + // UI building + void BuildClassFilterRow(IDetailChildrenBuilder& StructBuilder, bool bSourceEditable) const; + virtual void BuildSingleBranch(IDetailChildrenBuilder& StructBuilder) override; + virtual void BuildArrayBranch(IDetailChildrenBuilder& StructBuilder) override; + + // Metadata / filter + void TryApplyMetaClass(); + void ResolveEffectiveFilter(); + + // Delegates & validation + void BindDelegates(); + void OnClassFilterChanged(); + void OnValuesChanged(); + void ValidateAll(); + bool IsElementValid(TSharedPtr ElementHandle) const; + static void InvalidateElement(TSharedPtr ElementHandle); + + // Value access + static UObject* GetObjectValue(TSharedPtr ElementHandle); + static void SetObjectValue(TSharedPtr ElementHandle, UObject* NewObj); + + // Permissions (inline owner queries) + bool ShouldShowSourceRow() const; + bool IsSourceEditable() const; + static bool AreValuesEditable() { return true; } + + // Value struct accessor + struct FFlowDataPinValue_Object* GetValueStruct() const; + + // Widget + TSharedRef BuildObjectValueWidgetForElement(TSharedPtr ElementHandle) const; +}; \ No newline at end of file diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueOwnerCustomization.h b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueOwnerCustomization.h new file mode 100644 index 000000000..62943e8bc --- /dev/null +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueOwnerCustomization.h @@ -0,0 +1,62 @@ +#pragma once + +#if WITH_EDITOR + +#include "IDetailCustomization.h" +#include "DetailLayoutBuilder.h" +#include "Delegates/Delegate.h" +#include "Templates/SharedPointer.h" + +#include "Interfaces/FlowDataPinValueOwnerInterface.h" + +class IFlowDataPinValueOwnerInterface; + +/* +* Template customization for owner types implementing IFlowDataPinValueOwnerInterface. +* Captures the layout builder pointer and installs a rebuild delegate. +*/ +template +class TFlowDataPinValueOwnerCustomization : public IDetailCustomization +{ +public: + static TSharedRef MakeInstance() + { + return MakeShareable(new TFlowDataPinValueOwnerCustomization()); + } + + virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override + { + CachedBuilder = &DetailBuilder; + + TArray> Objects; + DetailBuilder.GetObjectsBeingCustomized(Objects); + + for (TWeakObjectPtr& Obj : Objects) + { + OwnerT* TypedOwner = Cast(Obj.Get()); + if (!TypedOwner) + { + continue; + } + + if (IFlowDataPinValueOwnerInterface* InterfacePtr = Cast(TypedOwner)) + { + InterfacePtr->SetFlowDataPinValuesRebuildDelegate( + FSimpleDelegate::CreateSP(this, &TFlowDataPinValueOwnerCustomization::RequestRebuild)); + } + } + } + +private: + IDetailLayoutBuilder* CachedBuilder = nullptr; + + void RequestRebuild() + { + if (CachedBuilder) + { + CachedBuilder->ForceRefreshDetails(); // Full rebuild; will recreate this customization instance + } + } +}; + +#endif // WITH_EDITOR \ No newline at end of file diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueOwnerCustomizations.h b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueOwnerCustomizations.h new file mode 100644 index 000000000..9cf99f9e8 --- /dev/null +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueOwnerCustomizations.h @@ -0,0 +1,13 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "FlowDataPinValueOwnerCustomization.h" + +#include "Asset/FlowAssetParams.h" +#include "Nodes/FlowNodeBase.h" +#include "FlowExecutableActorComponent.h" + +using FFlowAssetParamsCustomization = TFlowDataPinValueOwnerCustomization; +using FFlowNodeBaseCustomization = TFlowDataPinValueOwnerCustomization; +using FFlowExecutableActorComponentCustomization = TFlowDataPinValueOwnerCustomization; diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueStandardCustomizations.h b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueStandardCustomizations.h new file mode 100644 index 000000000..0d8767e61 --- /dev/null +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueStandardCustomizations.h @@ -0,0 +1,27 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "FlowDataPinValueCustomization.h" +#include "Types/FlowDataPinValuesStandard.h" + +// Specialized customizations +#include "DetailCustomizations/FlowDataPinValueCustomization_Enum.h" +#include "DetailCustomizations/FlowDataPinValueCustomization_Class.h" +#include "DetailCustomizations/FlowDataPinValueCustomization_Object.h" + +// Scalar / simple using aliases +using FFlowDataPinValueCustomization_Bool = TFlowDataPinValueCustomization; +using FFlowDataPinValueCustomization_Int = TFlowDataPinValueCustomization; +using FFlowDataPinValueCustomization_Int64 = TFlowDataPinValueCustomization; +using FFlowDataPinValueCustomization_Float = TFlowDataPinValueCustomization; +using FFlowDataPinValueCustomization_Double = TFlowDataPinValueCustomization; +using FFlowDataPinValueCustomization_Name = TFlowDataPinValueCustomization; +using FFlowDataPinValueCustomization_String = TFlowDataPinValueCustomization; +using FFlowDataPinValueCustomization_Text = TFlowDataPinValueCustomization; +using FFlowDataPinValueCustomization_Vector = TFlowDataPinValueCustomization; +using FFlowDataPinValueCustomization_Rotator = TFlowDataPinValueCustomization; +using FFlowDataPinValueCustomization_Transform = TFlowDataPinValueCustomization; +using FFlowDataPinValueCustomization_GameplayTag = TFlowDataPinValueCustomization; +using FFlowDataPinValueCustomization_GameplayTagContainer = TFlowDataPinValueCustomization; +using FFlowDataPinValueCustomization_InstancedStruct = TFlowDataPinValueCustomization; diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowNamedDataPinOutputPropertyCustomization.h b/Source/FlowEditor/Public/DetailCustomizations/FlowNamedDataPinPropertyCustomization.h similarity index 92% rename from Source/FlowEditor/Public/DetailCustomizations/FlowNamedDataPinOutputPropertyCustomization.h rename to Source/FlowEditor/Public/DetailCustomizations/FlowNamedDataPinPropertyCustomization.h index f63e1e18b..c6a642bd6 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowNamedDataPinOutputPropertyCustomization.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowNamedDataPinPropertyCustomization.h @@ -4,8 +4,6 @@ #include "UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h" -#include "Types/FlowDataPinProperties.h" - // Details customization for FFlowPin class FFlowNamedDataPinPropertyCustomization : public IFlowExtendedPropertyTypeCustomization { diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowNodeAddOn_Details.h b/Source/FlowEditor/Public/DetailCustomizations/FlowNodeAddOn_Details.h index 05544d12b..423820ec3 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowNodeAddOn_Details.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowNodeAddOn_Details.h @@ -2,9 +2,13 @@ #pragma once +#include "FlowDataPinValueOwnerCustomization.h" #include "IDetailCustomization.h" +#include "Templates/SharedPointer.h" -class FFlowNodeAddOn_Details final : public IDetailCustomization +class UFlowNodeAddOn; + +class FFlowNodeAddOn_Details final : public TFlowDataPinValueOwnerCustomization { public: static TSharedRef MakeInstance() diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowNode_Details.h b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_Details.h index ce85a66d6..297d81b5f 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowNode_Details.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_Details.h @@ -2,9 +2,13 @@ #pragma once +#include "FlowDataPinValueOwnerCustomization.h" #include "IDetailCustomization.h" +#include "Templates/SharedPointer.h" -class FFlowNode_Details final : public IDetailCustomization +class UFlowNode; + +class FFlowNode_Details final : public TFlowDataPinValueOwnerCustomization { public: static TSharedRef MakeInstance() diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowPinCustomization.h b/Source/FlowEditor/Public/DetailCustomizations/FlowPinCustomization.h index 81fdde281..d73b8c7c7 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowPinCustomization.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowPinCustomization.h @@ -4,7 +4,7 @@ #include "UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h" -#include "Types/FlowDataPinProperties.h" +struct FFlowPin; // Details customization for FFlowPin class FFlowPinCustomization : public IFlowExtendedPropertyTypeCustomization diff --git a/Source/FlowEditor/Public/FlowEditorModule.h b/Source/FlowEditor/Public/FlowEditorModule.h index 018bfd314..8dec688cf 100644 --- a/Source/FlowEditor/Public/FlowEditorModule.h +++ b/Source/FlowEditor/Public/FlowEditorModule.h @@ -16,7 +16,7 @@ struct FGraphPanelPinConnectionFactory; class FFlowAssetEditor; class UFlowAsset; -struct FLOWEDITOR_API FFLowAssetCategoryPaths : EAssetCategoryPaths +struct FLOWEDITOR_API FFlowAssetCategoryPaths : EAssetCategoryPaths { static FAssetCategoryPath Flow; }; diff --git a/Source/FlowEditor/Public/Graph/FlowGraph.h b/Source/FlowEditor/Public/Graph/FlowGraph.h index 4c9decc2c..0fe54ae72 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraph.h +++ b/Source/FlowEditor/Public/Graph/FlowGraph.h @@ -21,6 +21,8 @@ class FLOWEDITOR_API UFlowGraph : public UEdGraph UPROPERTY() int32 GraphVersion; + static constexpr int32 CurrentGraphVersion = 2; + /** if set, graph modifications won't cause updates in internal tree structure * flag allows freezing update during heavy changes like pasting new nodes */ @@ -37,6 +39,8 @@ class FLOWEDITOR_API UFlowGraph : public UEdGraph void RefreshGraph(); protected: + void UpgradeAllFlowNodePins(); + void RecursivelyRefreshAddOns(UFlowGraphNode& FromFlowGraphNode); static void RecursivelySetupAllFlowGraphNodesForEditing(UFlowGraphNode& FromFlowGraphNode); diff --git a/Source/FlowEditor/Public/Graph/FlowGraphNodesPolicy.h b/Source/FlowEditor/Public/Graph/FlowGraphNodesPolicy.h new file mode 100644 index 000000000..1b40db35f --- /dev/null +++ b/Source/FlowEditor/Public/Graph/FlowGraphNodesPolicy.h @@ -0,0 +1,30 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "FlowGraphNodesPolicy.generated.h" + +class UFlowNodeBase; + +USTRUCT() +struct FFlowGraphNodesPolicy +{ + GENERATED_BODY(); + +public: +#if WITH_EDITORONLY_DATA + UPROPERTY(Config, EditAnywhere, Category = "Nodes") + TArray AllowedCategories; + + UPROPERTY(Config, EditAnywhere, Category = "Nodes") + TArray DisallowedCategories; +#endif + +#if WITH_EDITOR +public: + bool IsNodeAllowedByPolicy(const UFlowNodeBase* FlowNodeBase) const; + +protected: + static bool IsAnySubcategory(const FString& CheckCategory, const TArray& Categories); +#endif +}; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphPinFactory.h b/Source/FlowEditor/Public/Graph/FlowGraphPinFactory.h index 23f748d66..67b2f7204 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphPinFactory.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphPinFactory.h @@ -5,8 +5,6 @@ #include "EdGraphSchema_K2.h" #include "EdGraphUtilities.h" -#include "FlowGraphPinFactory.generated.h" - struct FFlowPin; class FFlowGraphPinFactory : public FGraphPanelPinFactory @@ -18,13 +16,3 @@ class FFlowGraphPinFactory : public FGraphPanelPinFactory static int32 GatherValidPinsCount(const TArray& Pins); }; - -// Thin subclass of UEdGraphSchema_K2 to gain access to PC_* defines so we can assert their values -UCLASS() -class UFlowK2SchemaSubclassForAccess : public UEdGraphSchema_K2 -{ - GENERATED_BODY() - -public: - static void AssertPinCategoryNames(); -}; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h index 070f82151..a912b60a4 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h @@ -6,6 +6,7 @@ #include "Runtime/Launch/Resources/Version.h" #include "Templates/SubclassOf.h" +#include "Asset/FlowPinTypeMatchPolicy.h" #include "FlowGraphSchema.generated.h" class UFlowAsset; @@ -13,6 +14,7 @@ class UFlowNode; class UFlowNodeAddOn; class UFlowNodeBase; class UFlowGraphNode; +struct FFlowPinType; DECLARE_MULTICAST_DELEGATE(FFlowGraphSchemaRefresh); @@ -31,11 +33,6 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema static TMap BlueprintFlowNodeAddOns; static TMap, TSubclassOf> GraphNodesByFlowNodes; - // cached pointers to struct types - static const UScriptStruct* VectorStruct; - static const UScriptStruct* RotatorStruct; - static const UScriptStruct* TransformStruct; - static bool bBlueprintCompilationPending; public: @@ -74,6 +71,16 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema // FlowGraphSchema + static const FFlowPinType* LookupDataPinTypeForPinCategory(const FName& PinCategory); + + void EnsurePinTypesInitialized(); + + bool ArePinSubCategoryObjectsCompatible( + const UStruct* OutputStruct, + const UStruct* InputStruct, + const FFlowPinTypeMatchPolicy& PinTypeMatchPolicy, + FPinConnectionResponse& OutConnectionResponse) const; + /** * Returns true if the two pin types are schema compatible. Handles outputting a more derived * type to an input pin expecting a less derived type. @@ -117,9 +124,16 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema static bool IsPIESimulating(); protected: - static UFlowGraphNode* CreateDefaultNode(UEdGraph& Graph, const TSubclassOf& NodeClass, const FVector2D& Offset, bool bPlacedAsGhostNode); - static bool ArePinCategoriesEffectivelyMatching(const FName& InputPinCategory, const FName& OutputPinCategory, bool bAllowImplicitCasts = true); + // These are the policies for matching data pin types + UPROPERTY(Transient) + TMap PinTypeMatchPolicies; + + // TODO (gtaylor) The mechanism for customizing PinTypeMatchPolicies will need some revision. + // I am going with a simple virtual method on schema For Now(tm) but expect a revision in how this is done, in the future. + virtual void InitializedPinTypes(); + + static UFlowGraphNode* CreateDefaultNode(UEdGraph& Graph, const TSubclassOf& NodeClass, const FVector2D& Offset, bool bPlacedAsGhostNode); private: static void ApplyNodeOrAddOnFilter(const UFlowAsset* AssetClassDefaults, const UClass* FlowNodeClass, TArray& FilteredNodes); diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h index 18a91a7a4..007d54c8f 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h @@ -7,6 +7,7 @@ #include "GameplayTagContainer.h" #include "FlowTypes.h" +#include "Graph/FlowGraphNodesPolicy.h" #include "FlowGraphSettings.generated.h" class UFlowNodeBase; @@ -98,6 +99,10 @@ class FLOWEDITOR_API UFlowGraphSettings : public UDeveloperSettings UPROPERTY(EditAnywhere, config, Category = "Nodes", meta = (ConfigRestartRequired = true)) TArray> NodesHiddenFromPalette; + /** Configurable map of FlowAsset subclasses to the FlowAssetNodePolicy for that subclass */ + UPROPERTY(EditAnywhere, Config, Category = "Nodes", meta = (ConfigRestartRequired = true, AllowedClasses = "/Script/Flow.FlowAsset")) + TMap PerAssetSubclassFlowNodePolicies; + /** Allows anyone to override Flow Palette category for specific nodes without modifying source code.*/ UPROPERTY(EditAnywhere, config, Category = "Nodes") TMap, FString> OverridenNodeCategories; @@ -184,6 +189,9 @@ class FLOWEDITOR_API UFlowGraphSettings : public UDeveloperSettings virtual FName GetCategoryName() const override { return FName("Flow Graph"); } virtual FText GetSectionText() const override { return INVTEXT("Graph Settings"); } + // Override-safe category query for flow node + static FString GetNodeCategoryForNode(const UFlowNodeBase& FlowNodeBase); + #if WITH_EDITOR const TMap& EnsureNodeDisplayStylesMap(); bool TryAddDefaultNodeDisplayStyle(const FFlowNodeDisplayStyleConfig& StyleConfig); diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h index a9d715862..c01ccef93 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h @@ -93,6 +93,8 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode */ void InsertNewNode(UEdGraphPin* FromPin, UEdGraphPin* NewLinkPin, TSet& OutNodeList); + void MarkNeedsFullReconstruction() { bNeedsFullReconstruction = true; } + // UEdGraphNode virtual void ReconstructNode() override; virtual void AllocateDefaultPins() override; @@ -157,7 +159,7 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode // -- virtual void OnNodeDoubleClicked() const; - virtual void OnNodeDoubleClickedInPIE() const {}; + virtual void OnNodeDoubleClickedInPIE() const {} /** check if node has any errors, used for assigning colors on graph */ virtual bool HasErrors() const; @@ -208,11 +210,6 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode // @return true, if pins cannot be connected due to node's inner logic, put message for user in OutReason virtual bool IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const { return false; } -protected: - // Gets the PinCategory from the FlowPin - // (accounting for FFlowPin structs that predate the PinCategory field) - static const FName& GetPinCategoryFromFlowPin(const FFlowPin& FlowPin); - ////////////////////////////////////////////////////////////////////////// // Execution Override diff --git a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h index a7977813b..2428d41b9 100644 --- a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h @@ -4,6 +4,7 @@ #include "SGraphNode.h" #include "KismetPins/SGraphPinExec.h" +#include "Runtime/Launch/Resources/Version.h" #include "Graph/Nodes/FlowGraphNode.h" @@ -35,10 +36,10 @@ class FLOWEDITOR_API SFlowGraphNode : public SGraphNode virtual void GetNodeInfoPopups(FNodeInfoContext* Context, TArray& Popups) const override; virtual const FSlateBrush* GetShadowBrush(bool bSelected) const override; - #if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 6 -virtual void GetOverlayBrushes(bool bSelected, const FVector2D WidgetSize, TArray& Brushes) const override; +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 6 + virtual void GetOverlayBrushes(bool bSelected, const FVector2D WidgetSize, TArray& Brushes) const override; #else -virtual void GetOverlayBrushes(bool bSelected, const FVector2f& WidgetSize, TArray& Brushes) const override; + virtual void GetOverlayBrushes(bool bSelected, const FVector2f& WidgetSize, TArray& Brushes) const override; #endif // -- diff --git a/Source/FlowEditor/Public/UnrealExtensions/IFlowCuratedNamePropertyCustomization.h b/Source/FlowEditor/Public/UnrealExtensions/IFlowCuratedNamePropertyCustomization.h index cabc2636a..6bd95250d 100644 --- a/Source/FlowEditor/Public/UnrealExtensions/IFlowCuratedNamePropertyCustomization.h +++ b/Source/FlowEditor/Public/UnrealExtensions/IFlowCuratedNamePropertyCustomization.h @@ -12,7 +12,7 @@ class FLOWEDITOR_API IFlowCuratedNamePropertyCustomization : public IFlowExtendedPropertyTypeCustomization { protected: - // IExtendedPropertyTypeCustomization + // IFlowExtendedPropertyTypeCustomization virtual void CreateHeaderRowWidget(FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; // --- @@ -34,9 +34,9 @@ class FLOWEDITOR_API IFlowCuratedNamePropertyCustomization : public IFlowExtende void AddToCachedTextList(const TSharedPtr Text); void InsertAtHeadOfCachedTextList(const TSharedPtr Text); - bool CustomIsResetToDefaultVisible(TSharedPtr Property) const; - void CustomResetToDefault(TSharedPtr Property); - bool CustomIsEnabled() const; + bool CustomIsResetToDefaultVisible(TSharedPtr Property) const { return CustomIsResetToDefaultVisibleImpl(Property); } + void CustomResetToDefault(TSharedPtr Property) { CustomResetToDefaultImpl(Property); } + bool CustomIsEnabled() const { return CustomIsEnabledImpl(); } // IFlowCuratedNamePropertyCustomization virtual TSharedPtr GetCuratedNamePropertyHandle() const = 0; @@ -44,6 +44,9 @@ class FLOWEDITOR_API IFlowCuratedNamePropertyCustomization : public IFlowExtende virtual bool TryGetCuratedName(FName& OutName) const = 0; virtual TArray GetCuratedNameOptions() const = 0; virtual bool AllowNameNoneIfOtherOptionsExist() const { return true; } + virtual bool CustomIsResetToDefaultVisibleImpl(TSharedPtr Property) const; + virtual void CustomResetToDefaultImpl(TSharedPtr Property); + virtual bool CustomIsEnabledImpl() const; // --- public: diff --git a/Source/FlowEditor/Public/UnrealExtensions/VisibilityArrayBuilder.h b/Source/FlowEditor/Public/UnrealExtensions/VisibilityArrayBuilder.h new file mode 100644 index 000000000..f73e536b1 --- /dev/null +++ b/Source/FlowEditor/Public/UnrealExtensions/VisibilityArrayBuilder.h @@ -0,0 +1,278 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "IDetailCustomNodeBuilder.h" +#include "IDetailChildrenBuilder.h" +#include "DetailWidgetRow.h" +#include "PropertyHandle.h" + +#include "Widgets/DeclarativeSyntaxSupport.h" +#include "Widgets/SBoxPanel.h" +#include "Widgets/Text/STextBlock.h" +#include "PropertyCustomizationHelpers.h" +#include "SResetToDefaultMenu.h" + +DECLARE_DELEGATE_FourParams(FOnGenerateArrayElementWidgetVisible, + TSharedRef, // ElementHandle + int32, // Index + IDetailChildrenBuilder&, // ChildrenBuilder + const TAttribute&); // RowVisibility + +/* +* FVisibilityArrayBuilder +* +* A fork of UE's FDetailArrayBuilder that: +* - Adds a live visibility getter via SetVisibilityGetter(TFunction) +* - Uses a 4‑parameter element generation delegate: +* (ElementHandle, Index, ChildrenBuilder, RowVisibility) +* - Works around engine behavior where setting NodeRow.Visibility() on a custom node +* header does not always live‑update by wrapping the header name & value widgets +* in SBoxes whose Visibility attributes are dynamic. +* +* Usage: +* TSharedRef ArrayBuilder = +* MakeShareable(new FVisibilityArrayBuilder(ValuesHandle.ToSharedRef(), true, true, true)); +* +* ArrayBuilder->SetVisibilityGetter([this]() +* { +* uint8 Raw = 0; +* if (MultiTypeHandle.IsValid() && +* MultiTypeHandle->GetValue(Raw) == FPropertyAccess::Success && +* (EFlowDataMultiType)Raw == EFlowDataMultiType::Array) +* { +* return EVisibility::Visible; +* } +* return EVisibility::Collapsed; +* }); +* +* ArrayBuilder->OnGenerateArrayElementWidget( +* FOnGenerateArrayElementWidgetVisible::CreateSP( +* this, &FFlowDataPinValueCustomization_Enum::GenerateArrayElementVisible)); +* +* StructBuilder.AddCustomBuilder(ArrayBuilder); +* +* Notes: +* - Structural changes (Add / Remove / Reorder) still require RequestRefresh() to rebuild rows. +* - The dynamic visibility lambda MUST read the property handle each time (no cached enum mode). +* - If you want to supply your own header (e.g. to insert custom buttons), construct with +* bInGenerateHeader = false +* and add a separate AddCustomRow() above the builder with its own .Visibility binding. +*/ + +class FVisibilityArrayBuilder : public IDetailCustomNodeBuilder +{ +public: + FVisibilityArrayBuilder(TSharedRef InBaseProperty, + bool bInGenerateHeader = true, + bool bInDisplayResetToDefault = true, + bool bInDisplayElementNum = true) + : ArrayProperty(InBaseProperty->AsArray()) + , BaseProperty(InBaseProperty) + , bGenerateHeader(bInGenerateHeader) + , bDisplayResetToDefault(bInDisplayResetToDefault) + , bDisplayElementNum(bInDisplayElementNum) + { + check(ArrayProperty.IsValid()); + + FSimpleDelegate OnNumChildrenChanged = + FSimpleDelegate::CreateRaw(this, &FVisibilityArrayBuilder::OnNumChildrenChanged); + OnNumElementsChangedHandle = ArrayProperty->SetOnNumElementsChanged(OnNumChildrenChanged); + + // Hide original property presentation so only our custom builder is shown. + BaseProperty->MarkHiddenByCustomization(); + } + + ~FVisibilityArrayBuilder() + { + if (ArrayProperty.IsValid()) + { + ArrayProperty->UnregisterOnNumElementsChanged(OnNumElementsChangedHandle); + } + } + + // Non-copyable / non-movable: avoid duplicate delegate registrations + FVisibilityArrayBuilder(const FVisibilityArrayBuilder&) = delete; + FVisibilityArrayBuilder& operator=(const FVisibilityArrayBuilder&) = delete; + FVisibilityArrayBuilder(FVisibilityArrayBuilder&&) = delete; + FVisibilityArrayBuilder& operator=(FVisibilityArrayBuilder&&) = delete; + + // Assign a visibility callback (evaluated whenever Slate queries the attribute). + FVisibilityArrayBuilder& SetVisibilityGetter(TFunction&& InGetter) + { + VisibilityGetter = MoveTemp(InGetter); + return *this; + } + + void SetDisplayName(const FText& InDisplayName) + { + DisplayName = InDisplayName; + } + + void OnGenerateArrayElementWidget(FOnGenerateArrayElementWidgetVisible InDelegate) + { + OnGenerateArrayElementWidgetDelegate = InDelegate; + } + + // IDetailCustomNodeBuilder interface + virtual bool RequiresTick() const override { return false; } + virtual void Tick(float /*DeltaTime*/) override {} + + virtual FName GetName() const override + { + return BaseProperty->GetProperty()->GetFName(); + } + + virtual bool InitiallyCollapsed() const override { return false; } + + virtual void GenerateHeaderRowContent(FDetailWidgetRow& NodeRow) override + { + if (!bGenerateHeader) + { + return; + } + + // Dynamic visibility attribute for header contents (NOT on NodeRow itself). + TAttribute DynamicVis = MakeDynamicVisibilityAttribute(); + + // Horizontal box for value content (mirrors stock array builder's layout). + TSharedPtr ContentHorizontalBox; + SAssignNew(ContentHorizontalBox, SHorizontalBox); + + if (bDisplayElementNum) + { + ContentHorizontalBox->AddSlot() + [ + BaseProperty->CreatePropertyValueWidget() + ]; + } + + FUIAction CopyAction; + FUIAction PasteAction; + BaseProperty->CreateDefaultPropertyCopyPasteActions(CopyAction, PasteAction); + + NodeRow + // Leave NodeRow itself always present; hide internal widgets via SBox visibility. + .FilterString(!DisplayName.IsEmpty() ? DisplayName : BaseProperty->GetPropertyDisplayName()) + .NameContent() + [ + SNew(SBox) + .Visibility(DynamicVis) + [ + BaseProperty->CreatePropertyNameWidget(DisplayName, FText::GetEmpty()) + ] + ] + .ValueContent() + [ + SNew(SBox) + .Visibility(DynamicVis) + [ + ContentHorizontalBox.ToSharedRef() + ] + ] + .CopyAction(CopyAction) + .PasteAction(PasteAction); + + if (bDisplayResetToDefault) + { + TSharedPtr ResetToDefaultMenu; + ContentHorizontalBox->AddSlot() + .AutoWidth() + .Padding(FMargin(2.f, 0.f, 0.f, 0.f)) + [ + SAssignNew(ResetToDefaultMenu, SResetToDefaultMenu) + ]; + ResetToDefaultMenu->AddProperty(BaseProperty); + } + } + + virtual void GenerateChildContent(IDetailChildrenBuilder& ChildrenBuilder) override + { + uint32 NumChildren = 0; + ArrayProperty->GetNumElements(NumChildren); + + TAttribute DynamicVis = MakeDynamicVisibilityAttribute(); + + for (uint32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex) + { + TSharedRef ElementHandle = ArrayProperty->GetElement(ChildIndex); + + if (OnGenerateArrayElementWidgetDelegate.IsBound()) + { + OnGenerateArrayElementWidgetDelegate.Execute( + ElementHandle, + static_cast(ChildIndex), + ChildrenBuilder, + DynamicVis); + } + else + { + IDetailPropertyRow& Row = ChildrenBuilder.AddProperty(ElementHandle); + Row.Visibility(DynamicVis); + } + } + } + + // Manual refresh (not virtual in some engine versions) + void RefreshChildren() + { + OnRebuildChildren.ExecuteIfBound(); + } + + virtual TSharedPtr GetPropertyHandle() const + { + return BaseProperty; + } + +protected: + virtual void SetOnRebuildChildren(FSimpleDelegate InOnRebuildChildren) override + { + OnRebuildChildren = InOnRebuildChildren; + } + + void OnNumChildrenChanged() + { + OnRebuildChildren.ExecuteIfBound(); + } + +private: + TAttribute MakeDynamicVisibilityAttribute() const + { + // Optimization: if no custom getter, return a simple constant attribute (no lambda capture) + if (!VisibilityGetter) + { + return TAttribute(EVisibility::Visible); + } + + // Capture 'this' only when needed + return TAttribute::CreateLambda([this]() + { + return VisibilityGetter(); + }); + } + +private: + // Display name override + FText DisplayName; + + // Element generator + FOnGenerateArrayElementWidgetVisible OnGenerateArrayElementWidgetDelegate; + + // Array + base property handles + TSharedPtr ArrayProperty; + TSharedRef BaseProperty; + + // Rebuild delegate + FSimpleDelegate OnRebuildChildren; + + // Visibility getter (live) + TFunction VisibilityGetter; + + // Config + bool bGenerateHeader; + bool bDisplayResetToDefault; + bool bDisplayElementNum; + + // Delegate handle for array size changes + FDelegateHandle OnNumElementsChangedHandle; +}; \ No newline at end of file From 48ce4a231ac302ad15734d2b0ed74bc5c02962c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sun, 11 Jan 2026 22:37:16 +0100 Subject: [PATCH 372/485] compilation fix --- Source/Flow/Public/FlowPinSubsystem.h | 5 ++--- Source/Flow/Public/Types/FlowPinType.h | 4 +++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Source/Flow/Public/FlowPinSubsystem.h b/Source/Flow/Public/FlowPinSubsystem.h index 66fff9a38..23660f635 100644 --- a/Source/Flow/Public/FlowPinSubsystem.h +++ b/Source/Flow/Public/FlowPinSubsystem.h @@ -3,14 +3,13 @@ #pragma once #include "Subsystems/EngineSubsystem.h" -#include "Types/FlowPinType.h" #include "StructUtils/InstancedStruct.h" #include "Templates/UnrealTypeTraits.h" +#include "Types/FlowPinType.h" +#include "Types/FlowPinTypeName.h" #include "FlowPinSubsystem.generated.h" -struct FFlowPinTypeName; - UCLASS(MinimalApi) class UFlowPinSubsystem : public UEngineSubsystem { diff --git a/Source/Flow/Public/Types/FlowPinType.h b/Source/Flow/Public/Types/FlowPinType.h index 798b93d5d..a9201d41c 100644 --- a/Source/Flow/Public/Types/FlowPinType.h +++ b/Source/Flow/Public/Types/FlowPinType.h @@ -15,11 +15,13 @@ class FFormatArgumentValue; class IPropertyHandle; + class UFlowNodeBase; class UFlowNode; struct FFlowDataPinResult; -struct FFlowPin; struct FFlowDataPinValue; +struct FFlowPin; +struct FFlowPinTypeName; USTRUCT(BlueprintType) struct FFlowPinType From 11866ae791e746f22fe78aba3ac75e015539294c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sun, 11 Jan 2026 22:41:13 +0100 Subject: [PATCH 373/485] cleanup --- Source/Flow/Public/Types/FlowDataPinValue.h | 1 - Source/Flow/Public/Types/FlowPinTypeName.h | 21 ++++++++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Source/Flow/Public/Types/FlowDataPinValue.h b/Source/Flow/Public/Types/FlowDataPinValue.h index 983310c7c..737222faa 100644 --- a/Source/Flow/Public/Types/FlowDataPinValue.h +++ b/Source/Flow/Public/Types/FlowDataPinValue.h @@ -3,7 +3,6 @@ #pragma once #include "UObject/NameTypes.h" -#include "UObject/ObjectPtr.h" #include "FlowPinEnums.h" #include "FlowPinType.h" diff --git a/Source/Flow/Public/Types/FlowPinTypeName.h b/Source/Flow/Public/Types/FlowPinTypeName.h index 1531c289c..ace66576e 100644 --- a/Source/Flow/Public/Types/FlowPinTypeName.h +++ b/Source/Flow/Public/Types/FlowPinTypeName.h @@ -3,7 +3,6 @@ #pragma once #include "UObject/NameTypes.h" - #include "FlowPinTypeName.generated.h" USTRUCT(BlueprintType) @@ -16,9 +15,21 @@ struct FFlowPinTypeName FName Name = NAME_None; FFlowPinTypeName() = default; - explicit FFlowPinTypeName(const TCHAR* InPinName) : Name(FName(InPinName)) {} - explicit FFlowPinTypeName(const FName& InName) : Name(InName) {} - explicit FFlowPinTypeName(const FString& InString) : Name(FName(InString)) {} + + explicit FFlowPinTypeName(const TCHAR* InPinName) + : Name(FName(InPinName)) + { + } + + explicit FFlowPinTypeName(const FName& InName) + : Name(InName) + { + } + + explicit FFlowPinTypeName(const FString& InString) + : Name(FName(InString)) + { + } friend inline uint32 GetTypeHash(const FFlowPinTypeName& PinTypeName) { @@ -30,4 +41,4 @@ struct FFlowPinTypeName FString ToString() const { return Name.ToString(); } bool IsNone() const { return Name.IsNone(); } -}; \ No newline at end of file +}; From af300a9bba9da080aeb13408ddc30c2f4dd9c763 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sun, 11 Jan 2026 22:46:57 +0100 Subject: [PATCH 374/485] UE 5.5 compilation fixes --- .../Types/FlowDataPinBlueprintLibrary.cpp | 2 +- .../Public/Types/FlowDataPinValuesStandard.h | 34 +++++++++---------- .../Private/Graph/Widgets/SFlowGraphNode.cpp | 5 +++ 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/Source/Flow/Private/Types/FlowDataPinBlueprintLibrary.cpp b/Source/Flow/Private/Types/FlowDataPinBlueprintLibrary.cpp index 373bf0ae2..383e04522 100644 --- a/Source/Flow/Private/Types/FlowDataPinBlueprintLibrary.cpp +++ b/Source/Flow/Private/Types/FlowDataPinBlueprintLibrary.cpp @@ -2151,7 +2151,7 @@ FSoftClassPath UFlowDataPinBlueprintLibrary::GetClassValue(const FFlowDataPinVal return ClassDataPinValue.Values[Index]; } UE_LOG(LogFlow, Error, TEXT("Insufficient values in Class Data Pin Value.")); - return nullptr; + return FSoftClassPath(); } TArray UFlowDataPinBlueprintLibrary::GetClassValues(FFlowDataPinValue_Class& ClassDataPinValue) diff --git a/Source/Flow/Public/Types/FlowDataPinValuesStandard.h b/Source/Flow/Public/Types/FlowDataPinValuesStandard.h index 462c62739..bfe5a18a2 100644 --- a/Source/Flow/Public/Types/FlowDataPinValuesStandard.h +++ b/Source/Flow/Public/Types/FlowDataPinValuesStandard.h @@ -19,7 +19,7 @@ //====================================================================== // Bool //====================================================================== -USTRUCT(MinimalApi, BlueprintType, DisplayName = "Bool - Flow DataPin Value", meta = (FlowPinType = "Bool", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructBool")) +USTRUCT(BlueprintType, DisplayName = "Bool - Flow DataPin Value", meta = (FlowPinType = "Bool", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructBool")) struct FFlowDataPinValue_Bool : public FFlowDataPinValue { GENERATED_BODY() @@ -42,7 +42,7 @@ struct FFlowDataPinValue_Bool : public FFlowDataPinValue //====================================================================== // Int (int32) //====================================================================== -USTRUCT(MinimalApi, BlueprintType, DisplayName = "Int - Flow DataPin Value", meta = (FlowPinType = "Int", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructInt")) +USTRUCT(BlueprintType, DisplayName = "Int - Flow DataPin Value", meta = (FlowPinType = "Int", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructInt")) struct FFlowDataPinValue_Int : public FFlowDataPinValue { GENERATED_BODY() @@ -65,7 +65,7 @@ struct FFlowDataPinValue_Int : public FFlowDataPinValue //====================================================================== // Int64 //====================================================================== -USTRUCT(MinimalApi, BlueprintType, DisplayName = "Int64 - Flow DataPin Value", meta = (FlowPinType = "Int64", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructInt64")) +USTRUCT(BlueprintType, DisplayName = "Int64 - Flow DataPin Value", meta = (FlowPinType = "Int64", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructInt64")) struct FFlowDataPinValue_Int64 : public FFlowDataPinValue { GENERATED_BODY() @@ -88,7 +88,7 @@ struct FFlowDataPinValue_Int64 : public FFlowDataPinValue //====================================================================== // Float //====================================================================== -USTRUCT(MinimalApi, BlueprintType, DisplayName = "Float - Flow DataPin Value", meta = (FlowPinType = "Float", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructFloat")) +USTRUCT(BlueprintType, DisplayName = "Float - Flow DataPin Value", meta = (FlowPinType = "Float", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructFloat")) struct FFlowDataPinValue_Float : public FFlowDataPinValue { GENERATED_BODY() @@ -111,7 +111,7 @@ struct FFlowDataPinValue_Float : public FFlowDataPinValue //====================================================================== // Double //====================================================================== -USTRUCT(MinimalApi, BlueprintType, DisplayName = "Double - Flow DataPin Value", meta = (FlowPinType = "Double", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructDouble")) +USTRUCT(BlueprintType, DisplayName = "Double - Flow DataPin Value", meta = (FlowPinType = "Double", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructDouble")) struct FFlowDataPinValue_Double : public FFlowDataPinValue { GENERATED_BODY() @@ -134,7 +134,7 @@ struct FFlowDataPinValue_Double : public FFlowDataPinValue //====================================================================== // Name //====================================================================== -USTRUCT(MinimalApi, BlueprintType, DisplayName = "Name - Flow DataPin Value", meta = (FlowPinType = "Name", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructName")) +USTRUCT(BlueprintType, DisplayName = "Name - Flow DataPin Value", meta = (FlowPinType = "Name", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructName")) struct FFlowDataPinValue_Name : public FFlowDataPinValue { GENERATED_BODY() @@ -157,7 +157,7 @@ struct FFlowDataPinValue_Name : public FFlowDataPinValue //====================================================================== // String //====================================================================== -USTRUCT(MinimalApi, BlueprintType, DisplayName = "String - Flow DataPin Value", meta = (FlowPinType = "String", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructString")) +USTRUCT(BlueprintType, DisplayName = "String - Flow DataPin Value", meta = (FlowPinType = "String", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructString")) struct FFlowDataPinValue_String : public FFlowDataPinValue { GENERATED_BODY() @@ -180,7 +180,7 @@ struct FFlowDataPinValue_String : public FFlowDataPinValue //====================================================================== // Text //====================================================================== -USTRUCT(MinimalApi, BlueprintType, DisplayName = "Text - Flow DataPin Value", meta = (FlowPinType = "Text", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructText")) +USTRUCT(BlueprintType, DisplayName = "Text - Flow DataPin Value", meta = (FlowPinType = "Text", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructText")) struct FFlowDataPinValue_Text : public FFlowDataPinValue { GENERATED_BODY() @@ -203,7 +203,7 @@ struct FFlowDataPinValue_Text : public FFlowDataPinValue //====================================================================== // Enum //====================================================================== -USTRUCT(MinimalApi, BlueprintType, DisplayName = "Enum - Flow DataPin Value", meta = (FlowPinType = "Enum", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructEnum")) +USTRUCT(BlueprintType, DisplayName = "Enum - Flow DataPin Value", meta = (FlowPinType = "Enum", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructEnum")) struct FFlowDataPinValue_Enum : public FFlowDataPinValue { GENERATED_BODY() @@ -299,7 +299,7 @@ struct FFlowDataPinValue_Enum : public FFlowDataPinValue //====================================================================== // Vector //====================================================================== -USTRUCT(MinimalApi, BlueprintType, DisplayName = "Vector - Flow DataPin Value", meta = (FlowPinType = "Vector", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructVector")) +USTRUCT(BlueprintType, DisplayName = "Vector - Flow DataPin Value", meta = (FlowPinType = "Vector", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructVector")) struct FFlowDataPinValue_Vector : public FFlowDataPinValue { GENERATED_BODY() @@ -322,7 +322,7 @@ struct FFlowDataPinValue_Vector : public FFlowDataPinValue //====================================================================== // Rotator //====================================================================== -USTRUCT(MinimalApi, BlueprintType, DisplayName = "Rotator - Flow DataPin Value", meta = (FlowPinType = "Rotator", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructRotator")) +USTRUCT(BlueprintType, DisplayName = "Rotator - Flow DataPin Value", meta = (FlowPinType = "Rotator", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructRotator")) struct FFlowDataPinValue_Rotator : public FFlowDataPinValue { GENERATED_BODY() @@ -345,7 +345,7 @@ struct FFlowDataPinValue_Rotator : public FFlowDataPinValue //====================================================================== // Transform //====================================================================== -USTRUCT(MinimalApi, BlueprintType, DisplayName = "Transform - Flow DataPin Value", meta = (FlowPinType = "Transform", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructTransform")) +USTRUCT(BlueprintType, DisplayName = "Transform - Flow DataPin Value", meta = (FlowPinType = "Transform", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructTransform")) struct FFlowDataPinValue_Transform : public FFlowDataPinValue { GENERATED_BODY() @@ -368,7 +368,7 @@ struct FFlowDataPinValue_Transform : public FFlowDataPinValue //====================================================================== // GameplayTag //====================================================================== -USTRUCT(MinimalApi, BlueprintType, DisplayName = "GameplayTag - Flow DataPin Value", meta = (FlowPinType = "GameplayTag", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructGameplayTag")) +USTRUCT(BlueprintType, DisplayName = "GameplayTag - Flow DataPin Value", meta = (FlowPinType = "GameplayTag", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructGameplayTag")) struct FFlowDataPinValue_GameplayTag : public FFlowDataPinValue { GENERATED_BODY() @@ -391,7 +391,7 @@ struct FFlowDataPinValue_GameplayTag : public FFlowDataPinValue //====================================================================== // GameplayTagContainer //====================================================================== -USTRUCT(MinimalApi, BlueprintType, DisplayName = "GameplayTagContainer - Flow DataPin Value", meta = (FlowPinType = "GameplayTagContainer", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructGameplayTagContainer")) +USTRUCT(BlueprintType, DisplayName = "GameplayTagContainer - Flow DataPin Value", meta = (FlowPinType = "GameplayTagContainer", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructGameplayTagContainer")) struct FFlowDataPinValue_GameplayTagContainer : public FFlowDataPinValue { GENERATED_BODY() @@ -416,7 +416,7 @@ struct FFlowDataPinValue_GameplayTagContainer : public FFlowDataPinValue //====================================================================== // InstancedStruct //====================================================================== -USTRUCT(MinimalApi, BlueprintType, DisplayName = "InstancedStruct - Flow DataPin Value", meta = (FlowPinType = "InstancedStruct", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructInstancedStruct")) +USTRUCT(BlueprintType, DisplayName = "InstancedStruct - Flow DataPin Value", meta = (FlowPinType = "InstancedStruct", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructInstancedStruct")) struct FFlowDataPinValue_InstancedStruct : public FFlowDataPinValue { GENERATED_BODY() @@ -438,7 +438,7 @@ struct FFlowDataPinValue_InstancedStruct : public FFlowDataPinValue //====================================================================== // Object //====================================================================== -USTRUCT(MinimalApi, BlueprintType, DisplayName = "Object - Flow DataPin Value", meta = (FlowPinType = "Object", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructObject")) +USTRUCT(BlueprintType, DisplayName = "Object - Flow DataPin Value", meta = (FlowPinType = "Object", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructObject")) struct FFlowDataPinValue_Object : public FFlowDataPinValue { GENERATED_BODY() @@ -468,7 +468,7 @@ struct FFlowDataPinValue_Object : public FFlowDataPinValue //====================================================================== // Class //====================================================================== -USTRUCT(MinimalApi, BlueprintType, DisplayName = "Class - Flow DataPin Value", meta = (FlowPinType = "Class", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructClass")) +USTRUCT(BlueprintType, DisplayName = "Class - Flow DataPin Value", meta = (FlowPinType = "Class", HasNativeMake = "/Script/Flow.FlowDataPinBlueprintLibrary.MakeStructClass")) struct FFlowDataPinValue_Class : public FFlowDataPinValue { GENERATED_BODY() diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp index 0f81baeaf..b6d4cfea7 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp @@ -375,8 +375,13 @@ void SFlowGraphNode::UpdateGraphNode() .IsGraphNodeHovered(this, &SGraphNode::IsHovered); GetOrAddSlot(ENodeZone::TopCenter) +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 6 + .SlotOffset(TAttribute(CommentBubble.Get(), &SCommentBubble::GetOffset)) + .SlotSize(TAttribute(CommentBubble.Get(), &SCommentBubble::GetSize)) +#else .SlotOffset2f(TAttribute(CommentBubble.Get(), &SCommentBubble::GetOffset2f)) .SlotSize2f(TAttribute(CommentBubble.Get(), &SCommentBubble::GetSize2f)) +#endif .AllowScaling(TAttribute(CommentBubble.Get(), &SCommentBubble::IsScalingAllowed)) .VAlign(VAlign_Top) [ From 22f316af76d2960890c706d39f08b79621d66b81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sat, 17 Jan 2026 15:51:00 +0100 Subject: [PATCH 375/485] Flow plugin is now DISABLED BY DEFAULT Change suggested Riot Games, but the idea isn't new. This plugin tends to be part of codebase shared between projects in studios. In that case, it is desired to have this plugin as part of a shared codebase or repository, but it's not desired to have the plugin enabled for every project Update Flow.uplugin --- Flow.uplugin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.uplugin b/Flow.uplugin index ec3fc0671..f4eb2d4c3 100644 --- a/Flow.uplugin +++ b/Flow.uplugin @@ -8,7 +8,7 @@ "DocsURL" : "https://github.com/MothCocoon/FlowGraph/wiki", "MarketplaceURL" : "", "SupportURL": "https://discord.gg/Xmtr6GhbmW", - "EnabledByDefault" : true, + "EnabledByDefault" : false, "CanContainContent" : false, "IsBetaVersion" : false, "Installed" : false, From 8e4ee160b8c21fc42eb176294c2672498aee84dd Mon Sep 17 00:00:00 2001 From: CodingBot <20263103+ameaninglessname@users.noreply.github.com> Date: Sun, 18 Jan 2026 22:59:55 +0800 Subject: [PATCH 376/485] Fixed: build error when making installed build (#312) --- Source/Flow/Public/Types/FlowArray.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Flow/Public/Types/FlowArray.h b/Source/Flow/Public/Types/FlowArray.h index b5817ca53..3d770c4f1 100644 --- a/Source/Flow/Public/Types/FlowArray.h +++ b/Source/Flow/Public/Types/FlowArray.h @@ -5,6 +5,7 @@ #include "Algo/Unique.h" #include "Containers/Array.h" #include "Math/RandomStream.h" +#include "Templates/Greater.h" namespace FlowArray { @@ -87,4 +88,4 @@ namespace FlowArray return ValueString; } -} \ No newline at end of file +} From bc7ac977a849625e33c94cbf8ce43582dc95fe26 Mon Sep 17 00:00:00 2001 From: Mor Ohana Date: Sun, 18 Jan 2026 17:05:09 +0200 Subject: [PATCH 377/485] Fix OnComponentNotify node not using identity tag match type (#315) --- .../Actor/FlowNode_OnNotifyFromActor.cpp | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/Source/Flow/Private/Nodes/Actor/FlowNode_OnNotifyFromActor.cpp b/Source/Flow/Private/Nodes/Actor/FlowNode_OnNotifyFromActor.cpp index b6f073671..c34d00118 100644 --- a/Source/Flow/Private/Nodes/Actor/FlowNode_OnNotifyFromActor.cpp +++ b/Source/Flow/Private/Nodes/Actor/FlowNode_OnNotifyFromActor.cpp @@ -35,7 +35,25 @@ void UFlowNode_OnNotifyFromActor::ForgetActor(TWeakObjectPtr Actor, TWea void UFlowNode_OnNotifyFromActor::OnNotifyFromComponent(UFlowComponent* Component, const FGameplayTag& Tag) { - if (Component->IdentityTags.HasAnyExact(IdentityTags) && (!NotifyTags.IsValid() || NotifyTags.HasTagExact(Tag))) + bool IdentityMatches = false; + + switch (IdentityMatchType) + { + case EFlowTagContainerMatchType::HasAny: + IdentityMatches = Component->IdentityTags.HasAny(IdentityTags); + break; + case EFlowTagContainerMatchType::HasAnyExact: + IdentityMatches = Component->IdentityTags.HasAnyExact(IdentityTags); + break; + case EFlowTagContainerMatchType::HasAll: + IdentityMatches = Component->IdentityTags.HasAll(IdentityTags); + break; + case EFlowTagContainerMatchType::HasAllExact: + IdentityMatches = Component->IdentityTags.HasAllExact(IdentityTags); + break; + } + + if (IdentityMatches && (!NotifyTags.IsValid() || NotifyTags.HasTagExact(Tag))) { OnEventReceived(); } From b1493048fa0f0882750d30fe3454bbf69656fa00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gregor=20S=C3=B6nnichsen?= <42077794+gregorhcs@users.noreply.github.com> Date: Sun, 18 Jan 2026 16:11:53 +0100 Subject: [PATCH 378/485] Made IsBoundToWorld() const (#321) --- Source/Flow/Private/FlowAsset.cpp | 2 +- Source/Flow/Public/FlowAsset.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index df435c480..c594e4767 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -1242,7 +1242,7 @@ void UFlowAsset::OnLoad_Implementation() { } -bool UFlowAsset::IsBoundToWorld_Implementation() +bool UFlowAsset::IsBoundToWorld_Implementation() const { return bWorldBound; } diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index d562b8d8b..bf67f0c37 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -421,7 +421,7 @@ class FLOW_API UFlowAsset : public UObject public: UFUNCTION(BlueprintNativeEvent, Category = "SaveGame") - bool IsBoundToWorld(); + bool IsBoundToWorld() const; ////////////////////////////////////////////////////////////////////////// // FlowAssetParams support (Start node params for a flow graph) From c8563ec868d0d17d95c1a094de59e4afa5ccc85a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gregor=20S=C3=B6nnichsen?= <42077794+gregorhcs@users.noreply.github.com> Date: Sun, 18 Jan 2026 16:14:15 +0100 Subject: [PATCH 379/485] Exposed flow subsystem runtime state fields to subclasses (#322) --- Source/Flow/Public/FlowSubsystem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Flow/Public/FlowSubsystem.h b/Source/Flow/Public/FlowSubsystem.h index 16bd02571..492742214 100644 --- a/Source/Flow/Public/FlowSubsystem.h +++ b/Source/Flow/Public/FlowSubsystem.h @@ -37,7 +37,7 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem friend class UFlowComponent; friend class UFlowNode_SubGraph; -private: +protected: /* All asset templates with active instances */ UPROPERTY() TArray> InstancedTemplates; From 409b1fcaae817886add861acc9df38d317891467 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gregor=20S=C3=B6nnichsen?= <42077794+gregorhcs@users.noreply.github.com> Date: Sun, 18 Jan 2026 16:16:15 +0100 Subject: [PATCH 380/485] Fixed rare crash in LogError(..) caused by invalid flow node self or owner (#323) --- Source/Flow/Private/Nodes/FlowNodeBase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp index cfd97e4cd..fd80a5977 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBase.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -834,7 +834,7 @@ void UFlowNodeBase::LogError(FString Message, const EFlowOnScreenMessageType OnS StatsSubsystem->AddDisplayDelegate([WeakThis = TWeakObjectPtr(this), Message](FText& OutText, FLinearColor& OutColor) { const UFlowNodeBase* ThisPtr = WeakThis.Get(); - if (ThisPtr && ThisPtr->GetFlowNodeSelfOrOwner()->GetActivationState() != EFlowNodeState::NeverActivated) + if (ThisPtr && ThisPtr->GetFlowNodeSelfOrOwner() && ThisPtr->GetFlowNodeSelfOrOwner()->GetActivationState() != EFlowNodeState::NeverActivated) { OutText = FText::FromString(Message); OutColor = FLinearColor::Red; From 15b96ea351cfac2a617f345ccd6d7f889d3e0d10 Mon Sep 17 00:00:00 2001 From: MaksymKapelianovych <48297221+MaksymKapelianovych@users.noreply.github.com> Date: Tue, 27 Jan 2026 00:09:38 +0200 Subject: [PATCH 381/485] Flow debugger update (#282) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Functional changes - Open the Flow Asset editor on breakpoint hit instead of just freezing. - Node breakpoint is no longer triggered if there is a triggered breakpoint for any pin at the same time. - Added Debug Menu allowing to enable/disable/remove all breakpoints in the graph. - Added initial support for filtering out inspected instances by world. This is primarily for multiplayer to separate server instances from client ones. - Select the correct flow asset instance, instead of the first one spawned with the same name in multiplayer. Visual changes - In the top right corner of the graph, the PIE status will be displayed, based on the selected world/instance. - Visual changes for breadcrumbs (added trailing delimiter, background color, max width). - Clicking on a breadcrumb now will focus the SubGraph node that created the inspected instance. - The button "Go to Parent" was removed from the toolbar (the same can be achieved with breadcrumbs), but the command was kept to retain the ability to use a shortcut for this action. --------- Co-authored-by: Krzysiek Justyński --- Source/Flow/Private/FlowAsset.cpp | 58 ++-- Source/Flow/Private/Nodes/FlowNode.cpp | 4 +- Source/Flow/Public/FlowAsset.h | 11 +- .../Debugger/FlowDebuggerSubsystem.cpp | 137 +++++++- .../Public/Debugger/FlowDebuggerSubsystem.h | 20 +- .../Private/Asset/FlowAssetEditor.cpp | 170 +++++----- .../Private/Asset/FlowAssetToolbar.cpp | 275 ++++++++++++---- .../Asset/FlowDebugEditorSubsystem.cpp | 28 +- .../FlowEditor/Private/FlowEditorCommands.cpp | 6 +- .../FlowEditor/Private/FlowEditorModule.cpp | 6 - Source/FlowEditor/Private/FlowEditorStyle.cpp | 2 - .../Private/Graph/FlowGraphEditor.cpp | 303 +++++++++++------- .../Private/Graph/FlowGraphUtils.cpp | 19 +- .../Graph/Nodes/FlowGraphNode_SubGraph.cpp | 2 +- .../FlowEditor/Public/Asset/FlowAssetEditor.h | 1 - .../Public/Asset/FlowAssetToolbar.h | 80 +++-- .../Public/Asset/FlowDebugEditorSubsystem.h | 2 +- Source/FlowEditor/Public/FlowEditorCommands.h | 7 +- Source/FlowEditor/Public/FlowEditorModule.h | 10 +- .../FlowEditor/Public/Graph/FlowGraphEditor.h | 11 + .../FlowEditor/Public/Graph/FlowGraphUtils.h | 2 + 21 files changed, 797 insertions(+), 357 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index c594e4767..c4b1305ca 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -20,6 +20,7 @@ #include "Engine/World.h" #include "Serialization/MemoryReader.h" #include "Serialization/MemoryWriter.h" +#include "Algo/AnyOf.h" #if WITH_EDITOR #include "AssetRegistry/AssetRegistryModule.h" @@ -115,7 +116,7 @@ void UFlowAsset::PostLoad() { UnregisterNode(Guid); } - + ReconcileBaseAssetParams(FFlowAssetParamsUtils::GetLastSavedTimestampForObject(this)); } } @@ -153,7 +154,7 @@ void UFlowAsset::ReconcileBaseAssetParams(const FDateTime& AssetLastSavedTimesta if (EFlowReconcilePropertiesResult_Classifiers::IsErrorResult(ReconcileResult)) { UE_LOG(LogFlow, Error, TEXT("Failed to reconcile BaseAssetParams for %s: %s"), - *BaseAssetParamsPtr->GetPathName(), *UEnum::GetDisplayValueAsText(ReconcileResult).ToString()); + *BaseAssetParamsPtr->GetPathName(), *UEnum::GetDisplayValueAsText(ReconcileResult).ToString()); } } @@ -213,9 +214,9 @@ UFlowAssetParams* UFlowAsset::GenerateParamsFromStartNode() FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked("AssetRegistry"); AssetRegistryModule.Get().AssetCreated(NewParams); - + FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked("ContentBrowser"); - TArray AssetsToSync = { NewParams }; + TArray AssetsToSync = {NewParams}; ContentBrowserModule.Get().SyncBrowserToAssets(AssetsToSync, true); return NewParams; @@ -442,7 +443,7 @@ void UFlowAsset::RegisterNode(const FGuid& NewGuid, UFlowNode* NewNode) if (TryUpdateManagedFlowPinsForNode(*NewNode)) { - (void) NewNode->OnReconstructionRequested.ExecuteIfBound(); + (void)NewNode->OnReconstructionRequested.ExecuteIfBound(); } } @@ -557,7 +558,7 @@ void UFlowAsset::HarvestNodeConnections(UFlowNode* TargetNode) } } } - + bool UFlowAsset::TryGetDefaultForInputPinName(const FStructProperty& StructProperty, const void* Container, FString& OutString) { // We also look in the USTRUCT for DefaultForInputFlowPin @@ -802,7 +803,7 @@ TArray UFlowAsset::GatherPinsConnectedToPin(const FConnectedPin& ConnectedPins.Append(GuidNodePair.Value->GetKnownConnectionsToPin(Pin)); } } - + return ConnectedPins; } @@ -825,7 +826,7 @@ int32 UFlowAsset::RemoveInstance(UFlowAsset* Instance) #if WITH_EDITOR if (InspectedInstance.IsValid() && InspectedInstance.Get() == Instance) { - SetInspectedInstance(NAME_None); + SetInspectedInstance(nullptr); } #endif @@ -838,7 +839,7 @@ void UFlowAsset::ClearInstances() #if WITH_EDITOR if (InspectedInstance.IsValid()) { - SetInspectedInstance(NAME_None); + SetInspectedInstance(nullptr); } #endif @@ -854,35 +855,28 @@ void UFlowAsset::ClearInstances() } #if WITH_EDITOR -void UFlowAsset::GetInstanceDisplayNames(TArray>& OutDisplayNames) const +void UFlowAsset::SetInspectedInstance(TWeakObjectPtr NewInspectedInstance) { - for (const UFlowAsset* Instance : ActiveInstances) + if (NewInspectedInstance.IsValid()) { - OutDisplayNames.Emplace(MakeShareable(new FName(Instance->GetDisplayName()))); - } -} + if (InspectedInstance == NewInspectedInstance) + { + // Nothing changed + return; + } -void UFlowAsset::SetInspectedInstance(const FName& NewInspectedInstanceName) -{ - if (NewInspectedInstanceName.IsNone()) - { - InspectedInstance = nullptr; - } - else - { - for (UFlowAsset* ActiveInstance : ActiveInstances) + bool bIsNewInstancePresent = Algo::AnyOf(ActiveInstances, [NewInspectedInstance](const UFlowAsset* ActiveInstance) { - if (ActiveInstance && ActiveInstance->GetDisplayName() == NewInspectedInstanceName) - { - if (!InspectedInstance.IsValid() || InspectedInstance != ActiveInstance) - { - InspectedInstance = ActiveInstance; - } - break; - } + return ActiveInstance && ActiveInstance == NewInspectedInstance; + }); + + if (!ensureMsgf(bIsNewInstancePresent, TEXT("Trying to set %s as InspectedInstance, but it is not one of the ActiveInstances"), *NewInspectedInstance->GetName())) + { + NewInspectedInstance = nullptr; } } + InspectedInstance = NewInspectedInstance; BroadcastDebuggerRefresh(); } @@ -953,7 +947,7 @@ void UFlowAsset::PreStartFlow() if (TemplateAsset->ActiveInstances.Num() == 1) { // this instance is the only active one, set it directly as Inspected Instance - TemplateAsset->SetInspectedInstance(GetDisplayName()); + TemplateAsset->SetInspectedInstance(this); } else { diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index be876bee5..c281c9a09 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -885,7 +885,7 @@ void UFlowNode::TriggerInput(const FName& PinName, const EFlowPinActivationType if (const UFlowAsset* FlowAssetTemplate = GetFlowAsset()->GetTemplateAsset()) { - (void)FlowAssetTemplate->OnPinTriggered.ExecuteIfBound(NodeGuid, PinName); + (void)FlowAssetTemplate->OnPinTriggered.ExecuteIfBound(this, PinName); } #endif } @@ -951,7 +951,7 @@ void UFlowNode::TriggerOutput(const FName PinName, const bool bFinish /*= false* if (const UFlowAsset* FlowAssetTemplate = GetFlowAsset()->GetTemplateAsset()) { - FlowAssetTemplate->OnPinTriggered.ExecuteIfBound(NodeGuid, PinName); + FlowAssetTemplate->OnPinTriggered.ExecuteIfBound(this, PinName); } } else diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index bf67f0c37..e2889cb17 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -26,7 +26,7 @@ class UFlowAssetParams; #if !UE_BUILD_SHIPPING DECLARE_DELEGATE(FFlowGraphEvent); -DECLARE_DELEGATE_TwoParams(FFlowSignalEvent, const FGuid& /*NodeGuid*/, const FName& /*PinName*/); +DECLARE_DELEGATE_TwoParams(FFlowSignalEvent, const UFlowNode* /*Node*/, const FName& /*PinName*/); #endif /** @@ -244,7 +244,7 @@ class FLOW_API UFlowAsset : public UObject TArray> ActiveInstances; #if WITH_EDITORONLY_DATA - TWeakObjectPtr InspectedInstance; + TWeakObjectPtr InspectedInstance; // Message log for storing runtime errors/notes/warnings that will only last until the next game run // Log lives in the asset template, so it can be inspected after ending the PIE @@ -254,15 +254,14 @@ class FLOW_API UFlowAsset : public UObject public: void AddInstance(UFlowAsset* Instance); int32 RemoveInstance(UFlowAsset* Instance); + TConstArrayView> GetActiveInstances() const { return ActiveInstances; } void ClearInstances(); int32 GetInstancesNum() const { return ActiveInstances.Num(); } #if WITH_EDITOR - void GetInstanceDisplayNames(TArray>& OutDisplayNames) const; - - void SetInspectedInstance(const FName& NewInspectedInstanceName); - UFlowAsset* GetInspectedInstance() const { return InspectedInstance.IsValid() ? InspectedInstance.Get() : nullptr; } + void SetInspectedInstance(TWeakObjectPtr NewInspectedInstance); + const UFlowAsset* GetInspectedInstance() const { return InspectedInstance.IsValid() ? InspectedInstance.Get() : nullptr; } DECLARE_EVENT(UFlowAsset, FRefreshDebuggerEvent); diff --git a/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp b/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp index b9543a4aa..6711c5c81 100644 --- a/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp +++ b/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp @@ -16,6 +16,7 @@ #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowDebuggerSubsystem) UFlowDebuggerSubsystem::UFlowDebuggerSubsystem() + : bPausedAtFlowBreakpoint(false) { UFlowSubsystem::OnInstancedTemplateAdded.BindUObject(this, &ThisClass::OnInstancedTemplateAdded); UFlowSubsystem::OnInstancedTemplateRemoved.BindUObject(this, &ThisClass::OnInstancedTemplateRemoved); @@ -44,15 +45,18 @@ void UFlowDebuggerSubsystem::OnInstancedTemplateRemoved(UFlowAsset* AssetTemplat AssetTemplate->OnPinTriggered.Unbind(); } -void UFlowDebuggerSubsystem::OnPinTriggered(const FGuid& NodeGuid, const FName& PinName) +void UFlowDebuggerSubsystem::OnPinTriggered(const UFlowNode* Node, const FName& PinName) { - if (FindBreakpoint(NodeGuid, PinName)) + if (bPausedAtFlowBreakpoint) { - MarkAsHit(NodeGuid, PinName); + return; } - // Node breakpoints waits on any pin triggered - MarkAsHit(NodeGuid); + if (!TryMarkAsHit(Node, PinName)) + { + // Node breakpoints waits on any pin triggered, but check it only if there is no hit pin breakpoint + TryMarkAsHit(Node); + } } void UFlowDebuggerSubsystem::AddBreakpoint(const FGuid& NodeGuid) @@ -74,6 +78,20 @@ void UFlowDebuggerSubsystem::AddBreakpoint(const FGuid& NodeGuid, const FName& P SaveSettings(); } +void UFlowDebuggerSubsystem::RemoveAllBreakpoints(const TWeakObjectPtr FlowAsset) +{ + UFlowDebuggerSettings* Settings = GetMutableDefault(); + for (auto& [NodeGuid, Node] : FlowAsset->GetNodes()) + { + if (Settings->NodeBreakpoints.Contains(NodeGuid)) + { + Settings->NodeBreakpoints.Remove(NodeGuid); + } + } + + SaveSettings(); +} + void UFlowDebuggerSubsystem::RemoveAllBreakpoints(const FGuid& NodeGuid) { UFlowDebuggerSettings* Settings = GetMutableDefault(); @@ -206,6 +224,20 @@ FFlowBreakpoint* UFlowDebuggerSubsystem::FindBreakpoint(const FGuid& NodeGuid, c return NodeBreakpoint ? NodeBreakpoint->PinBreakpoints.Find(PinName) : nullptr; } +bool UFlowDebuggerSubsystem::HasAnyBreakpoints(const TWeakObjectPtr FlowAsset) +{ + UFlowDebuggerSettings* Settings = GetMutableDefault(); + for (const TPair& Node : FlowAsset->GetNodes()) + { + if (Settings->NodeBreakpoints.Find(Node.Key)) + { + return true; + } + } + + return false; +} + void UFlowDebuggerSubsystem::SetBreakpointEnabled(const FGuid& NodeGuid, const bool bEnabled) { if (FFlowBreakpoint* NodeBreakpoint = FindBreakpoint(NodeGuid)) @@ -224,6 +256,28 @@ void UFlowDebuggerSubsystem::SetBreakpointEnabled(const FGuid& NodeGuid, const F } } +void UFlowDebuggerSubsystem::SetAllBreakpointsEnabled(const TWeakObjectPtr FlowAsset, const bool bEnabled) +{ + UFlowDebuggerSettings* Settings = GetMutableDefault(); + for (const TPair& Node : FlowAsset->GetNodes()) + { + if (FNodeBreakpoint* NodeBreakpoint = Settings->NodeBreakpoints.Find(Node.Key)) + { + if (NodeBreakpoint->Breakpoint.IsActive()) + { + NodeBreakpoint->Breakpoint.SetEnabled(bEnabled); + } + + for (auto& [Name, PinBreakpoint] : NodeBreakpoint->PinBreakpoints) + { + PinBreakpoint.SetEnabled(bEnabled); + } + } + } + + SaveSettings(); +} + bool UFlowDebuggerSubsystem::IsBreakpointEnabled(const FGuid& NodeGuid) { if (const FFlowBreakpoint* PinBreakpoint = FindBreakpoint(NodeGuid)) @@ -244,31 +298,87 @@ bool UFlowDebuggerSubsystem::IsBreakpointEnabled(const FGuid& NodeGuid, const FN return false; } -void UFlowDebuggerSubsystem::MarkAsHit(const FGuid& NodeGuid) +bool UFlowDebuggerSubsystem::HasAnyBreakpointsEnabled(const TWeakObjectPtr FlowAsset) { - if (FFlowBreakpoint* NodeBreakpoint = FindBreakpoint(NodeGuid)) + UFlowDebuggerSettings* Settings = GetMutableDefault(); + for (const TPair& Node : FlowAsset->GetNodes()) + { + if (FNodeBreakpoint* NodeBreakpoint = Settings->NodeBreakpoints.Find(Node.Key)) + { + if (NodeBreakpoint->Breakpoint.IsActive() && NodeBreakpoint->Breakpoint.IsEnabled()) + { + return true; + } + + for (auto& [Name, PinBreakpoint] : NodeBreakpoint->PinBreakpoints) + { + if (PinBreakpoint.IsEnabled()) + { + return true; + } + } + } + } + + return false; +} + +bool UFlowDebuggerSubsystem::HasAnyBreakpointsDisabled(const TWeakObjectPtr FlowAsset) +{ + UFlowDebuggerSettings* Settings = GetMutableDefault(); + for (const TPair& Node : FlowAsset->GetNodes()) + { + if (FNodeBreakpoint* NodeBreakpoint = Settings->NodeBreakpoints.Find(Node.Key)) + { + if (NodeBreakpoint->Breakpoint.IsActive() && !NodeBreakpoint->Breakpoint.IsEnabled()) + { + return true; + } + + for (auto& [Name, PinBreakpoint] : NodeBreakpoint->PinBreakpoints) + { + if (!PinBreakpoint.IsEnabled()) + { + return true; + } + } + } + } + + return false; +} + +bool UFlowDebuggerSubsystem::TryMarkAsHit(const UFlowNode* Node) +{ + if (FFlowBreakpoint* NodeBreakpoint = FindBreakpoint(Node->NodeGuid)) { if (NodeBreakpoint->IsEnabled()) { NodeBreakpoint->MarkAsHit(true); - PauseSession(); + PauseSession(Node); + return true; } } + + return false; } -void UFlowDebuggerSubsystem::MarkAsHit(const FGuid& NodeGuid, const FName& PinName) +bool UFlowDebuggerSubsystem::TryMarkAsHit(const UFlowNode* Node, const FName& PinName) { - if (FFlowBreakpoint* PinBreakpoint = FindBreakpoint(NodeGuid, PinName)) + if (FFlowBreakpoint* PinBreakpoint = FindBreakpoint(Node->NodeGuid, PinName)) { if (PinBreakpoint->IsEnabled()) { PinBreakpoint->MarkAsHit(true); - PauseSession(); + PauseSession(Node); + return true; } } + + return false; } -void UFlowDebuggerSubsystem::PauseSession() +void UFlowDebuggerSubsystem::PauseSession(const UFlowNode* Node) { SetPause(true); } @@ -315,8 +425,9 @@ void UFlowDebuggerSubsystem::SetPause(const bool bPause) void UFlowDebuggerSubsystem::ClearHitBreakpoints() { - UFlowDebuggerSettings* Settings = GetMutableDefault(); + bPausedAtFlowBreakpoint = false; + UFlowDebuggerSettings* Settings = GetMutableDefault(); for (TPair& NodeBreakpoint : Settings->NodeBreakpoints) { NodeBreakpoint.Value.Breakpoint.MarkAsHit(false); diff --git a/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h b/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h index c83e004cd..705b49d28 100644 --- a/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h +++ b/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h @@ -8,7 +8,9 @@ #include "FlowDebuggerSubsystem.generated.h" class UEdGraphNode; + class UFlowAsset; +class UFlowNode; /** * Persistent subsystem supporting Flow Graph debugging. @@ -24,16 +26,20 @@ class FLOWDEBUGGER_API UFlowDebuggerSubsystem : public UEngineSubsystem virtual bool ShouldCreateSubsystem(UObject* Outer) const override; +protected: + bool bPausedAtFlowBreakpoint; + protected: virtual void OnInstancedTemplateAdded(UFlowAsset* AssetTemplate); virtual void OnInstancedTemplateRemoved(UFlowAsset* AssetTemplate) const; - virtual void OnPinTriggered(const FGuid& NodeGuid, const FName& PinName); + virtual void OnPinTriggered(const UFlowNode* Node, const FName& PinName); public: virtual void AddBreakpoint(const FGuid& NodeGuid); virtual void AddBreakpoint(const FGuid& NodeGuid, const FName& PinName); + virtual void RemoveAllBreakpoints(const TWeakObjectPtr FlowAsset); virtual void RemoveAllBreakpoints(const FGuid& NodeGuid); virtual void RemoveNodeBreakpoint(const FGuid& NodeGuid); virtual void RemovePinBreakpoint(const FGuid& NodeGuid, const FName& PinName); @@ -48,18 +54,22 @@ class FLOWDEBUGGER_API UFlowDebuggerSubsystem : public UEngineSubsystem virtual FFlowBreakpoint* FindBreakpoint(const FGuid& NodeGuid); virtual FFlowBreakpoint* FindBreakpoint(const FGuid& NodeGuid, const FName& PinName); + static bool HasAnyBreakpoints(const TWeakObjectPtr FlowAsset); virtual void SetBreakpointEnabled(const FGuid& NodeGuid, bool bEnabled); virtual void SetBreakpointEnabled(const FGuid& NodeGuid, const FName& PinName, bool bEnabled); + virtual void SetAllBreakpointsEnabled(const TWeakObjectPtr FlowAsset, bool bEnabled); virtual bool IsBreakpointEnabled(const FGuid& NodeGuid); virtual bool IsBreakpointEnabled(const FGuid& NodeGuid, const FName& PinName); + static bool HasAnyBreakpointsEnabled(const TWeakObjectPtr FlowAsset); + static bool HasAnyBreakpointsDisabled(const TWeakObjectPtr FlowAsset); protected: - virtual void MarkAsHit(const FGuid& NodeGuid); - virtual void MarkAsHit(const FGuid& NodeGuid, const FName& PinName); - - virtual void PauseSession(); + virtual bool TryMarkAsHit(const UFlowNode* Node); + virtual bool TryMarkAsHit(const UFlowNode* Node, const FName& PinName); + + virtual void PauseSession(const UFlowNode* Node); virtual void ResumeSession(); void SetPause(const bool bPause); diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index 4e451e1b6..86023789f 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -25,7 +25,6 @@ #include "Misc/UObjectToken.h" #include "Modules/ModuleManager.h" #include "PropertyEditorModule.h" -#include "FlowEditorModule.h" #include "ToolMenus.h" #include "Widgets/Docking/SDockTab.h" @@ -108,34 +107,34 @@ void FFlowAssetEditor::RegisterTabSpawners(const TSharedRef& FAssetEditorToolkit::RegisterTabSpawners(InTabManager); InTabManager->RegisterTabSpawner(DetailsTab, FOnSpawnTab::CreateSP(this, &FFlowAssetEditor::SpawnTab_Details)) - .SetDisplayName(LOCTEXT("DetailsTab", "Details")) - .SetGroup(WorkspaceMenuCategoryRef) - .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.Tabs.Details")); + .SetDisplayName(LOCTEXT("DetailsTab", "Details")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.Tabs.Details")); InTabManager->RegisterTabSpawner(GraphTab, FOnSpawnTab::CreateSP(this, &FFlowAssetEditor::SpawnTab_Graph)) - .SetDisplayName(LOCTEXT("GraphTab", "Graph")) - .SetGroup(WorkspaceMenuCategoryRef) - .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "GraphEditor.EventGraph_16x")); + .SetDisplayName(LOCTEXT("GraphTab", "Graph")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "GraphEditor.EventGraph_16x")); InTabManager->RegisterTabSpawner(PaletteTab, FOnSpawnTab::CreateSP(this, &FFlowAssetEditor::SpawnTab_Palette)) - .SetDisplayName(LOCTEXT("PaletteTab", "Palette")) - .SetGroup(WorkspaceMenuCategoryRef) - .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.Tabs.Palette")); + .SetDisplayName(LOCTEXT("PaletteTab", "Palette")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.Tabs.Palette")); InTabManager->RegisterTabSpawner(RuntimeLogTab, FOnSpawnTab::CreateSP(this, &FFlowAssetEditor::SpawnTab_RuntimeLog)) - .SetDisplayName(LOCTEXT("RuntimeLog", "Runtime Log")) - .SetGroup(WorkspaceMenuCategoryRef) - .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.Tabs.CompilerResults")); - + .SetDisplayName(LOCTEXT("RuntimeLog", "Runtime Log")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.Tabs.CompilerResults")); + InTabManager->RegisterTabSpawner(SearchTab, FOnSpawnTab::CreateSP(this, &FFlowAssetEditor::SpawnTab_Search)) - .SetDisplayName(LOCTEXT("SearchTab", "Search")) - .SetGroup(WorkspaceMenuCategoryRef) - .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.Tabs.FindResults")); - + .SetDisplayName(LOCTEXT("SearchTab", "Search")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.Tabs.FindResults")); + InTabManager->RegisterTabSpawner(ValidationLogTab, FOnSpawnTab::CreateSP(this, &FFlowAssetEditor::SpawnTab_ValidationLog)) - .SetDisplayName(LOCTEXT("ValidationLog", "Validation Log")) - .SetGroup(WorkspaceMenuCategoryRef) - .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Debug")); + .SetDisplayName(LOCTEXT("ValidationLog", "Validation Log")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Debug")); } void FFlowAssetEditor::UnregisterTabSpawners(const TSharedRef& InTabManager) @@ -188,6 +187,7 @@ void FFlowAssetEditor::SaveAsset_Execute() FAssetEditorToolkit::SaveAsset_Execute(); } + void FFlowAssetEditor::SaveAssetAs_Execute() { DoPresaveAssetUpdate(); @@ -318,57 +318,55 @@ void FFlowAssetEditor::InitFlowAssetEditor(const EToolkitMode::Type Mode, const ->AddArea ( FTabManager::NewPrimaryArea()->SetOrientation(Orient_Horizontal) - ->Split - ( - FTabManager::NewStack() - ->SetSizeCoefficient(0.225f) - ->AddTab(DetailsTab, ETabState::OpenedTab) - ) - ->Split - ( - FTabManager::NewSplitter() - ->SetSizeCoefficient(0.65f) - ->SetOrientation(Orient_Vertical) - ->Split - ( - FTabManager::NewStack() - ->SetSizeCoefficient(0.8f) - ->SetHideTabWell(true) - ->AddTab(GraphTab, ETabState::OpenedTab) - ) - ->Split - ( - FTabManager::NewStack() - ->SetSizeCoefficient(0.15f) - ->AddTab(RuntimeLogTab, ETabState::ClosedTab) - ) - ->Split - ( - FTabManager::NewStack() - ->SetSizeCoefficient(0.15f) - ->AddTab(SearchTab, ETabState::ClosedTab) - ) - ->Split - ( - FTabManager::NewStack() - ->SetSizeCoefficient(0.15f) - ->AddTab(ValidationLogTab, ETabState::ClosedTab) - ) - ) - ->Split - ( - FTabManager::NewStack() - ->SetSizeCoefficient(0.125f) - ->AddTab(PaletteTab, ETabState::OpenedTab) - ) + ->Split + ( + FTabManager::NewStack() + ->SetSizeCoefficient(0.225f) + ->AddTab(DetailsTab, ETabState::OpenedTab) + ) + ->Split + ( + FTabManager::NewSplitter() + ->SetSizeCoefficient(0.65f) + ->SetOrientation(Orient_Vertical) + ->Split + ( + FTabManager::NewStack() + ->SetSizeCoefficient(0.8f) + ->SetHideTabWell(true) + ->AddTab(GraphTab, ETabState::OpenedTab) + ) + ->Split + ( + FTabManager::NewStack() + ->SetSizeCoefficient(0.15f) + ->AddTab(RuntimeLogTab, ETabState::ClosedTab) + ) + ->Split + ( + FTabManager::NewStack() + ->SetSizeCoefficient(0.15f) + ->AddTab(SearchTab, ETabState::ClosedTab) + ) + ->Split + ( + FTabManager::NewStack() + ->SetSizeCoefficient(0.15f) + ->AddTab(ValidationLogTab, ETabState::ClosedTab) + ) + ) + ->Split + ( + FTabManager::NewStack() + ->SetSizeCoefficient(0.125f) + ->AddTab(PaletteTab, ETabState::OpenedTab) + ) ); constexpr bool bCreateDefaultStandaloneMenu = true; constexpr bool bCreateDefaultToolbar = true; InitAssetEditor(Mode, InitToolkitHost, TEXT("FlowEditorApp"), StandaloneDefaultLayout, bCreateDefaultStandaloneMenu, bCreateDefaultToolbar, ObjectToEdit, false); - InitalizeExtenders(); - RegenerateMenusAndToolbars(); } @@ -397,37 +395,30 @@ void FFlowAssetEditor::BindToolbarCommands() // Editing ToolkitCommands->MapAction(ToolbarCommands.RefreshAsset, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::RefreshAsset), - FCanExecuteAction::CreateStatic(&FFlowAssetEditor::CanEdit)); + FExecuteAction::CreateSP(this, &FFlowAssetEditor::RefreshAsset), + FCanExecuteAction::CreateStatic(&FFlowAssetEditor::CanEdit)); ToolkitCommands->MapAction(ToolbarCommands.ValidateAsset, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::ValidateAsset_Internal), - FCanExecuteAction()); - + FExecuteAction::CreateSP(this, &FFlowAssetEditor::ValidateAsset_Internal), + FCanExecuteAction()); + ToolkitCommands->MapAction(ToolbarCommands.SearchInAsset, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::SearchInAsset), - FCanExecuteAction()); + FExecuteAction::CreateSP(this, &FFlowAssetEditor::SearchInAsset), + FCanExecuteAction()); ToolkitCommands->MapAction(ToolbarCommands.EditAssetDefaults, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::EditAssetDefaults_Clicked), - FCanExecuteAction()); + FExecuteAction::CreateSP(this, &FFlowAssetEditor::EditAssetDefaults_Clicked), + FCanExecuteAction()); // Engine's Play commands ToolkitCommands->Append(FPlayWorldCommands::GlobalPlayWorldActions.ToSharedRef()); // Debugging ToolkitCommands->MapAction(ToolbarCommands.GoToParentInstance, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::GoToParentInstance), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanGoToParentInstance), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanGoToParentInstance)); -} - -void FFlowAssetEditor::InitalizeExtenders() -{ - FFlowEditorModule* FlowEditorModule = &FModuleManager::LoadModuleChecked("FlowEditor"); - AddMenuExtender(FlowEditorModule->GetMenuExtensibilityManager()->GetAllExtenders(GetToolkitCommands(), GetEditingObjects())); - AddToolbarExtender(FlowEditorModule->GetToolBarExtensibilityManager()->GetAllExtenders(GetToolkitCommands(), GetEditingObjects())); + FExecuteAction::CreateSP(this, &FFlowAssetEditor::GoToParentInstance), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanGoToParentInstance), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanGoToParentInstance)); } void FFlowAssetEditor::RefreshAsset() @@ -487,7 +478,7 @@ void FFlowAssetEditor::GoToParentInstance() const UFlowAsset* AssetThatInstancedThisAsset = FlowAsset->GetInspectedInstance()->GetParentInstance(); GEditor->GetEditorSubsystem()->OpenEditorForAsset(AssetThatInstancedThisAsset->GetTemplateAsset()); - AssetThatInstancedThisAsset->GetTemplateAsset()->SetInspectedInstance(AssetThatInstancedThisAsset->GetDisplayName()); + AssetThatInstancedThisAsset->GetTemplateAsset()->SetInspectedInstance(AssetThatInstancedThisAsset); } bool FFlowAssetEditor::CanGoToParentInstance() @@ -541,8 +532,8 @@ void FFlowAssetEditor::CreateWidgets() void FFlowAssetEditor::CreateGraphWidget() { - SAssignNew(GraphEditor, SFlowGraphEditor, SharedThis(this)) - .DetailsView(DetailsView); + SAssignNew(GraphEditor, SFlowGraphEditor, SharedThis(this)) + .DetailsView(DetailsView); } bool FFlowAssetEditor::CanEdit() @@ -639,4 +630,5 @@ void FFlowAssetEditor::JumpToNode(const UEdGraphNode* Node) const GetFlowGraph()->JumpToNode(Node, false); } } + #undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp index c794ad0e5..4c0214241 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp @@ -2,12 +2,14 @@ #include "Asset/FlowAssetToolbar.h" +#include "Graph/FlowGraphUtils.h" #include "Asset/FlowAssetEditor.h" #include "Asset/FlowAssetEditorContext.h" #include "Asset/SAssetRevisionMenu.h" #include "FlowEditorCommands.h" #include "FlowAsset.h" +#include "Nodes/Graph/FlowNode_SubGraph.h" #include "Kismet2/DebuggerCommands.h" #include "Misc/Attribute.h" @@ -30,22 +32,34 @@ // Flow Asset Instance List FText SFlowAssetInstanceList::NoInstanceSelectedText = LOCTEXT("NoInstanceSelected", "No instance selected"); +FText SFlowAssetInstanceList::AllContextsText = LOCTEXT("All", "All"); void SFlowAssetInstanceList::Construct(const FArguments& InArgs, const TWeakObjectPtr InTemplateAsset) { TemplateAsset = InTemplateAsset; + if (TemplateAsset.IsValid()) { TemplateAsset->OnDebuggerRefresh().AddSP(this, &SFlowAssetInstanceList::RefreshInstances); RefreshInstances(); } - // create dropdown - SAssignNew(Dropdown, SComboBox>) - .OptionsSource(&InstanceNames) - .Visibility_Static(&SFlowAssetInstanceList::GetDebuggerVisibility) - .OnGenerateWidget(this, &SFlowAssetInstanceList::OnGenerateWidget) - .OnSelectionChanged(this, &SFlowAssetInstanceList::OnSelectionChanged) + ContextComboBox = SNew(SComboBox>) + .OptionsSource(&Contexts) + .Visibility(this, &SFlowAssetInstanceList::GetContextVisibility) + .OnGenerateWidget(this, &SFlowAssetInstanceList::OnGenerateContextWidget) + .OnSelectionChanged(this, &SFlowAssetInstanceList::OnContextSelectionChanged) + .ContentPadding(FMargin(0.f, 2.f)) + [ + SNew(STextBlock) + .Text(this, &SFlowAssetInstanceList::GetSelectedContextName) + ]; + + InstanceComboBox = SNew(SComboBox>) + .OptionsSource(&Instances) + .OnGenerateWidget(this, &SFlowAssetInstanceList::OnGenerateInstanceWidget) + .OnSelectionChanged(this, &SFlowAssetInstanceList::OnInstanceSelectionChanged) + .ContentPadding(FMargin(0.f, 2.f)) [ SNew(STextBlock) .Text(this, &SFlowAssetInstanceList::GetSelectedInstanceName) @@ -53,7 +67,20 @@ void SFlowAssetInstanceList::Construct(const FArguments& InArgs, const TWeakObje ChildSlot [ - Dropdown.ToSharedRef() + SNew(SHorizontalBox) + .Visibility_Static(&SFlowAssetInstanceList::GetDebuggerVisibility) + + SHorizontalBox::Slot() + .AutoWidth() + .Padding(0.0f, 0.0f, 8.0f, 0.0f) + [ + ContextComboBox.ToSharedRef() + ] + + SHorizontalBox::Slot() + .AutoWidth() + .Padding(0.0f, 0.0f, 4.0f, 0.0f) + [ + InstanceComboBox.ToSharedRef() + ] ]; } @@ -65,59 +92,172 @@ SFlowAssetInstanceList::~SFlowAssetInstanceList() } } +EVisibility SFlowAssetInstanceList::GetDebuggerVisibility() +{ + return GEditor->PlayWorld ? EVisibility::Visible : EVisibility::Collapsed; +} + void SFlowAssetInstanceList::RefreshInstances() { - // collect instance names of this Flow Asset - InstanceNames = {MakeShareable(new FName(*NoInstanceSelectedText.ToString()))}; - TemplateAsset->GetInstanceDisplayNames(InstanceNames); + Contexts.Empty(); + Instances.Empty(); + InstancesPerContext.Empty(); - // select instance - if (const UFlowAsset* InspectedInstance = TemplateAsset->GetInspectedInstance()) + if (GEditor->ShouldEndPlayMap()) + { + // prevent redundant refreshing list for every asset instance being destroyed + return; + } + + // add empty context, users sees this as the default "All" option + NoContext = MakeShareable(new FObjectKey(nullptr)); + Contexts.Add(NoContext); + + // add empty instance as default + Instances.Add(MakeShareable(new FObjectKey(nullptr))); + + // gather all instances of given UFlowAsset + for (const UFlowAsset* ActiveInstance : TemplateAsset->GetActiveInstances()) { - const FName& InspectedInstanceName = InspectedInstance->GetDisplayName(); - for (const TSharedPtr& Instance : InstanceNames) + Instances.Add(MakeShareable(new FObjectKey(ActiveInstance))); + + // support World context in case of online multiplayer { - if (*Instance == InspectedInstanceName) + const UWorld* World = ActiveInstance->GetWorld(); + if (World && !InstancesPerContext.Contains(World)) { - SelectedInstance = Instance; - break; + FText WorldName = FText::FromString(GetDebugStringForWorld(World)); + Contexts.Add(MakeShareable(new FObjectKey(World))); + InstancesPerContext.Add(World, FFlowAssetInstanceContext(WorldName)); + } + + if (FFlowAssetInstanceContext* FoundContext = InstancesPerContext.Find(World)) + { + FoundContext->AssetInstances.Add(Instances.Last()); } } + + // todo: support Local Player context in case of split-screen } - else + + // set empty context by default, user must choose a specific context + if (!SelectedContext.IsValid() || !Contexts.Contains(SelectedContext)) { - // default object is always available - SelectedInstance = InstanceNames[0]; + SelectedContext = NoContext; + } + + // pre-select instance if current one does no longer exists + if (!SelectedInstance.IsValid() || !Instances.Contains(SelectedInstance)) + { + if (Instances.Num() > 1) + { + if (SelectedContext->ResolveObjectPtr()) + { + // try to set first Instance for a selected context + const FFlowAssetInstanceContext* InstanceContext = InstancesPerContext.Find(*SelectedContext.Get()); + if (InstanceContext && InstanceContext->AssetInstances.Num() > 0) + { + SelectedInstance = InstanceContext->AssetInstances[0]; + } + } + else + { + // set first active instance for any context + SelectedInstance = Instances[1]; + } + } + else + { + // set empty instance if there's no active instances + SelectedInstance = Instances[0]; + } } } -EVisibility SFlowAssetInstanceList::GetDebuggerVisibility() +EVisibility SFlowAssetInstanceList::GetContextVisibility() const { - return GEditor->PlayWorld ? EVisibility::Visible : EVisibility::Collapsed; + // switching makes sense only if we have at least 1 specific context + return InstancesPerContext.Num() > 1 ? EVisibility::Visible : EVisibility::Collapsed; } -TSharedRef SFlowAssetInstanceList::OnGenerateWidget(const TSharedPtr Item) const +TSharedRef SFlowAssetInstanceList::OnGenerateContextWidget(TSharedPtr Item) { - return SNew(STextBlock).Text(FText::FromName(*Item.Get())); + const FFlowAssetInstanceContext* Context = InstancesPerContext.Find(Item->ResolveObjectPtr()); + return SNew(STextBlock).Text(Context ? Context->DisplayText : AllContextsText); } -void SFlowAssetInstanceList::OnSelectionChanged(const TSharedPtr SelectedItem, const ESelectInfo::Type SelectionType) +void SFlowAssetInstanceList::OnContextSelectionChanged(TSharedPtr SelectedItem, ESelectInfo::Type SelectionType) +{ + if (SelectionType != ESelectInfo::Direct) + { + SelectedContext = SelectedItem; + + if (TemplateAsset.IsValid()) + { + TemplateAsset->SetInspectedInstance(nullptr); + } + } +} + +FText SFlowAssetInstanceList::GetSelectedContextName() const +{ + if (SelectedContext.IsValid()) + { + const UObject* Context = SelectedContext->ResolveObjectPtr(); + if (const FFlowAssetInstanceContext* InstanceContext = InstancesPerContext.Find(Context)) + { + return InstanceContext->DisplayText; + } + } + + return AllContextsText; +} + +TSharedRef SFlowAssetInstanceList::OnGenerateInstanceWidget(const TSharedPtr Item) const +{ + return SNew(STextBlock).Text(JoinInstanceAndContextTexts(*Item.Get())); +} + +void SFlowAssetInstanceList::OnInstanceSelectionChanged(const TSharedPtr SelectedItem, const ESelectInfo::Type SelectionType) { if (SelectionType != ESelectInfo::Direct) { SelectedInstance = SelectedItem; + const UFlowAsset* Instance = Cast(SelectedInstance->ResolveObjectPtr()); if (TemplateAsset.IsValid()) { - const FName NewSelectedInstanceName = (SelectedInstance.IsValid() && *SelectedInstance != *InstanceNames[0]) ? *SelectedInstance : NAME_None; - TemplateAsset->SetInspectedInstance(NewSelectedInstanceName); + TemplateAsset->SetInspectedInstance(Instance); } } } FText SFlowAssetInstanceList::GetSelectedInstanceName() const { - return SelectedInstance.IsValid() ? FText::FromName(*SelectedInstance) : NoInstanceSelectedText; + return SelectedInstance.IsValid() ? JoinInstanceAndContextTexts(*SelectedInstance.Get()) : NoInstanceSelectedText; +} + +FText SFlowAssetInstanceList::JoinInstanceAndContextTexts(const FObjectKey& AssetInstance) const +{ + if (const UFlowAsset* Instance = Cast(AssetInstance.ResolveObjectPtr())) + { + FText Result = FText::FromName(Instance->GetDisplayName()); + + // add context name if there are multiple contexts present + if (InstancesPerContext.Num() > 1) + { + if (const FFlowAssetInstanceContext* Context = InstancesPerContext.Find(Instance->GetWorld())) + { + static const FText OpeningBracket = FText::AsCultureInvariant(TEXT("(")); + static const FText ClosingBracket = FText::AsCultureInvariant(TEXT(")")); + FText::Format(Result, OpeningBracket, Context->DisplayText, ClosingBracket); + } + } + + return Result; + } + + return NoInstanceSelectedText; } ////////////////////////////////////////////////////////////////////////// @@ -129,31 +269,44 @@ void SFlowAssetBreadcrumb::Construct(const FArguments& InArgs, const TWeakObject // create breadcrumb SAssignNew(BreadcrumbTrail, SBreadcrumbTrail) - .OnCrumbClicked(this, &SFlowAssetBreadcrumb::OnCrumbClicked) - .Visibility_Static(&SFlowAssetInstanceList::GetDebuggerVisibility) - .ButtonStyle(FAppStyle::Get(), "FlatButton") - .DelimiterImage(FAppStyle::GetBrush("Sequencer.BreadcrumbIcon")) - .PersistentBreadcrumbs(true) - .TextStyle(FAppStyle::Get(), "Sequencer.BreadcrumbText"); + .Visibility_Static(&SFlowAssetInstanceList::GetDebuggerVisibility) + .OnCrumbClicked(this, &SFlowAssetBreadcrumb::OnCrumbClicked) + .ButtonStyle(FAppStyle::Get(), "SimpleButton") + .TextStyle(FAppStyle::Get(), "NormalText") + .ButtonContentPadding(FMargin(2.0f, 4.0f)) + .DelimiterImage(FAppStyle::GetBrush("Icons.ChevronRight")) + .ShowLeadingDelimiter(true) + .PersistentBreadcrumbs(true); ChildSlot [ - SNew(SVerticalBox) - + SVerticalBox::Slot() - .HAlign(HAlign_Right) - .VAlign(VAlign_Center) - .AutoHeight() - .Padding(25.0f, 10.0f) + SNew(SBorder) + .Visibility(this, &SFlowAssetBreadcrumb::GetBreadcrumbVisibility) + .BorderImage(new FSlateRoundedBoxBrush(FStyleColors::Transparent, 4, FStyleColors::InputOutline, 1)) [ - BreadcrumbTrail.ToSharedRef() + SNew(SBox) + .MaxDesiredWidth(500.f) + [ + BreadcrumbTrail.ToSharedRef() + ] ] ]; - // fill breadcrumb + TemplateAsset->OnDebuggerRefresh().AddSP(this, &SFlowAssetBreadcrumb::FillBreadcrumb); + FillBreadcrumb(); +} + +EVisibility SFlowAssetBreadcrumb::GetBreadcrumbVisibility() const +{ + return GEditor->PlayWorld && TemplateAsset->GetInspectedInstance() ? EVisibility::Visible : EVisibility::Collapsed; +} + +void SFlowAssetBreadcrumb::FillBreadcrumb() const +{ BreadcrumbTrail->ClearCrumbs(); - if (UFlowAsset* InspectedInstance = TemplateAsset->GetInspectedInstance()) + if (const UFlowAsset* InspectedInstance = TemplateAsset->GetInspectedInstance()) { - TArray> InstancesFromRoot = {InspectedInstance}; + TArray> InstancesFromRoot = {InspectedInstance}; const UFlowAsset* CheckedInstance = InspectedInstance; while (UFlowAsset* ParentInstance = CheckedInstance->GetParentInstance()) @@ -162,13 +315,12 @@ void SFlowAssetBreadcrumb::Construct(const FArguments& InArgs, const TWeakObject CheckedInstance = ParentInstance; } - for (TWeakObjectPtr Instance : InstancesFromRoot) + for (int32 Index = 0; Index < InstancesFromRoot.Num(); Index++) { - if (Instance.IsValid()) - { - const FFlowBreadcrumb NewBreadcrumb = FFlowBreadcrumb(Instance); - BreadcrumbTrail->PushCrumb(FText::FromName(NewBreadcrumb.InstanceName), FFlowBreadcrumb(Instance)); - } + TWeakObjectPtr Instance = InstancesFromRoot[Index]; + TWeakObjectPtr ChildInstance = Index < InstancesFromRoot.Num() - 1 ? InstancesFromRoot[Index + 1] : nullptr; + + BreadcrumbTrail->PushCrumb(FText::FromName(Instance->GetDisplayName()), FFlowBreadcrumb(Instance, ChildInstance)); } } } @@ -176,9 +328,22 @@ void SFlowAssetBreadcrumb::Construct(const FArguments& InArgs, const TWeakObject void SFlowAssetBreadcrumb::OnCrumbClicked(const FFlowBreadcrumb& Item) const { const UFlowAsset* InspectedInstance = TemplateAsset->GetInspectedInstance(); - if (InspectedInstance == nullptr || Item.InstanceName != InspectedInstance->GetDisplayName()) + if (InspectedInstance == nullptr || Item.CurrentInstance != TemplateAsset) { - GEditor->GetEditorSubsystem()->OpenEditorForAsset(Item.AssetPathName); + const TWeakObjectPtr ClickedInstance = Item.CurrentInstance; + UFlowAsset* ClickedTemplateAsset = ClickedInstance->GetTemplateAsset(); + + if (GEditor->GetEditorSubsystem()->OpenEditorForAsset(ClickedTemplateAsset)) + { + ClickedTemplateAsset->SetInspectedInstance(ClickedInstance); + if (const TSharedPtr FlowAssetEditor = FFlowGraphUtils::GetFlowAssetEditor(ClickedTemplateAsset)) + { + if (!Item.ChildInstance.IsExplicitlyNull()) + { + FlowAssetEditor->JumpToNode(Item.ChildInstance->GetNodeOwningThisAssetInstance()->GetGraphNode()); + } + } + } } } @@ -203,7 +368,7 @@ void FFlowAssetToolbar::BuildAssetToolbar(UToolMenu* ToolbarMenu) const Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().ValidateAsset)); Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().EditAssetDefaults)); } - + { FToolMenuSection& Section = ToolbarMenu->AddSection("View"); Section.InsertPosition = FToolMenuInsert("FlowAsset", EToolMenuInsertType::After); @@ -227,7 +392,7 @@ void FFlowAssetToolbar::BuildAssetToolbar(UToolMenu* ToolbarMenu) const InSection.AddEntry(DiffEntry); } })); - + Section.AddEntry(FToolMenuEntry::InitToolBarButton( FFlowToolbarCommands::Get().SearchInAsset, TAttribute(), @@ -324,8 +489,6 @@ void FFlowAssetToolbar::BuildDebuggerToolbar(UToolMenu* ToolbarMenu) const FPlayWorldCommands::BuildToolbar(InSection); InSection.AddEntry(FToolMenuEntry::InitWidget("AssetInstances", SNew(SFlowAssetInstanceList, Context->GetFlowAsset()), FText(), true)); - - InSection.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().GoToParentInstance)); InSection.AddEntry(FToolMenuEntry::InitWidget("AssetBreadcrumb", SNew(SFlowAssetBreadcrumb, Context->GetFlowAsset()), FText(), true)); } })); diff --git a/Source/FlowEditor/Private/Asset/FlowDebugEditorSubsystem.cpp b/Source/FlowEditor/Private/Asset/FlowDebugEditorSubsystem.cpp index b30394a0e..63b68d048 100644 --- a/Source/FlowEditor/Private/Asset/FlowDebugEditorSubsystem.cpp +++ b/Source/FlowEditor/Private/Asset/FlowDebugEditorSubsystem.cpp @@ -3,6 +3,7 @@ #include "Asset/FlowDebugEditorSubsystem.h" #include "Asset/FlowAssetEditor.h" #include "Asset/FlowMessageLogListing.h" +#include "Graph/FlowGraphUtils.h" #include "Editor/UnrealEdEngine.h" #include "Engine/Engine.h" @@ -92,10 +93,33 @@ void UFlowDebugEditorSubsystem::OnEndPIE(const bool bIsSimulating) } } -void UFlowDebugEditorSubsystem::PauseSession() +void UFlowDebugEditorSubsystem::PauseSession(const UFlowNode* Node) { - if (!GUnrealEd->SetPIEWorldsPaused(true)) + if (GEditor->ShouldEndPlayMap()) { + return; + } + + if (GUnrealEd->SetPIEWorldsPaused(true)) + { + bPausedAtFlowBreakpoint = true; + + const UFlowAsset* HitInstance = Node->GetFlowAsset(); + if (ensure(HitInstance)) + { + UFlowAsset* AssetTemplate = HitInstance->GetTemplateAsset(); + AssetTemplate->SetInspectedInstance(HitInstance); + + UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem(); + if (AssetEditorSubsystem->OpenEditorForAsset(AssetTemplate)) + { + if (const TSharedPtr FlowAssetEditor = FFlowGraphUtils::GetFlowAssetEditor(AssetTemplate)) + { + FlowAssetEditor->JumpToNode(Node->GetGraphNode()); + } + } + } + GUnrealEd->PlaySessionPaused(); } } diff --git a/Source/FlowEditor/Private/FlowEditorCommands.cpp b/Source/FlowEditor/Private/FlowEditorCommands.cpp index 8e681edec..63c3b64a0 100644 --- a/Source/FlowEditor/Private/FlowEditorCommands.cpp +++ b/Source/FlowEditor/Private/FlowEditorCommands.cpp @@ -12,7 +12,7 @@ #define LOCTEXT_NAMESPACE "FlowGraphCommands" FFlowToolbarCommands::FFlowToolbarCommands() - : TCommands("FlowToolbar", LOCTEXT("FlowToolbar", "Flow Toobar"), NAME_None, FFlowEditorStyle::GetStyleSetName()) + : TCommands("FlowToolbar", LOCTEXT("FlowToolbar", "Flow Toolbar"), NAME_None, FFlowEditorStyle::GetStyleSetName()) { } @@ -46,6 +46,10 @@ void FFlowGraphCommands::RegisterCommands() UI_COMMAND(DisablePinBreakpoint, "Disable Pin Breakpoint", "Disables a breakpoint on the pin", EUserInterfaceActionType::Button, FInputChord()); UI_COMMAND(TogglePinBreakpoint, "Toggle Pin Breakpoint", "Toggles a breakpoint on the pin", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(EnableAllBreakpoints,"Enable All Breakpoints", "Enable all breakpoints", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(DisableAllBreakpoints, "Disable All Breakpoints", "Disable all breakpoints", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(RemoveAllBreakpoints, "Delete All Breakpoints", "Delete all breakpoints", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control | EModifierKey::Shift, EKeys::F9)); + UI_COMMAND(EnableNode, "Enable Node", "Default state, node is fully executed.", EUserInterfaceActionType::Button, FInputChord()); UI_COMMAND(DisableNode, "Disable Node", "No logic executed, any Input Pin activation is ignored. Node instantly enters a deactivated state.", EUserInterfaceActionType::Button, FInputChord()); UI_COMMAND(SetPassThrough, "Set Pass Through", "Internal node logic not executed. All connected outputs are triggered, node finishes its work.", EUserInterfaceActionType::Button, FInputChord()); diff --git a/Source/FlowEditor/Private/FlowEditorModule.cpp b/Source/FlowEditor/Private/FlowEditorModule.cpp index 282fab0d2..625bed085 100644 --- a/Source/FlowEditor/Private/FlowEditorModule.cpp +++ b/Source/FlowEditor/Private/FlowEditorModule.cpp @@ -60,9 +60,6 @@ FAssetCategoryPath FFlowAssetCategoryPaths::Flow(LOCTEXT("Flow", "Flow")); void FFlowEditorModule::StartupModule() { - MenuExtensibilityManager = MakeShared(); - ToolBarExtensibilityManager = MakeShared(); - FFlowEditorStyle::Initialize(); TrySetFlowNodeDisplayStyleDefaults(); @@ -102,9 +99,6 @@ void FFlowEditorModule::StartupModule() void FFlowEditorModule::ShutdownModule() { - MenuExtensibilityManager.Reset(); - ToolBarExtensibilityManager.Reset(); - FFlowEditorStyle::Shutdown(); UnregisterDetailCustomizations(); diff --git a/Source/FlowEditor/Private/FlowEditorStyle.cpp b/Source/FlowEditor/Private/FlowEditorStyle.cpp index 23c067776..f37844996 100644 --- a/Source/FlowEditor/Private/FlowEditorStyle.cpp +++ b/Source/FlowEditor/Private/FlowEditorStyle.cpp @@ -37,8 +37,6 @@ void FFlowEditorStyle::Initialize() StyleSet->Set("FlowToolbar.SearchInAsset", new IMAGE_BRUSH_SVG( "Starship/Common/Search", Icon20)); StyleSet->Set("FlowToolbar.EditAssetDefaults", new IMAGE_BRUSH_SVG("Starship/Common/Details", Icon20)); - StyleSet->Set("FlowToolbar.GoToParentInstance", new IMAGE_BRUSH("Icons/icon_DebugStepOut_40x", Icon40)); - StyleSet->Set("FlowGraph.BreakpointEnabled", new IMAGE_BRUSH("Old/Kismet2/Breakpoint_Valid", FVector2D(24.0f, 24.0f))); StyleSet->Set("FlowGraph.BreakpointDisabled", new IMAGE_BRUSH("Old/Kismet2/Breakpoint_Disabled", FVector2D(24.0f, 24.0f))); StyleSet->Set("FlowGraph.BreakpointHit", new IMAGE_BRUSH("Old/Kismet2/IP_Breakpoint", Icon40)); diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index ccdd093fb..4d14f5b57 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -34,10 +34,11 @@ void SFlowGraphEditor::Construct(const FArguments& InArgs, const TSharedPtrGetEngineSubsystem(); BindGraphCommands(); + CreateDebugMenu(); SGraphEditor::FArguments Arguments; Arguments._AdditionalCommands = CommandList; - Arguments._Appearance = GetGraphAppearanceInfo(); + Arguments._Appearance = TAttribute::CreateSP(this, &SFlowGraphEditor::GetGraphAppearanceInfo); Arguments._GraphToEdit = FlowAsset->GetGraph(); Arguments._GraphEvents = InArgs._GraphEvents; Arguments._AutoExpandActionMenu = true; @@ -67,209 +68,239 @@ void SFlowGraphEditor::BindGraphCommands() // Graph commands CommandList->MapAction(GraphEditorCommands.CreateComment, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnCreateComment), - FCanExecuteAction::CreateStatic(&SFlowGraphEditor::CanEdit)); + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnCreateComment), + FCanExecuteAction::CreateStatic(&SFlowGraphEditor::CanEdit)); CommandList->MapAction(GraphEditorCommands.StraightenConnections, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnStraightenConnections)); - + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnStraightenConnections)); + CommandList->MapAction(GraphEditorCommands.DeleteAndReconnectNodes, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::DeleteSelectedNodes), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanDeleteNodes)); + FExecuteAction::CreateSP(this, &SFlowGraphEditor::DeleteSelectedNodes), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanDeleteNodes)); // Generic Node commands CommandList->MapAction(GenericCommands.Undo, - FExecuteAction::CreateStatic(&SFlowGraphEditor::UndoGraphAction), - FCanExecuteAction::CreateStatic(&SFlowGraphEditor::CanEdit)); + FExecuteAction::CreateStatic(&SFlowGraphEditor::UndoGraphAction), + FCanExecuteAction::CreateStatic(&SFlowGraphEditor::CanEdit)); CommandList->MapAction(GenericCommands.Redo, - FExecuteAction::CreateStatic(&SFlowGraphEditor::RedoGraphAction), - FCanExecuteAction::CreateStatic(&SFlowGraphEditor::CanEdit)); + FExecuteAction::CreateStatic(&SFlowGraphEditor::RedoGraphAction), + FCanExecuteAction::CreateStatic(&SFlowGraphEditor::CanEdit)); CommandList->MapAction(GenericCommands.SelectAll, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::SelectAllNodes), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanSelectAllNodes)); + FExecuteAction::CreateSP(this, &SFlowGraphEditor::SelectAllNodes), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanSelectAllNodes)); CommandList->MapAction(GenericCommands.Delete, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::DeleteSelectedNodes), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanDeleteNodes)); + FExecuteAction::CreateSP(this, &SFlowGraphEditor::DeleteSelectedNodes), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanDeleteNodes)); CommandList->MapAction(GenericCommands.Copy, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::CopySelectedNodes), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanCopyNodes)); + FExecuteAction::CreateSP(this, &SFlowGraphEditor::CopySelectedNodes), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanCopyNodes)); CommandList->MapAction(GenericCommands.Cut, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::CutSelectedNodes), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanCutNodes)); + FExecuteAction::CreateSP(this, &SFlowGraphEditor::CutSelectedNodes), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanCutNodes)); CommandList->MapAction(GenericCommands.Paste, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::PasteNodes), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanPasteNodes)); + FExecuteAction::CreateSP(this, &SFlowGraphEditor::PasteNodes), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanPasteNodes)); CommandList->MapAction(GenericCommands.Duplicate, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::DuplicateNodes), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanDuplicateNodes)); + FExecuteAction::CreateSP(this, &SFlowGraphEditor::DuplicateNodes), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanDuplicateNodes)); // Pin commands CommandList->MapAction(FlowGraphCommands.ReconstructNode, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::ReconstructNode), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanReconstructNode)); + FExecuteAction::CreateSP(this, &SFlowGraphEditor::ReconstructNode), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanReconstructNode)); CommandList->MapAction(FlowGraphCommands.AddInput, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::AddInput), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanAddInput)); + FExecuteAction::CreateSP(this, &SFlowGraphEditor::AddInput), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanAddInput)); CommandList->MapAction(FlowGraphCommands.AddOutput, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::AddOutput), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanAddOutput)); + FExecuteAction::CreateSP(this, &SFlowGraphEditor::AddOutput), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanAddOutput)); CommandList->MapAction(FlowGraphCommands.RemovePin, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::RemovePin), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanRemovePin)); + FExecuteAction::CreateSP(this, &SFlowGraphEditor::RemovePin), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanRemovePin)); // Breakpoint commands CommandList->MapAction(GraphEditorCommands.AddBreakpoint, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnAddBreakpoint), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanAddBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanAddBreakpoint) + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnAddBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanAddBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanAddBreakpoint) ); CommandList->MapAction(GraphEditorCommands.RemoveBreakpoint, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnRemoveBreakpoint), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanRemoveBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanRemoveBreakpoint) + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnRemoveBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanRemoveBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanRemoveBreakpoint) ); CommandList->MapAction(GraphEditorCommands.EnableBreakpoint, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnEnableBreakpoint), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanEnableBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanEnableBreakpoint) + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnEnableBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanEnableBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanEnableBreakpoint) ); CommandList->MapAction(GraphEditorCommands.DisableBreakpoint, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnDisableBreakpoint), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanDisableBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanDisableBreakpoint) + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnDisableBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanDisableBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanDisableBreakpoint) ); CommandList->MapAction(GraphEditorCommands.ToggleBreakpoint, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnToggleBreakpoint), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanToggleBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanToggleBreakpoint) + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnToggleBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanToggleBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanToggleBreakpoint) ); // Pin Breakpoint commands CommandList->MapAction(FlowGraphCommands.AddPinBreakpoint, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnAddPinBreakpoint), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanAddPinBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanAddPinBreakpoint) + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnAddPinBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanAddPinBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanAddPinBreakpoint) ); CommandList->MapAction(FlowGraphCommands.RemovePinBreakpoint, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnRemovePinBreakpoint), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanRemovePinBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanRemovePinBreakpoint) + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnRemovePinBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanRemovePinBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanRemovePinBreakpoint) ); CommandList->MapAction(FlowGraphCommands.EnablePinBreakpoint, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnEnablePinBreakpoint), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanEnablePinBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanEnablePinBreakpoint) + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnEnablePinBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanEnablePinBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanEnablePinBreakpoint) ); CommandList->MapAction(FlowGraphCommands.DisablePinBreakpoint, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnDisablePinBreakpoint), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanDisablePinBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanDisablePinBreakpoint) + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnDisablePinBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanDisablePinBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanDisablePinBreakpoint) ); CommandList->MapAction(FlowGraphCommands.TogglePinBreakpoint, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnTogglePinBreakpoint), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanTogglePinBreakpoint), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanTogglePinBreakpoint) + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnTogglePinBreakpoint), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanTogglePinBreakpoint), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanTogglePinBreakpoint) ); + CommandList->MapAction(FlowGraphCommands.EnableAllBreakpoints, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::EnableAllBreakpoints), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::HasAnyDisabledBreakpoints)); + + CommandList->MapAction(FlowGraphCommands.DisableAllBreakpoints, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::DisableAllBreakpoints), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::HasAnyEnabledBreakpoints)); + + CommandList->MapAction(FlowGraphCommands.RemoveAllBreakpoints, + FExecuteAction::CreateSP(this, &SFlowGraphEditor::RemoveAllBreakpoints), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::HasAnyBreakpoints)); + // Execution Override commands CommandList->MapAction(FlowGraphCommands.EnableNode, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::SetSignalMode, EFlowSignalMode::Enabled), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::Enabled), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::Enabled) + FExecuteAction::CreateSP(this, &SFlowGraphEditor::SetSignalMode, EFlowSignalMode::Enabled), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::Enabled), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::Enabled) ); CommandList->MapAction(FlowGraphCommands.DisableNode, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::SetSignalMode, EFlowSignalMode::Disabled), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::Disabled), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::Disabled) + FExecuteAction::CreateSP(this, &SFlowGraphEditor::SetSignalMode, EFlowSignalMode::Disabled), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::Disabled), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::Disabled) ); CommandList->MapAction(FlowGraphCommands.SetPassThrough, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::SetSignalMode, EFlowSignalMode::PassThrough), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::PassThrough), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::PassThrough) + FExecuteAction::CreateSP(this, &SFlowGraphEditor::SetSignalMode, EFlowSignalMode::PassThrough), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::PassThrough), + FIsActionChecked(), + FIsActionButtonVisible::CreateSP(this, &SFlowGraphEditor::CanSetSignalMode, EFlowSignalMode::PassThrough) ); CommandList->MapAction(FlowGraphCommands.ForcePinActivation, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnForcePinActivation), - FCanExecuteAction::CreateStatic(&SFlowGraphEditor::IsPIE), - FIsActionChecked(), - FIsActionButtonVisible::CreateStatic(&SFlowGraphEditor::IsPIE) + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnForcePinActivation), + FCanExecuteAction::CreateStatic(&SFlowGraphEditor::IsPIE), + FIsActionChecked(), + FIsActionButtonVisible::CreateStatic(&SFlowGraphEditor::IsPIE) ); // Jump commands CommandList->MapAction(FlowGraphCommands.FocusViewport, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::FocusViewport), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanFocusViewport)); + FExecuteAction::CreateSP(this, &SFlowGraphEditor::FocusViewport), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanFocusViewport)); CommandList->MapAction(FlowGraphCommands.JumpToNodeDefinition, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::JumpToNodeDefinition), - FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanJumpToNodeDefinition)); + FExecuteAction::CreateSP(this, &SFlowGraphEditor::JumpToNodeDefinition), + FCanExecuteAction::CreateSP(this, &SFlowGraphEditor::CanJumpToNodeDefinition)); // Organisation Commands CommandList->MapAction(GraphEditorCommands.AlignNodesTop, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnAlignTop)); + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnAlignTop)); CommandList->MapAction(GraphEditorCommands.AlignNodesMiddle, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnAlignMiddle)); + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnAlignMiddle)); CommandList->MapAction(GraphEditorCommands.AlignNodesBottom, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnAlignBottom)); + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnAlignBottom)); CommandList->MapAction(GraphEditorCommands.AlignNodesLeft, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnAlignLeft)); + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnAlignLeft)); CommandList->MapAction(GraphEditorCommands.AlignNodesCenter, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnAlignCenter)); + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnAlignCenter)); CommandList->MapAction(GraphEditorCommands.AlignNodesRight, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnAlignRight)); + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnAlignRight)); CommandList->MapAction(GraphEditorCommands.StraightenConnections, - FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnStraightenConnections)); + FExecuteAction::CreateSP(this, &SFlowGraphEditor::OnStraightenConnections)); +} + +void SFlowGraphEditor::CreateDebugMenu() +{ + const FNewToolMenuDelegate AddDebugSection = FNewToolMenuDelegate::CreateLambda([this](UToolMenu* InMenu) + { + FToolMenuSection& Section = InMenu->AddSection("Breakpoints", LOCTEXT("Breakpoints", "Breakpoints")); + const FFlowGraphCommands& FlowGraphCommands = FFlowGraphCommands::Get(); + + Section.AddMenuEntryWithCommandList(FlowGraphCommands.EnableAllBreakpoints, CommandList); + Section.AddMenuEntryWithCommandList(FlowGraphCommands.DisableAllBreakpoints, CommandList); + Section.AddMenuEntryWithCommandList(FlowGraphCommands.RemoveAllBreakpoints, CommandList); + }); + + FToolMenuSection& MainSection = UToolMenus::Get()->ExtendMenu("AssetEditor.FlowEditor.MainMenu")->FindOrAddSection(NAME_None); + FToolMenuEntry& DebugEntry = MainSection.AddSubMenu( + "Debug", + LOCTEXT("Debug", "Debug"), + LOCTEXT("DebugTooltip", "Open the debug menu"), + AddDebugSection + ); + + DebugEntry.InsertPosition = FToolMenuInsert("Edit", EToolMenuInsertType::After); } FGraphAppearanceInfo SFlowGraphEditor::GetGraphAppearanceInfo() const { FGraphAppearanceInfo AppearanceInfo; AppearanceInfo.CornerText = GetCornerText(); - - if (IsPlaySessionPaused()) - { - AppearanceInfo.PIENotifyText = LOCTEXT("PausedLabel", "PAUSED"); - } - + AppearanceInfo.PIENotifyText = GetPIENotifyText(); return AppearanceInfo; } @@ -278,6 +309,19 @@ FText SFlowGraphEditor::GetCornerText() const return LOCTEXT("AppearanceCornerText_FlowAsset", "FLOW"); } +FText SFlowGraphEditor::GetPIENotifyText() const +{ + if (const UFlowAsset* InspectedInstance = FlowAsset->GetInspectedInstance()) + { + if (const UWorld* InspectedWorld = InspectedInstance->GetWorld()) + { + return FText::FromString(GetDebugStringForWorld(InspectedWorld)); + } + } + + return LOCTEXT("PIENotifyText_FlowAsset", "Template"); +} + void SFlowGraphEditor::UndoGraphAction() { GEditor->UndoTransaction(); @@ -339,7 +383,7 @@ bool SFlowGraphEditor::IsPIE() bool SFlowGraphEditor::IsPlaySessionPaused() { bool bPaused = true; - + for (const FWorldContext& PieContext : GUnrealEd->GetWorldContexts()) { const UWorld* PlayWorld = PieContext.World(); @@ -370,7 +414,7 @@ void SFlowGraphEditor::OnSelectedNodesChanged(const TSet& Nodes) { if (const UFlowGraphNode* GraphNode = Cast(*SetIt)) { - SelectedObjects.Add(Cast(GraphNode->GetFlowNodeBase())); + SelectedObjects.Add(GraphNode->GetFlowNodeBase()); } else { @@ -410,7 +454,7 @@ TSet SFlowGraphEditor::GetSelectedFlowNodes() const void SFlowGraphEditor::ReconnectExecPins(const UFlowGraphNode* Node) { - if(Node == nullptr) + if (Node == nullptr) { return; } @@ -496,7 +540,7 @@ void SFlowGraphEditor::DeleteSelectedNodes() { DebuggerSubsystem->RemoveAllBreakpoints(Node->NodeGuid); } - + if (const UFlowGraphNode* FlowGraphNode = Cast(Node)) { if (const UFlowNode* FlowNode = Cast(FlowGraphNode->GetFlowNodeBase())) @@ -759,7 +803,7 @@ void SFlowGraphEditor::PasteNodesHere(const FVector2D& Location) // (for now? perhaps we register AddOns in the future?) FlowAsset->RegisterNode(PastedNode->NodeGuid, FlowNode); } - + PastedFlowGraphNode->RemoveAllSubNodes(); } } @@ -1369,6 +1413,45 @@ bool SFlowGraphEditor::CanTogglePinBreakpoint() return false; } +void SFlowGraphEditor::EnableAllBreakpoints() const +{ + if (DebuggerSubsystem.IsValid()) + { + DebuggerSubsystem->SetAllBreakpointsEnabled(FlowAsset, true); + } +} + +bool SFlowGraphEditor::HasAnyDisabledBreakpoints() const +{ + return DebuggerSubsystem.IsValid() ? DebuggerSubsystem->HasAnyBreakpointsDisabled(FlowAsset) : false; +} + +void SFlowGraphEditor::DisableAllBreakpoints() const +{ + if (DebuggerSubsystem.IsValid()) + { + DebuggerSubsystem->SetAllBreakpointsEnabled(FlowAsset, false); + } +} + +bool SFlowGraphEditor::HasAnyEnabledBreakpoints() const +{ + return DebuggerSubsystem.IsValid() ? DebuggerSubsystem->HasAnyBreakpointsEnabled(FlowAsset) : false; +} + +void SFlowGraphEditor::RemoveAllBreakpoints() const +{ + if (DebuggerSubsystem.IsValid()) + { + DebuggerSubsystem->RemoveAllBreakpoints(FlowAsset); + } +} + +bool SFlowGraphEditor::HasAnyBreakpoints() const +{ + return DebuggerSubsystem.IsValid() ? DebuggerSubsystem->HasAnyBreakpoints(FlowAsset) : false; +} + void SFlowGraphEditor::SetSignalMode(const EFlowSignalMode Mode) const { for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) @@ -1408,7 +1491,7 @@ void SFlowGraphEditor::OnForcePinActivation() void SFlowGraphEditor::FocusViewport() const { // Iterator used but should only contain one node - for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) + for (const UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { const UFlowNode* FlowNode = Cast(SelectedNode->GetFlowNodeBase()); if (UFlowNode* InspectedInstance = FlowNode->GetInspectedInstance()) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp b/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp index ac1672261..5712c0dce 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp @@ -16,11 +16,20 @@ TSharedPtr FFlowGraphUtils::GetFlowAssetEditor(const UEdGraph* TSharedPtr FlowAssetEditor; if (const UFlowAsset* FlowAsset = Cast(Graph)->GetFlowAsset()) { - const TSharedPtr FoundAssetEditor = FToolkitManager::Get().FindEditorForAsset(FlowAsset); - if (FoundAssetEditor.IsValid()) - { - FlowAssetEditor = StaticCastSharedPtr(FoundAssetEditor); - } + FlowAssetEditor = GetFlowAssetEditor(FlowAsset); + } + return FlowAssetEditor; +} + +TSharedPtr FFlowGraphUtils::GetFlowAssetEditor(const UFlowAsset* FlowAsset) +{ + check(FlowAsset); + + TSharedPtr FlowAssetEditor; + const TSharedPtr FoundAssetEditor = FToolkitManager::Get().FindEditorForAsset(FlowAsset); + if (FoundAssetEditor.IsValid()) + { + FlowAssetEditor = StaticCastSharedPtr(FoundAssetEditor); } return FlowAssetEditor; } diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_SubGraph.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_SubGraph.cpp index 4030ba39b..6cd615b1b 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_SubGraph.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_SubGraph.cpp @@ -27,6 +27,6 @@ void UFlowGraphNode_SubGraph::OnNodeDoubleClickedInPIE() const const TWeakObjectPtr SubFlowInstance = GetFlowAsset()->GetFlowInstance(SubGraphNode); if (SubFlowInstance.IsValid()) { - SubGraphNode->GetFlowAsset()->GetTemplateAsset()->SetInspectedInstance(SubFlowInstance->GetDisplayName()); + SubGraphNode->GetFlowAsset()->GetTemplateAsset()->SetInspectedInstance(SubFlowInstance); } } diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index 8c479b8e0..881f6c26e 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -126,7 +126,6 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit protected: virtual void CreateToolbar(); virtual void BindToolbarCommands(); - virtual void InitalizeExtenders(); virtual void RefreshAsset(); virtual void RefreshDetails(); diff --git a/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h b/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h index 0a867e672..20fd406fd 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h @@ -11,13 +11,34 @@ class FFlowAssetEditor; class UFlowAssetEditorContext; class UToolMenu; +////////////////////////////////////////////////////////////////////////// +// Flow Asset Instance Context + +struct FFlowAssetInstanceContext +{ + FText DisplayText; + TArray> AssetInstances; + + FFlowAssetInstanceContext() + { + } + + explicit FFlowAssetInstanceContext(const FText& InDisplayText) + : DisplayText(InDisplayText) + { + } +}; + ////////////////////////////////////////////////////////////////////////// // Flow Asset Instance List class FLOWEDITOR_API SFlowAssetInstanceList : public SCompoundWidget { public: - SLATE_BEGIN_ARGS(SFlowAssetInstanceList) {} + SLATE_BEGIN_ARGS(SFlowAssetInstanceList) + { + } + SLATE_END_ARGS() void Construct(const FArguments& InArgs, const TWeakObjectPtr InTemplateAsset); @@ -25,19 +46,33 @@ class FLOWEDITOR_API SFlowAssetInstanceList : public SCompoundWidget static EVisibility GetDebuggerVisibility(); -private: +protected: void RefreshInstances(); - TSharedRef OnGenerateWidget(TSharedPtr Item) const; - void OnSelectionChanged(TSharedPtr SelectedItem, ESelectInfo::Type SelectionType); + EVisibility GetContextVisibility() const; + TSharedRef OnGenerateContextWidget(TSharedPtr Item); + void OnContextSelectionChanged(TSharedPtr SelectedItem, ESelectInfo::Type SelectionType); + FText GetSelectedContextName() const; + + TSharedRef OnGenerateInstanceWidget(TSharedPtr Item) const; + void OnInstanceSelectionChanged(TSharedPtr SelectedItem, ESelectInfo::Type SelectionType); FText GetSelectedInstanceName() const; + FText JoinInstanceAndContextTexts(const FObjectKey& AssetInstance) const; TWeakObjectPtr TemplateAsset; - TSharedPtr>> Dropdown; - TArray> InstanceNames; - TSharedPtr SelectedInstance; + TSharedPtr>> ContextComboBox; + TSharedPtr>> InstanceComboBox; + TArray> Contexts; + TArray> Instances; + TMap InstancesPerContext; + + TSharedPtr NoContext; + TSharedPtr SelectedContext; + TSharedPtr SelectedInstance; + + static FText AllContextsText; static FText NoInstanceSelectedText; }; @@ -49,29 +84,36 @@ class FLOWEDITOR_API SFlowAssetInstanceList : public SCompoundWidget */ struct FLOWEDITOR_API FFlowBreadcrumb { - const FString AssetPathName; - const FName InstanceName; + const TWeakObjectPtr CurrentInstance; + const TWeakObjectPtr ChildInstance; FFlowBreadcrumb() - : AssetPathName(FString()) - , InstanceName(NAME_None) - {} - - explicit FFlowBreadcrumb(const TWeakObjectPtr FlowAsset) - : AssetPathName(FlowAsset->GetTemplateAsset()->GetPathName()) - , InstanceName(FlowAsset->GetDisplayName()) - {} + : CurrentInstance(nullptr) + , ChildInstance(nullptr) + { + } + + explicit FFlowBreadcrumb(const TWeakObjectPtr InCurrentInstance, const TWeakObjectPtr InChildInstance) + : CurrentInstance(InCurrentInstance) + , ChildInstance(InChildInstance) + { + } }; class FLOWEDITOR_API SFlowAssetBreadcrumb : public SCompoundWidget { public: - SLATE_BEGIN_ARGS(SFlowAssetInstanceList) {} + SLATE_BEGIN_ARGS(SFlowAssetInstanceList) + { + } + SLATE_END_ARGS() void Construct(const FArguments& InArgs, const TWeakObjectPtr InTemplateAsset); private: + EVisibility GetBreadcrumbVisibility() const; + void FillBreadcrumb() const; void OnCrumbClicked(const FFlowBreadcrumb& Item) const; TWeakObjectPtr TemplateAsset; @@ -89,7 +131,7 @@ class FLOWEDITOR_API FFlowAssetToolbar : public TSharedFromThis MakeDiffMenu(const UFlowAssetEditorContext* InContext); - + void BuildDebuggerToolbar(UToolMenu* ToolbarMenu) const; private: diff --git a/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h b/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h index 3cbe13fa8..b05d15517 100644 --- a/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h +++ b/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h @@ -33,5 +33,5 @@ class FLOWEDITOR_API UFlowDebugEditorSubsystem : public UFlowDebuggerSubsystem virtual void OnResumePIE(const bool bIsSimulating); virtual void OnEndPIE(const bool bIsSimulating); - virtual void PauseSession() override; + virtual void PauseSession(const UFlowNode* Node) override; }; diff --git a/Source/FlowEditor/Public/FlowEditorCommands.h b/Source/FlowEditor/Public/FlowEditorCommands.h index d83f84d5b..2629a4adc 100644 --- a/Source/FlowEditor/Public/FlowEditorCommands.h +++ b/Source/FlowEditor/Public/FlowEditorCommands.h @@ -37,13 +37,18 @@ class FLOWEDITOR_API FFlowGraphCommands : public TCommands TSharedPtr AddOutput; TSharedPtr RemovePin; - /** Breakpoints */ + /** Pin Breakpoints */ TSharedPtr AddPinBreakpoint; TSharedPtr RemovePinBreakpoint; TSharedPtr EnablePinBreakpoint; TSharedPtr DisablePinBreakpoint; TSharedPtr TogglePinBreakpoint; + /** Breakpoints */ + TSharedPtr EnableAllBreakpoints; + TSharedPtr DisableAllBreakpoints; + TSharedPtr RemoveAllBreakpoints; + /** Execution Override */ TSharedPtr EnableNode; TSharedPtr DisableNode; diff --git a/Source/FlowEditor/Public/FlowEditorModule.h b/Source/FlowEditor/Public/FlowEditorModule.h index 8dec688cf..458a106e3 100644 --- a/Source/FlowEditor/Public/FlowEditorModule.h +++ b/Source/FlowEditor/Public/FlowEditorModule.h @@ -31,15 +31,15 @@ class FLOWEDITOR_API FFlowEditorModule : public IModuleInterface, public IHasMen TSet CustomClassLayouts; TSet CustomStructLayouts; - TSharedPtr MenuExtensibilityManager; - TSharedPtr ToolBarExtensibilityManager; - public: virtual void StartupModule() override; virtual void ShutdownModule() override; - virtual TSharedPtr GetMenuExtensibilityManager() override { return MenuExtensibilityManager; } - virtual TSharedPtr GetToolBarExtensibilityManager() override { return ToolBarExtensibilityManager; } + UE_DEPRECATED(5.5, "The old method has been removed. Please use UToolMenus::Get()->ExtendMenu() instead. You can find example in SFlowGraphEditor::CreateDebugMenu().") + virtual TSharedPtr GetMenuExtensibilityManager() override { return nullptr; } + + UE_DEPRECATED(5.5, "The old method has been removed. Please use UToolMenus::Get()->ExtendMenu() instead. You can find example in SFlowGraphEditor::CreateDebugMenu().") + virtual TSharedPtr GetToolBarExtensibilityManager() override { return nullptr; } private: void TrySetFlowNodeDisplayStyleDefaults() const; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h index 331ebb1e1..15c6ee9b5 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h @@ -38,10 +38,12 @@ class FLOWEDITOR_API SFlowGraphEditor : public SGraphEditor public: void Construct(const FArguments& InArgs, const TSharedPtr InAssetEditor); + virtual void CreateDebugMenu(); virtual void BindGraphCommands(); virtual FGraphAppearanceInfo GetGraphAppearanceInfo() const; virtual FText GetCornerText() const; + virtual FText GetPIENotifyText() const; private: static void UndoGraphAction(); @@ -147,6 +149,15 @@ class FLOWEDITOR_API SFlowGraphEditor : public SGraphEditor bool CanToggleBreakpoint() const; bool CanTogglePinBreakpoint(); + void EnableAllBreakpoints() const; + bool HasAnyDisabledBreakpoints() const; + + void DisableAllBreakpoints() const; + bool HasAnyEnabledBreakpoints() const; + + void RemoveAllBreakpoints() const; + bool HasAnyBreakpoints() const; + void SetSignalMode(const EFlowSignalMode Mode) const; bool CanSetSignalMode(const EFlowSignalMode Mode) const; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphUtils.h b/Source/FlowEditor/Public/Graph/FlowGraphUtils.h index bb7731dac..fecb73ee5 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphUtils.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphUtils.h @@ -5,6 +5,7 @@ #include "CoreMinimal.h" #include "Templates/SharedPointer.h" +class UFlowAsset; class FFlowAssetEditor; class SFlowGraphEditor; class UEdGraph; @@ -15,6 +16,7 @@ class FLOWEDITOR_API FFlowGraphUtils FFlowGraphUtils() {} static TSharedPtr GetFlowAssetEditor(const UEdGraph* Graph); + static TSharedPtr GetFlowAssetEditor(const UFlowAsset* FlowAsset); static TSharedPtr GetFlowGraphEditor(const UEdGraph* Graph); static FString RemovePrefixFromNodeText(const FText& Source); From 2792a33d220b9b124aefe7d8455f10963dc72e69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Mon, 26 Jan 2026 23:23:47 +0100 Subject: [PATCH 382/485] typo fixed --- Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp index 4c0214241..4ef7e0220 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp @@ -250,7 +250,7 @@ FText SFlowAssetInstanceList::JoinInstanceAndContextTexts(const FObjectKey& Asse { static const FText OpeningBracket = FText::AsCultureInvariant(TEXT("(")); static const FText ClosingBracket = FText::AsCultureInvariant(TEXT(")")); - FText::Format(Result, OpeningBracket, Context->DisplayText, ClosingBracket); + Result = FText::Format(Result, OpeningBracket, Context->DisplayText, ClosingBracket); } } From 13b557661924e77b11484d058fdb85e5520ef65e Mon Sep 17 00:00:00 2001 From: LindyHopperGT <91915878+LindyHopperGT@users.noreply.github.com> Date: Tue, 27 Jan 2026 09:56:25 -0800 Subject: [PATCH 383/485] Converted the syntax for creating FFlowDataPinResults structs to be uniform (#329) --- .../Types/FlowDataPinBlueprintLibrary.cpp | 129 +++--------------- .../Types/FlowDataPinValuesStandard.cpp | 24 ++++ .../Public/Types/FlowDataPinValuesStandard.h | 3 + 3 files changed, 46 insertions(+), 110 deletions(-) diff --git a/Source/Flow/Private/Types/FlowDataPinBlueprintLibrary.cpp b/Source/Flow/Private/Types/FlowDataPinBlueprintLibrary.cpp index 383e04522..f756bcbcd 100644 --- a/Source/Flow/Private/Types/FlowDataPinBlueprintLibrary.cpp +++ b/Source/Flow/Private/Types/FlowDataPinBlueprintLibrary.cpp @@ -2167,198 +2167,107 @@ TArray UFlowDataPinBlueprintLibrary::GetClassValues(FFlowDataPin FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_Bool(const TArray& BoolValues) { - FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); - - FFlowDataPinValue_Bool ValueStruct; - ValueStruct.Values = BoolValues; - Out.ResultValue = TInstancedStruct::Make(ValueStruct); - + FFlowDataPinResult Out{ FFlowDataPinValue_Bool(BoolValues) }; return Out; } FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_Int(const TArray& IntValues) { - FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); - - FFlowDataPinValue_Int ValueStruct; - ValueStruct.Values = IntValues; - Out.ResultValue = TInstancedStruct::Make(ValueStruct); - + FFlowDataPinResult Out{ FFlowDataPinValue_Int(IntValues) }; return Out; } FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_Int64(const TArray& Int64Values) { - FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); - - FFlowDataPinValue_Int64 ValueStruct; - ValueStruct.Values = Int64Values; - Out.ResultValue = TInstancedStruct::Make(ValueStruct); - + FFlowDataPinResult Out{ FFlowDataPinValue_Int64(Int64Values) }; return Out; } FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_Float(const TArray& FloatValues) { - FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); - - FFlowDataPinValue_Float ValueStruct; - ValueStruct.Values = FloatValues; - Out.ResultValue = TInstancedStruct::Make(ValueStruct); - + FFlowDataPinResult Out{ FFlowDataPinValue_Float(FloatValues) }; return Out; } FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_Double(const TArray& DoubleValues) { - FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); - - FFlowDataPinValue_Double ValueStruct; - ValueStruct.Values = DoubleValues; - Out.ResultValue = TInstancedStruct::Make(ValueStruct); - + FFlowDataPinResult Out{ FFlowDataPinValue_Double(DoubleValues) }; return Out; } FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_Name(const TArray& NameValues) { - FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); - - FFlowDataPinValue_Name ValueStruct; - ValueStruct.Values = NameValues; - Out.ResultValue = TInstancedStruct::Make(ValueStruct); - + FFlowDataPinResult Out{ FFlowDataPinValue_Name(NameValues) }; return Out; } FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_String(const TArray& StringValues) { - FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); - - FFlowDataPinValue_String ValueStruct; - ValueStruct.Values = StringValues; - Out.ResultValue = TInstancedStruct::Make(ValueStruct); - + FFlowDataPinResult Out{ FFlowDataPinValue_String(StringValues) }; return Out; } FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_Text(const TArray& TextValues) { - FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); - - FFlowDataPinValue_Text ValueStruct; - ValueStruct.Values = TextValues; - Out.ResultValue = TInstancedStruct::Make(ValueStruct); - + FFlowDataPinResult Out{ FFlowDataPinValue_Text(TextValues) }; return Out; } FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_Enum(const TArray& EnumValues, UEnum* EnumClass) { - FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); - if (EnumClass) { - FFlowDataPinValue_Enum ValueStruct; - ValueStruct.EnumClass = EnumClass; - ValueStruct.Values.Reserve(EnumValues.Num()); - - for (uint8 RawValue : EnumValues) - { - const FName EnumName = EnumClass->GetNameByValue(RawValue); - ValueStruct.Values.Add(EnumName); - } - - Out.ResultValue = TInstancedStruct::Make(ValueStruct); + FFlowDataPinResult Out{ FFlowDataPinValue_Enum(*EnumClass, EnumValues) }; + return Out; } - return Out; + return FFlowDataPinResult(EFlowDataPinResolveResult::FailedUnknownEnumValue); } FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_Vector(const TArray& VectorValues) { - FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); - - FFlowDataPinValue_Vector ValueStruct; - ValueStruct.Values = VectorValues; - Out.ResultValue = TInstancedStruct::Make(ValueStruct); - + FFlowDataPinResult Out{ FFlowDataPinValue_Vector(VectorValues) }; return Out; } FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_Rotator(const TArray& RotatorValues) { - FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); - - FFlowDataPinValue_Rotator ValueStruct; - ValueStruct.Values = RotatorValues; - Out.ResultValue = TInstancedStruct::Make(ValueStruct); - + FFlowDataPinResult Out{ FFlowDataPinValue_Rotator(RotatorValues) }; return Out; } FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_Transform(const TArray& TransformValues) { - FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); - - FFlowDataPinValue_Transform ValueStruct; - ValueStruct.Values = TransformValues; - Out.ResultValue = TInstancedStruct::Make(ValueStruct); - + FFlowDataPinResult Out{ FFlowDataPinValue_Transform(TransformValues) }; return Out; } FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_GameplayTag(const TArray& GameplayTagValues) { - FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); - - FFlowDataPinValue_GameplayTag ValueStruct; - ValueStruct.Values = GameplayTagValues; - Out.ResultValue = TInstancedStruct::Make(ValueStruct); - + FFlowDataPinResult Out{ FFlowDataPinValue_GameplayTag(GameplayTagValues) }; return Out; } FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_GameplayTagContainer(FGameplayTagContainer GameplayTagContainerValue) { - FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); - - FFlowDataPinValue_GameplayTagContainer ValueStruct; - ValueStruct.Values = GameplayTagContainerValue; - Out.ResultValue = TInstancedStruct::Make(ValueStruct); - + FFlowDataPinResult Out{ FFlowDataPinValue_GameplayTagContainer(GameplayTagContainerValue) }; return Out; } FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_InstancedStruct(const TArray& InstancedStructValues) { - FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); - - FFlowDataPinValue_InstancedStruct ValueStruct; - ValueStruct.Values = InstancedStructValues; - Out.ResultValue = TInstancedStruct::Make(ValueStruct); - + FFlowDataPinResult Out{ FFlowDataPinValue_InstancedStruct(InstancedStructValues) }; return Out; } FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_Object(const TArray& ObjectValues) { - FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); - - FFlowDataPinValue_Object ValueStruct; - ValueStruct.Values = ObjectValues; - Out.ResultValue = TInstancedStruct::Make(ValueStruct); - + FFlowDataPinResult Out{ FFlowDataPinValue_Object(ObjectValues) }; return Out; } FFlowDataPinResult UFlowDataPinBlueprintLibrary::MakeFlowDataPinResult_Class(const TArray& ClassValues) { - FFlowDataPinResult Out(EFlowDataPinResolveResult::Success); - - FFlowDataPinValue_Class ValueStruct; - ValueStruct.Values = ClassValues; - - Out.ResultValue = TInstancedStruct::Make(ValueStruct); + FFlowDataPinResult Out{ FFlowDataPinValue_Class(ClassValues) }; return Out; } \ No newline at end of file diff --git a/Source/Flow/Private/Types/FlowDataPinValuesStandard.cpp b/Source/Flow/Private/Types/FlowDataPinValuesStandard.cpp index 2d8b4db79..5e1e7627a 100644 --- a/Source/Flow/Private/Types/FlowDataPinValuesStandard.cpp +++ b/Source/Flow/Private/Types/FlowDataPinValuesStandard.cpp @@ -267,6 +267,22 @@ FFlowDataPinValue_Enum::FFlowDataPinValue_Enum(const TSoftObjectPtr& InEn #endif } +FFlowDataPinValue_Enum::FFlowDataPinValue_Enum(UEnum& InEnumClass, const TArray& InValues) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Array; +#endif + + EnumClass = &InEnumClass; + Values.Reserve(InValues.Num()); + + for (uint8 RawValue : InValues) + { + const FName EnumValueName = InEnumClass.GetNameByValue(RawValue); + Values.Add(EnumValueName); + } +} + #if WITH_EDITOR void FFlowDataPinValue_Enum::OnEnumNameChanged() { @@ -532,6 +548,14 @@ FFlowDataPinValue_Object::FFlowDataPinValue_Object(const TArray& InActo } } +FFlowDataPinValue_Object::FFlowDataPinValue_Object(const TArray& InObjects, UClass* InClassFilter) +{ +#if WITH_EDITOR + MultiType = EFlowDataMultiType::Array; +#endif + Values = InObjects; +} + bool FFlowDataPinValue_Object::TryConvertValuesToString(FString& OutString) const { OutString = FlowArray::FormatArrayString(Values, diff --git a/Source/Flow/Public/Types/FlowDataPinValuesStandard.h b/Source/Flow/Public/Types/FlowDataPinValuesStandard.h index bfe5a18a2..245921e5f 100644 --- a/Source/Flow/Public/Types/FlowDataPinValuesStandard.h +++ b/Source/Flow/Public/Types/FlowDataPinValuesStandard.h @@ -228,6 +228,8 @@ struct FFlowDataPinValue_Enum : public FFlowDataPinValue FLOW_API FFlowDataPinValue_Enum() = default; FLOW_API FFlowDataPinValue_Enum(const TSoftObjectPtr& InEnumClass, const ValueType& InValue); FLOW_API FFlowDataPinValue_Enum(const TSoftObjectPtr& InEnumClass, const TArray& InValues); + FLOW_API FFlowDataPinValue_Enum(UEnum& InEnumClass, const TArray& InValues); + #if WITH_EDITOR FLOW_API void OnEnumNameChanged(); @@ -458,6 +460,7 @@ struct FFlowDataPinValue_Object : public FFlowDataPinValue FLOW_API FFlowDataPinValue_Object() = default; FLOW_API FFlowDataPinValue_Object(TObjectPtr InObject, UClass* InClassFilter = UObject::StaticClass()); FLOW_API FFlowDataPinValue_Object(const TArray>& InObjects, UClass* InClassFilter = UObject::StaticClass()); + FLOW_API FFlowDataPinValue_Object(const TArray& InObjects, UClass* InClassFilter = UObject::StaticClass()); FLOW_API FFlowDataPinValue_Object(AActor* InActor, UClass* InClassFilter = nullptr /* nullptr here defaults to AActor::StaticClass() */ ); FLOW_API FFlowDataPinValue_Object(const TArray& InActors, UClass* InClassFilter = nullptr /* nullptr here defaults to AActor::StaticClass() */); From 4a468988e89966a5702c1d2c0566708466aef6fc Mon Sep 17 00:00:00 2001 From: Numblaze Date: Tue, 27 Jan 2026 20:30:44 +0100 Subject: [PATCH 384/485] Flow Node Validation enhancements (#298) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Flow Node Validation improvements - UFlowNode::ValidateNode() is now blueprint-implementable - UFlowNode now has functions to log validation errors, warnings and notes - UFlowAsset validation fix : if the validation contains only warnings and/or notes but no errors, the messages are still logged * Moved validation code from UFlowNode to UFlowNodeBase Also added meta = DevelopmentOnly for blueprint validation functions * Flow Node AddOns validation handled in UFlowAsset::ValidateAsset * Update FlowNodeBase.cpp * Added parent node support for Flow Node AddOns ++ UFlowNodeBase::GetParentNode ++ UFlowNodeAddon property ParentNode (editor only) with getter (overiding UFlowNodeBase::GetParentNode) and setter ++ UFlowNode returns self for GetParentNode ++ UFlowGraphNode::SetParentNodeForSubNode attempts to set the parent node to the flow node instance if it is an addOn - UFlowGraphNode::AddSubNode SubNode->SetParentNodeForSubNode is executed after SubNode->PostPlacedNewNode() to ensure the instance is set for parent detection - Updated FFlowGraphToken::FFlowGraphToken to use the new getter of the parent node * integrate calling K2_ValidateNode with validating flow pins * replaced caching editor-time pointer to Owning Flow Node with overriding GetGraphNode we might want to cache editor-time pointer to owning Flow Node t may require more work than just caching it, as pointer needs to be updated during editor operations --------- Co-authored-by: Krzysiek Justyński --- Source/Flow/Private/AddOns/FlowNodeAddOn.cpp | 26 ++++++-- Source/Flow/Private/FlowAsset.cpp | 59 ++++++++++++++++++- Source/Flow/Private/FlowMessageLog.cpp | 4 +- Source/Flow/Private/Nodes/FlowNode.cpp | 4 +- Source/Flow/Private/Nodes/FlowNodeBase.cpp | 42 ++++++++++++- Source/Flow/Public/AddOns/FlowNodeAddOn.h | 12 ++-- Source/Flow/Public/FlowAsset.h | 6 ++ Source/Flow/Public/Nodes/FlowNode.h | 14 ++++- Source/Flow/Public/Nodes/FlowNodeBase.h | 54 ++++++++++++----- .../Private/Graph/Nodes/FlowGraphNode.cpp | 1 + 10 files changed, 186 insertions(+), 36 deletions(-) diff --git a/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp b/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp index 67da90d0e..87537f30b 100644 --- a/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp +++ b/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp @@ -17,6 +17,21 @@ UFlowNodeAddOn::UFlowNodeAddOn() #endif } +#if WITH_EDITOR +UEdGraphNode* UFlowNodeAddOn::GetGraphNode() const +{ + // todo: we might want to cache editor-time pointer to owning Flow Node + // it may require more work than just caching it, as pointer needs + // to be updated during editor operations + if (const UFlowNode* OwningFlowNode = FindOwningFlowNode()) + { + return OwningFlowNode->GetGraphNode(); + } + + return nullptr; +} +#endif + void UFlowNodeAddOn::InitializeInstance() { CacheFlowNode(); @@ -65,9 +80,8 @@ EFlowAddOnAcceptResult UFlowNodeAddOn::AcceptFlowNodeAddOnParent_Implementation( UFlowNode* UFlowNodeAddOn::GetFlowNode() const { - // We are making the assumption that this would addlways be known - // during runtime and that we are not calling this method before the addon has been - // initialized. + // We are making the assumption that this would always be known during runtime + // and that we are not calling this method before the addon has been initialized. ensure(FlowNode); return FlowNode; @@ -122,7 +136,7 @@ bool UFlowNodeAddOn::IsSupportedInputPinName(const FName& PinName) const void UFlowNodeAddOn::CacheFlowNode() { FlowNode = FindOwningFlowNode(); - + ensureAsRuntimeWarning(FlowNode); } @@ -132,7 +146,7 @@ TArray UFlowNodeAddOn::GetPinsForContext(const TArray& Conte TArray ContextPins = Super::GetContextInputs(); ContextPins.Reserve(ContextPins.Num() + Context.Num()); - + for (const FFlowPin& InputPin : Context) { if (InputPin.IsValid()) @@ -160,6 +174,6 @@ TArray UFlowNodeAddOn::GetContextOutputs() const void UFlowNodeAddOn::RequestReconstructionOnOwningFlowNode() const { - (void) OnAddOnRequestedParentReconstruction.ExecuteIfBound(); + (void)OnAddOnRequestedParentReconstruction.ExecuteIfBound(); } #endif // WITH_EDITOR diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index c4b1305ca..44c351c3d 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -35,7 +35,9 @@ #include "UObject/Package.h" FString UFlowAsset::ValidationError_NodeClassNotAllowed = TEXT("Node class {0} is not allowed in this asset."); +FString UFlowAsset::ValidationError_AddOnNodeClassNotAllowed = TEXT("AddOn Node class {0} is not allowed in this asset."); FString UFlowAsset::ValidationError_NullNodeInstance = TEXT("Node with GUID {0} is NULL"); +FString UFlowAsset::ValidationError_NullAddOnNodeInstance = TEXT("Node with GUID {0} has NULL AddOn(s)"); #endif #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowAsset) @@ -259,9 +261,21 @@ EDataValidationResult UFlowAsset::ValidateAsset(FFlowMessageLog& MessageLog) } Node.Value->ValidationLog.Messages.Empty(); - if (Node.Value->ValidateNode() == EDataValidationResult::Invalid) + Node.Value->ValidateNode(); + MessageLog.Messages.Append(Node.Value->ValidationLog.Messages); + + // Validate AddOns + for (UFlowNodeAddOn* AddOn : Node.Value->GetFlowNodeAddOnChildren()) { - MessageLog.Messages.Append(Node.Value->ValidationLog.Messages); + if (IsValid(AddOn)) + { + ValidateAddOnTree(*AddOn, MessageLog); + } + else + { + const FString ErrorMsg = FString::Format(*ValidationError_NullAddOnNodeInstance, { *Node.Key.ToString() }); + MessageLog.Error(*ErrorMsg, this); + } } } else @@ -271,7 +285,17 @@ EDataValidationResult UFlowAsset::ValidateAsset(FFlowMessageLog& MessageLog) } } - return MessageLog.Messages.Num() > 0 ? EDataValidationResult::Invalid : EDataValidationResult::Valid; + // if at least one error has been has been logged : mark the asset as invalid + for (const TSharedRef& Msg : MessageLog.Messages) + { + if (Msg->GetSeverity() == EMessageSeverity::Error) + { + return EDataValidationResult::Invalid; + } + } + + // otherwise, the asset is considered valid (even with warnings or notes) + return EDataValidationResult::Valid; } bool UFlowAsset::IsNodeOrAddOnClassAllowed(const UClass* FlowNodeOrAddOnClass, FText* OutOptionalFailureReason) const @@ -376,6 +400,35 @@ bool UFlowAsset::IsFlowNodeClassInDeniedClasses(const UClass& FlowNodeClass) con return false; } +void UFlowAsset::ValidateAddOnTree(UFlowNodeAddOn& AddOn, FFlowMessageLog& MessageLog) +{ + // Filter unauthorized addon nodes + FText FailureReason; + if (!IsNodeOrAddOnClassAllowed(AddOn.GetClass(), &FailureReason)) + { + const FString ErrorMsg = + FailureReason.IsEmpty() + ? FString::Format(*ValidationError_AddOnNodeClassNotAllowed, { *AddOn.GetClass()->GetName() }) + : FailureReason.ToString(); + + MessageLog.Error(*ErrorMsg, AddOn.GetFlowNodeSelfOrOwner()); + } + + // Validate AddOn + AddOn.ValidationLog.Messages.Empty(); + AddOn.ValidateNode(); + MessageLog.Messages.Append(AddOn.ValidationLog.Messages); + + // Validate Children + for (UFlowNodeAddOn* Child : AddOn.GetFlowNodeAddOnChildren()) + { + if (IsValid(Child)) + { + ValidateAddOnTree(*Child, MessageLog); + } + } +} + bool UFlowAsset::IsFlowNodeClassInAllowedClasses(const UClass& FlowNodeClass, const TSubclassOf& RequiredAncestor) const { diff --git a/Source/Flow/Private/FlowMessageLog.cpp b/Source/Flow/Private/FlowMessageLog.cpp index 39c1d2997..fd3feb5d8 100644 --- a/Source/Flow/Private/FlowMessageLog.cpp +++ b/Source/Flow/Private/FlowMessageLog.cpp @@ -57,7 +57,7 @@ TSharedPtr FFlowGraphToken::Create(const UFlowNodeBase* InFlowNod Message.AddToken(MakeShareable(new FFlowGraphToken(InFlowNodeBase))); return Message.GetMessageTokens().Last(); } - + return nullptr; } @@ -79,7 +79,7 @@ TSharedPtr FFlowGraphToken::Create(const UEdGraphPin* InPin, FTok Message.AddToken(MakeShareable(new FFlowGraphToken(InPin->GetOwningNode(), InPin))); return Message.GetMessageTokens().Last(); } - + return nullptr; } diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index c281c9a09..9ee950557 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -46,7 +46,6 @@ UFlowNode::UFlowNode(const FObjectInitializer& ObjectInitializer) } #if WITH_EDITOR - void UFlowNode::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { Super::PostEditChangeProperty(PropertyChangedEvent); @@ -68,7 +67,7 @@ void UFlowNode::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEve EDataValidationResult UFlowNode::ValidateNode() { - EDataValidationResult ValidationResult = EDataValidationResult::Valid; + EDataValidationResult ValidationResult = Super::ValidateNode(); // Validate that output and input pins have unique names TSet UniquePinNames; @@ -99,7 +98,6 @@ void UFlowNode::ValidateFlowPinArrayIsUnique(const TArray& FlowPins, T } } } - #endif void UFlowNode::PostLoad() diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp index fd80a5977..5f55d2f49 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBase.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -268,8 +268,30 @@ FString UFlowNodeBase::GetStatusString() const { return K2_GetStatusString(); } + #endif // WITH_EDITOR +void UFlowNodeBase::LogValidationError(const FString& Message) +{ +#if WITH_EDITOR + ValidationLog.Error(*Message, this); +#endif +} + +void UFlowNodeBase::LogValidationWarning(const FString& Message) +{ +#if WITH_EDITOR + ValidationLog.Warning(*Message, this); +#endif +} + +void UFlowNodeBase::LogValidationNote(const FString& Message) +{ +#if WITH_EDITOR + ValidationLog.Note(*Message, this); +#endif +} + UFlowAsset* UFlowNodeBase::GetFlowAsset() const { // In the case of an AddOn, we want our containing FlowNode's Outer, not our own @@ -565,6 +587,11 @@ void UFlowNodeBase::SetGraphNode(UEdGraphNode* NewGraphNode) UpdateNodeConfigText(); } +void UFlowNodeBase::SetCanDelete(const bool CanDelete) +{ + bCanDelete = CanDelete; +} + void UFlowNodeBase::SetupForEditing(UEdGraphNode& EdGraphNode) { SetGraphNode(&EdGraphNode); @@ -930,6 +957,18 @@ bool UFlowNodeBase::BuildMessage(FString& Message) const } #endif +EDataValidationResult UFlowNodeBase::ValidateNode() +{ + EDataValidationResult ValidationResult = EDataValidationResult::NotValidated; + + if (GetClass()->IsFunctionImplementedInScript(GET_FUNCTION_NAME_CHECKED(UFlowNodeBase, K2_ValidateNode))) + { + ValidationResult = K2_ValidateNode(); + } + + return ValidationResult; +} + bool UFlowNodeBase::TryAddValueToFormatNamedArguments(const FFlowNamedDataPinProperty& NamedDataPinProperty, FFormatNamedArguments& InOutArguments) const { const FFlowDataPinValue& DataPinValue = NamedDataPinProperty.DataPinValue.Get(); @@ -1114,4 +1153,5 @@ FFlowDataPinResult_Class UFlowNodeBase::TryResolveDataPinAsClass(const FName& Pi ResolveResult.SetValueFromObjectPtr(Value); return ResolveResult; } -// -- \ No newline at end of file + +// -- diff --git a/Source/Flow/Public/AddOns/FlowNodeAddOn.h b/Source/Flow/Public/AddOns/FlowNodeAddOn.h index 47fb5ab4c..aabea5c4d 100644 --- a/Source/Flow/Public/AddOns/FlowNodeAddOn.h +++ b/Source/Flow/Public/AddOns/FlowNodeAddOn.h @@ -17,6 +17,9 @@ class UFlowNodeAddOn : public UFlowNodeBase { GENERATED_BODY() +public: + FLOW_API UFlowNodeAddOn(); + protected: // The FlowNode that contains this AddOn // (accessible only when initialized, runtime only) @@ -33,13 +36,14 @@ class UFlowNodeAddOn : public UFlowNodeBase UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "FlowNodeAddOn") TArray OutputPins; #endif - -public: - - FLOW_API UFlowNodeAddOn(); +public: // UFlowNodeBase +#if WITH_EDITOR + virtual UEdGraphNode* GetGraphNode() const override; +#endif + // AddOns may opt in to be eligible for a given parent // - ParentTemplate - the template of the FlowNode or FlowNodeAddOn that is being considered as a potential parent // - AdditionalAddOnsToAssumeAreChildren - other AddOns to assume that are already child AddOns for the purposes of this test. diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index e2889cb17..ee470558d 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -75,7 +75,9 @@ class FLOW_API UFlowAsset : public UObject FSimpleDelegate OnDetailsRefreshRequested; static FString ValidationError_NodeClassNotAllowed; + static FString ValidationError_AddOnNodeClassNotAllowed; static FString ValidationError_NullNodeInstance; + static FString ValidationError_NullAddOnNodeInstance; private: UPROPERTY() @@ -100,6 +102,10 @@ class FLOW_API UFlowAsset : public UObject bool IsFlowNodeClassInAllowedClasses(const UClass& FlowNodeClass, const TSubclassOf& RequiredAncestor = nullptr) const; bool IsFlowNodeClassInDeniedClasses(const UClass& FlowNodeClass) const; + +private: + // Recursively validates the given addon and its children. + void ValidateAddOnTree(UFlowNodeAddOn& AddOn, FFlowMessageLog& MessageLog); #endif ////////////////////////////////////////////////////////////////////////// diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 9e2aec140..8a60acfb2 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -63,9 +63,6 @@ class FLOW_API UFlowNode // UObject virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; // -- - - virtual EDataValidationResult ValidateNode(); - void ValidateFlowPinArrayIsUnique(const TArray& FlowPins, TSet& InOutUniquePinNames, EDataValidationResult& InOutResult); #endif // Inherits Guid after graph node @@ -83,6 +80,11 @@ class FLOW_API UFlowNode // by default based on the node Guid, // but may be overridden in subclasses to supply some other value. virtual int32 GetRandomSeed() const override { return GetTypeHash(NodeGuid); } + + virtual const UFlowNode* GetParentNode() const override + { + return UFlowNodeBase::GetFlowNodeSelfOrOwner(); + } public: virtual bool CanFinishGraph() const { return false; } @@ -297,6 +299,12 @@ class FLOW_API UFlowNode static FString MissingClass; static FString NoActorsFound; +#if WITH_EDITOR +protected: + virtual EDataValidationResult ValidateNode() override; + void ValidateFlowPinArrayIsUnique(const TArray& FlowPins, TSet& InOutUniquePinNames, EDataValidationResult& InOutResult); +#endif + ////////////////////////////////////////////////////////////////////////// // Executing node instance diff --git a/Source/Flow/Public/Nodes/FlowNodeBase.h b/Source/Flow/Public/Nodes/FlowNodeBase.h index e5441eff0..be3e17604 100644 --- a/Source/Flow/Public/Nodes/FlowNodeBase.h +++ b/Source/Flow/Public/Nodes/FlowNodeBase.h @@ -106,6 +106,9 @@ class FLOW_API UFlowNodeBase UFUNCTION(BlueprintPure, Category = "FlowNode") virtual int32 GetRandomSeed() const PURE_VIRTUAL(GetRandomSeed, return 0;); + // Returns the owning top-level Flow node. + virtual const UFlowNode* GetParentNode() const PURE_VIRTUAL(GetParentNode, return nullptr;); + ////////////////////////////////////////////////////////////////////////// // Pins @@ -327,13 +330,13 @@ class FLOW_API UFlowNodeBase protected: UPROPERTY() TObjectPtr GraphNode; - + UPROPERTY(EditDefaultsOnly, Category = "FlowNode") uint8 bDisplayNodeTitleWithoutPrefix : 1; - + uint8 bCanDelete : 1 ; uint8 bCanDuplicate : 1; - + UPROPERTY(EditDefaultsOnly, Category = "FlowNode") bool bNodeDeprecated; @@ -343,15 +346,16 @@ class FLOW_API UFlowNodeBase FFlowNodeEvent OnReconstructionRequested; FFlowNodeEvent OnAddOnRequestedParentReconstruction; - FFlowMessageLog ValidationLog; #endif // WITH_EDITORONLY_DATA #if WITH_EDITOR public: virtual void PostLoad() override; - + void SetGraphNode(UEdGraphNode* NewGraphNode); - UEdGraphNode* GetGraphNode() const { return GraphNode; } + virtual UEdGraphNode* GetGraphNode() const { return GraphNode; } + + void SetCanDelete(const bool CanDelete); // Set up UFlowNodeBase when being opened for edit in the editor virtual void SetupForEditing(UEdGraphNode& EdGraphNode); @@ -362,18 +366,14 @@ class FLOW_API UFlowNodeBase // UObject virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; // -- - + + void RequestReconstruction() const { (void) OnReconstructionRequested.ExecuteIfBound(); }; + // used when import graph from another asset virtual void PostImport() {} // Called by owning FlowNode to add to its Status String. - // (may be multi-line) virtual FString GetStatusString() const; - - void RequestReconstruction() const { (void) OnReconstructionRequested.ExecuteIfBound(); }; - - void SetCanDelete(bool CanDelete) { bCanDelete = CanDelete;} - #endif protected: @@ -445,7 +445,12 @@ class FLOW_API UFlowNodeBase ////////////////////////////////////////////////////////////////////////// // Debug support - + +#if WITH_EDITORONLY_DATA +protected: + FFlowMessageLog ValidationLog; +#endif // WITH_EDITORONLY_DATA + #if WITH_EDITOR public: // Short summary of node's content - displayed over node as NodeInfoPopup @@ -474,6 +479,27 @@ class FLOW_API UFlowNodeBase protected: bool BuildMessage(FString& Message) const; #endif + +#if WITH_EDITOR + virtual EDataValidationResult ValidateNode(); +#endif + + // Optional validation override for Blueprints + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode|Validation", meta = (DisplayName = "Validate Node", DevelopmentOnly)) + EDataValidationResult K2_ValidateNode(); + + // Log validation error (editor-only) + UFUNCTION(BlueprintCallable, Category = "FlowNode|Validation", meta = (DevelopmentOnly)) + void LogValidationError(const FString& Message); + + // Log validation warning (editor-only) + UFUNCTION(BlueprintCallable, Category = "FlowNode|Validation", meta = (DevelopmentOnly)) + void LogValidationWarning(const FString& Message); + + // Log validation note (editor-only) + UFUNCTION(BlueprintCallable, Category = "FlowNode|Validation", meta = (DevelopmentOnly)) + void LogValidationNote(const FString& Message); + // -- }; // Templates & inline implementations: diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 420b5bdc7..9864a54e4 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -1491,6 +1491,7 @@ void UFlowGraphNode::AddSubNode(UFlowGraphNode* SubNode, class UEdGraph* ParentG SubNode->CreateNewGuid(); SubNode->PostPlacedNewNode(); + SubNode->AllocateDefaultPins(); SubNode->AutowireNewNode(nullptr); From f4056089b8b9bc651d3e2d480d8f4148c19922f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Tue, 27 Jan 2026 21:13:02 +0100 Subject: [PATCH 385/485] Exposed methods to public to enable flow node status display in runtime debuggers #319 Updated UFlowNode class layout a bit, same order of Data Pin methods in header and cpp --- Source/Flow/Private/Nodes/FlowNode.cpp | 158 ++++++++++----------- Source/Flow/Private/Nodes/FlowNodeBase.cpp | 13 +- Source/Flow/Public/Nodes/FlowNode.h | 96 +++++++------ Source/Flow/Public/Nodes/FlowNodeBase.h | 3 +- 4 files changed, 139 insertions(+), 131 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 9ee950557..753f36e93 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -415,54 +415,36 @@ void UFlowNode::SetAutoOutputDataPins(const TArray& AutoOutputPins) AutoOutputDataPins = AutoOutputPins; } -void UFlowNode::SetConnections(const TMap& InConnections) -{ - TMap OldConnections = Connections; - Connections = InConnections; - OnConnectionsChanged(OldConnections); -} - #endif // WITH_EDITOR -// #FlowDataPinLegacy -void UFlowNode::FixupDataPinTypes() -{ - FixupDataPinTypesForArray(InputPins); - FixupDataPinTypesForArray(OutputPins); -#if WITH_EDITOR - FixupDataPinTypesForArray(AutoInputDataPins); - FixupDataPinTypesForArray(AutoOutputDataPins); -#endif -} - -void UFlowNode::FixupDataPinTypesForArray(TArray& MutableDataPinArray) +FFlowDataPinResult UFlowNode::TrySupplyDataPin_Implementation(FName PinName) const { - for (FFlowPin& MutableFlowPin : MutableDataPinArray) + const FFlowPin* FlowPin = FindOutputPinByName(PinName); + if (!FlowPin) { - FixupDataPinTypesForPin(MutableFlowPin); + // Also look in the Input Pins (for supplying default values for unconnected pins) + FlowPin = FindInputPinByName(PinName); + if (!FlowPin) + { + return FFlowDataPinResult(EFlowDataPinResolveResult::FailedUnknownPin); + } } -} -void UFlowNode::FixupDataPinTypesForPin(FFlowPin& MutableDataPin) -{ - const FFlowPinTypeName NewPinTypeName = FFlowPin::GetPinTypeNameForLegacyPinType(MutableDataPin.PinType); - - if (!NewPinTypeName.IsNone()) + const FFlowPinType* DataPinType = FlowPin->ResolveFlowPinType(); + if (!DataPinType) { - MutableDataPin.SetPinTypeName(NewPinTypeName); + return FFlowDataPinResult(EFlowDataPinResolveResult::FailedMismatchedType); } - if (MutableDataPin.GetPinTypeName().IsNone()) + FFlowDataPinResult SuppliedResult; + if (TryGatherPropertyOwnersAndPopulateResult(PinName, *DataPinType, *FlowPin, SuppliedResult)) { - // Ensure we have a pin type even if the enum was invalid before - MutableDataPin.SetPinTypeName(FFlowPinType_Exec::GetPinTypeNameStatic()); + return SuppliedResult; } - MutableDataPin.PinType = EFlowPinType::Invalid; + return FFlowDataPinResult(EFlowDataPinResolveResult::FailedUnknownPin); } -// -- - bool UFlowNode::TryFindPropertyByPinName( const UObject& PropertyOwnerObject, const FName& PinName, @@ -506,34 +488,6 @@ void UFlowNode::GatherPotentialPropertyOwnersForDataPins(TArray& InOutOwners.AddUnique(this); } -FFlowDataPinResult UFlowNode::TrySupplyDataPin_Implementation(FName PinName) const -{ - const FFlowPin* FlowPin = FindOutputPinByName(PinName); - if (!FlowPin) - { - // Also look in the Input Pins (for supplying default values for unconnected pins) - FlowPin = FindInputPinByName(PinName); - if (!FlowPin) - { - return FFlowDataPinResult(EFlowDataPinResolveResult::FailedUnknownPin); - } - } - - const FFlowPinType* DataPinType = FlowPin->ResolveFlowPinType(); - if (!DataPinType) - { - return FFlowDataPinResult(EFlowDataPinResolveResult::FailedMismatchedType); - } - - FFlowDataPinResult SuppliedResult; - if (TryGatherPropertyOwnersAndPopulateResult(PinName, *DataPinType, *FlowPin, SuppliedResult)) - { - return SuppliedResult; - } - - return FFlowDataPinResult(EFlowDataPinResolveResult::FailedUnknownPin); -} - bool UFlowNode::TryGatherPropertyOwnersAndPopulateResult( const FName& PinName, const FFlowPinType& DataPinType, @@ -559,10 +513,9 @@ bool UFlowNode::TryGatherPropertyOwnersAndPopulateResult( return false; } +// -- -bool UFlowNode::TryGetFlowDataPinSupplierDatasForPinName( - const FName& PinName, - TFlowPinValueSupplierDataArray& InOutPinValueSupplierDatas) const +bool UFlowNode::TryGetFlowDataPinSupplierDatasForPinName(const FName& PinName, TFlowPinValueSupplierDataArray& InOutPinValueSupplierDatas) const { const IFlowDataPinValueSupplierInterface* ThisAsPinValueSupplier = Cast(this); @@ -621,6 +574,66 @@ bool UFlowNode::TryGetFlowDataPinSupplierDatasForPinName( return !InOutPinValueSupplierDatas.IsEmpty(); } +void UFlowNode::AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const +{ + // Gather all of the potential providers for this DataPin + TArray PropertyOwnerObjects; + GatherPotentialPropertyOwnersForDataPins(PropertyOwnerObjects); + + // GenerateDataPins for all of the potential providers + for (const UObject* PropertyOwnerObject : PropertyOwnerObjects) + { + checkf(IsValid(PropertyOwnerObject), TEXT("Every UObject provided by GatherPotentialPropertyOwnersForDataPins must be valid")); + + InOutWorkingData.AddFlowDataPinsForClassProperties(*PropertyOwnerObject); + } +} + +// #FlowDataPinLegacy +void UFlowNode::FixupDataPinTypes() +{ + FixupDataPinTypesForArray(InputPins); + FixupDataPinTypesForArray(OutputPins); +#if WITH_EDITOR + FixupDataPinTypesForArray(AutoInputDataPins); + FixupDataPinTypesForArray(AutoOutputDataPins); +#endif +} + +void UFlowNode::FixupDataPinTypesForArray(TArray& MutableDataPinArray) +{ + for (FFlowPin& MutableFlowPin : MutableDataPinArray) + { + FixupDataPinTypesForPin(MutableFlowPin); + } +} + +void UFlowNode::FixupDataPinTypesForPin(FFlowPin& MutableDataPin) +{ + const FFlowPinTypeName NewPinTypeName = FFlowPin::GetPinTypeNameForLegacyPinType(MutableDataPin.PinType); + + if (!NewPinTypeName.IsNone()) + { + MutableDataPin.SetPinTypeName(NewPinTypeName); + } + + if (MutableDataPin.GetPinTypeName().IsNone()) + { + // Ensure we have a pin type even if the enum was invalid before + MutableDataPin.SetPinTypeName(FFlowPinType_Exec::GetPinTypeNameStatic()); + } + + MutableDataPin.PinType = EFlowPinType::Invalid; +} +// -- + +void UFlowNode::SetConnections(const TMap& InConnections) +{ + const TMap OldConnections = Connections; + Connections = InConnections; + OnConnectionsChanged(OldConnections); +} + TSet UFlowNode::GatherConnectedNodes() const { TSet Result; @@ -1089,21 +1102,6 @@ TArray UFlowNode::GetPinRecords(const FName& PinName, const EEdGraph } } -void UFlowNode::AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const -{ - // Gather all of the potential providers for this DataPin - TArray PropertyOwnerObjects; - GatherPotentialPropertyOwnersForDataPins(PropertyOwnerObjects); - - // GenerateDataPins for all of the potential providers - for (const UObject* PropertyOwnerObject : PropertyOwnerObjects) - { - checkf(IsValid(PropertyOwnerObject), TEXT("Every UObject provided by GatherPotentialPropertyOwnersForDataPins must be valid")); - - InOutWorkingData.AddFlowDataPinsForClassProperties(*PropertyOwnerObject); - } -} - #endif FString UFlowNode::GetIdentityTagDescription(const FGameplayTag& Tag) diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp index 5f55d2f49..ec0b0e2eb 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBase.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -264,11 +264,6 @@ TArray UFlowNodeBase::GetContextOutputs() const return ContextOutputs; } -FString UFlowNodeBase::GetStatusString() const -{ - return K2_GetStatusString(); -} - #endif // WITH_EDITOR void UFlowNodeBase::LogValidationError(const FString& Message) @@ -627,7 +622,14 @@ void UFlowNodeBase::PostEditChangeProperty(FPropertyChangedEvent& PropertyChange UpdateNodeConfigText(); } +#endif // WITH_EDITOR +FString UFlowNodeBase::GetStatusString() const +{ + return K2_GetStatusString(); +} + +#if WITH_EDITOR FString UFlowNodeBase::GetNodeCategory() const { if (GetClass()->ClassGeneratedBy) @@ -762,7 +764,6 @@ bool UFlowNodeBase::IsFlowNamedPropertiesSupplier() const { return Implements(); } - #endif FText UFlowNodeBase::K2_GetNodeTitle_Implementation() const diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 8a60acfb2..67be1b730 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -20,14 +20,12 @@ * A Flow Node is UObject-based node designed to handle entire gameplay feature within single node. */ UCLASS(Abstract, Blueprintable, HideCategories = Object) -class FLOW_API UFlowNode - : public UFlowNodeBase - , public IFlowDataPinGeneratorInterface - , public IFlowDataPinValueSupplierInterface - , public IVisualLoggerDebugSnapshotInterface +class FLOW_API UFlowNode : public UFlowNodeBase + , public IFlowDataPinGeneratorInterface + , public IFlowDataPinValueSupplierInterface + , public IVisualLoggerDebugSnapshotInterface { GENERATED_UCLASS_BODY() - friend class SFlowGraphNode; friend class UFlowAsset; friend class UFlowGraphNode; @@ -80,13 +78,13 @@ class FLOW_API UFlowNode // by default based on the node Guid, // but may be overridden in subclasses to supply some other value. virtual int32 GetRandomSeed() const override { return GetTypeHash(NodeGuid); } - + virtual const UFlowNode* GetParentNode() const override { return UFlowNodeBase::GetFlowNodeSelfOrOwner(); } -public: +public: virtual bool CanFinishGraph() const { return false; } protected: @@ -171,6 +169,14 @@ class FLOW_API UFlowNode TMap Connections; public: +#if WITH_EDITOR + void SetConnections(const TMap& InConnections); + + virtual void OnConnectionsChanged(const TMap& OldConnections) + { + } +#endif // WITH_EDITOR + FConnectedPin GetConnection(const FName OutputName) const { return Connections.FindRef(OutputName); } UE_DEPRECATED(5.5, "Please use GatherConnectedNodes instead.") @@ -178,7 +184,7 @@ class FLOW_API UFlowNode UFUNCTION(BlueprintPure, Category= "FlowNode") TSet GatherConnectedNodes() const; - + FName GetPinConnectedToNode(const FGuid& OtherNodeGuid); UFUNCTION(BlueprintPure, Category= "FlowNode") @@ -198,7 +204,6 @@ class FLOW_API UFlowNode static void RecursiveFindNodesByClass(UFlowNode* Node, const TSubclassOf Class, uint8 Depth, TArray& OutNodes); protected: - // Slow and fast lookup functions, based on whether we are proactively caching the connections for quick lookup // in the Connections array (by PinCategory) bool FindConnectedNodeForPinFast(const FName& FlowPinName, FGuid* FoundGuid = nullptr, FName* OutConnectedPinName = nullptr) const; @@ -209,13 +214,12 @@ class FLOW_API UFlowNode // As such, this function may not return anything even if the Node is connected to the Pin. // Use UFlowAsset::GetAllPinsConnectedToPin() to do a guaranteed find of all Connections. TArray GetKnownConnectionsToPin(const FConnectedPin& Pin) const; - + ////////////////////////////////////////////////////////////////////////// // Data Pins public: - -#if WITH_EDITORONLY_DATA +#if WITH_EDITORONLY_DATA UPROPERTY(VisibleDefaultsOnly, AdvancedDisplay, Category = "FlowNode", meta = (GetByRef)) TArray AutoInputDataPins; @@ -228,12 +232,9 @@ class FLOW_API UFlowNode void SetAutoOutputDataPins(const TArray& AutoOutputPins); const TArray& GetAutoInputDataPins() const { return AutoInputDataPins; } const TArray& GetAutoOutputDataPins() const { return AutoOutputDataPins; } - + TArray& GetMutableAutoInputDataPins() { return AutoInputDataPins; } TArray& GetMutableAutoOutputDataPins() { return AutoOutputDataPins; } - - void SetConnections(const TMap& InConnections); - virtual void OnConnectionsChanged(const TMap& OldConnections) { } #endif // WITH_EDITOR // IFlowDataPinValueSupplierInterface @@ -249,6 +250,15 @@ class FLOW_API UFlowNode const FProperty*& OutFoundProperty, TInstancedStruct& OutFoundInstancedStruct) const; +protected: + // Static implementation of the default TryFindPropertyByPinName (which subclasses can incorporate into overrides) + static bool TryFindPropertyByPinName_Static( + const UObject& PropertyOwnerObject, + const FName& PinName, + const FProperty*& OutFoundProperty, + TInstancedStruct& OutFoundInstancedStruct); + +public: // Advanced helper for TrySupplyDataPin, which can be overridden in subclasses to provide additional or replacement object(s) // for sourcing the properties for the given pin name. These objects will have PopulateResult called on them. // (this function is used for cases like ExecuteComponent) @@ -260,13 +270,16 @@ class FLOW_API UFlowNode const FFlowPin& FlowPin, FFlowDataPinResult& OutSuppliedResult) const; -protected: - // Static implementation of the default TryFindPropertyByPinName (which subclasses can incorperate into overrides) - static bool TryFindPropertyByPinName_Static( - const UObject& PropertyOwnerObject, - const FName& PinName, - const FProperty*& OutFoundProperty, - TInstancedStruct& OutFoundInstancedStruct); + using TFlowPinValueSupplierDataArray = FlowArray::TInlineArray; + bool TryGetFlowDataPinSupplierDatasForPinName(const FName& PinName, TFlowPinValueSupplierDataArray& InOutPinValueSupplierDatas) const; + + // IFlowDataPinGeneratorInterface +#if WITH_EDITOR + +public: + virtual void AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const override; +#endif + // -- // #FlowDataPinLegacy public: @@ -277,19 +290,6 @@ class FLOW_API UFlowNode static void FixupDataPinTypesForPin(FFlowPin& MutableDataPin); // -- -public: - - using TFlowPinValueSupplierDataArray = FlowArray::TInlineArray; - bool TryGetFlowDataPinSupplierDatasForPinName( - const FName& PinName, - TFlowPinValueSupplierDataArray& InOutPinValueSupplierDatas) const; - - // IFlowDataPinGeneratorInterface -#if WITH_EDITOR - virtual void AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const override; -#endif - // -- - ////////////////////////////////////////////////////////////////////////// // Debugger @@ -300,11 +300,12 @@ class FLOW_API UFlowNode static FString NoActorsFound; #if WITH_EDITOR + protected: virtual EDataValidationResult ValidateNode() override; void ValidateFlowPinArrayIsUnique(const TArray& FlowPins, TSet& InOutUniquePinNames, EDataValidationResult& InOutResult); #endif - + ////////////////////////////////////////////////////////////////////////// // Executing node instance @@ -331,7 +332,6 @@ class FLOW_API UFlowNode void TriggerFlush(); protected: - // Trigger execution of input pin void TriggerInput(const FName& PinName, const EFlowPinActivationType ActivationType = EFlowPinActivationType::Default); @@ -365,30 +365,38 @@ class FLOW_API UFlowNode UFUNCTION(BlueprintNativeEvent, Category = "FlowNode") void OnPassThrough(); - + ////////////////////////////////////////////////////////////////////////// // Utils -#if WITH_EDITOR public: +#if WITH_EDITOR UFlowNode* GetInspectedInstance() const; TMap GetWireRecords() const; TArray GetPinRecords(const FName& PinName, const EEdGraphPinDirection PinDirection) const; +#endif // Information displayed while node is working - displayed over node as NodeInfoPopup FString GetStatusStringForNodeAndAddOns() const; + +#if WITH_EDITOR virtual bool GetStatusBackgroundColor(FLinearColor& OutColor) const; +#endif +protected: + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Get Status Background Color")) + bool K2_GetStatusBackgroundColor(FLinearColor& OutColor) const; + +#if WITH_EDITOR + +public: virtual FString GetAssetPath(); virtual UObject* GetAssetToEdit(); virtual AActor* GetActorToFocus(); #endif protected: - UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Get Status Background Color")) - bool K2_GetStatusBackgroundColor(FLinearColor& OutColor) const; - UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Get Asset Path")) FString K2_GetAssetPath(); diff --git a/Source/Flow/Public/Nodes/FlowNodeBase.h b/Source/Flow/Public/Nodes/FlowNodeBase.h index be3e17604..152bfe769 100644 --- a/Source/Flow/Public/Nodes/FlowNodeBase.h +++ b/Source/Flow/Public/Nodes/FlowNodeBase.h @@ -371,10 +371,11 @@ class FLOW_API UFlowNodeBase // used when import graph from another asset virtual void PostImport() {} +#endif +public: // Called by owning FlowNode to add to its Status String. virtual FString GetStatusString() const; -#endif protected: // Information displayed while node is working - displayed over node as NodeInfoPopup From 827dc63cfd73bd62b02bb096cff85a6baab0fcef Mon Sep 17 00:00:00 2001 From: LindyHopperGT <91915878+LindyHopperGT@users.noreply.github.com> Date: Tue, 27 Jan 2026 12:23:04 -0800 Subject: [PATCH 386/485] Search improvements vs. flow mainline (#326) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Krzysiek Justyński --- .../Nodes/Graph/FlowNode_FormatText.cpp | 6 +- .../Private/Asset/FlowObjectDiff.cpp | 2 +- Source/FlowEditor/Private/Asset/SFlowDiff.cpp | 51 +- Source/FlowEditor/Private/Find/FindInFlow.cpp | 1007 +++++++++++++---- .../Private/Find/SFindInFlowFilterPopup.cpp | 145 +++ .../FlowEditor/Private/FlowEditorModule.cpp | 41 + .../Private/Graph/Widgets/SFlowGraphNode.cpp | 4 +- Source/FlowEditor/Public/Find/FindInFlow.h | 150 ++- .../FlowEditor/Public/Find/FindInFlowEnums.h | 48 + .../Public/Find/SFindInFlowFilterPopup.h | 42 + Source/FlowEditor/Public/FlowEditorModule.h | 8 +- .../Public/Graph/FlowGraphEditorSettings.h | 10 + .../FlowEditor/Public/Graph/FlowGraphSchema.h | 3 + 13 files changed, 1209 insertions(+), 308 deletions(-) create mode 100644 Source/FlowEditor/Private/Find/SFindInFlowFilterPopup.cpp create mode 100644 Source/FlowEditor/Public/Find/FindInFlowEnums.h create mode 100644 Source/FlowEditor/Public/Find/SFindInFlowFilterPopup.h diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_FormatText.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_FormatText.cpp index e6564105d..13c055433 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_FormatText.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_FormatText.cpp @@ -7,6 +7,8 @@ #define LOCTEXT_NAMESPACE "FlowNode_FormatText" +const FName UFlowNode_FormatText::OUTPIN_TextOutput("Formatted Text"); + UFlowNode_FormatText::UFlowNode_FormatText(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { @@ -15,12 +17,12 @@ UFlowNode_FormatText::UFlowNode_FormatText(const FObjectInitializer& ObjectIniti NodeDisplayStyle = FlowNodeStyle::Terminal; #endif - OutputPins.Add(FFlowPin(TEXT("Formatted Text"), FFlowPinType_Text::GetPinTypeNameStatic())); + OutputPins.Add(FFlowPin(OUTPIN_TextOutput, FFlowPinType_Text::GetPinTypeNameStatic())); } FFlowDataPinResult UFlowNode_FormatText::TrySupplyDataPin_Implementation(FName PinName) const { - if (PinName == TEXT("Formatted Text")) + if (PinName == OUTPIN_TextOutput) { FText FormattedText; const EFlowDataPinResolveResult FormatResult = TryResolveFormatText(PinName, FormattedText); diff --git a/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp b/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp index 270eab9ac..dd94960d2 100644 --- a/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp +++ b/Source/FlowEditor/Private/Asset/FlowObjectDiff.cpp @@ -72,7 +72,7 @@ void FFlowObjectDiff::DiffProperties(TArray& OutProperty if (OldDetailsView.IsValid() && NewDetailsView.IsValid()) { static constexpr bool bSortByDisplayOrder = true; - //OldDetailsView->DiffAgainst(*NewDetailsView.Get(), OutPropertyDiffsArray, bSortByDisplayOrder); + OldDetailsView->DiffAgainst(*NewDetailsView.Get(), OutPropertyDiffsArray, bSortByDisplayOrder); } } diff --git a/Source/FlowEditor/Private/Asset/SFlowDiff.cpp b/Source/FlowEditor/Private/Asset/SFlowDiff.cpp index 96540b6dc..2b1be2976 100644 --- a/Source/FlowEditor/Private/Asset/SFlowDiff.cpp +++ b/Source/FlowEditor/Private/Asset/SFlowDiff.cpp @@ -1,9 +1,10 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "Asset/SFlowDiff.h" -#include "Asset/FlowDiffControl.h" +#include "Asset/FlowDiffControl.h" #include "FlowAsset.h" +#include "Graph/Nodes/FlowGraphNode.h" #include "EdGraphUtilities.h" #include "Editor.h" @@ -38,11 +39,11 @@ static int32 GetCurrentIndex(SListView> const& Lis const TArray>& Selected = ListView.GetSelectedItems(); if (Selected.Num() == 1) { - for (const TSharedPtr& Diff : ListViewSource) + for (int32 Index = 0; Index < ListViewSource.Num(); ++Index) { - if (Diff == Selected[0]) + if (ListViewSource[Index] == Selected[0]) { - return 0; + return Index; } } } @@ -52,23 +53,21 @@ static int32 GetCurrentIndex(SListView> const& Lis void FlowDiffUtils::SelectNextRow(SListView>& ListView, const TArray>& ListViewSource) { const int32 CurrentIndex = GetCurrentIndex(ListView, ListViewSource); - if (CurrentIndex == ListViewSource.Num() - 1) + const int32 NextIndex = CurrentIndex + 1; + if (ListViewSource.IsValidIndex(NextIndex)) { - return; + ListView.SetSelection(ListViewSource[NextIndex]); } - - ListView.SetSelection(ListViewSource[CurrentIndex + 1]); } void FlowDiffUtils::SelectPrevRow(SListView>& ListView, const TArray>& ListViewSource) { const int32 CurrentIndex = GetCurrentIndex(ListView, ListViewSource); - if (CurrentIndex == 0) + const int32 PrevIndex = CurrentIndex - 1; + if (ListViewSource.IsValidIndex(PrevIndex)) { - return; + ListView.SetSelection(ListViewSource[PrevIndex]); } - - ListView.SetSelection(ListViewSource[CurrentIndex - 1]); } bool FlowDiffUtils::HasNextDifference(const SListView>& ListView, const TArray>& ListViewSource) @@ -538,16 +537,28 @@ void FFlowDiffPanel::GeneratePanel(UEdGraph* Graph, TSharedPtr(); - GraphEditorCommands->MapAction(FGenericCommands::Get().Copy, - FExecuteAction::CreateRaw(this, &FFlowDiffPanel::CopySelectedNodes), - FCanExecuteAction::CreateRaw(this, &FFlowDiffPanel::CanCopyNodes) + GraphEditorCommands->MapAction( + FGenericCommands::Get().Copy, + FExecuteAction::CreateRaw(this, &FFlowDiffPanel::CopySelectedNodes), + FCanExecuteAction::CreateRaw(this, &FFlowDiffPanel::CanCopyNodes) ); } @@ -683,14 +694,14 @@ void SFlowDiff::HandleGraphChanged(const FString& GraphPath) const TAttribute FocusedDiffResult = TAttribute::CreateLambda( [this, RealDifferencesStartIndex]() { - int32 FocusedDiffResult = INDEX_NONE; + int32 FocusedIndex = INDEX_NONE; if (RealDifferencesStartIndex != INDEX_NONE) { - FocusedDiffResult = DiffTreeView::CurrentDifference(DifferencesTreeView.ToSharedRef(), RealDifferences) - RealDifferencesStartIndex; + FocusedIndex = DiffTreeView::CurrentDifference(DifferencesTreeView.ToSharedRef(), RealDifferences) - RealDifferencesStartIndex; } // find selected index in all the graphs, and subtract the index of the first entry in this graph - return FocusedDiffResult; + return FocusedIndex; }); // only regenerate PanelOld if the old graph has changed @@ -934,4 +945,4 @@ void SFlowDiff::OnModeChanged(const FName& InNewViewMode) const UpdateTopSectionVisibility(InNewViewMode); } -#undef LOCTEXT_NAMESPACE +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/FlowEditor/Private/Find/FindInFlow.cpp b/Source/FlowEditor/Private/Find/FindInFlow.cpp index d098da8d5..648728c78 100644 --- a/Source/FlowEditor/Private/Find/FindInFlow.cpp +++ b/Source/FlowEditor/Private/Find/FindInFlow.cpp @@ -2,14 +2,20 @@ #include "Find/FindInFlow.h" #include "Asset/FlowAssetEditor.h" +#include "Find/SFindInFlowFilterPopup.h" #include "Graph/FlowGraphEditor.h" +#include "Graph/FlowGraphEditorSettings.h" #include "Graph/FlowGraphUtils.h" #include "Graph/Nodes/FlowGraphNode.h" - #include "FlowAsset.h" +#include "FlowEditorModule.h" #include "Nodes/FlowNode.h" +#include "Nodes/FlowNodeBase.h" +#include "AddOns/FlowNodeAddOn.h" #include "Nodes/Graph/FlowNode_SubGraph.h" +#include "AssetRegistry/AssetRegistryModule.h" +#include "AssetRegistry/ARFilter.h" #include "EdGraph/EdGraph.h" #include "EdGraph/EdGraphNode.h" #include "Framework/Application/SlateApplication.h" @@ -22,16 +28,22 @@ #include "Layout/WidgetPath.h" #include "Math/Color.h" #include "Misc/Attribute.h" +#include "Misc/EnumRange.h" +#include "Misc/ScopedSlowTask.h" #include "SlotBase.h" +#include "Subsystems/AssetEditorSubsystem.h" #include "Styling/AppStyle.h" #include "Styling/SlateColor.h" #include "Templates/Casts.h" #include "Types/SlateStructs.h" #include "UObject/Class.h" #include "UObject/ObjectPtr.h" +#include "UObject/TopLevelAssetPath.h" #include "Widgets/Images/SImage.h" -#include "Widgets/Input/SCheckBox.h" #include "Widgets/Input/SSearchBox.h" +#include "Widgets/Input/SComboBox.h" +#include "Widgets/Input/SSpinBox.h" +#include "Widgets/Input/SButton.h" #include "Widgets/Layout/SBorder.h" #include "Widgets/Layout/SBox.h" #include "Widgets/SBoxPanel.h" @@ -39,22 +51,58 @@ #include "Widgets/Text/STextBlock.h" #include "Widgets/Views/STableRow.h" -class ITableRow; -class SWidget; -struct FSlateBrush; - #define LOCTEXT_NAMESPACE "FindInFlow" +////////////////////////////////////////////////////////////////////////// +// FFindInFlowCache + +TMap, TMap>> FFindInFlowCache::CategoryStringCache; + +void FFindInFlowCache::OnFlowAssetChanged(UFlowAsset& ChangedFlowAsset) +{ + TArray> EntriesToRemove; + + for (const auto& KV : CategoryStringCache) + { + const TWeakObjectPtr& EdNodePtr = KV.Key; + + UEdGraphNode* EdNode = EdNodePtr.Get(); + + if (!IsValid(EdNode)) + { + EntriesToRemove.Add(EdNodePtr); + + continue; + } + + UEdGraph* EdGraph = ChangedFlowAsset.GetGraph(); + if (EdGraph->Nodes.Contains(EdNode)) + { + EntriesToRemove.Add(EdNodePtr); + } + } + + for (const TWeakObjectPtr& EdNodePtr : EntriesToRemove) + { + CategoryStringCache.Remove(EdNodePtr); + } +} + ////////////////////////////////////////////////////////////////////////// // FFindInFlowResult -FFindInFlowResult::FFindInFlowResult(const FString& InValue) - : Value(InValue), GraphNode(nullptr) +FFindInFlowResult::FFindInFlowResult(const FString& InValue, UFlowAsset* InOwningFlowAsset) + : Value(InValue) + , OwningFlowAsset(InOwningFlowAsset) { } -FFindInFlowResult::FFindInFlowResult(const FString& InValue, TSharedPtr& InParent, UEdGraphNode* InNode, bool bInIsSubGraphNode) - : Value(InValue), GraphNode(InNode), Parent(InParent), bIsSubGraphNode(bInIsSubGraphNode) +FFindInFlowResult::FFindInFlowResult(const FString& InValue, TSharedPtr InParent, UEdGraphNode* InNode, bool bInIsSubGraphNode, UFlowAsset* InOwningFlowAsset) + : Value(InValue) + , GraphNode(InNode) + , OwningFlowAsset(InOwningFlowAsset) + , Parent(InParent) + , bIsSubGraphNode(bInIsSubGraphNode) { } @@ -68,50 +116,49 @@ TSharedRef FFindInFlowResult::CreateIcon() const .ColorAndOpacity(IconColor); } -FReply FFindInFlowResult::OnClick(TWeakPtr FlowAssetEditorPtr, TSharedPtr Root) +FReply FFindInFlowResult::OnClick(TWeakPtr FlowAssetEditorPtr) { - if (FlowAssetEditorPtr.IsValid() && GraphNode.IsValid()) + if (GraphNode.IsValid()) { - if (Parent.IsValid() && !bIsSubGraphNode) - { - FlowAssetEditorPtr.Pin()->JumpToNode(GraphNode.Get()); - } - else + if (UEdGraph* Graph = GraphNode->GetGraph()) { - FlowAssetEditorPtr.Pin()->JumpToNode(Parent.Pin()->GraphNode.Get()); + if (UFlowAsset* Asset = Cast(Graph->GetOuter())) + { + GEditor->GetEditorSubsystem()->OpenEditorForAsset(Asset); + if (TSharedPtr Editor = FFlowGraphUtils::GetFlowAssetEditor(Graph)) + { + Editor->JumpToNode(GraphNode.Get()); + } + } } } - + else if (OwningFlowAsset.IsValid()) + { + GEditor->GetEditorSubsystem()->OpenEditorForAsset(OwningFlowAsset.Get()); + } return FReply::Handled(); } -FReply FFindInFlowResult::OnDoubleClick(TSharedPtr Root) const +FReply FFindInFlowResult::OnDoubleClick() const { - if (!Parent.IsValid() || !bIsSubGraphNode) + if (bIsSubGraphNode && Parent.IsValid()) { - return FReply::Handled(); - } - const UFlowGraphNode* ParentGraphNode = Cast(Parent.Pin()->GraphNode); - if (!ParentGraphNode || !ParentGraphNode->GetFlowNodeBase()) - { - return FReply::Handled(); - } - - if (UFlowNode* FlowNode = Cast(ParentGraphNode->GetFlowNodeBase())) - { - if (UObject* AssetToEdit = FlowNode->GetAssetToEdit()) + if (const UFlowGraphNode* ParentNode = Cast(Parent.Pin()->GraphNode.Get())) { - UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem(); - if (AssetEditorSubsystem->OpenEditorForAsset(AssetToEdit)) + if (UFlowNode_SubGraph* SubGraph = Cast(ParentNode->GetFlowNodeBase())) { - if (const TSharedPtr FlowAssetEditor = FFlowGraphUtils::GetFlowAssetEditor(GraphNode->GetGraph())) + if (UObject* Target = SubGraph->GetAssetToEdit()) { - FlowAssetEditor->JumpToNode(GraphNode.Get()); + GEditor->GetEditorSubsystem()->OpenEditorForAsset(Target); + if (TSharedPtr Editor = FFlowGraphUtils::GetFlowAssetEditor(GraphNode->GetGraph())) + { + Editor->JumpToNode(GraphNode.Get()); + } } } } } - + return FReply::Handled(); } @@ -127,304 +174,789 @@ FString FFindInFlowResult::GetDescriptionText() const FString FFindInFlowResult::GetCommentText() const { - if (GraphNode.IsValid()) - { - return GraphNode->NodeComment; - } - - return FString(); + return GraphNode.IsValid() ? GraphNode->NodeComment : FString(); } FString FFindInFlowResult::GetNodeTypeText() const { - if (GraphNode.IsValid()) + if (!GraphNode.IsValid()) { - FString NodeClassName; - const UFlowGraphNode* FlowGraphNode = Cast(GraphNode.Get()); - if (FlowGraphNode && FlowGraphNode->GetFlowNodeBase()) - { - NodeClassName = FlowGraphNode->GetFlowNodeBase()->GetClass()->GetName(); - } - else - { - NodeClassName = GraphNode->GetClass()->GetName(); - } - const int32 Pos = NodeClassName.Find("_"); - if (Pos == INDEX_NONE) - { - return NodeClassName; - } - else + return FString(); + } + + if (const UFlowGraphNode* FlowGraphNode = Cast(GraphNode.Get())) + { + if (UFlowNodeBase* Base = FlowGraphNode->GetFlowNodeBase()) { - return NodeClassName.RightChop(Pos + 1); + return Base->GetClass()->GetDisplayNameText().ToString(); } } - return FString(); + return GraphNode->GetClass()->GetDisplayNameText().ToString(); } FText FFindInFlowResult::GetToolTipText() const { - FString ToolTipStr = TEXT("Click to focus on nodes."); - if (bIsSubGraphNode) + FString Tip = GetNodeTypeText() + TEXT("\n") + GetDescriptionText(); + + if (!GetCommentText().IsEmpty()) { - ToolTipStr += TEXT("\nDouble click to focus on subgraph nodes"); + Tip += TEXT("\n") + GetCommentText(); } - return FText::FromString(ToolTipStr); + + if (!MatchedPropertySnippet.IsEmpty()) + { + Tip += TEXT("\n\nMatched: ") + MatchedPropertySnippet; + } + + return FText::FromString(Tip); +} + +FText FFindInFlowResult::GetMatchedSnippet() const +{ + return FText::FromString(MatchedPropertySnippet); +} + +FText FFindInFlowResult::GetMatchedCategoriesText() const +{ + if (MatchedFlags == EFlowSearchFlags::None) + { + return FText::GetEmpty(); + } + + TArray DisplayNames; + + for (EFlowSearchFlags Flag : MakeFlagsRange(EFlowSearchFlags::All)) + { + if (EnumHasAnyFlags(MatchedFlags, Flag)) + { + FText DisplayName = UEnum::GetDisplayValueAsText(Flag); + if (!DisplayName.IsEmpty()) + { + DisplayNames.Add(DisplayName); + } + } + } + + if (DisplayNames.Num() == 0) + { + return FText::GetEmpty(); + } + + return FText::Join(FText::FromString(TEXT(", ")), DisplayNames); } ////////////////////////////////////////////////////////////////////////// // SFindInFlow -void SFindInFlow::Construct( const FArguments& InArgs, TSharedPtr InFlowAssetEditor) +void SFindInFlow::Construct(const FArguments& InArgs, TSharedPtr InFlowAssetEditor) { FlowAssetEditorPtr = InFlowAssetEditor; + SearchResults.Setup(); + + // Load INI settings + const UFlowGraphEditorSettings* Settings = UFlowGraphEditorSettings::Get(); + if (ensure(Settings)) + { + MaxSearchDepth = Settings->DefaultMaxSearchDepth; + SearchFlags = static_cast(Settings->DefaultSearchFlags); + } - this->ChildSlot + // Populate scope options + FLOW_ASSERT_ENUM_MAX(EFlowSearchScope, 3); + for (EFlowSearchScope Scope : TEnumRange()) + { + if (FlowEnum::IsValidEnumValue(Scope)) + { + ScopeOptionList.Add(MakeShareable(new EFlowSearchScope(Scope))); + } + } + SelectedScopeOption = ScopeOptionList[0]; + + SAssignNew(SearchTextField, SSearchBox) + .OnTextCommitted(this, &SFindInFlow::OnSearchTextCommitted); + + SAssignNew(SearchButton, SButton) + .Text(LOCTEXT("SearchButton", "Search")) + .OnClicked(this, &SFindInFlow::OnSearchButtonClicked); + + SAssignNew(MaxDepthSpinBox, SSpinBox) + .MinValue(0) + .MaxValue(10) + .Value(MaxSearchDepth) + .OnValueChanged(this, &SFindInFlow::OnMaxDepthChanged) + .ToolTipText(LOCTEXT("MaxDepthTooltip", "Maximum recursion depth when searching inside objects")); + + SAssignNew(TreeView, STreeViewType) + .TreeItemsSource(&SearchResults.ItemsFound) + .OnGenerateRow(this, &SFindInFlow::OnGenerateRow) + .OnGetChildren(this, &SFindInFlow::OnGetChildren) + .OnSelectionChanged(this, &SFindInFlow::OnTreeSelectionChanged) + .OnMouseButtonDoubleClick(this, &SFindInFlow::OnTreeSelectionDoubleClicked); + + ChildSlot [ SNew(SVerticalBox) - +SVerticalBox::Slot() - .AutoHeight() - [ - SNew(SHorizontalBox) - +SHorizontalBox::Slot() - .FillWidth(1) - [ - SAssignNew(SearchTextField, SSearchBox) - .HintText(LOCTEXT("FlowEditorSearchHint", "Enter text to find nodes...")) - .OnTextChanged(this, &SFindInFlow::OnSearchTextChanged) - .OnTextCommitted(this, &SFindInFlow::OnSearchTextCommitted) - ] - +SHorizontalBox::Slot() - .Padding(10,0,5,0) - .AutoWidth() - .VAlign(VAlign_Center) - [ - SNew(STextBlock) - .Text(LOCTEXT("FlowEditorSubGraphSearchText", "Find In SubGraph ")) - ] - +SHorizontalBox::Slot() - .AutoWidth() + + SVerticalBox::Slot() + .AutoHeight() [ - SNew(SCheckBox) - .OnCheckStateChanged(this, &SFindInFlow::OnFindInSubGraphStateChanged) - .ToolTipText(LOCTEXT("FlowEditorSubGraphSearchHint", "Checkin means search also in sub graph.")) + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .FillWidth(1.0f) + .VAlign(VAlign_Center) + [ + SearchTextField.ToSharedRef() + ] + + SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Center) + .Padding(4, 0) + [ + SearchButton.ToSharedRef() + ] + + SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Center) + .Padding(4, 0) + [ + SNew(STextBlock) + .Text(LOCTEXT("FiltersLabel", "Filters:")) + ] + + SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Center) + .Padding(4, 0) + [ + SNew(SButton) + .ButtonStyle(FAppStyle::Get(), "HoverHintOnly") + .ToolTipText(LOCTEXT("EditFiltersTooltip", "Edit search filters")) + .OnClicked_Lambda([this]() + { + FFindInFlowApplyDelegate OnSaveAsDefault = FFindInFlowApplyDelegate::CreateLambda([this](EFlowSearchFlags Flags) + { + if (UFlowGraphEditorSettings* Settings = UFlowGraphEditorSettings::Get()) + { + Settings->DefaultSearchFlags = static_cast(Flags); + Settings->SaveConfig(); + } + }); + + TSharedRef FilterPopup = SNew(SFindInFlowFilterPopup) + .OnApply(FFindInFlowApplyDelegate::CreateLambda([this](EFlowSearchFlags NewSearchFlags) + { + SearchFlags = NewSearchFlags; + + InitiateSearch(); + })) + .OnSaveAsDefault(OnSaveAsDefault) + .InitialFlags(SearchFlags); + + FSlateApplication::Get().PushMenu( + AsShared(), + FWidgetPath(), + FilterPopup, + FSlateApplication::Get().GetCursorPos(), + FPopupTransitionEffect::ContextMenu); + + return FReply::Handled(); + }) + [ + SNew(STextBlock) + .Text_Lambda([this]() + { + int32 ActiveCount = FMath::CountBits(static_cast(SearchFlags)); + return FText::Format(LOCTEXT("ActiveFilters", "{0} Active"), FText::AsNumber(ActiveCount)); + }) + ] + ] + + SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Center) + .Padding(4, 0) + [ + SNew(SComboBox>) + .OptionsSource(&ScopeOptionList) + .OnGenerateWidget(this, &SFindInFlow::GenerateScopeWidget) + .OnSelectionChanged(this, &SFindInFlow::OnScopeChanged) + [ + SNew(STextBlock).Text(this, &SFindInFlow::GetCurrentScopeText) + ] + ] + + SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Center) + .Padding(4, 0) + [ + SNew(STextBlock) + .Text(LOCTEXT("MaxDepthLabel", "Max Depth:")) + ] + + SHorizontalBox::Slot() + .AutoWidth() + [ + MaxDepthSpinBox.ToSharedRef() + ] ] - ] - +SVerticalBox::Slot() - .FillHeight(1.0f) - .Padding(0.f, 4.f, 0.f, 0.f) - [ - SNew(SBorder) - .BorderImage(FAppStyle::GetBrush("Menu.Background")) + + SVerticalBox::Slot() + .FillHeight(1.0f) [ - SAssignNew(TreeView, STreeViewType) - .TreeItemsSource(&ItemsFound) - .OnGenerateRow(this, &SFindInFlow::OnGenerateRow) - .OnGetChildren(this, &SFindInFlow::OnGetChildren) - .OnSelectionChanged(this, &SFindInFlow::OnTreeSelectionChanged) - .OnMouseButtonDoubleClick(this, &SFindInFlow::OnTreeSelectionDoubleClicked) - .SelectionMode(ESelectionMode::Multi) + SNew(SBorder) + .BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder")) + [ + TreeView.ToSharedRef() + ] ] - ] ]; } void SFindInFlow::FocusForUse() const { - // NOTE: Careful, GeneratePathToWidget can be reentrant in that it can call visibility delegates and such - FWidgetPath FilterTextBoxWidgetPath; - FSlateApplication::Get().GeneratePathToWidgetUnchecked(SearchTextField.ToSharedRef(), FilterTextBoxWidgetPath); - - // Set keyboard focus directly - FSlateApplication::Get().SetKeyboardFocus(FilterTextBoxWidgetPath, EFocusCause::SetDirectly); + if (SearchTextField.IsValid()) + { + FSlateApplication::Get().SetKeyboardFocus(SearchTextField.ToSharedRef()); + SearchTextField->SelectAllText(); + } } void SFindInFlow::OnSearchTextChanged(const FText& Text) { SearchValue = Text.ToString(); - +} + +void SFindInFlow::OnSearchTextCommitted(const FText& Text, ETextCommit::Type) +{ + SearchValue = Text.ToString(); + + InitiateSearch(); +} + +FReply SFindInFlow::OnSearchButtonClicked() +{ InitiateSearch(); + + return FReply::Handled(); +} + +void SFindInFlow::OnScopeChanged(TSharedPtr NewSelection, ESelectInfo::Type) +{ + SelectedScopeOption = NewSelection; + SearchScope = *NewSelection; } -void SFindInFlow::OnSearchTextCommitted(const FText& Text, ETextCommit::Type CommitType) +void SFindInFlow::OnMaxDepthChanged(int32 NewDepth) { - OnSearchTextChanged(Text); + MaxSearchDepth = NewDepth; + + // Save to INI + if (UFlowGraphEditorSettings* Settings = UFlowGraphEditorSettings::Get()) + { + Settings->DefaultMaxSearchDepth = NewDepth; + Settings->SaveConfig(); + } +} + +TSharedRef SFindInFlow::GenerateScopeWidget(TSharedPtr Item) const +{ + return SNew(STextBlock) + .Text(UEnum::GetDisplayValueAsText(*Item.Get())); +} + +FText SFindInFlow::GetCurrentScopeText() const +{ + return UEnum::GetDisplayValueAsText(*SelectedScopeOption.Get()); } void SFindInFlow::InitiateSearch() { + FFlowEditorModule* FlowEditorModule = &FModuleManager::LoadModuleChecked("FlowEditor"); + if (ensure(FlowEditorModule)) + { + FlowEditorModule->RegisterForAssetChanges(); + } + + SearchResults.Reset(); + + HighlightText = FText::FromString(SearchValue); + TreeView->RequestTreeRefresh(); + + if (SearchValue.IsEmpty()) + { + return; + } + TArray Tokens; SearchValue.ParseIntoArray(Tokens, TEXT(" "), true); + for (FString& Token : Tokens) + { + Token = Token.ToUpper(); + } + + TSharedPtr Editor = FlowAssetEditorPtr.Pin(); + if (!Editor.IsValid()) + { + return; + } - for (auto It(ItemsFound.CreateIterator()); It; ++It) + UFlowAsset* CurrentAsset = Editor->GetFlowAsset(); + if (!CurrentAsset || !CurrentAsset->GetGraph()) { - TreeView->SetItemExpansion(*It, false); + return; } - ItemsFound.Empty(); - if (Tokens.Num() > 0) + + const TSubclassOf CurrentAssetClass = CurrentAsset->GetClass(); + + constexpr int32 Depth = 0; + + switch (SearchScope) { - HighlightText = FText::FromString(SearchValue); - MatchTokens(Tokens); + case EFlowSearchScope::ThisAssetOnly: + { + FSearchResult AssetRoot = MakeShareable(new FFindInFlowResult(CurrentAsset->GetName(), CurrentAsset)); + ProcessAsset(CurrentAsset, AssetRoot, Tokens, Depth); + + if (AssetRoot->Children.Num() > 0) + { + SearchResults.ItemsFound.Add(AssetRoot); + + // Auto-expand the current asset's results + TreeView->SetItemExpansion(AssetRoot, true); + } + } + break; + + case EFlowSearchScope::AllOfThisType: + case EFlowSearchScope::AllFlowAssets: + { + FAssetRegistryModule& Registry = FModuleManager::LoadModuleChecked("AssetRegistry"); + TArray Assets; + FARFilter Filter; + Filter.bRecursiveClasses = true; + + if (SearchScope == EFlowSearchScope::AllFlowAssets) + { + Filter.ClassPaths.Add(FTopLevelAssetPath(UFlowAsset::StaticClass()->GetClassPathName())); + } + else + { + Filter.ClassPaths.Add(FTopLevelAssetPath(CurrentAsset->GetClass()->GetClassPathName())); + } + + Registry.Get().GetAssets(Filter, Assets); + + FScopedSlowTask Task(Assets.Num(), LOCTEXT("SearchingAssets", "Searching Flow Assets...")); + Task.MakeDialog(); + + int32 CurrentAssetIndex = 0; + + for (const FAssetData& Data : Assets) + { + UFlowAsset* Asset = Cast(Data.GetAsset()); + if (!IsValid(Asset)) + { + continue; + } + + CurrentAssetIndex++; + + Task.EnterProgressFrame(1, FText::Format(LOCTEXT("SearchingAsset", "Searching {0}/{1}: {2}..."), CurrentAssetIndex, Assets.Num(), FText::FromString(Asset->GetName()))); + + FSearchResult AssetRoot = MakeShareable(new FFindInFlowResult(Asset->GetName(), Asset)); + ProcessAsset(Asset, AssetRoot, Tokens, Depth); + + if (AssetRoot->Children.Num() > 0) + { + SearchResults.ItemsFound.Add(AssetRoot); + + // Auto-expand only the current asset + if (Asset == CurrentAsset) + { + TreeView->SetItemExpansion(AssetRoot, true); + } + } + } + } + break; + + default: + checkNoEntry(); + break; } - // Insert a fake result to inform user if none found - if (ItemsFound.Num() == 0) + // Add "No results" placeholder if nothing found + if (SearchResults.ItemsFound.IsEmpty()) { - ItemsFound.Add(MakeShared(LOCTEXT("FlowEditorSearchNoResults", "No Results found").ToString())); + FSearchResult NoResults = MakeShareable(new FFindInFlowResult(TEXT("No results found"))); + SearchResults.ItemsFound.Add(NoResults); } TreeView->RequestTreeRefresh(); +} + +bool SFindInFlow::ProcessAsset(UFlowAsset* Asset, FSearchResult ParentResult, const TArray& Tokens, int32 Depth) +{ + if (!Asset || !Asset->GetGraph() || Depth >= MaxSearchDepth || SearchResults.VisitedAssets.Contains(Asset)) + { + return false; + } + + SearchResults.VisitedAssets.Add(Asset); + + bool bAnyMatches = false; - for (auto It(ItemsFound.CreateIterator()); It; ++It) + for (UEdGraphNode* EdNode : Asset->GetGraph()->Nodes) { - TreeView->SetItemExpansion(*It, true); + const TMap>* CategoryStrings = BuildCategoryStrings(EdNode, Depth); + + if (!CategoryStrings) + { + continue; + } + + EFlowSearchFlags NodeMatchedFlags = EFlowSearchFlags::None; + + for (const TPair>& Pair : *CategoryStrings) + { + const TSet& StringSet = Pair.Value; + if (EnumHasAnyFlags(SearchFlags, Pair.Key) && StringSetMatchesSearchTokens(Tokens, StringSet)) + { + EnumAddFlags(NodeMatchedFlags, Pair.Key); + } + } + + if (NodeMatchedFlags != EFlowSearchFlags::None) + { + FString Title = EdNode->GetNodeTitle(ENodeTitleType::ListView).ToString(); + if (Title.IsEmpty()) + { + Title = EdNode->GetClass()->GetName(); + } + + FSearchResult Result = MakeShareable(new FFindInFlowResult(Title, ParentResult, EdNode, Depth > 0, Asset)); + Result->MatchedFlags = NodeMatchedFlags; + ParentResult->Children.Add(Result); + + bAnyMatches = true; + } + + bAnyMatches |= RecurseIntoSubgraphsIfEnabled(EdNode, ParentResult, Tokens, Depth); } + + return bAnyMatches; } -void SFindInFlow::MatchTokens(const TArray& Tokens) +bool SFindInFlow::RecurseIntoSubgraphsIfEnabled(UEdGraphNode* EdNode, FSearchResult ParentResult, const TArray& Tokens, int32 Depth) { - RootSearchResult.Reset(); - - const UEdGraph* Graph = nullptr; - const TSharedPtr FocusedGraphEditor = FlowAssetEditorPtr.Pin()->GetFlowGraph(); - if (FocusedGraphEditor.IsValid()) + if (!EnumHasAnyFlags(SearchFlags, EFlowSearchFlags::Subgraphs)) + { + return false; + } + + UFlowGraphNode* FlowGraphNode = Cast(EdNode); + if (!FlowGraphNode || !FlowGraphNode->GetFlowNodeBase()) + { + return false; + } + + UFlowNode_SubGraph* SubGraph = Cast(FlowGraphNode->GetFlowNodeBase()); + if (!SubGraph) + { + return false; + } + + UFlowAsset* SubAsset = Cast(SubGraph->GetAssetToEdit()); + if (!SubAsset) + { + return false; + } + + const FString SubgraphStr = + SearchResults.VisitedAssets.Contains(SubAsset) ? + TEXT(" (repeat subgraph)") : + TEXT(" (Subgraph)"); + + const FString SubTitle = SubAsset->GetName() + SubgraphStr; + FSearchResult SubResult = MakeShareable(new FFindInFlowResult(SubTitle, ParentResult, EdNode, true, SubAsset)); + + // Subgraphs don't count against depth + if (ProcessAsset(SubAsset, SubResult, Tokens, Depth)) + { + ParentResult->Children.Add(SubResult); + + return true; + } + + return false; +} + +const TMap>* SFindInFlow::BuildCategoryStrings(UEdGraphNode* EdNode, int32 Depth) const +{ + if (!IsValid(EdNode)) + { + return nullptr; + } + + // Check cache first + if (const TMap>* Cached = FFindInFlowCache::CategoryStringCache.Find(EdNode)) + { + return Cached; + } + + TMap> NewResultMap; + + UpdateSearchFlagToStringMapForEdGraphNode(*EdNode, NewResultMap, Depth); + + UFlowGraphNode* FlowGraphNode = Cast(EdNode); + if (IsValid(FlowGraphNode)) + { + UFlowNodeBase* FlowNodeBase = FlowGraphNode->GetFlowNodeBase(); + if (IsValid(FlowNodeBase)) + { + UpdateSearchFlagToStringMapForFlowNodeBase(*FlowNodeBase, NewResultMap, Depth); + } + } + + // Now add the new map to the search cache + const TMap>* AddedResultMap = &FFindInFlowCache::CategoryStringCache.Add(EdNode, NewResultMap); + return AddedResultMap; +} + +void SFindInFlow::UpdateSearchFlagToStringMapForEdGraphNode(const UEdGraphNode& EdGraphNode, TMap>& SearchFlagToStringMap, int32 Depth) const +{ + // Comments + if (EnumHasAnyFlags(SearchFlags, EFlowSearchFlags::Comments)) + { + TSet& CommentsSet = SearchFlagToStringMap.FindOrAdd(EFlowSearchFlags::Comments); + CommentsSet.Add(EdGraphNode.NodeComment); + } +} + +void SFindInFlow::UpdateSearchFlagToStringMapForFlowNodeBase(const UFlowNodeBase& FlowNodeBase, TMap>& SearchFlagToStringMap, int32 Depth) const +{ + // Node Titles + if (EnumHasAnyFlags(SearchFlags, EFlowSearchFlags::Titles)) + { + TSet& TitlesSet = SearchFlagToStringMap.FindOrAdd(EFlowSearchFlags::Titles); + TitlesSet.Add(FlowNodeBase.GetNodeTitle().ToString()); + } + + // Tooltips + if (EnumHasAnyFlags(SearchFlags, EFlowSearchFlags::Tooltips)) + { + TSet& TooltipsSet = SearchFlagToStringMap.FindOrAdd(EFlowSearchFlags::Tooltips); + TooltipsSet.Add(FlowNodeBase.GetNodeToolTip().ToString()); + } + + // Classes + if (EnumHasAnyFlags(SearchFlags, EFlowSearchFlags::Classes)) + { + TSet& ClassesSet = SearchFlagToStringMap.FindOrAdd(EFlowSearchFlags::Classes); + + const FString DisplayName = FlowNodeBase.GetClass()->GetDisplayNameText().ToString(); + ClassesSet.Add(DisplayName); + + const FString NativeName = FlowNodeBase.GetClass()->GetName(); + ClassesSet.Add(NativeName); + } + + // Descriptions + if (EnumHasAnyFlags(SearchFlags, EFlowSearchFlags::Descriptions)) + { + TSet& DescriptionsSet = SearchFlagToStringMap.FindOrAdd(EFlowSearchFlags::Descriptions); + + DescriptionsSet.Add(FlowNodeBase.GetNodeDescription()); + } + + // Config Text + if (EnumHasAnyFlags(SearchFlags, EFlowSearchFlags::ConfigText)) + { + TSet& ConfigSet = SearchFlagToStringMap.FindOrAdd(EFlowSearchFlags::ConfigText); + ConfigSet.Add(FlowNodeBase.GetNodeConfigText().ToString()); + } + + // Property-based scouring + if (EnumHasAnyFlags(SearchFlags, EFlowSearchFlags::PropertiesFlags)) + { + AppendPropertyValues(&FlowNodeBase, FlowNodeBase.GetClass(), &FlowNodeBase, SearchFlagToStringMap, Depth); + } + + // AddOns + if (EnumHasAnyFlags(SearchFlags, EFlowSearchFlags::AddOns)) + { + TSet& AddOnsSet = SearchFlagToStringMap.FindOrAdd(EFlowSearchFlags::AddOns); + + FlowNodeBase.ForEachAddOnConst([AddOnsSet, this, &SearchFlagToStringMap, &Depth](const UFlowNodeAddOn& AddOn) + { + // No depth penalty for AddOns + UpdateSearchFlagToStringMapForFlowNodeBase(AddOn, SearchFlagToStringMap, Depth); + + return EFlowForEachAddOnFunctionReturnValue::Continue; + }); + } +} + +void SFindInFlow::AppendPropertyValues(const void* Container, const UStruct* Struct, const UObject* ParentObject, TMap>& SearchFlagToStringMap, int32 Depth) const +{ + int32 MaxDepth = 1; + if (const UFlowGraphEditorSettings* Settings = UFlowGraphEditorSettings::Get()) { - Graph = FocusedGraphEditor->GetCurrentGraph(); + MaxDepth = Settings->DefaultMaxSearchDepth; } - if (Graph == nullptr) + if (!Container || !Struct || !ParentObject || Depth >= MaxDepth) { return; } - - RootSearchResult = MakeShared(FString("FlowEditorRoot")); - for (auto It(Graph->Nodes.CreateConstIterator()); It; ++It) + for (TFieldIterator It(Struct, EFieldIteratorFlags::IncludeSuper); It; ++It) { - UEdGraphNode* Node = *It; - - const FString NodeName = Node->GetNodeTitle(ENodeTitleType::ListView).ToString(); - FSearchResult NodeResult(new FFindInFlowResult(NodeName, RootSearchResult, Node)); - FString NodeSearchString = NodeName + Node->GetClass()->GetName() + Node->NodeComment; + FProperty* Prop = *It; + if (!Prop->HasAnyPropertyFlags(CPF_Edit | CPF_SimpleDisplay | CPF_AdvancedDisplay | CPF_BlueprintVisible | CPF_Config)) + { + continue; + } - if (const UFlowGraphNode* FlowGraphNode = Cast(Node)) + const void* ValuePtr = Prop->ContainerPtrToValuePtr(Container); + + if (EnumHasAnyFlags(SearchFlags, EFlowSearchFlags::PropertyNames)) { - FString NodeDescription = FlowGraphNode->GetNodeDescription(); - NodeSearchString += NodeDescription; - - UFlowNode_SubGraph* SubGraphNode = Cast(FlowGraphNode->GetFlowNodeBase()); - if (bFindInSubGraph && SubGraphNode) + TSet& PropertyNamesSet = SearchFlagToStringMap.FindOrAdd(EFlowSearchFlags::PropertyNames); + + const FString DisplayName = Prop->GetMetaData(TEXT("DisplayName")); + + if (!DisplayName.IsEmpty()) { - if (const UFlowAsset* FlowAsset = Cast(SubGraphNode->GetAssetToEdit()); FlowAsset && FlowAsset->GetGraph()) - { - for (auto ChildIt(FlowAsset->GetGraph()->Nodes.CreateConstIterator()); ChildIt; ++ChildIt) - { - MatchTokensInChild(Tokens, *ChildIt, NodeResult); - } - } + PropertyNamesSet.Add(DisplayName); } + + PropertyNamesSet.Add(Prop->GetName()); + } + + if (EnumHasAnyFlags(SearchFlags, EFlowSearchFlags::PropertyValues)) + { + TSet& PropertyValuesSet = SearchFlagToStringMap.FindOrAdd(EFlowSearchFlags::PropertyValues); + + FString ValueStr; + UObject* MutableParentObject = const_cast(ParentObject); + Prop->ExportText_InContainer(0, ValueStr, Container, nullptr, MutableParentObject, PPF_None); + ValueStr = ValueStr.Replace(TEXT("\""), TEXT("")).TrimStartAndEnd(); + + PropertyValuesSet.Add(ValueStr); } - NodeSearchString = NodeSearchString.Replace(TEXT(" "), TEXT("")); - const bool bNodeMatchesSearch = StringMatchesSearchTokens(Tokens, NodeSearchString); - - if ((NodeResult->Children.Num() > 0) || bNodeMatchesSearch) + if (EnumHasAnyFlags(SearchFlags, EFlowSearchFlags::Tooltips)) { - ItemsFound.Add(NodeResult); + TSet& TooltipsSet = SearchFlagToStringMap.FindOrAdd(EFlowSearchFlags::Tooltips); + TooltipsSet.Add(Prop->GetMetaData(TEXT("ToolTip"))); + } + + if (FStructProperty* StructProp = CastField(Prop)) + { + // Recurse into structs (no depth penalty) + AppendPropertyValues(ValuePtr, StructProp->Struct, ParentObject, SearchFlagToStringMap, Depth); + } + else if (FObjectProperty* ObjProp = CastField(Prop)) + { + // Recurse into inline objects (incurs a depth penalty) + UObject* Obj = ObjProp->GetObjectPropertyValue(ValuePtr); + if (IsValid(Obj) && !Obj->HasAnyFlags(RF_ClassDefaultObject)) + { + AppendPropertyValues(Obj, Obj->GetClass(), Obj, SearchFlagToStringMap, Depth + 1); + } } } } -void SFindInFlow::MatchTokensInChild(const TArray& Tokens, UEdGraphNode* Child, FSearchResult ParentNode) +bool SFindInFlow::StringMatchesSearchTokens(const TArray& Tokens, const FString& ComparisonString) { - if (Child == nullptr) + int32 MatchedTokenCount = 0; + const int32 TotalTokenCount = Tokens.Num(); + + // Must match all tokens + for (const FString& Token : Tokens) { - return; + if (ComparisonString.Contains(Token)) + { + ++MatchedTokenCount; + } + else + { + break; + } } - const FString ChildName = Child->GetNodeTitle(ENodeTitleType::ListView).ToString(); - FString ChildSearchString = ChildName + Child->GetClass()->GetName() + Child->NodeComment; - if (const UFlowGraphNode* FlowGraphNode = Cast(Child)) + if (MatchedTokenCount == TotalTokenCount) { - FString NodeDescription = FlowGraphNode->GetNodeDescription(); - ChildSearchString += NodeDescription; + return true; } - ChildSearchString = ChildSearchString.Replace(TEXT(" "), TEXT("")); - if (StringMatchesSearchTokens(Tokens, ChildSearchString)) + else { - const FSearchResult DecoratorResult(new FFindInFlowResult(ChildName, ParentNode, Child, true)); - ParentNode->Children.Add(DecoratorResult); + return false; + } +} + +bool SFindInFlow::StringSetMatchesSearchTokens(const TArray& Tokens, const TSet& StringSet) +{ + for (const FString& StringFromSet : StringSet) + { + if (StringMatchesSearchTokens(Tokens, StringFromSet)) + { + return true; + } } + + return false; } -TSharedRef SFindInFlow::OnGenerateRow( FSearchResult InItem, const TSharedRef& OwnerTable ) +TSharedRef SFindInFlow::OnGenerateRow(FSearchResult InItem, const TSharedRef& OwnerTable) { - return SNew(STableRow< TSharedPtr >, OwnerTable) + return SNew(STableRow, OwnerTable) .ToolTip(SNew(SToolTip).Text(InItem->GetToolTipText())) [ SNew(SHorizontalBox) - +SHorizontalBox::Slot() - .VAlign(VAlign_Center) - .AutoWidth() - [ - SNew(SBox) - .MinDesiredWidth(300) + + SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Center) + .Padding(2, 0) [ - SNew(SHorizontalBox) - +SHorizontalBox::Slot() - .AutoWidth() - [ - InItem->CreateIcon() - ] - +SHorizontalBox::Slot() - .VAlign(VAlign_Center) - .AutoWidth() - .Padding(2, 0) - [ - SNew(STextBlock) + InItem->CreateIcon() + ] + + SHorizontalBox::Slot() + .VAlign(VAlign_Center) + .Padding(4, 0) + [ + SNew(STextBlock) .Text(FText::FromString(InItem->Value)) .HighlightText(HighlightText) - ] ] - ] - +SHorizontalBox::Slot() - .VAlign(VAlign_Center) - [ - SNew(STextBlock) - .Text(FText::FromString(InItem->GetDescriptionText())) - .HighlightText(HighlightText) - ] - +SHorizontalBox::Slot() - .AutoWidth() - .VAlign(VAlign_Center) - [ - SNew(STextBlock) - .Text(FText::FromString(InItem->GetNodeTypeText())) - .HighlightText(HighlightText) - ] - +SHorizontalBox::Slot() - .HAlign(HAlign_Right) - .VAlign(VAlign_Center) - [ - SNew(STextBlock) - .Text(FText::FromString(InItem->GetCommentText())) - .ColorAndOpacity(FLinearColor::Yellow) - .HighlightText(HighlightText) - ] + + SHorizontalBox::Slot() + .VAlign(VAlign_Center) + .Padding(4, 0) + [ + SNew(STextBlock) + .Text(FText::FromString(InItem->GetNodeTypeText())) + .ColorAndOpacity(FSlateColor(FLinearColor(0.6f, 0.8f, 1.0f))) + ] + + SHorizontalBox::Slot() + .HAlign(HAlign_Right) + .VAlign(VAlign_Center) + .Padding(4, 0) + [ + SNew(STextBlock) + .Text(InItem->GetMatchedCategoriesText()) + .ColorAndOpacity(FSlateColor(FLinearColor(0.8f, 0.8f, 0.8f))) + ] ]; } -void SFindInFlow::OnGetChildren(FSearchResult InItem, TArray< FSearchResult >& OutChildren) +void SFindInFlow::OnGetChildren(FSearchResult InItem, TArray& OutChildren) { - OutChildren += InItem->Children; + OutChildren = InItem->Children; } -void SFindInFlow::OnTreeSelectionChanged(FSearchResult Item , ESelectInfo::Type) +void SFindInFlow::OnTreeSelectionChanged(FSearchResult Item, ESelectInfo::Type) { if (Item.IsValid()) { - Item->OnClick(FlowAssetEditorPtr, RootSearchResult); + Item->OnClick(FlowAssetEditorPtr); } } @@ -432,33 +964,8 @@ void SFindInFlow::OnTreeSelectionDoubleClicked(FSearchResult Item) { if (Item.IsValid()) { - Item->OnDoubleClick(RootSearchResult); + Item->OnDoubleClick(); } } -void SFindInFlow::OnFindInSubGraphStateChanged(ECheckBoxState CheckBoxState) -{ - bFindInSubGraph = CheckBoxState == ECheckBoxState::Checked; - InitiateSearch(); -} - -bool SFindInFlow::StringMatchesSearchTokens(const TArray& Tokens, const FString& ComparisonString) -{ - bool bFoundAllTokens = true; - - //search the entry for each token, it must have all of them to pass - for (auto TokItr(Tokens.CreateConstIterator()); TokItr; ++TokItr) - { - const FString& Token = *TokItr; - if (!ComparisonString.Contains(Token)) - { - bFoundAllTokens = false; - break; - } - } - return bFoundAllTokens; -} - -///////////////////////////////////////////////////// - #undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/Find/SFindInFlowFilterPopup.cpp b/Source/FlowEditor/Private/Find/SFindInFlowFilterPopup.cpp new file mode 100644 index 000000000..7735a2d90 --- /dev/null +++ b/Source/FlowEditor/Private/Find/SFindInFlowFilterPopup.cpp @@ -0,0 +1,145 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Find/SFindInFlowFilterPopup.h" +#include "Widgets/Input/SCheckBox.h" +#include "Widgets/Text/STextBlock.h" +#include "Widgets/Input/SButton.h" +#include "Widgets/Layout/SScrollBox.h" +#include "Framework/Application/SlateApplication.h" + +#define LOCTEXT_NAMESPACE "FindInFlow" + +void SFindInFlowFilterPopup::Construct(const FArguments& InArgs) +{ + OnApplyDelegate = InArgs._OnApply; + OnSaveAsDefaultDelegate = InArgs._OnSaveAsDefault; + ProposedFlags = InArgs._InitialFlags; + + // Build the checkbox container with slots added during construction + SAssignNew(CheckBoxContainer, SVerticalBox); + + for (EFlowSearchFlags Flag : MakeFlagsRange(EFlowSearchFlags::All)) + { + CheckBoxContainer->AddSlot() + .AutoHeight() + [ + SNew(SCheckBox) + .IsChecked(this, &SFindInFlowFilterPopup::GetCheckState, Flag) + .OnCheckStateChanged_Lambda([this, Flag](ECheckBoxState NewState) + { + if (NewState == ECheckBoxState::Checked) + { + EnumAddFlags(ProposedFlags, Flag); + } + else + { + EnumRemoveFlags(ProposedFlags, Flag); + } + }) + [ + SNew(STextBlock) + .Text(UEnum::GetDisplayValueAsText(Flag)) + ] + ]; + } + + ChildSlot + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .AutoHeight() + .Padding(10) + [ + SNew(STextBlock) + .Text(LOCTEXT("FilterPopupTitle", "Select Search Filters:")) + .Font(FAppStyle::GetFontStyle("NormalFontBold")) + ] + + SVerticalBox::Slot() + .FillHeight(1.0f) + .Padding(10, 5) + [ + SNew(SScrollBox) + + SScrollBox::Slot() + [ + CheckBoxContainer.ToSharedRef() + ] + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(10) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + [ + SNew(SButton) + .Text(LOCTEXT("ToggleAllFilters", "Toggle All")) + .OnClicked(this, &SFindInFlowFilterPopup::OnToggleAllClicked) + ] + + SHorizontalBox::Slot() + .AutoWidth() + [ + SNew(SButton) + .Text(LOCTEXT("SaveAsDefaultFilters", "Save as Default")) + .OnClicked(this, &SFindInFlowFilterPopup::OnSaveAsDefaultClicked) + ] + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(10) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + [ + SNew(SButton) + .Text(LOCTEXT("CancelFilters", "Cancel")) + .OnClicked(this, &SFindInFlowFilterPopup::OnCancelClicked) + ] + + SHorizontalBox::Slot() + .AutoWidth() + [ + SNew(SButton) + .Text(LOCTEXT("ApplyFilters", "Apply")) + .OnClicked(this, &SFindInFlowFilterPopup::OnApplyClicked) + ] + ] + ]; +} + +FReply SFindInFlowFilterPopup::OnApplyClicked() +{ + OnApplyDelegate.ExecuteIfBound(ProposedFlags); + FSlateApplication::Get().DismissAllMenus(); + return FReply::Handled(); +} + +FReply SFindInFlowFilterPopup::OnCancelClicked() +{ + FSlateApplication::Get().DismissAllMenus(); + return FReply::Handled(); +} + +FReply SFindInFlowFilterPopup::OnToggleAllClicked() +{ + if (ProposedFlags != EFlowSearchFlags::None) + { + ProposedFlags = EFlowSearchFlags::None; + } + else + { + ProposedFlags = EFlowSearchFlags::All; + } + + CheckBoxContainer->Invalidate(EInvalidateWidgetReason::Layout); + + return FReply::Handled(); +} + +FReply SFindInFlowFilterPopup::OnSaveAsDefaultClicked() +{ + OnSaveAsDefaultDelegate.ExecuteIfBound(ProposedFlags); + return FReply::Handled(); +} + +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/FlowEditor/Private/FlowEditorModule.cpp b/Source/FlowEditor/Private/FlowEditorModule.cpp index 625bed085..20158b634 100644 --- a/Source/FlowEditor/Private/FlowEditorModule.cpp +++ b/Source/FlowEditor/Private/FlowEditorModule.cpp @@ -35,6 +35,7 @@ #include "FlowAsset.h" #include "AddOns/FlowNodeAddOn.h" #include "Asset/FlowAssetParamsTypes.h" +#include "Find/FindInFlow.h" #include "Nodes/Actor/FlowNode_ComponentObserver.h" #include "Nodes/Actor/FlowNode_PlayLevelSequence.h" #include "Nodes/Graph/FlowNode_CustomInput.h" @@ -43,6 +44,7 @@ #include "Types/FlowNamedDataPinProperty.h" #include "AssetToolsModule.h" +#include "AssetRegistry/AssetRegistryModule.h" #include "EdGraphUtilities.h" #include "IAssetSearchModule.h" #include "Framework/MultiBox/MultiBoxBuilder.h" @@ -97,6 +99,20 @@ void FFlowEditorModule::StartupModule() ModulesChangedHandle = FModuleManager::Get().OnModulesChanged().AddRaw(this, &FFlowEditorModule::ModulesChangesCallback); } +void FFlowEditorModule::RegisterForAssetChanges() +{ + if (!bIsRegisteredForAssetChanges) + { + // Register asset change detection for search cache invalidation + FAssetRegistryModule& AssetRegistry = FModuleManager::LoadModuleChecked("AssetRegistry"); + AssetRegistry.Get().OnAssetUpdated().AddRaw(this, &FFlowEditorModule::OnAssetUpdated); + AssetRegistry.Get().OnAssetRenamed().AddRaw(this, &FFlowEditorModule::OnAssetRenamed); + AssetRegistry.Get().OnAssetRemoved().AddRaw(this, &FFlowEditorModule::OnAssetUpdated); + + bIsRegisteredForAssetChanges = true; + } +} + void FFlowEditorModule::ShutdownModule() { FFlowEditorStyle::Shutdown(); @@ -110,6 +126,18 @@ void FFlowEditorModule::ShutdownModule() SequencerModule.UnRegisterTrackEditor(FlowTrackCreateEditorHandle); FModuleManager::Get().OnModulesChanged().Remove(ModulesChangedHandle); + + if (bIsRegisteredForAssetChanges && FModuleManager::Get().IsModuleLoaded("AssetRegistry")) + { + // Unregister asset change detection + FAssetRegistryModule& AssetRegistry = FModuleManager::Get().GetModuleChecked("AssetRegistry"); + + AssetRegistry.Get().OnAssetUpdated().RemoveAll(this); + AssetRegistry.Get().OnAssetRenamed().RemoveAll(this); + AssetRegistry.Get().OnAssetRemoved().RemoveAll(this); + + bIsRegisteredForAssetChanges = false; + } } void FFlowEditorModule::TrySetFlowNodeDisplayStyleDefaults() const @@ -308,6 +336,19 @@ TSharedRef FFlowEditorModule::CreateFlowAssetEditor(const EToo return NewFlowAssetEditor; } +void FFlowEditorModule::OnAssetUpdated(const FAssetData& AssetData) +{ + if (UFlowAsset* FlowAsset = Cast(AssetData.GetAsset())) + { + FFindInFlowCache::OnFlowAssetChanged(*FlowAsset); + } +} + +void FFlowEditorModule::OnAssetRenamed(const FAssetData& AssetData, const FString& OldObjectPath) +{ + OnAssetUpdated(AssetData); +} + #undef LOCTEXT_NAMESPACE IMPLEMENT_MODULE(FFlowEditorModule, FlowEditor) \ No newline at end of file diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp index b6d4cfea7..78c54ff23 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp @@ -416,7 +416,7 @@ FSlateColor SFlowGraphNode::GetConfigBoxBackgroundColor() const void SFlowGraphNode::CreateBelowPinControls(const TSharedPtr InnerVerticalBox) { - static const FMargin ConfigBoxPadding = FMargin(2.0f, 0.0f, 1.0f, 0.0); + static const FMargin ConfigBoxPadding = FMargin(2.0f, 0.0f, 1.0f, 0.0f); // Add a box to wrap around the Config Text area to make it a more visually distinct part of the node TSharedPtr BelowPinsBox; @@ -1224,4 +1224,4 @@ void SFlowGraphNode::SetOwner(const TSharedRef& OwnerPanel) } } -#undef LOCTEXT_NAMESPACE +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/FlowEditor/Public/Find/FindInFlow.h b/Source/FlowEditor/Public/Find/FindInFlow.h index 848bcff4d..9c8321416 100644 --- a/Source/FlowEditor/Public/Find/FindInFlow.h +++ b/Source/FlowEditor/Public/Find/FindInFlow.h @@ -20,32 +20,38 @@ #include "UObject/WeakObjectPtrTemplates.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "Widgets/SCompoundWidget.h" +#include "Widgets/Input/SSpinBox.h" #include "Widgets/Views/STableViewBase.h" #include "Widgets/Views/STreeView.h" +#include "Types/FlowEnumUtils.h" +#include "FindInFlowEnums.h" + class ITableRow; class SWidget; class UFlowGraphNode; class UEdGraphNode; +class UFlowAsset; +class UFlowNodeBase; /** Item that matched the search results */ class FFindInFlowResult { -public: +public: /** Create a root (or only text) result */ - FFindInFlowResult(const FString& InValue); - + FFindInFlowResult(const FString& InValue, UFlowAsset* InOwningFlowAsset = nullptr); + /** Create a flow node result */ - FFindInFlowResult(const FString& InValue, TSharedPtr& InParent, UEdGraphNode* InNode, bool bInIsSubGraphNode = false); + FFindInFlowResult(const FString& InValue, TSharedPtr InParent, UEdGraphNode* InNode, bool bInIsSubGraphNode = false, UFlowAsset* InOwningFlowAsset = nullptr); /** Called when user clicks on the search item */ - FReply OnClick(TWeakPtr FlowAssetEditor, TSharedPtr Root); - + FReply OnClick(TWeakPtr FlowAssetEditorPtr); + /** Called when user double clicks on the search item */ - FReply OnDoubleClick(TSharedPtr Root) const; + FReply OnDoubleClick() const; /** Create an icon to represent the result */ - TSharedRef CreateIcon() const; + TSharedRef CreateIcon() const; /** Gets the description on flow node if any */ FString GetDescriptionText() const; @@ -59,15 +65,30 @@ class FFindInFlowResult /** Gets the node tool tip */ FText GetToolTipText() const; - /** Any children listed under this flow node (decorators and services) */ + /** Returns a snippet of the matched property/value for tooltip */ + FText GetMatchedSnippet() const; + + /** Human-readable list of categories this result matched in */ + FText GetMatchedCategoriesText() const; + + /** Any children listed under this flow node (decorators, services, addons, subnodes) */ TArray< TSharedPtr > Children; /** The string value for this result */ FString Value; + /** Stores a snippet of the matched property/value (e.g. "Damage:50") */ + FString MatchedPropertySnippet; + + /** Which search categories actually produced a hit for this item */ + EFlowSearchFlags MatchedFlags = EFlowSearchFlags::None; + /** The graph node that this search result refers to */ TWeakObjectPtr GraphNode; + /** The owning flow asset for this result */ + TWeakObjectPtr OwningFlowAsset; + /** Search result parent */ TWeakPtr Parent; @@ -75,11 +96,46 @@ class FFindInFlowResult bool bIsSubGraphNode = false; }; +struct FFindInFlowCache +{ + /** Removes all cached data for the changed flow asset */ + static void OnFlowAssetChanged(UFlowAsset& ChangedFlowAsset); + + /** Cache searchable strings per node (for repeat searches) */ + static TMap, TMap>> CategoryStringCache; +}; + +struct FFindInFlowAllResults +{ + typedef TSharedPtr FSearchResult; + + /** we need to keep a handle on the root result, because it won't show up in the tree */ + FSearchResult RootSearchResult; + + /** This buffer stores the currently displayed results */ + TArray ItemsFound; + + /** Visited assets to prevent cycles in subgraph recursion */ + TSet VisitedAssets; + + void Setup() + { + RootSearchResult = MakeShareable(new FFindInFlowResult(TEXT("Root"))); + } + + void Reset() + { + ItemsFound.Empty(); + RootSearchResult->Children.Empty(); + VisitedAssets.Empty(); + } +}; + /** Widget for searching for (Flow nodes) across focused FlowNodes */ class SFindInFlow : public SCompoundWidget { public: - SLATE_BEGIN_ARGS(SFindInFlow){} + SLATE_BEGIN_ARGS(SFindInFlow) {} SLATE_END_ARGS() void Construct(const FArguments& InArgs, TSharedPtr InFlowAssetEditor); @@ -87,7 +143,8 @@ class SFindInFlow : public SCompoundWidget /** Focuses this widget's search box */ void FocusForUse() const; -private: +protected: + typedef TSharedPtr FSearchResult; typedef STreeView STreeViewType; @@ -97,55 +154,84 @@ class SFindInFlow : public SCompoundWidget /** Called when user commits text */ void OnSearchTextCommitted(const FText& Text, ETextCommit::Type CommitType); + /** Called when search button is clicked */ + FReply OnSearchButtonClicked(); + /** Get the children of a row */ void OnGetChildren(FSearchResult InItem, TArray& OutChildren); /** Called when user clicks on a new result */ void OnTreeSelectionChanged(FSearchResult Item, ESelectInfo::Type SelectInfo); - + /* Called when user double clicks on a new result */ - void OnTreeSelectionDoubleClicked( FSearchResult Item ); + void OnTreeSelectionDoubleClicked(FSearchResult Item); - /** Called when whether find in sub graph changed */ - void OnFindInSubGraphStateChanged(ECheckBoxState CheckBoxState); + /** Called when scope selection changed */ + void OnScopeChanged(TSharedPtr NewSelection, ESelectInfo::Type SelectInfo); + + /** Called when max depth changed */ + void OnMaxDepthChanged(int32 NewDepth); /** Called when a new row is being generated */ TSharedRef OnGenerateRow(FSearchResult InItem, const TSharedRef& OwnerTable); /** Begins the search based on the SearchValue */ void InitiateSearch(); - - /** Find any results that contain all of the tokens */ - void MatchTokens(const TArray& Tokens); - /** Find if child contains all of the tokens and add a result accordingly */ - static void MatchTokensInChild(const TArray& Tokens, UEdGraphNode* Child, FSearchResult ParentNode); - + /** Build searchable string from node and its FlowNodeBase + AddOns */ + const TMap>* BuildCategoryStrings(UEdGraphNode* Node, int32 Depth) const; + /** Determines if a string matches the search tokens */ static bool StringMatchesSearchTokens(const TArray& Tokens, const FString& ComparisonString); + static bool StringSetMatchesSearchTokens(const TArray& Tokens, const TSet& StringSet); + + /** Generate widget for scope combo */ + TSharedRef GenerateScopeWidget(TSharedPtr Item) const; + + /** Get current scope display text */ + FText GetCurrentScopeText() const; + + bool ProcessAsset(UFlowAsset* Asset, FSearchResult ParentResult, const TArray& Tokens, int32 Depth); + + bool RecurseIntoSubgraphsIfEnabled(UEdGraphNode* EdNode, FSearchResult ParentResult, const TArray& Tokens, int32 Depth); -private: + void UpdateSearchFlagToStringMapForEdGraphNode(const UEdGraphNode& EdGraphNode, TMap>& SearchFlagToStringMap, int32 Depth) const; + void UpdateSearchFlagToStringMapForFlowNodeBase(const UFlowNodeBase& FlowNodeBase, TMap>& SearchFlagToStringMap, int32 Depth) const; + void AppendPropertyValues(const void* Container, const UStruct* Struct, const UObject* ParentObject, TMap>& SearchFlagToStringMap, int32 Depth) const; + +protected: /** Pointer back to the flow editor that owns us */ TWeakPtr FlowAssetEditorPtr; - + /** The tree view displays the results */ TSharedPtr TreeView; /** The search text box */ TSharedPtr SearchTextField; - - /** This buffer stores the currently displayed results */ - TArray ItemsFound; - /** we need to keep a handle on the root result, because it won't show up in the tree */ - FSearchResult RootSearchResult; + /** The search button */ + TSharedPtr SearchButton; + + /** Struct with all of the search results */ + FFindInFlowAllResults SearchResults; + + /** Repeat Search Caching */ + FFindInFlowCache SearchCache; /** The string to highlight in the results */ FText HighlightText; /** The string to search for */ - FString SearchValue; + FString SearchValue; - /** Using to control whether search in sub graph */ - bool bFindInSubGraph = false; -}; + /** Search configuration */ + EFlowSearchFlags SearchFlags = EFlowSearchFlags::DefaultSearchFlags; + + TSharedPtr> MaxDepthSpinBox; + int32 MaxSearchDepth = 3; + + /** Scope selection */ + TArray> ScopeOptionList; + TSharedPtr SelectedScopeOption; + EFlowSearchScope SearchScope = EFlowSearchScope::ThisAssetOnly; +}; \ No newline at end of file diff --git a/Source/FlowEditor/Public/Find/FindInFlowEnums.h b/Source/FlowEditor/Public/Find/FindInFlowEnums.h new file mode 100644 index 000000000..e0c7a6887 --- /dev/null +++ b/Source/FlowEditor/Public/Find/FindInFlowEnums.h @@ -0,0 +1,48 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Types/FlowEnumUtils.h" + +#include "FindInFlowEnums.generated.h" + +/** Bitflags controlling what parts of a Flow node are included in search */ +UENUM(Meta = (Bitflags, UseEnumValuesAsMaskValuesInEditor = "true")) +enum class EFlowSearchFlags : uint32 +{ + None = 0 UMETA(Hidden), + + Titles = 1 << 0 UMETA(DisplayName = "Titles"), + Tooltips = 1 << 1 UMETA(DisplayName = "Tooltips"), + Classes = 1 << 2 UMETA(DisplayName = "Classes"), + Comments = 1 << 3 UMETA(DisplayName = "Comments"), + Descriptions = 1 << 4 UMETA(DisplayName = "Descriptions"), + ConfigText = 1 << 5 UMETA(DisplayName = "Config Text"), + PropertyNames = 1 << 6 UMETA(DisplayName = "Property Names"), + PropertyValues = 1 << 7 UMETA(DisplayName = "Property Values"), + AddOns = 1 << 8 UMETA(DisplayName = "Add-Ons"), + Subgraphs = 1 << 9 UMETA(DisplayName = "Subgraphs"), + + All = + Titles | Tooltips | Classes | Comments | Descriptions | ConfigText | + PropertyNames | PropertyValues | AddOns | Subgraphs UMETA(Hidden), + + // Default mask — used at startup and for "reset" + DefaultSearchFlags = All UMETA(Hidden), + PropertiesFlags = PropertyNames | PropertyValues | Tooltips UMETA(Hidden), +}; +ENUM_CLASS_FLAGS(EFlowSearchFlags); + +/** Search scope — intentionally minimal */ +UENUM() +enum class EFlowSearchScope : uint8 +{ + ThisAssetOnly UMETA(DisplayName = "This Asset", ToolTip = "Search only the currently open Flow Asset"), + AllOfThisType UMETA(DisplayName = "All Flow Assets of This Type",ToolTip = "Search all Flow Assets of this type (or subclasses)"), + AllFlowAssets UMETA(DisplayName = "All Flow Assets", ToolTip = "Search every Flow Asset in the project"), + + Max UMETA(Hidden), + Invalid UMETA(Hidden), + Min = 0 UMETA(Hidden), +}; +FLOW_ENUM_RANGE_VALUES(EFlowSearchScope); \ No newline at end of file diff --git a/Source/FlowEditor/Public/Find/SFindInFlowFilterPopup.h b/Source/FlowEditor/Public/Find/SFindInFlowFilterPopup.h new file mode 100644 index 000000000..49843b068 --- /dev/null +++ b/Source/FlowEditor/Public/Find/SFindInFlowFilterPopup.h @@ -0,0 +1,42 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Widgets/DeclarativeSyntaxSupport.h" +#include "Widgets/SCompoundWidget.h" +#include "Widgets/SBoxPanel.h" + +#include "FindInFlowEnums.h" + +DECLARE_DELEGATE_OneParam(FFindInFlowApplyDelegate, EFlowSearchFlags); + +class SFindInFlowFilterPopup : public SCompoundWidget +{ +public: + SLATE_BEGIN_ARGS(SFindInFlowFilterPopup) {} + SLATE_ARGUMENT(FFindInFlowApplyDelegate, OnApply) + SLATE_ARGUMENT(FFindInFlowApplyDelegate, OnSaveAsDefault) + SLATE_ARGUMENT(EFlowSearchFlags, InitialFlags) + SLATE_END_ARGS() + + void Construct(const FArguments& InArgs); + +protected: + + EFlowSearchFlags ProposedFlags = EFlowSearchFlags::DefaultSearchFlags; + + FFindInFlowApplyDelegate OnApplyDelegate; + FFindInFlowApplyDelegate OnSaveAsDefaultDelegate; + + TSharedPtr CheckBoxContainer; + + ECheckBoxState GetCheckState(EFlowSearchFlags Flag) const + { + return EnumHasAnyFlags(ProposedFlags, Flag) ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; + } + + FReply OnApplyClicked(); + FReply OnCancelClicked(); + FReply OnToggleAllClicked(); + FReply OnSaveAsDefaultClicked(); +}; \ No newline at end of file diff --git a/Source/FlowEditor/Public/FlowEditorModule.h b/Source/FlowEditor/Public/FlowEditorModule.h index 458a106e3..9e32e6605 100644 --- a/Source/FlowEditor/Public/FlowEditorModule.h +++ b/Source/FlowEditor/Public/FlowEditorModule.h @@ -30,6 +30,7 @@ class FLOWEDITOR_API FFlowEditorModule : public IModuleInterface, public IHasMen TArray> RegisteredAssetActions; TSet CustomClassLayouts; TSet CustomStructLayouts; + bool bIsRegisteredForAssetChanges = false; public: virtual void StartupModule() override; @@ -41,6 +42,8 @@ class FLOWEDITOR_API FFlowEditorModule : public IModuleInterface, public IHasMen UE_DEPRECATED(5.5, "The old method has been removed. Please use UToolMenus::Get()->ExtendMenu() instead. You can find example in SFlowGraphEditor::CreateDebugMenu().") virtual TSharedPtr GetToolBarExtensibilityManager() override { return nullptr; } + void RegisterForAssetChanges(); + private: void TrySetFlowNodeDisplayStyleDefaults() const; @@ -65,4 +68,7 @@ class FLOWEDITOR_API FFlowEditorModule : public IModuleInterface, public IHasMen public: static TSharedRef CreateFlowAssetEditor(const EToolkitMode::Type Mode, const TSharedPtr& InitToolkitHost, UFlowAsset* FlowAsset); -}; + + void OnAssetUpdated(const FAssetData& AssetData); + void OnAssetRenamed(const FAssetData& AssetData, const FString& OldObjectPath); +}; \ No newline at end of file diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h index f19656734..8695702ca 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h @@ -3,6 +3,8 @@ #pragma once #include "Engine/DeveloperSettings.h" +#include "Find/FindInFlowEnums.h" + #include "FlowGraphEditorSettings.generated.h" UENUM() @@ -67,6 +69,14 @@ class FLOWEDITOR_API UFlowGraphEditorSettings : public UDeveloperSettings UPROPERTY(EditAnywhere, config, Category = "Wires") bool bHighlightOutputWiresOfSelectedNodes; + // Default search filter flags for the Flow Editor + UPROPERTY(VisibleAnywhere, config, Category = "Search", meta = (Bitmask, BitmaskEnum = "/Script/Flow.EFlowSearchFlags")) + uint32 DefaultSearchFlags = uint32(EFlowSearchFlags::DefaultSearchFlags); + + // Max search depth for inline objects in the Flow Editor + UPROPERTY(EditAnywhere, config, Category = "Search", meta = (ClampMin = 1)) + int32 DefaultMaxSearchDepth = 1; + public: virtual FName GetCategoryName() const override { return FName("Flow Graph"); } virtual FText GetSectionText() const override { return INVTEXT("User Settings"); } diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h index a912b60a4..78918f786 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h @@ -2,6 +2,9 @@ #pragma once +#include "Asset/FlowPinTypeMatchPolicy.h" +#include "Types/FlowPinTypeNamesStandard.h" + #include "EdGraph/EdGraphSchema.h" #include "Runtime/Launch/Resources/Version.h" #include "Templates/SubclassOf.h" From 7fc1819ead8405d12bf07c6087e042c825884760 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gregor=20S=C3=B6nnichsen?= <42077794+gregorhcs@users.noreply.github.com> Date: Wed, 28 Jan 2026 17:33:06 +0100 Subject: [PATCH 387/485] Added option to tone down on-screen error messages (#324) --- Source/Flow/Private/FlowComponent.cpp | 2 +- Source/Flow/Private/Nodes/FlowNode.cpp | 3 ++- Source/Flow/Private/Nodes/FlowNodeBase.cpp | 2 +- Source/Flow/Public/FlowSettings.h | 6 ++++++ Source/Flow/Public/FlowTypes.h | 3 ++- 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Source/Flow/Private/FlowComponent.cpp b/Source/Flow/Private/FlowComponent.cpp index de7eb5bd7..d26b4551d 100644 --- a/Source/Flow/Private/FlowComponent.cpp +++ b/Source/Flow/Private/FlowComponent.cpp @@ -303,7 +303,7 @@ void UFlowComponent::LogError(FString Message, const EFlowOnScreenMessageType On } } } - else + else if (OnScreenMessageType == EFlowOnScreenMessageType::Temporary) { GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, Message); } diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 753f36e93..263e08002 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -943,7 +943,8 @@ void UFlowNode::TriggerOutput(const FName PinName, const bool bFinish /*= false* if (HasFinished()) { // do not trigger output if node is already finished or aborted - LogError(TEXT("Trying to TriggerOutput after finished or aborted")); + LogError(TEXT("Trying to TriggerOutput after finished or aborted"), + GetDefault()->FinishedNodeTriggerErrorLogScreenPersistency); return; } diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp index ec0b0e2eb..4dc15b292 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBase.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -874,7 +874,7 @@ void UFlowNodeBase::LogError(FString Message, const EFlowOnScreenMessageType OnS } } } - else + else if (OnScreenMessageType == EFlowOnScreenMessageType::Temporary) { GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, Message); } diff --git a/Source/Flow/Public/FlowSettings.h b/Source/Flow/Public/FlowSettings.h index 9dbe4d63a..f85ff5e06 100644 --- a/Source/Flow/Public/FlowSettings.h +++ b/Source/Flow/Public/FlowSettings.h @@ -2,6 +2,7 @@ #pragma once +#include "FlowTypes.h" #include "Engine/DeveloperSettings.h" #include "Templates/SubclassOf.h" #include "UObject/SoftObjectPath.h" @@ -43,6 +44,11 @@ class FLOW_API UFlowSettings : public UDeveloperSettings // by incorporating data that would otherwise go in the Description UPROPERTY(EditAnywhere, config, Category = "Nodes") bool bUseAdaptiveNodeTitles; + + // On screen message type for runtime errors when a flow node + // is attempted to be triggered after being finished or aborted. + UPROPERTY(EditAnywhere, config, Category = "Nodes") + EFlowOnScreenMessageType FinishedNodeTriggerErrorLogScreenPersistency = EFlowOnScreenMessageType::Permanent; #if WITH_EDITOR DECLARE_DELEGATE(FFlowSettingsEvent); diff --git a/Source/Flow/Public/FlowTypes.h b/Source/Flow/Public/FlowTypes.h index 301615181..099359843 100644 --- a/Source/Flow/Public/FlowTypes.h +++ b/Source/Flow/Public/FlowTypes.h @@ -111,7 +111,8 @@ UENUM(BlueprintType) enum class EFlowOnScreenMessageType : uint8 { Temporary, - Permanent + Permanent, + Disabled }; UENUM(BlueprintType) From a33475382ab3230ea45434e9ffc020811842ec29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gregor=20S=C3=B6nnichsen?= <42077794+gregorhcs@users.noreply.github.com> Date: Wed, 28 Jan 2026 17:41:38 +0100 Subject: [PATCH 388/485] Added virtual method to allow saving non-executing flow nodes (#320) --- Source/Flow/Private/FlowAsset.cpp | 2 +- Source/Flow/Private/Nodes/FlowNode.cpp | 5 +++++ Source/Flow/Public/Nodes/FlowNode.h | 3 +++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 44c351c3d..a6d32fb47 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -1216,7 +1216,7 @@ FFlowAssetSaveData UFlowAsset::SaveInstance(TArray& SavedFlo GetNodesInExecutionOrder(GetDefaultEntryNode(), NodesInExecutionOrder); for (UFlowNode* Node : NodesInExecutionOrder) { - if (Node && Node->ActivationState == EFlowNodeState::Active) + if (Node && Node->ShouldSave()) { // iterate SubGraphs if (UFlowNode_SubGraph* SubGraphNode = Cast(Node)) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 263e08002..64d2aea7d 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -1079,6 +1079,11 @@ void UFlowNode::OnPassThrough_Implementation() Finish(); } +bool UFlowNode::ShouldSave_Implementation() +{ + return GetActivationState() == EFlowNodeState::Active; +} + #if WITH_EDITOR TMap UFlowNode::GetWireRecords() const { diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 67be1b730..f5d50bc5f 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -366,6 +366,9 @@ class FLOW_API UFlowNode : public UFlowNodeBase UFUNCTION(BlueprintNativeEvent, Category = "FlowNode") void OnPassThrough(); + UFUNCTION(BlueprintNativeEvent, Category = "FlowNode") + bool ShouldSave(); + ////////////////////////////////////////////////////////////////////////// // Utils From aa5a26a31afd1c55fe9e6a12f1262e8522bfd9bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Wed, 28 Jan 2026 17:45:40 +0100 Subject: [PATCH 389/485] actually let don't print TriggerOutput warning on the screen, is too intrusive --- Source/Flow/Private/Nodes/FlowNode.cpp | 3 +-- Source/Flow/Public/FlowSettings.h | 6 ------ 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 263e08002..e1d5e12e7 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -943,8 +943,7 @@ void UFlowNode::TriggerOutput(const FName PinName, const bool bFinish /*= false* if (HasFinished()) { // do not trigger output if node is already finished or aborted - LogError(TEXT("Trying to TriggerOutput after finished or aborted"), - GetDefault()->FinishedNodeTriggerErrorLogScreenPersistency); + LogError(TEXT("Trying to TriggerOutput after finished or aborted"), EFlowOnScreenMessageType::Disabled); return; } diff --git a/Source/Flow/Public/FlowSettings.h b/Source/Flow/Public/FlowSettings.h index f85ff5e06..1a8468bbc 100644 --- a/Source/Flow/Public/FlowSettings.h +++ b/Source/Flow/Public/FlowSettings.h @@ -2,7 +2,6 @@ #pragma once -#include "FlowTypes.h" #include "Engine/DeveloperSettings.h" #include "Templates/SubclassOf.h" #include "UObject/SoftObjectPath.h" @@ -45,11 +44,6 @@ class FLOW_API UFlowSettings : public UDeveloperSettings UPROPERTY(EditAnywhere, config, Category = "Nodes") bool bUseAdaptiveNodeTitles; - // On screen message type for runtime errors when a flow node - // is attempted to be triggered after being finished or aborted. - UPROPERTY(EditAnywhere, config, Category = "Nodes") - EFlowOnScreenMessageType FinishedNodeTriggerErrorLogScreenPersistency = EFlowOnScreenMessageType::Permanent; - #if WITH_EDITOR DECLARE_DELEGATE(FFlowSettingsEvent); FFlowSettingsEvent OnAdaptiveNodeTitlesChanged; From 93230642dcf15ec41ea402203fb8ee48e8394d8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Wed, 28 Jan 2026 17:51:47 +0100 Subject: [PATCH 390/485] non-editor compilation fixes --- Source/Flow/Private/Nodes/FlowNode.cpp | 4 ++++ Source/Flow/Private/Nodes/FlowNodeBase.cpp | 2 ++ Source/Flow/Public/Nodes/FlowNode.h | 8 ++------ Source/Flow/Public/Nodes/FlowNodeBase.h | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index f2defef21..aa319b35a 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -574,6 +574,7 @@ bool UFlowNode::TryGetFlowDataPinSupplierDatasForPinName(const FName& PinName, T return !InOutPinValueSupplierDatas.IsEmpty(); } +#if WITH_EDITOR void UFlowNode::AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const { // Gather all of the potential providers for this DataPin @@ -588,6 +589,7 @@ void UFlowNode::AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingD InOutWorkingData.AddFlowDataPinsForClassProperties(*PropertyOwnerObject); } } +#endif // #FlowDataPinLegacy void UFlowNode::FixupDataPinTypes() @@ -627,12 +629,14 @@ void UFlowNode::FixupDataPinTypesForPin(FFlowPin& MutableDataPin) } // -- +#if WITH_EDITOR void UFlowNode::SetConnections(const TMap& InConnections) { const TMap OldConnections = Connections; Connections = InConnections; OnConnectionsChanged(OldConnections); } +#endif TSet UFlowNode::GatherConnectedNodes() const { diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp index 4dc15b292..56360b9a1 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBase.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -958,6 +958,7 @@ bool UFlowNodeBase::BuildMessage(FString& Message) const } #endif +#if WITH_EDITOR EDataValidationResult UFlowNodeBase::ValidateNode() { EDataValidationResult ValidationResult = EDataValidationResult::NotValidated; @@ -969,6 +970,7 @@ EDataValidationResult UFlowNodeBase::ValidateNode() return ValidationResult; } +#endif bool UFlowNodeBase::TryAddValueToFormatNamedArguments(const FFlowNamedDataPinProperty& NamedDataPinProperty, FFormatNamedArguments& InOutArguments) const { diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index f5d50bc5f..6e2d6fbb1 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -171,11 +171,8 @@ class FLOW_API UFlowNode : public UFlowNodeBase public: #if WITH_EDITOR void SetConnections(const TMap& InConnections); - - virtual void OnConnectionsChanged(const TMap& OldConnections) - { - } -#endif // WITH_EDITOR + virtual void OnConnectionsChanged(const TMap& OldConnections) {} +#endif FConnectedPin GetConnection(const FName OutputName) const { return Connections.FindRef(OutputName); } @@ -275,7 +272,6 @@ class FLOW_API UFlowNode : public UFlowNodeBase // IFlowDataPinGeneratorInterface #if WITH_EDITOR - public: virtual void AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const override; #endif diff --git a/Source/Flow/Public/Nodes/FlowNodeBase.h b/Source/Flow/Public/Nodes/FlowNodeBase.h index 152bfe769..ef8fb5004 100644 --- a/Source/Flow/Public/Nodes/FlowNodeBase.h +++ b/Source/Flow/Public/Nodes/FlowNodeBase.h @@ -483,7 +483,7 @@ class FLOW_API UFlowNodeBase #if WITH_EDITOR virtual EDataValidationResult ValidateNode(); -#endif +#endif // Optional validation override for Blueprints UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode|Validation", meta = (DisplayName = "Validate Node", DevelopmentOnly)) From 83e2d1da787004c5767e40da9954184c3b379308 Mon Sep 17 00:00:00 2001 From: Danamarik <39652402+Danamarik@users.noreply.github.com> Date: Fri, 30 Jan 2026 14:15:00 +0200 Subject: [PATCH 391/485] API extension support (#330) * FlowNodeBlueprintLibrary and FlowNodeAddOn cross-module inheritance support --- Source/Flow/Public/AddOns/FlowNodeAddOn.h | 2 +- Source/FlowEditor/Public/Nodes/FlowNodeBlueprintFactory.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Flow/Public/AddOns/FlowNodeAddOn.h b/Source/Flow/Public/AddOns/FlowNodeAddOn.h index aabea5c4d..e2d2931be 100644 --- a/Source/Flow/Public/AddOns/FlowNodeAddOn.h +++ b/Source/Flow/Public/AddOns/FlowNodeAddOn.h @@ -41,7 +41,7 @@ class UFlowNodeAddOn : public UFlowNodeBase // UFlowNodeBase #if WITH_EDITOR - virtual UEdGraphNode* GetGraphNode() const override; + FLOW_API virtual UEdGraphNode* GetGraphNode() const override; #endif // AddOns may opt in to be eligible for a given parent diff --git a/Source/FlowEditor/Public/Nodes/FlowNodeBlueprintFactory.h b/Source/FlowEditor/Public/Nodes/FlowNodeBlueprintFactory.h index 9ce489369..e3752525e 100644 --- a/Source/FlowEditor/Public/Nodes/FlowNodeBlueprintFactory.h +++ b/Source/FlowEditor/Public/Nodes/FlowNodeBlueprintFactory.h @@ -19,9 +19,9 @@ class UFlowNodeBaseBlueprintFactory : public UFactory TSubclassOf ParentClass; // UFactory - virtual bool ConfigureProperties() override; - virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn, FName CallingContext) override; - virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; + FLOWEDITOR_API virtual bool ConfigureProperties() override; + FLOWEDITOR_API virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn, FName CallingContext) override; + FLOWEDITOR_API virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; void ShowCannotCreateBlueprintDialog(); // -- From af1c0da9a79c4fcf302e1c5b7beb18bd1a5b6d02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Fri, 30 Jan 2026 18:22:20 +0100 Subject: [PATCH 392/485] safer check --- Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp index 4ef7e0220..7c0d96fc1 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp @@ -338,7 +338,7 @@ void SFlowAssetBreadcrumb::OnCrumbClicked(const FFlowBreadcrumb& Item) const ClickedTemplateAsset->SetInspectedInstance(ClickedInstance); if (const TSharedPtr FlowAssetEditor = FFlowGraphUtils::GetFlowAssetEditor(ClickedTemplateAsset)) { - if (!Item.ChildInstance.IsExplicitlyNull()) + if (Item.ChildInstance.IsValid()) { FlowAssetEditor->JumpToNode(Item.ChildInstance->GetNodeOwningThisAssetInstance()->GetGraphNode()); } From d226bd5aa82f6a55613f2902e502aa523a4b37cf Mon Sep 17 00:00:00 2001 From: LindyHopperGT <91915878+LindyHopperGT@users.noreply.github.com> Date: Sun, 1 Feb 2026 01:42:42 -0800 Subject: [PATCH 393/485] Bugfix for AddOns duplicating when a node is Refreshed in Editor (#331) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AddOns would duplicate in editor when a node was refreshed after integrating from mainline. AddOns have their own UFlowGraphNode (UEdGraphNode), and shouldn't be returning their UFlowNode's GraphNode. This is likely what was confusing the editor code and causing a duplication of the addons. This PR removes the override to GetGraphNode(). There may be other ramifications in the debugger upgrade code where this override was introduced, if it relies on this version's behavior. Co-authored-by: Krzysiek Justyński --- Source/Flow/Private/AddOns/FlowNodeAddOn.cpp | 15 --------------- Source/Flow/Public/AddOns/FlowNodeAddOn.h | 8 +------- 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp b/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp index 87537f30b..3a809ddff 100644 --- a/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp +++ b/Source/Flow/Private/AddOns/FlowNodeAddOn.cpp @@ -17,21 +17,6 @@ UFlowNodeAddOn::UFlowNodeAddOn() #endif } -#if WITH_EDITOR -UEdGraphNode* UFlowNodeAddOn::GetGraphNode() const -{ - // todo: we might want to cache editor-time pointer to owning Flow Node - // it may require more work than just caching it, as pointer needs - // to be updated during editor operations - if (const UFlowNode* OwningFlowNode = FindOwningFlowNode()) - { - return OwningFlowNode->GetGraphNode(); - } - - return nullptr; -} -#endif - void UFlowNodeAddOn::InitializeInstance() { CacheFlowNode(); diff --git a/Source/Flow/Public/AddOns/FlowNodeAddOn.h b/Source/Flow/Public/AddOns/FlowNodeAddOn.h index e2d2931be..1fe43bd6d 100644 --- a/Source/Flow/Public/AddOns/FlowNodeAddOn.h +++ b/Source/Flow/Public/AddOns/FlowNodeAddOn.h @@ -38,13 +38,7 @@ class UFlowNodeAddOn : public UFlowNodeBase #endif public: - // UFlowNodeBase - -#if WITH_EDITOR - FLOW_API virtual UEdGraphNode* GetGraphNode() const override; -#endif - - // AddOns may opt in to be eligible for a given parent + // AddOns may opt in to be eligible for a given parent // - ParentTemplate - the template of the FlowNode or FlowNodeAddOn that is being considered as a potential parent // - AdditionalAddOnsToAssumeAreChildren - other AddOns to assume that are already child AddOns for the purposes of this test. // This list will be populated with the 'other' AddOns in a multi-paste operation in the editor, From cf4534ea7f998e044c5a6c9d3f298668935bc88d Mon Sep 17 00:00:00 2001 From: LindyHopperGT <91915878+LindyHopperGT@users.noreply.github.com> Date: Sat, 7 Feb 2026 06:05:34 -0800 Subject: [PATCH 394/485] Flow debugger fixes (#328) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Search improvements vs. flow mainline * Flow Debugger Fixes Data Pin Values displayed in Tooltips Flow debugger stops at breakpoints bugfix * Reverted this fix for PR * restore changes from other PRs * integrated usage of Flow Node as main parameter passed with breakpoint hit * Integrations with our version * compile fixes * reverted formatting changes * restored previous class layout --------- Co-authored-by: Krzysztof Justyński --- Source/Flow/Private/FlowAsset.cpp | 21 ++ .../Private/Interfaces/FlowExecutionGate.cpp | 134 +++++++++ Source/Flow/Private/Nodes/FlowPin.cpp | 1 - .../Flow/Private/Types/FlowDataPinValue.cpp | 2 +- .../Types/FlowDataPinValuesStandard.cpp | 38 +++ Source/Flow/Public/FlowAsset.h | 6 +- .../Public/Interfaces/FlowExecutionGate.h | 45 +++ Source/Flow/Public/Nodes/FlowPin.h | 1 - .../Public/Types/FlowDataPinValuesStandard.h | 67 ++--- .../Debugger/FlowDebuggerSubsystem.cpp | 280 +++++++++++++----- .../Public/Debugger/FlowDebuggerSubsystem.h | 69 +++-- .../Private/Asset/FlowAssetToolbar.cpp | 1 + .../Asset/FlowDebugEditorSubsystem.cpp | 116 ++++++-- .../Private/Graph/FlowGraphEditor.cpp | 164 ++++++---- .../Private/Graph/Nodes/FlowGraphNode.cpp | 90 ++++-- .../FlowEditor/Public/Asset/FlowAssetEditor.h | 2 +- .../Public/Asset/FlowDebugEditorSubsystem.h | 7 +- Source/FlowEditor/Public/FlowEditorModule.h | 1 + .../FlowEditor/Public/Graph/FlowGraphEditor.h | 8 + 19 files changed, 822 insertions(+), 231 deletions(-) create mode 100644 Source/Flow/Private/Interfaces/FlowExecutionGate.cpp create mode 100644 Source/Flow/Public/Interfaces/FlowExecutionGate.h diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index a6d32fb47..63014aea2 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -8,6 +8,7 @@ #include "AddOns/FlowNodeAddOn.h" #include "Asset/FlowAssetParams.h" #include "Asset/FlowAssetParamsUtils.h" +#include "Interfaces/FlowExecutionGate.h" #include "Nodes/FlowNodeBase.h" #include "Nodes/Graph/FlowNode_CustomInput.h" #include "Nodes/Graph/FlowNode_CustomOutput.h" @@ -1012,6 +1013,11 @@ void UFlowAsset::PreStartFlow() void UFlowAsset::StartFlow(IFlowDataPinValueSupplierInterface* DataPinValueSupplier) { + if (FFlowExecutionGate::IsHalted()) + { + return; + } + PreStartFlow(); if (UFlowNode* ConnectedEntryNode = GetDefaultEntryNode()) @@ -1075,6 +1081,11 @@ TWeakObjectPtr UFlowAsset::GetFlowInstance(UFlowNode_SubGraph* SubGr void UFlowAsset::TriggerCustomInput_FromSubGraph(UFlowNode_SubGraph* SubGraphNode, const FName& EventName) const { + if (FFlowExecutionGate::IsHalted()) + { + return; + } + // NOTE (gtaylor) Custom Input nodes cannot currently add data pins (like Start or DefineProperties nodes can) // but we may want to allow them to source parameters, so I am providing the subgraph node as the // IFlowDataPinValueSupplierInterface when triggering the node (even though it's not used at this time). @@ -1088,6 +1099,11 @@ void UFlowAsset::TriggerCustomInput_FromSubGraph(UFlowNode_SubGraph* SubGraphNod void UFlowAsset::TriggerCustomInput(const FName& EventName, IFlowDataPinValueSupplierInterface* DataPinValueSupplier) { + if (FFlowExecutionGate::IsHalted()) + { + return; + } + for (UFlowNode_CustomInput* CustomInputNode : CustomInputNodes) { if (CustomInputNode->EventName == EventName) @@ -1127,6 +1143,11 @@ void UFlowAsset::TriggerCustomOutput(const FName& EventName) void UFlowAsset::TriggerInput(const FGuid& NodeGuid, const FName& PinName, const FConnectedPin& FromPin) { + if (FFlowExecutionGate::EnqueueDeferredTriggerInput(this, NodeGuid, PinName, FromPin)) + { + return; + } + if (UFlowNode* Node = Nodes.FindRef(NodeGuid)) { if (!ActiveNodes.Contains(Node)) diff --git a/Source/Flow/Private/Interfaces/FlowExecutionGate.cpp b/Source/Flow/Private/Interfaces/FlowExecutionGate.cpp new file mode 100644 index 000000000..44fd864f2 --- /dev/null +++ b/Source/Flow/Private/Interfaces/FlowExecutionGate.cpp @@ -0,0 +1,134 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Interfaces/FlowExecutionGate.h" + +#include "FlowAsset.h" +#include "Nodes/FlowPin.h" + +namespace FlowExecutionGate_Private +{ + struct FDeferredTriggerInput + { + TWeakObjectPtr FlowAssetInstance; + FGuid NodeGuid; + FName PinName; + FConnectedPin FromPin; + }; + + static TArray DeferredTriggerInputs; + static bool bIsFlushing = false; +} + +IFlowExecutionGate* FFlowExecutionGate::Gate = nullptr; + +void FFlowExecutionGate::SetGate(IFlowExecutionGate* InGate) +{ + Gate = InGate; +} + +IFlowExecutionGate* FFlowExecutionGate::GetGate() +{ + return Gate; +} + +bool FFlowExecutionGate::IsHalted() +{ + return (Gate != nullptr) && Gate->IsFlowExecutionHalted(); +} + +bool FFlowExecutionGate::EnqueueDeferredTriggerInput(UFlowAsset* FlowAssetInstance, const FGuid& NodeGuid, const FName& PinName, const FConnectedPin& FromPin) +{ + using namespace FlowExecutionGate_Private; + + // If we're halted, always enqueue (even during flushing). The whole point is to stop propagation. + if (IsHalted()) + { + if (!IsValid(FlowAssetInstance)) + { + return true; // treat as handled while halted + } + + FDeferredTriggerInput& Entry = DeferredTriggerInputs.AddDefaulted_GetRef(); + Entry.FlowAssetInstance = FlowAssetInstance; + Entry.NodeGuid = NodeGuid; + Entry.PinName = PinName; + Entry.FromPin = FromPin; + + return true; + } + + // Not halted: + // During flush we must not enqueue "normal" triggers (we want them to execute now), + // otherwise we can get infinite deferral. + if (bIsFlushing) + { + return false; + } + + return false; +} + +void FFlowExecutionGate::FlushDeferredTriggerInputs() +{ + using namespace FlowExecutionGate_Private; + + if (bIsFlushing) + { + return; + } + + // Do not flush while halted; callers should clear the halt first. + if (IsHalted()) + { + return; + } + + if (DeferredTriggerInputs.IsEmpty()) + { + return; + } + + bIsFlushing = true; + + // Move into a local array so new deferred triggers can be added while we flush. + TArray Local = MoveTemp(DeferredTriggerInputs); + DeferredTriggerInputs.Reset(); + + for (int32 Index = 0; Index < Local.Num(); ++Index) + { + // If a breakpoint was hit during this flush, stop immediately and re-queue remaining work. + if (IsHalted()) + { + const int32 Remaining = Local.Num() - Index; + if (Remaining > 0) + { + TArray RemainingItems; + RemainingItems.Reserve(Remaining); + + for (int32 j = Index; j < Local.Num(); ++j) + { + RemainingItems.Add(Local[j]); + } + + // RemainingItems should run before any items that may already be queued. + if (!DeferredTriggerInputs.IsEmpty()) + { + RemainingItems.Append(MoveTemp(DeferredTriggerInputs)); + } + + DeferredTriggerInputs = MoveTemp(RemainingItems); + } + + bIsFlushing = false; + return; + } + + const FDeferredTriggerInput& Entry = Local[Index]; + if (UFlowAsset* Asset = Entry.FlowAssetInstance.Get()) + { + Asset->TriggerDeferredInputFromDebugger(Entry.NodeGuid, Entry.PinName, Entry.FromPin); + } + } + + bIsFlushing = false; +} \ No newline at end of file diff --git a/Source/Flow/Private/Nodes/FlowPin.cpp b/Source/Flow/Private/Nodes/FlowPin.cpp index 84a3351ad..8c2a34cd5 100644 --- a/Source/Flow/Private/Nodes/FlowPin.cpp +++ b/Source/Flow/Private/Nodes/FlowPin.cpp @@ -18,7 +18,6 @@ // Pin Record #if !UE_BUILD_SHIPPING -FString FPinRecord::NoActivations = TEXT("No activations"); FString FPinRecord::PinActivations = TEXT("Pin activations"); FString FPinRecord::ForcedActivation = TEXT(" (forced activation)"); FString FPinRecord::PassThroughActivation = TEXT(" (pass-through activation)"); diff --git a/Source/Flow/Private/Types/FlowDataPinValue.cpp b/Source/Flow/Private/Types/FlowDataPinValue.cpp index b10c3e206..34de3e9f7 100644 --- a/Source/Flow/Private/Types/FlowDataPinValue.cpp +++ b/Source/Flow/Private/Types/FlowDataPinValue.cpp @@ -8,7 +8,7 @@ #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowDataPinValue) -const FString FFlowDataPinValue::StringArraySeparator = TEXT(","); +const FString FFlowDataPinValue::StringArraySeparator = TEXT(", "); const FFlowPinType* FFlowDataPinValue::LookupPinType() const { diff --git a/Source/Flow/Private/Types/FlowDataPinValuesStandard.cpp b/Source/Flow/Private/Types/FlowDataPinValuesStandard.cpp index 5e1e7627a..a31bd141c 100644 --- a/Source/Flow/Private/Types/FlowDataPinValuesStandard.cpp +++ b/Source/Flow/Private/Types/FlowDataPinValuesStandard.cpp @@ -495,6 +495,44 @@ FFlowDataPinValue_InstancedStruct::FFlowDataPinValue_InstancedStruct(const TArra #endif } +bool FFlowDataPinValue_InstancedStruct::TryConvertValuesToString(FString& OutString) const +{ + const FInstancedStruct DefaultValue; + + OutString = FlowArray::FormatArrayString( + Values, + [&DefaultValue](const FInstancedStruct& InstancedStruct) + { + FString ExportedString; + + constexpr UObject* ParentObject = nullptr; + constexpr UObject* ExportRootScope = nullptr; + + const bool bExported = InstancedStruct.ExportTextItem( + ExportedString, + DefaultValue, + ParentObject, + PPF_None, + ExportRootScope); + + if (!bExported) + { + // Fallback: just show the contained struct type name (or None) + if (const UScriptStruct* ScriptStruct = InstancedStruct.GetScriptStruct()) + { + return ScriptStruct->GetName(); + } + + return FString(); + } + + return ExportedString; + }, + StringArraySeparator); + + return true; +} + //====================================================================== // Object //====================================================================== diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index ee470558d..bd16210e7 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -26,7 +26,7 @@ class UFlowAssetParams; #if !UE_BUILD_SHIPPING DECLARE_DELEGATE(FFlowGraphEvent); -DECLARE_DELEGATE_TwoParams(FFlowSignalEvent, const UFlowNode* /*Node*/, const FName& /*PinName*/); +DECLARE_DELEGATE_TwoParams(FFlowSignalEvent, UFlowNode* /*FlowNode*/, const FName& /*PinName*/); #endif /** @@ -358,6 +358,10 @@ class FLOW_API UFlowAsset : public UObject // Get Flow Asset instance created by the given SubGraph node TWeakObjectPtr GetFlowInstance(UFlowNode_SubGraph* SubGraphNode) const; + // Public trigger input signature for the FFlowExecutionGate mechanism in the Flow Debugger + FORCEINLINE void TriggerDeferredInputFromDebugger(const FGuid& NodeGuid, const FName& PinName, const FConnectedPin& FromPin) + { TriggerInput(NodeGuid, PinName, FromPin); } + protected: void TriggerCustomInput_FromSubGraph(UFlowNode_SubGraph* Node, const FName& EventName) const; void TriggerCustomOutput(const FName& EventName); diff --git a/Source/Flow/Public/Interfaces/FlowExecutionGate.h b/Source/Flow/Public/Interfaces/FlowExecutionGate.h new file mode 100644 index 000000000..8e1abdf66 --- /dev/null +++ b/Source/Flow/Public/Interfaces/FlowExecutionGate.h @@ -0,0 +1,45 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "CoreMinimal.h" + +class UFlowAsset; + +/** + * Implemented by a debugger/runtime system (in another module) that can halt Flow execution. + * Flow runtime queries this through FFlowExecutionGate without depending on the debugger module. + */ +class FLOW_API IFlowExecutionGate +{ +public: + virtual ~IFlowExecutionGate() = default; + + /** Return true when Flow execution should be halted globally. */ + virtual bool IsFlowExecutionHalted() const = 0; +}; + +/** + * Global registry + minimal deferred-execution queue for Flow runtime. + */ +class FLOW_API FFlowExecutionGate +{ +public: + static void SetGate(IFlowExecutionGate* InGate); + static IFlowExecutionGate* GetGate(); + + /** True if a gate exists and it currently wants Flow execution halted. */ + static bool IsHalted(); + + /** If halted, queues the trigger for later. Returns true if queued (caller should early-out). */ + static bool EnqueueDeferredTriggerInput(UFlowAsset* FlowAssetInstance, const FGuid& NodeGuid, const FName& PinName, const struct FConnectedPin& FromPin); + + /** + * Flushes queued trigger inputs (FIFO). + * Safe to call even if nothing is queued. + */ + static void FlushDeferredTriggerInputs(); + +private: + static IFlowExecutionGate* Gate; +}; \ No newline at end of file diff --git a/Source/Flow/Public/Nodes/FlowPin.h b/Source/Flow/Public/Nodes/FlowPin.h index 465a2cf14..9aa88d484 100644 --- a/Source/Flow/Public/Nodes/FlowPin.h +++ b/Source/Flow/Public/Nodes/FlowPin.h @@ -388,7 +388,6 @@ struct FLOW_API FPinRecord FString HumanReadableTime; EFlowPinActivationType ActivationType; - static FString NoActivations; static FString PinActivations; static FString ForcedActivation; static FString PassThroughActivation; diff --git a/Source/Flow/Public/Types/FlowDataPinValuesStandard.h b/Source/Flow/Public/Types/FlowDataPinValuesStandard.h index 245921e5f..ff731e65a 100644 --- a/Source/Flow/Public/Types/FlowDataPinValuesStandard.h +++ b/Source/Flow/Public/Types/FlowDataPinValuesStandard.h @@ -35,8 +35,8 @@ struct FFlowDataPinValue_Bool : public FFlowDataPinValue FLOW_API FFlowDataPinValue_Bool(ValueType InValue); FLOW_API FFlowDataPinValue_Bool(const TArray& InValues); - virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } - virtual bool TryConvertValuesToString(FString& OutString) const override; + FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; }; //====================================================================== @@ -58,8 +58,8 @@ struct FFlowDataPinValue_Int : public FFlowDataPinValue FLOW_API FFlowDataPinValue_Int(ValueType InValue); FLOW_API FFlowDataPinValue_Int(const TArray& InValues); - virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } - virtual bool TryConvertValuesToString(FString& OutString) const override; + FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; }; //====================================================================== @@ -81,8 +81,8 @@ struct FFlowDataPinValue_Int64 : public FFlowDataPinValue FLOW_API FFlowDataPinValue_Int64(ValueType InValue); FLOW_API FFlowDataPinValue_Int64(const TArray& InValues); - virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } - virtual bool TryConvertValuesToString(FString& OutString) const override; + FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; }; //====================================================================== @@ -104,8 +104,8 @@ struct FFlowDataPinValue_Float : public FFlowDataPinValue FLOW_API FFlowDataPinValue_Float(ValueType InValue); FLOW_API FFlowDataPinValue_Float(const TArray& InValues); - virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } - virtual bool TryConvertValuesToString(FString& OutString) const override; + FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; }; //====================================================================== @@ -127,8 +127,8 @@ struct FFlowDataPinValue_Double : public FFlowDataPinValue FLOW_API FFlowDataPinValue_Double(ValueType InValue); FLOW_API FFlowDataPinValue_Double(const TArray& InValues); - virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } - virtual bool TryConvertValuesToString(FString& OutString) const override; + FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; }; //====================================================================== @@ -150,8 +150,8 @@ struct FFlowDataPinValue_Name : public FFlowDataPinValue FLOW_API FFlowDataPinValue_Name(const ValueType& InValue); FLOW_API FFlowDataPinValue_Name(const TArray& InValues); - virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } - virtual bool TryConvertValuesToString(FString& OutString) const override; + FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; }; //====================================================================== @@ -173,8 +173,8 @@ struct FFlowDataPinValue_String : public FFlowDataPinValue FLOW_API FFlowDataPinValue_String(const ValueType& InValue); FLOW_API FFlowDataPinValue_String(const TArray& InValues); - virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } - virtual bool TryConvertValuesToString(FString& OutString) const override; + FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; }; //====================================================================== @@ -196,8 +196,8 @@ struct FFlowDataPinValue_Text : public FFlowDataPinValue FLOW_API FFlowDataPinValue_Text(const ValueType& InValue); FLOW_API FFlowDataPinValue_Text(const TArray& InValues); - virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } - virtual bool TryConvertValuesToString(FString& OutString) const override; + FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; }; //====================================================================== @@ -235,9 +235,9 @@ struct FFlowDataPinValue_Enum : public FFlowDataPinValue FLOW_API void OnEnumNameChanged(); #endif - virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } virtual UField* GetFieldType() const override; - virtual bool TryConvertValuesToString(FString& OutString) const override; + FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; // Helper templates template @@ -317,8 +317,8 @@ struct FFlowDataPinValue_Vector : public FFlowDataPinValue FLOW_API FFlowDataPinValue_Vector(const ValueType& InValue); FLOW_API FFlowDataPinValue_Vector(const TArray& InValues); - virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } - virtual bool TryConvertValuesToString(FString& OutString) const override; + FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; }; //====================================================================== @@ -340,8 +340,8 @@ struct FFlowDataPinValue_Rotator : public FFlowDataPinValue FLOW_API FFlowDataPinValue_Rotator(const ValueType& InValue); FLOW_API FFlowDataPinValue_Rotator(const TArray& InValues); - virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } - virtual bool TryConvertValuesToString(FString& OutString) const override; + FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; }; //====================================================================== @@ -363,8 +363,8 @@ struct FFlowDataPinValue_Transform : public FFlowDataPinValue FLOW_API FFlowDataPinValue_Transform(const ValueType& InValue); FLOW_API FFlowDataPinValue_Transform(const TArray& InValues); - virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } - virtual bool TryConvertValuesToString(FString& OutString) const override; + FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; }; //====================================================================== @@ -386,8 +386,8 @@ struct FFlowDataPinValue_GameplayTag : public FFlowDataPinValue FLOW_API FFlowDataPinValue_GameplayTag(const ValueType& InValue); FLOW_API FFlowDataPinValue_GameplayTag(const TArray& InValues); - virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } - virtual bool TryConvertValuesToString(FString& OutString) const override; + FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; }; //====================================================================== @@ -411,8 +411,8 @@ struct FFlowDataPinValue_GameplayTagContainer : public FFlowDataPinValue FLOW_API FFlowDataPinValue_GameplayTagContainer(const TArray& InValues); FLOW_API FFlowDataPinValue_GameplayTagContainer(const TArray& InValues); - virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } - virtual bool TryConvertValuesToString(FString& OutString) const override; + FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; }; //====================================================================== @@ -434,7 +434,8 @@ struct FFlowDataPinValue_InstancedStruct : public FFlowDataPinValue FLOW_API FFlowDataPinValue_InstancedStruct(const ValueType& InValue); FLOW_API FFlowDataPinValue_InstancedStruct(const TArray& InValues); - virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; }; //====================================================================== @@ -464,8 +465,8 @@ struct FFlowDataPinValue_Object : public FFlowDataPinValue FLOW_API FFlowDataPinValue_Object(AActor* InActor, UClass* InClassFilter = nullptr /* nullptr here defaults to AActor::StaticClass() */ ); FLOW_API FFlowDataPinValue_Object(const TArray& InActors, UClass* InClassFilter = nullptr /* nullptr here defaults to AActor::StaticClass() */); - virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } - virtual bool TryConvertValuesToString(FString& OutString) const override; + FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; }; //====================================================================== @@ -494,6 +495,6 @@ struct FFlowDataPinValue_Class : public FFlowDataPinValue FLOW_API FFlowDataPinValue_Class(const UClass* InClass, UClass* InClassFilter = UObject::StaticClass()); FLOW_API FFlowDataPinValue_Class(const TArray& InClasses, UClass* InClassFilter = UObject::StaticClass()); - virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } - virtual bool TryConvertValuesToString(FString& OutString) const override; + FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } + FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; }; \ No newline at end of file diff --git a/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp b/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp index 6711c5c81..e014fe8e1 100644 --- a/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp +++ b/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp @@ -16,12 +16,28 @@ #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowDebuggerSubsystem) UFlowDebuggerSubsystem::UFlowDebuggerSubsystem() - : bPausedAtFlowBreakpoint(false) { UFlowSubsystem::OnInstancedTemplateAdded.BindUObject(this, &ThisClass::OnInstancedTemplateAdded); UFlowSubsystem::OnInstancedTemplateRemoved.BindUObject(this, &ThisClass::OnInstancedTemplateRemoved); } +void UFlowDebuggerSubsystem::Initialize(FSubsystemCollectionBase& Collection) +{ + Super::Initialize(Collection); + + FFlowExecutionGate::SetGate(this); +} + +void UFlowDebuggerSubsystem::Deinitialize() +{ + if (FFlowExecutionGate::GetGate() == this) + { + FFlowExecutionGate::SetGate(nullptr); + } + + Super::Deinitialize(); +} + bool UFlowDebuggerSubsystem::ShouldCreateSubsystem(UObject* Outer) const { // Only create an instance if there is no override implementation defined elsewhere @@ -37,26 +53,29 @@ bool UFlowDebuggerSubsystem::ShouldCreateSubsystem(UObject* Outer) const void UFlowDebuggerSubsystem::OnInstancedTemplateAdded(UFlowAsset* AssetTemplate) { + check(IsValid(AssetTemplate)); + AssetTemplate->OnPinTriggered.BindUObject(this, &ThisClass::OnPinTriggered); } -void UFlowDebuggerSubsystem::OnInstancedTemplateRemoved(UFlowAsset* AssetTemplate) const +void UFlowDebuggerSubsystem::OnInstancedTemplateRemoved(UFlowAsset* AssetTemplate) { + check(IsValid(AssetTemplate)); + AssetTemplate->OnPinTriggered.Unbind(); + + OnDebuggerFlowAssetTemplateRemoved.Broadcast(*AssetTemplate); } -void UFlowDebuggerSubsystem::OnPinTriggered(const UFlowNode* Node, const FName& PinName) +void UFlowDebuggerSubsystem::OnPinTriggered(UFlowNode* FlowNode, const FName& PinName) { - if (bPausedAtFlowBreakpoint) + if (FindBreakpoint(FlowNode->NodeGuid, PinName)) { - return; + MarkAsHit(FlowNode, PinName); } - if (!TryMarkAsHit(Node, PinName)) - { - // Node breakpoints waits on any pin triggered, but check it only if there is no hit pin breakpoint - TryMarkAsHit(Node); - } + // Node breakpoints waits on any pin triggered + MarkAsHit(FlowNode); } void UFlowDebuggerSubsystem::AddBreakpoint(const FGuid& NodeGuid) @@ -158,15 +177,23 @@ void UFlowDebuggerSubsystem::RemoveObsoletePinBreakpoints(const UEdGraphNode* No PinNames.Emplace(Pin->PinName); } - for (TPair& PinBreakpoint : NodeBreakpoint->PinBreakpoints) + TArray PinsToRemove; + PinsToRemove.Reserve(NodeBreakpoint->PinBreakpoints.Num()); + + for (const TPair& PinBreakpoint : NodeBreakpoint->PinBreakpoints) { if (!PinNames.Contains(PinBreakpoint.Key)) { - NodeBreakpoint->PinBreakpoints.Remove(PinBreakpoint.Key); - bAnythingRemoved = true; + PinsToRemove.Add(PinBreakpoint.Key); } } + for (const FName& PinName : PinsToRemove) + { + NodeBreakpoint->PinBreakpoints.Remove(PinName); + bAnythingRemoved = true; + } + if (NodeBreakpoint->IsEmpty()) { Settings->NodeBreakpoints.Remove(Node->NodeGuid); @@ -298,21 +325,44 @@ bool UFlowDebuggerSubsystem::IsBreakpointEnabled(const FGuid& NodeGuid, const FN return false; } -bool UFlowDebuggerSubsystem::HasAnyBreakpointsEnabled(const TWeakObjectPtr FlowAsset) +bool UFlowDebuggerSubsystem::HasAnyBreakpointsEnabled(const TWeakObjectPtr& FlowAsset) { - UFlowDebuggerSettings* Settings = GetMutableDefault(); - for (const TPair& Node : FlowAsset->GetNodes()) + return HasAnyBreakpointsMatching(FlowAsset, true); +} + +bool UFlowDebuggerSubsystem::HasAnyBreakpointsDisabled(const TWeakObjectPtr& FlowAsset) +{ + return HasAnyBreakpointsMatching(FlowAsset, false); +} + +bool UFlowDebuggerSubsystem::HasAnyBreakpointsMatching(const TWeakObjectPtr& FlowAsset, bool bDesiresEnabled) +{ + if (!FlowAsset.IsValid()) { - if (FNodeBreakpoint* NodeBreakpoint = Settings->NodeBreakpoints.Find(Node.Key)) + return false; + } + + const UFlowDebuggerSettings* Settings = GetDefault(); + if (!Settings) + { + return false; + } + + for (const TPair& NodePair : FlowAsset->GetNodes()) + { + if (const FNodeBreakpoint* NodeBreakpoint = Settings->NodeBreakpoints.Find(NodePair.Key)) { - if (NodeBreakpoint->Breakpoint.IsActive() && NodeBreakpoint->Breakpoint.IsEnabled()) + // Node-level breakpoint must be active to count (matches original behavior) + if (NodeBreakpoint->Breakpoint.IsActive() && + (NodeBreakpoint->Breakpoint.IsEnabled() == bDesiresEnabled)) { return true; } - for (auto& [Name, PinBreakpoint] : NodeBreakpoint->PinBreakpoints) + // Pin-level breakpoints + for (const auto& PinPair : NodeBreakpoint->PinBreakpoints) { - if (PinBreakpoint.IsEnabled()) + if (PinPair.Value.IsEnabled() == bDesiresEnabled) { return true; } @@ -323,111 +373,178 @@ bool UFlowDebuggerSubsystem::HasAnyBreakpointsEnabled(const TWeakObjectPtr FlowAsset) +void UFlowDebuggerSubsystem::RequestHaltFlowExecution(const UFlowNode* Node) { - UFlowDebuggerSettings* Settings = GetMutableDefault(); - for (const TPair& Node : FlowAsset->GetNodes()) + bHaltFlowExecution = true; + HaltedOnFlowAssetInstance = Node->GetFlowAsset(); + HaltedOnNodeGuid = Node->NodeGuid; +} + +void UFlowDebuggerSubsystem::ClearHaltFlowExecution() +{ + bHaltFlowExecution = false; + HaltedOnFlowAssetInstance.Reset(); + HaltedOnNodeGuid.Invalidate(); +} + +void UFlowDebuggerSubsystem::ClearLastHitBreakpoint() +{ + if (!LastHitNodeGuid.IsValid()) { - if (FNodeBreakpoint* NodeBreakpoint = Settings->NodeBreakpoints.Find(Node.Key)) - { - if (NodeBreakpoint->Breakpoint.IsActive() && !NodeBreakpoint->Breakpoint.IsEnabled()) - { - return true; - } + return; + } - for (auto& [Name, PinBreakpoint] : NodeBreakpoint->PinBreakpoints) - { - if (!PinBreakpoint.IsEnabled()) - { - return true; - } - } + // Pin breakpoint "hit" state lives in the PinBreakpoints map, node breakpoint "hit" lives on NodeBreakpoint.Breakpoint. + if (!LastHitPinName.IsNone()) + { + if (FFlowBreakpoint* PinBreakpoint = FindBreakpoint(LastHitNodeGuid, LastHitPinName)) + { + PinBreakpoint->MarkAsHit(false); + } + } + else + { + if (FFlowBreakpoint* NodeBreakpoint = FindBreakpoint(LastHitNodeGuid)) + { + NodeBreakpoint->MarkAsHit(false); } } - return false; + LastHitNodeGuid.Invalidate(); + LastHitPinName = NAME_None; } -bool UFlowDebuggerSubsystem::TryMarkAsHit(const UFlowNode* Node) +void UFlowDebuggerSubsystem::MarkAsHit(const UFlowNode* FlowNode) { - if (FFlowBreakpoint* NodeBreakpoint = FindBreakpoint(Node->NodeGuid)) + if (FFlowBreakpoint* NodeBreakpoint = FindBreakpoint(FlowNode->NodeGuid)) { if (NodeBreakpoint->IsEnabled()) { + // Ensure only one breakpoint location is "hit" at a time. + ClearLastHitBreakpoint(); + NodeBreakpoint->MarkAsHit(true); - PauseSession(Node); - return true; + + LastHitNodeGuid = FlowNode->NodeGuid; + LastHitPinName = NAME_None; + + RequestHaltFlowExecution(FlowNode); + + OnDebuggerBreakpointHit.Broadcast(FlowNode); + + PauseSession(*FlowNode); } } - - return false; } -bool UFlowDebuggerSubsystem::TryMarkAsHit(const UFlowNode* Node, const FName& PinName) +void UFlowDebuggerSubsystem::MarkAsHit(const UFlowNode* FlowNode, const FName& PinName) { - if (FFlowBreakpoint* PinBreakpoint = FindBreakpoint(Node->NodeGuid, PinName)) + if (FFlowBreakpoint* PinBreakpoint = FindBreakpoint(FlowNode->NodeGuid, PinName)) { if (PinBreakpoint->IsEnabled()) { + // Ensure only one breakpoint location is "hit" at a time. + ClearLastHitBreakpoint(); + PinBreakpoint->MarkAsHit(true); - PauseSession(Node); - return true; + + LastHitNodeGuid = FlowNode->NodeGuid; + LastHitPinName = PinName; + + RequestHaltFlowExecution(FlowNode); + + OnDebuggerBreakpointHit.Broadcast(FlowNode); + + PauseSession(*FlowNode); } } - - return false; } -void UFlowDebuggerSubsystem::PauseSession(const UFlowNode* Node) +void UFlowDebuggerSubsystem::PauseSession(const UFlowNode& FlowNode) { - SetPause(true); + SetPause(FlowNode, true); } -void UFlowDebuggerSubsystem::ResumeSession() +void UFlowDebuggerSubsystem::ResumeSession(const UFlowNode& FlowNode) { - SetPause(false); + SetPause(FlowNode, false); } -void UFlowDebuggerSubsystem::SetPause(const bool bPause) +void UFlowDebuggerSubsystem::SetPause(const UFlowNode& FlowNode, const bool bPause) { - // experimental implementation, untested, shows intent for future development - // here be dragons: same as APlayerController::SetPause, but we allow debugger to pause on clients - if (const UWorld* World = GEngine->GetWorldFromContextObject(this, EGetWorldErrorMode::LogAndReturnNull)) + // Default bWasPaused to opposite of bPause + // (which we hope to get a better measure if we can get access to what we need) + bool bWasPaused = !bPause; + + AGameModeBase* GameMode = nullptr; + APlayerController* PlayerController = nullptr; + + const UFlowAsset* FlowAssetInstance = FlowNode.GetFlowAsset(); + const UWorld* World = FlowAssetInstance->GetWorld(); + if (IsValid(World)) + { + GameMode = World->GetAuthGameMode(); + + if (IsValid(GameMode)) + { + bWasPaused = GameMode->IsPaused(); + } + + const UGameInstance* GameInstance = World->GetGameInstance(); + if (IsValid(GameInstance)) + { + PlayerController = GameInstance->GetFirstLocalPlayerController(); + } + } + + if (bWasPaused != bPause) { - if (const UGameInstance* GameInstance = World->GetGameInstance()) + if (bPause) { - if (APlayerController* PlayerController = GameInstance->GetFirstLocalPlayerController()) + // Pausing (from an unpaused state) + + if (IsValid(PlayerController)) { - if (AGameModeBase* const GameMode = GetWorld()->GetAuthGameMode()) + if (IsValid(GameMode)) + { + GameMode->SetPause(PlayerController); + } + + if (AWorldSettings* WorldSettings = PlayerController->GetWorldSettings()) { - const bool bCurrentPauseState = PlayerController->IsPaused(); - if (bPause && !bCurrentPauseState) - { - GameMode->SetPause(PlayerController); - - if (AWorldSettings* WorldSettings = PlayerController->GetWorldSettings()) - { - WorldSettings->ForceNetUpdate(); - } - } - else if (!bPause && bCurrentPauseState) - { - if (GameMode->ClearPause()) - { - ClearHitBreakpoints(); - } - } + WorldSettings->ForceNetUpdate(); } } + + // Broadcast the Pause event + OnDebuggerPaused.Broadcast(*FlowAssetInstance); + } + else + { + // Resuming (from a paused state) + + ClearHaltFlowExecution(); + + // Replay any Flow propagation that was deferred while execution was halted. + FFlowExecutionGate::FlushDeferredTriggerInputs(); + + // Intentionally do NOT clear hit flags here. The editor-specific resume path will clear the last-hit + // breakpoint safely (without racing against immediate breakpoint hits during flush). + if (IsValid(GameMode)) + { + (void) GameMode->ClearPause(); + } + + // Broadcast the Resume event + OnDebuggerResumed.Broadcast(*FlowAssetInstance); } } } void UFlowDebuggerSubsystem::ClearHitBreakpoints() { - bPausedAtFlowBreakpoint = false; - UFlowDebuggerSettings* Settings = GetMutableDefault(); + for (TPair& NodeBreakpoint : Settings->NodeBreakpoints) { NodeBreakpoint.Value.Breakpoint.MarkAsHit(false); @@ -437,6 +554,9 @@ void UFlowDebuggerSubsystem::ClearHitBreakpoints() PinBreakpoint.Value.MarkAsHit(false); } } + + LastHitNodeGuid.Invalidate(); + LastHitPinName = NAME_None; } bool UFlowDebuggerSubsystem::IsBreakpointHit(const FGuid& NodeGuid) @@ -463,4 +583,4 @@ void UFlowDebuggerSubsystem::SaveSettings() { UFlowDebuggerSettings* Settings = GetMutableDefault(); Settings->SaveConfig(); -} +} \ No newline at end of file diff --git a/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h b/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h index 705b49d28..bc4f42219 100644 --- a/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h +++ b/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h @@ -5,6 +5,8 @@ #include "Subsystems/EngineSubsystem.h" #include "Debugger/FlowDebuggerTypes.h" +#include "Interfaces/FlowExecutionGate.h" + #include "FlowDebuggerSubsystem.generated.h" class UEdGraphNode; @@ -12,30 +14,37 @@ class UEdGraphNode; class UFlowAsset; class UFlowNode; +DECLARE_MULTICAST_DELEGATE_OneParam(FFlowAssetDebuggerEvent, const UFlowAsset& /*FlowAsset*/); +DECLARE_MULTICAST_DELEGATE_OneParam(FFlowAssetDebuggerBreakpointHitEvent, const UFlowNode* /*FlowNode*/); + /** - * Persistent subsystem supporting Flow Graph debugging. - * It might be utilized to use cook-specific graph debugger. - */ +* Persistent subsystem supporting Flow Graph debugging. +* It might be utilized to use cook-specific graph debugger. +*/ UCLASS() -class FLOWDEBUGGER_API UFlowDebuggerSubsystem : public UEngineSubsystem +class FLOWDEBUGGER_API UFlowDebuggerSubsystem : public UEngineSubsystem, public IFlowExecutionGate { GENERATED_BODY() public: UFlowDebuggerSubsystem(); - virtual bool ShouldCreateSubsystem(UObject* Outer) const override; + virtual void Initialize(FSubsystemCollectionBase& Collection) override; + virtual void Deinitialize() override; -protected: - bool bPausedAtFlowBreakpoint; + virtual bool ShouldCreateSubsystem(UObject* Outer) const override; protected: virtual void OnInstancedTemplateAdded(UFlowAsset* AssetTemplate); - virtual void OnInstancedTemplateRemoved(UFlowAsset* AssetTemplate) const; + virtual void OnInstancedTemplateRemoved(UFlowAsset* AssetTemplate); - virtual void OnPinTriggered(const UFlowNode* Node, const FName& PinName); + virtual void OnPinTriggered(UFlowNode* FlowNode, const FName& PinName); public: + // IFlowExecutionGate + virtual bool IsFlowExecutionHalted() const override { return bHaltFlowExecution; } + // -- + virtual void AddBreakpoint(const FGuid& NodeGuid); virtual void AddBreakpoint(const FGuid& NodeGuid, const FName& PinName); @@ -62,24 +71,50 @@ class FLOWDEBUGGER_API UFlowDebuggerSubsystem : public UEngineSubsystem virtual bool IsBreakpointEnabled(const FGuid& NodeGuid); virtual bool IsBreakpointEnabled(const FGuid& NodeGuid, const FName& PinName); - static bool HasAnyBreakpointsEnabled(const TWeakObjectPtr FlowAsset); - static bool HasAnyBreakpointsDisabled(const TWeakObjectPtr FlowAsset); + static bool HasAnyBreakpointsEnabled(const TWeakObjectPtr& FlowAsset); + static bool HasAnyBreakpointsDisabled(const TWeakObjectPtr& FlowAsset); + static bool HasAnyBreakpointsMatching(const TWeakObjectPtr& FlowAsset, bool bDesiresEnabled); protected: - virtual bool TryMarkAsHit(const UFlowNode* Node); - virtual bool TryMarkAsHit(const UFlowNode* Node, const FName& PinName); + virtual void MarkAsHit(const UFlowNode* FlowNode); + virtual void MarkAsHit(const UFlowNode* FlowNode, const FName& PinName); - virtual void PauseSession(const UFlowNode* Node); - virtual void ResumeSession(); - void SetPause(const bool bPause); + virtual void PauseSession(const UFlowNode& FlowNode); + virtual void ResumeSession(const UFlowNode& FlowNode); + void SetPause(const UFlowNode& FlowNode, const bool bPause); + /** + * Clears the "currently hit" breakpoint only (node or pin). + * This avoids races where blanket-clearing all hit flags can erase a newly-hit breakpoint during resume/flush. + */ + void ClearLastHitBreakpoint(); + + /** Clears hit state for all breakpoints. Prefer ClearLastHitBreakpoint() for resume/step logic. */ virtual void ClearHitBreakpoints(); +protected: + void RequestHaltFlowExecution(const UFlowNode* Node); + void ClearHaltFlowExecution(); + public: virtual bool IsBreakpointHit(const FGuid& NodeGuid); virtual bool IsBreakpointHit(const FGuid& NodeGuid, const FName& PinName); + // Delegates for debugger events (broadcast when pausing, resuming, or hitting breakpoints) + FFlowAssetDebuggerEvent OnDebuggerPaused; + FFlowAssetDebuggerEvent OnDebuggerResumed; + FFlowAssetDebuggerBreakpointHitEvent OnDebuggerBreakpointHit; + FFlowAssetDebuggerEvent OnDebuggerFlowAssetTemplateRemoved; + private: + bool bHaltFlowExecution = false; + TWeakObjectPtr HaltedOnFlowAssetInstance; + FGuid HaltedOnNodeGuid; + + // Track the single breakpoint location that is currently "hit" (node or pin). + FGuid LastHitNodeGuid; + FName LastHitPinName; + /** Saves any modifications made to breakpoints */ virtual void SaveSettings(); -}; +}; \ No newline at end of file diff --git a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp index 7c0d96fc1..860aeabe4 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp @@ -11,6 +11,7 @@ #include "FlowAsset.h" #include "Nodes/Graph/FlowNode_SubGraph.h" +#include "Brushes/SlateRoundedBoxBrush.h" #include "Kismet2/DebuggerCommands.h" #include "Misc/Attribute.h" #include "Misc/MessageDialog.h" diff --git a/Source/FlowEditor/Private/Asset/FlowDebugEditorSubsystem.cpp b/Source/FlowEditor/Private/Asset/FlowDebugEditorSubsystem.cpp index 63b68d048..90cac2020 100644 --- a/Source/FlowEditor/Private/Asset/FlowDebugEditorSubsystem.cpp +++ b/Source/FlowEditor/Private/Asset/FlowDebugEditorSubsystem.cpp @@ -3,12 +3,18 @@ #include "Asset/FlowDebugEditorSubsystem.h" #include "Asset/FlowAssetEditor.h" #include "Asset/FlowMessageLogListing.h" +#include "Graph/FlowGraph.h" +#include "Graph/FlowGraphEditor.h" #include "Graph/FlowGraphUtils.h" +#include "Graph/Nodes/FlowGraphNode.h" +#include "Interfaces/FlowExecutionGate.h" +#include "FlowAsset.h" #include "Editor/UnrealEdEngine.h" #include "Engine/Engine.h" #include "Engine/World.h" #include "Framework/Notifications/NotificationManager.h" +#include "Subsystems/AssetEditorSubsystem.h" #include "Templates/Function.h" #include "UnrealEdGlobals.h" #include "Widgets/Notifications/SNotificationList.h" @@ -22,6 +28,8 @@ UFlowDebugEditorSubsystem::UFlowDebugEditorSubsystem() FEditorDelegates::BeginPIE.AddUObject(this, &ThisClass::OnBeginPIE); FEditorDelegates::ResumePIE.AddUObject(this, &ThisClass::OnResumePIE); FEditorDelegates::EndPIE.AddUObject(this, &ThisClass::OnEndPIE); + + OnDebuggerBreakpointHit.AddUObject(this, &ThisClass::OnBreakpointHit); } void UFlowDebugEditorSubsystem::OnInstancedTemplateAdded(UFlowAsset* AssetTemplate) @@ -35,7 +43,7 @@ void UFlowDebugEditorSubsystem::OnInstancedTemplateAdded(UFlowAsset* AssetTempla } } -void UFlowDebugEditorSubsystem::OnInstancedTemplateRemoved(UFlowAsset* AssetTemplate) const +void UFlowDebugEditorSubsystem::OnInstancedTemplateRemoved(UFlowAsset* AssetTemplate) { AssetTemplate->OnRuntimeMessageAdded().RemoveAll(this); @@ -54,19 +62,34 @@ void UFlowDebugEditorSubsystem::OnRuntimeMessageAdded(const UFlowAsset* AssetTem void UFlowDebugEditorSubsystem::OnBeginPIE(const bool bIsSimulating) { - // clear all logs from a previous session + // Clear all logs from a previous session RuntimeLogs.Empty(); + + // Clear any stale "hit" state from previous run + ClearHitBreakpoints(); } void UFlowDebugEditorSubsystem::OnResumePIE(const bool bIsSimulating) { - ClearHitBreakpoints(); + // Editor-level resume event (also used by Advance Single Frame). + // This does not necessarily flow through AGameModeBase::ClearPause(), so we must unhalt Flow here. + // + // Clear only the last-hit breakpoint to return to enabled/disabled visuals without racing against + // a newly hit breakpoint during FlushDeferredTriggerInputs(). + ClearHaltFlowExecution(); + ClearLastHitBreakpoint(); + + FFlowExecutionGate::FlushDeferredTriggerInputs(); } void UFlowDebugEditorSubsystem::OnEndPIE(const bool bIsSimulating) { + // Ensure we don't carry over a halted state between PIE sessions. ClearHitBreakpoints(); + ClearHaltFlowExecution(); + FFlowExecutionGate::FlushDeferredTriggerInputs(); + for (const TPair, TSharedPtr>& Log : RuntimeLogs) { if (Log.Key.IsValid() && Log.Value->NumMessages(EMessageSeverity::Warning) > 0) @@ -93,35 +116,84 @@ void UFlowDebugEditorSubsystem::OnEndPIE(const bool bIsSimulating) } } -void UFlowDebugEditorSubsystem::PauseSession(const UFlowNode* Node) +void UFlowDebugEditorSubsystem::PauseSession(const UFlowNode& FlowNode) +{ + Super::PauseSession(FlowNode); + + constexpr bool bShouldBePaused = true; + const bool bWasPaused = GUnrealEd->SetPIEWorldsPaused(bShouldBePaused); + if (!bWasPaused) + { + GUnrealEd->PlaySessionPaused(); + } +} + +void UFlowDebugEditorSubsystem::ResumeSession(const UFlowNode& FlowNode) { - if (GEditor->ShouldEndPlayMap()) + Super::ResumeSession(FlowNode); + + constexpr bool bShouldBePaused = false; + const bool bWasPaused = GUnrealEd->SetPIEWorldsPaused(bShouldBePaused); + if (bWasPaused) + { + GUnrealEd->PlaySessionResumed(); + } +} + +void UFlowDebugEditorSubsystem::OnBreakpointHit(const UFlowNode* FlowNode) const +{ + UFlowAsset* TemplateAsset = const_cast(FlowNode->GetFlowAsset()->GetTemplateAsset()); + if (!IsValid(TemplateAsset)) { return; } - if (GUnrealEd->SetPIEWorldsPaused(true)) + UAssetEditorSubsystem* AssetEditorSubsystem = GEditor ? GEditor->GetEditorSubsystem() : nullptr; + if (!AssetEditorSubsystem) { - bPausedAtFlowBreakpoint = true; + return; + } - const UFlowAsset* HitInstance = Node->GetFlowAsset(); - if (ensure(HitInstance)) - { - UFlowAsset* AssetTemplate = HitInstance->GetTemplateAsset(); - AssetTemplate->SetInspectedInstance(HitInstance); + if (!AssetEditorSubsystem->OpenEditorForAsset(TemplateAsset)) + { + return; + } - UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem(); - if (AssetEditorSubsystem->OpenEditorForAsset(AssetTemplate)) - { - if (const TSharedPtr FlowAssetEditor = FFlowGraphUtils::GetFlowAssetEditor(AssetTemplate)) - { - FlowAssetEditor->JumpToNode(Node->GetGraphNode()); - } - } + TemplateAsset->SetInspectedInstance(FlowNode->GetFlowAsset()); + + UFlowGraph* FlowGraph = Cast(TemplateAsset->GetGraph()); + if (!IsValid(FlowGraph)) + { + return; + } + + // NOTE: This may be redundant call, but it ensures Slate re-queries breakpoint hit state and updates node overlays immediately. + FlowGraph->NotifyGraphChanged(); + + UEdGraphNode* NodeToFocus = nullptr; + for (UEdGraphNode* Node : FlowGraph->Nodes) + { + UFlowGraphNode* FlowGraphNode = Cast(Node); + if (IsValid(FlowGraphNode) && FlowGraphNode->NodeGuid == FlowNode->NodeGuid) + { + NodeToFocus = FlowGraphNode; + break; } + } - GUnrealEd->PlaySessionPaused(); + if (!NodeToFocus) + { + return; + } + + const TSharedPtr GraphEditor = FFlowGraphUtils::GetFlowGraphEditor(FlowGraph); + if (GraphEditor.IsValid()) + { + constexpr bool bRequestRename = false; + constexpr bool bSelectNode = true; + + GraphEditor->JumpToNode(NodeToFocus, bRequestRename, bSelectNode); } } -#undef LOCTEXT_NAMESPACE +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index 4d14f5b57..0e9a97c82 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -19,6 +19,9 @@ #include "LevelEditor.h" #include "Modules/ModuleManager.h" #include "ScopedTransaction.h" +#include "ToolMenu.h" +#include "ToolMenuDelegates.h" +#include "ToolMenus.h" #include "UnrealEdGlobals.h" #include "Widgets/Docking/SDockTab.h" #include "Algo/AnyOf.h" @@ -54,6 +57,69 @@ void SFlowGraphEditor::Construct(const FArguments& InArgs, const TSharedPtrPinType.PinCategory)) + { + return false; + } + + // - If the owning node is not a UFlowGraphNode, allow it. + // - If it is a UFlowGraphNode, require it to allow breakpoints. + const UEdGraphNode* EdNode = Pin->GetOwningNode(); + if (!EdNode) + { + return false; + } + + const UFlowGraphNode* FlowNode = Cast(EdNode); + if (FlowNode && !FlowNode->CanPlaceBreakpoints()) + { + return false; + } + + OutNodeGuid = EdNode->NodeGuid; + OutPinName = Pin->PinName; + return true; +} + +const FFlowBreakpoint* SFlowGraphEditor::FindPinBreakpoint(UFlowDebuggerSubsystem* InDebuggerSubsystem, const UEdGraphPin* Pin) +{ + if (!InDebuggerSubsystem) + { + return nullptr; + } + + FGuid NodeGuid; + FName PinName; + if (!GetValidExecBreakpointPinContext(Pin, NodeGuid, PinName)) + { + return nullptr; + } + + return InDebuggerSubsystem->FindBreakpoint(NodeGuid, PinName); +} + +bool SFlowGraphEditor::HasPinBreakpoint(UFlowDebuggerSubsystem* InDebuggerSubsystem, const UEdGraphPin* Pin) +{ + return FindPinBreakpoint(InDebuggerSubsystem, Pin) != nullptr; +} + +bool SFlowGraphEditor::HasEnabledPinBreakpoint(UFlowDebuggerSubsystem* InDebuggerSubsystem, const UEdGraphPin* Pin) +{ + if (const FFlowBreakpoint* BP = FindPinBreakpoint(InDebuggerSubsystem, Pin)) + { + return BP->IsEnabled(); + } + + return false; +} + void SFlowGraphEditor::BindGraphCommands() { FGraphEditorCommands::Register(); @@ -1148,11 +1214,14 @@ void SFlowGraphEditor::OnAddPinBreakpoint() check(DebuggerSubsystem.IsValid()); if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - const UFlowGraphNode* OwningNode = Cast(Pin->GetOwningNode()); - if (!OwningNode || OwningNode->CanPlaceBreakpoints()) + FGuid NodeGuid; + FName PinName; + if (!GetValidExecBreakpointPinContext(Pin, NodeGuid, PinName)) { - DebuggerSubsystem->AddBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName); + return; } + + DebuggerSubsystem->AddBreakpoint(NodeGuid, PinName); } } @@ -1178,11 +1247,14 @@ bool SFlowGraphEditor::CanAddPinBreakpoint() check(DebuggerSubsystem.IsValid()); if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - const UFlowGraphNode* OwningNode = Cast(Pin->GetOwningNode()); - if (!OwningNode || OwningNode->CanPlaceBreakpoints()) + FGuid NodeGuid; + FName PinName; + if (!GetValidExecBreakpointPinContext(Pin, NodeGuid, PinName)) { - return DebuggerSubsystem->FindBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName) == nullptr; + return false; } + + return DebuggerSubsystem->FindBreakpoint(NodeGuid, PinName) == nullptr; } return false; @@ -1205,11 +1277,14 @@ void SFlowGraphEditor::OnRemovePinBreakpoint() check(DebuggerSubsystem.IsValid()); if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - const UFlowGraphNode* OwningNode = Cast(Pin->GetOwningNode()); - if (!OwningNode || OwningNode->CanPlaceBreakpoints()) + FGuid NodeGuid; + FName PinName; + if (!GetValidExecBreakpointPinContext(Pin, NodeGuid, PinName)) { - DebuggerSubsystem->RemovePinBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName); + return; } + + DebuggerSubsystem->RemovePinBreakpoint(NodeGuid, PinName); } } @@ -1233,16 +1308,7 @@ bool SFlowGraphEditor::CanRemoveBreakpoint() const bool SFlowGraphEditor::CanRemovePinBreakpoint() { check(DebuggerSubsystem.IsValid()); - if (const UEdGraphPin* Pin = GetGraphPinForMenu()) - { - const UFlowGraphNode* OwningNode = Cast(Pin->GetOwningNode()); - if (!OwningNode || OwningNode->CanPlaceBreakpoints()) - { - return DebuggerSubsystem->FindBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName) != nullptr; - } - } - - return false; + return HasPinBreakpoint(DebuggerSubsystem.Get(), GetGraphPinForMenu()); } void SFlowGraphEditor::OnEnableBreakpoint() const @@ -1262,11 +1328,14 @@ void SFlowGraphEditor::OnEnablePinBreakpoint() check(DebuggerSubsystem.IsValid()); if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - const UFlowGraphNode* OwningNode = Cast(Pin->GetOwningNode()); - if (!OwningNode || OwningNode->CanPlaceBreakpoints()) + FGuid NodeGuid; + FName PinName; + if (!GetValidExecBreakpointPinContext(Pin, NodeGuid, PinName)) { - DebuggerSubsystem->SetBreakpointEnabled(Pin->GetOwningNode()->NodeGuid, Pin->PinName, true); + return; } + + DebuggerSubsystem->SetBreakpointEnabled(NodeGuid, PinName, true); } } @@ -1289,17 +1358,8 @@ bool SFlowGraphEditor::CanEnableBreakpoint() const bool SFlowGraphEditor::CanEnablePinBreakpoint() { - if (const UEdGraphPin* Pin = GetGraphPinForMenu()) - { - const UFlowGraphNode* OwningNode = Cast(Pin->GetOwningNode()); - if (!OwningNode || OwningNode->CanPlaceBreakpoints()) - { - const FFlowBreakpoint* Breakpoint = DebuggerSubsystem->FindBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName); - return Breakpoint && !Breakpoint->IsEnabled(); - } - } - - return false; + return HasPinBreakpoint(DebuggerSubsystem.Get(), GetGraphPinForMenu()) + && !HasEnabledPinBreakpoint(DebuggerSubsystem.Get(), GetGraphPinForMenu()); } void SFlowGraphEditor::OnDisableBreakpoint() const @@ -1319,11 +1379,14 @@ void SFlowGraphEditor::OnDisablePinBreakpoint() check(DebuggerSubsystem.IsValid()); if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - const UFlowGraphNode* OwningNode = Cast(Pin->GetOwningNode()); - if (!OwningNode || OwningNode->CanPlaceBreakpoints()) + FGuid NodeGuid; + FName PinName; + if (!GetValidExecBreakpointPinContext(Pin, NodeGuid, PinName)) { - DebuggerSubsystem->SetBreakpointEnabled(Pin->GetOwningNode()->NodeGuid, Pin->PinName, false); + return; } + + DebuggerSubsystem->SetBreakpointEnabled(NodeGuid, PinName, false); } } @@ -1348,17 +1411,7 @@ bool SFlowGraphEditor::CanDisableBreakpoint() const bool SFlowGraphEditor::CanDisablePinBreakpoint() { check(DebuggerSubsystem.IsValid()); - if (const UEdGraphPin* Pin = GetGraphPinForMenu()) - { - const UFlowGraphNode* OwningNode = Cast(Pin->GetOwningNode()); - if (!OwningNode || OwningNode->CanPlaceBreakpoints()) - { - const FFlowBreakpoint* Breakpoint = DebuggerSubsystem->FindBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName); - return Breakpoint && Breakpoint->IsEnabled(); - } - } - - return false; + return HasEnabledPinBreakpoint(DebuggerSubsystem.Get(), GetGraphPinForMenu()); } void SFlowGraphEditor::OnToggleBreakpoint() const @@ -1378,11 +1431,14 @@ void SFlowGraphEditor::OnTogglePinBreakpoint() check(DebuggerSubsystem.IsValid()); if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - const UFlowGraphNode* OwningNode = Cast(Pin->GetOwningNode()); - if (!OwningNode || OwningNode->CanPlaceBreakpoints()) + FGuid NodeGuid; + FName PinName; + if (!GetValidExecBreakpointPinContext(Pin, NodeGuid, PinName)) { - DebuggerSubsystem->ToggleBreakpoint(Pin->GetOwningNode()->NodeGuid, Pin->PinName); + return; } + + DebuggerSubsystem->ToggleBreakpoint(NodeGuid, PinName); } } @@ -1403,11 +1459,9 @@ bool SFlowGraphEditor::CanTogglePinBreakpoint() { if (const UEdGraphPin* Pin = GetGraphPinForMenu()) { - const UFlowGraphNode* OwningNode = Cast(Pin->GetOwningNode()); - if (!OwningNode || OwningNode->CanPlaceBreakpoints()) - { - return true; - } + FGuid NodeGuid; + FName PinName; + return GetValidExecBreakpointPinContext(Pin, NodeGuid, PinName); } return false; diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 9864a54e4..6626c8173 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -15,6 +15,8 @@ #include "Graph/FlowGraphSettings.h" #include "Graph/Widgets/SFlowGraphNode.h" #include "Graph/Widgets/SGraphEditorActionMenuFlow.h" +#include "Interfaces/FlowDataPinValueSupplierInterface.h" +#include "Types/FlowDataPinValue.h" #include "BlueprintNodeHelpers.h" #include "Developer/ToolMenus/Public/ToolMenus.h" @@ -1082,8 +1084,10 @@ void UFlowGraphNode::GetPinHoverText(const UEdGraphPin& Pin, FString& HoverTextO // start with the default hover text (from the pin's tool-tip) Super::GetPinHoverText(Pin, HoverTextOut); + const bool bHasValidPlayWorld = IsValid(GEditor->PlayWorld); + // add information on pin activations - if (GEditor->PlayWorld) + if (bHasValidPlayWorld) { if (const UFlowNode* InspectedNodeInstance = GetInspectedNodeInstance()) { @@ -1093,11 +1097,7 @@ void UFlowGraphNode::GetPinHoverText(const UEdGraphPin& Pin, FString& HoverTextO } const TArray& PinRecords = InspectedNodeInstance->GetPinRecords(Pin.PinName, Pin.Direction); - if (PinRecords.Num() == 0) - { - HoverTextOut.Append(FPinRecord::NoActivations); - } - else + if (PinRecords.Num() > 0) { HoverTextOut.Append(FPinRecord::PinActivations); for (int32 i = 0; i < PinRecords.Num(); i++) @@ -1107,22 +1107,78 @@ void UFlowGraphNode::GetPinHoverText(const UEdGraphPin& Pin, FString& HoverTextO switch (PinRecords[i].ActivationType) { - case EFlowPinActivationType::Default: - break; - case EFlowPinActivationType::Forced: - HoverTextOut.Append(FPinRecord::ForcedActivation); - break; - case EFlowPinActivationType::PassThrough: - HoverTextOut.Append(FPinRecord::PassThroughActivation); - break; - default: ; + case EFlowPinActivationType::Default: + break; + case EFlowPinActivationType::Forced: + HoverTextOut.Append(FPinRecord::ForcedActivation); + break; + case EFlowPinActivationType::PassThrough: + HoverTextOut.Append(FPinRecord::PassThroughActivation); + break; + default:; } } } } } -} + // add information on data pin values (only for data pins) + const bool bIsDataPinCategory = !FFlowPin::IsExecPinCategory(Pin.PinType.PinCategory); + if (bIsDataPinCategory) + { + const UEdGraphPin* GraphPinObj = &Pin; + + // Prefer showing runtime values when PIE (consistent with activation history) + const UFlowNodeBase* FlowNodeBase = GetFlowNodeBase(); + + if (bHasValidPlayWorld) + { + FlowNodeBase = GetInspectedNodeInstance(); + } + + FFlowDataPinResult DataResult(EFlowDataPinResolveResult::FailedNullFlowNodeBase); + + if (IsValid(FlowNodeBase)) + { + if (GraphPinObj->Direction == EGPD_Input) + { + // Input pins: do a pin resolve to source the value + DataResult = FlowNodeBase->TryResolveDataPin(GraphPinObj->PinName); + } + else + { + // Output pins: ask this node what it supplies for that output data pin + const UFlowNode* FlowNode = Cast(FlowNodeBase); + if (FlowNode) + { + DataResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPin(FlowNode, GraphPinObj->PinName); + } + } + } + + FString ValueString; + + if (FlowPinType::IsSuccess(DataResult.Result) && DataResult.ResultValue.IsValid()) + { + const FFlowDataPinValue& Value = DataResult.ResultValue.Get(); + if (!Value.TryConvertValuesToString(ValueString)) + { + ValueString = TEXT(""); + } + } + else + { + ValueString = TEXT(""); + } + + if (!HoverTextOut.IsEmpty()) + { + HoverTextOut.Append(LINE_TERMINATOR).Append(LINE_TERMINATOR); + } + + HoverTextOut.Appendf(TEXT("Value: %s"), *ValueString); + } +} void UFlowGraphNode::ForcePinActivation(const FEdGraphPinReference PinReference) const { @@ -1991,4 +2047,4 @@ bool UFlowGraphNode::CanAcceptSubNodeAsChild(const UFlowGraphNode& OtherSubNode, return false; } -#undef LOCTEXT_NAMESPACE +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index 881f6c26e..630cec7d4 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -121,7 +121,7 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit public: /** Edits the specified FlowAsset object */ - void InitFlowAssetEditor(const EToolkitMode::Type Mode, const TSharedPtr& InitToolkitHost, UObject* ObjectToEdit); + virtual void InitFlowAssetEditor(const EToolkitMode::Type Mode, const TSharedPtr& InitToolkitHost, UObject* ObjectToEdit); protected: virtual void CreateToolbar(); diff --git a/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h b/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h index b05d15517..4b2f839be 100644 --- a/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h +++ b/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h @@ -25,7 +25,7 @@ class FLOWEDITOR_API UFlowDebugEditorSubsystem : public UFlowDebuggerSubsystem TMap, TSharedPtr> RuntimeLogs; virtual void OnInstancedTemplateAdded(UFlowAsset* AssetTemplate) override; - virtual void OnInstancedTemplateRemoved(UFlowAsset* AssetTemplate) const override; + virtual void OnInstancedTemplateRemoved(UFlowAsset* AssetTemplate) override; void OnRuntimeMessageAdded(const UFlowAsset* AssetTemplate, const TSharedRef& Message) const; @@ -33,5 +33,8 @@ class FLOWEDITOR_API UFlowDebugEditorSubsystem : public UFlowDebuggerSubsystem virtual void OnResumePIE(const bool bIsSimulating); virtual void OnEndPIE(const bool bIsSimulating); - virtual void PauseSession(const UFlowNode* Node) override; + virtual void PauseSession(const UFlowNode& FlowNode) override; + virtual void ResumeSession(const UFlowNode& FlowNode) override; + + void OnBreakpointHit(const UFlowNode* FlowNode) const; }; diff --git a/Source/FlowEditor/Public/FlowEditorModule.h b/Source/FlowEditor/Public/FlowEditorModule.h index 9e32e6605..b75187d91 100644 --- a/Source/FlowEditor/Public/FlowEditorModule.h +++ b/Source/FlowEditor/Public/FlowEditorModule.h @@ -30,6 +30,7 @@ class FLOWEDITOR_API FFlowEditorModule : public IModuleInterface, public IHasMen TArray> RegisteredAssetActions; TSet CustomClassLayouts; TSet CustomStructLayouts; + bool bIsRegisteredForAssetChanges = false; public: diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h index 15c6ee9b5..8c292ece1 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h @@ -10,7 +10,9 @@ class FFlowAssetEditor; class IDetailsView; +class UEdGraphPin; class UFlowDebuggerSubsystem; +struct FFlowBreakpoint; /** * @@ -109,6 +111,12 @@ class FLOWEDITOR_API SFlowGraphEditor : public SGraphEditor virtual void ReconstructNode() const; virtual bool CanReconstructNode() const; + // ---- Pin breakpoint helpers ---- + static bool GetValidExecBreakpointPinContext(const UEdGraphPin* Pin, FGuid& OutNodeGuid, FName& OutPinName); + static const FFlowBreakpoint* FindPinBreakpoint(UFlowDebuggerSubsystem* InDebuggerSubsystem, const UEdGraphPin* Pin); + static bool HasPinBreakpoint(UFlowDebuggerSubsystem* InDebuggerSubsystem, const UEdGraphPin* Pin); + static bool HasEnabledPinBreakpoint(UFlowDebuggerSubsystem* InDebuggerSubsystem, const UEdGraphPin* Pin); + private: void AddInput() const; bool CanAddInput() const; From 21f8adc0fcfafce3cccdcd3794520df8fcf0c640 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sat, 7 Feb 2026 15:24:42 +0100 Subject: [PATCH 395/485] removed requirement to use removed interface class --- Source/Flow/Public/FlowAsset.h | 8 ++++---- Source/Flow/Public/FlowSettings.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index bd16210e7..58a28b340 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -397,10 +397,7 @@ class FLOW_API UFlowAsset : public UObject const TArray& GetRecordedNodes() const { return RecordedNodes; } ////////////////////////////////////////////////////////////////////////// -// Expected Owner Class support (for use with CallOwnerFunction nodes) - -public: - UClass* GetExpectedOwnerClass() const { return ExpectedOwnerClass; } +// Expected Owner Class support protected: // Expects to be owned (at runtime) by an object with this class (or one of its subclasses) @@ -408,6 +405,9 @@ class FLOW_API UFlowAsset : public UObject // it will consider the component's owner for the AActor UPROPERTY(EditAnywhere, Category = "Flow") TSubclassOf ExpectedOwnerClass; + +public: + UClass* GetExpectedOwnerClass() const { return ExpectedOwnerClass; } ////////////////////////////////////////////////////////////////////////// // SaveGame support diff --git a/Source/Flow/Public/FlowSettings.h b/Source/Flow/Public/FlowSettings.h index 1a8468bbc..f73bf5202 100644 --- a/Source/Flow/Public/FlowSettings.h +++ b/Source/Flow/Public/FlowSettings.h @@ -49,8 +49,8 @@ class FLOW_API UFlowSettings : public UDeveloperSettings FFlowSettingsEvent OnAdaptiveNodeTitlesChanged; #endif - // Default class to use as a FlowAsset's "ExpectedOwnerClass" - UPROPERTY(EditAnywhere, Config, Category = "Nodes", meta = (MustImplement = "/Script/Flow.FlowOwnerInterface")) + // Default class to use as a FlowAsset's "ExpectedOwnerClass" + UPROPERTY(EditAnywhere, Config, Category = "Nodes") FSoftClassPath DefaultExpectedOwnerClass; public: From 1b396b1e1659b91404afcdba16905692219279f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sat, 7 Feb 2026 15:25:13 +0100 Subject: [PATCH 396/485] removed virtual specifier --- Source/Flow/Public/Nodes/FlowNodeBase.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Flow/Public/Nodes/FlowNodeBase.h b/Source/Flow/Public/Nodes/FlowNodeBase.h index ef8fb5004..3f4566ec5 100644 --- a/Source/Flow/Public/Nodes/FlowNodeBase.h +++ b/Source/Flow/Public/Nodes/FlowNodeBase.h @@ -353,7 +353,7 @@ class FLOW_API UFlowNodeBase virtual void PostLoad() override; void SetGraphNode(UEdGraphNode* NewGraphNode); - virtual UEdGraphNode* GetGraphNode() const { return GraphNode; } + UEdGraphNode* GetGraphNode() const { return GraphNode; } void SetCanDelete(const bool CanDelete); From a3318d01e402741f256252f513a84f63b41e3370 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sat, 7 Feb 2026 16:14:45 +0100 Subject: [PATCH 397/485] Integrated last two big breakpoint improvements Fixed resuming session. --- .../Debugger/FlowDebuggerSubsystem.cpp | 50 ++++++++++--------- .../Public/Debugger/FlowDebuggerSubsystem.h | 6 +-- .../Asset/FlowDebugEditorSubsystem.cpp | 15 +++--- .../Public/Asset/FlowDebugEditorSubsystem.h | 4 +- 4 files changed, 40 insertions(+), 35 deletions(-) diff --git a/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp b/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp index e014fe8e1..9037a4f1a 100644 --- a/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp +++ b/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp @@ -432,7 +432,7 @@ void UFlowDebuggerSubsystem::MarkAsHit(const UFlowNode* FlowNode) OnDebuggerBreakpointHit.Broadcast(FlowNode); - PauseSession(*FlowNode); + PauseSession(); } } } @@ -455,23 +455,26 @@ void UFlowDebuggerSubsystem::MarkAsHit(const UFlowNode* FlowNode, const FName& P OnDebuggerBreakpointHit.Broadcast(FlowNode); - PauseSession(*FlowNode); + PauseSession(); } } } -void UFlowDebuggerSubsystem::PauseSession(const UFlowNode& FlowNode) +void UFlowDebuggerSubsystem::PauseSession() { - SetPause(FlowNode, true); + SetPause(true); } -void UFlowDebuggerSubsystem::ResumeSession(const UFlowNode& FlowNode) +void UFlowDebuggerSubsystem::ResumeSession() { - SetPause(FlowNode, false); + SetPause(false); } -void UFlowDebuggerSubsystem::SetPause(const UFlowNode& FlowNode, const bool bPause) +void UFlowDebuggerSubsystem::SetPause(const bool bPause) { + // experimental implementation, won't work yet, shows intent for future development + // here be dragons: same as APlayerController::SetPause, but we allow debugger to pause on clients + // Default bWasPaused to opposite of bPause // (which we hope to get a better measure if we can get access to what we need) bool bWasPaused = !bPause; @@ -479,21 +482,22 @@ void UFlowDebuggerSubsystem::SetPause(const UFlowNode& FlowNode, const bool bPau AGameModeBase* GameMode = nullptr; APlayerController* PlayerController = nullptr; - const UFlowAsset* FlowAssetInstance = FlowNode.GetFlowAsset(); - const UWorld* World = FlowAssetInstance->GetWorld(); - if (IsValid(World)) + if (HaltedOnFlowAssetInstance.IsValid()) { - GameMode = World->GetAuthGameMode(); - - if (IsValid(GameMode)) + if (const UWorld* World = HaltedOnFlowAssetInstance->GetWorld()) { - bWasPaused = GameMode->IsPaused(); - } + GameMode = World->GetAuthGameMode(); - const UGameInstance* GameInstance = World->GetGameInstance(); - if (IsValid(GameInstance)) - { - PlayerController = GameInstance->GetFirstLocalPlayerController(); + if (IsValid(GameMode)) + { + bWasPaused = GameMode->IsPaused(); + } + + const UGameInstance* GameInstance = World->GetGameInstance(); + if (IsValid(GameInstance)) + { + PlayerController = GameInstance->GetFirstLocalPlayerController(); + } } } @@ -517,7 +521,7 @@ void UFlowDebuggerSubsystem::SetPause(const UFlowNode& FlowNode, const bool bPau } // Broadcast the Pause event - OnDebuggerPaused.Broadcast(*FlowAssetInstance); + OnDebuggerPaused.Broadcast(*HaltedOnFlowAssetInstance.Get()); } else { @@ -532,11 +536,11 @@ void UFlowDebuggerSubsystem::SetPause(const UFlowNode& FlowNode, const bool bPau // breakpoint safely (without racing against immediate breakpoint hits during flush). if (IsValid(GameMode)) { - (void) GameMode->ClearPause(); + (void)GameMode->ClearPause(); } // Broadcast the Resume event - OnDebuggerResumed.Broadcast(*FlowAssetInstance); + OnDebuggerResumed.Broadcast(*HaltedOnFlowAssetInstance.Get()); } } } @@ -583,4 +587,4 @@ void UFlowDebuggerSubsystem::SaveSettings() { UFlowDebuggerSettings* Settings = GetMutableDefault(); Settings->SaveConfig(); -} \ No newline at end of file +} diff --git a/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h b/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h index bc4f42219..2267ac318 100644 --- a/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h +++ b/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h @@ -79,9 +79,9 @@ class FLOWDEBUGGER_API UFlowDebuggerSubsystem : public UEngineSubsystem, public virtual void MarkAsHit(const UFlowNode* FlowNode); virtual void MarkAsHit(const UFlowNode* FlowNode, const FName& PinName); - virtual void PauseSession(const UFlowNode& FlowNode); - virtual void ResumeSession(const UFlowNode& FlowNode); - void SetPause(const UFlowNode& FlowNode, const bool bPause); + virtual void PauseSession(); + virtual void ResumeSession(); + void SetPause(const bool bPause); /** * Clears the "currently hit" breakpoint only (node or pin). diff --git a/Source/FlowEditor/Private/Asset/FlowDebugEditorSubsystem.cpp b/Source/FlowEditor/Private/Asset/FlowDebugEditorSubsystem.cpp index 90cac2020..f00f98c80 100644 --- a/Source/FlowEditor/Private/Asset/FlowDebugEditorSubsystem.cpp +++ b/Source/FlowEditor/Private/Asset/FlowDebugEditorSubsystem.cpp @@ -71,14 +71,15 @@ void UFlowDebugEditorSubsystem::OnBeginPIE(const bool bIsSimulating) void UFlowDebugEditorSubsystem::OnResumePIE(const bool bIsSimulating) { - // Editor-level resume event (also used by Advance Single Frame). - // This does not necessarily flow through AGameModeBase::ClearPause(), so we must unhalt Flow here. - // // Clear only the last-hit breakpoint to return to enabled/disabled visuals without racing against // a newly hit breakpoint during FlushDeferredTriggerInputs(). ClearHaltFlowExecution(); ClearLastHitBreakpoint(); + // Editor-level resume event (also used by Advance Single Frame). + // This does not necessarily flow through AGameModeBase::ClearPause(), so we must unhalt Flow here. + ResumeSession(); + FFlowExecutionGate::FlushDeferredTriggerInputs(); } @@ -116,9 +117,9 @@ void UFlowDebugEditorSubsystem::OnEndPIE(const bool bIsSimulating) } } -void UFlowDebugEditorSubsystem::PauseSession(const UFlowNode& FlowNode) +void UFlowDebugEditorSubsystem::PauseSession() { - Super::PauseSession(FlowNode); + // do not call Super, non-PIE world has its only Pause/Resume logic constexpr bool bShouldBePaused = true; const bool bWasPaused = GUnrealEd->SetPIEWorldsPaused(bShouldBePaused); @@ -128,9 +129,9 @@ void UFlowDebugEditorSubsystem::PauseSession(const UFlowNode& FlowNode) } } -void UFlowDebugEditorSubsystem::ResumeSession(const UFlowNode& FlowNode) +void UFlowDebugEditorSubsystem::ResumeSession() { - Super::ResumeSession(FlowNode); + // do not call Super, non-PIE world has its only Pause/Resume logic constexpr bool bShouldBePaused = false; const bool bWasPaused = GUnrealEd->SetPIEWorldsPaused(bShouldBePaused); diff --git a/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h b/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h index 4b2f839be..cc874e10a 100644 --- a/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h +++ b/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h @@ -33,8 +33,8 @@ class FLOWEDITOR_API UFlowDebugEditorSubsystem : public UFlowDebuggerSubsystem virtual void OnResumePIE(const bool bIsSimulating); virtual void OnEndPIE(const bool bIsSimulating); - virtual void PauseSession(const UFlowNode& FlowNode) override; - virtual void ResumeSession(const UFlowNode& FlowNode) override; + virtual void PauseSession() override; + virtual void ResumeSession() override; void OnBreakpointHit(const UFlowNode* FlowNode) const; }; From a442c41d384bbc1a4ed88914210881828530cde0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sat, 7 Feb 2026 16:29:50 +0100 Subject: [PATCH 398/485] removed obsolete toolbar command --- .../Private/Asset/FlowAssetEditor.cpp | 20 ------------------- .../FlowEditor/Private/FlowEditorCommands.cpp | 2 -- .../FlowEditor/Public/Asset/FlowAssetEditor.h | 3 --- Source/FlowEditor/Public/FlowEditorCommands.h | 2 -- 4 files changed, 27 deletions(-) diff --git a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp index 86023789f..ed2881f1d 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -412,13 +412,6 @@ void FFlowAssetEditor::BindToolbarCommands() // Engine's Play commands ToolkitCommands->Append(FPlayWorldCommands::GlobalPlayWorldActions.ToSharedRef()); - - // Debugging - ToolkitCommands->MapAction(ToolbarCommands.GoToParentInstance, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::GoToParentInstance), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanGoToParentInstance), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanGoToParentInstance)); } void FFlowAssetEditor::RefreshAsset() @@ -473,19 +466,6 @@ void FFlowAssetEditor::EditAssetDefaults_Clicked() const DetailsView->SetObject(FlowAsset); } -void FFlowAssetEditor::GoToParentInstance() -{ - const UFlowAsset* AssetThatInstancedThisAsset = FlowAsset->GetInspectedInstance()->GetParentInstance(); - - GEditor->GetEditorSubsystem()->OpenEditorForAsset(AssetThatInstancedThisAsset->GetTemplateAsset()); - AssetThatInstancedThisAsset->GetTemplateAsset()->SetInspectedInstance(AssetThatInstancedThisAsset); -} - -bool FFlowAssetEditor::CanGoToParentInstance() -{ - return FlowAsset->GetInspectedInstance() && FlowAsset->GetInspectedInstance()->GetNodeOwningThisAssetInstance() != nullptr; -} - void FFlowAssetEditor::CreateWidgets() { // Details View diff --git a/Source/FlowEditor/Private/FlowEditorCommands.cpp b/Source/FlowEditor/Private/FlowEditorCommands.cpp index 63c3b64a0..bcba22b00 100644 --- a/Source/FlowEditor/Private/FlowEditorCommands.cpp +++ b/Source/FlowEditor/Private/FlowEditorCommands.cpp @@ -23,8 +23,6 @@ void FFlowToolbarCommands::RegisterCommands() UI_COMMAND(SearchInAsset, "Search", "Search in the current Flow Graph", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control | EModifierKey::Shift, EKeys::F)); UI_COMMAND(EditAssetDefaults, "Asset Defaults", "Edit the FlowAsset default properties", EUserInterfaceActionType::Button, FInputChord()); - - UI_COMMAND(GoToParentInstance, "Go To Parent", "Open editor for the Flow Asset that created this Flow instance", EUserInterfaceActionType::Button, FInputChord()); } FFlowGraphCommands::FFlowGraphCommands() diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index 630cec7d4..9fd612580 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -139,9 +139,6 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit void EditAssetDefaults_Clicked() const; - virtual void GoToParentInstance(); - virtual bool CanGoToParentInstance(); - virtual void CreateWidgets(); virtual void CreateGraphWidget(); diff --git a/Source/FlowEditor/Public/FlowEditorCommands.h b/Source/FlowEditor/Public/FlowEditorCommands.h index 2629a4adc..b7d3aa95c 100644 --- a/Source/FlowEditor/Public/FlowEditorCommands.h +++ b/Source/FlowEditor/Public/FlowEditorCommands.h @@ -18,8 +18,6 @@ class FLOWEDITOR_API FFlowToolbarCommands : public TCommands SearchInAsset; TSharedPtr EditAssetDefaults; - TSharedPtr GoToParentInstance; - virtual void RegisterCommands() override; }; From d07b4e7e333d6abc7ed9906d4b0487674d0e10eb Mon Sep 17 00:00:00 2001 From: Evan Date: Fri, 13 Feb 2026 10:12:55 -0800 Subject: [PATCH 399/485] Made CanFinishGraph BlueprintImplementableEvent overridable (#342) Added the ability for CanFinishGraph to be overridden in blueprint in the same way that CanUserAddInput can through a K2_CanFinishGraph function. --- Source/Flow/Public/Nodes/FlowNode.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 6e2d6fbb1..8fee5eebd 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -85,7 +85,7 @@ class FLOW_API UFlowNode : public UFlowNodeBase } public: - virtual bool CanFinishGraph() const { return false; } + virtual bool CanFinishGraph() const { return K2_CanFinishGraph(); } protected: UPROPERTY(EditDefaultsOnly, Category = "FlowNode") @@ -154,6 +154,9 @@ class FLOW_API UFlowNode : public UFlowNodeBase #endif protected: + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Can Finish Graph")) + bool K2_CanFinishGraph() const; + UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Can User Add Input")) bool K2_CanUserAddInput() const; From a2f9d518221b35a076d728d42aad4e6a03d96eaf Mon Sep 17 00:00:00 2001 From: Evan Date: Fri, 13 Feb 2026 10:13:05 -0800 Subject: [PATCH 400/485] Made NotifyFromGraph BlueprintCallable (#340) I have a blueprint node and want to notify my parent directly from it. It's possible in C++ so seems reasonable to make it possible in BP too. --- Source/Flow/Public/FlowComponent.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Flow/Public/FlowComponent.h b/Source/Flow/Public/FlowComponent.h index 333779911..5f5ef8a7f 100644 --- a/Source/Flow/Public/FlowComponent.h +++ b/Source/Flow/Public/FlowComponent.h @@ -132,6 +132,7 @@ class FLOW_API UFlowComponent : public UActorComponent, public IFlowAssetProvide FGameplayTagContainer NotifyTagsFromGraph; public: + UFUNCTION(BlueprintCallable, Category = "Flow") virtual void NotifyFromGraph(const FGameplayTagContainer& NotifyTags, const EFlowNetMode NetMode = EFlowNetMode::Authority); private: From c5a2932611d0dc72cc62e705c261bfe6cf1c9d05 Mon Sep 17 00:00:00 2001 From: Evan Date: Fri, 13 Feb 2026 10:13:14 -0800 Subject: [PATCH 401/485] Fixed some datapin colors (#339) A few of the data pins weren't set to the correct color for their underlying type. --- Source/Flow/Public/Types/FlowPinTypesStandard.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Flow/Public/Types/FlowPinTypesStandard.h b/Source/Flow/Public/Types/FlowPinTypesStandard.h index e8bca110d..64e7a37df 100644 --- a/Source/Flow/Public/Types/FlowPinTypesStandard.h +++ b/Source/Flow/Public/Types/FlowPinTypesStandard.h @@ -324,7 +324,7 @@ struct FLOW_API FFlowPinType_Vector : public FFlowPinType virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinTypeNameVector; } #if WITH_EDITOR - virtual FLinearColor GetPinColor() const override { return GetDefault()->StructPinTypeColor; } + virtual FLinearColor GetPinColor() const override { return GetDefault()->VectorPinTypeColor; } virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; virtual UObject* GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const override; #endif @@ -350,7 +350,7 @@ struct FLOW_API FFlowPinType_Rotator : public FFlowPinType virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinTypeNameRotator; } #if WITH_EDITOR - virtual FLinearColor GetPinColor() const override { return GetDefault()->StructPinTypeColor; } + virtual FLinearColor GetPinColor() const override { return GetDefault()->RotatorPinTypeColor; } virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; virtual UObject* GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const override; #endif @@ -376,7 +376,7 @@ struct FLOW_API FFlowPinType_Transform : public FFlowPinType virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinTypeNameTransform; } #if WITH_EDITOR - virtual FLinearColor GetPinColor() const override { return GetDefault()->StructPinTypeColor; } + virtual FLinearColor GetPinColor() const override { return GetDefault()->TransformPinTypeColor; } virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; virtual UObject* GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const override; #endif From 001b730e3f8a1061bb3f5c8c50ed7684eeba66c9 Mon Sep 17 00:00:00 2001 From: LindyHopperGT <91915878+LindyHopperGT@users.noreply.github.com> Date: Fri, 13 Feb 2026 11:21:48 -0800 Subject: [PATCH 402/485] Flow deferred trigger queuing, Data Pin support for Addongs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flow deferred trigger queuing rigger Outputs deferred while processing an Input Trigger This is a change to the core flow triggering logic to fix a category of sequencing bugs from the previous behavior. It would immediately fully process a triggered input and so on down the chain of flow nodes, without allowing the current flow node to finish executing, this caused a whole category of problems where the node wasn't able to finish its execution before being interrupted by a retirgger (from downstream) and AddOns wouldn't execute at the same time as their owning flow node reliably Now FlowAsset will queue any triggers generated while processing a trigger, and flush them when ending the processing of that trigger. We also integrated the debugger queued trigger caching mechanism to use the same syste. Subclasses of UFlowAsset that do their own deferred asset triggering can disable this feature, except for the debugger portion, which is still processed using the UFlowAsset queue. CR (Riot) - JDurica, BJarvinen Flow data pin support for AddOns [Flow] Converted FlowDataPinValueSupplierInterface to C++ only The blueprint functionality for this has been removed Rationale: The Execute_TrySupplyDataPin overhead is non-trivial vs. a simple virtual call in C++ Could still create custom supply logic in C++ It's a really technical thing to do in Blueprint The new Data Pin Value wrappers for blueprint are highly functional and usable; providing a very clean and easy method to add data pins to blueprint nodes Removing the support could allow me to simplify the Pin authoring interface in blueprint (for exec pins) to make it more clear, since they wouldn't need to also support authoring data pins in that way. [Flow] Added FlowNode_BlueprintDataPinSupplierBase This is a thin c++ class that exposes TrySupplyDataPin override capability for blueprint if they need it. Suggested by a thread on the Flow Discord, where a user indicated a legitimate use case for still wanting to override TrySupplyDataPin in blueprint This class provides a targeted override capability to blueprint, while still leaving the blueprint dispatch out of the UFlowNode base implementation (for better perf in the general case) CR - BJarvinen, ECalder [Flow] Added factory creation method for FlowAssetParams Previously, we only supported flow asset params by right-clicking the desired parent asset, now we also have a Create Asset entry (like Flow Asset, or Flow Node Blueprint, etc.) The new factory create still requires a parent params asset, which is selected via an asset-picker. Created common child asset creation code for both methods of creating child FlowAssetParams to share CR - BJarvinen [Flow] FlowAssetParams configuration for Subgraph nodes Can now statically assign AssetParams to use on a Subgraph node for the associated (flow) Asset Can also dynamically source the AssetParams to use via an input data pin for the Subgraph node If the Subgraph node pins are connected, the connected supplier is preferred over the FlowAssetParams Otherwise, the FlowAssetParams object (if chosen) will supply the value (this is the new part) If no assigned FlowAssetParams, the Start node in the subgraph's default values are used (this was pre-existing behavior) Also: Fixed some code in and around sourcing subgraph Start node pin values to allow this sourcing to work properly Fixed some issues preventing the tooltip inspection for data pins in subgraphs from correctly finding the correct source value Removed an incorrect IsHalted check, which was preventing subgraphs from starting when at a breakpoint (as they aren't queued, this would just drop the start of the subgraph rather than deferring it) CR - BJarvinen, ECalder [Flow] Data Pin Reroute support Added Reroute node support for Flow Data Pins (previously was Exec pins only) Slight cost during lookup due to passing through the reroute node(s) to get to the actual node Same cost as regular reroutes in the form of full flow node for a clerical purpose. (It would be nice to strip these out of the graph for shipping flow graphs, but that's a future goal) CR - JDurica, BJarvinen Co-Authored-By: Krzysztof Justyński --- Source/Flow/Private/Asset/FlowAssetParams.cpp | 4 +- .../Private/Asset/FlowAssetParamsUtils.cpp | 142 ++++++++++- .../Asset/FlowDeferredTransitionScope.cpp | 32 +++ Source/Flow/Private/FlowAsset.cpp | 212 +++++++++++++--- Source/Flow/Private/FlowSettings.cpp | 1 + Source/Flow/Private/FlowSubsystem.cpp | 58 +++++ .../Private/Interfaces/FlowExecutionGate.cpp | 111 -------- .../Private/Nodes/Developer/FlowNode_Log.cpp | 17 +- Source/Flow/Private/Nodes/FlowNode.cpp | 144 +++++++---- Source/Flow/Private/Nodes/FlowNodeBase.cpp | 2 +- Source/Flow/Private/Nodes/FlowPin.cpp | 42 +--- .../FlowNode_BlueprintDataPinSupplierBase.cpp | 26 ++ .../Nodes/Graph/FlowNode_DefineProperties.cpp | 22 +- .../Nodes/Graph/FlowNode_FormatText.cpp | 12 +- .../Private/Nodes/Graph/FlowNode_Start.cpp | 6 +- .../Private/Nodes/Graph/FlowNode_SubGraph.cpp | 50 +++- .../Private/Nodes/Route/FlowNode_Reroute.cpp | 46 ++++ .../Types/FlowAutoDataPinsWorkingData.cpp | 238 +++++++++++++++++- Source/Flow/Private/Types/FlowPinType.cpp | 2 +- .../Private/Types/FlowPinTypesStandard.cpp | 68 ++--- Source/Flow/Public/AddOns/FlowNodeAddOn.h | 4 +- Source/Flow/Public/Asset/FlowAssetParams.h | 10 +- .../Flow/Public/Asset/FlowAssetParamsUtils.h | 24 +- .../Asset/FlowDeferredTransitionScope.h | 36 +++ Source/Flow/Public/FlowAsset.h | 129 ++++++---- Source/Flow/Public/FlowSettings.h | 19 +- Source/Flow/Public/FlowSubsystem.h | 9 + .../FlowDataPinValueSupplierInterface.h | 10 +- .../Public/Interfaces/FlowExecutionGate.h | 9 - .../Public/Nodes/Developer/FlowNode_Log.h | 3 + Source/Flow/Public/Nodes/FlowNode.h | 35 ++- Source/Flow/Public/Nodes/FlowNodeBase.h | 51 ++++ Source/Flow/Public/Nodes/FlowPin.h | 77 ++---- .../FlowNode_BlueprintDataPinSupplierBase.h | 33 +++ .../Nodes/Graph/FlowNode_DefineProperties.h | 6 + .../Public/Nodes/Graph/FlowNode_FormatText.h | 10 +- .../Flow/Public/Nodes/Graph/FlowNode_Start.h | 2 +- .../Public/Nodes/Graph/FlowNode_SubGraph.h | 21 +- .../Public/Nodes/Route/FlowNode_Reroute.h | 15 +- .../Types/FlowAutoDataPinsWorkingData.h | 70 +++++- Source/Flow/Public/Types/FlowPinEnums.h | 5 +- Source/Flow/Public/Types/FlowPinType.h | 2 +- .../Public/Types/FlowPinTypeNodeTemplates.h | 4 +- .../Flow/Public/Types/FlowPinTypeTemplates.h | 4 +- .../Flow/Public/Types/FlowPinTypesStandard.h | 34 +-- .../Debugger/FlowDebuggerSubsystem.cpp | 185 +++++++------- .../Public/Debugger/FlowDebuggerSubsystem.h | 53 +++- .../Asset/AssetDefinition_FlowAssetParams.cpp | 101 ++------ .../Private/Asset/FlowAssetParamsFactory.cpp | 170 +++++++++++++ .../Asset/FlowDebugEditorSubsystem.cpp | 99 ++++++-- .../FlowPinCustomization.cpp | 2 - .../Private/Graph/FlowGraphSchema.cpp | 24 +- .../Private/Graph/Nodes/FlowGraphNode.cpp | 15 +- .../Graph/Nodes/FlowGraphNode_Branch.cpp | 2 +- .../Graph/Nodes/FlowGraphNode_Reroute.cpp | 26 ++ .../Private/Graph/Widgets/SFlowGraphNode.cpp | 75 ++++++ .../Public/Asset/FlowAssetParamsFactory.h | 34 +++ .../Public/Asset/FlowDebugEditorSubsystem.h | 8 +- .../Public/Graph/Nodes/FlowGraphNode.h | 2 +- .../Graph/Nodes/FlowGraphNode_Reroute.h | 2 + .../Public/Graph/Widgets/SFlowGraphNode.h | 3 +- 61 files changed, 1936 insertions(+), 722 deletions(-) create mode 100644 Source/Flow/Private/Asset/FlowDeferredTransitionScope.cpp create mode 100644 Source/Flow/Private/Nodes/Graph/FlowNode_BlueprintDataPinSupplierBase.cpp create mode 100644 Source/Flow/Public/Asset/FlowDeferredTransitionScope.h create mode 100644 Source/Flow/Public/Nodes/Graph/FlowNode_BlueprintDataPinSupplierBase.h create mode 100644 Source/FlowEditor/Private/Asset/FlowAssetParamsFactory.cpp create mode 100644 Source/FlowEditor/Public/Asset/FlowAssetParamsFactory.h diff --git a/Source/Flow/Private/Asset/FlowAssetParams.cpp b/Source/Flow/Private/Asset/FlowAssetParams.cpp index 62ae577f7..e9983aef8 100644 --- a/Source/Flow/Private/Asset/FlowAssetParams.cpp +++ b/Source/Flow/Private/Asset/FlowAssetParams.cpp @@ -367,12 +367,12 @@ void UFlowAssetParams::RebuildPropertiesMap() } #endif -bool UFlowAssetParams::CanSupplyDataPinValues_Implementation() const +bool UFlowAssetParams::CanSupplyDataPinValues() const { return !PropertyMap.IsEmpty(); } -FFlowDataPinResult UFlowAssetParams::TrySupplyDataPin_Implementation(FName PinName) const +FFlowDataPinResult UFlowAssetParams::TrySupplyDataPin(FName PinName) const { if (const TInstancedStruct* Found = PropertyMap.Find(PinName)) { diff --git a/Source/Flow/Private/Asset/FlowAssetParamsUtils.cpp b/Source/Flow/Private/Asset/FlowAssetParamsUtils.cpp index 6cf293d5e..f7688920a 100644 --- a/Source/Flow/Private/Asset/FlowAssetParamsUtils.cpp +++ b/Source/Flow/Private/Asset/FlowAssetParamsUtils.cpp @@ -6,6 +6,20 @@ #include "Misc/DateTime.h" #include "HAL/FileManager.h" +#if WITH_EDITOR +#include "Asset/FlowAssetParams.h" +#include "FlowLogChannels.h" + +#include "AssetRegistry/AssetRegistryModule.h" +#include "AssetToolsModule.h" +#include "ContentBrowserModule.h" +#include "FileHelpers.h" +#include "IContentBrowserSingleton.h" +#include "Misc/MessageDialog.h" +#include "Modules/ModuleManager.h" +#include "SourceControlHelpers.h" +#endif + #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowAssetParamsUtils) #if WITH_EDITOR @@ -116,4 +130,130 @@ bool FFlowAssetParamsUtils::ArePropertiesEqual( return A.DataPinValue == B.DataPinValue; } -#endif +UFlowAssetParams* FFlowAssetParamsUtils::CreateChildParamsAsset(UFlowAssetParams& ParentParams, const bool bShowDialogs, FText* OutOptionalFailureReason) +{ + if (!IsValid(&ParentParams)) + { + FailCreateChild( + NSLOCTEXT("FlowAssetParamsUtils", "InvalidParent", "Invalid Parent Flow Asset Params."), + bShowDialogs, + OutOptionalFailureReason); + + return nullptr; + } + + const FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked("AssetTools"); + + const FString PackagePath = FPackageName::GetLongPackagePath(ParentParams.GetPackage()->GetPathName()); + const FString BaseAssetName = ParentParams.GetName(); + + // Generate a unique name for the new asset params + FString UniquePackageName; + FString UniqueAssetName; + AssetToolsModule.Get().CreateUniqueAssetName(PackagePath + TEXT("/") + BaseAssetName, TEXT(""), UniquePackageName, UniqueAssetName); + + if (UniqueAssetName.IsEmpty()) + { + FailCreateChild( + FText::Format( + NSLOCTEXT("FlowAssetParamsUtils", "UniqueNameFail", "Failed to generate unique asset name for child params of {0}."), + FText::FromString(BaseAssetName)), + bShowDialogs, + OutOptionalFailureReason); + + return nullptr; + } + + // Create the new asset params + UFlowAssetParams* NewParams = Cast( + AssetToolsModule.Get().CreateAsset(UniqueAssetName, PackagePath, ParentParams.GetClass(), nullptr)); + + if (!IsValid(NewParams)) + { + FailCreateChild( + FText::Format( + NSLOCTEXT("FlowAssetParamsUtils", "CreateAssetFail", "Failed to create child Flow Asset Params: {0}."), + FText::FromString(UniqueAssetName)), + bShowDialogs, + OutOptionalFailureReason); + + return nullptr; + } + + // Best-effort source control integration (before save) + if (USourceControlHelpers::IsAvailable()) + { + const FString FileName = USourceControlHelpers::PackageFilename(NewParams->GetPathName()); + if (!USourceControlHelpers::CheckOutOrAddFile(FileName)) + { + UE_LOG(LogFlow, Warning, TEXT("Failed to check out/add %s; saved in-memory only"), *NewParams->GetPathName()); + } + } + + // Configure from parent (copies OwnerFlowAsset + Properties, sets ParentParams, rebuilds PropertyMap, marks dirty) + NewParams->ConfigureFlowAssetParams(ParentParams.OwnerFlowAsset, &ParentParams, ParentParams.Properties); + + // Reconcile (cycle detection, flattened inheritance, etc.) + const EFlowReconcilePropertiesResult ReconcileResult = NewParams->ReconcilePropertiesWithParentParams(); + if (EFlowReconcilePropertiesResult_Classifiers::IsErrorResult(ReconcileResult)) + { + FailCreateChild( + FText::Format( + NSLOCTEXT("FlowAssetParamsUtils", "ReconcileFail", + "Created asset but reconciliation failed.\n\nAsset: {0}\nError: {1}\n\nThe asset may be invalid and should be reviewed."), + FText::FromString(NewParams->GetPathName()), + UEnum::GetDisplayValueAsText(ReconcileResult)), + bShowDialogs, + OutOptionalFailureReason); + + // Keep going: asset exists and may still be useful for debugging/fixing + } + + // Save the package (force save even if not prompted) + { + UPackage* Package = NewParams->GetPackage(); + const TArray PackagesToSave = { Package }; + + constexpr bool bForceSave = true; + if (!UEditorLoadingAndSavingUtils::SavePackages(PackagesToSave, bForceSave)) + { + FailCreateChild( + FText::Format( + NSLOCTEXT("FlowAssetParamsUtils", "SaveFail", "Failed to save child Flow Asset Params: {0}."), + FText::FromString(NewParams->GetPathName())), + bShowDialogs, + OutOptionalFailureReason); + + // Still return the in-memory asset + } + } + + // Register + sync to Content Browser + { + const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked("AssetRegistry"); + AssetRegistryModule.Get().AssetCreated(NewParams); + + const FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked("ContentBrowser"); + const TArray AssetsToSync = { NewParams }; + ContentBrowserModule.Get().SyncBrowserToAssets(AssetsToSync, true); + } + + return NewParams; +} + +void FFlowAssetParamsUtils::FailCreateChild(const FText& Reason, const bool bShowDialogs, FText* OutOptionalFailureReason) +{ + if (OutOptionalFailureReason) + { + *OutOptionalFailureReason = Reason; + } + + UE_LOG(LogFlow, Error, TEXT("%s"), *Reason.ToString()); + + if (bShowDialogs) + { + FMessageDialog::Open(EAppMsgType::Ok, Reason); + } +} + +#endif \ No newline at end of file diff --git a/Source/Flow/Private/Asset/FlowDeferredTransitionScope.cpp b/Source/Flow/Private/Asset/FlowDeferredTransitionScope.cpp new file mode 100644 index 000000000..c5ac2b4db --- /dev/null +++ b/Source/Flow/Private/Asset/FlowDeferredTransitionScope.cpp @@ -0,0 +1,32 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Asset/FlowDeferredTransitionScope.h" +#include "FlowAsset.h" +#include "Interfaces/FlowExecutionGate.h" + +void FFlowDeferredTransitionScope::EnqueueDeferredTrigger(const FFlowDeferredTriggerInput& Entry) +{ + check(bIsOpen); + + DeferredTriggers.Add(Entry); +} + +bool FFlowDeferredTransitionScope::TryFlushDeferredTriggers(UFlowAsset& OwningFlowAsset) +{ + // Ensure the scope is closed before beginning flushing + CloseScope(); + + // Remove and trigger each deferred trigger input + while (!DeferredTriggers.IsEmpty() && !FFlowExecutionGate::IsHalted()) + { + const FFlowDeferredTriggerInput Entry = DeferredTriggers[0]; + DeferredTriggers.RemoveAt(0, 1, EAllowShrinking::No); + + OwningFlowAsset.TriggerInput(Entry.NodeGuid, Entry.PinName, Entry.FromPin); + } + + check(DeferredTriggers.IsEmpty() || FFlowExecutionGate::IsHalted()); + + // Return true if everything flushed without being interrupted by an ExecutionGate + return DeferredTriggers.IsEmpty(); +} \ No newline at end of file diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 63014aea2..2e054f9dd 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -646,30 +646,34 @@ bool UFlowAsset::TryUpdateManagedFlowPinsForNode(UFlowNode& FlowNode) FlowNode.GetAutoInputDataPins(), FlowNode.GetAutoOutputDataPins()); - // Allow the node to auto-generate data pins - FlowNode.AutoGenerateDataPins(WorkingData); + bool bAutoInputDataPinsChanged = false; + bool bAutoOutputDataPinsChanged = false; - // If the auto-generated data pins array changed, it counts as dirty as well - const bool bAutoInputDataPinsChanged = WorkingData.DidAutoInputDataPinsChange(); - const bool bAutoOutputDataPinsChanged = WorkingData.DidAutoOutputDataPinsChange(); - - if (bAutoInputDataPinsChanged || bAutoOutputDataPinsChanged) + if (WorkingData.AutoGenerateDataPinsForFlowNode(FlowNode, bAutoInputDataPinsChanged, bAutoOutputDataPinsChanged)) { FlowNode.SetFlags(RF_Transactional); FlowNode.Modify(); // Lock-in the data that changed. - if (bAutoInputDataPinsChanged || bAutoOutputDataPinsChanged) + TArray FlowPinsNext; + const int32 LargestPinNum = FMath::Max(WorkingData.AutoInputDataPinsNext.Num(), WorkingData.AutoOutputDataPinsNext.Num()); + + if (bAutoInputDataPinsChanged) { - if (bAutoInputDataPinsChanged) - { - FlowNode.SetAutoInputDataPins(WorkingData.AutoInputDataPinsNext); - } + FlowPinsNext.Reserve(LargestPinNum); - if (bAutoOutputDataPinsChanged) - { - FlowNode.SetAutoOutputDataPins(WorkingData.AutoOutputDataPinsNext); - } + WorkingData.BuildNextFlowPinArray(WorkingData.AutoInputDataPinsNext, FlowPinsNext); + + FlowNode.SetAutoInputDataPins(FlowPinsNext); + } + + if (bAutoOutputDataPinsChanged) + { + FlowPinsNext.Reserve(LargestPinNum); + + WorkingData.BuildNextFlowPinArray(WorkingData.AutoOutputDataPinsNext, FlowPinsNext); + + FlowNode.SetAutoOutputDataPins(FlowPinsNext); } FlowNode.PostEditChange(); @@ -971,6 +975,9 @@ void UFlowAsset::InitializeInstance(const TWeakObjectPtr InOwner, UFlow void UFlowAsset::DeinitializeInstance() { + // These should have been flushed in FinishFlow() + check(DeferredTransitionScopes.IsEmpty()); + if (IsInstanceInitialized()) { for (const TPair& Node : ObjectPtrDecay(Nodes)) @@ -1013,11 +1020,6 @@ void UFlowAsset::PreStartFlow() void UFlowAsset::StartFlow(IFlowDataPinValueSupplierInterface* DataPinValueSupplier) { - if (FFlowExecutionGate::IsHalted()) - { - return; - } - PreStartFlow(); if (UFlowNode* ConnectedEntryNode = GetDefaultEntryNode()) @@ -1037,6 +1039,8 @@ void UFlowAsset::FinishFlow(const EFlowFinishPolicy InFinishPolicy, const bool b { FinishPolicy = InFinishPolicy; + CancelAndWarnForUnflushedDeferredTriggers(); + // end execution of this asset and all of its nodes for (UFlowNode* Node : ActiveNodes) { @@ -1058,6 +1062,62 @@ void UFlowAsset::FinishFlow(const EFlowFinishPolicy InFinishPolicy, const bool b } } +void UFlowAsset::CancelAndWarnForUnflushedDeferredTriggers() +{ + // Aggressively drop any pending deferred triggers — graph is done + // In normal execution these should have been flushed via PopDeferredTransitionScope() in TriggerInputDirect + // In the debugger they should have been flushed by ResumePIE + // Remaining scopes here usually mean: + // - early/abnormal termination (e.g. FinishFlow called from unexpected place) + // - exception/early return before Pop + // - forced deinitialization during active execution (e.g. PIE stop, subsystem cleanup) + if (!DeferredTransitionScopes.IsEmpty()) + { + int32 TotalDroppedTriggers = 0; + + for (const TSharedPtr& ScopePtr : DeferredTransitionScopes) + { + if (!ScopePtr.IsValid()) + { + continue; + } + + const TArray& Triggers = ScopePtr->GetDeferredTriggers(); + + if (TotalDroppedTriggers == 0 && !Triggers.IsEmpty()) + { + UE_LOG(LogFlow, Warning, TEXT("FlowAsset '%s' is finishing with %d lingering deferred transition scope(s) — dropping them. " + "This is usually unexpected and may indicate a bug or abnormal termination."), + *GetName(), DeferredTransitionScopes.Num()); + } + + TotalDroppedTriggers += Triggers.Num(); + + for (const FFlowDeferredTriggerInput& Trigger : Triggers) + { + const UFlowNode* ToNode = GetNode(Trigger.NodeGuid); + const UFlowNode* FromNode = Trigger.FromPin.NodeGuid.IsValid() ? GetNode(Trigger.FromPin.NodeGuid) : nullptr; + + UE_LOG(LogFlow, Error, + TEXT(" → Dropped deferred trigger:\n") + TEXT(" To Node: %s (%s)\n") + TEXT(" To Pin: %s\n") + TEXT(" From Node: %s (%s)\n") + TEXT(" From Pin: %s"), + *ToNode->GetName(), + *Trigger.NodeGuid.ToString(), + *Trigger.PinName.ToString(), + *FromNode->GetName(), + *Trigger.FromPin.NodeGuid.ToString(), + *Trigger.FromPin.PinName.ToString() + ); + } + } + + ClearAllDeferredTriggerScopes(); + } +} + bool UFlowAsset::HasStartedFlow() const { return RecordedNodes.Num() > 0; @@ -1081,11 +1141,6 @@ TWeakObjectPtr UFlowAsset::GetFlowInstance(UFlowNode_SubGraph* SubGr void UFlowAsset::TriggerCustomInput_FromSubGraph(UFlowNode_SubGraph* SubGraphNode, const FName& EventName) const { - if (FFlowExecutionGate::IsHalted()) - { - return; - } - // NOTE (gtaylor) Custom Input nodes cannot currently add data pins (like Start or DefineProperties nodes can) // but we may want to allow them to source parameters, so I am providing the subgraph node as the // IFlowDataPinValueSupplierInterface when triggering the node (even though it's not used at this time). @@ -1099,11 +1154,6 @@ void UFlowAsset::TriggerCustomInput_FromSubGraph(UFlowNode_SubGraph* SubGraphNod void UFlowAsset::TriggerCustomInput(const FName& EventName, IFlowDataPinValueSupplierInterface* DataPinValueSupplier) { - if (FFlowExecutionGate::IsHalted()) - { - return; - } - for (UFlowNode_CustomInput* CustomInputNode : CustomInputNodes) { if (CustomInputNode->EventName == EventName) @@ -1143,11 +1193,33 @@ void UFlowAsset::TriggerCustomOutput(const FName& EventName) void UFlowAsset::TriggerInput(const FGuid& NodeGuid, const FName& PinName, const FConnectedPin& FromPin) { - if (FFlowExecutionGate::EnqueueDeferredTriggerInput(this, NodeGuid, PinName, FromPin)) + if (FFlowExecutionGate::IsHalted()) { - return; + // Halt always takes precedence for debugger correctness + EnqueueDeferredTrigger(NodeGuid, PinName, FromPin); } + else if (ShouldDeferTriggers()) + { + // Defer only if we have an open the top scope + if (!DeferredTransitionScopes.IsEmpty() && DeferredTransitionScopes.Top()->IsOpen()) + { + EnqueueDeferredTrigger(NodeGuid, PinName, FromPin); + } + else + { + const TSharedPtr CurrentScope = PushDeferredTransitionScope(); + TriggerInputDirect(NodeGuid, PinName, FromPin); + PopDeferredTransitionScope(CurrentScope); + } + } + else + { + TriggerInputDirect(NodeGuid, PinName, FromPin); + } +} +void UFlowAsset::TriggerInputDirect(const FGuid& NodeGuid, const FName& PinName, const FConnectedPin& FromPin) +{ if (UFlowNode* Node = Nodes.FindRef(NodeGuid)) { if (!ActiveNodes.Contains(Node)) @@ -1160,6 +1232,80 @@ void UFlowAsset::TriggerInput(const FGuid& NodeGuid, const FName& PinName, const } } +bool UFlowAsset::ShouldDeferTriggers() const +{ + return GetDefault()->bDeferTriggeredOutputsWhileTriggering; +} + +TSharedPtr UFlowAsset::PushDeferredTransitionScope() +{ + // Close the former top scope (if any) + if (!DeferredTransitionScopes.IsEmpty()) + { + const TSharedPtr& FormerTop = DeferredTransitionScopes.Top(); + FormerTop->CloseScope(); + } + + // Push a fresh open scope + return DeferredTransitionScopes.Add_GetRef(MakeShared()); +} + +bool UFlowAsset::TryFlushAndRemoveDeferredTransitionScope(const TSharedPtr& ScopeToFlush) +{ + if (ScopeToFlush->TryFlushDeferredTriggers(*this)) + { + // Remove the exact instance we were holding (handles nested push/pop cases) + DeferredTransitionScopes.RemoveSingle(ScopeToFlush); + return true; + } + else + { + // Flush was interrupted — should only happen due to execution gate halt + check(FFlowExecutionGate::IsHalted()); + return false; + } +} + +void UFlowAsset::EnqueueDeferredTrigger(const FGuid& NodeGuid, const FName& PinName, const FConnectedPin& FromPin) +{ + if (DeferredTransitionScopes.IsEmpty() || !DeferredTransitionScopes.Top()->IsOpen()) + { + // This should only occur when halted at an execution gate + check(FFlowExecutionGate::IsHalted()); + PushDeferredTransitionScope(); + } + + // Always enqueue to the current innermost (top) scope + DeferredTransitionScopes.Top()->EnqueueDeferredTrigger(FFlowDeferredTriggerInput{ NodeGuid, PinName, FromPin }); +} + +bool UFlowAsset::TryFlushAllDeferredTriggerScopes() +{ + while (const TSharedPtr TopScope = GetTopDeferredTransitionScope()) + { + if (!TryFlushAndRemoveDeferredTransitionScope(TopScope)) + { + break; + } + + // Keep flushing until stack is empty or we hit an ExecutionGate halt + } + + check(DeferredTransitionScopes.IsEmpty() || FFlowExecutionGate::IsHalted()); + + return DeferredTransitionScopes.IsEmpty(); +} + +void UFlowAsset::ClearAllDeferredTriggerScopes() +{ + DeferredTransitionScopes.Reset(); +} + +TSharedPtr UFlowAsset::GetTopDeferredTransitionScope() const +{ + return !DeferredTransitionScopes.IsEmpty() ? DeferredTransitionScopes.Top() : nullptr; +} + void UFlowAsset::FinishNode(UFlowNode* Node) { if (ActiveNodes.Contains(Node)) diff --git a/Source/Flow/Private/FlowSettings.cpp b/Source/Flow/Private/FlowSettings.cpp index e098071fd..e6f835f4e 100644 --- a/Source/Flow/Private/FlowSettings.cpp +++ b/Source/Flow/Private/FlowSettings.cpp @@ -11,6 +11,7 @@ UFlowSettings::UFlowSettings(const FObjectInitializer& ObjectInitializer) , bWarnAboutMissingIdentityTags(true) , bLogOnSignalDisabled(true) , bLogOnSignalPassthrough(true) + , bDeferTriggeredOutputsWhileTriggering(true) , bUseAdaptiveNodeTitles(false) , DefaultExpectedOwnerClass(UFlowComponent::StaticClass()) { diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index 70a88eafe..9a3cf1ec7 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -7,6 +7,7 @@ #include "FlowLogChannels.h" #include "FlowSave.h" #include "FlowSettings.h" +#include "Interfaces/FlowExecutionGate.h" #include "Nodes/Graph/FlowNode_SubGraph.h" #include "Engine/GameInstance.h" @@ -159,6 +160,63 @@ void UFlowSubsystem::FinishAllRootFlows(UObject* Owner, const EFlowFinishPolicy } } +bool UFlowSubsystem::TryFlushAllDeferredTriggerScopes() +{ + // Flush deferred triggers on all active runtime instances. + // Flush order follows InstancedTemplates iteration + per-template ActiveInstances. + // This provides reasonable per-asset FIFO but is not a strict global FIFO across assets. + // A more precise global queue could be implemented later if cross-asset ordering becomes critical. + const TArray CapturedInstancedTemplates = InstancedTemplates; + for (UFlowAsset* Template : CapturedInstancedTemplates) + { + if (!IsValid(Template)) + { + continue; + } + + for (UFlowAsset* Instance : Template->GetActiveInstances()) + { + if (FFlowExecutionGate::IsHalted()) + { + break; + } + + if (IsValid(Instance)) + { + const bool bFlushed = Instance->TryFlushAllDeferredTriggerScopes(); + + // The only case where we allow a flush to stop before completing + // is if we hit an execution gate halt + check(bFlushed || FFlowExecutionGate::IsHalted()); + } + } + } + + // The only case where we allow a flush to stop before completing + // is if we hit an execution gate halt + const bool bCompletedFlushAll = !FFlowExecutionGate::IsHalted(); + return bCompletedFlushAll; +} + +void UFlowSubsystem::ClearAllDeferredTriggerScopes() +{ + for (UFlowAsset* Template : InstancedTemplates) + { + if (!IsValid(Template)) + { + continue; + } + + for (UFlowAsset* Instance : Template->GetActiveInstances()) + { + if (IsValid(Instance)) + { + Instance->ClearAllDeferredTriggerScopes(); + } + } + } +} + UFlowAsset* UFlowSubsystem::CreateSubFlow(UFlowNode_SubGraph* SubGraphNode, const FString& SavedInstanceName, const bool bPreloading /* = false */) { UFlowAsset* NewInstance = nullptr; diff --git a/Source/Flow/Private/Interfaces/FlowExecutionGate.cpp b/Source/Flow/Private/Interfaces/FlowExecutionGate.cpp index 44fd864f2..e09b7d02a 100644 --- a/Source/Flow/Private/Interfaces/FlowExecutionGate.cpp +++ b/Source/Flow/Private/Interfaces/FlowExecutionGate.cpp @@ -5,20 +5,6 @@ #include "FlowAsset.h" #include "Nodes/FlowPin.h" -namespace FlowExecutionGate_Private -{ - struct FDeferredTriggerInput - { - TWeakObjectPtr FlowAssetInstance; - FGuid NodeGuid; - FName PinName; - FConnectedPin FromPin; - }; - - static TArray DeferredTriggerInputs; - static bool bIsFlushing = false; -} - IFlowExecutionGate* FFlowExecutionGate::Gate = nullptr; void FFlowExecutionGate::SetGate(IFlowExecutionGate* InGate) @@ -34,101 +20,4 @@ IFlowExecutionGate* FFlowExecutionGate::GetGate() bool FFlowExecutionGate::IsHalted() { return (Gate != nullptr) && Gate->IsFlowExecutionHalted(); -} - -bool FFlowExecutionGate::EnqueueDeferredTriggerInput(UFlowAsset* FlowAssetInstance, const FGuid& NodeGuid, const FName& PinName, const FConnectedPin& FromPin) -{ - using namespace FlowExecutionGate_Private; - - // If we're halted, always enqueue (even during flushing). The whole point is to stop propagation. - if (IsHalted()) - { - if (!IsValid(FlowAssetInstance)) - { - return true; // treat as handled while halted - } - - FDeferredTriggerInput& Entry = DeferredTriggerInputs.AddDefaulted_GetRef(); - Entry.FlowAssetInstance = FlowAssetInstance; - Entry.NodeGuid = NodeGuid; - Entry.PinName = PinName; - Entry.FromPin = FromPin; - - return true; - } - - // Not halted: - // During flush we must not enqueue "normal" triggers (we want them to execute now), - // otherwise we can get infinite deferral. - if (bIsFlushing) - { - return false; - } - - return false; -} - -void FFlowExecutionGate::FlushDeferredTriggerInputs() -{ - using namespace FlowExecutionGate_Private; - - if (bIsFlushing) - { - return; - } - - // Do not flush while halted; callers should clear the halt first. - if (IsHalted()) - { - return; - } - - if (DeferredTriggerInputs.IsEmpty()) - { - return; - } - - bIsFlushing = true; - - // Move into a local array so new deferred triggers can be added while we flush. - TArray Local = MoveTemp(DeferredTriggerInputs); - DeferredTriggerInputs.Reset(); - - for (int32 Index = 0; Index < Local.Num(); ++Index) - { - // If a breakpoint was hit during this flush, stop immediately and re-queue remaining work. - if (IsHalted()) - { - const int32 Remaining = Local.Num() - Index; - if (Remaining > 0) - { - TArray RemainingItems; - RemainingItems.Reserve(Remaining); - - for (int32 j = Index; j < Local.Num(); ++j) - { - RemainingItems.Add(Local[j]); - } - - // RemainingItems should run before any items that may already be queued. - if (!DeferredTriggerInputs.IsEmpty()) - { - RemainingItems.Append(MoveTemp(DeferredTriggerInputs)); - } - - DeferredTriggerInputs = MoveTemp(RemainingItems); - } - - bIsFlushing = false; - return; - } - - const FDeferredTriggerInput& Entry = Local[Index]; - if (UFlowAsset* Asset = Entry.FlowAssetInstance.Get()) - { - Asset->TriggerDeferredInputFromDebugger(Entry.NodeGuid, Entry.PinName, Entry.FromPin); - } - } - - bIsFlushing = false; } \ No newline at end of file diff --git a/Source/Flow/Private/Nodes/Developer/FlowNode_Log.cpp b/Source/Flow/Private/Nodes/Developer/FlowNode_Log.cpp index 794625eed..10bb0ecf7 100644 --- a/Source/Flow/Private/Nodes/Developer/FlowNode_Log.cpp +++ b/Source/Flow/Private/Nodes/Developer/FlowNode_Log.cpp @@ -33,7 +33,7 @@ void UFlowNode_Log::ExecuteInput(const FName& PinName) const EFlowDataPinResolveResult MessageResult = TryResolveDataPinValue(GET_MEMBER_NAME_CHECKED(ThisClass, Message), ResolvedMessage); // #FlowDataPinLegacy - retire this backward compatibility when we remove legacy data pin support? - FLOW_ASSERT_ENUM_MAX(EFlowDataPinResolveResult, 8); + FLOW_ASSERT_ENUM_MAX(EFlowDataPinResolveResult, 9); if (MessageResult == EFlowDataPinResolveResult::FailedUnknownPin) { // Handle lookup of a FlowNode_Log that predated DataPins @@ -82,19 +82,8 @@ void UFlowNode_Log::ExecuteInput(const FName& PinName) void UFlowNode_Log::PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChainEvent) { const auto& Property = PropertyChainEvent.PropertyChain.GetActiveMemberNode()->GetValue(); - - if (Property->GetFName() == GET_MEMBER_NAME_CHECKED(ThisClass, NamedProperties)) - { - for (FFlowNamedDataPinProperty& NamedProperty : NamedProperties) - { - const UScriptStruct* ScriptStruct = NamedProperty.DataPinValue.GetScriptStruct(); - if (IsValid(ScriptStruct) && ScriptStruct->IsChildOf()) - { - FFlowDataPinValue& Value = NamedProperty.DataPinValue.GetMutable(); - Value.bIsInputPin = true; - } - } - } + constexpr bool bIsInput = true; + OnPostEditEnsureAllNamedPropertiesPinDirection(*Property, bIsInput); Super::PostEditChangeChainProperty(PropertyChainEvent); } diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index aa319b35a..652f23bdd 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -417,7 +417,7 @@ void UFlowNode::SetAutoOutputDataPins(const TArray& AutoOutputPins) #endif // WITH_EDITOR -FFlowDataPinResult UFlowNode::TrySupplyDataPin_Implementation(FName PinName) const +FFlowDataPinResult UFlowNode::TrySupplyDataPin(FName PinName) const { const FFlowPin* FlowPin = FindOutputPinByName(PinName); if (!FlowPin) @@ -483,9 +483,20 @@ bool UFlowNode::TryFindPropertyByPinName_Static( void UFlowNode::GatherPotentialPropertyOwnersForDataPins(TArray& InOutOwners) const { - // TODO (gtaylor) Also add any AddOns that can supply data pins, when/if we want to add AddOn data pin supply support + check(!InOutOwners.Contains(this)); - InOutOwners.AddUnique(this); + InOutOwners.Add(this); + + // Give all of the AddOns a chance to supply data pins as well + (void) ForEachAddOnConst( + [&](const UFlowNodeAddOn& AddOn) + { + check(!InOutOwners.Contains(&AddOn)); + + InOutOwners.Add(&AddOn); + + return EFlowForEachAddOnFunctionReturnValue::Continue; + }); } bool UFlowNode::TryGatherPropertyOwnersAndPopulateResult( @@ -494,22 +505,66 @@ bool UFlowNode::TryGatherPropertyOwnersAndPopulateResult( const FFlowPin& FlowPin, FFlowDataPinResult& OutSuppliedResult) const { - // Gather all of the potential providers for this DataPin + // Gather all potential UObject instances that might own properties + // mapped to data pins on this node (usually the node itself + any referenced objects) TArray PropertyOwnerObjects; GatherPotentialPropertyOwnersForDataPins(PropertyOwnerObjects); - // Look through all of the potential providers - for (const UObject* PropertyOwnerObject : PropertyOwnerObjects) + // Early out if we have no possible owners at all + if (PropertyOwnerObjects.IsEmpty()) { - const UFlowNode& FlowNodeThis = *this; + LogError(FString::Printf(TEXT("No property owners available for data pin '%s' on node %s"), + *PinName.ToString(), *GetName()), EFlowOnScreenMessageType::Temporary); - checkf(IsValid(PropertyOwnerObject), TEXT("Every UObject provided by GatherPotentialPropertyOwnersForDataPins must be valid")); + return false; + } + + const UObject* PropertyOwnerObject = nullptr; + FName PropertyNameToLookup; + + // Look up explicit mapping (used for non-default owners or disambiguated pins) + if (const FFlowPinPropertySource* FlowPropertySource = MapDataPinNameToPropertySource.Find(PinName)) + { + const int32 OwnerIndex = FlowPropertySource->PropertyOwnerIndex; - if (DataPinType.PopulateResult(*PropertyOwnerObject, FlowNodeThis, FlowPin, OutSuppliedResult)) + if (PropertyOwnerObjects.IsValidIndex(OwnerIndex)) { - return true; + PropertyOwnerObject = PropertyOwnerObjects[OwnerIndex]; + PropertyNameToLookup = FlowPropertySource->PropertyName; + } + else + { + // Critical: mapped index is out of bounds → configuration or generation bug + LogError(FString::Printf(TEXT("Invalid property owner index %d for pin '%s' on node %s (max %d owners)"), + OwnerIndex, *PinName.ToString(), *GetName(), PropertyOwnerObjects.Num() - 1), + EFlowOnScreenMessageType::Temporary); + + return false; } } + else + { + check(!PropertyOwnerObjects.IsEmpty()); + + // Fallback for unmapped pins → assume default owner (index 0) + pin name == property name + PropertyOwnerObject = PropertyOwnerObjects[0]; + PropertyNameToLookup = PinName; + } + + if (!PropertyOwnerObject) + { + LogError(FString::Printf(TEXT("Failed to resolve property owner for data pin '%s' on node %s"), + *PinName.ToString(), *GetName()), EFlowOnScreenMessageType::Temporary); + + return false; + } + + // Populate the value for the pin on the its owner object + const UFlowNode& FlowNodeThis = *this; + if (DataPinType.PopulateResult(*PropertyOwnerObject, FlowNodeThis, PropertyNameToLookup, OutSuppliedResult)) + { + return true; + } return false; } @@ -530,15 +585,10 @@ bool UFlowNode::TryGetFlowDataPinSupplierDatasForPinName(const FName& PinName, T // Potentially add this current node as a default value supplier // (this will be pushed down the priority queue as higher priority suppliers are found) - if (ThisAsPinValueSupplier && IFlowDataPinValueSupplierInterface::Execute_CanSupplyDataPinValues(this)) - { - FFlowPinValueSupplierData NewPinValueSupplier; - NewPinValueSupplier.PinValueSupplier = ThisAsPinValueSupplier; - NewPinValueSupplier.SupplierPinName = PinName; - - // Put this node as the backup supplier - InOutPinValueSupplierDatas.Add(NewPinValueSupplier); - } + FFlowPinValueSupplierData NewPinValueSupplier; + NewPinValueSupplier.PinValueSupplier = ThisAsPinValueSupplier; + NewPinValueSupplier.SupplierPinName = PinName; + TryAddSupplierDataToArray(NewPinValueSupplier, InOutPinValueSupplierDatas); // If the pin is connected, try to add the connected node as the priority supplier FFlowPinValueSupplierData ConnectedPinValueSupplier; @@ -550,23 +600,11 @@ bool UFlowNode::TryGetFlowDataPinSupplierDatasForPinName(const FName& PinName, T { const UFlowNode* SupplierFlowNode = FlowAsset->GetNode(ConnectedNodeGuid); - // If the connected node can supply data pin values, insert it into the top of the priority queue - const IFlowDataPinValueSupplierInterface* SupplierFlowNodeAsInterface = Cast(SupplierFlowNode); - if (SupplierFlowNodeAsInterface && IFlowDataPinValueSupplierInterface::Execute_CanSupplyDataPinValues(SupplierFlowNode)) + if (IsValid(SupplierFlowNode)) { - ConnectedPinValueSupplier.PinValueSupplier = SupplierFlowNodeAsInterface; - - InOutPinValueSupplierDatas.Add(ConnectedPinValueSupplier); - } + ConnectedPinValueSupplier.PinValueSupplier = Cast(SupplierFlowNode); - // Exception case for nodes with external suppliers, recurse here to crawl further - // to the external supplier's connected pin as our most preferred source (see block comment above). - if (const IFlowNodeWithExternalDataPinSupplierInterface* HasExternalPinSupplierInterface = Cast(SupplierFlowNode)) - { - if (const UFlowNode* ExternalDataPinSupplierFlowNode = Cast(HasExternalPinSupplierInterface->GetExternalDataPinSupplier())) - { - return ExternalDataPinSupplierFlowNode->TryGetFlowDataPinSupplierDatasForPinName(ConnectedPinValueSupplier.SupplierPinName, InOutPinValueSupplierDatas); - } + TryAddSupplierDataToArray(ConnectedPinValueSupplier, InOutPinValueSupplierDatas); } } } @@ -574,6 +612,26 @@ bool UFlowNode::TryGetFlowDataPinSupplierDatasForPinName(const FName& PinName, T return !InOutPinValueSupplierDatas.IsEmpty(); } +void UFlowNode::TryAddSupplierDataToArray(FFlowPinValueSupplierData& InOutSupplierData, TFlowPinValueSupplierDataArray& InOutPinValueSupplierDatas) const +{ + // If the connected node can supply data pin values, insert it into the top of the priority queue + const UFlowNode* SupplierFlowNode = CastChecked(InOutSupplierData.PinValueSupplier); + if (InOutSupplierData.PinValueSupplier && SupplierFlowNode->CanSupplyDataPinValues()) + { + InOutPinValueSupplierDatas.Add(InOutSupplierData); + } + + // Exception case for nodes with external suppliers, recurse here to crawl further + // to the external supplier's connected pin as our most preferred source (see block comment above). + if (const IFlowNodeWithExternalDataPinSupplierInterface* HasExternalPinSupplierInterface = Cast(SupplierFlowNode)) + { + if (const UFlowNode* ExternalDataPinSupplierFlowNode = Cast(HasExternalPinSupplierInterface->GetExternalDataPinSupplier())) + { + ExternalDataPinSupplierFlowNode->TryGetFlowDataPinSupplierDatasForPinName(InOutSupplierData.SupplierPinName, InOutPinValueSupplierDatas); + } + } +} + #if WITH_EDITOR void UFlowNode::AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const { @@ -582,11 +640,13 @@ void UFlowNode::AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingD GatherPotentialPropertyOwnersForDataPins(PropertyOwnerObjects); // GenerateDataPins for all of the potential providers - for (const UObject* PropertyOwnerObject : PropertyOwnerObjects) + for (int32 PropertyOwnerIndex = 0; PropertyOwnerIndex < PropertyOwnerObjects.Num(); ++PropertyOwnerIndex) { + const UObject* PropertyOwnerObject = PropertyOwnerObjects[PropertyOwnerIndex]; + checkf(IsValid(PropertyOwnerObject), TEXT("Every UObject provided by GatherPotentialPropertyOwnersForDataPins must be valid")); - InOutWorkingData.AddFlowDataPinsForClassProperties(*PropertyOwnerObject); + InOutWorkingData.AddFlowDataPinsForClassProperties(*PropertyOwnerObject, PropertyOwnerIndex); } } #endif @@ -712,7 +772,7 @@ FFlowPin* UFlowNode::FindOutputPinByName(const FName& PinName) return nullptr; } -bool UFlowNode::IsInputConnected(const FFlowPin& FlowPin) const +bool UFlowNode::IsInputConnected(const FFlowPin& FlowPin, FGuid* FoundGuid, FName* OutConnectedPinName) const { if (!InputPins.Contains(FlowPin.PinName)) { @@ -723,15 +783,15 @@ bool UFlowNode::IsInputConnected(const FFlowPin& FlowPin) const { // We don't cache the input exec pins for fast lookup in Connections, so use the slow path for them: - return FindConnectedNodeForPinSlow(FlowPin.PinName); + return FindConnectedNodeForPinSlow(FlowPin.PinName, FoundGuid, OutConnectedPinName); } else { - return FindConnectedNodeForPinFast(FlowPin.PinName); + return FindConnectedNodeForPinFast(FlowPin.PinName, FoundGuid, OutConnectedPinName); } } -bool UFlowNode::IsOutputConnected(const FFlowPin& FlowPin) const +bool UFlowNode::IsOutputConnected(const FFlowPin& FlowPin, FGuid* FirstFoundGuid, FName* OutFirstConnectedPinName) const { if (!OutputPins.Contains(FlowPin.PinName)) { @@ -740,13 +800,13 @@ bool UFlowNode::IsOutputConnected(const FFlowPin& FlowPin) const if (FlowPin.IsExecPin()) { - return FindConnectedNodeForPinFast(FlowPin.PinName); + return FindConnectedNodeForPinFast(FlowPin.PinName, FirstFoundGuid, OutFirstConnectedPinName); } else { // We don't cache the input data pins for fast lookup in Connections, so use the slow path for them: - return FindConnectedNodeForPinSlow(FlowPin.PinName); + return FindConnectedNodeForPinSlow(FlowPin.PinName, FirstFoundGuid, OutFirstConnectedPinName); } } diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp index 56360b9a1..8f60f3e4e 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBase.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -1020,7 +1020,7 @@ FFlowDataPinResult UFlowNodeBase::TryResolveDataPin(FName PinName) const { const FFlowPinValueSupplierData& SupplierData = PinValueSupplierDatas[Index]; - DataPinResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPin(CastChecked(SupplierData.PinValueSupplier), SupplierData.SupplierPinName); + DataPinResult = SupplierData.PinValueSupplier->TrySupplyDataPin(SupplierData.SupplierPinName); if (FlowPinType::IsSuccess(DataPinResult.Result)) { diff --git a/Source/Flow/Private/Nodes/FlowPin.cpp b/Source/Flow/Private/Nodes/FlowPin.cpp index 8c2a34cd5..41581cc3c 100644 --- a/Source/Flow/Private/Nodes/FlowPin.cpp +++ b/Source/Flow/Private/Nodes/FlowPin.cpp @@ -149,6 +149,13 @@ FEdGraphPinType FFlowPin::BuildEdGraphPinType() const return EdGraphPinType; } + +void FFlowPin::ConfigureFromEdGraphPin(const FEdGraphPinType& EdGraphPinType) +{ + PinTypeName.Name = EdGraphPinType.PinCategory; + PinSubCategoryObject = EdGraphPinType.PinSubCategoryObject; + ContainerType = EdGraphPinType.ContainerType; +} #endif const FFlowPinType* FFlowPin::ResolveFlowPinType() const @@ -199,42 +206,9 @@ FFlowPinTypeName FFlowPin::GetPinTypeNameForLegacyPinType(EFlowPinType PinType) return FFlowPinTypeName(); } } +// -- #if WITH_EDITOR -void FFlowPin::PostEditChangedPinTypeOrSubCategorySource() -{ - // PinTypes with PinSubCategoryObjects will need to update this function - - // Must be called from PostEditChangeProperty() by an owning UObject - - if (PinTypeName == FFlowPinType_Class::GetPinTypeNameStatic()) - { - PinSubCategoryObject = SubCategoryClassFilter; - } - else if (PinTypeName == FFlowPinType_Object::GetPinTypeNameStatic()) - { - PinSubCategoryObject = SubCategoryObjectFilter; - } - else if (PinTypeName == FFlowPinType_Enum::GetPinTypeNameStatic()) - { - if (!SubCategoryEnumName.IsEmpty()) - { - SubCategoryEnumClass = UClass::TryFindTypeSlow(SubCategoryEnumName, EFindFirstObjectOptions::ExactClass); - if (SubCategoryEnumClass != nullptr && !FFlowPin::ValidateEnum(*SubCategoryEnumClass)) - { - SubCategoryEnumClass = nullptr; - } - } - - PinSubCategoryObject = SubCategoryEnumClass; - } - else - { - TrySetStructSubCategoryObjectFromPinType(); - } -} - -// -- FText FFlowPin::BuildHeaderText() const { diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_BlueprintDataPinSupplierBase.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_BlueprintDataPinSupplierBase.cpp new file mode 100644 index 000000000..d1011182c --- /dev/null +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_BlueprintDataPinSupplierBase.cpp @@ -0,0 +1,26 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Nodes/Graph/FlowNode_BlueprintDataPinSupplierBase.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_BlueprintDataPinSupplierBase) + +UFlowNode_BlueprintDataPinSupplierBase::UFlowNode_BlueprintDataPinSupplierBase(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ +#if WITH_EDITOR + NodeDisplayStyle = FlowNodeStyle::Default; + Category = TEXT("Graph"); +#endif + + AllowedSignalModes = {EFlowSignalMode::Enabled, EFlowSignalMode::Disabled}; +} + +FFlowDataPinResult UFlowNode_BlueprintDataPinSupplierBase::TrySupplyDataPin(FName PinName) const +{ + return BP_TrySupplyDataPin(PinName); +} + +FFlowDataPinResult UFlowNode_BlueprintDataPinSupplierBase::BP_TrySupplyDataPin_Implementation(FName PinName) const +{ + return Super::TrySupplyDataPin(PinName); +} diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_DefineProperties.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_DefineProperties.cpp index 6d453eaf0..6b116310c 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_DefineProperties.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_DefineProperties.cpp @@ -58,6 +58,22 @@ bool UFlowNode_DefineProperties::TryFindPropertyByPinName( } #if WITH_EDITOR +void UFlowNode_DefineProperties::OnPostEditEnsureAllNamedPropertiesPinDirection(const FProperty& Property, bool bIsInput) +{ + if (Property.GetFName() == GET_MEMBER_NAME_CHECKED(ThisClass, NamedProperties)) + { + for (FFlowNamedDataPinProperty& NamedProperty : NamedProperties) + { + const UScriptStruct* ScriptStruct = NamedProperty.DataPinValue.GetScriptStruct(); + if (IsValid(ScriptStruct) && ScriptStruct->IsChildOf()) + { + FFlowDataPinValue& Value = NamedProperty.DataPinValue.GetMutable(); + Value.bIsInputPin = bIsInput; + } + } + } +} + void UFlowNode_DefineProperties::AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const { Super::AutoGenerateDataPins(InOutWorkingData); @@ -67,14 +83,16 @@ void UFlowNode_DefineProperties::AutoGenerateDataPins(FFlowAutoDataPinsWorkingDa if (DataPinProperty.IsValid()) { const FFlowDataPinValue& DataPinValue = DataPinProperty.DataPinValue.Get(); + const FName PropertyOwnerObjectName = GetFName(); + constexpr int32 PropertyOwnerIndex = 0; if (DataPinValue.IsInputPin()) { - InOutWorkingData.AutoInputDataPinsNext.AddUnique(DataPinProperty.CreateFlowPin()); + InOutWorkingData.AutoInputDataPinsNext.Add(FFlowPinSourceData(DataPinProperty.CreateFlowPin(), PropertyOwnerObjectName, PropertyOwnerIndex, &DataPinValue)); } else { - InOutWorkingData.AutoOutputDataPinsNext.AddUnique(DataPinProperty.CreateFlowPin()); + InOutWorkingData.AutoOutputDataPinsNext.Add(FFlowPinSourceData(DataPinProperty.CreateFlowPin(), PropertyOwnerObjectName, PropertyOwnerIndex, &DataPinValue)); } } } diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_FormatText.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_FormatText.cpp index 13c055433..5da5f18b3 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_FormatText.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_FormatText.cpp @@ -20,7 +20,7 @@ UFlowNode_FormatText::UFlowNode_FormatText(const FObjectInitializer& ObjectIniti OutputPins.Add(FFlowPin(OUTPIN_TextOutput, FFlowPinType_Text::GetPinTypeNameStatic())); } -FFlowDataPinResult UFlowNode_FormatText::TrySupplyDataPin_Implementation(FName PinName) const +FFlowDataPinResult UFlowNode_FormatText::TrySupplyDataPin(FName PinName) const { if (PinName == OUTPIN_TextOutput) { @@ -37,7 +37,7 @@ FFlowDataPinResult UFlowNode_FormatText::TrySupplyDataPin_Implementation(FName P } } - return Super::TrySupplyDataPin_Implementation(PinName); + return Super::TrySupplyDataPin(PinName); } EFlowDataPinResolveResult UFlowNode_FormatText::TryResolveFormatText(const FName& PinName, FText& OutFormattedText) const @@ -55,6 +55,14 @@ EFlowDataPinResolveResult UFlowNode_FormatText::TryResolveFormatText(const FName } #if WITH_EDITOR +void UFlowNode_FormatText::PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChainEvent) +{ + const auto& Property = PropertyChainEvent.PropertyChain.GetActiveMemberNode()->GetValue(); + constexpr bool bIsInput = true; + OnPostEditEnsureAllNamedPropertiesPinDirection(*Property, bIsInput); + + Super::PostEditChangeChainProperty(PropertyChainEvent); +} void UFlowNode_FormatText::UpdateNodeConfigText_Implementation() { diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_Start.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_Start.cpp index b8f399b78..81683dfc9 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_Start.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_Start.cpp @@ -44,11 +44,11 @@ bool UFlowNode_Start::TryAppendExternalInputPins(TArray& InOutPins) co #endif // WITH_EDITOR -FFlowDataPinResult UFlowNode_Start::TrySupplyDataPin_Implementation(FName PinName) const +FFlowDataPinResult UFlowNode_Start::TrySupplyDataPin(FName PinName) const { if (FlowDataPinValueSupplierInterface) { - FFlowDataPinResult SuppliedResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPin(FlowDataPinValueSupplierInterface.GetObject(), PinName); + FFlowDataPinResult SuppliedResult = FlowDataPinValueSupplierInterface->TrySupplyDataPin(PinName); if (FlowPinType::IsSuccess(SuppliedResult.Result)) { @@ -56,6 +56,6 @@ FFlowDataPinResult UFlowNode_Start::TrySupplyDataPin_Implementation(FName PinNam } } - return Super::TrySupplyDataPin_Implementation(PinName); + return Super::TrySupplyDataPin(PinName); } diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp index 9e74acc08..cdb0a7e4a 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp @@ -14,6 +14,7 @@ FFlowPin UFlowNode_SubGraph::StartPin(TEXT("Start")); FFlowPin UFlowNode_SubGraph::FinishPin(TEXT("Finish")); +const FName UFlowNode_SubGraph::AssetParams_MemberName = GET_MEMBER_NAME_CHECKED(ThisClass, AssetParams); UFlowNode_SubGraph::UFlowNode_SubGraph(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) @@ -212,7 +213,15 @@ void UFlowNode_SubGraph::AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOu TArray ExternalInputPins; if (ExternalPinSuppliedNode->TryAppendExternalInputPins(ExternalInputPins)) { - InOutWorkingData.AutoInputDataPinsNext.Append(ExternalInputPins); + const int32 NewNum = InOutWorkingData.AutoInputDataPinsNext.Num() + ExternalInputPins.Num(); + InOutWorkingData.AutoInputDataPinsNext.Reserve(NewNum); + + const FName PropertyOwnerObjectName = GetFName(); + + for (const FFlowPin& FlowPin : ExternalInputPins) + { + InOutWorkingData.AutoInputDataPinsNext.Add(FFlowPinSourceData(FlowPin, PropertyOwnerObjectName)); + } } } } @@ -249,10 +258,43 @@ void UFlowNode_SubGraph::PostEditChangeProperty(FPropertyChangedEvent& PropertyC } } -bool UFlowNode_SubGraph::CanSupplyDataPinValues_Implementation() const +FFlowDataPinResult UFlowNode_SubGraph::TrySupplyDataPin(FName PinName) const { - // SubGraph node cannot supply data-pin values directly (they are created via AutoGenerateDataPins instead) - return false; + if (PinName == AssetParams_MemberName) + { + // Prevent infinite recursion by sourcing the AssetParams pin directly + // (otherwise, it would attempt to resolve it below and infinitely crash our stack. + // don't ask me how I know). + return Super::TrySupplyDataPin(PinName); + } + + if (!IsInputConnected(PinName)) + { + const bool bHasAssetParams = IsInputConnected(AssetParams_MemberName) || !AssetParams.IsNull(); + if (bHasAssetParams) + { + // If not connected, we can source the value from the asset data params (if available) + TObjectPtr Value = nullptr; + const EFlowDataPinResolveResult ResultEnum = Super::TryResolveDataPinValue(AssetParams_MemberName, Value); + if (FlowPinType::IsSuccess(ResultEnum) && IsValid(Value)) + { + if (const IFlowDataPinValueSupplierInterface* SupplierInterface = Cast(Value)) + { + return SupplierInterface->TrySupplyDataPin(PinName); + } + else + { + LogError(FString::Printf(TEXT("Could not cast object %s to IFlowDataPinValueSupplierInterface! This is unexpected."), *Value->GetName())); + + return FFlowDataPinResult(EFlowDataPinResolveResult::FailedWithError); + } + } + } + } + + // Prefer the standard lookup if the pin is connected + // (or if there is no FlowAssetParams to ask) + return Super::TrySupplyDataPin(PinName); } void UFlowNode_SubGraph::SubscribeToAssetChanges() diff --git a/Source/Flow/Private/Nodes/Route/FlowNode_Reroute.cpp b/Source/Flow/Private/Nodes/Route/FlowNode_Reroute.cpp index 2c24b9900..d04c1a87c 100644 --- a/Source/Flow/Private/Nodes/Route/FlowNode_Reroute.cpp +++ b/Source/Flow/Private/Nodes/Route/FlowNode_Reroute.cpp @@ -1,6 +1,7 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #include "Nodes/Route/FlowNode_Reroute.h" +#include "FlowAsset.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_Reroute) @@ -18,3 +19,48 @@ void UFlowNode_Reroute::ExecuteInput(const FName& PinName) { TriggerFirstOutput(true); } + +#if WITH_EDITOR +void UFlowNode_Reroute::ConfigureInputPin(const UFlowNode& ConnectedNode, const FEdGraphPinType& EdGraphPinType) +{ + FFlowPin* InputPin = FindInputPinByName(UFlowNode::DefaultInputPin.PinName); + check(InputPin); + + InputPin->ConfigureFromEdGraphPin(EdGraphPinType); +} + +void UFlowNode_Reroute::ConfigureOutputPin(const UFlowNode& ConnectedNode, const FEdGraphPinType& EdGraphPinType) +{ + FFlowPin* OutputPin = FindOutputPinByName(UFlowNode::DefaultOutputPin.PinName); + check(OutputPin); + + OutputPin->ConfigureFromEdGraphPin(EdGraphPinType); +} +#endif + +FFlowDataPinResult UFlowNode_Reroute::TrySupplyDataPin(FName PinName) const +{ + const FFlowPin* InputPin = FindInputPinByName(UFlowNode::DefaultInputPin.PinName); + if (!InputPin) + { + return FFlowDataPinResult(EFlowDataPinResolveResult::FailedUnknownPin); + } + + FGuid FoundGuid; + FName ConnectedPinName; + if (!IsInputConnected(*InputPin, &FoundGuid, &ConnectedPinName)) + { + return FFlowDataPinResult(EFlowDataPinResolveResult::FailedNotConnected); + } + + const UFlowNode* ConnectedFlowNodeSupplier = GetFlowAsset()->GetNode(FoundGuid); + if (!IsValid(ConnectedFlowNodeSupplier)) + { + checkf(IsValid(ConnectedFlowNodeSupplier), TEXT("This node should be valid if IsInputConnected returned true")); + + return FFlowDataPinResult(EFlowDataPinResolveResult::FailedNotConnected); + } + + // Hand-off to the connected flow node to supply the value + return ConnectedFlowNodeSupplier->TrySupplyDataPin(ConnectedPinName); +} diff --git a/Source/Flow/Private/Types/FlowAutoDataPinsWorkingData.cpp b/Source/Flow/Private/Types/FlowAutoDataPinsWorkingData.cpp index 78107aa17..fd3fba57c 100644 --- a/Source/Flow/Private/Types/FlowAutoDataPinsWorkingData.cpp +++ b/Source/Flow/Private/Types/FlowAutoDataPinsWorkingData.cpp @@ -2,31 +2,75 @@ #include "Types/FlowAutoDataPinsWorkingData.h" #include "FlowLogChannels.h" +#include "Nodes/FlowNode.h" #include "Types/FlowDataPinValue.h" #include "Types/FlowStructUtils.h" #if WITH_EDITOR + +bool FFlowAutoDataPinsWorkingData::AutoGenerateDataPinsForFlowNode(UFlowNode& FlowNode, bool& bAutoInputDataPinsChanged, bool& bAutoOutputDataPinsChanged) +{ + // Allow the node to auto-generate data pins + FlowNode.AutoGenerateDataPins(*this); + + DisambiguateAndRebuildDataPinPropertySourceMap(FlowNode); + + bAutoInputDataPinsChanged = DidAutoInputDataPinsChange(); + bAutoOutputDataPinsChanged = DidAutoOutputDataPinsChange(); + + // Return true if any of the data pins changed + return bAutoInputDataPinsChanged || bAutoOutputDataPinsChanged; +} + bool FFlowAutoDataPinsWorkingData::DidAutoInputDataPinsChange() const { - return !FFlowPin::DeepArePinArraysMatching(AutoInputDataPinsPrev, AutoInputDataPinsNext); + return !CheckIfProposedPinsMatchPreviousPins(AutoInputDataPinsPrev, AutoInputDataPinsNext); } bool FFlowAutoDataPinsWorkingData::DidAutoOutputDataPinsChange() const { - return !FFlowPin::DeepArePinArraysMatching(AutoOutputDataPinsPrev, AutoOutputDataPinsNext); + return !CheckIfProposedPinsMatchPreviousPins(AutoOutputDataPinsPrev, AutoOutputDataPinsNext); +} + +bool FFlowAutoDataPinsWorkingData::CheckIfProposedPinsMatchPreviousPins(const TArray& PrevPins, const TArray& ProposedPins) +{ + if (PrevPins.Num() != ProposedPins.Num()) + { + return false; + } + + for (int32 Index = 0; Index < PrevPins.Num(); ++Index) + { + if (!PrevPins[Index].DeepIsEqual(ProposedPins[Index].FlowPin)) + { + return false; + } + } + + return true; +} + +void FFlowAutoDataPinsWorkingData::BuildNextFlowPinArray(const TArray& PinSourceDatas, TArray& OutFlowPins) +{ + OutFlowPins.Reset(); + + for (const FFlowPinSourceData& PinSourceData : PinSourceDatas) + { + OutFlowPins.Add(PinSourceData.FlowPin); + } } -void FFlowAutoDataPinsWorkingData::AddFlowDataPinsForClassProperties(const UObject& ObjectContainer) +void FFlowAutoDataPinsWorkingData::AddFlowDataPinsForClassProperties(const UObject& ObjectContainer, int32 PropertyOwnerIndex) { // Try to harvest pins to auto-generate and/or bind to for each property in the flow node UClass* Class = ObjectContainer.GetClass(); for (TFieldIterator PropertyIt(Class); PropertyIt; ++PropertyIt) { - AddFlowDataPinForProperty(*PropertyIt, ObjectContainer); + AddFlowDataPinForProperty(*PropertyIt, ObjectContainer, PropertyOwnerIndex); } } -void FFlowAutoDataPinsWorkingData::AddFlowDataPinForProperty(const FProperty* Property, const UObject& ObjectContainer) +void FFlowAutoDataPinsWorkingData::AddFlowDataPinForProperty(const FProperty* Property, const UObject& ObjectContainer, int32 PropertyOwnerIndex) { bool bIsInputPin = false; @@ -94,7 +138,7 @@ void FFlowAutoDataPinsWorkingData::AddFlowDataPinForProperty(const FProperty* Pr bIsInputPin = bIsInputPin || DefaultForInputFlowPinName != nullptr; // Default assumption is the pin will be an output pin, unless metadata specifies otherwise - TArray* FlowPinArray = bIsInputPin ? &AutoInputDataPinsNext : &AutoOutputDataPinsNext; + TArray* FlowPinArray = bIsInputPin ? &AutoInputDataPinsNext : &AutoOutputDataPinsNext; // Create the new FlowPin FFlowPin NewFlowPin = FlowPinType->CreateFlowPinFromProperty(*Property, Container); @@ -117,7 +161,8 @@ void FFlowAutoDataPinsWorkingData::AddFlowDataPinForProperty(const FProperty* Pr } } - FlowPinArray->Add(NewFlowPin); + const FName PropertyOwnerObjectName = ObjectContainer.GetFName(); + FlowPinArray->Add(FFlowPinSourceData(NewFlowPin, PropertyOwnerObjectName, PropertyOwnerIndex, DataPinValue)); if (DataPinValue) { @@ -126,4 +171,183 @@ void FFlowAutoDataPinsWorkingData::AddFlowDataPinForProperty(const FProperty* Pr } } +void FFlowAutoDataPinsWorkingData::DisambiguateAndRebuildDataPinPropertySourceMap(UFlowNode& FlowNode) +{ + FlowNode.MapDataPinNameToPropertySource.Reset(); + + AddInputDataPinsToMap(FlowNode); + AddOutputDataPinsToMap(FlowNode); +} + +void FFlowAutoDataPinsWorkingData::AddInputDataPinsToMap(UFlowNode& FlowNode) +{ + for (const FFlowPinSourceData& PinSource : AutoInputDataPinsNext) + { + if (PinSource.FlowPin.IsExecPin()) + { + continue; + } + + if (PinSource.PropertyOwnerIndex == 0) + { + // default owner is inferred at runtime + continue; + } + + AddPinMappingToNode( + FlowNode, + PinSource.FlowPin.PinName, + PinSource.FlowPin.PinName, + PinSource.PropertyOwnerIndex); + } +} + +void FFlowAutoDataPinsWorkingData::AddOutputDataPinsToMap(UFlowNode& FlowNode) +{ + if (AutoOutputDataPinsNext.IsEmpty()) + { + return; + } + + // Group output pins by their original/base name to detect duplicates + TMap> MapPinNameToNextPinIndices; + for (int32 Idx = 0; Idx < AutoOutputDataPinsNext.Num(); ++Idx) + { + const FFlowPinSourceData& Pin = AutoOutputDataPinsNext[Idx]; + + const bool bIsDataPin = !Pin.FlowPin.IsExecPin(); + if (bIsDataPin) + { + TArray& UseCountArray = MapPinNameToNextPinIndices.FindOrAdd(Pin.FlowPin.PinName); + UseCountArray.Add(Idx); + } + } + + // Collect names that will remain unchanged (for collision avoidance during renaming) + TSet ReservedFinalNames; + for (const auto& KV : MapPinNameToNextPinIndices) + { + const TArray& Indices = KV.Value; + if (Indices.Num() == 1) + { + const int32 Idx = Indices[0]; + ReservedFinalNames.Add(AutoOutputDataPinsNext[Idx].FlowPin.PinName); + } + } + + // Process each group: map unique pins, rename and map duplicates + for (const auto& KV : MapPinNameToNextPinIndices) + { + const TArray& Indices = KV.Value; + if (Indices.Num() == 1) + { + const int32 Idx = Indices[0]; + const FFlowPinSourceData& Pin = AutoOutputDataPinsNext[Idx]; + + const bool bCanOmitPropertyFromMap = Pin.PropertyOwnerIndex == 0; + if (!bCanOmitPropertyFromMap) + { + AddPinMappingToNode( + FlowNode, + Pin.FlowPin.PinName, + Pin.FlowPin.PinName, + Pin.PropertyOwnerIndex); + } + + continue; + } + + // Handle duplicate names — assign unique suffixes + TSet UsedNames = ReservedFinalNames; + + // 1-based for user-facing display + uint32 LogicalDuplicateIndex = 1; + + for (int32 Idx : Indices) + { + FFlowPinSourceData& Pin = AutoOutputDataPinsNext[Idx]; + const FName OriginalName = Pin.FlowPin.PinName; + + DisambiguateDuplicateOutputDataPin(Pin, UsedNames, LogicalDuplicateIndex); + + const FName& DisambiguatedName = Pin.FlowPin.PinName; + AddPinMappingToNode( + FlowNode, + DisambiguatedName, + OriginalName, + Pin.PropertyOwnerIndex); + + ++LogicalDuplicateIndex; + } + } +} + +void FFlowAutoDataPinsWorkingData::AddPinMappingToNode( + UFlowNode& FlowNode, + const FName& FinalPinName, + const FName& OriginalPinName, + int32 PropertyOwnerIndex) +{ + // Omit trivial cases that runtime lookup can infer + if (PropertyOwnerIndex == 0 && FinalPinName == OriginalPinName) + { + return; + } + + FlowNode.MapDataPinNameToPropertySource.Add( + FinalPinName, + FFlowPinPropertySource(OriginalPinName, PropertyOwnerIndex)); +} + +void FFlowAutoDataPinsWorkingData::DisambiguateDuplicateOutputDataPin( + FFlowPinSourceData& PinSourceData, + TSet& InOutUsedNames, + uint32 LogicalDuplicateIndex) +{ + const FName BaseName = PinSourceData.FlowPin.PinName; + + uint32 DisambiguationSuffix = LogicalDuplicateIndex; + while (true) + { + const FName Candidate = FName(FString::Printf(TEXT("%s_%u"), *BaseName.ToString(), DisambiguationSuffix)); + if (!InOutUsedNames.Contains(Candidate)) + { + PinSourceData.FlowPin.PinName = Candidate; + InOutUsedNames.Add(Candidate); + + break; + } + + ++DisambiguationSuffix; + + if (!ensure(DisambiguationSuffix < 1000000u)) + { + UE_LOG(LogFlow, Error, TEXT("Pin name disambiguation failed for %s"), *BaseName.ToString()); + break; + } + } + + // Apply friendly name with logical (1-based) duplicate index + const FString FriendlyStr = FString::Printf(TEXT("%s (%u)"), *PinSourceData.FlowPin.PinFriendlyName.ToString(), LogicalDuplicateIndex); + PinSourceData.FlowPin.PinFriendlyName = FText::FromString(FriendlyStr); + + // Enhance tooltip with source information + FString& Tooltip = PinSourceData.FlowPin.PinToolTip; + if (!PinSourceData.PropertyOwnerObjectName.IsNone()) + { + if (!Tooltip.IsEmpty()) + { + Tooltip += TEXT("\n"); + } + + Tooltip += FString::Printf(TEXT("Output of %s"), *PinSourceData.PropertyOwnerObjectName.ToString()); + } + + // Update blueprint-facing property name + if (PinSourceData.DataPinValue) + { + PinSourceData.DataPinValue->PropertyPinName = PinSourceData.FlowPin.PinName; + } +} + #endif diff --git a/Source/Flow/Private/Types/FlowPinType.cpp b/Source/Flow/Private/Types/FlowPinType.cpp index c73ffd183..39bc03252 100644 --- a/Source/Flow/Private/Types/FlowPinType.cpp +++ b/Source/Flow/Private/Types/FlowPinType.cpp @@ -33,7 +33,7 @@ bool FFlowPinType::ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FNa return false; } -bool FFlowPinType::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +bool FFlowPinType::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const { OutResult.Result = EFlowDataPinResolveResult::FailedMismatchedType; return false; diff --git a/Source/Flow/Private/Types/FlowPinTypesStandard.cpp b/Source/Flow/Private/Types/FlowPinTypesStandard.cpp index b959fe6d9..6dd857b04 100644 --- a/Source/Flow/Private/Types/FlowPinTypesStandard.cpp +++ b/Source/Flow/Private/Types/FlowPinTypesStandard.cpp @@ -36,32 +36,32 @@ const FFlowPinTypeName FFlowPinType_InstancedStruct::PinTypeNameInstancedStruct const FFlowPinTypeName FFlowPinType_Object::PinTypeNameObject = FFlowPinTypeName(FFlowPinTypeNamesStandard::PinTypeNameObject); const FFlowPinTypeName FFlowPinType_Class::PinTypeNameClass = FFlowPinTypeName(FFlowPinTypeNamesStandard::PinTypeNameClass); -bool FFlowPinType_Bool::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +bool FFlowPinType_Bool::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const { - return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, PropertyName, OutResult); } -bool FFlowPinType_Int::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +bool FFlowPinType_Int::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const { - return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, PropertyName, OutResult); } -bool FFlowPinType_Int64::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +bool FFlowPinType_Int64::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const { - return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, PropertyName, OutResult); } -bool FFlowPinType_Float::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +bool FFlowPinType_Float::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const { - return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, PropertyName, OutResult); } -bool FFlowPinType_Double::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +bool FFlowPinType_Double::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const { - return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, PropertyName, OutResult); } -bool FFlowPinType_Enum::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +bool FFlowPinType_Enum::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const { using TFlowPinType = FFlowPinType_Enum; using TValue = TFlowPinType::ValueType; @@ -71,7 +71,7 @@ bool FFlowPinType_Enum::PopulateResult(const UObject& PropertyOwnerObject, const TInstancedStruct ValueStruct; const FProperty* FoundProperty = nullptr; - if (!Node.TryFindPropertyByPinName(PropertyOwnerObject, Pin.PinName, FoundProperty, ValueStruct)) + if (!Node.TryFindPropertyByPinName(PropertyOwnerObject, PropertyName, FoundProperty, ValueStruct)) { OutResult.Result = EFlowDataPinResolveResult::FailedUnknownPin; return false; @@ -97,59 +97,59 @@ bool FFlowPinType_Enum::PopulateResult(const UObject& PropertyOwnerObject, const return false; } -bool FFlowPinType_Name::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +bool FFlowPinType_Name::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const { - return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, PropertyName, OutResult); } -bool FFlowPinType_String::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +bool FFlowPinType_String::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const { - return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, PropertyName, OutResult); } -bool FFlowPinType_Text::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +bool FFlowPinType_Text::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const { - return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, PropertyName, OutResult); } -bool FFlowPinType_Vector::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +bool FFlowPinType_Vector::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const { - return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, PropertyName, OutResult); } -bool FFlowPinType_Rotator::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +bool FFlowPinType_Rotator::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const { - return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, PropertyName, OutResult); } -bool FFlowPinType_Transform::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +bool FFlowPinType_Transform::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const { - return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, PropertyName, OutResult); } -bool FFlowPinType_GameplayTag::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +bool FFlowPinType_GameplayTag::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const { - return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, PropertyName, OutResult); } -bool FFlowPinType_GameplayTagContainer::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +bool FFlowPinType_GameplayTagContainer::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const { - return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, PropertyName, OutResult); } -bool FFlowPinType_InstancedStruct::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +bool FFlowPinType_InstancedStruct::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const { - return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, PropertyName, OutResult); } -bool FFlowPinType_Object::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +bool FFlowPinType_Object::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const { - return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, PropertyName, OutResult); } -bool FFlowPinType_Class::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const +bool FFlowPinType_Class::PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const { - return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, Pin, OutResult); + return FlowPinType::PopulateResultTemplate(PropertyOwnerObject, Node, PropertyName, OutResult); } #if WITH_EDITOR diff --git a/Source/Flow/Public/AddOns/FlowNodeAddOn.h b/Source/Flow/Public/AddOns/FlowNodeAddOn.h index 1fe43bd6d..abe446cf9 100644 --- a/Source/Flow/Public/AddOns/FlowNodeAddOn.h +++ b/Source/Flow/Public/AddOns/FlowNodeAddOn.h @@ -38,7 +38,9 @@ class UFlowNodeAddOn : public UFlowNodeBase #endif public: - // AddOns may opt in to be eligible for a given parent + // UFlowNodeBase + + // AddOns may opt in to be eligible for a given parent // - ParentTemplate - the template of the FlowNode or FlowNodeAddOn that is being considered as a potential parent // - AdditionalAddOnsToAssumeAreChildren - other AddOns to assume that are already child AddOns for the purposes of this test. // This list will be populated with the 'other' AddOns in a multi-paste operation in the editor, diff --git a/Source/Flow/Public/Asset/FlowAssetParams.h b/Source/Flow/Public/Asset/FlowAssetParams.h index 43b9f23eb..f55e5c93b 100644 --- a/Source/Flow/Public/Asset/FlowAssetParams.h +++ b/Source/Flow/Public/Asset/FlowAssetParams.h @@ -54,8 +54,8 @@ class FLOW_API UFlowAssetParams // -- // IFlowDataPinValueSupplierInterface - virtual bool CanSupplyDataPinValues_Implementation() const override; - virtual FFlowDataPinResult TrySupplyDataPin_Implementation(FName PinName) const override; + virtual bool CanSupplyDataPinValues() const override; + virtual FFlowDataPinResult TrySupplyDataPin(FName PinName) const override; // -- // IFlowAssetProviderInterface @@ -73,6 +73,9 @@ class FLOW_API UFlowAssetParams const TSoftObjectPtr& InOwnerFlowAsset, TArray& MutablePropertiesFromStartNode); + // Updates properties from ParentParams, handling inheritance and name enforcement. + EFlowReconcilePropertiesResult ReconcilePropertiesWithParentParams(); + void ConfigureFlowAssetParams(TSoftObjectPtr OwnerAsset, TSoftObjectPtr InParentParams, const TArray& InProperties); // IFlowDataPinValueOwnerInterface @@ -99,9 +102,6 @@ class FLOW_API UFlowAssetParams protected: - // Updates properties from ParentParams, handling inheritance and name enforcement. - EFlowReconcilePropertiesResult ReconcilePropertiesWithParentParams(); - EFlowReconcilePropertiesResult CheckForParentCycle() const; void ModifyAndRebuildPropertiesMap(); diff --git a/Source/Flow/Public/Asset/FlowAssetParamsUtils.h b/Source/Flow/Public/Asset/FlowAssetParamsUtils.h index 9c1f966a1..ec6d85b5c 100644 --- a/Source/Flow/Public/Asset/FlowAssetParamsUtils.h +++ b/Source/Flow/Public/Asset/FlowAssetParamsUtils.h @@ -8,6 +8,7 @@ #include "FlowAssetParamsUtils.generated.h" class UObject; +class UFlowAssetParams; struct FFlowNamedDataPinProperty; /** @@ -40,5 +41,26 @@ struct FLOW_API FFlowAssetParamsUtils static bool ArePropertiesEqual( const FFlowNamedDataPinProperty& A, const FFlowNamedDataPinProperty& B); + + /** + * Create Flow Asset Params asset from a parent params asset. + * - Creates the new asset in the same folder as the parent + * - Uses parent's asset name as the base for unique name generation (ParentName, ParentName_1, ...) + * - Copies OwnerFlowAsset + Properties and sets ParentParams to the provided parent + * - Runs ReconcilePropertiesWithParentParams (cycle detection, flattened inheritance, etc.) + * - Attempts source control checkout/add + * - Saves the new package + * - Registers and syncs to Content Browser + * + * @param ParentParams The parent params asset to inherit from. Must be valid. + * @param bShowDialogs If true, errors are surfaced via modal dialogs as well as logs. + * @param OutOptionalFailureReason If provided, filled with a human-readable error message on failure. + * @return The created child params asset or nullptr on failure. + */ + static UFlowAssetParams* CreateChildParamsAsset(UFlowAssetParams& ParentParams, const bool bShowDialogs = true, FText* OutOptionalFailureReason = nullptr); + +protected: + static void FailCreateChild(const FText& Reason, const bool bShowDialogs, FText* OutOptionalFailureReason); + #endif -}; \ No newline at end of file +}; diff --git a/Source/Flow/Public/Asset/FlowDeferredTransitionScope.h b/Source/Flow/Public/Asset/FlowDeferredTransitionScope.h new file mode 100644 index 000000000..66a3783be --- /dev/null +++ b/Source/Flow/Public/Asset/FlowDeferredTransitionScope.h @@ -0,0 +1,36 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Misc/Guid.h" + +#include "Nodes/FlowPin.h" + +class UFlowAsset; + +struct FFlowDeferredTriggerInput +{ + FGuid NodeGuid; + FName PinName; + FConnectedPin FromPin; +}; + +struct FLOW_API FFlowDeferredTransitionScope +{ +public: + void EnqueueDeferredTrigger(const FFlowDeferredTriggerInput& Entry); + bool TryFlushDeferredTriggers(UFlowAsset& OwningFlowAsset); + + void CloseScope() { bIsOpen = false; } + bool IsOpen() const { return bIsOpen; } + + const TArray& GetDeferredTriggers() const { return DeferredTriggers; } + +protected: + + // Deferred triggers for this scope + TArray DeferredTriggers; + + // Is currently accepting new deferred triggers + bool bIsOpen = true; +}; diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 58a28b340..68acdc681 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -5,13 +5,15 @@ #include "FlowSave.h" #include "FlowTypes.h" #include "Asset/FlowAssetParamsTypes.h" +#include "Asset/FlowDeferredTransitionScope.h" #include "Nodes/FlowNode.h" #if WITH_EDITOR #include "FlowMessageLog.h" #endif - +#include "Templates/SharedPointer.h" #include "UObject/ObjectKey.h" + #include "FlowAsset.generated.h" class UFlowNode_CustomOutput; @@ -30,7 +32,7 @@ DECLARE_DELEGATE_TwoParams(FFlowSignalEvent, UFlowNode* /*FlowNode*/, const FNam #endif /** - * Single asset containing flow nodes. + * Asset containing Flow nodes organized as non-linear graph. */ UCLASS(BlueprintType, hideCategories = Object) class FLOW_API UFlowAsset : public UObject @@ -46,12 +48,13 @@ class FLOW_API UFlowAsset : public UObject friend class FFlowAssetDetails; friend class FFlowNode_SubGraphDetails; friend class UFlowGraphSchema; + friend struct FFlowDeferredTransitionScope; UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Flow Asset") FGuid AssetGuid; - // Set it to False, if this asset is instantiated as Root Flow for owner that doesn't live in the world - // This allows to SaveGame support works properly, if owner of Root Flow would be Game Instance or its subsystem + /* Set it to False, if this asset is instantiated as Root Flow for owner that doesn't live in the world. + * This allows to SaveGame support works properly, if owner of Root Flow would be Game Instance or its subsystem. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Flow Asset") bool bWorldBound; @@ -90,7 +93,7 @@ class FLOW_API UFlowAsset : public UObject virtual EDataValidationResult ValidateAsset(FFlowMessageLog& MessageLog); - // Returns whether the node class is allowed in this flow asset + /* Returns whether the node class is allowed in this flow asset. */ bool IsNodeOrAddOnClassAllowed(const UClass* FlowNodeClass, FText* OutOptionalFailureReason = nullptr) const; virtual TSubclassOf GetDefaultFlowAssetForSubgraphs() const { return GetClass(); } @@ -104,7 +107,7 @@ class FLOW_API UFlowAsset : public UObject bool IsFlowNodeClassInDeniedClasses(const UClass& FlowNodeClass) const; private: - // Recursively validates the given addon and its children. + /* Recursively validates the given addon and its children. */ void ValidateAddOnTree(UFlowNodeAddOn& AddOn, FFlowMessageLog& MessageLog); #endif @@ -126,17 +129,13 @@ class FLOW_API UFlowAsset : public UObject #if WITH_EDITORONLY_DATA protected: - /** - * Custom Inputs define custom entry points in graph, it's similar to blueprint Custom Events - * Sub Graph node using this Flow Asset will generate context Input Pin for every valid Event name on this list - */ + /* Custom Inputs define custom entry points in graph, it's similar to blueprint Custom Events. + * Sub Graph node using this Flow Asset will generate context Input Pin for every valid Event name on this list. */ UPROPERTY(EditAnywhere, Category = "Sub Graph") TArray CustomInputs; - /** - * Custom Outputs define custom graph outputs, this allows to send signals to the parent graph while executing this graph - * Sub Graph node using this Flow Asset will generate context Output Pin for every valid Event name on this list - */ + /* Custom Outputs define custom graph outputs, this allows to send signals to the parent graph while executing this graph. + * Sub Graph node using this Flow Asset will generate context Output Pin for every valid Event name on this list. */ UPROPERTY(EditAnywhere, Category = "Sub Graph") TArray CustomOutputs; #endif // WITH_EDITORONLY_DATA @@ -150,12 +149,12 @@ class FLOW_API UFlowAsset : public UObject void RegisterNode(const FGuid& NewGuid, UFlowNode* NewNode); void UnregisterNode(const FGuid& NodeGuid); - // Processes nodes and updates pin connections from the graph to the UFlowNode (processes all nodes in the graph if passed nullptr) + /* Processes nodes and updates pin connections from the graph to the UFlowNode (processes all nodes in the graph if passed nullptr). */ void HarvestNodeConnections(UFlowNode* TargetNode = nullptr); static bool TryGetDefaultForInputPinName(const FStructProperty& StructProperty, const void* Container, FString& OutString); - // Updates the auto-generated pins and bindings for a given FlowNode, returns true if any changes were made. + /* Updates the auto-generated pins and bindings for a given FlowNode, returns true if any changes were made. */ static bool TryUpdateManagedFlowPinsForNode(UFlowNode& FlowNode); #endif @@ -181,10 +180,10 @@ class FLOW_API UFlowAsset : public UObject UFUNCTION(BlueprintPure, Category = "FlowAsset") virtual UFlowNode* GetDefaultEntryNode() const; - // Gathers all of the nodes that are connected to the Start & Custom Inputs of the flow graph + /* Gathers all the nodes that are connected to the Start & Custom Inputs of the flow graph. */ TArray GatherNodesConnectedToAllInputs() const; - // Return all other Pins connected to the passed Pin. + /* Return all other Pins connected to the passed Pin. */ TArray GatherPinsConnectedToPin(const FConnectedPin& Pin) const; UFUNCTION(BlueprintPure, Category = "FlowAsset", meta = (DeterminesOutputType = "FlowNodeClass")) @@ -245,15 +244,15 @@ class FLOW_API UFlowAsset : public UObject // Instances of the template asset private: - // Original object holds references to instances + /* Original object holds references to instances. */ UPROPERTY(Transient) TArray> ActiveInstances; #if WITH_EDITORONLY_DATA TWeakObjectPtr InspectedInstance; - // Message log for storing runtime errors/notes/warnings that will only last until the next game run - // Log lives in the asset template, so it can be inspected after ending the PIE + /* Message log for storing runtime errors/notes/warnings that will only last until the next game run. + * Log lives in the asset template, so it can be inspected after ending the PIE. */ TSharedPtr RuntimeLog; #endif @@ -291,29 +290,29 @@ class FLOW_API UFlowAsset : public UObject UPROPERTY() TObjectPtr TemplateAsset; - // Object that spawned Root Flow instance, i.e. World Settings or Player Controller - // This pointer is passed to child instances: Flow Asset instances created by the SubGraph nodes + /* Object that spawned Root Flow instance, i.e. World Settings or Player Controller. + * This pointer is passed to child instances: Flow Asset instances created by the SubGraph nodes. */ TWeakObjectPtr Owner; - // SubGraph node that created this Flow Asset instance + /* SubGraph node that created this Flow Asset instance. */ TWeakObjectPtr NodeOwningThisAssetInstance; - // Flow Asset instances created by SubGraph nodes placed in the current graph + /* Flow Asset instances created by SubGraph nodes placed in the current graph. */ TMap, TWeakObjectPtr> ActiveSubGraphs; - // Optional entry points to the graph, similar to blueprint Custom Events - // Contains nodes only if it is initialized instance (see InitializeInstance, IsInstanceInitialized), empty otherwise + /* Optional entry points to the graph, similar to blueprint Custom Events. + * Contains nodes only if it is initialized instance (see InitializeInstance, IsInstanceInitialized), empty otherwise. */ UPROPERTY() TSet> CustomInputNodes; UPROPERTY() TSet> PreloadedNodes; - // Nodes that have any work left, not marked as Finished yet + /* Nodes that have any work left, not marked as Finished yet. */ UPROPERTY() TArray> ActiveNodes; - // All nodes active in the past, done their work + /* All nodes active in the past, done their work. */ UPROPERTY() TArray> RecordedNodes; @@ -329,8 +328,8 @@ class FLOW_API UFlowAsset : public UObject UFlowAsset* GetTemplateAsset() const { return TemplateAsset; } - // Object that spawned Root Flow instance, i.e. World Settings or Player Controller - // This pointer is passed to child instances: Flow Asset instances created by the SubGraph nodes + /* Object that spawned Root Flow instance, i.e. World Settings or Player Controller. + * This pointer is passed to child instances: Flow Asset instances created by the SubGraph nodes. */ UFUNCTION(BlueprintPure, Category = "Flow") UObject* GetOwner() const { return Owner.Get(); } @@ -340,11 +339,11 @@ class FLOW_API UFlowAsset : public UObject return Owner.IsValid() ? Cast(Owner) : nullptr; } - // Returns the Owner as an Actor, or if Owner is a Component, return its Owner as an Actor + /* Returns the Owner as an Actor, or if Owner is a Component, return its Owner as an Actor. */ UFUNCTION(BlueprintPure, Category = "Flow") AActor* TryFindActorOwner() const; - // Opportunity to preload content of project-specific nodes + /* Opportunity to preload content of project-specific nodes. */ virtual void PreloadNodes() {} virtual void PreStartFlow(); @@ -355,18 +354,14 @@ class FLOW_API UFlowAsset : public UObject bool HasStartedFlow() const; void TriggerCustomInput(const FName& EventName, IFlowDataPinValueSupplierInterface* DataPinValueSupplier = nullptr); - // Get Flow Asset instance created by the given SubGraph node + /* Get Flow Asset instance created by the given SubGraph node. */ TWeakObjectPtr GetFlowInstance(UFlowNode_SubGraph* SubGraphNode) const; - // Public trigger input signature for the FFlowExecutionGate mechanism in the Flow Debugger - FORCEINLINE void TriggerDeferredInputFromDebugger(const FGuid& NodeGuid, const FName& PinName, const FConnectedPin& FromPin) - { TriggerInput(NodeGuid, PinName, FromPin); } - protected: void TriggerCustomInput_FromSubGraph(UFlowNode_SubGraph* Node, const FName& EventName) const; void TriggerCustomOutput(const FName& EventName); - // TODO: Extend FromPin through to Node level Trigger functions + /* todo: Extend FromPin through to Node level Trigger functions. */ virtual void TriggerInput(const FGuid& NodeGuid, const FName& PinName, const FConnectedPin& FromPin); virtual void FinishNode(UFlowNode* Node); @@ -384,25 +379,59 @@ class FLOW_API UFlowAsset : public UObject UFlowNode_SubGraph* GetNodeOwningThisAssetInstance() const; UFlowAsset* GetParentInstance() const; - // Are there any active nodes? + /* Are there any active nodes? */ UFUNCTION(BlueprintPure, Category = "Flow") bool IsActive() const { return ActiveNodes.Num() > 0; } - // Returns nodes that have any work left, not marked as Finished yet + /* Returns nodes that have any work left, not marked as Finished yet. */ UFUNCTION(BlueprintPure, Category = "Flow") const TArray& GetActiveNodes() const { return ActiveNodes; } - // Returns nodes active in the past, done their work + /* Returns nodes active in the past, done their work. */ UFUNCTION(BlueprintPure, Category = "Flow") const TArray& GetRecordedNodes() const { return RecordedNodes; } +////////////////////////////////////////////////////////////////////////// +// Deferred trigger support + +public: + /* Try to flush (and clear) all Deferred Trigger scopes. + * Can fail to flush all if a FFlowExecutionGate causes a new halt. */ + bool TryFlushAllDeferredTriggerScopes(); + + /* Clear (do not trigger) any remaining deferred transitions (for shutdown cases). */ + void ClearAllDeferredTriggerScopes(); + +protected: + /* Stack of active deferred transition scopes (innermost = top). + * Stored as TSharedPtr so callers can safely cache a reference to a specific scope + * without it being invalidated by array reallocations/resizes during nested triggers. */ + TArray> DeferredTransitionScopes; + + /* Allow subclasses to disable the standard defer trigger mechanism */ + virtual bool ShouldDeferTriggers() const; + + void EnqueueDeferredTrigger(const FGuid& NodeGuid, const FName& PinName, const FConnectedPin& FromPin); + bool TryFlushAndRemoveDeferredTransitionScope(const TSharedPtr& Scope); + + TSharedPtr PushDeferredTransitionScope(); + void PopDeferredTransitionScope(const TSharedPtr& Scope) { TryFlushAndRemoveDeferredTransitionScope(Scope); } + + void CancelAndWarnForUnflushedDeferredTriggers(); + + /* Returns a shared pointer to the current top (innermost) deferred transition scope, + * or nullptr if there is no active scope. Safe to cache and use later. */ + TSharedPtr GetTopDeferredTransitionScope() const; + + /* Trigger the node directly (no deferral, no new scope). */ + void TriggerInputDirect(const FGuid& NodeGuid, const FName& PinName, const FConnectedPin& FromPin); + ////////////////////////////////////////////////////////////////////////// // Expected Owner Class support protected: - // Expects to be owned (at runtime) by an object with this class (or one of its subclasses) - // NOTE - If the class is an AActor, and the flow asset is owned by a component, - // it will consider the component's owner for the AActor + /* Expects to be owned (at runtime) by an object with this class (or one of its subclasses). + * If the class is an AActor, and the Flow Asset is owned by a component, it will consider the component's owner for the AActor. */ UPROPERTY(EditAnywhere, Category = "Flow") TSubclassOf ExpectedOwnerClass; @@ -433,20 +462,20 @@ class FLOW_API UFlowAsset : public UObject bool IsBoundToWorld() const; ////////////////////////////////////////////////////////////////////////// -// FlowAssetParams support (Start node params for a flow graph) +// FlowAssetParams support (Start node params for a Flow graph) - // Default parameters asset for this Flow Asset (optional) + /* Default parameters asset for this Flow Asset (optional). */ UPROPERTY(EditAnywhere, Category = FlowAssetParams, meta = (ShowCreateNew, HideChildParams)) FFlowAssetParamsPtr BaseAssetParams; #if WITH_EDITOR - // Called before saving the asset. + /* Called before saving the asset. */ virtual void PreSaveRoot(FObjectPreSaveRootContext ObjectSaveContext) override; - // Generates a new params asset from the Start node. + /* Generates a new params asset from the Start node. */ UFlowAssetParams* GenerateParamsFromStartNode(); - // Generates the FlowAssetParams name for the 'base' (root) asset, used when creating the params asset + /* Generates the FlowAssetParams name for the 'base' (root) asset, used when creating the params asset. */ virtual FString GenerateParamsAssetName() const; protected: diff --git a/Source/Flow/Public/FlowSettings.h b/Source/Flow/Public/FlowSettings.h index f73bf5202..0c7765586 100644 --- a/Source/Flow/Public/FlowSettings.h +++ b/Source/Flow/Public/FlowSettings.h @@ -23,24 +23,29 @@ class FLOW_API UFlowSettings : public UDeveloperSettings virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; #endif - // Set if to False, if you don't want to create client-side Flow Graphs - // And you don't access to the Flow Component registry on clients + /* Set if to False, if you don't want to create client-side Flow Graphs. + * And you don't access to the Flow Component registry on clients. */ UPROPERTY(Config, EditAnywhere, Category = "Networking") bool bCreateFlowSubsystemOnClients; UPROPERTY(Config, EditAnywhere, Category = "SaveSystem") bool bWarnAboutMissingIdentityTags; - // If enabled, runtime logs will be added when a flow node signal mode is set to Disabled + /* If enabled, runtime logs will be added when a flow node signal mode is set to Disabled. */ UPROPERTY(Config, EditAnywhere, Category = "Flow") bool bLogOnSignalDisabled; - // If enabled, runtime logs will be added when a flow node signal mode is set to Pass-through + /* If enabled, runtime logs will be added when a flow node signal mode is set to Pass-through. */ UPROPERTY(Config, EditAnywhere, Category = "Flow") bool bLogOnSignalPassthrough; - // Adjust the Titles for FlowNodes to be more expressive than default - // by incorporating data that would otherwise go in the Description + /* If True, defer the Triggered Outputs for a FlowAsset while it is currently processing a TriggeredInput. + * If False, use legacy behavior for backward compatability. */ + UPROPERTY(Config, EditAnywhere, Category = "Flow") + bool bDeferTriggeredOutputsWhileTriggering; + + /* Adjust the Titles for FlowNodes to be more expressive than default + * by incorporating data that would otherwise go in the Description. */ UPROPERTY(EditAnywhere, config, Category = "Nodes") bool bUseAdaptiveNodeTitles; @@ -49,7 +54,7 @@ class FLOW_API UFlowSettings : public UDeveloperSettings FFlowSettingsEvent OnAdaptiveNodeTitlesChanged; #endif - // Default class to use as a FlowAsset's "ExpectedOwnerClass" + /* Default class to use as a FlowAsset's "ExpectedOwnerClass". */ UPROPERTY(EditAnywhere, Config, Category = "Nodes") FSoftClassPath DefaultExpectedOwnerClass; diff --git a/Source/Flow/Public/FlowSubsystem.h b/Source/Flow/Public/FlowSubsystem.h index 492742214..201e4e852 100644 --- a/Source/Flow/Public/FlowSubsystem.h +++ b/Source/Flow/Public/FlowSubsystem.h @@ -59,6 +59,15 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem static FNativeFlowAssetEvent OnInstancedTemplateRemoved; #endif +public: + // Try to flush (and clear) all Deferred Trigger scopes + // (can fail to flush all if a FFlowExecutionGate causes a new halt) + bool TryFlushAllDeferredTriggerScopes(); + + // Clear (do not trigger) any remaining deferred transitions + // (for shutdown cases) + void ClearAllDeferredTriggerScopes(); + protected: UPROPERTY() TObjectPtr LoadedSaveGame; diff --git a/Source/Flow/Public/Interfaces/FlowDataPinValueSupplierInterface.h b/Source/Flow/Public/Interfaces/FlowDataPinValueSupplierInterface.h index 7a6266f23..a98e19cef 100644 --- a/Source/Flow/Public/Interfaces/FlowDataPinValueSupplierInterface.h +++ b/Source/Flow/Public/Interfaces/FlowDataPinValueSupplierInterface.h @@ -10,7 +10,7 @@ // Interface to define a Flow Data Pin value supplier. This is generally a UFlowNode subclass, // but we may support external suppliers that are not flow nodes in the future // (eg, for supplying configuration values for the root graph) -UINTERFACE(MinimalAPI, Blueprintable, DisplayName = "Flow Data Pin Value Supplier Interface") +UINTERFACE(MinimalAPI, NotBlueprintable, DisplayName = "Flow Data Pin Value Supplier Interface") class UFlowDataPinValueSupplierInterface : public UInterface { GENERATED_BODY() @@ -23,11 +23,7 @@ class FLOW_API IFlowDataPinValueSupplierInterface public: // Can this node actually supply Data Pin values? // Implementers of this interface will need to use their own logic to answer this question. - UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Can Supply DataPin Values") - bool CanSupplyDataPinValues() const; - virtual bool CanSupplyDataPinValues_Implementation() const { return true; } + virtual bool CanSupplyDataPinValues() const { return true; } - UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin") - FFlowDataPinResult TrySupplyDataPin(FName PinName) const; - virtual FFlowDataPinResult TrySupplyDataPin_Implementation(FName PinName) const { return FFlowDataPinResult(); } + virtual FFlowDataPinResult TrySupplyDataPin(FName PinName) const { return FFlowDataPinResult(); } }; diff --git a/Source/Flow/Public/Interfaces/FlowExecutionGate.h b/Source/Flow/Public/Interfaces/FlowExecutionGate.h index 8e1abdf66..8859dc266 100644 --- a/Source/Flow/Public/Interfaces/FlowExecutionGate.h +++ b/Source/Flow/Public/Interfaces/FlowExecutionGate.h @@ -31,15 +31,6 @@ class FLOW_API FFlowExecutionGate /** True if a gate exists and it currently wants Flow execution halted. */ static bool IsHalted(); - /** If halted, queues the trigger for later. Returns true if queued (caller should early-out). */ - static bool EnqueueDeferredTriggerInput(UFlowAsset* FlowAssetInstance, const FGuid& NodeGuid, const FName& PinName, const struct FConnectedPin& FromPin); - - /** - * Flushes queued trigger inputs (FIFO). - * Safe to call even if nothing is queued. - */ - static void FlushDeferredTriggerInputs(); - private: static IFlowExecutionGate* Gate; }; \ No newline at end of file diff --git a/Source/Flow/Public/Nodes/Developer/FlowNode_Log.h b/Source/Flow/Public/Nodes/Developer/FlowNode_Log.h index 0fdf3fa77..3c7825607 100644 --- a/Source/Flow/Public/Nodes/Developer/FlowNode_Log.h +++ b/Source/Flow/Public/Nodes/Developer/FlowNode_Log.h @@ -57,4 +57,7 @@ class FLOW_API UFlowNode_Log : public UFlowNode_DefineProperties virtual void UpdateNodeConfigText_Implementation() override; #endif + +public: + EFlowLogVerbosity GetVerbosity() const { return Verbosity; } }; diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 8fee5eebd..5080bff89 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -16,6 +16,22 @@ #include "FlowNode.generated.h" +// Entry in MapDataPinNameToPropertySource for how to source a non-trivial pin mapping in TryGatherPropertyOwnersAndPopulateResult +USTRUCT() +struct FFlowPinPropertySource +{ + GENERATED_BODY() + + FFlowPinPropertySource() = default; + FFlowPinPropertySource(const FName& InPropertyName, int32 InPropertyOwnerIndex) + : PropertyName(InPropertyName) + , PropertyOwnerIndex(InPropertyOwnerIndex) + { } + + FName PropertyName; + int32 PropertyOwnerIndex = INDEX_NONE; +}; + /** * A Flow Node is UObject-based node designed to handle entire gameplay feature within single node. */ @@ -26,6 +42,7 @@ class FLOW_API UFlowNode : public UFlowNodeBase , public IVisualLoggerDebugSnapshotInterface { GENERATED_UCLASS_BODY() + friend class SFlowGraphNode; friend class UFlowAsset; friend class UFlowGraphNode; @@ -193,8 +210,8 @@ class FLOW_API UFlowNode : public UFlowNodeBase UFUNCTION(BlueprintPure, Category= "FlowNode") bool IsOutputConnected(const FName& PinName, bool bErrorIfPinNotFound = true) const; - bool IsInputConnected(const FFlowPin& FlowPin) const; - bool IsOutputConnected(const FFlowPin& FlowPin) const; + bool IsInputConnected(const FFlowPin& FlowPin, FGuid* FoundGuid = nullptr, FName* OutConnectedPinName = nullptr) const; + bool IsOutputConnected(const FFlowPin& FlowPin, FGuid* FirstFoundGuid = nullptr, FName* OutFirstConnectedPinName = nullptr) const; FFlowPin* FindInputPinByName(const FName& PinName); FFlowPin* FindOutputPinByName(const FName& PinName); @@ -219,6 +236,8 @@ class FLOW_API UFlowNode : public UFlowNodeBase // Data Pins public: + using TFlowPinValueSupplierDataArray = FlowArray::TInlineArray; + #if WITH_EDITORONLY_DATA UPROPERTY(VisibleDefaultsOnly, AdvancedDisplay, Category = "FlowNode", meta = (GetByRef)) TArray AutoInputDataPins; @@ -227,6 +246,12 @@ class FLOW_API UFlowNode : public UFlowNodeBase TArray AutoOutputDataPins; #endif // WITH_EDITORONLY_DATA + // Map for PinName to Property supplier for non-trivial data pin property lookups + // (non-trivial means a different pin name from its property source, or a non-zero property owner object index) + // see TryGatherPropertyOwnersAndPopulateResult() + UPROPERTY() + TMap MapDataPinNameToPropertySource; + #if WITH_EDITOR void SetAutoInputDataPins(const TArray& AutoInputPins); void SetAutoOutputDataPins(const TArray& AutoOutputPins); @@ -239,7 +264,7 @@ class FLOW_API UFlowNode : public UFlowNodeBase // IFlowDataPinValueSupplierInterface public: - virtual FFlowDataPinResult TrySupplyDataPin_Implementation(FName PinName) const override; + virtual FFlowDataPinResult TrySupplyDataPin(FName PinName) const override; // Advanced helper for TrySupplyDataPin, which can be overridden in subclasses to provide alternate sourcing for properties. // If returns true, either OutFoundProperty or OutFoundInstancedStruct is expected to carry the property value. @@ -251,6 +276,9 @@ class FLOW_API UFlowNode : public UFlowNodeBase TInstancedStruct& OutFoundInstancedStruct) const; protected: + // Helper for TryGetFlowDataPinSupplierDatasForPinName() + void TryAddSupplierDataToArray(FFlowPinValueSupplierData& InOutSupplierData, TFlowPinValueSupplierDataArray& InOutPinValueSupplierDatas) const; + // Static implementation of the default TryFindPropertyByPinName (which subclasses can incorporate into overrides) static bool TryFindPropertyByPinName_Static( const UObject& PropertyOwnerObject, @@ -270,7 +298,6 @@ class FLOW_API UFlowNode : public UFlowNodeBase const FFlowPin& FlowPin, FFlowDataPinResult& OutSuppliedResult) const; - using TFlowPinValueSupplierDataArray = FlowArray::TInlineArray; bool TryGetFlowDataPinSupplierDatasForPinName(const FName& PinName, TFlowPinValueSupplierDataArray& InOutPinValueSupplierDatas) const; // IFlowDataPinGeneratorInterface diff --git a/Source/Flow/Public/Nodes/FlowNodeBase.h b/Source/Flow/Public/Nodes/FlowNodeBase.h index 3f4566ec5..25894ff35 100644 --- a/Source/Flow/Public/Nodes/FlowNodeBase.h +++ b/Source/Flow/Public/Nodes/FlowNodeBase.h @@ -29,6 +29,36 @@ struct FFlowPinType; DECLARE_DELEGATE(FFlowNodeEvent); #endif +/** + * Describes an overlay icon to display on a flow node in the editor. + */ +USTRUCT() +struct FLOW_API FFlowNodeOverlayIcon +{ + GENERATED_BODY() + + FFlowNodeOverlayIcon() = default; + + explicit FFlowNodeOverlayIcon(const FName& InBrushName, const FVector2D& InOffset = FVector2D::ZeroVector, const FName& InStyleSetName = NAME_None) + : BrushName(InBrushName) + , Offset(InOffset) + , StyleSetName(InStyleSetName) + { + } + + /** Name of the brush to use for the icon */ + UPROPERTY() + FName BrushName = NAME_None; + + /** Offset from the top-left corner of the node (position X moves right, positive Y moves down) */ + UPROPERTY() + FVector2D Offset = FVector2D::ZeroVector; + + /** Name of the StyleSet that contains your brush. If left empty Flow will first search the default Flow StyleSet and then the default Unreal StyleSet */ + UPROPERTY() + FName StyleSetName = NAME_None; +}; + typedef TFunction FConstFlowNodeAddOnFunction; typedef TFunction FFlowNodeAddOnFunction; @@ -419,6 +449,27 @@ class FLOW_API UFlowNodeBase FText GetGeneratedDisplayName() const; + /** + * Returns overlay icons to display on this node instance in the editor. + * Icons are positioned relative to the top-left corner of the node. + * @param OutOverlayIcons Brush and positioning details of each icon to overlay on the node. + * @param WidgetSize The size of the Node in the editor. Useful for determining offset position values for each overlay icon. + */ +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 6 + virtual void GetOverlayIcons(TArray& OutOverlayIcons, const FVector2D WidgetSize) const {}; +#else + virtual void GetOverlayIcons(TArray& OutOverlayIcons, const FVector2f& WidgetSize) const {}; +#endif + + /** + * Gets details to draw an optional corner icon on the node. + * If this function returns true and valid Brush details are given then the corresponding icon will be displayed centered on the top-right of the node. + * @param OutBrushName The Brush name of the icon to display. + * @param OutStyleSetName The StyleSet name of the icon to display. If NAME_None is set we will first search the default Flow StyleSet and then the default Unreal StyleSet. + * @return Returns true if the Node wants to display an icon in the top-right corner. + */ + virtual bool GetCornerIcon(FName& OutBrushName, FName& OutStyleSetName) const { return false; } + protected: void EnsureNodeDisplayStyle(); #endif // WITH_EDITOR diff --git a/Source/Flow/Public/Nodes/FlowPin.h b/Source/Flow/Public/Nodes/FlowPin.h index 9aa88d484..752ae6a5d 100644 --- a/Source/Flow/Public/Nodes/FlowPin.h +++ b/Source/Flow/Public/Nodes/FlowPin.h @@ -24,17 +24,17 @@ struct FLOW_API FFlowPin GENERATED_BODY() // A logical name, used during execution of pin - UPROPERTY(EditDefaultsOnly, Category = DataPins) + UPROPERTY(EditDefaultsOnly, Category = FlowPin) FName PinName; // An optional Display Name, you can use it to override PinName without the need to update graph connections - UPROPERTY(EditDefaultsOnly, Category = DataPins) + UPROPERTY(EditDefaultsOnly, Category = FlowPin) FText PinFriendlyName; - UPROPERTY(EditDefaultsOnly, Category = DataPins) + UPROPERTY(EditDefaultsOnly, Category = FlowPin) FString PinToolTip; - // PinType (implies PinCategory) + // Deprecated PinType, use PinTypeName instead (all standard names are defined in FFlowPinTypeNamesStandard) UPROPERTY(Meta = (DeprecatedProperty, DeprecationMessage = "Use PinTypeName instead")) EFlowPinType PinType = EFlowPinType::Invalid; @@ -43,46 +43,25 @@ struct FLOW_API FFlowPin EPinContainerType ContainerType = EPinContainerType::None; protected: - UPROPERTY(EditDefaultsOnly, Category = DataPins) + UPROPERTY() FFlowPinTypeName PinTypeName = FFlowPinTypeName(FFlowPinTypeNamesStandard::PinTypeNameExec); // Sub-category object // (used to identify the struct or class type for some PinCategories) - UPROPERTY(VisibleAnywhere, Category = DataPins) + UPROPERTY() TWeakObjectPtr PinSubCategoryObject; -#if WITH_EDITORONLY_DATA - // Filter for limiting the compatible classes for this data pin. - // This property is editor-only, but it is automatically copied into PinSubCategoryObject if the PinTypeName matches (for runtime use). - UPROPERTY(EditAnywhere, Category = DataPins, meta = (EditCondition = "PinTypeName == Class", EditConditionHides)) - TSubclassOf SubCategoryClassFilter = UClass::StaticClass(); - - // Filter for limiting the compatible object types for this data pin. - // This property is editor-only, but it is automatically copied into PinSubCategoryObject if the PinTypeName matches (for runtime use). - UPROPERTY(EditAnywhere, Category = DataPins, meta = (EditCondition = "PinTypeName == Object", EditConditionHides)) - TSubclassOf SubCategoryObjectFilter = UObject::StaticClass(); - - // Configuration option for setting the EnumClass to a Blueprint Enum - // (C++ enums must bind by name using SubCategoryEnumName, due to a limitation with UE's UEnum discovery). - // This property is editor-only, but it is automatically copied into PinSubCategoryObject if the PinType matches (for runtime use). - UPROPERTY(EditAnywhere, Category = DataPins, meta = (EditCondition = "PinTypeName == Enum", EditConditionHides)) - TObjectPtr SubCategoryEnumClass = nullptr; - - // name of enum defined in c++ code, will take priority over asset from EnumType property - // (this is a work-around because EnumClass cannot find C++ Enums, - // so you need to type the name of the enum in here, manually) - // See also: FFlowPin::PostEditChangedEnumName() - UPROPERTY(EditAnywhere, Category = DataPins, meta = (EditCondition = "PinTypeName == Enum", EditConditionHides)) - FString SubCategoryEnumName; -#endif - public: - FFlowPin() : PinName(NAME_None) { } + FFlowPin(const FFlowPin& InFlowPin) = default; + FFlowPin(FFlowPin&& InFlowPin) = default; + FFlowPin& operator =(FFlowPin&& InFlowPin) = default; + FFlowPin& operator =(const FFlowPin& InFlowPin) = default; + explicit FFlowPin(const FName& InPinName) : PinName(InPinName) { @@ -205,13 +184,13 @@ struct FLOW_API FFlowPin public: #if WITH_EDITOR - // Must be called from PostEditChangeProperty() by an owning UObject - // whenever PinType, - void PostEditChangedPinTypeOrSubCategorySource(); FText BuildHeaderText() const; static bool ValidateEnum(const UEnum& EnumType); -#endif // WITH_EDITOR + + FEdGraphPinType BuildEdGraphPinType() const; + void ConfigureFromEdGraphPin(const FEdGraphPinType& EdGraphPinType); +#endif void SetPinTypeName(const FFlowPinTypeName& InTypeName); const FFlowPinTypeName& GetPinTypeName() const { return PinTypeName; } @@ -219,17 +198,12 @@ struct FLOW_API FFlowPin void SetPinSubCategoryObject(UObject* Object) { PinSubCategoryObject = Object; } static FFlowPinTypeName GetPinTypeNameForLegacyPinType(EFlowPinType PinType); -#if WITH_EDITOR - FEdGraphPinType BuildEdGraphPinType() const; -#endif - const TWeakObjectPtr& GetPinSubCategoryObject() const { return PinSubCategoryObject; } - FORCEINLINE_DEBUGGABLE static bool DeepArePinArraysMatching(const TArray& Left, const TArray& Right); - // FFlowPin instance signatures for "trait" functions bool IsExecPin() const; static bool IsExecPinCategory(const FName& PC); + FORCEINLINE bool IsDataPin() const { return !IsExecPin(); } // -- // Metadata keys for properties that bind and auto-generate Data Pins: @@ -278,25 +252,6 @@ struct FLOW_API FFlowPin void TrySetStructSubCategoryObjectFromPinType(); }; -// Inline implementations -bool FFlowPin::DeepArePinArraysMatching(const TArray& Left, const TArray& Right) -{ - if (Left.Num() != Right.Num()) - { - return false; - } - - for (int32 Index = 0; Index < Left.Num(); ++Index) - { - if (!Left[Index].DeepIsEqual(Right[Index])) - { - return false; - } - } - - return true; -} - USTRUCT() struct FLOW_API FFlowPinHandle { diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_BlueprintDataPinSupplierBase.h b/Source/Flow/Public/Nodes/Graph/FlowNode_BlueprintDataPinSupplierBase.h new file mode 100644 index 000000000..162b235e8 --- /dev/null +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_BlueprintDataPinSupplierBase.h @@ -0,0 +1,33 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Nodes/FlowNode.h" + +#include "FlowNode_BlueprintDataPinSupplierBase.generated.h" + +/** + * FlowNode to give an event to blueprint for supplying data pin values on-demand + * (as there is no longer a blueprint override to TrySupplyDataPin) + */ +UCLASS(Abstract, Blueprintable, meta = (DisplayName = "Blueprint Data-Pin Supplier base")) +class FLOW_API UFlowNode_BlueprintDataPinSupplierBase : public UFlowNode +{ + GENERATED_UCLASS_BODY() + +public: + + // IFlowDataPinValueSupplierInterface + virtual FFlowDataPinResult TrySupplyDataPin(FName PinName) const override; + // -- + + // Blueprint signature for TrySupplyDataPin override + UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin") + FFlowDataPinResult BP_TrySupplyDataPin(FName PinName) const; + + // Blueprint access for the 'standard' implementation of TrySupplyDataPin + // (for cases where they want to override some pins, but maybe not all, they can have the BP + // override call this version to handle any cases it doesn't want to handle) + UFUNCTION(BlueprintPure, Category = DataPins, DisplayName = "Try Supply DataPin (standard implementation)") + FFlowDataPinResult BP_Super_TrySupplyDataPin(FName PinName) const { return Super::TrySupplyDataPin(PinName); } +}; diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h b/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h index 2d5d21bb8..ea882dd97 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h @@ -53,4 +53,10 @@ class FLOW_API UFlowNode_DefineProperties const FName& PinName, const FProperty*& OutFoundProperty, TInstancedStruct& OutFoundInstancedStruct) const override; + +#if WITH_EDITOR + // Utility function for subclasses, if they want to force a named property to be a Input or Output + // (unused in this class) + void OnPostEditEnsureAllNamedPropertiesPinDirection(const FProperty& Property, bool bIsInput); +#endif }; diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_FormatText.h b/Source/Flow/Public/Nodes/Graph/FlowNode_FormatText.h index bc699ddc6..52ba2dc32 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_FormatText.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_FormatText.h @@ -22,18 +22,22 @@ class FLOW_API UFlowNode_FormatText : public UFlowNode_DefineProperties UPROPERTY(EditAnywhere, Category = "Flow", meta = (DefaultForInputFlowPin, FlowPinType = Text)) FText FormatText; -protected: - #if WITH_EDITOR public: + // UObject + virtual void PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent) override; + // -- + virtual void UpdateNodeConfigText_Implementation() override; #endif +protected: + EFlowDataPinResolveResult TryResolveFormatText(const FName& PinName, FText& OutFormattedText) const; public: // IFlowDataPinValueSupplierInterface - virtual FFlowDataPinResult TrySupplyDataPin_Implementation(FName PinName) const override; + virtual FFlowDataPinResult TrySupplyDataPin(FName PinName) const override; // -- static const FName OUTPIN_TextOutput; diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_Start.h b/Source/Flow/Public/Nodes/Graph/FlowNode_Start.h index 8b31a392b..c62ccfd2b 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_Start.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_Start.h @@ -40,6 +40,6 @@ class FLOW_API UFlowNode_Start // -- // IFlowDataPinValueSupplierInterface - virtual FFlowDataPinResult TrySupplyDataPin_Implementation(FName PinName) const override; + virtual FFlowDataPinResult TrySupplyDataPin(FName PinName) const override; // -- }; diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h b/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h index 572a42aa3..016ef6489 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h @@ -6,6 +6,8 @@ #include "FlowNode_SubGraph.generated.h" +class UFlowAssetParams; + /** * Creates instance of provided Flow Asset and starts its execution */ @@ -26,12 +28,12 @@ class FLOW_API UFlowNode_SubGraph : public UFlowNode UPROPERTY(EditAnywhere, Category = "Graph") TSoftObjectPtr Asset; - // TODO (gtaylor) Create FlowAssetParams option for the Subgraph & reconcile with connected input pins' values + /* Flow Asset Params to use as the data pin value supplier for the Asset */ + UPROPERTY(EditAnywhere, Category = "Graph", meta = (DefaultForInputFlowPin, FlowPinType = "Object")) + TSoftObjectPtr AssetParams; - /* - * Allow to create instance of the same Flow Asset as the asset containing this node - * Enabling it may cause an infinite loop, if graph would keep creating copies of itself - */ + /* Allow to create instance of the same Flow Asset as the asset containing this node. + * Enabling it may cause an infinite loop, if graph would keep creating copies of itself. */ UPROPERTY(EditAnywhere, Category = "Graph") bool bCanInstanceIdenticalAsset; @@ -53,15 +55,14 @@ class FLOW_API UFlowNode_SubGraph : public UFlowNode protected: virtual void OnLoad_Implementation() override; - #if WITH_EDITORONLY_DATA protected: - // All the classes allowed to be used as assets on this subgraph node + /* All the classes allowed to be used as assets on this subgraph node. */ UPROPERTY() TArray> AllowedAssignedAssetClasses; - // All the classes disallowed to be used as assets on this subgraph node + /* All the classes disallowed to be used as assets on this subgraph node. */ UPROPERTY() TArray> DeniedAssignedAssetClasses; #endif @@ -87,7 +88,7 @@ class FLOW_API UFlowNode_SubGraph : public UFlowNode // -- // IFlowDataPinValueSupplierInterface - virtual bool CanSupplyDataPinValues_Implementation() const override; + virtual FFlowDataPinResult TrySupplyDataPin(FName PinName) const override; // -- // IFlowDataPinGeneratorInterface @@ -97,4 +98,6 @@ class FLOW_API UFlowNode_SubGraph : public UFlowNode private: void SubscribeToAssetChanges(); #endif + + static const FName AssetParams_MemberName; }; diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_Reroute.h b/Source/Flow/Public/Nodes/Route/FlowNode_Reroute.h index 3556dbe91..6a8f8fb97 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_Reroute.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_Reroute.h @@ -12,7 +12,20 @@ UCLASS(NotBlueprintable, meta = (DisplayName = "Reroute")) class FLOW_API UFlowNode_Reroute final : public UFlowNode { GENERATED_UCLASS_BODY() - + protected: + // IFlowCoreExecutableInterface virtual void ExecuteInput(const FName& PinName) override; + // -- + + // IFlowDataPinValueSupplierInterface + virtual FFlowDataPinResult TrySupplyDataPin(FName PinName) const override; + // -- + +public: +#if WITH_EDITOR + // For configuration from connecting pins via UFlowGraphNode_Reroute + void ConfigureInputPin(const UFlowNode& ConnectedNode, const FEdGraphPinType& EdGraphPinType); + void ConfigureOutputPin(const UFlowNode& ConnectedNode, const FEdGraphPinType& EdGraphPinType); +#endif }; diff --git a/Source/Flow/Public/Types/FlowAutoDataPinsWorkingData.h b/Source/Flow/Public/Types/FlowAutoDataPinsWorkingData.h index 1a5f41bd7..8b99a93a0 100644 --- a/Source/Flow/Public/Types/FlowAutoDataPinsWorkingData.h +++ b/Source/Flow/Public/Types/FlowAutoDataPinsWorkingData.h @@ -4,25 +4,73 @@ #include "Nodes/FlowPin.h" -// Working Data struct for the UFlowDataPinGeneratorNodeInterface::AutoGenerateDataPins function +#if WITH_EDITOR + +class UFlowNode; +class UObject; +struct FFlowDataPinValue; + +// Container for pin data collected during automatic pin generation +struct FFlowPinSourceData +{ + FFlowPinSourceData(const FFlowPin& InFlowPin, const FName& InPropertyOwnerObjectName, int32 InPropertyOwnerIndex = 0, const FFlowDataPinValue* InDataPinValue = nullptr) + : FlowPin(InFlowPin) + , PropertyOwnerObjectName(InPropertyOwnerObjectName) + , PropertyOwnerIndex(InPropertyOwnerIndex) + , DataPinValue(InDataPinValue) + { + } + + FFlowPin FlowPin; + FName PropertyOwnerObjectName; + int32 PropertyOwnerIndex = INDEX_NONE; + const FFlowDataPinValue* DataPinValue = nullptr; +}; + +// Transient working data used during auto-generation of data pins struct FFlowAutoDataPinsWorkingData { +public: FFlowAutoDataPinsWorkingData(const TArray& InputPinsPrev, const TArray& OutputPinsPrev) : AutoInputDataPinsPrev(InputPinsPrev) , AutoOutputDataPinsPrev(OutputPinsPrev) - { } + { + } -#if WITH_EDITOR - FLOW_API void AddFlowDataPinsForClassProperties(const UObject& ObjectContainer); - FLOW_API void AddFlowDataPinForProperty(const FProperty* Property, const UObject& ObjectContainer); + FLOW_API bool AutoGenerateDataPinsForFlowNode(UFlowNode& FlowNode, bool& bAutoInputDataPinsChanged, bool& bAutoOutputDataPinsChanged); - FLOW_API bool DidAutoInputDataPinsChange() const; - FLOW_API bool DidAutoOutputDataPinsChange() const; -#endif + FLOW_API void AddFlowDataPinsForClassProperties(const UObject& ObjectContainer, int32 PropertyOwnerIndex); + FLOW_API static void BuildNextFlowPinArray(const TArray& PinSourceDatas, TArray& OutFlowPins); + +protected: + void DisambiguateAndRebuildDataPinPropertySourceMap(UFlowNode& FlowNode); + void AddInputDataPinsToMap(UFlowNode& FlowNode); + void AddOutputDataPinsToMap(UFlowNode& FlowNode); + void AddFlowDataPinForProperty(const FProperty* Property, const UObject& ObjectContainer, int32 PropertyOwnerIndex); + + void AddPinMappingToNode( + UFlowNode& FlowNode, + const FName& FinalPinName, + const FName& OriginalPinName, + int32 PropertyOwnerIndex); + + bool DidAutoInputDataPinsChange() const; + bool DidAutoOutputDataPinsChange() const; + + static void DisambiguateDuplicateOutputDataPin( + FFlowPinSourceData& PinSourceData, + TSet& InOutUsedNames, + uint32 LogicalDuplicateIndex); + + static bool CheckIfProposedPinsMatchPreviousPins(const TArray& PrevPins, const TArray& ProposedPins); + +public: const TArray& AutoInputDataPinsPrev; const TArray& AutoOutputDataPinsPrev; - - TArray AutoInputDataPinsNext; - TArray AutoOutputDataPinsNext; + + TArray AutoInputDataPinsNext; + TArray AutoOutputDataPinsNext; }; + +#endif \ No newline at end of file diff --git a/Source/Flow/Public/Types/FlowPinEnums.h b/Source/Flow/Public/Types/FlowPinEnums.h index 53bbecfb4..8998bb7da 100644 --- a/Source/Flow/Public/Types/FlowPinEnums.h +++ b/Source/Flow/Public/Types/FlowPinEnums.h @@ -88,6 +88,9 @@ enum class EFlowDataPinResolveResult : uint8 // Tried to extract with a null FlowNodeBase FailedNullFlowNodeBase, + // The pin is not connected to a node (used in reroutes) + FailedNotConnected, + // Failed with an error message (see the error log) FailedWithError, @@ -200,4 +203,4 @@ namespace EFlowSingleFromArray_Classifiers } } } -}; \ No newline at end of file +}; diff --git a/Source/Flow/Public/Types/FlowPinType.h b/Source/Flow/Public/Types/FlowPinType.h index a9201d41c..03f217702 100644 --- a/Source/Flow/Public/Types/FlowPinType.h +++ b/Source/Flow/Public/Types/FlowPinType.h @@ -39,7 +39,7 @@ struct FFlowPinType // Value resolution FLOW_API virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const; - FLOW_API virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const; + FLOW_API virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const; #if WITH_EDITOR // Editor visualization diff --git a/Source/Flow/Public/Types/FlowPinTypeNodeTemplates.h b/Source/Flow/Public/Types/FlowPinTypeNodeTemplates.h index a59cc3c24..0c32158a0 100644 --- a/Source/Flow/Public/Types/FlowPinTypeNodeTemplates.h +++ b/Source/Flow/Public/Types/FlowPinTypeNodeTemplates.h @@ -12,7 +12,7 @@ namespace FlowPinType { template - static bool PopulateResultTemplate(const UObject& PropertyOwnerObject, const UFlowNode& FlowNode, const FFlowPin& Pin, FFlowDataPinResult& OutResult) + static bool PopulateResultTemplate(const UObject& PropertyOwnerObject, const UFlowNode& FlowNode, const FName& PropertyName, FFlowDataPinResult& OutResult) { using TValue = typename TPinType::ValueType; using TWrapper = typename TPinType::WrapperType; @@ -21,7 +21,7 @@ namespace FlowPinType TInstancedStruct ValueStruct; const FProperty* FoundProperty = nullptr; - if (!FlowNode.TryFindPropertyByPinName(PropertyOwnerObject, Pin.PinName, FoundProperty, ValueStruct)) + if (!FlowNode.TryFindPropertyByPinName(PropertyOwnerObject, PropertyName, FoundProperty, ValueStruct)) { OutResult.Result = EFlowDataPinResolveResult::FailedUnknownPin; return false; diff --git a/Source/Flow/Public/Types/FlowPinTypeTemplates.h b/Source/Flow/Public/Types/FlowPinTypeTemplates.h index 6bad39388..f3b0660dd 100644 --- a/Source/Flow/Public/Types/FlowPinTypeTemplates.h +++ b/Source/Flow/Public/Types/FlowPinTypeTemplates.h @@ -834,7 +834,7 @@ namespace FlowPinType for (int32 i = 0; i < Num; ++i) { const FSoftObjectPath Path = InnerSoftProp->GetPropertyValue(ArrHelper.GetRawPtr(i)).ToSoftObjectPath(); - OutValues.Add(Cast(Path.ResolveObject())); + OutValues.Add(Cast(Path.TryLoad())); } return EFlowDataPinResolveResult::Success; } @@ -859,7 +859,7 @@ namespace FlowPinType else if (const TSoftProperty* SoftObjProp = CastField(Property)) { const FSoftObjectPath Path = SoftObjProp->GetPropertyValue_InContainer(Container).ToSoftObjectPath(); - OutValues = { Cast(Path.ResolveObject()) }; + OutValues = { Cast(Path.TryLoad()) }; return EFlowDataPinResolveResult::Success; } else if (const FWeakObjectProperty* WeakProp = CastField(Property)) diff --git a/Source/Flow/Public/Types/FlowPinTypesStandard.h b/Source/Flow/Public/Types/FlowPinTypesStandard.h index 64e7a37df..7828c5dc0 100644 --- a/Source/Flow/Public/Types/FlowPinTypesStandard.h +++ b/Source/Flow/Public/Types/FlowPinTypesStandard.h @@ -101,7 +101,7 @@ struct FLOW_API FFlowPinType_Bool : public FFlowPinType virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; #endif - virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; // Int @@ -126,7 +126,7 @@ struct FLOW_API FFlowPinType_Int : public FFlowPinType virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; #endif - virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; // Int64 @@ -151,7 +151,7 @@ struct FLOW_API FFlowPinType_Int64 : public FFlowPinType virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; #endif - virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; // Float @@ -176,7 +176,7 @@ struct FLOW_API FFlowPinType_Float : public FFlowPinType virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; #endif - virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; // Double @@ -201,7 +201,7 @@ struct FLOW_API FFlowPinType_Double : public FFlowPinType virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; #endif - virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; // Name @@ -226,7 +226,7 @@ struct FLOW_API FFlowPinType_Name : public FFlowPinType virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; #endif - virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; // String @@ -251,7 +251,7 @@ struct FLOW_API FFlowPinType_String : public FFlowPinType virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; #endif - virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; // Text @@ -276,7 +276,7 @@ struct FLOW_API FFlowPinType_Text : public FFlowPinType virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; #endif - virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; // Enum @@ -303,7 +303,7 @@ struct FLOW_API FFlowPinType_Enum : public FFlowPinType virtual UObject* GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const override; #endif - virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; // Vector @@ -329,7 +329,7 @@ struct FLOW_API FFlowPinType_Vector : public FFlowPinType virtual UObject* GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const override; #endif - virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; // Rotator @@ -355,7 +355,7 @@ struct FLOW_API FFlowPinType_Rotator : public FFlowPinType virtual UObject* GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const override; #endif - virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; // Transform @@ -381,7 +381,7 @@ struct FLOW_API FFlowPinType_Transform : public FFlowPinType virtual UObject* GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const override; #endif - virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; // GameplayTag @@ -407,7 +407,7 @@ struct FLOW_API FFlowPinType_GameplayTag : public FFlowPinType virtual UObject* GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const override; #endif - virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; // GameplayTagContainer @@ -434,7 +434,7 @@ struct FLOW_API FFlowPinType_GameplayTagContainer : public FFlowPinType virtual UObject* GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const override; #endif - virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; // InstancedStruct @@ -460,7 +460,7 @@ struct FLOW_API FFlowPinType_InstancedStruct : public FFlowPinType virtual UObject* GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const override; #endif - virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; // Object @@ -489,7 +489,7 @@ struct FLOW_API FFlowPinType_Object : public FFlowPinType static UClass* TryGetMetaClassFromProperty(const FProperty& MetaDataProperty); #endif - virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; // Class @@ -515,5 +515,5 @@ struct FLOW_API FFlowPinType_Class : public FFlowPinType virtual UObject* GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const override; #endif - virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FFlowPin& Pin, FFlowDataPinResult& OutResult) const override; + virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; \ No newline at end of file diff --git a/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp b/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp index 9037a4f1a..4a731fe1d 100644 --- a/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp +++ b/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp @@ -26,6 +26,8 @@ void UFlowDebuggerSubsystem::Initialize(FSubsystemCollectionBase& Collection) Super::Initialize(Collection); FFlowExecutionGate::SetGate(this); + + SetFlowDebuggerState(EFlowDebuggerState::InitialRunning, nullptr); } void UFlowDebuggerSubsystem::Deinitialize() @@ -35,6 +37,8 @@ void UFlowDebuggerSubsystem::Deinitialize() FFlowExecutionGate::SetGate(nullptr); } + SetFlowDebuggerState(EFlowDebuggerState::Invalid, nullptr); + Super::Deinitialize(); } @@ -373,20 +377,6 @@ bool UFlowDebuggerSubsystem::HasAnyBreakpointsMatching(const TWeakObjectPtrGetFlowAsset(); - HaltedOnNodeGuid = Node->NodeGuid; -} - -void UFlowDebuggerSubsystem::ClearHaltFlowExecution() -{ - bHaltFlowExecution = false; - HaltedOnFlowAssetInstance.Reset(); - HaltedOnNodeGuid.Invalidate(); -} - void UFlowDebuggerSubsystem::ClearLastHitBreakpoint() { if (!LastHitNodeGuid.IsValid()) @@ -428,11 +418,9 @@ void UFlowDebuggerSubsystem::MarkAsHit(const UFlowNode* FlowNode) LastHitNodeGuid = FlowNode->NodeGuid; LastHitPinName = NAME_None; - RequestHaltFlowExecution(FlowNode); - OnDebuggerBreakpointHit.Broadcast(FlowNode); - PauseSession(); + PauseSession(*FlowNode->GetFlowAsset()); } } } @@ -451,98 +439,26 @@ void UFlowDebuggerSubsystem::MarkAsHit(const UFlowNode* FlowNode, const FName& P LastHitNodeGuid = FlowNode->NodeGuid; LastHitPinName = PinName; - RequestHaltFlowExecution(FlowNode); - OnDebuggerBreakpointHit.Broadcast(FlowNode); - PauseSession(); + PauseSession(*FlowNode->GetFlowAsset()); } } } -void UFlowDebuggerSubsystem::PauseSession() +void UFlowDebuggerSubsystem::PauseSession(UFlowAsset& FlowAssetInstance) { - SetPause(true); + SetFlowDebuggerState(EFlowDebuggerState::Paused, &FlowAssetInstance); } -void UFlowDebuggerSubsystem::ResumeSession() +void UFlowDebuggerSubsystem::ResumeSession(UFlowAsset& FlowAssetInstance) { - SetPause(false); + SetFlowDebuggerState(EFlowDebuggerState::Resumed, &FlowAssetInstance); } -void UFlowDebuggerSubsystem::SetPause(const bool bPause) +void UFlowDebuggerSubsystem::StopSession() { - // experimental implementation, won't work yet, shows intent for future development - // here be dragons: same as APlayerController::SetPause, but we allow debugger to pause on clients - - // Default bWasPaused to opposite of bPause - // (which we hope to get a better measure if we can get access to what we need) - bool bWasPaused = !bPause; - - AGameModeBase* GameMode = nullptr; - APlayerController* PlayerController = nullptr; - - if (HaltedOnFlowAssetInstance.IsValid()) - { - if (const UWorld* World = HaltedOnFlowAssetInstance->GetWorld()) - { - GameMode = World->GetAuthGameMode(); - - if (IsValid(GameMode)) - { - bWasPaused = GameMode->IsPaused(); - } - - const UGameInstance* GameInstance = World->GetGameInstance(); - if (IsValid(GameInstance)) - { - PlayerController = GameInstance->GetFirstLocalPlayerController(); - } - } - } - - if (bWasPaused != bPause) - { - if (bPause) - { - // Pausing (from an unpaused state) - - if (IsValid(PlayerController)) - { - if (IsValid(GameMode)) - { - GameMode->SetPause(PlayerController); - } - - if (AWorldSettings* WorldSettings = PlayerController->GetWorldSettings()) - { - WorldSettings->ForceNetUpdate(); - } - } - - // Broadcast the Pause event - OnDebuggerPaused.Broadcast(*HaltedOnFlowAssetInstance.Get()); - } - else - { - // Resuming (from a paused state) - - ClearHaltFlowExecution(); - - // Replay any Flow propagation that was deferred while execution was halted. - FFlowExecutionGate::FlushDeferredTriggerInputs(); - - // Intentionally do NOT clear hit flags here. The editor-specific resume path will clear the last-hit - // breakpoint safely (without racing against immediate breakpoint hits during flush). - if (IsValid(GameMode)) - { - (void)GameMode->ClearPause(); - } - - // Broadcast the Resume event - OnDebuggerResumed.Broadcast(*HaltedOnFlowAssetInstance.Get()); - } - } + SetFlowDebuggerState(EFlowDebuggerState::Invalid, nullptr); } void UFlowDebuggerSubsystem::ClearHitBreakpoints() @@ -559,8 +475,7 @@ void UFlowDebuggerSubsystem::ClearHitBreakpoints() } } - LastHitNodeGuid.Invalidate(); - LastHitPinName = NAME_None; + ClearLastHitBreakpoint(); } bool UFlowDebuggerSubsystem::IsBreakpointHit(const FGuid& NodeGuid) @@ -588,3 +503,77 @@ void UFlowDebuggerSubsystem::SaveSettings() UFlowDebuggerSettings* Settings = GetMutableDefault(); Settings->SaveConfig(); } + +void UFlowDebuggerSubsystem::SetFlowDebuggerState(EFlowDebuggerState NextState, UFlowAsset* FlowAssetInstance) +{ + if (FlowDebuggerState == NextState) + { + return; + } + + const EFlowDebuggerState PrevState = FlowDebuggerState; + FlowDebuggerState = NextState; + + ManageGameModePaused(PrevState, NextState, FlowAssetInstance); + + // OnFlowDebuggerStateChanged MUST be the final operation in SetFlowDebuggerState + // as it could potentially cause a new FlowDebuggerState entered + { + OnFlowDebuggerStateChanged(PrevState, NextState, FlowAssetInstance); + return; + } +} + +void UFlowDebuggerSubsystem::ManageGameModePaused(EFlowDebuggerState PrevState, EFlowDebuggerState NextState, UFlowAsset* FlowAssetInstance) +{ + if (!IsValid(FlowAssetInstance)) + { + return; + } + + const UWorld* World = FlowAssetInstance->GetWorld(); + AGameModeBase* GameMode = World->GetAuthGameMode(); + if (!IsValid(GameMode)) + { + // No game mode on non-server instances + return; + } + + using namespace EFlowDebuggerState_Classifiers; + + const bool bIsPauseGameModeStatePrev = IsPausedGameState(PrevState); + const bool bIsPauseGameModeStateNext = IsPausedGameState(NextState); + + if (bIsPauseGameModeStatePrev == bIsPauseGameModeStateNext) + { + return; + } + + // Gather some pointers + const UGameInstance* GameInstance = World->GetGameInstance(); + APlayerController* FirstLocalPlayerController = nullptr; + if (IsValid(GameInstance)) + { + FirstLocalPlayerController = GameInstance->GetFirstLocalPlayerController(); + } + + // Change the GameMode pause state + if (bIsPauseGameModeStateNext) + { + if (FirstLocalPlayerController) + { + GameMode->SetPause(FirstLocalPlayerController); + + if (AWorldSettings* WorldSettings = World->GetWorldSettings()) + { + WorldSettings->ForceNetUpdate(); + } + } + } + else + { + // Intentionally do NOT clear hit flags here. The editor-specific resume path will clear the last-hit + // breakpoint safely (without racing against immediate breakpoint hits during flush). + (void)GameMode->ClearPause(); + } +} diff --git a/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h b/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h index 2267ac318..ef454af6d 100644 --- a/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h +++ b/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h @@ -6,6 +6,7 @@ #include "Debugger/FlowDebuggerTypes.h" #include "Interfaces/FlowExecutionGate.h" +#include "Types/FlowEnumUtils.h" #include "FlowDebuggerSubsystem.generated.h" @@ -14,6 +15,37 @@ class UEdGraphNode; class UFlowAsset; class UFlowNode; +UENUM() +enum class EFlowDebuggerState +{ + // Initialized, running, but never halted + InitialRunning, + + // Running after being pausing + Resumed, + + // Currently paused at a breakpoint + Paused, + + Max UMETA(Hidden), + Invalid = -1 UMETA(Hidden), + Min = 0 UMETA(Hidden), + + // Subranges for classifier checks + PausedGameFirst = Paused UMETA(Hidden), + PausedGameLast = Paused UMETA(Hidden), + + FlushDeferredTriggersFirst = Resumed UMETA(Hidden), + FlushDeferredTriggersLast = Resumed UMETA(Hidden), +}; +FLOW_ENUM_RANGE_VALUES(EFlowDebuggerState); + +namespace EFlowDebuggerState_Classifiers +{ + FORCEINLINE bool IsPausedGameState(EFlowDebuggerState State) { return FLOW_IS_ENUM_IN_SUBRANGE(State, EFlowDebuggerState::PausedGame); } + FORCEINLINE bool IsFlushDeferredTriggersState(EFlowDebuggerState State) { return FLOW_IS_ENUM_IN_SUBRANGE(State, EFlowDebuggerState::FlushDeferredTriggers); } +} + DECLARE_MULTICAST_DELEGATE_OneParam(FFlowAssetDebuggerEvent, const UFlowAsset& /*FlowAsset*/); DECLARE_MULTICAST_DELEGATE_OneParam(FFlowAssetDebuggerBreakpointHitEvent, const UFlowNode* /*FlowNode*/); @@ -42,7 +74,7 @@ class FLOWDEBUGGER_API UFlowDebuggerSubsystem : public UEngineSubsystem, public public: // IFlowExecutionGate - virtual bool IsFlowExecutionHalted() const override { return bHaltFlowExecution; } + virtual bool IsFlowExecutionHalted() const override { return EFlowDebuggerState_Classifiers::IsPausedGameState(FlowDebuggerState); } // -- virtual void AddBreakpoint(const FGuid& NodeGuid); @@ -79,9 +111,9 @@ class FLOWDEBUGGER_API UFlowDebuggerSubsystem : public UEngineSubsystem, public virtual void MarkAsHit(const UFlowNode* FlowNode); virtual void MarkAsHit(const UFlowNode* FlowNode, const FName& PinName); - virtual void PauseSession(); - virtual void ResumeSession(); - void SetPause(const bool bPause); + virtual void PauseSession(UFlowAsset& FlowAssetInstance); + virtual void ResumeSession(UFlowAsset& FlowAssetInstance); + virtual void StopSession(); /** * Clears the "currently hit" breakpoint only (node or pin). @@ -92,9 +124,12 @@ class FLOWDEBUGGER_API UFlowDebuggerSubsystem : public UEngineSubsystem, public /** Clears hit state for all breakpoints. Prefer ClearLastHitBreakpoint() for resume/step logic. */ virtual void ClearHitBreakpoints(); +private: + void SetFlowDebuggerState(EFlowDebuggerState NextState, UFlowAsset* FlowAssetInstance); + void ManageGameModePaused(EFlowDebuggerState PrevState, EFlowDebuggerState NextState, UFlowAsset* FlowAssetInstance); + protected: - void RequestHaltFlowExecution(const UFlowNode* Node); - void ClearHaltFlowExecution(); + virtual void OnFlowDebuggerStateChanged(EFlowDebuggerState PrevState, EFlowDebuggerState NextState, UFlowAsset* FlowAssetInstance) {} public: virtual bool IsBreakpointHit(const FGuid& NodeGuid); @@ -106,10 +141,8 @@ class FLOWDEBUGGER_API UFlowDebuggerSubsystem : public UEngineSubsystem, public FFlowAssetDebuggerBreakpointHitEvent OnDebuggerBreakpointHit; FFlowAssetDebuggerEvent OnDebuggerFlowAssetTemplateRemoved; -private: - bool bHaltFlowExecution = false; - TWeakObjectPtr HaltedOnFlowAssetInstance; - FGuid HaltedOnNodeGuid; +protected: + EFlowDebuggerState FlowDebuggerState = EFlowDebuggerState::Invalid; // Track the single breakpoint location that is currently "hit" (node or pin). FGuid LastHitNodeGuid; diff --git a/Source/FlowEditor/Private/Asset/AssetDefinition_FlowAssetParams.cpp b/Source/FlowEditor/Private/Asset/AssetDefinition_FlowAssetParams.cpp index 1c9748aaa..0f726f34d 100644 --- a/Source/FlowEditor/Private/Asset/AssetDefinition_FlowAssetParams.cpp +++ b/Source/FlowEditor/Private/Asset/AssetDefinition_FlowAssetParams.cpp @@ -2,17 +2,10 @@ #include "Asset/AssetDefinition_FlowAssetParams.h" #include "Asset/FlowAssetParams.h" -#include "FlowAsset.h" +#include "Asset/FlowAssetParamsUtils.h" #include "FlowEditorLogChannels.h" #include "FlowEditorModule.h" -#include "Types/FlowDataPinValuesStandard.h" -#include "AssetRegistry/AssetRegistryModule.h" -#include "AssetToolsModule.h" #include "ContentBrowserMenuContexts.h" -#include "ContentBrowserModule.h" -#include "FileHelpers.h" -#include "IContentBrowserSingleton.h" -#include "SourceControlHelpers.h" #include "ToolMenus.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(AssetDefinition_FlowAssetParams) @@ -36,7 +29,7 @@ TSoftClassPtr UAssetDefinition_FlowAssetParams::GetAssetClass() const TConstArrayView UAssetDefinition_FlowAssetParams::GetAssetCategories() const { - static const auto Categories = { FFlowAssetCategoryPaths::Flow }; + static const auto Categories = {FFlowAssetCategoryPaths::Flow}; return Categories; } @@ -70,83 +63,37 @@ namespace MenuExtension_FlowAssetParams return; } - FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked("AssetTools"); - const FString PackagePath = FPackageName::GetLongPackagePath(ParentParams->GetPackage()->GetPathName()); - const FString BaseAssetName = ParentParams->GetName(); - - FString UniquePackageName, UniqueAssetName; - AssetToolsModule.Get().CreateUniqueAssetName(PackagePath + TEXT("/") + BaseAssetName, TEXT(""), UniquePackageName, UniqueAssetName); - if (UniqueAssetName.IsEmpty()) - { - UE_LOG(LogFlowEditor, Error, TEXT("Failed to generate unique asset name for child params of %s"), *BaseAssetName); - return; - } - - UFlowAssetParams* NewParams = Cast( - AssetToolsModule.Get().CreateAsset(UniqueAssetName, PackagePath, ParentParams->GetClass(), nullptr)); - if (!IsValid(NewParams)) - { - UE_LOG(LogFlowEditor, Error, TEXT("Failed to create child Flow Asset Params: %s"), *UniqueAssetName); - return; - } - - if (USourceControlHelpers::IsAvailable()) - { - const FString FileName = USourceControlHelpers::PackageFilename(NewParams->GetPathName()); - if (!USourceControlHelpers::CheckOutOrAddFile(FileName)) - { - UE_LOG(LogFlowEditor, Warning, TEXT("Failed to check out/add %s; saved in-memory only"), *NewParams->GetPathName()); - } - } - - NewParams->ConfigureFlowAssetParams(ParentParams->OwnerFlowAsset, ParentParams, ParentParams->Properties); - - // Save the package (force save even if not prompted) - UPackage* Package = NewParams->GetPackage(); - TArray PackagesToSave = { Package }; - - // Saves without dialog/prompt - const bool bForceSave = true; - if (!UEditorLoadingAndSavingUtils::SavePackages(PackagesToSave, bForceSave)) - { - UE_LOG(LogFlowEditor, Error, TEXT("Failed to save child Flow Asset Params: %s"), *NewParams->GetPathName()); - return; - } - - FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked("ContentBrowser"); - FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked("AssetRegistry"); - AssetRegistryModule.Get().AssetCreated(NewParams); - TArray AssetsToSync = { NewParams }; - ContentBrowserModule.Get().SyncBrowserToAssets(AssetsToSync, true); + constexpr bool bShowDialogs = true; + FFlowAssetParamsUtils::CreateChildParamsAsset(*ParentParams, bShowDialogs); } static void RegisterContextMenu() { UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateLambda([]() + { + FToolMenuOwnerScoped OwnerScoped(UE_MODULE_NAME); + UToolMenu* Menu = UE::ContentBrowser::ExtendToolMenu_AssetContextMenu(UFlowAssetParams::StaticClass()); + + FToolMenuSection& Section = Menu->FindOrAddSection("GetAssetActions"); + Section.AddDynamicEntry("Flow Asset Params Commands", FNewToolMenuSectionDelegate::CreateLambda([](FToolMenuSection& InSection) { - FToolMenuOwnerScoped OwnerScoped(UE_MODULE_NAME); - UToolMenu* Menu = UE::ContentBrowser::ExtendToolMenu_AssetContextMenu(UFlowAssetParams::StaticClass()); - - FToolMenuSection& Section = Menu->FindOrAddSection("GetAssetActions"); - Section.AddDynamicEntry("Flow Asset Params Commands", FNewToolMenuSectionDelegate::CreateLambda([](FToolMenuSection& InSection) - { - const TAttribute Label = LOCTEXT("FlowAssetParams_CreateChildParams", "Create Child Params"); - const TAttribute ToolTip = LOCTEXT("FlowAssetParams_CreateChildParamsTooltip", "Creates a new Flow Asset Params inheriting from the selected params."); - const FSlateIcon Icon = FSlateIcon(); - - FToolUIAction UIAction; - UIAction.ExecuteAction = FToolMenuExecuteAction::CreateStatic(&ExecuteCreateChildParams); - UIAction.CanExecuteAction = FToolMenuCanExecuteAction::CreateLambda([](const FToolMenuContext& InContext) - { - const UContentBrowserAssetContextMenuContext* Context = UContentBrowserAssetContextMenuContext::FindContextWithAssets(InContext); - return Context && Context->SelectedAssets.Num() == 1; - }); - InSection.AddMenuEntry("FlowAssetParams_CreateChildParams", Label, ToolTip, Icon, UIAction); - })); + const TAttribute Label = LOCTEXT("FlowAssetParams_CreateChildParams", "Create Child Params"); + const TAttribute ToolTip = LOCTEXT("FlowAssetParams_CreateChildParamsTooltip", "Creates a new Flow Asset Params inheriting from the selected params."); + const FSlateIcon Icon = FSlateIcon(); + + FToolUIAction UIAction; + UIAction.ExecuteAction = FToolMenuExecuteAction::CreateStatic(&ExecuteCreateChildParams); + UIAction.CanExecuteAction = FToolMenuCanExecuteAction::CreateLambda([](const FToolMenuContext& InContext) + { + const UContentBrowserAssetContextMenuContext* Context = UContentBrowserAssetContextMenuContext::FindContextWithAssets(InContext); + return Context && Context->SelectedAssets.Num() == 1; + }); + InSection.AddMenuEntry("FlowAssetParams_CreateChildParams", Label, ToolTip, Icon, UIAction); })); + })); } static FDelayedAutoRegisterHelper DelayedAutoRegister(EDelayedRegisterRunPhase::EndOfEngineInit, &RegisterContextMenu); } -#undef LOCTEXT_NAMESPACE \ No newline at end of file +#undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/Asset/FlowAssetParamsFactory.cpp b/Source/FlowEditor/Private/Asset/FlowAssetParamsFactory.cpp new file mode 100644 index 000000000..12608a738 --- /dev/null +++ b/Source/FlowEditor/Private/Asset/FlowAssetParamsFactory.cpp @@ -0,0 +1,170 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Asset/FlowAssetParamsFactory.h" + +#include "Asset/FlowAssetParams.h" +#include "Asset/FlowAssetParamsUtils.h" + +#include "ContentBrowserModule.h" +#include "Framework/Application/SlateApplication.h" +#include "IContentBrowserSingleton.h" +#include "Misc/MessageDialog.h" +#include "Widgets/Input/SButton.h" +#include "Widgets/Layout/SBorder.h" +#include "Widgets/SBoxPanel.h" +#include "Widgets/SWindow.h" +#include "Widgets/Text/STextBlock.h" + +#define LOCTEXT_NAMESPACE "FlowAssetParamsFactory" + +UFlowAssetParamsFactory::UFlowAssetParamsFactory() +{ + SupportedClass = UFlowAssetParams::StaticClass(); + + bCreateNew = true; + bEditorImport = false; + bEditAfterNew = true; +} + +bool UFlowAssetParamsFactory::ConfigureProperties() +{ + SelectedParentParams.Reset(); + return ShowParentPickerDialog(); +} + +bool UFlowAssetParamsFactory::ShowParentPickerDialog() +{ + const FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked("ContentBrowser"); + + // Holds current required parent selection (the params asset) + TSharedPtr CurrentSelection = MakeShared(); + + FAssetPickerConfig ParamsPickerConfig; + ParamsPickerConfig.Filter.ClassPaths.Add(UFlowAssetParams::StaticClass()->GetClassPathName()); + ParamsPickerConfig.InitialAssetViewType = EAssetViewType::List; + ParamsPickerConfig.SelectionMode = ESelectionMode::Single; + ParamsPickerConfig.bAllowNullSelection = false; + ParamsPickerConfig.bFocusSearchBoxWhenOpened = true; + + ParamsPickerConfig.OnAssetSelected = FOnAssetSelected::CreateLambda( + [CurrentSelection](const FAssetData& AssetData) + { + *CurrentSelection = AssetData; + }); + + const TSharedRef ParamsPicker = ContentBrowserModule.Get().CreateAssetPicker(ParamsPickerConfig); + + bool bUserAccepted = false; + + TSharedPtr PickerWindow = SNew(SWindow) + .Title(LOCTEXT("CreateChildParamsTitle", "Create Flow Asset Params")) + .SizingRule(ESizingRule::UserSized) + .ClientSize(FVector2D(850, 600)) + .SupportsMinimize(false) + .SupportsMaximize(false); + + PickerWindow->SetContent( + SNew(SBorder) + .Padding(8.f) + [ + SNew(SVerticalBox) + + // Parent picker + + SVerticalBox::Slot() + .FillHeight(1.0f) + .Padding(0.f, 0.f, 0.f, 8.f) + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .AutoHeight() + [ + ParamsPicker + ] + ] + + // Buttons + + SVerticalBox::Slot() + .AutoHeight() + .HAlign(HAlign_Right) + [ + SNew(SHorizontalBox) + + + SHorizontalBox::Slot() + .AutoWidth() + .Padding(0.f, 0.f, 6.f, 0.f) + [ + SNew(SButton) + .Text(LOCTEXT("OK", "OK")) + .IsEnabled_Lambda([CurrentSelection]() + { + return CurrentSelection->IsValid(); + }) + .OnClicked_Lambda([&bUserAccepted, PickerWindow]() + { + bUserAccepted = true; + PickerWindow->RequestDestroyWindow(); + return FReply::Handled(); + }) + ] + + + SHorizontalBox::Slot() + .AutoWidth() + [ + SNew(SButton) + .Text(LOCTEXT("Cancel", "Cancel")) + .OnClicked_Lambda([PickerWindow]() + { + PickerWindow->RequestDestroyWindow(); + return FReply::Handled(); + }) + ] + ] + ] + ); + + FSlateApplication::Get().AddModalWindow(PickerWindow.ToSharedRef(), nullptr); + + if (!bUserAccepted || !CurrentSelection->IsValid()) + { + return false; + } + + SelectedParentParams = TSoftObjectPtr(CurrentSelection->ToSoftObjectPath()); + if (SelectedParentParams.IsNull()) + { + FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("NoParentSelected", "You must select a parent Flow Asset Params asset.")); + + return false; + } + + return true; +} + +UObject* UFlowAssetParamsFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) +{ + if (SelectedParentParams.IsNull()) + { + FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("FactoryMissingParent", "No parent params were selected.")); + + return nullptr; + } + + UFlowAssetParams* Parent = SelectedParentParams.LoadSynchronous(); + if (!IsValid(Parent)) + { + FMessageDialog::Open(EAppMsgType::Ok, + FText::Format(LOCTEXT("ParentLoadFail", "Failed to load selected parent params:\n{0}"), + FText::FromString(SelectedParentParams.ToString()))); + + return nullptr; + } + + FText FailureReason; + constexpr bool bShowDialogs = true; + UFlowAssetParams* NewParams = FFlowAssetParamsUtils::CreateChildParamsAsset(*Parent, bShowDialogs, &FailureReason); + + // FactoryCreateNew expects the created asset (or nullptr). The helper already shows dialogs/logs on failure. + return NewParams; +} + +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/FlowEditor/Private/Asset/FlowDebugEditorSubsystem.cpp b/Source/FlowEditor/Private/Asset/FlowDebugEditorSubsystem.cpp index f00f98c80..eed0440a5 100644 --- a/Source/FlowEditor/Private/Asset/FlowDebugEditorSubsystem.cpp +++ b/Source/FlowEditor/Private/Asset/FlowDebugEditorSubsystem.cpp @@ -9,6 +9,7 @@ #include "Graph/Nodes/FlowGraphNode.h" #include "Interfaces/FlowExecutionGate.h" #include "FlowAsset.h" +#include "FlowSubsystem.h" #include "Editor/UnrealEdEngine.h" #include "Engine/Engine.h" @@ -71,16 +72,14 @@ void UFlowDebugEditorSubsystem::OnBeginPIE(const bool bIsSimulating) void UFlowDebugEditorSubsystem::OnResumePIE(const bool bIsSimulating) { - // Clear only the last-hit breakpoint to return to enabled/disabled visuals without racing against - // a newly hit breakpoint during FlushDeferredTriggerInputs(). - ClearHaltFlowExecution(); - ClearLastHitBreakpoint(); - // Editor-level resume event (also used by Advance Single Frame). // This does not necessarily flow through AGameModeBase::ClearPause(), so we must unhalt Flow here. - ResumeSession(); + ClearLastHitBreakpoint(); - FFlowExecutionGate::FlushDeferredTriggerInputs(); + if (HaltedOnFlowAssetInstance.IsValid()) + { + ResumeSession(*HaltedOnFlowAssetInstance.Get()); + } } void UFlowDebugEditorSubsystem::OnEndPIE(const bool bIsSimulating) @@ -88,8 +87,7 @@ void UFlowDebugEditorSubsystem::OnEndPIE(const bool bIsSimulating) // Ensure we don't carry over a halted state between PIE sessions. ClearHitBreakpoints(); - ClearHaltFlowExecution(); - FFlowExecutionGate::FlushDeferredTriggerInputs(); + StopSession(); for (const TPair, TSharedPtr>& Log : RuntimeLogs) { @@ -117,27 +115,86 @@ void UFlowDebugEditorSubsystem::OnEndPIE(const bool bIsSimulating) } } -void UFlowDebugEditorSubsystem::PauseSession() +void UFlowDebugEditorSubsystem::PauseSession(UFlowAsset& FlowAssetInstance) +{ + HaltedOnFlowAssetInstance = &FlowAssetInstance; + + Super::PauseSession(FlowAssetInstance); +} + +void UFlowDebugEditorSubsystem::ResumeSession(UFlowAsset& FlowAssetInstance) { - // do not call Super, non-PIE world has its only Pause/Resume logic + HaltedOnFlowAssetInstance = &FlowAssetInstance; - constexpr bool bShouldBePaused = true; - const bool bWasPaused = GUnrealEd->SetPIEWorldsPaused(bShouldBePaused); - if (!bWasPaused) + Super::ResumeSession(FlowAssetInstance); +} + +void UFlowDebugEditorSubsystem::StopSession() +{ + // Drop any pending deferred triggers — we are stopping the session entirely + if (HaltedOnFlowAssetInstance.IsValid()) { - GUnrealEd->PlaySessionPaused(); + UFlowSubsystem* FlowSubsystem = HaltedOnFlowAssetInstance->GetFlowSubsystem(); + + if (IsValid(FlowSubsystem)) + { + FlowSubsystem->ClearAllDeferredTriggerScopes(); + } } + + HaltedOnFlowAssetInstance.Reset(); + + Super::StopSession(); } -void UFlowDebugEditorSubsystem::ResumeSession() +void UFlowDebugEditorSubsystem::OnFlowDebuggerStateChanged(EFlowDebuggerState PrevState, EFlowDebuggerState NextState, UFlowAsset* FlowAssetInstance) { - // do not call Super, non-PIE world has its only Pause/Resume logic + check(PrevState != NextState); + + using namespace EFlowDebuggerState_Classifiers; - constexpr bool bShouldBePaused = false; - const bool bWasPaused = GUnrealEd->SetPIEWorldsPaused(bShouldBePaused); - if (bWasPaused) + const bool bIsPausedGameStatePrev = IsPausedGameState(PrevState); + const bool bIsPausedGameStateNext = IsPausedGameState(NextState); + + // Handle Pause/Unpause of the game & pie systems + if (bIsPausedGameStatePrev != bIsPausedGameStateNext) + { + const bool bWasPaused = GUnrealEd->SetPIEWorldsPaused(bIsPausedGameStateNext); + + if (bIsPausedGameStateNext && !bWasPaused) + { + GUnrealEd->PlaySessionPaused(); + } + else if (!bIsPausedGameStateNext && bWasPaused) + { + GUnrealEd->PlaySessionResumed(); + } + } + + // Issue the broadcasts for specific state entry + FLOW_ASSERT_ENUM_MAX(EFlowDebuggerState, 3); + if (NextState == EFlowDebuggerState::Paused) { - GUnrealEd->PlaySessionResumed(); + OnDebuggerPaused.Broadcast(*FlowAssetInstance); + } + else if (NextState == EFlowDebuggerState::Resumed) + { + OnDebuggerResumed.Broadcast(*FlowAssetInstance); + } + + UFlowSubsystem* FlowSubsystem = + IsValid(FlowAssetInstance) ? + FlowAssetInstance->GetFlowSubsystem() : + nullptr; + + if (FlowSubsystem && IsFlushDeferredTriggersState(NextState)) + { + // Flush any deferred triggers now that halt is cleared. + FlowSubsystem->TryFlushAllDeferredTriggerScopes(); + + // NOTE (gtaylor) this flush needs to be the last thing we do in this function + // (thus the explicit return to emphasize it), as this flush can be interrupted by another breakpoint + return; } } diff --git a/Source/FlowEditor/Private/DetailCustomizations/FlowPinCustomization.cpp b/Source/FlowEditor/Private/DetailCustomizations/FlowPinCustomization.cpp index e5dfbfb30..e19623835 100644 --- a/Source/FlowEditor/Private/DetailCustomizations/FlowPinCustomization.cpp +++ b/Source/FlowEditor/Private/DetailCustomizations/FlowPinCustomization.cpp @@ -26,8 +26,6 @@ void FFlowPinCustomization::OnChildPropertyValueChanged() { if (FFlowPin* FlowPin = GetFlowPin()) { - FlowPin->PostEditChangedPinTypeOrSubCategorySource(); - IFlowExtendedPropertyTypeCustomization::OnAnyChildPropertyChanged(); } } diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index 68b6ce845..0995dca24 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -15,6 +15,7 @@ #include "FlowPinSubsystem.h" #include "FlowSettings.h" #include "AddOns/FlowNodeAddOn.h" +#include "Graph/Nodes/FlowGraphNode_Reroute.h" #include "Nodes/FlowNode.h" #include "Nodes/FlowNodeAddOnBlueprint.h" #include "Nodes/FlowNodeBlueprint.h" @@ -876,24 +877,25 @@ TSharedPtr UFlowGraphSchema::GetCreateCommentAction() cons PRAGMA_DISABLE_DEPRECATION_WARNINGS void UFlowGraphSchema::OnPinConnectionDoubleCicked(UEdGraphPin* PinA, UEdGraphPin* PinB, const FVector2D& GraphPosition) const { - if (!FFlowPin::IsExecPinCategory(PinA->PinType.PinCategory) || !FFlowPin::IsExecPinCategory(PinB->PinType.PinCategory)) - { - // Disallowing Reroute node creation for non-exec connections (until we have a good solution for it) - - return; - } - const FScopedTransaction Transaction(LOCTEXT("CreateFlowRerouteNodeOnWire", "Create Flow Reroute Node")); const FVector2D NodeSpacerSize(42.0f, 24.0f); const FVector2D KnotTopLeft = GraphPosition - (NodeSpacerSize * 0.5f); UEdGraph* ParentGraph = PinA->GetOwningNode()->GetGraph(); - UFlowGraphNode* NewReroute = FFlowGraphSchemaAction_NewNode::CreateNode(ParentGraph, nullptr, UFlowNode_Reroute::StaticClass(), KnotTopLeft, false); + UFlowGraphNode* NewEdNode = FFlowGraphSchemaAction_NewNode::CreateNode(ParentGraph, nullptr, UFlowNode_Reroute::StaticClass(), KnotTopLeft, false); + UFlowGraphNode_Reroute* NewRerouteEdNode = Cast(NewEdNode); - PinA->BreakLinkTo(PinB); - PinA->MakeLinkTo((PinA->Direction == EGPD_Output) ? NewReroute->InputPins[0] : NewReroute->OutputPins[0]); - PinB->MakeLinkTo((PinB->Direction == EGPD_Output) ? NewReroute->InputPins[0] : NewReroute->OutputPins[0]); + if (PinA->Direction == EGPD_Output) + { + check(PinB->Direction == EGPD_Input && PinA->Direction == EGPD_Output); + NewRerouteEdNode->ConfigureRerouteNodeFromPinConnections(*PinB, *PinA); + } + else + { + check(PinA->Direction == EGPD_Input && PinB->Direction == EGPD_Output); + NewRerouteEdNode->ConfigureRerouteNodeFromPinConnections(*PinA, *PinB); + } } PRAGMA_ENABLE_DEPRECATION_WARNINGS diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 6626c8173..92a0910ce 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -1140,20 +1140,7 @@ void UFlowGraphNode::GetPinHoverText(const UEdGraphPin& Pin, FString& HoverTextO if (IsValid(FlowNodeBase)) { - if (GraphPinObj->Direction == EGPD_Input) - { - // Input pins: do a pin resolve to source the value - DataResult = FlowNodeBase->TryResolveDataPin(GraphPinObj->PinName); - } - else - { - // Output pins: ask this node what it supplies for that output data pin - const UFlowNode* FlowNode = Cast(FlowNodeBase); - if (FlowNode) - { - DataResult = IFlowDataPinValueSupplierInterface::Execute_TrySupplyDataPin(FlowNode, GraphPinObj->PinName); - } - } + DataResult = FlowNodeBase->TryResolveDataPin(GraphPinObj->PinName); } FString ValueString; diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Branch.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Branch.cpp index 64b94ae75..bc9e37d6b 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Branch.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Branch.cpp @@ -8,7 +8,7 @@ UFlowGraphNode_Branch::UFlowGraphNode_Branch(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { - AssignedNodeClasses = {UFlowNode_Branch::StaticClass()}; + AssignedNodeClasses = {UFlowNode_Branch::StaticClass() }; } FSlateIcon UFlowGraphNode_Branch::GetIconAndTint(FLinearColor& OutColor) const diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Reroute.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Reroute.cpp index 63422adcd..eb5a83da5 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Reroute.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode_Reroute.cpp @@ -29,3 +29,29 @@ bool UFlowGraphNode_Reroute::CanPlaceBreakpoints() const { return false; } + +#if WITH_EDITOR +void UFlowGraphNode_Reroute::ConfigureRerouteNodeFromPinConnections(UEdGraphPin& InPin, UEdGraphPin& OutPin) +{ + UFlowNode_Reroute* NewRerouteTemplate = Cast(NodeInstance); + + UFlowGraphNode* FlowGraphNodeIn = Cast(InPin.GetOwningNode()); + UFlowNode* NodeIn = Cast(FlowGraphNodeIn->GetFlowNodeBase()); + + UFlowGraphNode* FlowGraphNodeOut = Cast(OutPin.GetOwningNode()); + UFlowNode* NodeOut = Cast(FlowGraphNodeOut->GetFlowNodeBase()); + + // Update the FlowPin structs on the UFlowNode_Reroute + NewRerouteTemplate->ConfigureInputPin(*NodeIn, InPin.PinType); + NewRerouteTemplate->ConfigureOutputPin(*NodeOut, OutPin.PinType); + + InPin.BreakLinkTo(&OutPin); + + // Copy the PinType information from the connected pins + InputPins[0]->PinType = InPin.PinType; + OutputPins[0]->PinType = OutPin.PinType; + + InPin.MakeLinkTo(OutputPins[0]); + OutPin.MakeLinkTo(InputPins[0]); +} +#endif diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp index 78c54ff23..f2b4a1186 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp @@ -29,6 +29,7 @@ #include "SNodePanel.h" #include "Styling/SlateColor.h" #include "TutorialMetaData.h" +#include "Styling/SlateStyleRegistry.h" #include "Widgets/Images/SImage.h" #include "Widgets/Input/SButton.h" #include "Widgets/Layout/SBorder.h" @@ -168,6 +169,80 @@ void SFlowGraphNode::GetOverlayBrushes(bool bSelected, const FVector2f& WidgetSi } } } + + // Node custom overlay icons + if (const UFlowNodeBase* FlowNodeBase = FlowGraphNode->GetFlowNodeBase()) + { + FName CornerIconBrushName = NAME_None; + FName CornerIconStyleSetName = NAME_None; + if (FlowNodeBase->GetCornerIcon(CornerIconBrushName, CornerIconStyleSetName)) + { + if (const FSlateBrush* CornerIconBrush = GetSlateBrush(CornerIconBrushName, CornerIconStyleSetName)) + { + FOverlayBrushInfo CornerIconInfo; + CornerIconInfo.Brush = CornerIconBrush; + CornerIconInfo.OverlayOffset.X = WidgetSize.X - (CornerIconBrush->ImageSize.X * .5f); + CornerIconInfo.OverlayOffset.Y = -CornerIconBrush->ImageSize.Y * .5f; + Brushes.Add(CornerIconInfo); + } + } + + TArray OverlayIcons; + FlowNodeBase->GetOverlayIcons(OverlayIcons, WidgetSize); + for (const FFlowNodeOverlayIcon& OverlayIcon : OverlayIcons) + { + if (OverlayIcon.BrushName.IsNone()) + { + continue; + } + + if (const FSlateBrush* IconBrush = GetSlateBrush(OverlayIcon.BrushName, OverlayIcon.StyleSetName)) + { + FOverlayBrushInfo IconBrushInfo; + IconBrushInfo.Brush = IconBrush; + IconBrushInfo.OverlayOffset = OverlayIcon.Offset; + Brushes.Add(IconBrushInfo); + } + } + } +} + +const FSlateBrush* SFlowGraphNode::GetSlateBrush(const FName BrushName, const FName StyleSetName) const +{ + if (!StyleSetName.IsNone()) + { + // If we have a specific Style Set Name try and find the brush there. + if (const ISlateStyle* AppStyle = FSlateStyleRegistry::FindSlateStyle(StyleSetName)) + { + const FSlateBrush* SlateBrush = AppStyle->GetBrush(BrushName); + if (SlateBrush != nullptr && SlateBrush != AppStyle->GetDefaultBrush()) + { + return SlateBrush; + } + } + } + else + { + // If we do not have a specific StyleSet Name then first search the default Flow Editor StyleSet + // and finally fallback to the default Unreal StyleSet. + + const FSlateBrush* SlateBrush = FFlowEditorStyle::Get()->GetBrush(BrushName); + + if (SlateBrush != nullptr && SlateBrush != FFlowEditorStyle::Get()->GetDefaultBrush()) + { + return SlateBrush; + } + else + { + SlateBrush = FAppStyle::GetBrush(BrushName); + if (SlateBrush != nullptr && SlateBrush != FAppStyle::GetDefaultBrush()) + { + return SlateBrush; + } + } + } + + return nullptr; } void SFlowGraphNode::GetPinBrush(const bool bLeftSide, const float WidgetWidth, const int32 PinIndex, const FFlowBreakpoint* Breakpoint, TArray& Brushes) const diff --git a/Source/FlowEditor/Public/Asset/FlowAssetParamsFactory.h b/Source/FlowEditor/Public/Asset/FlowAssetParamsFactory.h new file mode 100644 index 000000000..f31527681 --- /dev/null +++ b/Source/FlowEditor/Public/Asset/FlowAssetParamsFactory.h @@ -0,0 +1,34 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#pragma once + +#include "Factories/Factory.h" +#include "UObject/SoftObjectPtr.h" + +#include "FlowAssetParamsFactory.generated.h" + +class UFlowAssetParams; + +/** + * Factory for creating Flow Asset Params via the Content Browser "Add New" menu. + * This creation path is strictly for creating CHILD params: the user must select a Parent FlowAssetParams. + */ +UCLASS(HideCategories = Object) +class FLOWEDITOR_API UFlowAssetParamsFactory : public UFactory +{ + GENERATED_BODY() + +public: + UFlowAssetParamsFactory(); + + // UFactory + virtual bool ConfigureProperties() override; + virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; + // -- + +private: + // Required selection + TSoftObjectPtr SelectedParentParams; + + bool ShowParentPickerDialog(); +}; diff --git a/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h b/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h index cc874e10a..6833c0570 100644 --- a/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h +++ b/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h @@ -24,6 +24,8 @@ class FLOWEDITOR_API UFlowDebugEditorSubsystem : public UFlowDebuggerSubsystem protected: TMap, TSharedPtr> RuntimeLogs; + TWeakObjectPtr HaltedOnFlowAssetInstance; + virtual void OnInstancedTemplateAdded(UFlowAsset* AssetTemplate) override; virtual void OnInstancedTemplateRemoved(UFlowAsset* AssetTemplate) override; @@ -33,8 +35,10 @@ class FLOWEDITOR_API UFlowDebugEditorSubsystem : public UFlowDebuggerSubsystem virtual void OnResumePIE(const bool bIsSimulating); virtual void OnEndPIE(const bool bIsSimulating); - virtual void PauseSession() override; - virtual void ResumeSession() override; + virtual void PauseSession(UFlowAsset& FlowAssetInstance) override; + virtual void ResumeSession(UFlowAsset& FlowAssetInstance) override; + virtual void StopSession() override; + virtual void OnFlowDebuggerStateChanged(EFlowDebuggerState PrevState, EFlowDebuggerState NextState, UFlowAsset* FlowAssetInstance); void OnBreakpointHit(const UFlowNode* FlowNode) const; }; diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h index c01ccef93..c7a985ad3 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h @@ -31,7 +31,7 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode ////////////////////////////////////////////////////////////////////////// // Flow node -private: +protected: // The FlowNode or FlowNodeAddOn runtime instance that is being edited by this UFlowGraphNode UPROPERTY(Instanced) TObjectPtr NodeInstance; diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_Reroute.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_Reroute.h index a3c6dbd88..53d1f9fa7 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_Reroute.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_Reroute.h @@ -16,4 +16,6 @@ class FLOWEDITOR_API UFlowGraphNode_Reroute : public UFlowGraphNode // -- virtual bool CanPlaceBreakpoints() const override; + + void ConfigureRerouteNodeFromPinConnections(UEdGraphPin& InPin, UEdGraphPin &OutPin); }; diff --git a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h index 2428d41b9..140633284 100644 --- a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h @@ -42,7 +42,8 @@ class FLOWEDITOR_API SFlowGraphNode : public SGraphNode virtual void GetOverlayBrushes(bool bSelected, const FVector2f& WidgetSize, TArray& Brushes) const override; #endif // -- - + const FSlateBrush* GetSlateBrush(const FName BrushName, const FName StyleSetName) const; + // SGraphNode virtual void GetPinBrush(const bool bLeftSide, const float WidgetWidth, const int32 PinIndex, const struct FFlowBreakpoint* Breakpoint, TArray& Brushes) const; From 45b6e61dc697f4d591ffe58bdb320d49ace361ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sat, 14 Feb 2026 18:22:05 +0100 Subject: [PATCH 403/485] deprecated usage of custom getters in UDeveloperSettings classes --- Source/Flow/Private/FlowAsset.cpp | 2 +- Source/Flow/Private/FlowComponent.cpp | 4 +-- Source/Flow/Private/FlowSubsystem.cpp | 2 +- .../Nodes/Actor/FlowNode_ExecuteComponent.cpp | 4 +-- Source/Flow/Private/Nodes/FlowNode.cpp | 4 +-- .../Nodes/Graph/FlowNode_CustomEventBase.cpp | 2 +- .../Nodes/Graph/FlowNode_CustomInput.cpp | 2 +- .../Nodes/Graph/FlowNode_CustomOutput.cpp | 2 +- .../Private/Nodes/Graph/FlowNode_SubGraph.cpp | 4 +-- Source/Flow/Public/FlowSettings.h | 1 + .../Asset/AssetDefinition_FlowAsset.cpp | 2 +- .../Private/Asset/FlowAssetFactory.cpp | 2 +- Source/FlowEditor/Private/Find/FindInFlow.cpp | 31 +++++++--------- .../FlowEditor/Private/FlowEditorModule.cpp | 34 +++++++++--------- .../FlowGraphConnectionDrawingPolicy.cpp | 35 ++++++++++--------- .../Private/Graph/FlowGraphPinFactory.cpp | 2 +- .../Private/Graph/FlowGraphSchema.cpp | 12 +++---- .../Private/Graph/FlowGraphSettings.cpp | 11 +++--- .../Private/Graph/FlowGraphUtils.cpp | 2 +- .../Private/Graph/Nodes/FlowGraphNode.cpp | 13 +++---- .../Private/Graph/Widgets/SFlowGraphNode.cpp | 6 ++-- .../Graph/Widgets/SFlowGraphNode_SubGraph.cpp | 9 ++--- ...ssetTypeActions_FlowNodeAddOnBlueprint.cpp | 2 +- .../AssetTypeActions_FlowNodeBlueprint.cpp | 2 +- .../Private/Utils/SLevelEditorFlow.cpp | 2 +- .../Public/Graph/FlowGraphEditorSettings.h | 1 + .../Public/Graph/FlowGraphSettings.h | 3 +- 27 files changed, 96 insertions(+), 100 deletions(-) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 2e054f9dd..afbeae7f0 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -60,7 +60,7 @@ UFlowAsset::UFlowAsset(const FObjectInitializer& ObjectInitializer) AssetGuid = FGuid::NewGuid(); } - ExpectedOwnerClass = UFlowSettings::Get()->GetDefaultExpectedOwnerClass(); + ExpectedOwnerClass = GetDefault()->GetDefaultExpectedOwnerClass(); } #if WITH_EDITOR diff --git a/Source/Flow/Private/FlowComponent.cpp b/Source/Flow/Private/FlowComponent.cpp index d26b4551d..4ef2ef880 100644 --- a/Source/Flow/Private/FlowComponent.cpp +++ b/Source/Flow/Private/FlowComponent.cpp @@ -271,7 +271,7 @@ void UFlowComponent::OnRep_IdentityTags(const FGameplayTagContainer& PreviousTag void UFlowComponent::VerifyIdentityTags() const { - if (IdentityTags.IsEmpty() && UFlowSettings::Get()->bWarnAboutMissingIdentityTags) + if (IdentityTags.IsEmpty() && GetDefault()->bWarnAboutMissingIdentityTags) { FString Message = TEXT("Missing Identity Tags on the Flow Component creating Flow Asset instance! This gonna break loading SaveGame for this component!"); Message.Append(LINE_TERMINATOR).Append(TEXT("If you're not using SaveSystem, you can silence this warning by unchecking bWarnAboutMissingIdentityTags flag in Flow Settings.")); @@ -616,7 +616,7 @@ bool UFlowComponent::IsFlowNetMode(const EFlowNetMode NetMode) const case EFlowNetMode::Authority: return GetOwner()->HasAuthority(); case EFlowNetMode::ClientOnly: - return IsNetMode(NM_Client) && UFlowSettings::Get()->bCreateFlowSubsystemOnClients; + return IsNetMode(NM_Client) && GetDefault()->bCreateFlowSubsystemOnClients; case EFlowNetMode::ServerOnly: return IsNetMode(NM_DedicatedServer) || IsNetMode(NM_ListenServer); case EFlowNetMode::SinglePlayerOnly: diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index 9a3cf1ec7..85109f60f 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -41,7 +41,7 @@ bool UFlowSubsystem::ShouldCreateSubsystem(UObject* Outer) const } // in this case, we simply create subsystem for every instance of the game - if (UFlowSettings::Get()->bCreateFlowSubsystemOnClients) + if (GetDefault()->bCreateFlowSubsystemOnClients) { return true; } diff --git a/Source/Flow/Private/Nodes/Actor/FlowNode_ExecuteComponent.cpp b/Source/Flow/Private/Nodes/Actor/FlowNode_ExecuteComponent.cpp index c53d05182..301c17add 100644 --- a/Source/Flow/Private/Nodes/Actor/FlowNode_ExecuteComponent.cpp +++ b/Source/Flow/Private/Nodes/Actor/FlowNode_ExecuteComponent.cpp @@ -593,7 +593,7 @@ TSubclassOf UFlowNode_ExecuteComponent::TryGetExpectedActorOwnerClass() FText UFlowNode_ExecuteComponent::K2_GetNodeTitle_Implementation() const { - if (UFlowSettings::Get()->bUseAdaptiveNodeTitles) + if (GetDefault()->bUseAdaptiveNodeTitles) { FLOW_ASSERT_ENUM_MAX(EExecuteComponentSource, 4); @@ -653,7 +653,7 @@ void UFlowNode_ExecuteComponent::UpdateNodeConfigText_Implementation() #if WITH_EDITOR FText ComponentNameText; - const bool bUseAdaptiveNodeTitles = UFlowSettings::Get()->bUseAdaptiveNodeTitles; + const bool bUseAdaptiveNodeTitles = GetDefault()->bUseAdaptiveNodeTitles; if (!bUseAdaptiveNodeTitles) { FLOW_ASSERT_ENUM_MAX(EExecuteComponentSource, 4); diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 652f23bdd..4c168fb92 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -978,13 +978,13 @@ void UFlowNode::TriggerInput(const FName& PinName, const EFlowPinActivationType ExecuteInputForSelfAndAddOns(PinName); break; case EFlowSignalMode::Disabled: - if (UFlowSettings::Get()->bLogOnSignalDisabled) + if (GetDefault()->bLogOnSignalDisabled) { LogNote(FString::Printf(TEXT("Node disabled while triggering input %s"), *PinName.ToString())); } break; case EFlowSignalMode::PassThrough: - if (UFlowSettings::Get()->bLogOnSignalPassthrough) + if (GetDefault()->bLogOnSignalPassthrough) { LogNote(FString::Printf(TEXT("Signal pass-through on triggering input %s"), *PinName.ToString())); } diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_CustomEventBase.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_CustomEventBase.cpp index f9453778a..7204290c7 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_CustomEventBase.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_CustomEventBase.cpp @@ -33,7 +33,7 @@ void UFlowNode_CustomEventBase::SetEventName(const FName& InEventName) FString UFlowNode_CustomEventBase::GetNodeDescription() const { - if (UFlowSettings::Get()->bUseAdaptiveNodeTitles) + if (GetDefault()->bUseAdaptiveNodeTitles) { return Super::GetNodeDescription(); } diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_CustomInput.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_CustomInput.cpp index 94e4410f8..5682da1a7 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_CustomInput.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_CustomInput.cpp @@ -27,7 +27,7 @@ void UFlowNode_CustomInput::PostEditImport() #if WITH_EDITOR FText UFlowNode_CustomInput::K2_GetNodeTitle_Implementation() const { - if (!EventName.IsNone() && UFlowSettings::Get()->bUseAdaptiveNodeTitles) + if (!EventName.IsNone() && GetDefault()->bUseAdaptiveNodeTitles) { return FText::Format(LOCTEXT("CustomInputTitle", "{0} Input"), {FText::FromString(EventName.ToString())}); } diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_CustomOutput.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_CustomOutput.cpp index ee5263ee0..4128a48e1 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_CustomOutput.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_CustomOutput.cpp @@ -55,7 +55,7 @@ void UFlowNode_CustomOutput::ExecuteInput(const FName& PinName) #if WITH_EDITOR FText UFlowNode_CustomOutput::K2_GetNodeTitle_Implementation() const { - if (!EventName.IsNone() && UFlowSettings::Get()->bUseAdaptiveNodeTitles) + if (!EventName.IsNone() && GetDefault()->bUseAdaptiveNodeTitles) { return FText::Format(LOCTEXT("CustomOutputTitle", "{0} Output"), {FText::FromString(EventName.ToString())}); } diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp index cdb0a7e4a..02fefd7f4 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp @@ -110,7 +110,7 @@ void UFlowNode_SubGraph::OnLoad_Implementation() FText UFlowNode_SubGraph::K2_GetNodeTitle_Implementation() const { - if (UFlowSettings::Get()->bUseAdaptiveNodeTitles && !Asset.IsNull()) + if (GetDefault()->bUseAdaptiveNodeTitles && !Asset.IsNull()) { return FText::Format(LOCTEXT("SubGraphTitle", "{0}\n{1}"), {Super::K2_GetNodeTitle_Implementation(), FText::FromString(Asset.ToSoftObjectPath().GetAssetName())}); } @@ -120,7 +120,7 @@ FText UFlowNode_SubGraph::K2_GetNodeTitle_Implementation() const FString UFlowNode_SubGraph::GetNodeDescription() const { - if (!UFlowSettings::Get()->bUseAdaptiveNodeTitles && !Asset.IsNull()) + if (!GetDefault()->bUseAdaptiveNodeTitles && !Asset.IsNull()) { return Asset.ToSoftObjectPath().GetAssetName(); } diff --git a/Source/Flow/Public/FlowSettings.h b/Source/Flow/Public/FlowSettings.h index 0c7765586..6afddab6c 100644 --- a/Source/Flow/Public/FlowSettings.h +++ b/Source/Flow/Public/FlowSettings.h @@ -17,6 +17,7 @@ class FLOW_API UFlowSettings : public UDeveloperSettings { GENERATED_UCLASS_BODY() + UE_DEPRECATED(5.5, "Call GetDefault() instead.") static UFlowSettings* Get() { return CastChecked(UFlowSettings::StaticClass()->GetDefaultObject()); } #if WITH_EDITOR diff --git a/Source/FlowEditor/Private/Asset/AssetDefinition_FlowAsset.cpp b/Source/FlowEditor/Private/Asset/AssetDefinition_FlowAsset.cpp index ce1ef302e..276c61faa 100644 --- a/Source/FlowEditor/Private/Asset/AssetDefinition_FlowAsset.cpp +++ b/Source/FlowEditor/Private/Asset/AssetDefinition_FlowAsset.cpp @@ -28,7 +28,7 @@ TSoftClassPtr UAssetDefinition_FlowAsset::GetAssetClass() const TConstArrayView UAssetDefinition_FlowAsset::GetAssetCategories() const { - if (UFlowGraphSettings::Get()->bExposeFlowAssetCreation) + if (GetDefault()->bExposeFlowAssetCreation) { static const auto Categories = {FFlowAssetCategoryPaths::Flow}; return Categories; diff --git a/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp b/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp index d78daa59f..7514374b0 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetFactory.cpp @@ -76,7 +76,7 @@ bool UFlowAssetFactory::ConfigureProperties() bool UFlowAssetFactory::ConfigurePropertiesInternal(const FText& TitleText) { - AssetClass = UFlowGraphSettings::Get()->DefaultFlowAssetClass; + AssetClass = GetDefault()->DefaultFlowAssetClass; if (AssetClass) // Class was set in settings { return true; diff --git a/Source/FlowEditor/Private/Find/FindInFlow.cpp b/Source/FlowEditor/Private/Find/FindInFlow.cpp index 648728c78..3f596e39f 100644 --- a/Source/FlowEditor/Private/Find/FindInFlow.cpp +++ b/Source/FlowEditor/Private/Find/FindInFlow.cpp @@ -3,7 +3,6 @@ #include "Find/FindInFlow.h" #include "Asset/FlowAssetEditor.h" #include "Find/SFindInFlowFilterPopup.h" -#include "Graph/FlowGraphEditor.h" #include "Graph/FlowGraphEditorSettings.h" #include "Graph/FlowGraphUtils.h" #include "Graph/Nodes/FlowGraphNode.h" @@ -255,7 +254,7 @@ void SFindInFlow::Construct(const FArguments& InArgs, TSharedPtr(); if (ensure(Settings)) { MaxSearchDepth = Settings->DefaultMaxSearchDepth; @@ -332,20 +331,19 @@ void SFindInFlow::Construct(const FArguments& InArgs, TSharedPtr()) { - Settings->DefaultSearchFlags = static_cast(Flags); - Settings->SaveConfig(); + GraphEditorSettings->DefaultSearchFlags = static_cast(Flags); + GraphEditorSettings->SaveConfig(); } }); - TSharedRef FilterPopup = SNew(SFindInFlowFilterPopup) - .OnApply(FFindInFlowApplyDelegate::CreateLambda([this](EFlowSearchFlags NewSearchFlags) + const TSharedRef FilterPopup = SNew(SFindInFlowFilterPopup) + .OnApply(FFindInFlowApplyDelegate::CreateLambda([this](const EFlowSearchFlags NewSearchFlags) { SearchFlags = NewSearchFlags; - InitiateSearch(); })) .OnSaveAsDefault(OnSaveAsDefault) @@ -447,10 +445,10 @@ void SFindInFlow::OnMaxDepthChanged(int32 NewDepth) MaxSearchDepth = NewDepth; // Save to INI - if (UFlowGraphEditorSettings* Settings = UFlowGraphEditorSettings::Get()) + if (UFlowGraphEditorSettings* GraphEditorSettings = GetMutableDefault()) { - Settings->DefaultMaxSearchDepth = NewDepth; - Settings->SaveConfig(); + GraphEditorSettings->DefaultMaxSearchDepth = NewDepth; + GraphEditorSettings->SaveConfig(); } } @@ -502,10 +500,7 @@ void SFindInFlow::InitiateSearch() return; } - const TSubclassOf CurrentAssetClass = CurrentAsset->GetClass(); - constexpr int32 Depth = 0; - switch (SearchScope) { case EFlowSearchScope::ThisAssetOnly: @@ -781,9 +776,7 @@ void SFindInFlow::UpdateSearchFlagToStringMapForFlowNodeBase(const UFlowNodeBase // AddOns if (EnumHasAnyFlags(SearchFlags, EFlowSearchFlags::AddOns)) { - TSet& AddOnsSet = SearchFlagToStringMap.FindOrAdd(EFlowSearchFlags::AddOns); - - FlowNodeBase.ForEachAddOnConst([AddOnsSet, this, &SearchFlagToStringMap, &Depth](const UFlowNodeAddOn& AddOn) + FlowNodeBase.ForEachAddOnConst([this, &SearchFlagToStringMap, &Depth](const UFlowNodeAddOn& AddOn) { // No depth penalty for AddOns UpdateSearchFlagToStringMapForFlowNodeBase(AddOn, SearchFlagToStringMap, Depth); @@ -796,7 +789,7 @@ void SFindInFlow::UpdateSearchFlagToStringMapForFlowNodeBase(const UFlowNodeBase void SFindInFlow::AppendPropertyValues(const void* Container, const UStruct* Struct, const UObject* ParentObject, TMap>& SearchFlagToStringMap, int32 Depth) const { int32 MaxDepth = 1; - if (const UFlowGraphEditorSettings* Settings = UFlowGraphEditorSettings::Get()) + if (const UFlowGraphEditorSettings* Settings = GetDefault()) { MaxDepth = Settings->DefaultMaxSearchDepth; } diff --git a/Source/FlowEditor/Private/FlowEditorModule.cpp b/Source/FlowEditor/Private/FlowEditorModule.cpp index 20158b634..09906bf8c 100644 --- a/Source/FlowEditor/Private/FlowEditorModule.cpp +++ b/Source/FlowEditor/Private/FlowEditorModule.cpp @@ -75,7 +75,7 @@ void FFlowEditorModule::StartupModule() FEdGraphUtilities::RegisterVisualPinFactory(MakeShareable(new FFlowOutputPinHandleFactory())); // add Flow Toolbar - if (UFlowGraphSettings::Get()->bShowAssetToolbarAboveLevelEditor) + if (GetDefault()->bShowAssetToolbarAboveLevelEditor) { if (FLevelEditorModule* LevelEditorModule = FModuleManager::GetModulePtr(TEXT("LevelEditor"))) { @@ -145,21 +145,21 @@ void FFlowEditorModule::TrySetFlowNodeDisplayStyleDefaults() const // Force the Flow module to be loaded before we try to access the Settings FModuleManager::LoadModuleChecked("Flow"); - UFlowGraphSettings& Settings = *UFlowGraphSettings::Get(); - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::Node, FLinearColor(0.0f, 0.581f, 1.0f, 1.0f))); - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::Condition, FLinearColor(1.0f, 0.62f, 0.016f, 1.0f))); - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::Deprecated, FLinearColor(1.0f, 1.0f, 0.0f, 1.0f))); - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::Developer, FLinearColor(0.7f, 0.2f, 1.0f, 1.0f))); - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::InOut, FLinearColor(1.0f, 0.0f, 0.008f, 1.0f))); - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::Latent, FLinearColor(0.0f, 0.770f, 0.375f, 1.0f))); - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::Logic, FLinearColor(1.0f, 1.0f, 1.0f, 1.0f))); - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::SubGraph, FLinearColor(1.0f, 0.128f, 0.0f, 1.0f))); - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::Terminal, FLinearColor(1.0f, 0.0f, 0.008f, 1.0f))); - - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::AddOn, FLinearColor(0.0f, 0.581f, 1.0f, 1.0f))); - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::AddOn_PerSpawnedActor, FLinearColor(0.3f, 0.3f, 1.0f, 1.0f))); - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::AddOn_Predicate, FLinearColor(1.0f, 1.0f, 1.0f, 1.0f))); - (void) Settings.TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::AddOn_Predicate_Composite, FLinearColor(1.0f, 1.0f, 1.0f, 1.0f))); + UFlowGraphSettings* GraphSettings = GetMutableDefault(); + GraphSettings->TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::Node, FLinearColor(0.0f, 0.581f, 1.0f, 1.0f))); + GraphSettings->TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::Condition, FLinearColor(1.0f, 0.62f, 0.016f, 1.0f))); + GraphSettings->TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::Deprecated, FLinearColor(1.0f, 1.0f, 0.0f, 1.0f))); + GraphSettings->TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::Developer, FLinearColor(0.7f, 0.2f, 1.0f, 1.0f))); + GraphSettings->TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::InOut, FLinearColor(1.0f, 0.0f, 0.008f, 1.0f))); + GraphSettings->TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::Latent, FLinearColor(0.0f, 0.770f, 0.375f, 1.0f))); + GraphSettings->TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::Logic, FLinearColor(1.0f, 1.0f, 1.0f, 1.0f))); + GraphSettings->TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::SubGraph, FLinearColor(1.0f, 0.128f, 0.0f, 1.0f))); + GraphSettings->TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::Terminal, FLinearColor(1.0f, 0.0f, 0.008f, 1.0f))); + + GraphSettings->TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::AddOn, FLinearColor(0.0f, 0.581f, 1.0f, 1.0f))); + GraphSettings->TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::AddOn_PerSpawnedActor, FLinearColor(0.3f, 0.3f, 1.0f, 1.0f))); + GraphSettings->TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::AddOn_Predicate, FLinearColor(1.0f, 1.0f, 1.0f, 1.0f))); + GraphSettings->TryAddDefaultNodeDisplayStyle(FFlowNodeDisplayStyleConfig(FlowNodeStyle::AddOn_Predicate_Composite, FLinearColor(1.0f, 1.0f, 1.0f, 1.0f))); } void FFlowEditorModule::RegisterAssets() @@ -168,7 +168,7 @@ void FFlowEditorModule::RegisterAssets() // try to merge asset category with a built-in one { - const FText AssetCategoryText = UFlowGraphSettings::Get()->FlowAssetCategoryName; + const FText AssetCategoryText = GetDefault()->FlowAssetCategoryName; // Find matching built-in category if (!AssetCategoryText.IsEmpty()) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp b/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp index 895375833..34f8779bd 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphConnectionDrawingPolicy.cpp @@ -35,18 +35,20 @@ FFlowGraphConnectionDrawingPolicy::FFlowGraphConnectionDrawingPolicy(int32 InBac : FConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements) , GraphObj(InGraphObj) { + const UFlowGraphSettings* GraphSettings = GetDefault(); + // Cache off the editor options - RecentWireDuration = UFlowGraphSettings::Get()->RecentWireDuration; + RecentWireDuration = GraphSettings->RecentWireDuration; - InactiveColor = UFlowGraphSettings::Get()->InactiveWireColor; - RecentColor = UFlowGraphSettings::Get()->RecentWireColor; - RecordedColor = UFlowGraphSettings::Get()->RecordedWireColor; - SelectedColor = UFlowGraphSettings::Get()->SelectedWireColor; + InactiveColor = GraphSettings->InactiveWireColor; + RecentColor = GraphSettings->RecentWireColor; + RecordedColor = GraphSettings->RecordedWireColor; + SelectedColor = GraphSettings->SelectedWireColor; - InactiveWireThickness = UFlowGraphSettings::Get()->InactiveWireThickness; - RecentWireThickness = UFlowGraphSettings::Get()->RecentWireThickness; - RecordedWireThickness = UFlowGraphSettings::Get()->RecordedWireThickness; - SelectedWireThickness = UFlowGraphSettings::Get()->SelectedWireThickness; + InactiveWireThickness = GraphSettings->InactiveWireThickness; + RecentWireThickness = GraphSettings->RecentWireThickness; + RecordedWireThickness = GraphSettings->RecordedWireThickness; + SelectedWireThickness = GraphSettings->SelectedWireThickness; // Don't want to draw ending arrowheads ArrowImage = nullptr; @@ -89,7 +91,8 @@ void FFlowGraphConnectionDrawingPolicy::BuildPaths() } } - if (GraphObj && (UFlowGraphEditorSettings::Get()->bHighlightInputWiresOfSelectedNodes || UFlowGraphEditorSettings::Get()->bHighlightOutputWiresOfSelectedNodes)) + const UFlowGraphEditorSettings* GraphEditorSettings = GetDefault(); + if (GraphObj && (GraphEditorSettings->bHighlightInputWiresOfSelectedNodes || GraphEditorSettings->bHighlightOutputWiresOfSelectedNodes)) { const TSharedPtr FlowGraphEditor = FFlowGraphUtils::GetFlowGraphEditor(GraphObj); if (FlowGraphEditor.IsValid()) @@ -98,8 +101,8 @@ void FFlowGraphConnectionDrawingPolicy::BuildPaths() { for (UEdGraphPin* Pin : SelectedNode->Pins) { - if ((Pin->Direction == EGPD_Input && UFlowGraphEditorSettings::Get()->bHighlightInputWiresOfSelectedNodes) - || (Pin->Direction == EGPD_Output && UFlowGraphEditorSettings::Get()->bHighlightOutputWiresOfSelectedNodes)) + if ((Pin->Direction == EGPD_Input && GraphEditorSettings->bHighlightInputWiresOfSelectedNodes) + || (Pin->Direction == EGPD_Output && GraphEditorSettings->bHighlightOutputWiresOfSelectedNodes)) { for (UEdGraphPin* LinkedPin : Pin->LinkedTo) { @@ -118,7 +121,7 @@ void FFlowGraphConnectionDrawingPolicy::DrawConnection(int32 LayerId, const FVec void FFlowGraphConnectionDrawingPolicy::DrawConnection(int32 LayerId, const FVector2f& Start, const FVector2f& End, const FConnectionParams& Params) #endif { - switch (UFlowGraphSettings::Get()->ConnectionDrawType) + switch (GetDefault()->ConnectionDrawType) { case EFlowConnectionDrawType::Default: FConnectionDrawingPolicy::DrawConnection(LayerId, Start, End, Params); @@ -229,8 +232,8 @@ void FFlowGraphConnectionDrawingPolicy::Draw(TMap, FArranged void FFlowGraphConnectionDrawingPolicy::DrawCircuitSpline(const int32& LayerId, const FVector2f& Start, const FVector2f& End, const FConnectionParams& Params) const { - const FVector2f StartingPoint = FVector2f(Start.X + UFlowGraphSettings::Get()->CircuitConnectionSpacing.X, Start.Y); - const FVector2f EndPoint = FVector2f(End.X - UFlowGraphSettings::Get()->CircuitConnectionSpacing.Y, End.Y); + const FVector2f StartingPoint = FVector2f(Start.X + GetDefault()->CircuitConnectionSpacing.X, Start.Y); + const FVector2f EndPoint = FVector2f(End.X - GetDefault()->CircuitConnectionSpacing.Y, End.Y); const FVector2f ControlPoint = GetControlPoint(StartingPoint, EndPoint); const FVector2f StartDirection = (Params.StartDirection == EGPD_Output) ? FVector2f(1.0f, 0.0f) : FVector2f(-1.0f, 0.0f); @@ -285,7 +288,7 @@ void FFlowGraphConnectionDrawingPolicy::DrawCircuitConnection(const int32& Layer FVector2f FFlowGraphConnectionDrawingPolicy::GetControlPoint(const FVector2f& Source, const FVector2f& Target) { const FVector2f Delta = Target - Source; - const float Tangent = FMath::Tan(UFlowGraphSettings::Get()->CircuitConnectionAngle * (PI / 180.f)); + const float Tangent = FMath::Tan(GetDefault()->CircuitConnectionAngle * (PI / 180.f)); const float DeltaX = FMath::Abs(Delta.X); const float DeltaY = FMath::Abs(Delta.Y); diff --git a/Source/FlowEditor/Private/Graph/FlowGraphPinFactory.cpp b/Source/FlowEditor/Private/Graph/FlowGraphPinFactory.cpp index 55b874378..8072421d5 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphPinFactory.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphPinFactory.cpp @@ -31,7 +31,7 @@ TSharedPtr FFlowGraphPinFactory::CreatePin(UEdGraphPin* InPin) const const UFlowNode* FlowNode = Cast(FlowGraphNode->GetFlowNodeBase()); - if (!UFlowGraphSettings::Get()->bShowDefaultPinNames && IsValid(FlowNode)) + if (!GetDefault()->bShowDefaultPinNames && IsValid(FlowNode)) { if (InPin->Direction == EGPD_Input) { diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp index 0995dca24..64ed23855 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSchema.cpp @@ -1106,16 +1106,14 @@ void UFlowGraphSchema::ApplyNodeOrAddOnFilter(const UFlowAsset* EditedFlowAsset, return; } - const UFlowGraphSettings& FlowGraphSettings = *UFlowGraphSettings::Get(); - const bool bIsHiddenFromPaletteByNodeClass = FlowGraphSettings.NodesHiddenFromPalette.Contains(FlowNodeClass); - if (bIsHiddenFromPaletteByNodeClass) + const UFlowGraphSettings* GraphSettings = GetDefault(); + if (GraphSettings->NodesHiddenFromPalette.Contains(FlowNodeClass)) { return; } UFlowNodeBase* FlowNodeBaseCDO = FlowNodeClass->GetDefaultObject(); - const FFlowGraphNodesPolicy* FlowAssetPolicy = FlowGraphSettings.PerAssetSubclassFlowNodePolicies.Find(FSoftClassPath(EditedFlowAsset->GetClass())); - if (FlowAssetPolicy) + if (const FFlowGraphNodesPolicy* FlowAssetPolicy = GraphSettings->PerAssetSubclassFlowNodePolicies.Find(FSoftClassPath(EditedFlowAsset->GetClass()))) { const bool bIsAllowedByPolicy = FlowAssetPolicy->IsNodeAllowedByPolicy(FlowNodeBaseCDO); if (!bIsAllowedByPolicy) @@ -1131,7 +1129,7 @@ void UFlowGraphSchema::GetFlowNodeActions(FGraphActionMenuBuilder& ActionMenuBui { const TArray FilteredNodes = GetFilteredPlaceableNodesOrAddOns(EditedFlowAsset, NativeFlowNodes, BlueprintFlowNodes); - const UFlowGraphSettings& FlowGraphSettings = *UFlowGraphSettings::Get(); + const UFlowGraphSettings& GraphSettings = *GetDefault(); for (const UFlowNodeBase* FlowNodeBase : FilteredNodes) { // TODO (gtaylor) This should really be integrated into GetFilteredPlaceableNodesOrAddOns, @@ -1143,7 +1141,7 @@ void UFlowGraphSchema::GetFlowNodeActions(FGraphActionMenuBuilder& ActionMenuBui continue; } - TSharedPtr NewNodeAction(new FFlowGraphSchemaAction_NewNode(FlowNodeBase, FlowGraphSettings)); + TSharedPtr NewNodeAction(new FFlowGraphSchemaAction_NewNode(FlowNodeBase, GraphSettings)); ActionMenuBuilder.AddAction(NewNodeAction); } } diff --git a/Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp b/Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp index 595efcdde..05393725d 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphSettings.cpp @@ -100,8 +100,7 @@ void UFlowGraphSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyC FString UFlowGraphSettings::GetNodeCategoryForNode(const UFlowNodeBase& FlowNodeBase) { - const UFlowGraphSettings* GraphSettings = UFlowGraphSettings::Get(); - + const UFlowGraphSettings* GraphSettings = GetDefault(); if (const FString* CategoryOverridenByUser = GraphSettings->OverridenNodeCategories.Find(FlowNodeBase.GetClass())) { return *CategoryOverridenByUser; @@ -134,7 +133,7 @@ const TMap& UFlowGraphSettings::Ensur return NodeDisplayStylesMap; } -bool UFlowGraphSettings::TryAddDefaultNodeDisplayStyle(const FFlowNodeDisplayStyleConfig& StyleConfig) +void UFlowGraphSettings::TryAddDefaultNodeDisplayStyle(const FFlowNodeDisplayStyleConfig& StyleConfig) { const int32 FoundIndex = NodeDisplayStyles.FindLastByPredicate( @@ -151,13 +150,11 @@ bool UFlowGraphSettings::TryAddDefaultNodeDisplayStyle(const FFlowNodeDisplaySty if (FoundIndex != INDEX_NONE) { // Keep the existing config - - return false; + return; } NodeDisplayStyles.Add(StyleConfig); - - return true; + return; } const FLinearColor* UFlowGraphSettings::LookupNodeTitleColorForNode(const UFlowNodeBase& FlowNodeBase) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp b/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp index 5712c0dce..4e04b3dc5 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphUtils.cpp @@ -50,7 +50,7 @@ TSharedPtr FFlowGraphUtils::GetFlowGraphEditor(const UEdGraph* FString FFlowGraphUtils::RemovePrefixFromNodeText(const FText& Source) { FString SourceString = Source.ToString(); - TArray NodePrefixes = UFlowGraphSettings::Get()->NodePrefixesToRemove; + TArray NodePrefixes = GetDefault()->NodePrefixesToRemove; for (FString Prefix : NodePrefixes) { diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 92a0910ce..38668c28d 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -669,7 +669,7 @@ FText UFlowGraphNode::GetNodeTitle(ENodeTitleType::Type TitleType) const { if (NodeInstance) { - if (UFlowGraphEditorSettings::Get()->bShowNodeClass) + if (GetDefault()->bShowNodeClass) { FString CleanAssetName; if (NodeInstance->GetClass()->ClassGeneratedBy) @@ -705,7 +705,7 @@ FLinearColor UFlowGraphNode::GetNodeTitleColor() const return DynamicColor; } - if (const FLinearColor* StyleColor = UFlowGraphSettings::Get()->LookupNodeTitleColorForNode(*NodeInstance)) + if (const FLinearColor* StyleColor = GetMutableDefault()->LookupNodeTitleColorForNode(*NodeInstance)) { return *StyleColor; } @@ -735,7 +735,7 @@ FText UFlowGraphNode::GetTooltipText() const FString UFlowGraphNode::GetNodeDescription() const { - if (NodeInstance && (GEditor->PlayWorld == nullptr || UFlowGraphEditorSettings::Get()->bShowNodeDescriptionWhilePlaying)) + if (NodeInstance && (GEditor->PlayWorld == nullptr || GetDefault()->bShowNodeDescriptionWhilePlaying)) { return NodeInstance->GetNodeDescription(); } @@ -789,7 +789,7 @@ FLinearColor UFlowGraphNode::GetStatusBackgroundColor() const } } - return UFlowGraphSettings::Get()->NodeStatusBackground; + return GetDefault()->NodeStatusBackground; } bool UFlowGraphNode::IsContentPreloaded() const @@ -860,7 +860,8 @@ void UFlowGraphNode::OnNodeDoubleClicked() const UFlowNodeBase* FlowNodeBase = GetFlowNodeBase(); if (IsValid(FlowNodeBase)) { - if (UFlowGraphEditorSettings::Get()->NodeDoubleClickTarget == EFlowNodeDoubleClickTarget::NodeDefinition) + const EFlowNodeDoubleClickTarget DoubleClickTarget = GetDefault()->NodeDoubleClickTarget; + if (DoubleClickTarget == EFlowNodeDoubleClickTarget::NodeDefinition) { JumpToDefinition(); } @@ -887,7 +888,7 @@ void UFlowGraphNode::OnNodeDoubleClicked() const OnNodeDoubleClickedInPIE(); } } - else if (UFlowGraphEditorSettings::Get()->NodeDoubleClickTarget == EFlowNodeDoubleClickTarget::PrimaryAssetOrNodeDefinition) + else if (DoubleClickTarget == EFlowNodeDoubleClickTarget::PrimaryAssetOrNodeDefinition) { JumpToDefinition(); } diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp index f2b4a1186..3f0104d31 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode.cpp @@ -42,7 +42,7 @@ SFlowGraphPinExec::SFlowGraphPinExec() { - PinColorModifier = UFlowGraphSettings::Get()->ExecPinColorModifier; + PinColorModifier = GetDefault()->ExecPinColorModifier; } void SFlowGraphPinExec::Construct(const FArguments& InArgs, UEdGraphPin* InPin) @@ -85,7 +85,7 @@ void SFlowGraphNode::GetNodeInfoPopups(FNodeInfoContext* Context, TArrayGetNodeDescription(); if (!Description.IsEmpty()) { - const FGraphInformationPopupInfo DescriptionPopup = FGraphInformationPopupInfo(nullptr, UFlowGraphSettings::Get()->NodeDescriptionBackground, Description); + const FGraphInformationPopupInfo DescriptionPopup = FGraphInformationPopupInfo(nullptr, GetDefault()->NodeDescriptionBackground, Description); Popups.Add(DescriptionPopup); } @@ -99,7 +99,7 @@ void SFlowGraphNode::GetNodeInfoPopups(FNodeInfoContext* Context, TArrayIsContentPreloaded()) { - const FGraphInformationPopupInfo DescriptionPopup = FGraphInformationPopupInfo(nullptr, UFlowGraphSettings::Get()->NodeStatusBackground, TEXT("Preloaded")); + const FGraphInformationPopupInfo DescriptionPopup = FGraphInformationPopupInfo(nullptr, GetDefault()->NodeStatusBackground, TEXT("Preloaded")); Popups.Add(DescriptionPopup); } } diff --git a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode_SubGraph.cpp b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode_SubGraph.cpp index 81ef46c19..7cd94a877 100644 --- a/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode_SubGraph.cpp +++ b/Source/FlowEditor/Private/Graph/Widgets/SFlowGraphNode_SubGraph.cpp @@ -14,7 +14,8 @@ TSharedPtr SFlowGraphNode_SubGraph::GetComplexTooltip() { - if (UFlowGraphEditorSettings::Get()->bShowSubGraphPreview && FlowGraphNode) + const UFlowGraphEditorSettings* GraphEditorSettings = GetDefault(); + if (GraphEditorSettings->bShowSubGraphPreview && FlowGraphNode) { if (UFlowNode* FlowNode = Cast(FlowGraphNode->GetFlowNodeBase())) { @@ -22,7 +23,7 @@ TSharedPtr SFlowGraphNode_SubGraph::GetComplexTooltip() if (AssetToEdit && AssetToEdit->GetGraph()) { TSharedPtr TitleBarWidget = SNullWidget::NullWidget; - if (UFlowGraphEditorSettings::Get()->bShowSubGraphPath) + if (GraphEditorSettings->bShowSubGraphPath) { FString CleanAssetName = AssetToEdit->GetPathName(nullptr); const int32 SubStringIdx = CleanAssetName.Find(".", ESearchCase::IgnoreCase, ESearchDir::FromEnd); @@ -39,8 +40,8 @@ TSharedPtr SFlowGraphNode_SubGraph::GetComplexTooltip() return SNew(SToolTip) [ SNew(SBox) - .WidthOverride(UFlowGraphEditorSettings::Get()->SubGraphPreviewSize.X) - .HeightOverride(UFlowGraphEditorSettings::Get()->SubGraphPreviewSize.Y) + .WidthOverride(GraphEditorSettings->SubGraphPreviewSize.X) + .HeightOverride(GraphEditorSettings->SubGraphPreviewSize.Y) [ SNew(SOverlay) +SOverlay::Slot() diff --git a/Source/FlowEditor/Private/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.cpp b/Source/FlowEditor/Private/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.cpp index b04b7ce11..4d5775d1e 100644 --- a/Source/FlowEditor/Private/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.cpp +++ b/Source/FlowEditor/Private/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.cpp @@ -16,7 +16,7 @@ FText FAssetTypeActions_FlowNodeAddOnBlueprint::GetName() const uint32 FAssetTypeActions_FlowNodeAddOnBlueprint::GetCategories() { - return UFlowGraphSettings::Get()->bExposeFlowNodeCreation ? FFlowEditorModule::FlowAssetCategory : 0; + return GetDefault()->bExposeFlowNodeCreation ? FFlowEditorModule::FlowAssetCategory : 0; } UClass* FAssetTypeActions_FlowNodeAddOnBlueprint::GetSupportedClass() const diff --git a/Source/FlowEditor/Private/Nodes/AssetTypeActions_FlowNodeBlueprint.cpp b/Source/FlowEditor/Private/Nodes/AssetTypeActions_FlowNodeBlueprint.cpp index a145a98fc..06560acf8 100644 --- a/Source/FlowEditor/Private/Nodes/AssetTypeActions_FlowNodeBlueprint.cpp +++ b/Source/FlowEditor/Private/Nodes/AssetTypeActions_FlowNodeBlueprint.cpp @@ -17,7 +17,7 @@ FText FAssetTypeActions_FlowNodeBlueprint::GetName() const uint32 FAssetTypeActions_FlowNodeBlueprint::GetCategories() { - return UFlowGraphSettings::Get()->bExposeFlowNodeCreation ? FFlowEditorModule::FlowAssetCategory : 0; + return GetDefault()->bExposeFlowNodeCreation ? FFlowEditorModule::FlowAssetCategory : 0; } UClass* FAssetTypeActions_FlowNodeBlueprint::GetSupportedClass() const diff --git a/Source/FlowEditor/Private/Utils/SLevelEditorFlow.cpp b/Source/FlowEditor/Private/Utils/SLevelEditorFlow.cpp index 86069f193..7eb8d66d0 100644 --- a/Source/FlowEditor/Private/Utils/SLevelEditorFlow.cpp +++ b/Source/FlowEditor/Private/Utils/SLevelEditorFlow.cpp @@ -40,7 +40,7 @@ void SLevelEditorFlow::CreateFlowWidget() .AutoWidth() [ SNew(SObjectPropertyEntryBox) - .AllowedClass(UFlowGraphSettings::Get()->WorldAssetClass) + .AllowedClass(GetDefault()->WorldAssetClass) .DisplayThumbnail(false) .OnObjectChanged(this, &SLevelEditorFlow::OnFlowChanged) .ObjectPath(this, &SLevelEditorFlow::GetFlowAssetPath) // needs function to automatically refresh view upon data change diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h index 8695702ca..dea617112 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h @@ -26,6 +26,7 @@ class FLOWEDITOR_API UFlowGraphEditorSettings : public UDeveloperSettings public: UFlowGraphEditorSettings(); + UE_DEPRECATED(5.5, "Call GetDefault() instead.") static UFlowGraphEditorSettings* Get() { return StaticClass()->GetDefaultObject(); } #if WITH_EDITOR diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h index 007d54c8f..faafca568 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h @@ -60,6 +60,7 @@ class FLOWEDITOR_API UFlowGraphSettings : public UDeveloperSettings { GENERATED_UCLASS_BODY() + UE_DEPRECATED(5.5, "Call GetDefault() instead.") static UFlowGraphSettings* Get() { return StaticClass()->GetDefaultObject(); } virtual void PostInitProperties() override; @@ -194,7 +195,7 @@ class FLOWEDITOR_API UFlowGraphSettings : public UDeveloperSettings #if WITH_EDITOR const TMap& EnsureNodeDisplayStylesMap(); - bool TryAddDefaultNodeDisplayStyle(const FFlowNodeDisplayStyleConfig& StyleConfig); + void TryAddDefaultNodeDisplayStyle(const FFlowNodeDisplayStyleConfig& StyleConfig); const FLinearColor* LookupNodeTitleColorForNode(const UFlowNodeBase& FlowNodeBase); #endif }; From 96e84e5bee97849a48b66aa5866c082201cf6621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sat, 14 Feb 2026 18:22:23 +0100 Subject: [PATCH 404/485] updated deprecation note --- Source/Flow/Public/FlowAsset.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 68acdc681..0d2c9c66a 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -319,7 +319,7 @@ class FLOW_API UFlowAsset : public UObject EFlowFinishPolicy FinishPolicy; public: - UE_DEPRECATED(5.4, "Use version that takes a UFlowAssetReference instead.") + UE_DEPRECATED(5.5, "Use version that takes a UFlowAssetReference instead.") virtual void InitializeInstance(const TWeakObjectPtr InOwner, UFlowAsset* InTemplateAsset) { InitializeInstance(InOwner, *InTemplateAsset); } virtual void InitializeInstance(const TWeakObjectPtr InOwner, UFlowAsset& InTemplateAsset); From 54587df138d7761c3afd61ad68c9211496a00ed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sat, 14 Feb 2026 18:24:16 +0100 Subject: [PATCH 405/485] update class layout --- Source/Flow/Private/FlowSubsystem.cpp | 114 +++++++++++++------------- Source/Flow/Public/FlowSubsystem.h | 17 ++-- 2 files changed, 65 insertions(+), 66 deletions(-) diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index 85109f60f..6b936c16e 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -160,63 +160,6 @@ void UFlowSubsystem::FinishAllRootFlows(UObject* Owner, const EFlowFinishPolicy } } -bool UFlowSubsystem::TryFlushAllDeferredTriggerScopes() -{ - // Flush deferred triggers on all active runtime instances. - // Flush order follows InstancedTemplates iteration + per-template ActiveInstances. - // This provides reasonable per-asset FIFO but is not a strict global FIFO across assets. - // A more precise global queue could be implemented later if cross-asset ordering becomes critical. - const TArray CapturedInstancedTemplates = InstancedTemplates; - for (UFlowAsset* Template : CapturedInstancedTemplates) - { - if (!IsValid(Template)) - { - continue; - } - - for (UFlowAsset* Instance : Template->GetActiveInstances()) - { - if (FFlowExecutionGate::IsHalted()) - { - break; - } - - if (IsValid(Instance)) - { - const bool bFlushed = Instance->TryFlushAllDeferredTriggerScopes(); - - // The only case where we allow a flush to stop before completing - // is if we hit an execution gate halt - check(bFlushed || FFlowExecutionGate::IsHalted()); - } - } - } - - // The only case where we allow a flush to stop before completing - // is if we hit an execution gate halt - const bool bCompletedFlushAll = !FFlowExecutionGate::IsHalted(); - return bCompletedFlushAll; -} - -void UFlowSubsystem::ClearAllDeferredTriggerScopes() -{ - for (UFlowAsset* Template : InstancedTemplates) - { - if (!IsValid(Template)) - { - continue; - } - - for (UFlowAsset* Instance : Template->GetActiveInstances()) - { - if (IsValid(Instance)) - { - Instance->ClearAllDeferredTriggerScopes(); - } - } - } -} - UFlowAsset* UFlowSubsystem::CreateSubFlow(UFlowNode_SubGraph* SubGraphNode, const FString& SavedInstanceName, const bool bPreloading /* = false */) { UFlowAsset* NewInstance = nullptr; @@ -325,6 +268,63 @@ void UFlowSubsystem::RemoveInstancedTemplate(UFlowAsset* Template) InstancedTemplates.Remove(Template); } +bool UFlowSubsystem::TryFlushAllDeferredTriggerScopes() const +{ + // Flush deferred triggers on all active runtime instances. + // Flush order follows InstancedTemplates iteration + per-template ActiveInstances. + // This provides reasonable per-asset FIFO but is not a strict global FIFO across assets. + // A more precise global queue could be implemented later if cross-asset ordering becomes critical. + const TArray CapturedInstancedTemplates = InstancedTemplates; + for (const UFlowAsset* Template : CapturedInstancedTemplates) + { + if (!IsValid(Template)) + { + continue; + } + + for (UFlowAsset* Instance : Template->GetActiveInstances()) + { + if (FFlowExecutionGate::IsHalted()) + { + break; + } + + if (IsValid(Instance)) + { + const bool bFlushed = Instance->TryFlushAllDeferredTriggerScopes(); + + // The only case where we allow a flush to stop before completing + // is if we hit an execution gate halt + check(bFlushed || FFlowExecutionGate::IsHalted()); + } + } + } + + // The only case where we allow a flush to stop before completing + // is if we hit an execution gate halt + const bool bCompletedFlushAll = !FFlowExecutionGate::IsHalted(); + return bCompletedFlushAll; +} + +void UFlowSubsystem::ClearAllDeferredTriggerScopes() +{ + for (const UFlowAsset* Template : InstancedTemplates) + { + if (!IsValid(Template)) + { + continue; + } + + for (UFlowAsset* Instance : Template->GetActiveInstances()) + { + if (IsValid(Instance)) + { + Instance->ClearAllDeferredTriggerScopes(); + } + } + } +} + TMap UFlowSubsystem::GetRootInstances() const { TMap Result; diff --git a/Source/Flow/Public/FlowSubsystem.h b/Source/Flow/Public/FlowSubsystem.h index 201e4e852..cd649a509 100644 --- a/Source/Flow/Public/FlowSubsystem.h +++ b/Source/Flow/Public/FlowSubsystem.h @@ -59,15 +59,6 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem static FNativeFlowAssetEvent OnInstancedTemplateRemoved; #endif -public: - // Try to flush (and clear) all Deferred Trigger scopes - // (can fail to flush all if a FFlowExecutionGate causes a new halt) - bool TryFlushAllDeferredTriggerScopes(); - - // Clear (do not trigger) any remaining deferred transitions - // (for shutdown cases) - void ClearAllDeferredTriggerScopes(); - protected: UPROPERTY() TObjectPtr LoadedSaveGame; @@ -110,6 +101,14 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem virtual void AddInstancedTemplate(UFlowAsset* Template); virtual void RemoveInstancedTemplate(UFlowAsset* Template); +public: + /* Try to flush (and clear) all Deferred Trigger scopes. + * (can fail to flush all if a FFlowExecutionGate causes a new halt) */ + bool TryFlushAllDeferredTriggerScopes() const; + + /* Clear (do not trigger) any remaining deferred transitions. (for shutdown cases) */ + void ClearAllDeferredTriggerScopes(); + public: /* Returns all assets instanced by object from another system like World Settings */ UFUNCTION(BlueprintPure, Category = "FlowSubsystem") From 6384560fdcc817415419f98aa9600d8c87a6581e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sun, 15 Feb 2026 13:03:10 +0100 Subject: [PATCH 406/485] typo fixed --- Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h index dea617112..955d64afb 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h @@ -45,7 +45,7 @@ class FLOWEDITOR_API UFlowGraphEditorSettings : public UDeveloperSettings UPROPERTY(config, EditAnywhere, Category = "Nodes") bool bShowNodeDescriptionWhilePlaying; - // Pin names will will be displayed in a format that is easier to read, even if PinFriendlyName wasn't set + // Pin names will be displayed in a format that is easier to read, even if PinFriendlyName wasn't set UPROPERTY(EditAnywhere, config, Category = "Nodes") bool bEnforceFriendlyPinNames; From 97e101b4fc8ed0c33552a23faf6340dd4648fe1e Mon Sep 17 00:00:00 2001 From: Rhillion Date: Sun, 15 Feb 2026 12:30:41 +0000 Subject: [PATCH 407/485] Added addon descriptions to node descriptions, with editor settings option to disable (#270) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Krzysiek Justyński --- Source/Flow/Private/Nodes/FlowNodeBase.cpp | 10 ++++++- Source/Flow/Public/Nodes/FlowNodeBase.h | 3 ++ .../Private/Graph/FlowGraphEditorSettings.cpp | 1 + .../Private/Graph/Nodes/FlowGraphNode.cpp | 29 ++++++++++++++----- .../Public/Graph/FlowGraphEditorSettings.h | 4 +++ 5 files changed, 38 insertions(+), 9 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp index 8f60f3e4e..2e772ce5f 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBase.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -730,6 +730,14 @@ FString UFlowNodeBase::GetNodeDescription() const return K2_GetNodeDescription(); } +FString UFlowNodeBase::GetAddOnDescriptions() const +{ + return FString::JoinBy(AddOns, LINE_TERMINATOR, [](const UFlowNodeBase* Addon) + { + return Addon->GetNodeDescription(); + }); +} + bool UFlowNodeBase::CanModifyFlowDataPinType() const { return !IsPlacedInFlowAsset() || IsFlowNamedPropertiesSupplier(); @@ -962,7 +970,7 @@ bool UFlowNodeBase::BuildMessage(FString& Message) const EDataValidationResult UFlowNodeBase::ValidateNode() { EDataValidationResult ValidationResult = EDataValidationResult::NotValidated; - + if (GetClass()->IsFunctionImplementedInScript(GET_FUNCTION_NAME_CHECKED(UFlowNodeBase, K2_ValidateNode))) { ValidationResult = K2_ValidateNode(); diff --git a/Source/Flow/Public/Nodes/FlowNodeBase.h b/Source/Flow/Public/Nodes/FlowNodeBase.h index 25894ff35..1512af721 100644 --- a/Source/Flow/Public/Nodes/FlowNodeBase.h +++ b/Source/Flow/Public/Nodes/FlowNodeBase.h @@ -507,6 +507,9 @@ class FLOW_API UFlowNodeBase public: // Short summary of node's content - displayed over node as NodeInfoPopup virtual FString GetNodeDescription() const; + + // Complex summary of node's content including its addons + FString GetAddOnDescriptions() const; #endif protected: diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp index 766ed1b87..47c7f7a52 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp @@ -9,6 +9,7 @@ UFlowGraphEditorSettings::UFlowGraphEditorSettings() : NodeDoubleClickTarget(EFlowNodeDoubleClickTarget::PrimaryAssetOrNodeDefinition) , bShowNodeClass(false) , bShowNodeDescriptionWhilePlaying(true) + , bShowAddonDescriptions(true) , bEnforceFriendlyPinNames(false) , bShowSubGraphPreview(true) , bShowSubGraphPath(true) diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 38668c28d..b83b60496 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -361,7 +361,7 @@ void UFlowGraphNode::ReconstructNode() { FlowNode->UpdateNodeConfigText(); } - + // This ensures the graph editor 'Refresh' button still rebuilds all the graph widgets even if the FlowGraphNode has nothing to update // Ideally we could get rid of the 'Refresh' button, but I think it will keep being useful, esp. for users making rough custom widgets (void)OnReconstructNodeCompleted.ExecuteIfBound(); @@ -737,7 +737,22 @@ FString UFlowGraphNode::GetNodeDescription() const { if (NodeInstance && (GEditor->PlayWorld == nullptr || GetDefault()->bShowNodeDescriptionWhilePlaying)) { - return NodeInstance->GetNodeDescription(); + const UFlowGraphEditorSettings* GraphEditorSettings = GetDefault(); + if (GEditor->PlayWorld == nullptr || GraphEditorSettings->bShowNodeDescriptionWhilePlaying) + { + FString Result = NodeInstance->GetNodeDescription(); + + if (GraphEditorSettings->bShowAddonDescriptions) + { + FString AddonDescriptions = NodeInstance->GetAddOnDescriptions(); + if (!AddonDescriptions.IsEmpty()) + { + return Result.Append(LINE_TERMINATOR).Append(AddonDescriptions); + } + } + + return Result; + } } return FString(); @@ -1568,7 +1583,7 @@ void UFlowGraphNode::RemoveSubNode(UFlowGraphNode* SubNode) { SubNode->NodeInstance->OnAddOnRequestedParentReconstruction.Unbind(); } - + SubNodes.RemoveSingle(SubNode); RebuildRuntimeAddOnsFromEditorSubNodes(); @@ -1585,7 +1600,7 @@ void UFlowGraphNode::RemoveAllSubNodes() SubNode->NodeInstance->OnAddOnRequestedParentReconstruction.Unbind(); } } - + SubNodes.Reset(); RebuildRuntimeAddOnsFromEditorSubNodes(); @@ -1879,8 +1894,7 @@ bool UFlowGraphNode::TryUpdateNodePins() const bool bPinsChanged = false; - if (!FlowNodeInstance->CanUserAddInput() && - !CheckPinsMatch(RequiredNodeInputPins, ExistingNodeInputPins)) + if (!FlowNodeInstance->CanUserAddInput() && !CheckPinsMatch(RequiredNodeInputPins, ExistingNodeInputPins)) { FlowNodeInstance->Modify(); @@ -1890,8 +1904,7 @@ bool UFlowGraphNode::TryUpdateNodePins() const bPinsChanged = true; } - if (!FlowNodeInstance->CanUserAddOutput() && - !CheckPinsMatch(RequiredNodeOutputPins, ExistingNodeOutputPins)) + if (!FlowNodeInstance->CanUserAddOutput() && !CheckPinsMatch(RequiredNodeOutputPins, ExistingNodeOutputPins)) { FlowNodeInstance->Modify(); diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h index 955d64afb..047f75bcd 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h @@ -45,6 +45,10 @@ class FLOWEDITOR_API UFlowGraphEditorSettings : public UDeveloperSettings UPROPERTY(config, EditAnywhere, Category = "Nodes") bool bShowNodeDescriptionWhilePlaying; + // Display descriptions from attached addons in node descriptions + UPROPERTY(EditAnywhere, config, Category = "Nodes") + bool bShowAddonDescriptions; + // Pin names will be displayed in a format that is easier to read, even if PinFriendlyName wasn't set UPROPERTY(EditAnywhere, config, Category = "Nodes") bool bEnforceFriendlyPinNames; From 62574985cb3b2b628f1abdc8f2e93ce28c61a863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sun, 15 Feb 2026 14:12:42 +0100 Subject: [PATCH 408/485] added transaction for changing Signal mode contributed by MaksymKapelianovych in closed PR --- Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp | 2 ++ Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index 0e9a97c82..8f4656fa9 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -1508,6 +1508,8 @@ bool SFlowGraphEditor::HasAnyBreakpoints() const void SFlowGraphEditor::SetSignalMode(const EFlowSignalMode Mode) const { + const FScopedTransaction Transaction(LOCTEXT("SetSignalMode", "Set Signal Mode")); + for (UFlowGraphNode* SelectedNode : GetSelectedFlowNodes()) { SelectedNode->SetSignalMode(Mode); diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index b83b60496..0b4a20c79 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -1208,8 +1208,10 @@ void UFlowGraphNode::ForcePinActivation(const FEdGraphPinReference PinReference) void UFlowGraphNode::SetSignalMode(const EFlowSignalMode Mode) { - if (UFlowNode* FlowNode = Cast(NodeInstance)) + UFlowNode* FlowNode = Cast(NodeInstance); + if (FlowNode && FlowNode->SignalMode != Mode) { + FlowNode->Modify(); FlowNode->SignalMode = Mode; OnSignalModeChanged.ExecuteIfBound(); } From c78e8cc870c4d82353766d35ecd3a04d82635cd5 Mon Sep 17 00:00:00 2001 From: Jeff Ott Date: Sun, 15 Feb 2026 05:46:42 -0800 Subject: [PATCH 409/485] Prefer AsyncSaveGameToSlot of the synchronous SaveGameToSlot (#292) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Prefer AsyncSaveGameToSlot of the synchronous SaveGameToSlot * expose option to config Co-Authored-By: Krzysztof Justyński --- .../Flow/Private/Nodes/Graph/FlowNode_Checkpoint.cpp | 10 +++++++++- Source/Flow/Public/Nodes/Graph/FlowNode_Checkpoint.h | 8 +++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_Checkpoint.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_Checkpoint.cpp index 6b51e9074..f1e56ce2e 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_Checkpoint.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_Checkpoint.cpp @@ -9,6 +9,7 @@ UFlowNode_Checkpoint::UFlowNode_Checkpoint(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) + , bUseAsyncSave(false) { #if WITH_EDITOR Category = TEXT("Graph"); @@ -22,7 +23,14 @@ void UFlowNode_Checkpoint::ExecuteInput(const FName& PinName) UFlowSaveGame* NewSaveGame = Cast(UGameplayStatics::CreateSaveGameObject(UFlowSaveGame::StaticClass())); GetFlowSubsystem()->OnGameSaved(NewSaveGame); - UGameplayStatics::SaveGameToSlot(NewSaveGame, NewSaveGame->SaveSlotName, 0); + if (bUseAsyncSave) + { + UGameplayStatics::AsyncSaveGameToSlot(NewSaveGame, NewSaveGame->SaveSlotName, 0); + } + else + { + UGameplayStatics::SaveGameToSlot(NewSaveGame, NewSaveGame->SaveSlotName, 0); + } } TriggerFirstOutput(true); diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_Checkpoint.h b/Source/Flow/Public/Nodes/Graph/FlowNode_Checkpoint.h index a4ce5605e..8916fe53f 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_Checkpoint.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_Checkpoint.h @@ -9,12 +9,18 @@ * Save the state of the game to the save file * It's recommended to replace this with game-specific variant and this node to UFlowGraphSettings::HiddenNodes */ -UCLASS(NotBlueprintable, meta = (DisplayName = "Checkpoint", Keywords = "autosave, save")) +UCLASS(NotBlueprintable, Config = Game, defaultconfig, meta = (DisplayName = "Checkpoint", Keywords = "autosave, save")) class FLOW_API UFlowNode_Checkpoint final : public UFlowNode { GENERATED_UCLASS_BODY() protected: + /* Change setting by editing DefaultGame.ini, add section + * [/Script/Flow.FlowNode_Checkpoint] + * bUseAsyncSave=True */ + UPROPERTY(VisibleAnywhere, Config, Category = "Checkpoint") + bool bUseAsyncSave; + virtual void ExecuteInput(const FName& PinName) override; virtual void OnLoad_Implementation() override; }; From b3683a8e99ae61dcd352054b597952c33cbdbffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sun, 15 Feb 2026 18:33:07 +0100 Subject: [PATCH 410/485] Code style update * Using style for multi-line comments for all comments above method and property declarations. Sentences end with a dot. * Using only a single asterisk to begin a multi-line comment as this lets us align text better. * Update class, structs, enums and interfaces descriptions to use the style of multi-line comment. * Removed parentheses in comments as this actually reduces cognitive load (while using multi-line comment style), I guess? * Removed empty line between copyright and "#pragma once" to make these lines more compact. * Added a few missing copyrights. Occasional static analysis fixes. Adding "explicit" and "const" specifiers. Removed a few redundant includes. --- Source/Flow/Flow.Build.cs | 19 +- Source/Flow/Private/FlowLogChannels.cpp | 2 + Source/Flow/Private/FlowSettings.cpp | 25 +- Source/Flow/Private/Nodes/FlowNodeBase.cpp | 34 +- .../Types/FlowAutoDataPinsWorkingData.cpp | 2 +- Source/Flow/Public/AddOns/FlowNodeAddOn.h | 35 +- .../AddOns/FlowNodeAddOn_PredicateAND.h | 2 - .../AddOns/FlowNodeAddOn_PredicateNOT.h | 2 - .../Public/AddOns/FlowNodeAddOn_PredicateOR.h | 2 - Source/Flow/Public/Asset/FlowAssetParams.h | 12 +- .../Flow/Public/Asset/FlowAssetParamsTypes.h | 27 +- .../Flow/Public/Asset/FlowAssetParamsUtils.h | 1 - .../Asset/FlowDeferredTransitionScope.h | 6 +- .../Public/Asset/FlowPinTypeMatchPolicy.h | 3 +- Source/Flow/Public/FlowAsset.h | 1 - Source/Flow/Public/FlowComponent.h | 37 +- .../Public/FlowExecutableActorComponent.h | 3 +- Source/Flow/Public/FlowLogChannels.h | 1 + Source/Flow/Public/FlowMessageLog.h | 1 - Source/Flow/Public/FlowModule.h | 1 - Source/Flow/Public/FlowPinSubsystem.h | 1 - Source/Flow/Public/FlowSave.h | 3 +- Source/Flow/Public/FlowSettings.h | 29 +- Source/Flow/Public/FlowSubsystem.h | 3 - Source/Flow/Public/FlowTags.h | 1 - Source/Flow/Public/FlowTypes.h | 7 +- Source/Flow/Public/FlowWorldSettings.h | 1 - .../Interfaces/FlowAssetProviderInterface.h | 10 +- .../FlowContextPinSupplierInterface.h | 25 +- .../Interfaces/FlowCoreExecutableInterface.h | 26 +- .../FlowDataPinGeneratorInterface.h | 6 +- .../FlowDataPinPropertyProviderInterface.h | 11 +- .../FlowDataPinValueOwnerInterface.h | 14 +- .../FlowDataPinValueSupplierInterface.h | 13 +- .../Public/Interfaces/FlowExecutionGate.h | 7 +- .../FlowExternalExecutableInterface.h | 13 +- .../FlowNamedPropertiesSupplierInterface.h | 3 +- ...NodeWithExternalDataPinSupplierInterface.h | 16 +- .../Interfaces/FlowPredicateInterface.h | 6 +- .../LevelSequence/FlowLevelSequenceActor.h | 3 +- .../LevelSequence/FlowLevelSequencePlayer.h | 7 +- .../MovieSceneFlowRepeaterSection.h | 3 +- .../MovieScene/MovieSceneFlowSectionBase.h | 3 +- .../MovieScene/MovieSceneFlowTemplate.h | 1 - .../Public/MovieScene/MovieSceneFlowTrack.h | 10 +- .../MovieScene/MovieSceneFlowTriggerSection.h | 3 +- .../Nodes/Actor/FlowNode_ComponentObserver.h | 15 +- .../Nodes/Actor/FlowNode_ExecuteComponent.h | 24 +- .../Public/Nodes/Actor/FlowNode_NotifyActor.h | 3 +- .../Nodes/Actor/FlowNode_OnActorRegistered.h | 3 +- .../Actor/FlowNode_OnActorUnregistered.h | 3 +- .../Nodes/Actor/FlowNode_OnNotifyFromActor.h | 7 +- .../Nodes/Actor/FlowNode_PlayLevelSequence.h | 17 +- .../Public/Nodes/Developer/FlowNode_Log.h | 9 +- Source/Flow/Public/Nodes/FlowNode.h | 69 ++-- .../Public/Nodes/FlowNodeAddOnBlueprint.h | 1 - Source/Flow/Public/Nodes/FlowNodeBase.h | 127 ++++--- Source/Flow/Public/Nodes/FlowNodeBlueprint.h | 1 - Source/Flow/Public/Nodes/FlowPin.h | 95 ++--- .../FlowNode_BlueprintDataPinSupplierBase.h | 14 +- .../Public/Nodes/Graph/FlowNode_Checkpoint.h | 7 +- .../Nodes/Graph/FlowNode_CustomEventBase.h | 3 +- .../Public/Nodes/Graph/FlowNode_CustomInput.h | 3 +- .../Nodes/Graph/FlowNode_CustomOutput.h | 5 +- .../Nodes/Graph/FlowNode_DefineProperties.h | 11 +- .../Flow/Public/Nodes/Graph/FlowNode_Finish.h | 5 +- .../Public/Nodes/Graph/FlowNode_FormatText.h | 12 +- .../Flow/Public/Nodes/Graph/FlowNode_Start.h | 9 +- .../Public/Nodes/Graph/FlowNode_SubGraph.h | 6 +- .../Flow/Public/Nodes/Route/FlowNode_Branch.h | 8 +- .../Public/Nodes/Route/FlowNode_Counter.h | 3 +- .../Nodes/Route/FlowNode_ExecutionMultiGate.h | 7 +- .../Nodes/Route/FlowNode_ExecutionSequence.h | 5 +- .../Public/Nodes/Route/FlowNode_LogicalAND.h | 5 +- .../Public/Nodes/Route/FlowNode_LogicalOR.h | 13 +- .../Public/Nodes/Route/FlowNode_Reroute.h | 3 +- .../Flow/Public/Nodes/Route/FlowNode_Timer.h | 7 +- .../Public/Types/FlowActorOwnerComponentRef.h | 24 +- Source/Flow/Public/Types/FlowArray.h | 1 - .../Types/FlowAutoDataPinsWorkingData.h | 13 +- Source/Flow/Public/Types/FlowClassUtils.h | 1 - .../Types/FlowDataPinBlueprintLibrary.h | 347 +++++++++++------- .../Flow/Public/Types/FlowDataPinProperties.h | 200 +++++----- .../FlowDataPinPropertyToValueMigration.h | 48 +-- Source/Flow/Public/Types/FlowDataPinResults.h | 104 ++---- Source/Flow/Public/Types/FlowDataPinValue.h | 15 +- .../Public/Types/FlowDataPinValuesStandard.h | 84 +++-- Source/Flow/Public/Types/FlowEnumUtils.h | 10 +- .../Public/Types/FlowGameplayTagMapUtils.h | 29 +- .../Public/Types/FlowInjectComponentsHelper.h | 17 +- .../Types/FlowInjectComponentsManager.h | 18 +- .../Public/Types/FlowNamedDataPinProperty.h | 31 +- Source/Flow/Public/Types/FlowPinEnums.h | 7 +- Source/Flow/Public/Types/FlowPinType.h | 15 +- Source/Flow/Public/Types/FlowPinTypeName.h | 1 - .../Public/Types/FlowPinTypeNamesStandard.h | 11 +- .../Public/Types/FlowPinTypeNodeTemplates.h | 6 +- .../Flow/Public/Types/FlowPinTypeTemplates.h | 26 +- .../Flow/Public/Types/FlowPinTypesStandard.h | 78 ++-- Source/Flow/Public/Types/FlowStructUtils.h | 5 +- Source/FlowDebugger/FlowDebugger.Build.cs | 15 +- .../Public/Debugger/FlowDebuggerSettings.h | 1 - .../Public/Debugger/FlowDebuggerSubsystem.h | 13 +- .../Public/Debugger/FlowDebuggerTypes.h | 5 +- .../FlowDebugger/Public/FlowDebuggerModule.h | 1 - Source/FlowEditor/FlowEditor.Build.cs | 15 +- .../Private/FlowEditorLogChannels.cpp | 2 + .../Private/Graph/FlowGraphEditorSettings.cpp | 1 - .../Public/Asset/AssetDefinition_FlowAsset.h | 1 - .../Asset/AssetDefinition_FlowAssetParams.h | 7 +- .../FlowEditor/Public/Asset/FlowAssetEditor.h | 18 +- .../Public/Asset/FlowAssetEditorContext.h | 3 +- .../Public/Asset/FlowAssetFactory.h | 3 +- .../Public/Asset/FlowAssetIndexer.h | 5 +- .../Public/Asset/FlowAssetParamsFactory.h | 3 +- .../Public/Asset/FlowAssetToolbar.h | 28 +- .../Public/Asset/FlowDebugEditorSubsystem.h | 1 - .../FlowEditor/Public/Asset/FlowDiffControl.h | 18 +- .../FlowEditor/Public/Asset/FlowImportUtils.h | 13 +- .../Public/Asset/FlowMessageLogListing.h | 5 +- .../FlowEditor/Public/Asset/FlowObjectDiff.h | 23 +- .../Public/Asset/SAssetRevisionMenu.h | 30 +- Source/FlowEditor/Public/Asset/SFlowDiff.h | 97 ++--- .../FlowActorOwnerComponentRefCustomization.h | 9 +- .../DetailCustomizations/FlowAssetDetails.h | 1 - .../FlowAssetParamsPtrCustomization.h | 5 +- .../FlowDataPinValueCustomization.h | 5 +- .../FlowDataPinValueCustomization_Class.h | 1 - .../FlowDataPinValueCustomization_Enum.h | 1 - .../FlowDataPinValueCustomization_Object.h | 1 - .../FlowDataPinValueOwnerCustomization.h | 5 +- .../FlowDataPinValueOwnerCustomizations.h | 1 - .../FlowDataPinValueStandardCustomizations.h | 1 - .../FlowNamedDataPinPropertyCustomization.h | 5 +- .../FlowNodeAddOn_Details.h | 1 - .../FlowNode_ComponentObserverDetails.h | 1 - .../FlowNode_CustomEventBaseDetails.h | 1 - .../FlowNode_CustomInputDetails.h | 1 - .../FlowNode_CustomOutputDetails.h | 1 - .../DetailCustomizations/FlowNode_Details.h | 1 - .../FlowNode_PlayLevelSequenceDetails.h | 1 - .../FlowNode_SubGraphDetails.h | 1 - .../FlowPinCustomization.h | 8 +- Source/FlowEditor/Public/Find/FindInFlow.h | 108 +++--- .../FlowEditor/Public/Find/FindInFlowEnums.h | 10 +- .../Public/Find/SFindInFlowFilterPopup.h | 1 - Source/FlowEditor/Public/FlowEditorCommands.h | 21 +- Source/FlowEditor/Public/FlowEditorDefines.h | 1 - .../FlowEditor/Public/FlowEditorLogChannels.h | 1 + Source/FlowEditor/Public/FlowEditorModule.h | 1 - Source/FlowEditor/Public/FlowEditorStyle.h | 3 +- Source/FlowEditor/Public/Graph/FlowGraph.h | 14 +- .../Graph/FlowGraphConnectionDrawingPolicy.h | 19 +- .../FlowEditor/Public/Graph/FlowGraphEditor.h | 3 +- .../Public/Graph/FlowGraphEditorSettings.h | 22 +- .../Public/Graph/FlowGraphNodesPolicy.h | 1 - .../Public/Graph/FlowGraphPinFactory.h | 3 +- .../FlowEditor/Public/Graph/FlowGraphSchema.h | 17 +- .../Public/Graph/FlowGraphSchema_Actions.h | 22 +- .../Public/Graph/FlowGraphSettings.h | 43 ++- .../FlowEditor/Public/Graph/FlowGraphUtils.h | 1 - .../Public/Graph/Nodes/FlowGraphNode.h | 69 ++-- .../Public/Graph/Nodes/FlowGraphNode_Branch.h | 1 - .../Nodes/FlowGraphNode_ExecutionSequence.h | 1 - .../Public/Graph/Nodes/FlowGraphNode_Finish.h | 1 - .../Graph/Nodes/FlowGraphNode_Reroute.h | 1 - .../Public/Graph/Nodes/FlowGraphNode_Start.h | 1 - .../Graph/Nodes/FlowGraphNode_SubGraph.h | 1 - .../Public/Graph/Widgets/SFlowGraphNode.h | 29 +- .../Graph/Widgets/SFlowGraphNode_Finish.h | 1 - .../Graph/Widgets/SFlowGraphNode_Start.h | 1 - .../Graph/Widgets/SFlowGraphNode_SubGraph.h | 1 - .../Public/Graph/Widgets/SFlowPalette.h | 9 +- .../Widgets/SGraphEditorActionMenuFlow.h | 20 +- .../Public/MovieScene/FlowSection.h | 9 +- .../Public/MovieScene/FlowTrackEditor.h | 12 +- .../AssetTypeActions_FlowNodeAddOnBlueprint.h | 1 - .../AssetTypeActions_FlowNodeBlueprint.h | 1 - .../Public/Nodes/FlowNodeBlueprintFactory.h | 13 +- .../Public/Pins/SFlowInputPinHandle.h | 1 - .../Public/Pins/SFlowOutputPinHandle.h | 1 - .../FlowEditor/Public/Pins/SFlowPinHandle.h | 1 - .../IFlowCuratedNamePropertyCustomization.h | 24 +- .../IFlowExtendedPropertyTypeCustomization.h | 22 +- .../Public/Utils/SLevelEditorFlow.h | 1 - 185 files changed, 1426 insertions(+), 1478 deletions(-) diff --git a/Source/Flow/Flow.Build.cs b/Source/Flow/Flow.Build.cs index 3e5613ce3..c6847ce45 100644 --- a/Source/Flow/Flow.Build.cs +++ b/Source/Flow/Flow.Build.cs @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - using UnrealBuildTool; public class Flow : ModuleRules @@ -8,13 +7,13 @@ public Flow(ReadOnlyTargetRules target) : base(target) { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - PublicDependencyModuleNames.AddRange(new[] - { + PublicDependencyModuleNames.AddRange( + [ "LevelSequence" - }); + ]); - PrivateDependencyModuleNames.AddRange(new[] - { + PrivateDependencyModuleNames.AddRange( + [ "Core", "CoreUObject", "DeveloperSettings", @@ -25,18 +24,18 @@ public Flow(ReadOnlyTargetRules target) : base(target) "NetCore", "Slate", "SlateCore" - }); + ]); if (target.Type == TargetType.Editor) { - PublicDependencyModuleNames.AddRange(new[] - { + PublicDependencyModuleNames.AddRange( + [ "GraphEditor", "MessageLog", "PropertyEditor", "SourceControl", "UnrealEd" - }); + ]); } } } \ No newline at end of file diff --git a/Source/Flow/Private/FlowLogChannels.cpp b/Source/Flow/Private/FlowLogChannels.cpp index 2f4c168b4..fa8abf671 100644 --- a/Source/Flow/Private/FlowLogChannels.cpp +++ b/Source/Flow/Private/FlowLogChannels.cpp @@ -1,3 +1,5 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + #include "FlowLogChannels.h" DEFINE_LOG_CATEGORY(LogFlow); diff --git a/Source/Flow/Private/FlowSettings.cpp b/Source/Flow/Private/FlowSettings.cpp index e6f835f4e..d51f7a144 100644 --- a/Source/Flow/Private/FlowSettings.cpp +++ b/Source/Flow/Private/FlowSettings.cpp @@ -7,13 +7,13 @@ UFlowSettings::UFlowSettings(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) - , bCreateFlowSubsystemOnClients(true) - , bWarnAboutMissingIdentityTags(true) + , bDeferTriggeredOutputsWhileTriggering(true) , bLogOnSignalDisabled(true) , bLogOnSignalPassthrough(true) - , bDeferTriggeredOutputsWhileTriggering(true) + , bCreateFlowSubsystemOnClients(true) , bUseAdaptiveNodeTitles(false) , DefaultExpectedOwnerClass(UFlowComponent::StaticClass()) + , bWarnAboutMissingIdentityTags(true) { } @@ -21,25 +21,22 @@ UFlowSettings::UFlowSettings(const FObjectInitializer& ObjectInitializer) void UFlowSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { Super::PostEditChangeProperty(PropertyChangedEvent); - - if (PropertyChangedEvent.GetMemberPropertyName() == GET_MEMBER_NAME_CHECKED( UFlowSettings, bUseAdaptiveNodeTitles )) + + if (PropertyChangedEvent.GetMemberPropertyName() == GET_MEMBER_NAME_CHECKED(UFlowSettings, bUseAdaptiveNodeTitles)) { - (void) OnAdaptiveNodeTitlesChanged.ExecuteIfBound(); + (void)OnAdaptiveNodeTitlesChanged.ExecuteIfBound(); } } #endif UClass* UFlowSettings::GetDefaultExpectedOwnerClass() const { - return CastChecked(TryResolveOrLoadSoftClass(DefaultExpectedOwnerClass), ECastCheckedType::NullAllowed); -} + UClass* Result = DefaultExpectedOwnerClass.ResolveClass(); -UClass* UFlowSettings::TryResolveOrLoadSoftClass(const FSoftClassPath& SoftClassPath) -{ - if (UClass* Resolved = SoftClassPath.ResolveClass()) + if (Result == nullptr) { - return Resolved; + Result = DefaultExpectedOwnerClass.TryLoadClass(); } - return SoftClassPath.TryLoadClass(); -} \ No newline at end of file + return CastChecked(Result, ECastCheckedType::NullAllowed); +} diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp index 2e772ce5f..94a134b06 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBase.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -137,23 +137,6 @@ void UFlowNodeBase::OnActivate() } } -void UFlowNodeBase::ExecuteInputForSelfAndAddOns(const FName& PinName) -{ - // AddOns can introduce input pins to Nodes without the Node being aware of the addition. - // To ensure that Nodes and AddOns only get the input pins signaled that they expect, - // we are filtering the PinName vs. the expected InputPins before carrying on with the ExecuteInput - - if (IsSupportedInputPinName(PinName)) - { - ExecuteInput(PinName); - } - - for (UFlowNodeAddOn* AddOn : AddOns) - { - AddOn->ExecuteInputForSelfAndAddOns(PinName); - } -} - void UFlowNodeBase::ExecuteInput(const FName& PinName) { IFlowCoreExecutableInterface::ExecuteInput(PinName); @@ -179,6 +162,23 @@ void UFlowNodeBase::Cleanup() IFlowCoreExecutableInterface::Cleanup(); } +void UFlowNodeBase::ExecuteInputForSelfAndAddOns(const FName& PinName) +{ + // AddOns can introduce input pins to Nodes without the Node being aware of the addition. + // To ensure that Nodes and AddOns only get the input pins signaled that they expect, + // we are filtering the PinName vs. the expected InputPins before carrying on with the ExecuteInput + + if (IsSupportedInputPinName(PinName)) + { + ExecuteInput(PinName); + } + + for (UFlowNodeAddOn* AddOn : AddOns) + { + AddOn->ExecuteInputForSelfAndAddOns(PinName); + } +} + void UFlowNodeBase::TriggerOutputPin(const FFlowOutputPinHandle Pin, const bool bFinish, const EFlowPinActivationType ActivationType) { TriggerOutput(Pin.PinName, bFinish, ActivationType); diff --git a/Source/Flow/Private/Types/FlowAutoDataPinsWorkingData.cpp b/Source/Flow/Private/Types/FlowAutoDataPinsWorkingData.cpp index fd3fba57c..f003630f6 100644 --- a/Source/Flow/Private/Types/FlowAutoDataPinsWorkingData.cpp +++ b/Source/Flow/Private/Types/FlowAutoDataPinsWorkingData.cpp @@ -286,7 +286,7 @@ void FFlowAutoDataPinsWorkingData::AddPinMappingToNode( UFlowNode& FlowNode, const FName& FinalPinName, const FName& OriginalPinName, - int32 PropertyOwnerIndex) + const int32 PropertyOwnerIndex) { // Omit trivial cases that runtime lookup can infer if (PropertyOwnerIndex == 0 && FinalPinName == OriginalPinName) diff --git a/Source/Flow/Public/AddOns/FlowNodeAddOn.h b/Source/Flow/Public/AddOns/FlowNodeAddOn.h index abe446cf9..4ab18c78b 100644 --- a/Source/Flow/Public/AddOns/FlowNodeAddOn.h +++ b/Source/Flow/Public/AddOns/FlowNodeAddOn.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Nodes/FlowNodeBase.h" @@ -21,18 +20,18 @@ class UFlowNodeAddOn : public UFlowNodeBase FLOW_API UFlowNodeAddOn(); protected: - // The FlowNode that contains this AddOn - // (accessible only when initialized, runtime only) + /* The Flow Node that contains this AddOn. + * Accessible only when initialized, runtime only. */ UPROPERTY(Transient) TObjectPtr FlowNode; - // Input pins to add to the owning flow node - // If defined, ExecuteInput will only be executed for these inputs + /* Input pins to add to the owning Flow Node. + * If defined, ExecuteInput will only be executed for these inputs. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "FlowNodeAddOn") TArray InputPins; #if WITH_EDITORONLY_DATA - // Output pins to add to the owning flow node + /* Output pins to add to the owning Flow Node. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "FlowNodeAddOn") TArray OutputPins; #endif @@ -40,13 +39,13 @@ class UFlowNodeAddOn : public UFlowNodeBase public: // UFlowNodeBase - // AddOns may opt in to be eligible for a given parent - // - ParentTemplate - the template of the FlowNode or FlowNodeAddOn that is being considered as a potential parent - // - AdditionalAddOnsToAssumeAreChildren - other AddOns to assume that are already child AddOns for the purposes of this test. - // This list will be populated with the 'other' AddOns in a multi-paste operation in the editor, - // because some paste-targets can only accept a certain mix of addons, so we must know the rest of the set being pasted - // to make the correct decision about whether to allow AddOnTemplate to be added. - // https://forums.unrealengine.com/t/default-parameters-with-tarrays/330225 for details on AutoCreateRefTerm + /* AddOns may opt in to be eligible for a given parent. + * - ParentTemplate - the template of the FlowNode or FlowNodeAddOn that is being considered as a potential parent. + * - AdditionalAddOnsToAssumeAreChildren - other AddOns to assume that are already child AddOns for the purposes of this test. + * This list will be populated with the 'other' AddOns in a multi-paste operation in the editor, + * because some paste-targets can only accept a certain mix of addons, so we must know the rest of the set being pasted + * to make the correct decision about whether to allow AddOnTemplate to be added. + * See: https://forums.unrealengine.com/t/default-parameters-with-tarrays/330225 for details on AutoCreateRefTerm. */ UFUNCTION(BlueprintNativeEvent, BlueprintPure, Category = "FlowNodeAddOn", meta = (AutoCreateRefTerm = AdditionalAddOnsToAssumeAreChildren)) FLOW_API EFlowAddOnAcceptResult AcceptFlowNodeAddOnParent(const UFlowNodeBase* ParentTemplate, const TArray& AdditionalAddOnsToAssumeAreChildren) const; @@ -65,17 +64,17 @@ class UFlowNodeAddOn : public UFlowNodeBase // UFlowNodeAddOn - // The FlowNode that contains this AddOn - // (accessible only when initialized, runtime only) + /* The FlowNode that contains this AddOn. + * Accessible only when initialized, runtime only. */ UFUNCTION(BlueprintCallable, BlueprintPure, Category = "FlowNodeAddon", DisplayName = "Get Flow Node") FLOW_API UFlowNode* GetFlowNode() const; - // Will crawl the hierarchy until it finds a flow node (addons can be attached to other add-ons). + /* Will crawl the hierarchy until it finds a flow node (addons can be attached to other add-ons). */ FLOW_API UFlowNode* FindOwningFlowNode() const; // -- - // Returns a random seed suitable for this flow node addon - // by default, uses the seed for the Flow Node that this addon is attached to. + /* Returns a random seed suitable for this AddOn. + * By default, uses the seed for the Flow Node that this addon is attached to. */ FLOW_API virtual int32 GetRandomSeed() const override; #if WITH_EDITOR diff --git a/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateAND.h b/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateAND.h index c04202ebe..17885f9b8 100644 --- a/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateAND.h +++ b/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateAND.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "AddOns/FlowNodeAddOn.h" @@ -7,7 +6,6 @@ #include "FlowNodeAddOn_PredicateAND.generated.h" -// Forward Declarations class UFlowNode; UCLASS(MinimalApi, NotBlueprintable, meta = (DisplayName = "AND")) diff --git a/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateNOT.h b/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateNOT.h index 99d9e258b..4d3da6347 100644 --- a/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateNOT.h +++ b/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateNOT.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "AddOns/FlowNodeAddOn.h" @@ -7,7 +6,6 @@ #include "FlowNodeAddOn_PredicateNOT.generated.h" -// Forward Declarations class UFlowNode; UCLASS(MinimalApi, NotBlueprintable, meta = (DisplayName = "NOT")) diff --git a/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateOR.h b/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateOR.h index 2748506a4..dcd1dff30 100644 --- a/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateOR.h +++ b/Source/Flow/Public/AddOns/FlowNodeAddOn_PredicateOR.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "AddOns/FlowNodeAddOn.h" @@ -7,7 +6,6 @@ #include "FlowNodeAddOn_PredicateOR.generated.h" -// Forward Declarations class UFlowNode; UCLASS(MinimalApi, NotBlueprintable, meta = (DisplayName = "OR")) diff --git a/Source/Flow/Public/Asset/FlowAssetParams.h b/Source/Flow/Public/Asset/FlowAssetParams.h index f55e5c93b..5ce19f2dc 100644 --- a/Source/Flow/Public/Asset/FlowAssetParams.h +++ b/Source/Flow/Public/Asset/FlowAssetParams.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Engine/DataAsset.h" @@ -28,15 +27,15 @@ class FLOW_API UFlowAssetParams public: #if WITH_EDITORONLY_DATA - // Reference to the associated Flow Asset. + /* Reference to the associated Flow Asset. */ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = FlowAssetParams) TSoftObjectPtr OwnerFlowAsset; - // Reference to the "Parent" params object to inherit from (if any). + /* Reference to the "Parent" params object to inherit from (if any). */ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = FlowAssetParams) FFlowAssetParamsPtr ParentParams; - // Array of properties synchronized with the Start node (local adds/overrides; effective flattened via ReconcilePropertiesWithParentParams). + /* Array of properties synchronized with the Start node (local adds/overrides; effective flattened via ReconcilePropertiesWithParentParams). */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = FlowAssetParams, meta = (EditFixedSize)) TArray Properties; #endif @@ -67,13 +66,13 @@ class FLOW_API UFlowAssetParams virtual EDataValidationResult IsDataValid(FDataValidationContext& Context) const override; // -- - // Generates properties from the associated Start node or updates Start node from params. + /* Generates properties from the associated Start node or updates Start node from params. */ EFlowReconcilePropertiesResult ReconcilePropertiesWithStartNode( const FDateTime& FlowAssetLastSaveTimeStamp, const TSoftObjectPtr& InOwnerFlowAsset, TArray& MutablePropertiesFromStartNode); - // Updates properties from ParentParams, handling inheritance and name enforcement. + /* Updates properties from ParentParams, handling inheritance and name enforcement. */ EFlowReconcilePropertiesResult ReconcilePropertiesWithParentParams(); void ConfigureFlowAssetParams(TSoftObjectPtr OwnerAsset, TSoftObjectPtr InParentParams, const TArray& InProperties); @@ -105,7 +104,6 @@ class FLOW_API UFlowAssetParams EFlowReconcilePropertiesResult CheckForParentCycle() const; void ModifyAndRebuildPropertiesMap(); - void RebuildPropertiesMap(); #endif }; \ No newline at end of file diff --git a/Source/Flow/Public/Asset/FlowAssetParamsTypes.h b/Source/Flow/Public/Asset/FlowAssetParamsTypes.h index 95e50c7e0..604a09594 100644 --- a/Source/Flow/Public/Asset/FlowAssetParamsTypes.h +++ b/Source/Flow/Public/Asset/FlowAssetParamsTypes.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Types/FlowEnumUtils.h" @@ -9,7 +8,9 @@ class UFlowAssetParams; -// Result of reconciling Flow Asset Params with Start node or SuperParams. +/** + * Result of reconciling Flow Asset Params with Start node or SuperParams. + */ UENUM(BlueprintType) enum class EFlowReconcilePropertiesResult : uint8 { @@ -41,27 +42,29 @@ FLOW_ENUM_RANGE_VALUES(EFlowReconcilePropertiesResult) namespace EFlowReconcilePropertiesResult_Classifiers { - FORCEINLINE bool IsSuccessResult(EFlowReconcilePropertiesResult Result) { return FLOW_IS_ENUM_IN_SUBRANGE(Result, EFlowReconcilePropertiesResult::Success); } - FORCEINLINE bool IsModifiedResult(EFlowReconcilePropertiesResult Result) { return FLOW_IS_ENUM_IN_SUBRANGE(Result, EFlowReconcilePropertiesResult::Modified); } - FORCEINLINE bool IsErrorResult(EFlowReconcilePropertiesResult Result) { return FLOW_IS_ENUM_IN_SUBRANGE(Result, EFlowReconcilePropertiesResult::Error); } + FORCEINLINE bool IsSuccessResult(const EFlowReconcilePropertiesResult Result) { return FLOW_IS_ENUM_IN_SUBRANGE(Result, EFlowReconcilePropertiesResult::Success); } + FORCEINLINE bool IsModifiedResult(const EFlowReconcilePropertiesResult Result) { return FLOW_IS_ENUM_IN_SUBRANGE(Result, EFlowReconcilePropertiesResult::Modified); } + FORCEINLINE bool IsErrorResult(const EFlowReconcilePropertiesResult Result) { return FLOW_IS_ENUM_IN_SUBRANGE(Result, EFlowReconcilePropertiesResult::Error); } } -// Wrapper for TSoftObjectPtr to enable editor customization. -// -// Supported metadata tags: -// - ShowCreateNew - Should we show the "Create New" button? -// - HideChildParams - When showing a chooser, should we hide "Child" params or not? (Child params have a non-null ParentParams) +/** + * Wrapper for TSoftObjectPtr to enable editor customization. + * + * Supported metadata tags + * ShowCreateNew: Should we show the "Create New" button? + * HideChildParams: When showing a chooser, should we hide "Child" params or not? (Child params have a non-null ParentParams) + */ USTRUCT(BlueprintType) struct FLOW_API FFlowAssetParamsPtr { GENERATED_BODY() FFlowAssetParamsPtr() = default; - FFlowAssetParamsPtr(TSoftObjectPtr InAssetParamsPtr) : AssetPtr(InAssetParamsPtr) { } + explicit FFlowAssetParamsPtr(const TSoftObjectPtr InAssetParamsPtr) : AssetPtr(InAssetParamsPtr) { } UFlowAssetParams* ResolveFlowAssetParams() const; - // Reference to the Flow Asset Params. + /* Reference to the Flow Asset Params. */ UPROPERTY(EditAnywhere, Category = FlowAssetParams, meta = (EditAssetInline)) TSoftObjectPtr AssetPtr; }; diff --git a/Source/Flow/Public/Asset/FlowAssetParamsUtils.h b/Source/Flow/Public/Asset/FlowAssetParamsUtils.h index ec6d85b5c..a89149d69 100644 --- a/Source/Flow/Public/Asset/FlowAssetParamsUtils.h +++ b/Source/Flow/Public/Asset/FlowAssetParamsUtils.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Misc/DateTime.h" diff --git a/Source/Flow/Public/Asset/FlowDeferredTransitionScope.h b/Source/Flow/Public/Asset/FlowDeferredTransitionScope.h index 66a3783be..bc465e078 100644 --- a/Source/Flow/Public/Asset/FlowDeferredTransitionScope.h +++ b/Source/Flow/Public/Asset/FlowDeferredTransitionScope.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Misc/Guid.h" @@ -27,10 +26,9 @@ struct FLOW_API FFlowDeferredTransitionScope const TArray& GetDeferredTriggers() const { return DeferredTriggers; } protected: - - // Deferred triggers for this scope + /* Deferred triggers for this scope. */ TArray DeferredTriggers; - // Is currently accepting new deferred triggers + /* Is currently accepting new deferred triggers. */ bool bIsOpen = true; }; diff --git a/Source/Flow/Public/Asset/FlowPinTypeMatchPolicy.h b/Source/Flow/Public/Asset/FlowPinTypeMatchPolicy.h index ee2d8a32c..41e2f15bb 100644 --- a/Source/Flow/Public/Asset/FlowPinTypeMatchPolicy.h +++ b/Source/Flow/Public/Asset/FlowPinTypeMatchPolicy.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "FlowPinTypeMatchPolicy.generated.h" @@ -37,7 +36,7 @@ struct FFlowPinTypeMatchPolicy UPROPERTY() EFlowPinTypeMatchRules PinTypeMatchRules = EFlowPinTypeMatchRules::StandardPinTypeMatchRulesMask; - // Pin categories to allow beyond an exact match + /* Pin categories to allow beyond an exact match. */ UPROPERTY() TSet PinCategories; }; diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 0d2c9c66a..a34d545cf 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "FlowSave.h" diff --git a/Source/Flow/Public/FlowComponent.h b/Source/Flow/Public/FlowComponent.h index 5f5ef8a7f..76bd80753 100644 --- a/Source/Flow/Public/FlowComponent.h +++ b/Source/Flow/Public/FlowComponent.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Components/ActorComponent.h" @@ -99,20 +98,20 @@ class FLOW_API UFlowComponent : public UActorComponent, public IFlowAssetProvide // Component sending Notify Tags to Flow Graph, or any other listener private: - // Stores only recently sent tags + /* Stores only recently sent tags. */ UPROPERTY(ReplicatedUsing = OnRep_SentNotifyTags) FGameplayTagContainer RecentlySentNotifyTags; public: const FGameplayTagContainer& GetRecentlySentNotifyTags() const { return RecentlySentNotifyTags; } - // Send single notification from the actor to Flow graphs - // If set on server, it's always going to be replicated to clients + /* Send single notification from the actor to Flow graphs. + * If set on server, it's always going to be replicated to clients. */ UFUNCTION(BlueprintCallable, Category = "Flow") void NotifyGraph(const FGameplayTag NotifyTag, const EFlowNetMode NetMode = EFlowNetMode::Authority); - // Send multiple notifications at once - from the actor to Flow graphs - // If set on server, it's always going to be replicated to clients + /* Send multiple notifications at once - from the actor to Flow graphs. + * If set on server, it's always going to be replicated to clients. */ UFUNCTION(BlueprintCallable, Category = "Flow") void BulkNotifyGraph(const FGameplayTagContainer NotifyTags, const EFlowNetMode NetMode = EFlowNetMode::Authority); @@ -127,7 +126,7 @@ class FLOW_API UFlowComponent : public UActorComponent, public IFlowAssetProvide // Component receiving Notify Tags from Flow Graph private: - // Stores only recently replicated tags + /* Stores only recently replicated tags. */ UPROPERTY(ReplicatedUsing = OnRep_NotifyTagsFromGraph) FGameplayTagContainer NotifyTagsFromGraph; @@ -140,7 +139,7 @@ class FLOW_API UFlowComponent : public UActorComponent, public IFlowAssetProvide void OnRep_NotifyTagsFromGraph(); public: - // Receive notification from Flow graph or another Flow Component + /* Receive notification from Flow graph or another Flow Component. */ UPROPERTY(BlueprintAssignable, Category = "Flow") FFlowComponentDynamicNotify ReceiveNotify; @@ -148,12 +147,12 @@ class FLOW_API UFlowComponent : public UActorComponent, public IFlowAssetProvide // Sending Notify Tags between Flow components private: - // Stores only recently replicated tags + /* Stores only recently replicated tags. */ UPROPERTY(ReplicatedUsing = OnRep_NotifyTagsFromAnotherComponent) TArray NotifyTagsFromAnotherComponent; public: - // Send notification to another actor containing Flow Component + /* Send notification to another actor containing Flow Component. */ UFUNCTION(BlueprintCallable, Category = "Flow") virtual void NotifyActor(const FGameplayTag ActorTag, const FGameplayTag NotifyTag, const EFlowNetMode NetMode = EFlowNetMode::Authority); @@ -165,31 +164,31 @@ class FLOW_API UFlowComponent : public UActorComponent, public IFlowAssetProvide // Root Flow public: - // Asset that might instantiated as "Root Flow" + /* Asset that might be instantiated as "Root Flow". */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "RootFlow") TObjectPtr RootFlow; - // Flow Asset Params to use as the data pin value supplier for the Root Flow + /* Flow Asset Params to use as the data pin value supplier for the Root Flow.*/ UPROPERTY(EditAnywhere, Category = "RootFlow") FFlowAssetParamsPtr RootFlowParams; - // If true, component will start Root Flow on Begin Play + /* If true, component will start Root Flow on Begin Play. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "RootFlow") bool bAutoStartRootFlow; - // Networking mode for creating this Root Flow + /* Networking mode for creating this Root Flow. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "RootFlow") EFlowNetMode RootFlowMode; - // If false, another Root Flow instance won't be created from this component, if this Flow Asset is already instantiated + /* If false, another Root Flow instance won't be created from this component, if this Flow Asset is already instantiated. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "RootFlow") bool bAllowMultipleInstances; UPROPERTY(SaveGame) FString SavedAssetInstanceName; - // This will instantiate Flow Asset assigned on this component. - // Created Flow Asset instance will be a "root flow", as additional Flow Assets can be instantiated via Sub Graph node + /* This will instantiate Flow Asset assigned on this component. + * Created Flow Asset instance will be a "root flow", as additional Flow Assets can be instantiated via Sub Graph node. */ UFUNCTION(BlueprintCallable, Category = "RootFlow") virtual void StartRootFlow(); @@ -211,11 +210,11 @@ class FLOW_API UFlowComponent : public UActorComponent, public IFlowAssetProvide // Custom Input and Output events public: - // This will trigger a specific CustomInput on this components root flow + /* This will trigger a specific CustomInput on this component's root flow. */ UFUNCTION(BlueprintCallable, Category = "RootFlow") void TriggerRootFlowCustomInput(const FName& EventName) const; - // Called when a Root flow asset triggers a CustomOutput + /* Called when a Root flow asset triggers a CustomOutput. */ UFUNCTION(BlueprintImplementableEvent, DisplayName = "OnRootFlowCustomEvent") void BP_OnRootFlowCustomEvent(UFlowAsset* RootFlowInstance, const FName& EventName); diff --git a/Source/Flow/Public/FlowExecutableActorComponent.h b/Source/Flow/Public/FlowExecutableActorComponent.h index 960820b36..fa71c56a7 100644 --- a/Source/Flow/Public/FlowExecutableActorComponent.h +++ b/Source/Flow/Public/FlowExecutableActorComponent.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Components/ActorComponent.h" @@ -29,7 +28,7 @@ class FLOW_API UFlowExecutableActorComponent FSimpleDelegate FlowDataPinValuesRebuildDelegate; protected: - // FlowNodeBase that will execute this component in the FlowGraph on our behalf + /* FlowNodeBase that will execute this component in the FlowGraph on our behalf. */ UPROPERTY(Transient, BlueprintReadOnly, Category = DataPins) TObjectPtr FlowNodeProxy; diff --git a/Source/Flow/Public/FlowLogChannels.h b/Source/Flow/Public/FlowLogChannels.h index d56a50040..68c554028 100644 --- a/Source/Flow/Public/FlowLogChannels.h +++ b/Source/Flow/Public/FlowLogChannels.h @@ -1,3 +1,4 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #pragma once #include "Logging/LogMacros.h" diff --git a/Source/Flow/Public/FlowMessageLog.h b/Source/Flow/Public/FlowMessageLog.h index 859bf37f0..07ff9e9f1 100644 --- a/Source/Flow/Public/FlowMessageLog.h +++ b/Source/Flow/Public/FlowMessageLog.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #if WITH_EDITOR diff --git a/Source/Flow/Public/FlowModule.h b/Source/Flow/Public/FlowModule.h index d74fcb98b..b3ebd81e1 100644 --- a/Source/Flow/Public/FlowModule.h +++ b/Source/Flow/Public/FlowModule.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Modules/ModuleInterface.h" diff --git a/Source/Flow/Public/FlowPinSubsystem.h b/Source/Flow/Public/FlowPinSubsystem.h index 23660f635..fcaffa4b1 100644 --- a/Source/Flow/Public/FlowPinSubsystem.h +++ b/Source/Flow/Public/FlowPinSubsystem.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Subsystems/EngineSubsystem.h" diff --git a/Source/Flow/Public/FlowSave.h b/Source/Flow/Public/FlowSave.h index f79711f9c..b5ea9e30a 100644 --- a/Source/Flow/Public/FlowSave.h +++ b/Source/Flow/Public/FlowSave.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "GameFramework/SaveGame.h" @@ -68,7 +67,7 @@ struct FLOW_API FFlowComponentSaveData struct FLOW_API FFlowArchive : public FObjectAndNameAsStringProxyArchive { - FFlowArchive(FArchive& InInnerArchive) : FObjectAndNameAsStringProxyArchive(InInnerArchive, true) + explicit FFlowArchive(FArchive& InInnerArchive) : FObjectAndNameAsStringProxyArchive(InInnerArchive, true) { ArIsSaveGame = true; } diff --git a/Source/Flow/Public/FlowSettings.h b/Source/Flow/Public/FlowSettings.h index 6afddab6c..394f9e6a2 100644 --- a/Source/Flow/Public/FlowSettings.h +++ b/Source/Flow/Public/FlowSettings.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Engine/DeveloperSettings.h" @@ -7,10 +6,8 @@ #include "UObject/SoftObjectPath.h" #include "FlowSettings.generated.h" -class UFlowNode; - /** - * + * Mostly runtime settings of the Flow Graph. */ UCLASS(Config = Game, defaultconfig, meta = (DisplayName = "Flow")) class FLOW_API UFlowSettings : public UDeveloperSettings @@ -24,13 +21,10 @@ class FLOW_API UFlowSettings : public UDeveloperSettings virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; #endif - /* Set if to False, if you don't want to create client-side Flow Graphs. - * And you don't access to the Flow Component registry on clients. */ - UPROPERTY(Config, EditAnywhere, Category = "Networking") - bool bCreateFlowSubsystemOnClients; - - UPROPERTY(Config, EditAnywhere, Category = "SaveSystem") - bool bWarnAboutMissingIdentityTags; + /* If True, defer the Triggered Outputs for a FlowAsset while it is currently processing a TriggeredInput. + * If False, use legacy behavior for backward compatability. */ + UPROPERTY(Config, EditAnywhere, Category = "Flow") + bool bDeferTriggeredOutputsWhileTriggering; /* If enabled, runtime logs will be added when a flow node signal mode is set to Disabled. */ UPROPERTY(Config, EditAnywhere, Category = "Flow") @@ -40,10 +34,10 @@ class FLOW_API UFlowSettings : public UDeveloperSettings UPROPERTY(Config, EditAnywhere, Category = "Flow") bool bLogOnSignalPassthrough; - /* If True, defer the Triggered Outputs for a FlowAsset while it is currently processing a TriggeredInput. - * If False, use legacy behavior for backward compatability. */ - UPROPERTY(Config, EditAnywhere, Category = "Flow") - bool bDeferTriggeredOutputsWhileTriggering; + /* Set if to False, if you don't want to create client-side Flow Graphs. + * And you don't access to the Flow Component registry on clients. */ + UPROPERTY(Config, EditAnywhere, Category = "Networking") + bool bCreateFlowSubsystemOnClients; /* Adjust the Titles for FlowNodes to be more expressive than default * by incorporating data that would otherwise go in the Description. */ @@ -59,11 +53,12 @@ class FLOW_API UFlowSettings : public UDeveloperSettings UPROPERTY(EditAnywhere, Config, Category = "Nodes") FSoftClassPath DefaultExpectedOwnerClass; + UPROPERTY(Config, EditAnywhere, Category = "SaveSystem") + bool bWarnAboutMissingIdentityTags; + public: UClass* GetDefaultExpectedOwnerClass() const; - static UClass* TryResolveOrLoadSoftClass(const FSoftClassPath& SoftClassPath); - #if WITH_EDITORONLY_DATA virtual FName GetCategoryName() const override { return FName("Flow Graph"); } virtual FText GetSectionText() const override { return INVTEXT("Settings"); } diff --git a/Source/Flow/Public/FlowSubsystem.h b/Source/Flow/Public/FlowSubsystem.h index cd649a509..9711c57d0 100644 --- a/Source/Flow/Public/FlowSubsystem.h +++ b/Source/Flow/Public/FlowSubsystem.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "GameFramework/Actor.h" @@ -9,8 +8,6 @@ #include "FlowComponent.h" #include "FlowSubsystem.generated.h" -class UFlowAsset; -class UFlowNode_SubGraph; class IFlowDataPinValueSupplierInterface; DECLARE_DYNAMIC_MULTICAST_DELEGATE(FSimpleFlowEvent); diff --git a/Source/Flow/Public/FlowTags.h b/Source/Flow/Public/FlowTags.h index 1a88501d7..bc24ac741 100644 --- a/Source/Flow/Public/FlowTags.h +++ b/Source/Flow/Public/FlowTags.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "NativeGameplayTags.h" diff --git a/Source/Flow/Public/FlowTypes.h b/Source/Flow/Public/FlowTypes.h index 099359843..9b51d6c56 100644 --- a/Source/Flow/Public/FlowTypes.h +++ b/Source/Flow/Public/FlowTypes.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "GameplayTagContainer.h" @@ -50,9 +49,9 @@ namespace EFlowNodeState_Classifiers FORCEINLINE bool IsFinishedState(EFlowNodeState State) { return FLOW_IS_ENUM_IN_SUBRANGE(State, EFlowNodeState::Finished); } } -// Finish Policy value is read by Flow Node -// Nodes have opportunity to terminate themselves differently if Flow Graph has been aborted -// Example: Spawn node might despawn all actors if Flow Graph is aborted, not completed +/* Finish Policy value is read by Flow Node + * Nodes have opportunity to terminate themselves differently if Flow Graph has been aborted + * Example: Spawn node might despawn all actors if Flow Graph is aborted, not completed */ UENUM(BlueprintType) enum class EFlowFinishPolicy : uint8 { diff --git a/Source/Flow/Public/FlowWorldSettings.h b/Source/Flow/Public/FlowWorldSettings.h index 5ebd5e6a6..44d125a41 100644 --- a/Source/Flow/Public/FlowWorldSettings.h +++ b/Source/Flow/Public/FlowWorldSettings.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "GameFramework/WorldSettings.h" diff --git a/Source/Flow/Public/Interfaces/FlowAssetProviderInterface.h b/Source/Flow/Public/Interfaces/FlowAssetProviderInterface.h index 29d9bc95a..6cfde6454 100644 --- a/Source/Flow/Public/Interfaces/FlowAssetProviderInterface.h +++ b/Source/Flow/Public/Interfaces/FlowAssetProviderInterface.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "UObject/Interface.h" @@ -8,8 +7,10 @@ class UFlowAsset; -// Interface to define a UFlowAsset provider. -// This is used for filtering in FFlowAssetParamsPtrCustomization +/** + * Interface to define a UFlowAsset provider. + * This is used for filtering in FFlowAssetParamsPtrCustomization. + */ UINTERFACE(MinimalAPI, Blueprintable, DisplayName = "Flow Asset Provider Interface") class UFlowAssetProviderInterface : public UInterface { @@ -21,8 +22,7 @@ class FLOW_API IFlowAssetProviderInterface GENERATED_BODY() public: - - // Provide a FlowAsset for use in FFlowAssetParamsPtr resolution + /* Provide a FlowAsset for use in FFlowAssetParamsPtr resolution. */ UFUNCTION(BlueprintImplementableEvent, Category = FlowAssetParams, DisplayName = "ProvideFlowAsset") UFlowAsset* K2_ProvideFlowAsset() const; virtual UFlowAsset* ProvideFlowAsset() const; diff --git a/Source/Flow/Public/Interfaces/FlowContextPinSupplierInterface.h b/Source/Flow/Public/Interfaces/FlowContextPinSupplierInterface.h index ad2f59a29..45764be3c 100644 --- a/Source/Flow/Public/Interfaces/FlowContextPinSupplierInterface.h +++ b/Source/Flow/Public/Interfaces/FlowContextPinSupplierInterface.h @@ -1,15 +1,16 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "UObject/Interface.h" -#include "Nodes/FlowPin.h" +#include "Nodes/FlowPin.h" #include "FlowContextPinSupplierInterface.generated.h" -// A flow element (UFlowNode, UFlowNodeAddOn, etc.) that may supply context pins -// "Context Pins" are those that can be dynamically added/removed to a FlowNode by property -// settings on the flow node, by subobjects, etc. +/** + * A flow element (UFlowNode, UFlowNodeAddOn, etc.) that may supply context pins. + * "Context Pins" are those that can be dynamically added/removed to a FlowNode by property + * settings on the flow node, by subobjects, etc. + */ UINTERFACE(MinimalAPI, Blueprintable, DisplayName = "Flow ContextPin Supplier Interface") class UFlowContextPinSupplierInterface : public UInterface { @@ -23,31 +24,31 @@ class FLOW_API IFlowContextPinSupplierInterface public: #if WITH_EDITOR - // Be careful, enabling it might cause loading gigabytes of data as nodes would load all related data (i.e. Level Sequences) + /* Be careful, enabling it might cause loading gigabytes of data as nodes would load all related data (i.e. Level Sequences). */ virtual bool CanRefreshContextPinsOnLoad() const { return false; } -#endif // WITH_EDITOR +#endif UFUNCTION(BlueprintNativeEvent, Category = "FlowNode In-Editor Functions", DisplayName = "SupportsContextPins", meta = (DevelopmentOnly)) bool K2_SupportsContextPins() const; virtual bool K2_SupportsContextPins_Implementation() const; #if WITH_EDITOR - // Note: This method can only be called by native implementors of the interface, so we still have to manually handle and check - // classes that only implement the interface in Blueprint. + /* Note: This method can only be called by native implementors of the interface, so we still have to manually handle and check + * classes that only implement the interface in Blueprint. */ virtual bool SupportsContextPins() const { return Execute_K2_SupportsContextPins(Cast(this)); } -#endif // WITH_EDITOR +#endif UFUNCTION(BlueprintNativeEvent, Category = "FlowNode In-Editor Functions", DisplayName = "GetContextInputs", meta = (DevelopmentOnly)) TArray K2_GetContextInputs() const; virtual TArray K2_GetContextInputs_Implementation() const; #if WITH_EDITOR virtual TArray GetContextInputs() const { return Execute_K2_GetContextInputs(Cast(this)); } -#endif // WITH_EDITOR +#endif UFUNCTION(BlueprintNativeEvent, Category = "FlowNode In-Editor Functions", DisplayName = "GetContextOutputs", meta = (DevelopmentOnly)) TArray K2_GetContextOutputs() const; virtual TArray K2_GetContextOutputs_Implementation() const; #if WITH_EDITOR virtual TArray GetContextOutputs() const { return Execute_K2_GetContextOutputs(Cast(this)); } -#endif // WITH_EDITOR +#endif }; diff --git a/Source/Flow/Public/Interfaces/FlowCoreExecutableInterface.h b/Source/Flow/Public/Interfaces/FlowCoreExecutableInterface.h index f4a9c1271..a08ebfc5f 100644 --- a/Source/Flow/Public/Interfaces/FlowCoreExecutableInterface.h +++ b/Source/Flow/Public/Interfaces/FlowCoreExecutableInterface.h @@ -1,13 +1,14 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "UObject/Interface.h" #include "FlowCoreExecutableInterface.generated.h" -// Implemented by objects that can execute within a flow graph -// (eg, UFlowNode and UFlowNodeAddOn subclasses implement this) +/** + * Implemented by objects that can execute within a Flow Graph. + * Example: UFlowNode and UFlowNodeAddOn subclasses implement this. + */ UINTERFACE(MinimalAPI, Blueprintable, DisplayName = "Flow Core Executable Interface") class UFlowCoreExecutableInterface : public UInterface { @@ -19,44 +20,43 @@ class FLOW_API IFlowCoreExecutableInterface GENERATED_BODY() public: - - // Method called just after creating the node instance, while initializing the Flow Asset instance - // This happens before executing graph, only called during gameplay + /* Method called just after creating the node instance, while initializing the Flow Asset instance. + * This happens before executing graph, only called during gameplay. */ UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", DisplayName = "Initialize Instance") void K2_InitializeInstance(); virtual void InitializeInstance() { Execute_K2_InitializeInstance(Cast(this)); } - // Event called from UMKTFlowNode::DeinitializeInstance() + /* Event called from UMKTFlowNode::DeinitializeInstance(). */ UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", DisplayName = "Deinitialize Instance") void K2_DeinitializeInstance(); virtual void DeinitializeInstance() { Execute_K2_DeinitializeInstance(Cast(this)); } - // If preloading is enabled, will be called to preload content + /* If preloading is enabled, will be called to preload content. */ UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", DisplayName = "Preload Content") void K2_PreloadContent(); virtual void PreloadContent() { Execute_K2_PreloadContent(Cast(this)); } - // If preloading is enabled, will be called to flush content + /* If preloading is enabled, will be called to flush content. */ UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", DisplayName = "Flush Content") void K2_FlushContent(); virtual void FlushContent() { Execute_K2_FlushContent(Cast(this)); } - // Called immediately before the first input is triggered + /* Called immediately before the first input is triggered. */ UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", DisplayName = "OnActivate") void K2_OnActivate(); virtual void OnActivate() { Execute_K2_OnActivate(Cast(this)); } - // Event called after node finished the work + /* Event called after node finished the work. */ UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", DisplayName = "Cleanup") void K2_Cleanup(); virtual void Cleanup() { Execute_K2_Cleanup(Cast(this)); } - // Define what happens when node is terminated from the outside + /* Define what happens when node is terminated from the outside. */ UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", DisplayName = "Force Finish Node") void K2_ForceFinishNode(); virtual void ForceFinishNode() { Execute_K2_ForceFinishNode(Cast(this)); } - // Event reacting on triggering Input pin + /* Event reacting on triggering Input pin. */ UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", DisplayName = "Execute Input") void K2_ExecuteInput(const FName& PinName); virtual void ExecuteInput(const FName& PinName) { Execute_K2_ExecuteInput(Cast(this), PinName); } diff --git a/Source/Flow/Public/Interfaces/FlowDataPinGeneratorInterface.h b/Source/Flow/Public/Interfaces/FlowDataPinGeneratorInterface.h index fea685c08..ff9dfb92d 100644 --- a/Source/Flow/Public/Interfaces/FlowDataPinGeneratorInterface.h +++ b/Source/Flow/Public/Interfaces/FlowDataPinGeneratorInterface.h @@ -1,13 +1,15 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "UObject/Interface.h" + #include "FlowDataPinGeneratorInterface.generated.h" struct FFlowAutoDataPinsWorkingData; -// Interface for Classes that can auto-generate DataPins +/** + * Interface for Classes that can auto-generate DataPins. + */ UINTERFACE(MinimalAPI, NotBlueprintable, DisplayName = "Flow Data Pin Generator Interface", meta = (CannotImplementInterfaceInBlueprint)) class UFlowDataPinGeneratorInterface : public UInterface { diff --git a/Source/Flow/Public/Interfaces/FlowDataPinPropertyProviderInterface.h b/Source/Flow/Public/Interfaces/FlowDataPinPropertyProviderInterface.h index 21b964bcf..2a0338d90 100644 --- a/Source/Flow/Public/Interfaces/FlowDataPinPropertyProviderInterface.h +++ b/Source/Flow/Public/Interfaces/FlowDataPinPropertyProviderInterface.h @@ -1,15 +1,17 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "StructUtils/InstancedStruct.h" #include "UObject/Interface.h" + #include "FlowDataPinPropertyProviderInterface.generated.h" struct FFlowDataPinValue; -// Interface to define a FFlowDataPinValue provider. -// This is used in plumbing data in the AI Flow extension plugin into the Flow Data Pins framework. +/** + * Interface to define a FFlowDataPinValue provider. + * This is used in plumbing data in the AI Flow extension plugin into the Flow Data Pins framework. + */ UINTERFACE(MinimalAPI, NotBlueprintable) class UFlowDataPinPropertyProviderInterface : public UInterface { @@ -21,7 +23,6 @@ class FLOW_API IFlowDataPinPropertyProviderInterface GENERATED_BODY() public: - - // Provide a FFlowDataPinValue (instancedStruct) for the creation of data pins and supplying their values. + /* Provide a FFlowDataPinValue (instancedStruct) for the creation of data pins and supplying their values. */ virtual bool TryProvideFlowDataPinProperty(TInstancedStruct& OutFlowDataPinProperty) const = 0; }; diff --git a/Source/Flow/Public/Interfaces/FlowDataPinValueOwnerInterface.h b/Source/Flow/Public/Interfaces/FlowDataPinValueOwnerInterface.h index 3ae3136cc..9ed7eb8df 100644 --- a/Source/Flow/Public/Interfaces/FlowDataPinValueOwnerInterface.h +++ b/Source/Flow/Public/Interfaces/FlowDataPinValueOwnerInterface.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "UObject/Interface.h" @@ -21,23 +20,22 @@ class FLOW_API IFlowDataPinValueOwnerInterface public: #if WITH_EDITOR - - // Determines if the pin's type properties (bIsInputPin, MultiType) can be modified + /* Determines if the pin's type properties (bIsInputPin, MultiType) can be modified. */ virtual bool CanModifyFlowDataPinType() const { return true; } - // Determines if the bIsInputPin checkbox should be visible in the Details panel + /* Determines if the bIsInputPin checkbox should be visible in the Details panel. */ virtual bool ShowFlowDataPinValueInputPinCheckbox() const { return true; } - // Should the ClassFilter or EnumClass row be visible? + /* Should the ClassFilter or EnumClass row be visible? */ virtual bool ShowFlowDataPinValueClassFilter(const FFlowDataPinValue* Value) const { return true; } - // Base policy for whether the ClassFilter / Enum source can be edited + /* Base policy for whether the ClassFilter / Enum source can be edited. */ virtual bool CanEditFlowDataPinValueClassFilter(const FFlowDataPinValue* Value) const { return true; } - // Set the delegate that forces a layout rebuild (provided by owner detail customization). + /* Set the delegate that forces a layout rebuild (provided by owner detail customization). */ virtual void SetFlowDataPinValuesRebuildDelegate(FSimpleDelegate InDelegate) {} - // Request a details rebuild (executes delegate if bound). + /* Request a details rebuild (executes delegate if bound). */ virtual void RequestFlowDataPinValuesDetailsRebuild() {} #endif }; diff --git a/Source/Flow/Public/Interfaces/FlowDataPinValueSupplierInterface.h b/Source/Flow/Public/Interfaces/FlowDataPinValueSupplierInterface.h index a98e19cef..4efff617f 100644 --- a/Source/Flow/Public/Interfaces/FlowDataPinValueSupplierInterface.h +++ b/Source/Flow/Public/Interfaces/FlowDataPinValueSupplierInterface.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "UObject/Interface.h" @@ -7,9 +6,11 @@ #include "Types/FlowDataPinResults.h" #include "FlowDataPinValueSupplierInterface.generated.h" -// Interface to define a Flow Data Pin value supplier. This is generally a UFlowNode subclass, -// but we may support external suppliers that are not flow nodes in the future -// (eg, for supplying configuration values for the root graph) +/** + * Interface to define a Flow Data Pin value supplier. This is generally a UFlowNode subclass, + * but we may support external suppliers that are not flow nodes in the future. + * Example: for supplying configuration values for the root graph. + */ UINTERFACE(MinimalAPI, NotBlueprintable, DisplayName = "Flow Data Pin Value Supplier Interface") class UFlowDataPinValueSupplierInterface : public UInterface { @@ -21,8 +22,8 @@ class FLOW_API IFlowDataPinValueSupplierInterface GENERATED_BODY() public: - // Can this node actually supply Data Pin values? - // Implementers of this interface will need to use their own logic to answer this question. + /* Can this node actually supply Data Pin values? + * Implementers of this interface will need to use their own logic to answer this question. */ virtual bool CanSupplyDataPinValues() const { return true; } virtual FFlowDataPinResult TrySupplyDataPin(FName PinName) const { return FFlowDataPinResult(); } diff --git a/Source/Flow/Public/Interfaces/FlowExecutionGate.h b/Source/Flow/Public/Interfaces/FlowExecutionGate.h index 8859dc266..dc3240cb0 100644 --- a/Source/Flow/Public/Interfaces/FlowExecutionGate.h +++ b/Source/Flow/Public/Interfaces/FlowExecutionGate.h @@ -1,8 +1,7 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once -#include "CoreMinimal.h" +#include "UObject/Interface.h" class UFlowAsset; @@ -15,7 +14,7 @@ class FLOW_API IFlowExecutionGate public: virtual ~IFlowExecutionGate() = default; - /** Return true when Flow execution should be halted globally. */ + /* Return true when Flow execution should be halted globally. */ virtual bool IsFlowExecutionHalted() const = 0; }; @@ -28,7 +27,7 @@ class FLOW_API FFlowExecutionGate static void SetGate(IFlowExecutionGate* InGate); static IFlowExecutionGate* GetGate(); - /** True if a gate exists and it currently wants Flow execution halted. */ + /* True if a gate exists and it currently wants Flow execution halted. */ static bool IsHalted(); private: diff --git a/Source/Flow/Public/Interfaces/FlowExternalExecutableInterface.h b/Source/Flow/Public/Interfaces/FlowExternalExecutableInterface.h index 4117ef9b2..42bddc2a5 100644 --- a/Source/Flow/Public/Interfaces/FlowExternalExecutableInterface.h +++ b/Source/Flow/Public/Interfaces/FlowExternalExecutableInterface.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "UObject/Interface.h" @@ -9,8 +8,9 @@ class UFlowNodeBase; -// Implemented by external objects that can execute within a flow graph -// via a FlowNode or FlowNodeAddOn proxy +/** + * Implemented by external objects that can execute within a Flow Graph via a FlowNode or FlowNodeAddOn proxy. + */ UINTERFACE(MinimalAPI, Blueprintable, DisplayName = "Flow External Executable Interface") class UFlowExternalExecutableInterface : public UInterface { @@ -22,10 +22,9 @@ class FLOW_API IFlowExternalExecutableInterface GENERATED_BODY() public: - - // Called immediately prior to OnActivate() to set the native proxy that is executing the - // external executable object in the flow graph. This is primarily done so that the external element has a - // handle to call TriggerOutput() and Finish() when it has completed its work. + /* Called immediately prior to OnActivate() to set the native proxy that is executing the + * external executable object in the flow graph. This is primarily done so that the external element has a + * handle to call TriggerOutput() and Finish() when it has completed its work. */ UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", DisplayName = "PreActivateExternalFlowExecutable") void K2_PreActivateExternalFlowExecutable(const UFlowNodeBase* FlowNodeBase); virtual void PreActivateExternalFlowExecutable(UFlowNodeBase& FlowNodeBase); diff --git a/Source/Flow/Public/Interfaces/FlowNamedPropertiesSupplierInterface.h b/Source/Flow/Public/Interfaces/FlowNamedPropertiesSupplierInterface.h index b41bd87ac..042fc6fb4 100644 --- a/Source/Flow/Public/Interfaces/FlowNamedPropertiesSupplierInterface.h +++ b/Source/Flow/Public/Interfaces/FlowNamedPropertiesSupplierInterface.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "UObject/Interface.h" @@ -24,7 +23,7 @@ class FLOW_API IFlowNamedPropertiesSupplierInterface public: #if WITH_EDITOR - // Returns the array of named properties defined by this node. + /* Returns the array of named properties defined by this node. */ virtual TArray& GetMutableNamedProperties() = 0; #endif }; diff --git a/Source/Flow/Public/Interfaces/FlowNodeWithExternalDataPinSupplierInterface.h b/Source/Flow/Public/Interfaces/FlowNodeWithExternalDataPinSupplierInterface.h index 34247e8ee..10f86c2a4 100644 --- a/Source/Flow/Public/Interfaces/FlowNodeWithExternalDataPinSupplierInterface.h +++ b/Source/Flow/Public/Interfaces/FlowNodeWithExternalDataPinSupplierInterface.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "UObject/Interface.h" @@ -8,9 +7,11 @@ class IFlowDataPinValueSupplierInterface; struct FFlowPin; -// Interface for special flow node types that support an external data pin supplier. -// The primary (only?) implementing node is UFlowNode_Start, which supplies its pin data externally from -// either the SubGraph that instanced the graph that is being started. +/** + * Interface for special flow node types that support an external data pin supplier. + * The primary (only?) implementing node is UFlowNode_Start, which supplies its pin data externally from + * either the SubGraph that instanced the graph that is being started. + */ UINTERFACE(MinimalAPI, NotBlueprintable, DisplayName = "Flow Node With External Data Pin Value Supplier Interface") class UFlowNodeWithExternalDataPinSupplierInterface : public UInterface { @@ -22,13 +23,12 @@ class FLOW_API IFlowNodeWithExternalDataPinSupplierInterface GENERATED_BODY() public: - - // Set the external DataPinValueSupplier for this node to use. + /* Set the external DataPinValueSupplier for this node to use. */ virtual void SetDataPinValueSupplier(IFlowDataPinValueSupplierInterface* DataPinValueSupplier) = 0; - // Append the external InputPins for the external supplier to include in its own pins (eg, UFlowNode_Subgraph) + /* Append the external InputPins for the external supplier to include in its own pins (eg, UFlowNode_Subgraph). */ virtual bool TryAppendExternalInputPins(TArray& InOutPins) const { return false; } - // Get the IFlowDataPinValueSupplierInterface for the external supplier for this node + /* Get the IFlowDataPinValueSupplierInterface for the external supplier for this node. */ virtual IFlowDataPinValueSupplierInterface* GetExternalDataPinSupplier() const = 0; }; diff --git a/Source/Flow/Public/Interfaces/FlowPredicateInterface.h b/Source/Flow/Public/Interfaces/FlowPredicateInterface.h index 8dda3a27a..c383c2cb8 100644 --- a/Source/Flow/Public/Interfaces/FlowPredicateInterface.h +++ b/Source/Flow/Public/Interfaces/FlowPredicateInterface.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "UObject/Interface.h" @@ -9,7 +8,9 @@ class UFlowNodeAddOn; -// Predicate interface for AddOns +/** + * Predicate interface for AddOns. + */ UINTERFACE(MinimalAPI, BlueprintType, Blueprintable, DisplayName = "Flow Predicate Interface") class UFlowPredicateInterface : public UInterface { @@ -21,7 +22,6 @@ class FLOW_API IFlowPredicateInterface GENERATED_BODY() public: - UFUNCTION(BlueprintNativeEvent) bool EvaluatePredicate() const; virtual bool EvaluatePredicate_Implementation() const { return true; } diff --git a/Source/Flow/Public/LevelSequence/FlowLevelSequenceActor.h b/Source/Flow/Public/LevelSequence/FlowLevelSequenceActor.h index 506dafb40..99c6d5cf3 100644 --- a/Source/Flow/Public/LevelSequence/FlowLevelSequenceActor.h +++ b/Source/Flow/Public/LevelSequence/FlowLevelSequenceActor.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "LevelSequenceActor.h" @@ -8,7 +7,7 @@ class ULevelSequence; /** - * Custom ALevelSequenceActor is needed to override ULevelSequencePlayer class + * Custom ALevelSequenceActor is needed to override ULevelSequencePlayer class. */ UCLASS(hideCategories=(Rendering, Physics, LOD, Activation, Input)) class FLOW_API AFlowLevelSequenceActor : public ALevelSequenceActor diff --git a/Source/Flow/Public/LevelSequence/FlowLevelSequencePlayer.h b/Source/Flow/Public/LevelSequence/FlowLevelSequencePlayer.h index ba4e1f0a6..038125d14 100644 --- a/Source/Flow/Public/LevelSequence/FlowLevelSequencePlayer.h +++ b/Source/Flow/Public/LevelSequence/FlowLevelSequencePlayer.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "LevelSequencePlayer.h" @@ -8,7 +7,7 @@ class UFlowNode; /** - * Custom ULevelSequencePlayer allows for binding Flow Nodes to Level Sequence events + * Custom ULevelSequencePlayer allows for binding Flow Nodes to Level Sequence events. */ UCLASS() class FLOW_API UFlowLevelSequencePlayer : public ULevelSequencePlayer @@ -16,12 +15,12 @@ class FLOW_API UFlowLevelSequencePlayer : public ULevelSequencePlayer GENERATED_UCLASS_BODY() private: - // most likely this is a UFlowNode_PlayLevelSequence or its child + /* Most likely this is a UFlowNode_PlayLevelSequence or its child. */ UPROPERTY() TObjectPtr FlowEventReceiver; public: - // variant of ULevelSequencePlayer::CreateLevelSequencePlayer + /* Variant of ULevelSequencePlayer::CreateLevelSequencePlayer. */ static UFlowLevelSequencePlayer* CreateFlowLevelSequencePlayer( const UObject* WorldContextObject, ULevelSequence* LevelSequence, diff --git a/Source/Flow/Public/MovieScene/MovieSceneFlowRepeaterSection.h b/Source/Flow/Public/MovieScene/MovieSceneFlowRepeaterSection.h index 3642103f6..007f76f1c 100644 --- a/Source/Flow/Public/MovieScene/MovieSceneFlowRepeaterSection.h +++ b/Source/Flow/Public/MovieScene/MovieSceneFlowRepeaterSection.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "MovieSceneFlowSectionBase.h" @@ -18,7 +17,7 @@ class FLOW_API UMovieSceneFlowRepeaterSection : public UMovieSceneFlowSectionBas virtual TArrayView GetAllEntryPoints() override { return MakeArrayView(&EventName, 1); } #endif - /** The event that should be triggered each time this section is evaluated */ + /* The event that should be triggered each time this section is evaluated. */ UPROPERTY(EditAnywhere, Category = "Flow") FString EventName; }; diff --git a/Source/Flow/Public/MovieScene/MovieSceneFlowSectionBase.h b/Source/Flow/Public/MovieScene/MovieSceneFlowSectionBase.h index 6ea290bc2..93d0ac15c 100644 --- a/Source/Flow/Public/MovieScene/MovieSceneFlowSectionBase.h +++ b/Source/Flow/Public/MovieScene/MovieSceneFlowSectionBase.h @@ -1,12 +1,11 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "MovieSceneSection.h" #include "MovieSceneFlowSectionBase.generated.h" /** - * Base class for flow sections + * Base class for flow sections. */ UCLASS() class FLOW_API UMovieSceneFlowSectionBase : public UMovieSceneSection diff --git a/Source/Flow/Public/MovieScene/MovieSceneFlowTemplate.h b/Source/Flow/Public/MovieScene/MovieSceneFlowTemplate.h index 47ff27af4..827a4c29b 100644 --- a/Source/Flow/Public/MovieScene/MovieSceneFlowTemplate.h +++ b/Source/Flow/Public/MovieScene/MovieSceneFlowTemplate.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Evaluation/MovieSceneEvalTemplate.h" diff --git a/Source/Flow/Public/MovieScene/MovieSceneFlowTrack.h b/Source/Flow/Public/MovieScene/MovieSceneFlowTrack.h index 0176468ae..bc08d8aa9 100644 --- a/Source/Flow/Public/MovieScene/MovieSceneFlowTrack.h +++ b/Source/Flow/Public/MovieScene/MovieSceneFlowTrack.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Tracks/MovieSceneEventTrack.h" @@ -42,25 +41,26 @@ class FLOW_API UMovieSceneFlowTrack virtual void PostCompile(FMovieSceneEvaluationTrack& Track, const FMovieSceneTrackCompilerArgs& Args) const override; virtual bool SupportsMultipleRows() const override { return true; } virtual FMovieSceneTrackSegmentBlenderPtr GetTrackSegmentBlender() const override; + // -- #if WITH_EDITOR virtual FText GetDefaultDisplayName() const override; #endif - /** If events should be fired when passed playing the sequence forwards. */ + /* If events should be fired when passed playing the sequence forwards. */ UPROPERTY(EditAnywhere, Category=TrackEvent) uint32 bFireEventsWhenForwards:1; - /** If events should be fired when passed playing the sequence backwards. */ + /* If events should be fired when passed playing the sequence backwards. */ UPROPERTY(EditAnywhere, Category=TrackEvent) uint32 bFireEventsWhenBackwards:1; - /** Defines where in the evaluation to trigger events */ + /* Defines where in the evaluation to trigger events. */ UPROPERTY(EditAnywhere, Category=TrackEvent) EFireEventsAtPosition EventPosition; private: - /** The track's sections. */ + /* The track's sections. */ UPROPERTY() TArray> Sections; }; diff --git a/Source/Flow/Public/MovieScene/MovieSceneFlowTriggerSection.h b/Source/Flow/Public/MovieScene/MovieSceneFlowTriggerSection.h index 1650cd373..0023c61fb 100644 --- a/Source/Flow/Public/MovieScene/MovieSceneFlowTriggerSection.h +++ b/Source/Flow/Public/MovieScene/MovieSceneFlowTriggerSection.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Channels/MovieSceneStringChannel.h" @@ -22,7 +21,7 @@ class FLOW_API UMovieSceneFlowTriggerSection : public UMovieSceneFlowSectionBase virtual TArrayView GetAllEntryPoints() override { return StringChannel.GetData().GetValues(); } #endif - /** The channel that defines this section's timed events */ + /* The channel that defines this section's timed. */ UPROPERTY() FMovieSceneStringChannel StringChannel; }; diff --git a/Source/Flow/Public/Nodes/Actor/FlowNode_ComponentObserver.h b/Source/Flow/Public/Nodes/Actor/FlowNode_ComponentObserver.h index ecc5bbf35..5ff3708f5 100644 --- a/Source/Flow/Public/Nodes/Actor/FlowNode_ComponentObserver.h +++ b/Source/Flow/Public/Nodes/Actor/FlowNode_ComponentObserver.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "GameplayTagContainer.h" @@ -10,8 +9,8 @@ class UFlowComponent; /** - * Base class for nodes operating on actors with the Flow Component - * Such nodes usually wait until a specific action occurs in the actor + * Base class for nodes operating on actors with the Flow Component. + * Such nodes usually wait until a specific action occurs in the actor. */ UCLASS(Abstract, NotBlueprintable) class FLOW_API UFlowNode_ComponentObserver : public UFlowNode @@ -24,17 +23,17 @@ class FLOW_API UFlowNode_ComponentObserver : public UFlowNode UPROPERTY(EditAnywhere, Category = "ObservedComponent") FGameplayTagContainer IdentityTags; - // Container A: Identity Tags in Flow Component - // Container B: Identity Tags listed above + /* Container A: Identity Tags in Flow Component. + * Container B: Identity Tags listed above. */ UPROPERTY(EditAnywhere, Category = "ObservedComponent") EFlowTagContainerMatchType IdentityMatchType; - // This node will become Completed, if Success Limit > 0 and Success Count reaches this limit - // Set this to zero, if you'd like receive events indefinitely (node would finish work only if explicitly Stopped) + /* This node will become Completed, if Success Limit > 0 and Success Count reaches this limit. + * Set this to zero, if you'd like receive events indefinitely (node would finish work only if explicitly Stopped). */ UPROPERTY(EditAnywhere, Category = "Lifetime", meta = (ClampMin = 0)) int32 SuccessLimit; - // This node will become Completed, if Success Limit > 0 and Success Count reaches this limit + /* This node will become Completed, if Success Limit > 0 and Success Count reaches this limit. */ UPROPERTY(VisibleAnywhere, Category = "Lifetime", SaveGame) int32 SuccessCount; diff --git a/Source/Flow/Public/Nodes/Actor/FlowNode_ExecuteComponent.h b/Source/Flow/Public/Nodes/Actor/FlowNode_ExecuteComponent.h index 78f85f7e5..a992b15f1 100644 --- a/Source/Flow/Public/Nodes/Actor/FlowNode_ExecuteComponent.h +++ b/Source/Flow/Public/Nodes/Actor/FlowNode_ExecuteComponent.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Nodes/FlowNode.h" @@ -8,7 +7,6 @@ #include "FlowNode_ExecuteComponent.generated.h" -// Forward Declarations class UFlowInjectComponentsManager; UENUM() @@ -35,7 +33,7 @@ namespace EExecuteComponentSource_Classifiers } /** - * Execute a UActorComponent on the owning actor as if it was a flow subgraph + * Execute a UActorComponent on the owning actor as if it was a flow subgraph. */ UCLASS(NotBlueprintable, meta = (DisplayName = "Execute Component")) class FLOW_API UFlowNode_ExecuteComponent : public UFlowNode @@ -43,7 +41,6 @@ class FLOW_API UFlowNode_ExecuteComponent : public UFlowNode GENERATED_BODY() public: - UFlowNode_ExecuteComponent(); // IFlowCoreExecutableInterface @@ -86,7 +83,6 @@ class FLOW_API UFlowNode_ExecuteComponent : public UFlowNode #endif // WITH_EDITOR protected: - #if WITH_EDITOR void RefreshPins(); const UActorComponent* TryGetExpectedComponent() const; @@ -103,34 +99,32 @@ class FLOW_API UFlowNode_ExecuteComponent : public UFlowNode TSubclassOf TryGetExpectedActorOwnerClass() const; protected: - - // Executable Component (by name) on the expected Flow owning Actor - // (the component must implement the IFlowExecutableComponentInterface) + /* Executable Component (by name) on the expected Flow owning Actor. + * The component must implement the IFlowExecutableComponentInterface. */ UPROPERTY(EditAnywhere, Category = "Flow Executable Component", meta = (DisplayName = "Component to Execute", MustImplement = "/Script/Flow.FlowCoreExecutableInterface,/Script/Flow.FlowExternalExecutableInterface", EditConditionHides, EditCondition = "ComponentSource == EExecuteComponentSource::BindToExisting || ComponentSource == EExecuteComponentSource::Undetermined")) FFlowActorOwnerComponentRef ComponentRef; - // Component (template) to inject on the spawned actor, may be configured inline + /* Component (template) to inject on the spawned actor, may be configured inline. */ UPROPERTY(EditAnywhere, Instanced, Category = Configuration, DisplayName = "Inject & Execute Component (from Template)", meta = (MustImplement = "/Script/Flow.FlowCoreExecutableInterface,/Script/Flow.FlowExternalExecutableInterface", EditConditionHides, EditCondition = "ComponentSource == EExecuteComponentSource::InjectFromTemplate || ComponentSource == EExecuteComponentSource::Undetermined")) TObjectPtr ComponentTemplate = nullptr; - // Component (class) to inject on the spawned actor + /* Component (class) to inject on the spawned actor. */ UPROPERTY(EditAnywhere, Category = Configuration, DisplayName = "Inject & Execute Component (by Class)", meta = (MustImplement = "/Script/Flow.FlowCoreExecutableInterface,/Script/Flow.FlowExternalExecutableInterface", EditConditionHides, EditCondition = "ComponentSource == EExecuteComponentSource::InjectFromClass || ComponentSource == EExecuteComponentSource::Undetermined")) TSubclassOf ComponentClass = nullptr; - // Manager object to inject and remove components from the Flow owning Actor + /* Manager object to inject and remove components from the Flow owning Actor. */ UPROPERTY(Transient) TObjectPtr InjectComponentsManager = nullptr; - // Look for the component (by class) on the Actor and re-use it (rather than injecting) - // if the component already exists. + /* Look for the component (by class) on the Actor and re-use it (rather than injecting) if the component already exists. */ UPROPERTY(EditAnywhere, Category = Configuration, DisplayName = "Re-use existing component if found", meta = (EditConditionHides, EditCondition = "ComponentSource == EExecuteComponentSource::InjectFromClass")) bool bReuseExistingComponent = true; - // Allow injecting the component, if it cannot be found on the Actor + /* Allow injecting the component, if it cannot be found on the Actor. */ UPROPERTY(EditAnywhere, Category = Configuration, DisplayName = "Allow injecting component", meta = (EditConditionHides, EditCondition = "ComponentSource == EExecuteComponentSource::InjectFromClass && bReuseExistingComponent")) bool bAllowInjectComponent = true; - // Inject component(s) onto the owning Actor + /* Inject component(s) onto the owning Actor. */ UPROPERTY() EExecuteComponentSource ComponentSource = EExecuteComponentSource::Undetermined; }; diff --git a/Source/Flow/Public/Nodes/Actor/FlowNode_NotifyActor.h b/Source/Flow/Public/Nodes/Actor/FlowNode_NotifyActor.h index aa250b4bd..5d56505b0 100644 --- a/Source/Flow/Public/Nodes/Actor/FlowNode_NotifyActor.h +++ b/Source/Flow/Public/Nodes/Actor/FlowNode_NotifyActor.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "GameplayTagContainer.h" @@ -8,7 +7,7 @@ #include "FlowNode_NotifyActor.generated.h" /** - * Finds all Flow Components with matching Identity Tag and calls ReceiveNotify event on these components + * Finds all Flow Components with matching Identity Tag and calls ReceiveNotify event on these components. */ UCLASS(NotBlueprintable, meta = (DisplayName = "Notify Actor", Keywords = "event")) class FLOW_API UFlowNode_NotifyActor : public UFlowNode diff --git a/Source/Flow/Public/Nodes/Actor/FlowNode_OnActorRegistered.h b/Source/Flow/Public/Nodes/Actor/FlowNode_OnActorRegistered.h index b7bf05cd4..a7b7b1a12 100644 --- a/Source/Flow/Public/Nodes/Actor/FlowNode_OnActorRegistered.h +++ b/Source/Flow/Public/Nodes/Actor/FlowNode_OnActorRegistered.h @@ -1,12 +1,11 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Nodes/Actor/FlowNode_ComponentObserver.h" #include "FlowNode_OnActorRegistered.generated.h" /** - * Triggers output when Flow Component with matching Identity Tag appears in the world + * Triggers output when Flow Component with matching Identity Tag appears in the world. */ UCLASS(NotBlueprintable, meta = (DisplayName = "On Actor Registered", Keywords = "bind")) class FLOW_API UFlowNode_OnActorRegistered : public UFlowNode_ComponentObserver diff --git a/Source/Flow/Public/Nodes/Actor/FlowNode_OnActorUnregistered.h b/Source/Flow/Public/Nodes/Actor/FlowNode_OnActorUnregistered.h index 38b9199a2..3d9eab92a 100644 --- a/Source/Flow/Public/Nodes/Actor/FlowNode_OnActorUnregistered.h +++ b/Source/Flow/Public/Nodes/Actor/FlowNode_OnActorUnregistered.h @@ -1,12 +1,11 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Nodes/Actor/FlowNode_ComponentObserver.h" #include "FlowNode_OnActorUnregistered.generated.h" /** - * Triggers output when Flow Component with matching Identity Tag disappears from the world + * Triggers output when Flow Component with matching Identity Tag disappears from the world. */ UCLASS(NotBlueprintable, meta = (DisplayName = "On Actor Unregistered", Keywords = "unbind")) class FLOW_API UFlowNode_OnActorUnregistered : public UFlowNode_ComponentObserver diff --git a/Source/Flow/Public/Nodes/Actor/FlowNode_OnNotifyFromActor.h b/Source/Flow/Public/Nodes/Actor/FlowNode_OnNotifyFromActor.h index 9b9dda87a..4c2483e55 100644 --- a/Source/Flow/Public/Nodes/Actor/FlowNode_OnNotifyFromActor.h +++ b/Source/Flow/Public/Nodes/Actor/FlowNode_OnNotifyFromActor.h @@ -1,12 +1,11 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Nodes/Actor/FlowNode_ComponentObserver.h" #include "FlowNode_OnNotifyFromActor.generated.h" /** - * Triggers output when Flow Component with matching Identity Tag calls NotifyGraph function with matching Notify Tag + * Triggers output when Flow Component with matching Identity Tag calls NotifyGraph function with matching Notify Tag. */ UCLASS(NotBlueprintable, meta = (DisplayName = "On Notify From Actor")) class FLOW_API UFlowNode_OnNotifyFromActor : public UFlowNode_ComponentObserver @@ -17,8 +16,8 @@ class FLOW_API UFlowNode_OnNotifyFromActor : public UFlowNode_ComponentObserver UPROPERTY(EditAnywhere, Category = "Notify") FGameplayTagContainer NotifyTags; - // If true, node will check given Notify Tag is present in the Recently Sent Notify Tags - // This might be helpful in multiplayer, if client-side Flow Node started work after server sent the notify + /* If true, node will check given Notify Tag is present in the Recently Sent Notify Tags. + * This might be helpful in multiplayer, if client-side Flow Node started work after server sent the Notify. */ UPROPERTY(EditAnywhere, Category = "Notify") bool bRetroactive; diff --git a/Source/Flow/Public/Nodes/Actor/FlowNode_PlayLevelSequence.h b/Source/Flow/Public/Nodes/Actor/FlowNode_PlayLevelSequence.h index c878781ff..654e1c3ed 100644 --- a/Source/Flow/Public/Nodes/Actor/FlowNode_PlayLevelSequence.h +++ b/Source/Flow/Public/Nodes/Actor/FlowNode_PlayLevelSequence.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "EngineDefines.h" @@ -43,22 +42,22 @@ class FLOW_API UFlowNode_PlayLevelSequence : public UFlowNode UPROPERTY(EditAnywhere, Category = "Sequence") FLevelSequenceCameraSettings CameraSettings; - // Level Sequence playback can be moved to any place in the world by applying Transform Origin - // Enabling this option will use actor that created Root Flow instance, i.e. World Settings or Player Controller - // https://docs.unrealengine.com/5.0/en-US/creating-level-sequences-with-dynamic-transforms-in-unreal-engine/ + /* Level Sequence playback can be moved to any place in the world by applying Transform Origin. + * Enabling this option will use actor that created Root Flow instance, i.e. World Settings or Player Controller/ + * See https://docs.unrealengine.com/5.0/en-US/creating-level-sequences-with-dynamic-transforms-in-unreal-engine/ */ UPROPERTY(EditAnywhere, Category = "Sequence") bool bUseGraphOwnerAsTransformOrigin; - // If true, playback of this level sequence on the server will be synchronized across other clients + /* If true, playback of this level sequence on the server will be synchronized across other clients. */ UPROPERTY(EditAnywhere, Category = "Sequence") bool bReplicates; - // Always relevant for network (overrides bOnlyRelevantToOwner) + /* Always relevant for network (overrides bOnlyRelevantToOwner). */ UPROPERTY(EditAnywhere, Category = "Sequence") bool bAlwaysRelevant; - // If True, Play Rate will by multiplied by Custom Time Dilation - // Enabling this option will use Custom Time Dilation from actor that created Root Flow instance, i.e. World Settings or Player Controller + /* If True, Play Rate will by multiplied by Custom Time Dilation. + * Enabling this option will use Custom Time Dilation from actor that created Root Flow instance, i.e. World Settings or Player Controller. */ UPROPERTY(EditAnywhere, Category = "Sequence") bool bApplyOwnerTimeDilation; @@ -69,7 +68,7 @@ class FLOW_API UFlowNode_PlayLevelSequence : public UFlowNode UPROPERTY() TObjectPtr SequencePlayer; - // Play Rate set by the user in PlaybackSettings + /* Play Rate set by the user in PlaybackSettings. */ float CachedPlayRate; UPROPERTY(SaveGame) diff --git a/Source/Flow/Public/Nodes/Developer/FlowNode_Log.h b/Source/Flow/Public/Nodes/Developer/FlowNode_Log.h index 3c7825607..3c04ecceb 100644 --- a/Source/Flow/Public/Nodes/Developer/FlowNode_Log.h +++ b/Source/Flow/Public/Nodes/Developer/FlowNode_Log.h @@ -1,11 +1,12 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Nodes/Graph/FlowNode_DefineProperties.h" #include "FlowNode_Log.generated.h" -// Variant of ELogVerbosity +/** + * Variant of ELogVerbosity. + */ UENUM(BlueprintType) enum class EFlowLogVerbosity : uint8 { @@ -27,8 +28,8 @@ class FLOW_API UFlowNode_Log : public UFlowNode_DefineProperties GENERATED_UCLASS_BODY() private: - // The message to write to the log - // (if the Message input pin is not connected to another source) + /* The message to write to the log. + * If the Message input pin is not connected to another source. */ UPROPERTY(EditAnywhere, Category = "Flow", meta = (DefaultForInputFlowPin, FlowPinType = String)) FString Message; diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 5080bff89..82b675481 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "EdGraph/EdGraphNode.h" @@ -16,7 +15,9 @@ #include "FlowNode.generated.h" -// Entry in MapDataPinNameToPropertySource for how to source a non-trivial pin mapping in TryGatherPropertyOwnersAndPopulateResult +/** + * Entry in MapDataPinNameToPropertySource for how to source a non-trivial pin mapping in TryGatherPropertyOwnersAndPopulateResult. + */ USTRUCT() struct FFlowPinPropertySource { @@ -80,7 +81,7 @@ class FLOW_API UFlowNode : public UFlowNodeBase // -- #endif - // Inherits Guid after graph node + /* Inherits Guid after graph node. */ UPROPERTY() FGuid NodeGuid; @@ -91,9 +92,9 @@ class FLOW_API UFlowNode : public UFlowNodeBase UFUNCTION(BlueprintPure, Category = "FlowNode") const FGuid& GetGuid() const { return NodeGuid; } - // Returns a random seed suitable for this flow node, - // by default based on the node Guid, - // but may be overridden in subclasses to supply some other value. + /* Returns a random seed suitable for this flow node, + * by default based on the node Guid, + * but may be overridden in subclasses to supply some other value. */ virtual int32 GetRandomSeed() const override { return GetTypeHash(NodeGuid); } virtual const UFlowNode* GetParentNode() const override @@ -108,8 +109,8 @@ class FLOW_API UFlowNode : public UFlowNodeBase UPROPERTY(EditDefaultsOnly, Category = "FlowNode") TArray AllowedSignalModes; - // If enabled, signal will pass through node without calling ExecuteInput() - // Designed to handle patching + /* If enabled, signal will pass through node without calling ExecuteInput(). + * Designed to handle patching already released games. */ UPROPERTY() EFlowSignalMode SignalMode; @@ -121,11 +122,11 @@ class FLOW_API UFlowNode : public UFlowNodeBase static FFlowPin DefaultOutputPin; protected: - // Class-specific and user-added inputs + /* Class-specific and user-added inputs. */ UPROPERTY(EditDefaultsOnly, Category = "FlowNode") TArray InputPins; - // Class-specific and user-added outputs + /* Class-specific and user-added outputs. */ UPROPERTY(EditDefaultsOnly, Category = "FlowNode") TArray OutputPins; @@ -133,13 +134,13 @@ class FLOW_API UFlowNode : public UFlowNodeBase void AddOutputPins(const TArray& Pins); #if WITH_EDITOR - // Utility function to rebuild a pin array in editor (either InputPins or OutputPins, passed as InOutPins) - // returns true if the InOutPins array was rebuilt + /* Utility function to rebuild a pin array in editor (either InputPins or OutputPins, passed as InOutPins) + * returns true if the InOutPins array was rebuilt. */ bool RebuildPinArray(const TArray& NewPinNames, TArray& InOutPins, const FFlowPin& DefaultPin); bool RebuildPinArray(const TArray& NewPins, TArray& InOutPins, const FFlowPin& DefaultPin); #endif // WITH_EDITOR; - // always use default range for nodes with user-created outputs i.e. Execution Sequence + /* Always use default range for nodes with user-created outputs i.e. Execution Sequence. */ void SetNumberedInputPins(const uint8 FirstNumber = 0, const uint8 LastNumber = 1); void SetNumberedOutputPins(const uint8 FirstNumber = 0, const uint8 LastNumber = 1); @@ -184,7 +185,7 @@ class FLOW_API UFlowNode : public UFlowNodeBase // Connections to other nodes protected: - // Map input/outputs to the connected node and input pin + /* Map input/outputs to the connected node and input pin. */ UPROPERTY() TMap Connections; @@ -221,15 +222,15 @@ class FLOW_API UFlowNode : public UFlowNodeBase static void RecursiveFindNodesByClass(UFlowNode* Node, const TSubclassOf Class, uint8 Depth, TArray& OutNodes); protected: - // Slow and fast lookup functions, based on whether we are proactively caching the connections for quick lookup - // in the Connections array (by PinCategory) + /* Slow and fast lookup functions, based on whether we are proactively caching the connections for quick lookup + * in the Connections array (by PinCategory). */ bool FindConnectedNodeForPinFast(const FName& FlowPinName, FGuid* FoundGuid = nullptr, FName* OutConnectedPinName = nullptr) const; bool FindConnectedNodeForPinSlow(const FName& FlowPinName, FGuid* FoundGuid = nullptr, FName* OutConnectedPinName = nullptr) const; - // Return all connections to a Pin this Node knows about. - // Connections are only stored on one of the Nodes they connect depending on pin type. - // As such, this function may not return anything even if the Node is connected to the Pin. - // Use UFlowAsset::GetAllPinsConnectedToPin() to do a guaranteed find of all Connections. + /* Return all connections to a Pin this Node knows about. + * Connections are only stored on one of the Nodes they connect depending on pin type. + * As such, this function may not return anything even if the Node is connected to the Pin. + * Use UFlowAsset::GetAllPinsConnectedToPin() to do a guaranteed find of all Connections. */ TArray GetKnownConnectionsToPin(const FConnectedPin& Pin) const; ////////////////////////////////////////////////////////////////////////// @@ -244,11 +245,11 @@ class FLOW_API UFlowNode : public UFlowNodeBase UPROPERTY(VisibleDefaultsOnly, AdvancedDisplay, Category = "FlowNode", meta = (GetByRef)) TArray AutoOutputDataPins; -#endif // WITH_EDITORONLY_DATA +#endif - // Map for PinName to Property supplier for non-trivial data pin property lookups - // (non-trivial means a different pin name from its property source, or a non-zero property owner object index) - // see TryGatherPropertyOwnersAndPopulateResult() + /* Map for PinName to Property supplier for non-trivial data pin property lookups. + * Non-trivial means a different pin name from its property source, or a non-zero property owner object index. + * See TryGatherPropertyOwnersAndPopulateResult(). */ UPROPERTY() TMap MapDataPinNameToPropertySource; @@ -260,15 +261,15 @@ class FLOW_API UFlowNode : public UFlowNodeBase TArray& GetMutableAutoInputDataPins() { return AutoInputDataPins; } TArray& GetMutableAutoOutputDataPins() { return AutoOutputDataPins; } -#endif // WITH_EDITOR +#endif // IFlowDataPinValueSupplierInterface public: virtual FFlowDataPinResult TrySupplyDataPin(FName PinName) const override; - // Advanced helper for TrySupplyDataPin, which can be overridden in subclasses to provide alternate sourcing for properties. - // If returns true, either OutFoundProperty or OutFoundInstancedStruct is expected to carry the property value. - // (this function is used for cases like DefineProperties, Start, and blackboard lookup nodes) + /* Advanced helper for TrySupplyDataPin, which can be overridden in subclasses to provide alternate sourcing for properties. + * If returns true, either OutFoundProperty or OutFoundInstancedStruct is expected to carry the property value. + * This function is used for cases like DefineProperties, Start, and blackboard lookup nodes. */ virtual bool TryFindPropertyByPinName( const UObject& PropertyOwnerObject, const FName& PinName, @@ -276,7 +277,7 @@ class FLOW_API UFlowNode : public UFlowNodeBase TInstancedStruct& OutFoundInstancedStruct) const; protected: - // Helper for TryGetFlowDataPinSupplierDatasForPinName() + /* Helper for TryGetFlowDataPinSupplierDatasForPinName(). */ void TryAddSupplierDataToArray(FFlowPinValueSupplierData& InOutSupplierData, TFlowPinValueSupplierDataArray& InOutPinValueSupplierDatas) const; // Static implementation of the default TryFindPropertyByPinName (which subclasses can incorporate into overrides) @@ -287,9 +288,9 @@ class FLOW_API UFlowNode : public UFlowNodeBase TInstancedStruct& OutFoundInstancedStruct); public: - // Advanced helper for TrySupplyDataPin, which can be overridden in subclasses to provide additional or replacement object(s) - // for sourcing the properties for the given pin name. These objects will have PopulateResult called on them. - // (this function is used for cases like ExecuteComponent) + /* Advanced helper for TrySupplyDataPin, which can be overridden in subclasses to provide additional or replacement object(s) + * for sourcing the properties for the given pin name. These objects will have PopulateResult called on them. + * This function is used for cases like ExecuteComponent. */ virtual void GatherPotentialPropertyOwnersForDataPins(TArray& InOutOwners) const; bool TryGatherPropertyOwnersAndPopulateResult( @@ -358,7 +359,7 @@ class FLOW_API UFlowNode : public UFlowNodeBase void TriggerFlush(); protected: - // Trigger execution of input pin + /* Trigger execution of input pin. */ void TriggerInput(const FName& PinName, const EFlowPinActivationType ActivationType = EFlowPinActivationType::Default); protected: @@ -406,7 +407,7 @@ class FLOW_API UFlowNode : public UFlowNodeBase TArray GetPinRecords(const FName& PinName, const EEdGraphPinDirection PinDirection) const; #endif - // Information displayed while node is working - displayed over node as NodeInfoPopup + /* Information displayed while node is working - displayed over node as NodeInfoPopup. */ FString GetStatusStringForNodeAndAddOns() const; #if WITH_EDITOR diff --git a/Source/Flow/Public/Nodes/FlowNodeAddOnBlueprint.h b/Source/Flow/Public/Nodes/FlowNodeAddOnBlueprint.h index 6751828d8..9f4cf0f1b 100644 --- a/Source/Flow/Public/Nodes/FlowNodeAddOnBlueprint.h +++ b/Source/Flow/Public/Nodes/FlowNodeAddOnBlueprint.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "CoreMinimal.h" diff --git a/Source/Flow/Public/Nodes/FlowNodeBase.h b/Source/Flow/Public/Nodes/FlowNodeBase.h index 1512af721..d121afc5f 100644 --- a/Source/Flow/Public/Nodes/FlowNodeBase.h +++ b/Source/Flow/Public/Nodes/FlowNodeBase.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Templates/SubclassOf.h" @@ -46,15 +45,15 @@ struct FLOW_API FFlowNodeOverlayIcon { } - /** Name of the brush to use for the icon */ + /* Name of the brush to use for the icon */ UPROPERTY() FName BrushName = NAME_None; - /** Offset from the top-left corner of the node (position X moves right, positive Y moves down) */ + /* Offset from the top-left corner of the node (position X moves right, positive Y moves down) */ UPROPERTY() FVector2D Offset = FVector2D::ZeroVector; - /** Name of the StyleSet that contains your brush. If left empty Flow will first search the default Flow StyleSet and then the default Unreal StyleSet */ + /* Name of the StyleSet that contains your brush. If left empty Flow will first search the default Flow StyleSet and then the default Unreal StyleSet */ UPROPERTY() FName StyleSetName = NAME_None; }; @@ -62,7 +61,9 @@ struct FLOW_API FFlowNodeOverlayIcon typedef TFunction FConstFlowNodeAddOnFunction; typedef TFunction FFlowNodeAddOnFunction; -// Supplier + PinName (in that supplier) for a Flow Data Pin value +/** + * Supplier + PinName (in that supplier) for a Flow Data Pin value. + */ struct FFlowPinValueSupplierData { FName SupplierPinName; @@ -94,9 +95,6 @@ class FLOW_API UFlowNodeBase virtual UWorld* GetWorld() const override; // -- - // Dispatcher for ExecuteInput to ensure the AddOns get their ExecuteInput calls even if the node/addon - void ExecuteInputForSelfAndAddOns(const FName& PinName); - // IFlowCoreExecutableInterface virtual void InitializeInstance() override; virtual void DeinitializeInstance() override; @@ -111,32 +109,35 @@ class FLOW_API UFlowNodeBase virtual void Cleanup() override; // -- - // Finish execution of node, it will call Cleanup + /* Dispatcher for ExecuteInput to ensure the AddOns get their ExecuteInput calls even if the node/addon. */ + void ExecuteInputForSelfAndAddOns(const FName& PinName); + + /* Finish execution of node, it will call Cleanup. */ UFUNCTION(BlueprintCallable, Category = "FlowNode") virtual void Finish() PURE_VIRTUAL(Finish) - // Simply trigger the first Output Pin, convenient to use if node has only one output + /* Simply trigger the first Output Pin, convenient to use if node has only one output. */ UFUNCTION(BlueprintCallable, Category = "FlowNode") virtual void TriggerFirstOutput(const bool bFinish) PURE_VIRTUAL(TriggerFirstOutput) - // Cause a specific output to be triggered (by PinName) + /* Cause a specific output to be triggered (by PinName). */ UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (HidePin = "ActivationType")) virtual void TriggerOutput(const FName PinName, const bool bFinish = false, const EFlowPinActivationType ActivationType = EFlowPinActivationType::Default) PURE_VIRTUAL(TriggerOutput) - // TriggerOutput convenience aliases + /* TriggerOutput convenience aliases. */ void TriggerOutput(const FString& PinName, const bool bFinish = false); void TriggerOutput(const FText& PinName, const bool bFinish = false); void TriggerOutput(const TCHAR* PinName, const bool bFinish = false); - // Cause a specific output to be triggered (by PinHandle) + /* Cause a specific output to be triggered (by PinHandle). */ UFUNCTION(BlueprintCallable, Category = "FlowNode", meta = (HidePin = "ActivationType")) virtual void TriggerOutputPin(const FFlowOutputPinHandle Pin, const bool bFinish = false, const EFlowPinActivationType ActivationType = EFlowPinActivationType::Default); - // Returns a random seed suitable for this flow node base + /* Returns a random seed suitable for this flow node base. */ UFUNCTION(BlueprintPure, Category = "FlowNode") virtual int32 GetRandomSeed() const PURE_VIRTUAL(GetRandomSeed, return 0;); - // Returns the owning top-level Flow node. + /* Returns the owning top-level Flow node. */ virtual const UFlowNode* GetParentNode() const PURE_VIRTUAL(GetParentNode, return nullptr;); ////////////////////////////////////////////////////////////////////////// @@ -154,7 +155,7 @@ class FLOW_API UFlowNodeBase virtual TArray GetContextInputs() const override; virtual TArray GetContextOutputs() const override; // -- -#endif // WITH_EDITOR +#endif ////////////////////////////////////////////////////////////////////////// // Owners @@ -169,12 +170,12 @@ class FLOW_API UFlowNodeBase UFUNCTION(BlueprintPure, Category = "FlowNode") UFlowSubsystem* GetFlowSubsystem() const; - // Gets the Owning Actor for this Node's RootFlow - // (if the immediate parent is an UActorComponent, it will get that Component's actor) + /* Gets the Owning Actor for this Node's RootFlow. + * If the immediate parent is an UActorComponent, it will get that Component's actor. */ UFUNCTION(BlueprintCallable, Category = "FlowNode") AActor* TryGetRootFlowActorOwner() const; - // Gets the Owning Object for this Node's RootFlow + /* Gets the Owning Object for this Node's RootFlow. */ UFUNCTION(BlueprintCallable, Category = "FlowNode") UObject* TryGetRootFlowObjectOwner() const; @@ -184,18 +185,18 @@ class FLOW_API UFlowNodeBase // AddOn support protected: - // Flow Node AddOn attachments + /* Flow Node AddOn attachments. */ UPROPERTY(BlueprintReadOnly, Instanced, Category = "FlowNode") TArray> AddOns; protected: - // FlowNodes and AddOns may determine which AddOns are eligible to be their children - // - AddOnTemplate - the template of the FlowNodeAddOn that is being considered to be added as a child - // - AdditionalAddOnsToAssumeAreChildren - other AddOns to assume that are already child AddOns for the purposes of checking is AddOnTemplate is allowed. - // This list will be populated with the 'other' AddOns in a multi-paste operation in the editor, - // because some paste-targets can only accept a certain mix of addons, so we must know the rest of the set being pasted - // to make the correct decision about whether to allow AddOnTemplate to be added. - // https://forums.unrealengine.com/t/default-parameters-with-tarrays/330225 for details on AutoCreateRefTerm + /* FlowNodes and AddOns may determine which AddOns are eligible to be their children. + * - AddOnTemplate - the template of the FlowNodeAddOn that is being considered to be added as a child. + * - AdditionalAddOnsToAssumeAreChildren - other AddOns to assume that are already child AddOns for the purposes of checking is AddOnTemplate is allowed. + * This list will be populated with the 'other' AddOns in a multi-paste operation in the editor, + * because some paste-targets can only accept a certain mix of addons, so we must know the rest of the set being pasted + * to make the correct decision about whether to allow AddOnTemplate to be added. + * See https://forums.unrealengine.com/t/default-parameters-with-tarrays/330225 for details on AutoCreateRefTerm. */ UFUNCTION(BlueprintNativeEvent, BlueprintPure, Category = "FlowNode", meta = (AutoCreateRefTerm = AdditionalAddOnsToAssumeAreChildren)) EFlowAddOnAcceptResult AcceptFlowNodeAddOnChild(const UFlowNodeAddOn* AddOnTemplate, const TArray& AdditionalAddOnsToAssumeAreChildren) const; @@ -205,7 +206,7 @@ class FLOW_API UFlowNodeBase #if WITH_EDITOR virtual TArray& GetFlowNodeAddOnChildrenByEditor() { return MutableView(AddOns); } EFlowAddOnAcceptResult CheckAcceptFlowNodeAddOnChild(const UFlowNodeAddOn* AddOnTemplate, const TArray& AdditionalAddOnsToAssumeAreChildren) const; -#endif // WITH_EDITOR +#endif bool IsClassOrImplementsInterface(const UClass& InterfaceOrClass) const { @@ -220,7 +221,10 @@ class FLOW_API UFlowNodeBase return IsClassOrImplementsInterface(*TInterfaceOrClass::StaticClass()); } - // Call a function for all of this object's AddOns (recursively iterating AddOns inside AddOn) + /** + * Call a function for all of this object's AddOns (recursively iterating AddOns inside AddOn). + */ + EFlowForEachAddOnFunctionReturnValue ForEachAddOnConst(const FConstFlowNodeAddOnFunction& Function, EFlowForEachAddOnChildRule AddOnChildRule = EFlowForEachAddOnChildRule::AllChildren) const; EFlowForEachAddOnFunctionReturnValue ForEachAddOn(const FFlowNodeAddOnFunction& Function, EFlowForEachAddOnChildRule AddOnChildRule = EFlowForEachAddOnChildRule::AllChildren) const; @@ -277,27 +281,27 @@ class FLOW_API UFlowNodeBase FFlowDataPinResult TryResolveDataPin(FName PinName) const; public: - // Generic single-value resolve & extractor + /* Generic single-value resolve & extractor. */ template EFlowDataPinResolveResult TryResolveDataPinValue(const FName& PinName, typename TFlowPinType::ValueType& OutValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue) const; - // Generic array-value resolve & extractor + /* Generic array-value resolve & extractor. */ template EFlowDataPinResolveResult TryResolveDataPinValues(const FName& PinName, TArray& OutValues) const; - // Special-case single-value resolve & extractor for native enums + /* Special-case single-value resolve & extractor for native enums. */ template requires std::is_enum_v EFlowDataPinResolveResult TryResolveDataPinValue(const FName& PinName, TEnumType& OutValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue) const; - // Special-case array-value resolve & extractor for native enums + /* Special-case array-value resolve & extractor for native enums. */ template requires std::is_enum_v EFlowDataPinResolveResult TryResolveDataPinValues(const FName& PinName, TArray& OutValues) const; - // Special-case single-value resolve & extractor for enums (as FName values) + /* Special-case single-value resolve & extractor for enums (as FName values). */ template EFlowDataPinResolveResult TryResolveDataPinValue(const FName& PinName, FName& OutEnumValue, UEnum*& OutEnumClass, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue) const; - // Special-case array-value resolve & extractor for enums (as FName values) + /* Special-case array-value resolve & extractor for enums (as FName values). */ template EFlowDataPinResolveResult TryResolveDataPinValues(const FName& PinName, TArray& OutEnumValues, UEnum*& OutEnumClass) const; @@ -370,7 +374,7 @@ class FLOW_API UFlowNodeBase UPROPERTY(EditDefaultsOnly, Category = "FlowNode") bool bNodeDeprecated; - // If this node is deprecated, it might be replaced by another node + /* If this node is deprecated, it might be replaced by another node. */ UPROPERTY(EditDefaultsOnly, Category = "FlowNode") TSubclassOf ReplacedBy; @@ -387,10 +391,10 @@ class FLOW_API UFlowNodeBase void SetCanDelete(const bool CanDelete); - // Set up UFlowNodeBase when being opened for edit in the editor + /* Set up UFlowNodeBase when being opened for edit in the editor. */ virtual void SetupForEditing(UEdGraphNode& EdGraphNode); - // Opportunity to update node's data before UFlowGraphNode would call ReconstructNode() + /* Opportunity to update node's data before UFlowGraphNode would call ReconstructNode(). */ virtual void FixNode(UEdGraphNode* NewGraphNode); // UObject @@ -399,16 +403,16 @@ class FLOW_API UFlowNodeBase void RequestReconstruction() const { (void) OnReconstructionRequested.ExecuteIfBound(); }; - // used when import graph from another asset + /* Used when import graph from another asset. */ virtual void PostImport() {} #endif public: - // Called by owning FlowNode to add to its Status String. + /* Called by owning FlowNode to add to its Status String. */ virtual FString GetStatusString() const; protected: - // Information displayed while node is working - displayed over node as NodeInfoPopup + /* Information displayed while node is working - displayed over node as NodeInfoPopup. */ UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Get Status String")) FString K2_GetStatusString() const; @@ -420,28 +424,28 @@ class FLOW_API UFlowNodeBase UPROPERTY(EditDefaultsOnly, Category = "FlowNode", meta = (Categories = "Flow.NodeStyle")) FGameplayTag NodeDisplayStyle; - // Deprecated NodeStyle, replaced by NodeDisplayStyle + /* Deprecated NodeStyle, replaced by NodeDisplayStyle. */ UPROPERTY(meta = (DeprecatedProperty, DeprecationMessage = "Use the NodeDisplayStyle instead.")) EFlowNodeStyle NodeStyle; - // Set Node Style to custom to use your own color for this node (if using Flow.NodeStyle.Custom) + /* Set Node Style to custom to use your own color for this node (if using Flow.NodeStyle.Custom). */ UPROPERTY(EditDefaultsOnly, Category = "FlowNode", DisplayName = "Custom Node Color") FLinearColor NodeColor; - // Optional developer-facing text to explain the configuration of this node when viewed in the editor - // may be authored or set procedurally via UpdateNodeConfigText and SetNodeConfigText + /* Optional developer-facing text to explain the configuration of this node when viewed in the editor. + * May be authored or set procedurally via UpdateNodeConfigText and SetNodeConfigText. */ UPROPERTY(EditDefaultsOnly, AdvancedDisplay, Category = "FlowNode") FText DevNodeConfigText; #endif // WITH_EDITORONLY_DATA #if WITH_EDITOR public: - // WARNING! Call UFlowGraphSettings::GetNodeCategoryForNode() instead! + /* WARNING! Call UFlowGraphSettings::GetNodeCategoryForNode() instead! */ virtual FString GetNodeCategory() const; const FGameplayTag& GetNodeDisplayStyle() const { return NodeDisplayStyle; } - // This method allows to have different for every node instance, i.e. Red if node represents enemy, Green if node represents a friend + /* This method allows to have different for every node instance, i.e. Red if node represents enemy, Green if node represents a friend. */ virtual bool GetDynamicTitleColor(FLinearColor& OutColor) const; virtual FText GetNodeTitle() const { return K2_GetNodeTitle(); } @@ -485,13 +489,13 @@ class FLOW_API UFlowNodeBase virtual FText GetNodeConfigText() const; protected: - // Set the editor-only Config Text - // (for displaying config info on the Node in the flow graph, ignored in non-editor builds) + /* Set the editor-only Config Text. + * For displaying config info on the Node in the flow graph, ignored in non-editor builds. */ UFUNCTION(BlueprintCallable, Category = "FlowNode") void SetNodeConfigText(const FText& NodeConfigText); - // Called whenever a property change event occurs on this flow node object, - // giving the implementor a chance to update their NodeConfigText (via SetNodeConfigText) + /* Called whenever a property change event occurs on this flow node object, + * giving the implementor a chance to update their NodeConfigText (via SetNodeConfigText). */ UFUNCTION(BlueprintNativeEvent, Category = "FlowNode") void UpdateNodeConfigText(); @@ -501,19 +505,19 @@ class FLOW_API UFlowNodeBase #if WITH_EDITORONLY_DATA protected: FFlowMessageLog ValidationLog; -#endif // WITH_EDITORONLY_DATA +#endif #if WITH_EDITOR public: - // Short summary of node's content - displayed over node as NodeInfoPopup + /* Short summary of node's content - displayed over node as NodeInfoPopup. */ virtual FString GetNodeDescription() const; - // Complex summary of node's content including its addons + /* Complex summary of node's content including its addons. */ FString GetAddOnDescriptions() const; #endif protected: - // Short summary of node's content - displayed over node as NodeInfoPopup + /* Short summary of node's content - displayed over node as NodeInfoPopup. */ UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode", meta = (DisplayName = "Get Node Description")) FString K2_GetNodeDescription() const; @@ -539,25 +543,26 @@ class FLOW_API UFlowNodeBase virtual EDataValidationResult ValidateNode(); #endif - // Optional validation override for Blueprints + /* Optional validation override for Blueprints. */ UFUNCTION(BlueprintImplementableEvent, Category = "FlowNode|Validation", meta = (DisplayName = "Validate Node", DevelopmentOnly)) EDataValidationResult K2_ValidateNode(); - // Log validation error (editor-only) + /* Log validation error (editor-only). */ UFUNCTION(BlueprintCallable, Category = "FlowNode|Validation", meta = (DevelopmentOnly)) void LogValidationError(const FString& Message); - // Log validation warning (editor-only) + /* Log validation warning (editor-only). */ UFUNCTION(BlueprintCallable, Category = "FlowNode|Validation", meta = (DevelopmentOnly)) void LogValidationWarning(const FString& Message); - // Log validation note (editor-only) + /* Log validation note (editor-only). */ UFUNCTION(BlueprintCallable, Category = "FlowNode|Validation", meta = (DevelopmentOnly)) void LogValidationNote(const FString& Message); - // -- }; -// Templates & inline implementations: +/** + * Templates & inline implementations + */ template EFlowDataPinResolveResult UFlowNodeBase::TryResolveDataPinValue(const FName& PinName, typename TFlowPinType::ValueType& OutValue, EFlowSingleFromArray SingleFromArray /*= EFlowSingleFromArray::LastValue*/) const diff --git a/Source/Flow/Public/Nodes/FlowNodeBlueprint.h b/Source/Flow/Public/Nodes/FlowNodeBlueprint.h index 48cbba250..89652b451 100644 --- a/Source/Flow/Public/Nodes/FlowNodeBlueprint.h +++ b/Source/Flow/Public/Nodes/FlowNodeBlueprint.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Engine/Blueprint.h" diff --git a/Source/Flow/Public/Nodes/FlowPin.h b/Source/Flow/Public/Nodes/FlowPin.h index 752ae6a5d..3d5f91edb 100644 --- a/Source/Flow/Public/Nodes/FlowPin.h +++ b/Source/Flow/Public/Nodes/FlowPin.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Types/FlowPinEnums.h" @@ -23,22 +22,22 @@ struct FLOW_API FFlowPin { GENERATED_BODY() - // A logical name, used during execution of pin + /* A logical name, used during execution of pin. */ UPROPERTY(EditDefaultsOnly, Category = FlowPin) FName PinName; - // An optional Display Name, you can use it to override PinName without the need to update graph connections + /* An optional Display Name, you can use it to override PinName without the need to update graph connections. */ UPROPERTY(EditDefaultsOnly, Category = FlowPin) FText PinFriendlyName; UPROPERTY(EditDefaultsOnly, Category = FlowPin) FString PinToolTip; - // Deprecated PinType, use PinTypeName instead (all standard names are defined in FFlowPinTypeNamesStandard) + /* Deprecated PinType, use PinTypeName instead (all standard names are defined in FFlowPinTypeNamesStandard). */ UPROPERTY(Meta = (DeprecatedProperty, DeprecationMessage = "Use PinTypeName instead")) EFlowPinType PinType = EFlowPinType::Invalid; - // Only supporting None (Single) or Array for now(tm) for data pins via EFlowMultiType + /* Only supporting None (Single) or Array for now(tm) for data pins via EFlowMultiType. */ UPROPERTY() EPinContainerType ContainerType = EPinContainerType::None; @@ -46,8 +45,8 @@ struct FLOW_API FFlowPin UPROPERTY() FFlowPinTypeName PinTypeName = FFlowPinTypeName(FFlowPinTypeNamesStandard::PinTypeNameExec); - // Sub-category object - // (used to identify the struct or class type for some PinCategories) + /* Sub-category object + * Used to identify the struct or class type for some PinCategories. */ UPROPERTY() TWeakObjectPtr PinSubCategoryObject; @@ -206,44 +205,48 @@ struct FLOW_API FFlowPin FORCEINLINE bool IsDataPin() const { return !IsExecPin(); } // -- - // Metadata keys for properties that bind and auto-generate Data Pins: - - // SourceForOutputFlowPin - // May be used on a non-FFlowDataPinProperty within a UFlowNode to bind the - // output data pin to use the property as its source. - // - // If a string value is given, it is interpreted as the Data Pin's name, - // otherwise, the property's DisplayName (or lacking that, its authored name) - // will be assumed to also be the Pin's name. + // + + /** + * Metadata keys for properties that bind and auto-generate Data Pins. + */ + + /* SourceForOutputFlowPin + * May be used on a non-FFlowDataPinProperty within a UFlowNode to bind the + * output data pin to use the property as its source. + * + * If a string value is given, it is interpreted as the Data Pin's name, + * otherwise, the property's DisplayName (or lacking that, its authored name) + * will be assumed to also be the Pin's name. */ static const FName MetadataKey_SourceForOutputFlowPin; - // DefaultForInputFlowPin - // May be used on a non-FFlowDataPinProperty within a UFlowNode to bind the - // input data pin to use the property as its default value. - // - // If the input pin IS NOT connected to another node, then the bound property - // value will be supplied as a default. - // - // If the input pin IS connected to another node, then the connected node's supplied - // value will be used instead of the default from the bound property. - // - // If a string value is given, it is interpreted as the Data Pin's name, - // otherwise, the property's DisplayName (or lacking that, its authored name) - // will be assumed to also be the Pin's name. + /* DefaultForInputFlowPin + * May be used on a non-FFlowDataPinProperty within a UFlowNode to bind the + * Input data pin to use the property as its default value. + * + * If the input pin IS NOT connected to another node, then the bound property + * value will be supplied as a default. + * + * If the input pin IS connected to another node, then the connected node's supplied + * value will be used instead of the default from the bound property. + * + * If a string value is given, it is interpreted as the Data Pin's name, + * otherwise, the property's DisplayName (or lacking that, its authored name) + * will be assumed to also be the Pin's name. */ static const FName MetadataKey_DefaultForInputFlowPin; - // FlowPinType - // May be used on either a property (within a UFlowNode) or a USTRUCT declaration for - // a FFlowDataPinProperty subclass. - // - // If used on a property, then it indicates that a data pin of the given type should be auto-generated, - // and bound to the property. May be used in conjunction with SourceForOutputFlowPin or DefaultForInputFlowPin - // (but not both) to determine how the property binding is to be applied (as input default or output supply source) - // - // If used on a FFlowDataPinProperty struct declaration, then it defines the type of pin - // that should be auto-generated when the struct is used as a property in a UFlowNode. - // - // The string value of the metadata should exactly match a value in EFlowPinType + /* FlowPinType + * May be used on either a property (within a UFlowNode) or a USTRUCT declaration for + * a FFlowDataPinProperty subclass. + * + * If used on a property, then it indicates that a data pin of the given type should be auto-generated, + * and bound to the property. May be used in conjunction with SourceForOutputFlowPin or DefaultForInputFlowPin + * (but not both) to determine how the property binding is to be applied (as input default or output supply source) + * + * If used on a FFlowDataPinProperty struct declaration, then it defines the type of pin + * that should be auto-generated when the struct is used as a property in a UFlowNode. + * + * The string value of the metadata should exactly match a value in EFlowPinType. */ static const FName MetadataKey_FlowPinType; // -- @@ -257,7 +260,7 @@ struct FLOW_API FFlowPinHandle { GENERATED_BODY() - // Update SFlowPinHandleBase code if this property name would be ever changed + /* Update SFlowPinHandleBase code if this property name would be ever changed. */ UPROPERTY() FName PinName; @@ -287,7 +290,9 @@ struct FLOW_API FFlowOutputPinHandle : public FFlowPinHandle } }; -// Processing Flow Nodes creates map of connected pins +/** + * Processing Flow Nodes creates map of connected pins. + */ USTRUCT() struct FLOW_API FConnectedPin { @@ -335,7 +340,9 @@ enum class EFlowPinActivationType : uint8 PassThrough }; -// Every time pin is activated, we record it and display this data while user hovers mouse over pin +/** + * Every time pin is activated, we record it and display this data while user hovers mouse over pin. + */ #if !UE_BUILD_SHIPPING struct FLOW_API FPinRecord { diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_BlueprintDataPinSupplierBase.h b/Source/Flow/Public/Nodes/Graph/FlowNode_BlueprintDataPinSupplierBase.h index 162b235e8..d475041ed 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_BlueprintDataPinSupplierBase.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_BlueprintDataPinSupplierBase.h @@ -1,14 +1,11 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Nodes/FlowNode.h" - #include "FlowNode_BlueprintDataPinSupplierBase.generated.h" /** - * FlowNode to give an event to blueprint for supplying data pin values on-demand - * (as there is no longer a blueprint override to TrySupplyDataPin) + * FlowNode to give an event to blueprint for supplying data pin values on-demand. */ UCLASS(Abstract, Blueprintable, meta = (DisplayName = "Blueprint Data-Pin Supplier base")) class FLOW_API UFlowNode_BlueprintDataPinSupplierBase : public UFlowNode @@ -16,18 +13,17 @@ class FLOW_API UFlowNode_BlueprintDataPinSupplierBase : public UFlowNode GENERATED_UCLASS_BODY() public: - // IFlowDataPinValueSupplierInterface virtual FFlowDataPinResult TrySupplyDataPin(FName PinName) const override; // -- - // Blueprint signature for TrySupplyDataPin override + /* Blueprint signature for TrySupplyDataPin override. */ UFUNCTION(BlueprintNativeEvent, Category = DataPins, DisplayName = "Try Supply DataPin") FFlowDataPinResult BP_TrySupplyDataPin(FName PinName) const; - // Blueprint access for the 'standard' implementation of TrySupplyDataPin - // (for cases where they want to override some pins, but maybe not all, they can have the BP - // override call this version to handle any cases it doesn't want to handle) + /* Blueprint access for the 'standard' implementation of TrySupplyDataPin + * For cases where they want to override some pins, but maybe not all, they can have the BP + * override call this version to handle any cases it doesn't want to handle. */ UFUNCTION(BlueprintPure, Category = DataPins, DisplayName = "Try Supply DataPin (standard implementation)") FFlowDataPinResult BP_Super_TrySupplyDataPin(FName PinName) const { return Super::TrySupplyDataPin(PinName); } }; diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_Checkpoint.h b/Source/Flow/Public/Nodes/Graph/FlowNode_Checkpoint.h index 8916fe53f..bcf4b69de 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_Checkpoint.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_Checkpoint.h @@ -1,13 +1,12 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Nodes/FlowNode.h" #include "FlowNode_Checkpoint.generated.h" /** - * Save the state of the game to the save file - * It's recommended to replace this with game-specific variant and this node to UFlowGraphSettings::HiddenNodes + * Save the state of the game to the save file. + * It's recommended to replace this with game-specific variant and this node to UFlowGraphSettings::HiddenNodes. */ UCLASS(NotBlueprintable, Config = Game, defaultconfig, meta = (DisplayName = "Checkpoint", Keywords = "autosave, save")) class FLOW_API UFlowNode_Checkpoint final : public UFlowNode @@ -15,7 +14,7 @@ class FLOW_API UFlowNode_Checkpoint final : public UFlowNode GENERATED_UCLASS_BODY() protected: - /* Change setting by editing DefaultGame.ini, add section + /* Change setting by editing DefaultGame.ini, add section. * [/Script/Flow.FlowNode_Checkpoint] * bUseAsyncSave=True */ UPROPERTY(VisibleAnywhere, Config, Category = "Checkpoint") diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_CustomEventBase.h b/Source/Flow/Public/Nodes/Graph/FlowNode_CustomEventBase.h index 7e9944d25..2bf24f564 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_CustomEventBase.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_CustomEventBase.h @@ -1,12 +1,11 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Nodes/FlowNode.h" #include "FlowNode_CustomEventBase.generated.h" /** - * Base class for nodes used to receive/send events between graphs + * Base class for nodes used to receive/send events between graphs. */ UCLASS(Abstract, NotBlueprintable) class FLOW_API UFlowNode_CustomEventBase : public UFlowNode diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_CustomInput.h b/Source/Flow/Public/Nodes/Graph/FlowNode_CustomInput.h index 38bc168e4..43674a7a0 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_CustomInput.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_CustomInput.h @@ -1,12 +1,11 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "FlowNode_CustomEventBase.h" #include "FlowNode_CustomInput.generated.h" /** - * Triggers output upon activation of Input (matching this EventName) on the SubGraph node containing this graph + * Triggers output upon activation of Input (matching this EventName) on the SubGraph node containing this graph. */ UCLASS(NotBlueprintable, meta = (DisplayName = "Custom Input")) class FLOW_API UFlowNode_CustomInput : public UFlowNode_CustomEventBase diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_CustomOutput.h b/Source/Flow/Public/Nodes/Graph/FlowNode_CustomOutput.h index 4d283fb31..894a5795e 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_CustomOutput.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_CustomOutput.h @@ -1,13 +1,12 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "FlowNode_CustomEventBase.h" #include "FlowNode_CustomOutput.generated.h" /** - * Triggers output on SubGraph node containing this graph - * Triggered output name matches EventName selected on this node + * Triggers output on SubGraph node containing this graph. + * Triggered output name matches EventName selected on this node. */ UCLASS(NotBlueprintable, meta = (DisplayName = "Custom Output")) class FLOW_API UFlowNode_CustomOutput final : public UFlowNode_CustomEventBase diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h b/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h index ea882dd97..52e34ef11 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Interfaces/FlowNamedPropertiesSupplierInterface.h" @@ -9,7 +8,7 @@ #include "FlowNode_DefineProperties.generated.h" /** - * FlowNode to define data pin property literals for use connecting to data pin inputs in a flow graph + * FlowNode to define data pin property literals for use connecting to data pin inputs in a flow graph. */ UCLASS(Blueprintable, meta = (DisplayName = "Define Properties")) class FLOW_API UFlowNode_DefineProperties @@ -19,8 +18,8 @@ class FLOW_API UFlowNode_DefineProperties GENERATED_UCLASS_BODY() protected: - // Instance-defined properties. - // These will auto-generate a matching pin that is bound to its property as its data source. + /* Instance-defined properties. + * These will auto-generate a matching pin that is bound to its property as its data source. */ UPROPERTY(EditAnywhere, Category = "Configuration", DisplayName = Properties) TArray NamedProperties; @@ -55,8 +54,8 @@ class FLOW_API UFlowNode_DefineProperties TInstancedStruct& OutFoundInstancedStruct) const override; #if WITH_EDITOR - // Utility function for subclasses, if they want to force a named property to be a Input or Output - // (unused in this class) + /* Utility function for subclasses, if they want to force a named property to be Input or Output. + * Unused in this class. */ void OnPostEditEnsureAllNamedPropertiesPinDirection(const FProperty& Property, bool bIsInput); #endif }; diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_Finish.h b/Source/Flow/Public/Nodes/Graph/FlowNode_Finish.h index a9f9299b6..c3ef87784 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_Finish.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_Finish.h @@ -1,13 +1,12 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Nodes/FlowNode.h" #include "FlowNode_Finish.generated.h" /** - * Finish execution of this Flow Asset - * All active nodes and sub graphs will be deactivated + * Finish execution of this Flow Asset. + * All active nodes and sub graphs will be deactivated. */ UCLASS(NotBlueprintable, meta = (DisplayName = "Finish")) class FLOW_API UFlowNode_Finish : public UFlowNode diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_FormatText.h b/Source/Flow/Public/Nodes/Graph/FlowNode_FormatText.h index 52ba2dc32..85f7061e4 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_FormatText.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_FormatText.h @@ -1,14 +1,12 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Nodes/Graph/FlowNode_DefineProperties.h" - #include "FlowNode_FormatText.generated.h" /** - * Formats a text string using the standard UE FText formatting system - * using input pins as parameters and the output is delivered to OUTPIN_TextOutput + * Formats a text string using the standard UE FText formatting system. + * using input pins as parameters and the output is delivered to OUTPIN_TextOutput. */ UCLASS(NotBlueprintable, meta = (DisplayName = "Format Text", Keywords = "print")) class FLOW_API UFlowNode_FormatText : public UFlowNode_DefineProperties @@ -16,9 +14,9 @@ class FLOW_API UFlowNode_FormatText : public UFlowNode_DefineProperties GENERATED_UCLASS_BODY() private: - // Format text string - // (uses standard Unreal "FText" formatting: eg, {PinName} will refer to input called PinName) - // Note - complex types are exported "ToString" and InstancedStruct is not supported + /* Format text string. + * Uses standard Unreal "FText" formatting: eg, {PinName} will refer to input called PinName. + * Note: complex types are exported "ToString" and InstancedStruct is not supported. */ UPROPERTY(EditAnywhere, Category = "Flow", meta = (DefaultForInputFlowPin, FlowPinType = Text)) FText FormatText; diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_Start.h b/Source/Flow/Public/Nodes/Graph/FlowNode_Start.h index c62ccfd2b..0aea113cc 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_Start.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_Start.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Nodes/Graph/FlowNode_DefineProperties.h" @@ -7,7 +6,7 @@ #include "FlowNode_Start.generated.h" /** - * Execution of the graph always starts from this node + * Execution of the graph always starts from this node. */ UCLASS(NotBlueprintable, NotPlaceable, meta = (DisplayName = "Start")) class FLOW_API UFlowNode_Start @@ -19,14 +18,12 @@ class FLOW_API UFlowNode_Start friend class UFlowAsset; protected: - - // External DataPin Value Supplier - // (eg, the UFlowNode_SubGraph that instanced this Start node's flow asset) + /* External DataPin Value Supplier. + * Example: the UFlowNode_SubGraph that instanced this Start node's flow asset. */ UPROPERTY(Transient) TScriptInterface FlowDataPinValueSupplierInterface; public: - // IFlowCoreExecutableInterface virtual void ExecuteInput(const FName& PinName) override; // -- diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h b/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h index 016ef6489..ddc5c15da 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h @@ -1,15 +1,13 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Nodes/FlowNode.h" - #include "FlowNode_SubGraph.generated.h" class UFlowAssetParams; /** - * Creates instance of provided Flow Asset and starts its execution + * Creates instance of provided Flow Asset and starts its execution. */ UCLASS(NotBlueprintable, meta = (DisplayName = "Sub Graph")) class FLOW_API UFlowNode_SubGraph : public UFlowNode @@ -28,7 +26,7 @@ class FLOW_API UFlowNode_SubGraph : public UFlowNode UPROPERTY(EditAnywhere, Category = "Graph") TSoftObjectPtr Asset; - /* Flow Asset Params to use as the data pin value supplier for the Asset */ + /* Flow Asset Params to use as the data pin value supplier for the Asset. */ UPROPERTY(EditAnywhere, Category = "Graph", meta = (DefaultForInputFlowPin, FlowPinType = "Object")) TSoftObjectPtr AssetParams; diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_Branch.h b/Source/Flow/Public/Nodes/Route/FlowNode_Branch.h index 49adca144..522d6285e 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_Branch.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_Branch.h @@ -1,24 +1,22 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Nodes/FlowNode.h" - #include "FlowNode_Branch.generated.h" -// FEvaluates its AddOns that implement the IFlowPredicateInterface to determine the output pin to trigger +/** + * FEvaluates its AddOns that implement the IFlowPredicateInterface to determine the output pin to trigger. + */ UCLASS(MinimalApi, NotBlueprintable, meta = (DisplayName = "Branch")) class UFlowNode_Branch : public UFlowNode { GENERATED_UCLASS_BODY() public: - // UFlowNodeBase virtual EFlowAddOnAcceptResult AcceptFlowNodeAddOnChild_Implementation(const UFlowNodeAddOn* AddOnTemplate, const TArray& AdditionalAddOnsToAssumeAreChildren) const override; // -- - // Event reacting on triggering Input pin virtual void ExecuteInput(const FName& PinName) override; static const FName INPIN_Evaluate; diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_Counter.h b/Source/Flow/Public/Nodes/Route/FlowNode_Counter.h index 8346814d5..5ac982c71 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_Counter.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_Counter.h @@ -1,12 +1,11 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Nodes/FlowNode.h" #include "FlowNode_Counter.generated.h" /** - * Counts how many times signal entered this node + * Counts how many times signal entered this node. */ UCLASS(NotBlueprintable, meta = (DisplayName = "Counter")) class FLOW_API UFlowNode_Counter final : public UFlowNode diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionMultiGate.h b/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionMultiGate.h index 262529cbc..f39085f4b 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionMultiGate.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionMultiGate.h @@ -1,12 +1,11 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Nodes/FlowNode.h" #include "FlowNode_ExecutionMultiGate.generated.h" /** - * Executes a series of pins in order + * Executes a series of pins in order. */ UCLASS(NotBlueprintable, meta = (DisplayName = "Multi Gate", Keywords = "series, loop, random")) class FLOW_API UFlowNode_ExecutionMultiGate final : public UFlowNode @@ -16,8 +15,8 @@ class FLOW_API UFlowNode_ExecutionMultiGate final : public UFlowNode UPROPERTY(EditAnywhere, Category = "MultiGate") bool bRandom; - // Allow executing output pins again, without triggering Reset pin - // If set to False, every output pin can be triggered only once + /* Allow executing output pins again, without triggering Reset pin. + * If set to False, every output pin can be triggered only once/ */ UPROPERTY(EditAnywhere, Category = "MultiGate") bool bLoop; diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionSequence.h b/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionSequence.h index 73c081755..b0804a388 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionSequence.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_ExecutionSequence.h @@ -1,12 +1,11 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Nodes/FlowNode.h" #include "FlowNode_ExecutionSequence.generated.h" /** - * Executes all outputs sequentially + * Executes all outputs sequentially. */ UCLASS(NotBlueprintable, meta = (DisplayName = "Sequence")) class FLOW_API UFlowNode_ExecutionSequence final : public UFlowNode @@ -18,7 +17,7 @@ class FLOW_API UFlowNode_ExecutionSequence final : public UFlowNode * If enabled and the graph is saved during gameplay, this node * tracks and saves which pins it has executed. * - * If you add new connections or replace old connections with with + * If you add new connections or replace old connections with * different nodes, this node will detect the changes. If during gameplay * you load an old save game which had different connections, this node * will automatically execute the updated connections you created. diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_LogicalAND.h b/Source/Flow/Public/Nodes/Route/FlowNode_LogicalAND.h index 6296d07e1..cda94867e 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_LogicalAND.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_LogicalAND.h @@ -1,13 +1,12 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Nodes/FlowNode.h" #include "FlowNode_LogicalAND.generated.h" /** - * Logical AND - * Output will be triggered only once + * Logical AND. + * Output will be triggered only once. */ UCLASS(NotBlueprintable, meta = (DisplayName = "AND", Keywords = "&")) class FLOW_API UFlowNode_LogicalAND final : public UFlowNode diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_LogicalOR.h b/Source/Flow/Public/Nodes/Route/FlowNode_LogicalOR.h index 70ea21e82..67bbaae3f 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_LogicalOR.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_LogicalOR.h @@ -1,13 +1,12 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Nodes/FlowNode.h" #include "FlowNode_LogicalOR.generated.h" /** - * Logical OR - * Output will be triggered only once + * Logical OR. + * Output will be triggered only once. */ UCLASS(NotBlueprintable, meta = (DisplayName = "OR", Keywords = "|")) class FLOW_API UFlowNode_LogicalOR final : public UFlowNode @@ -17,13 +16,13 @@ class FLOW_API UFlowNode_LogicalOR final : public UFlowNode protected: UPROPERTY(EditAnywhere, Category = "Lifetime", SaveGame) bool bEnabled; - - // This node will become Blocked (not executed any more), if Execution Limit > 0 and Execution Count reaches this limit - // Set this to zero, if you'd like fire output indefinitely + + /* This node will become Blocked (not executed anymore), if Execution Limit > 0 and Execution Count reaches this limit. + * Set this to zero, if you'd like fire output indefinitely. */ UPROPERTY(EditAnywhere, Category = "Lifetime", meta = (ClampMin = 0)) int32 ExecutionLimit; - // This node will become Blocked (not executed any more), if Execution Limit > 0 and Execution Count reaches this limit + /* This node will become Blocked (not executed anymore), if Execution Limit > 0 and Execution Count reaches this limit. */ UPROPERTY(VisibleAnywhere, Category = "Lifetime", SaveGame) int32 ExecutionCount; diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_Reroute.h b/Source/Flow/Public/Nodes/Route/FlowNode_Reroute.h index 6a8f8fb97..d836d3ea7 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_Reroute.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_Reroute.h @@ -1,12 +1,11 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Nodes/FlowNode.h" #include "FlowNode_Reroute.generated.h" /** - * Reroute + * Reroute. */ UCLASS(NotBlueprintable, meta = (DisplayName = "Reroute")) class FLOW_API UFlowNode_Reroute final : public UFlowNode diff --git a/Source/Flow/Public/Nodes/Route/FlowNode_Timer.h b/Source/Flow/Public/Nodes/Route/FlowNode_Timer.h index 1c0732041..6279442b3 100644 --- a/Source/Flow/Public/Nodes/Route/FlowNode_Timer.h +++ b/Source/Flow/Public/Nodes/Route/FlowNode_Timer.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Engine/EngineTypes.h" @@ -7,7 +6,7 @@ #include "FlowNode_Timer.generated.h" /** - * Triggers outputs after time elapsed + * Triggers outputs after time elapsed. */ UCLASS(NotBlueprintable, meta = (DisplayName = "Timer", Keywords = "delay, step, tick")) class FLOW_API UFlowNode_Timer : public UFlowNode @@ -15,11 +14,11 @@ class FLOW_API UFlowNode_Timer : public UFlowNode GENERATED_UCLASS_BODY() protected: - // If the value is closer to 0, Timer will complete in next tick + /* If the value is closer to 0, Timer will complete in next tick. */ UPROPERTY(EditAnywhere, Category = "Timer", meta = (ClampMin = 0.0f, DefaultForInputFlowPin, FlowPinType = Float)) float CompletionTime; - // this allows to trigger other nodes multiple times before completing the Timer + /* This allows to trigger other nodes multiple times before completing the Timer. */ UPROPERTY(EditAnywhere, Category = "Timer", meta = (ClampMin = 0.0f)) float StepTime; diff --git a/Source/Flow/Public/Types/FlowActorOwnerComponentRef.h b/Source/Flow/Public/Types/FlowActorOwnerComponentRef.h index dd29bf4bf..a44a17d02 100644 --- a/Source/Flow/Public/Types/FlowActorOwnerComponentRef.h +++ b/Source/Flow/Public/Types/FlowActorOwnerComponentRef.h @@ -1,48 +1,42 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "UObject/ObjectPtr.h" - #include "FlowActorOwnerComponentRef.generated.h" -// Forward Declarations class UActorComponent; -// Similar to FAnimNodeFunctionRef, providing a FName-based Component binding -// that is resolved at runtime +/** + * Similar to FAnimNodeFunctionRef, providing a FName-based Component binding that is resolved at runtime. + */ USTRUCT(BlueprintType) struct FFlowActorOwnerComponentRef { GENERATED_BODY() public: - - // Tries to find the component by name on the given actor + /* Tries to find the component by name on the given actor. */ UActorComponent* TryResolveComponent(const AActor& InActor, bool bWarnIfFailed = true); - // In some cases, the component can be resolved directly + /* In some cases, the component can be resolved directly. */ void SetResolvedComponentDirect(UActorComponent& Component); - // Returns a the resolved component - // (assumes TryResolveComponent() was called previously) + /* Returns a resolved component. + * Assumes TryResolveComponent() was called previously. */ UActorComponent* GetResolvedComponent() const { return ResolvedComponent; } - // Accessors bool IsConfigured() const { return !ComponentName.IsNone(); } bool IsResolved() const; static UActorComponent* TryResolveComponentByName(const AActor& InActor, const FName& InComponentName); public: - - // The name of the component UPROPERTY(VisibleAnywhere, Category = "Flow Actor Owner Component") FName ComponentName = NAME_None; protected: - - // Cached resolved component (resolved at runtime by calling TryResolveComponent) + /* Cached resolved component. + * Resolved at runtime by calling TryResolveComponent. */ UPROPERTY(Transient) TObjectPtr ResolvedComponent = nullptr; }; diff --git a/Source/Flow/Public/Types/FlowArray.h b/Source/Flow/Public/Types/FlowArray.h index 3d770c4f1..f536495c0 100644 --- a/Source/Flow/Public/Types/FlowArray.h +++ b/Source/Flow/Public/Types/FlowArray.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Algo/Unique.h" diff --git a/Source/Flow/Public/Types/FlowAutoDataPinsWorkingData.h b/Source/Flow/Public/Types/FlowAutoDataPinsWorkingData.h index 8b99a93a0..058b69ef7 100644 --- a/Source/Flow/Public/Types/FlowAutoDataPinsWorkingData.h +++ b/Source/Flow/Public/Types/FlowAutoDataPinsWorkingData.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Nodes/FlowPin.h" @@ -10,7 +9,9 @@ class UFlowNode; class UObject; struct FFlowDataPinValue; -// Container for pin data collected during automatic pin generation +/** + * Container for pin data collected during automatic pin generation. + */ struct FFlowPinSourceData { FFlowPinSourceData(const FFlowPin& InFlowPin, const FName& InPropertyOwnerObjectName, int32 InPropertyOwnerIndex = 0, const FFlowDataPinValue* InDataPinValue = nullptr) @@ -27,7 +28,9 @@ struct FFlowPinSourceData const FFlowDataPinValue* DataPinValue = nullptr; }; -// Transient working data used during auto-generation of data pins +/** + * Transient working data used during auto-generation of data pins. + */ struct FFlowAutoDataPinsWorkingData { public: @@ -38,9 +41,7 @@ struct FFlowAutoDataPinsWorkingData } FLOW_API bool AutoGenerateDataPinsForFlowNode(UFlowNode& FlowNode, bool& bAutoInputDataPinsChanged, bool& bAutoOutputDataPinsChanged); - FLOW_API void AddFlowDataPinsForClassProperties(const UObject& ObjectContainer, int32 PropertyOwnerIndex); - FLOW_API static void BuildNextFlowPinArray(const TArray& PinSourceDatas, TArray& OutFlowPins); protected: @@ -61,7 +62,7 @@ struct FFlowAutoDataPinsWorkingData static void DisambiguateDuplicateOutputDataPin( FFlowPinSourceData& PinSourceData, TSet& InOutUsedNames, - uint32 LogicalDuplicateIndex); + const uint32 LogicalDuplicateIndex); static bool CheckIfProposedPinsMatchPreviousPins(const TArray& PrevPins, const TArray& ProposedPins); diff --git a/Source/Flow/Public/Types/FlowClassUtils.h b/Source/Flow/Public/Types/FlowClassUtils.h index 8c878e265..3be9d04da 100644 --- a/Source/Flow/Public/Types/FlowClassUtils.h +++ b/Source/Flow/Public/Types/FlowClassUtils.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Containers/Array.h" diff --git a/Source/Flow/Public/Types/FlowDataPinBlueprintLibrary.h b/Source/Flow/Public/Types/FlowDataPinBlueprintLibrary.h index 3e25e8572..a95921452 100644 --- a/Source/Flow/Public/Types/FlowDataPinBlueprintLibrary.h +++ b/Source/Flow/Public/Types/FlowDataPinBlueprintLibrary.h @@ -1,17 +1,17 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Kismet/BlueprintFunctionLibrary.h" #include "FlowDataPinValuesStandard.h" #include "FlowDataPinResults.h" -#include "FlowPinTypeTemplates.h" #include "FlowDataPinBlueprintLibrary.generated.h" struct FFlowDataPinValue; -// Auto‑cast operators for blueprint to their inner types +/** + * Auto‑cast operators for blueprint to their inner types + */ UCLASS() class UFlowDataPinBlueprintLibrary : public UBlueprintFunctionLibrary { @@ -27,7 +27,9 @@ class UFlowDataPinBlueprintLibrary : public UBlueprintFunctionLibrary public: - // ---------- Pin construction helpers ---------- + /** + * ---------- Pin construction helpers ---------- + */ UFUNCTION(BlueprintPure, Category = FlowPin, Meta = (BlueprintThreadSafe, DisplayName = "Make Flow Pin")) static UPARAM(DisplayName = "Flow Pin") FFlowPin MakeStruct(FName PinName, FText PinFriendlyName, FString PinToolTip) @@ -43,139 +45,141 @@ class UFlowDataPinBlueprintLibrary : public UBlueprintFunctionLibrary OutPinToolTip = Ref.PinToolTip; } - // ---------- Resolve As ... functions ---------- - // Full-featured resolve nodes with execution pins for detailed error handling. - // Use these when you need to branch on Success/Failure/Coercion or inspect the exact resolve result. + /** + * ---------- Resolve As ... functions ---------- + * Full-featured resolve nodes with execution pins for detailed error handling. + * Use these when you need to branch on Success/Failure/Coercion or inspect the exact resolve result. + */ - // Resolve a Bool DataPin Value to a single bool. + /* Resolve a Bool DataPin Value to a single bool. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Bool", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsBool(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Bool& BoolValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, bool& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Resolve a Bool DataPin Value to a bool array. + /* Resolve a Bool DataPin Value to a bool array. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Bool Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsBoolArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Bool& BoolValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); - // Resolve an Int DataPin Value to a single int32. + /* Resolve an Int DataPin Value to a single int32. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Int", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsInt(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Int& IntValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, int32& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Resolve an Int DataPin Value to an int32 array. + /* Resolve an Int DataPin Value to an int32 array. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Int Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsIntArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Int& IntValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); - // Resolve an Int64 DataPin Value to a single int64. + /* Resolve an Int64 DataPin Value to a single int64. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Int64", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsInt64(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Int64& Int64Value, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, int64& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Resolve an Int64 DataPin Value to an int64 array. + /* Resolve an Int64 DataPin Value to an int64 array. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Int64 Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsInt64Array(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Int64& Int64Value, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); - // Resolve a Float DataPin Value to a single float. + /* Resolve a Float DataPin Value to a single float. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Float", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsFloat(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Float& FloatValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, float& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Resolve a Float DataPin Value to a float array. + /* Resolve a Float DataPin Value to a float array. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Float Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsFloatArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Float& FloatValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); - // Resolve a Double DataPin Value to a single double. + /* Resolve a Double DataPin Value to a single double. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Double", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsDouble(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Double& DoubleValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, double& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Resolve a Double DataPin Value to a double array. + /* Resolve a Double DataPin Value to a double array. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Double Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsDoubleArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Double& DoubleValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); - // Resolve a Name DataPin Value to a single FName. + /* Resolve a Name DataPin Value to a single FName. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Name", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsName(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Name& NameValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, FName& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Resolve a Name DataPin Value to an FName array. + /* Resolve a Name DataPin Value to an FName array. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Name Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsNameArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Name& NameValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); - // Resolve a String DataPin Value to a single FString. + /* Resolve a String DataPin Value to a single FString. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As String", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsString(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_String& StringValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, FString& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Resolve a String DataPin Value to an FString array. + /* Resolve a String DataPin Value to an FString array. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As String Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsStringArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_String& StringValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); - // Resolve a Text DataPin Value to a single FText. + /* Resolve a Text DataPin Value to a single FText. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Text", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsText(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Text& TextValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, FText& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Resolve a Text DataPin Value to an FText array. + /* Resolve a Text DataPin Value to an FText array. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Text Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsTextArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Text& TextValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); - // Resolve an Enum DataPin Value to a single uint8. + /* Resolve an Enum DataPin Value to a single uint8. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Enum", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsEnum(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Enum& EnumValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, uint8& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Resolve an Enum DataPin Value to a uint8 array. + /* Resolve an Enum DataPin Value to a uint8 array. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Enum Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsEnumArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Enum& EnumValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); - // Resolve a Vector DataPin Value to a single FVector. + /* Resolve a Vector DataPin Value to a single FVector. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Vector", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsVector(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Vector& VectorValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, FVector& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Resolve a Vector DataPin Value to an FVector array. + /* Resolve a Vector DataPin Value to an FVector array. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Vector Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsVectorArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Vector& VectorValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); - // Resolve a Rotator DataPin Value to a single FRotator. + /* Resolve a Rotator DataPin Value to a single FRotator. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Rotator", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsRotator(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Rotator& RotatorValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, FRotator& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Resolve a Rotator DataPin Value to an FRotator array. + /* Resolve a Rotator DataPin Value to an FRotator array. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Rotator Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsRotatorArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Rotator& RotatorValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); - // Resolve a Transform DataPin Value to a single FTransform. + /* Resolve a Transform DataPin Value to a single FTransform. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Transform", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsTransform(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Transform& TransformValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, FTransform& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Resolve a Transform DataPin Value to an FTransform array. + /* Resolve a Transform DataPin Value to an FTransform array. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Transform Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsTransformArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Transform& TransformValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); - // Resolve a GameplayTag DataPin Value to a single FGameplayTag. + /* Resolve a GameplayTag DataPin Value to a single FGameplayTag. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As GameplayTag", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsGameplayTag(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_GameplayTag& GameplayTagValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, FGameplayTag& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Resolve a GameplayTag DataPin Value to an FGameplayTag array. + /* Resolve a GameplayTag DataPin Value to an FGameplayTag array. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As GameplayTag Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsGameplayTagArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_GameplayTag& GameplayTagValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); - // Resolve a GameplayTagContainer DataPin Value (scalar only). + /* Resolve a GameplayTagContainer DataPin Value (scalar only). */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As GameplayTagContainer", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsGameplayTagContainer(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_GameplayTagContainer& GameplayTagContainerValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, FGameplayTagContainer& Value); - // Resolve an InstancedStruct DataPin Value to a single FInstancedStruct. + /* Resolve an InstancedStruct DataPin Value to a single FInstancedStruct. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As InstancedStruct", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsInstancedStruct(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_InstancedStruct& InstancedStructValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, FInstancedStruct& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Resolve an InstancedStruct DataPin Value to an FInstancedStruct array. + /* Resolve an InstancedStruct DataPin Value to an FInstancedStruct array. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As InstancedStruct Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsInstancedStructArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_InstancedStruct& InstancedStructValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); - // Resolve an Object DataPin Value to a single UObject*. + /* Resolve an Object DataPin Value to a single UObject*. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Object", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsObject(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Object& ObjectValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, UObject*& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Resolve an Object DataPin Value to a UObject* array. + /* Resolve an Object DataPin Value to a UObject* array. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Object Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsObjectArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Object& ObjectValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); - // Resolve a Class DataPin Value to a single UClass*. + /* Resolve a Class DataPin Value to a single UClass*. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Class", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsClass(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Class& ClassValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, UClass*& Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Resolve a Class DataPin Value to a UClass* array. + /* Resolve a Class DataPin Value to a UClass* array. */ UFUNCTION(BlueprintCallable, Category = DataPins, meta = (DisplayName = "Resolve As Class Array", DefaultToSelf = "Target", ExpandEnumAsExecs = "Result")) static void ResolveAsClassArray(UFlowNodeBase* Target, UPARAM(Ref) const FFlowDataPinValue_Class& ClassValue, EFlowDataPinResolveSimpleResult& Result, EFlowDataPinResolveResult& ResultEnum, TArray& Values); @@ -183,135 +187,135 @@ class UFlowDataPinBlueprintLibrary : public UBlueprintFunctionLibrary // Easy-resolve convenience nodes. On failure, logs an error and returns a safe default (false/empty/null). // Use these for fast, fire-and-forget resolving when you don't expect failures. - // Easy Resolve a Bool DataPin Value to a single bool (last value if array). Logs error on failure. + /* Easy Resolve a Bool DataPin Value to a single bool (last value if array). Logs error on failure. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Bool", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static bool AutoConvert_TryResolveAsBool(UPARAM(Ref) const FFlowDataPinValue_Bool& BoolValue, const UFlowNodeBase* Target); - // Easy Resolve a Bool DataPin Value to a bool array. Logs error on failure and returns empty array. + /* Easy Resolve a Bool DataPin Value to a bool array. Logs error on failure and returns empty array. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Bool Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static TArray AutoConvert_TryResolveAsBoolArray(UPARAM(Ref) const FFlowDataPinValue_Bool& BoolValue, const UFlowNodeBase* Target); - // Easy Resolve an Int DataPin Value to a single int32. Logs error on failure. + /* Easy Resolve an Int DataPin Value to a single int32. Logs error on failure. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Int", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static int32 AutoConvert_TryResolveAsInt(UPARAM(Ref) const FFlowDataPinValue_Int& IntValue, const UFlowNodeBase* Target); - // Easy Resolve an Int DataPin Value to an int32 array. Logs error on failure and returns empty array. + /* Easy Resolve an Int DataPin Value to an int32 array. Logs error on failure and returns empty array. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Int Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static TArray AutoConvert_TryResolveAsIntArray(UPARAM(Ref) const FFlowDataPinValue_Int& IntValue, const UFlowNodeBase* Target); - // Easy Resolve an Int64 DataPin Value to a single int64. Logs error on failure. + /* Easy Resolve an Int64 DataPin Value to a single int64. Logs error on failure. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Int64", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static int64 AutoConvert_TryResolveAsInt64(UPARAM(Ref) const FFlowDataPinValue_Int64& Int64Value, const UFlowNodeBase* Target); - // Easy Resolve an Int64 DataPin Value to an int64 array. Logs error on failure and returns empty array. + /* Easy Resolve an Int64 DataPin Value to an int64 array. Logs error on failure and returns empty array. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Int64 Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static TArray AutoConvert_TryResolveAsInt64Array(UPARAM(Ref) const FFlowDataPinValue_Int64& Int64Value, const UFlowNodeBase* Target); - // Easy Resolve a Float DataPin Value to a single float. Logs error on failure. + /* Easy Resolve a Float DataPin Value to a single float. Logs error on failure. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Float", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static float AutoConvert_TryResolveAsFloat(UPARAM(Ref) const FFlowDataPinValue_Float& FloatValue, const UFlowNodeBase* Target); - // Easy Resolve a Float DataPin Value to a float array. Logs error on failure and returns empty array. + /* Easy Resolve a Float DataPin Value to a float array. Logs error on failure and returns empty array. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Float Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static TArray AutoConvert_TryResolveAsFloatArray(UPARAM(Ref) const FFlowDataPinValue_Float& FloatValue, const UFlowNodeBase* Target); - // Easy Resolve a Double DataPin Value to a single double. Logs error on failure. + /* Easy Resolve a Double DataPin Value to a single double. Logs error on failure. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Double", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static double AutoConvert_TryResolveAsDouble(UPARAM(Ref) const FFlowDataPinValue_Double& DoubleValue, const UFlowNodeBase* Target); - // Easy Resolve a Double DataPin Value to a double array. Logs error on failure and returns empty array. + /* Easy Resolve a Double DataPin Value to a double array. Logs error on failure and returns empty array. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Double Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static TArray AutoConvert_TryResolveAsDoubleArray(UPARAM(Ref) const FFlowDataPinValue_Double& DoubleValue, const UFlowNodeBase* Target); - // Easy Resolve a Name DataPin Value to a single FName. Logs error on failure. + /* Easy Resolve a Name DataPin Value to a single FName. Logs error on failure. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Name", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static FName AutoConvert_TryResolveAsName(UPARAM(Ref) const FFlowDataPinValue_Name& NameValue, const UFlowNodeBase* Target); - // Easy Resolve a Name DataPin Value to an FName array. Logs error on failure and returns empty array. + /* Easy Resolve a Name DataPin Value to an FName array. Logs error on failure and returns empty array. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Name Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static TArray AutoConvert_TryResolveAsNameArray(UPARAM(Ref) const FFlowDataPinValue_Name& NameValue, const UFlowNodeBase* Target); - // Easy Resolve a String DataPin Value to a single FString. Logs error on failure. + /* Easy Resolve a String DataPin Value to a single FString. Logs error on failure. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to String", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static FString AutoConvert_TryResolveAsString(UPARAM(Ref) const FFlowDataPinValue_String& StringValue, const UFlowNodeBase* Target); - // Easy Resolve a String DataPin Value to an FString array. Logs error on failure and returns empty array. + /* Easy Resolve a String DataPin Value to an FString array. Logs error on failure and returns empty array. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to String Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static TArray AutoConvert_TryResolveAsStringArray(UPARAM(Ref) const FFlowDataPinValue_String& StringValue, const UFlowNodeBase* Target); - // Easy Resolve a Text DataPin Value to a single FText. Logs error on failure. + /* Easy Resolve a Text DataPin Value to a single FText. Logs error on failure. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Text", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static FText AutoConvert_TryResolveAsText(UPARAM(Ref) const FFlowDataPinValue_Text& TextValue, const UFlowNodeBase* Target); - // Easy Resolve a Text DataPin Value to an FText array. Logs error on failure and returns empty array. + /* Easy Resolve a Text DataPin Value to an FText array. Logs error on failure and returns empty array. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Text Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static TArray AutoConvert_TryResolveAsTextArray(UPARAM(Ref) const FFlowDataPinValue_Text& TextValue, const UFlowNodeBase* Target); - // Easy Resolve an Enum DataPin Value to a single uint8. Logs error on failure. + /* Easy Resolve an Enum DataPin Value to a single uint8. Logs error on failure. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Enum", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static uint8 AutoConvert_TryResolveAsEnum(UPARAM(Ref) const FFlowDataPinValue_Enum& EnumValue, const UFlowNodeBase* Target); - // Easy Resolve an Enum DataPin Value to a uint8 array. Logs error on failure and returns empty array. + /* Easy Resolve an Enum DataPin Value to a uint8 array. Logs error on failure and returns empty array. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Enum Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static TArray AutoConvert_TryResolveAsEnumArray(UPARAM(Ref) const FFlowDataPinValue_Enum& EnumValue, const UFlowNodeBase* Target); - // Easy Resolve a Vector DataPin Value to a single FVector. Logs error on failure. + /* Easy Resolve a Vector DataPin Value to a single FVector. Logs error on failure. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Vector", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static FVector AutoConvert_TryResolveAsVector(UPARAM(Ref) const FFlowDataPinValue_Vector& VectorValue, const UFlowNodeBase* Target); - // Easy Resolve a Vector DataPin Value to an FVector array. Logs error on failure and returns empty array. + /* Easy Resolve a Vector DataPin Value to an FVector array. Logs error on failure and returns empty array. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Vector Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static TArray AutoConvert_TryResolveAsVectorArray(UPARAM(Ref) const FFlowDataPinValue_Vector& VectorValue, const UFlowNodeBase* Target); - // Easy Resolve a Rotator DataPin Value to a single FRotator. Logs error on failure. + /* Easy Resolve a Rotator DataPin Value to a single FRotator. Logs error on failure. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Rotator", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static FRotator AutoConvert_TryResolveAsRotator(UPARAM(Ref) const FFlowDataPinValue_Rotator& RotatorValue, const UFlowNodeBase* Target); - // Easy Resolve a Rotator DataPin Value to an FRotator array. Logs error on failure and returns empty array. + /* Easy Resolve a Rotator DataPin Value to an FRotator array. Logs error on failure and returns empty array. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Rotator Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static TArray AutoConvert_TryResolveAsRotatorArray(UPARAM(Ref) const FFlowDataPinValue_Rotator& RotatorValue, const UFlowNodeBase* Target); - // Easy Resolve a Transform DataPin Value to a single FTransform. Logs error on failure. + /* Easy Resolve a Transform DataPin Value to a single FTransform. Logs error on failure. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Transform", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static FTransform AutoConvert_TryResolveAsTransform(UPARAM(Ref) const FFlowDataPinValue_Transform& TransformValue, const UFlowNodeBase* Target); - // Easy Resolve a Transform DataPin Value to an FTransform array. Logs error on failure and returns empty array. + /* Easy Resolve a Transform DataPin Value to an FTransform array. Logs error on failure and returns empty array. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Transform Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static TArray AutoConvert_TryResolveAsTransformArray(UPARAM(Ref) const FFlowDataPinValue_Transform& TransformValue, const UFlowNodeBase* Target); - // Easy Resolve a GameplayTag DataPin Value to a single FGameplayTag. Logs error on failure. + /* Easy Resolve a GameplayTag DataPin Value to a single FGameplayTag. Logs error on failure. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to GameplayTag", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static FGameplayTag AutoConvert_TryResolveAsGameplayTag(UPARAM(Ref) const FFlowDataPinValue_GameplayTag& GameplayTagValue, const UFlowNodeBase* Target); - // Easy Resolve a GameplayTag DataPin Value to an FGameplayTag array. Logs error on failure and returns empty array. + /* Easy Resolve a GameplayTag DataPin Value to an FGameplayTag array. Logs error on failure and returns empty array. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to GameplayTag Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static TArray AutoConvert_TryResolveAsGameplayTagArray(UPARAM(Ref) const FFlowDataPinValue_GameplayTag& GameplayTagValue, const UFlowNodeBase* Target); - // Easy Resolve a GameplayTagContainer DataPin Value (scalar only). Logs error on failure. + /* Easy Resolve a GameplayTagContainer DataPin Value (scalar only). Logs error on failure. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to GameplayTagContainer", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static FGameplayTagContainer AutoConvert_TryResolveAsGameplayTagContainer(UPARAM(Ref) const FFlowDataPinValue_GameplayTagContainer& GameplayTagContainerValue, const UFlowNodeBase* Target); - // Easy Resolve an InstancedStruct DataPin Value to a single FInstancedStruct. Logs error on failure. + /* Easy Resolve an InstancedStruct DataPin Value to a single FInstancedStruct. Logs error on failure. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to InstancedStruct", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static FInstancedStruct AutoConvert_TryResolveAsInstancedStruct(UPARAM(Ref) const FFlowDataPinValue_InstancedStruct& InstancedStructValue, const UFlowNodeBase* Target); - // Easy Resolve an InstancedStruct DataPin Value to an FInstancedStruct array. Logs error on failure and returns empty array. + /* Easy Resolve an InstancedStruct DataPin Value to an FInstancedStruct array. Logs error on failure and returns empty array. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to InstancedStruct Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static TArray AutoConvert_TryResolveAsInstancedStructArray(UPARAM(Ref) const FFlowDataPinValue_InstancedStruct& InstancedStructValue, const UFlowNodeBase* Target); - // Easy Resolve an Object DataPin Value to a single UObject*. Logs error on failure. + /* Easy Resolve an Object DataPin Value to a single UObject*. Logs error on failure. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Object", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static UObject* AutoConvert_TryResolveAsObject(UPARAM(Ref) const FFlowDataPinValue_Object& ObjectValue, const UFlowNodeBase* Target); - // Easy Resolve an Object DataPin Value to a UObject* array. Logs error on failure and returns empty array. + /* Easy Resolve an Object DataPin Value to a UObject* array. Logs error on failure and returns empty array. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Object Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static TArray AutoConvert_TryResolveAsObjectArray(UPARAM(Ref) const FFlowDataPinValue_Object& ObjectValue, const UFlowNodeBase* Target); - // Easy Resolve a Class DataPin Value to a single UClass*. Logs error on failure. + /* Easy Resolve a Class DataPin Value to a single UClass*. Logs error on failure. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Class", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static UClass* AutoConvert_TryResolveAsClass(UPARAM(Ref) const FFlowDataPinValue_Class& ClassValue, const UFlowNodeBase* Target); - // Easy Resolve a Class DataPin Value to a UClass* array. Logs error on failure and returns empty array. + /* Easy Resolve a Class DataPin Value to a UClass* array. Logs error on failure and returns empty array. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Auto-Resolve to Class Array", CompactNodeTitle = "->", BlueprintAutocast, DefaultToSelf = "Target"), Category = DataPins) static TArray AutoConvert_TryResolveAsClassArray(UPARAM(Ref) const FFlowDataPinValue_Class& ClassValue, const UFlowNodeBase* Target); @@ -446,267 +450,332 @@ class UFlowDataPinBlueprintLibrary : public UBlueprintFunctionLibrary // Set functions: Safe for both input and output pins. // Get functions: ONLY safe on output pins. Using on an input pin triggers a runtime error in editor builds. - // Set a single bool on a Bool DataPin Value (input or output pin). Replaces any existing values. + /* Set a single bool on a Bool DataPin Value (input or output pin). + * Replaces any existing values. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Bool Value")) static void SetBoolValue(bool bInValue, UPARAM(Ref) FFlowDataPinValue_Bool& BoolValue) { BoolValue.Values = { bInValue }; } - // Set a bool array on a Bool DataPin Value (input or output pin). Replaces the entire array. + /* Set a bool array on a Bool DataPin Value (input or output pin). + * Replaces the entire array. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Bool Values")) static void SetBoolValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_Bool& BoolValue) { BoolValue.Values = InValues; } - // Get a single bool from an output Bool DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get a single bool from an output Bool DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Bool Value")) static bool GetBoolValue(UPARAM(Ref) const FFlowDataPinValue_Bool& BoolValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Get the full bool array from an output Bool DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get the full bool array from an output Bool DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Bool Values")) static TArray GetBoolValues(UPARAM(Ref) FFlowDataPinValue_Bool& BoolValue); - // Set a single int32 on an Int DataPin Value (input or output pin). Replaces any existing values. + /* Set a single int32 on an Int DataPin Value (input or output pin). + * Replaces any existing values. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Int Value")) static void SetIntValue(int32 InValue, UPARAM(Ref) FFlowDataPinValue_Int& IntValue) { IntValue.Values = { InValue }; } - // Set an int32 array on an Int DataPin Value (input or output pin). Replaces the entire array. + /* Set an int32 array on an Int DataPin Value (input or output pin). + * Replaces the entire array. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Int Values")) static void SetIntValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_Int& IntValue) { IntValue.Values = InValues; } - // Get a single int32 from an output Int DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get a single int32 from an output Int DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Int Value")) static int32 GetIntValue(UPARAM(Ref) const FFlowDataPinValue_Int& IntValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Get the full int32 array from an output Int DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get the full int32 array from an output Int DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Int Values")) static TArray GetIntValues(UPARAM(Ref) FFlowDataPinValue_Int& IntValue); - // Set a single int64 on an Int64 DataPin Value (input or output pin). Replaces any existing values. + /* Set a single int64 on an Int64 DataPin Value (input or output pin). + * Replaces any existing values. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Int64 Value")) static void SetInt64Value(int64 InValue, UPARAM(Ref) FFlowDataPinValue_Int64& Int64Value) { Int64Value.Values = { InValue }; } - // Set an int64 array on an Int64 DataPin Value (input or output pin). Replaces the entire array. + /* Set an int64 array on an Int64 DataPin Value (input or output pin). + * Replaces the entire array. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Int64 Values")) static void SetInt64Values(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_Int64& Int64Value) { Int64Value.Values = InValues; } - // Get a single int64 from an output Int64 DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get a single int64 from an output Int64 DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Int64 Value")) static int64 GetInt64Value(UPARAM(Ref) const FFlowDataPinValue_Int64& Int64Value, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Get the full int64 array from an output Int64 DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get the full int64 array from an output Int64 DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Int64 Values")) static TArray GetInt64Values(UPARAM(Ref) FFlowDataPinValue_Int64& Int64Value); - // Set a single float on a Float DataPin Value (input or output pin). Replaces any existing values. + /* Set a single float on a Float DataPin Value (input or output pin). + * Replaces any existing values. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Float Value")) static void SetFloatValue(float InValue, UPARAM(Ref) FFlowDataPinValue_Float& FloatValue) { FloatValue.Values = { InValue }; } - // Set a float array on a Float DataPin Value (input or output pin). Replaces the entire array. + /* Set a float array on a Float DataPin Value (input or output pin). + * Replaces the entire array. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Float Values")) static void SetFloatValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_Float& FloatValue) { FloatValue.Values = InValues; } - // Get a single float from an output Float DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get a single float from an output Float DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Float Value")) static float GetFloatValue(UPARAM(Ref) const FFlowDataPinValue_Float& FloatValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Get the full float array from an output Float DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get the full float array from an output Float DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Float Values")) static TArray GetFloatValues(UPARAM(Ref) FFlowDataPinValue_Float& FloatValue); - // Set a single double on a Double DataPin Value (input or output pin). Replaces any existing values. + /* Set a single double on a Double DataPin Value (input or output pin). + * Replaces any existing values. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Double Value")) static void SetDoubleValue(double InValue, UPARAM(Ref) FFlowDataPinValue_Double& DoubleValue) { DoubleValue.Values = { InValue }; } - // Set a double array on a Double DataPin Value (input or output pin). Replaces the entire array. + /* Set a double array on a Double DataPin Value (input or output pin). + * Replaces the entire array. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Double Values")) static void SetDoubleValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_Double& DoubleValue) { DoubleValue.Values = InValues; } - // Get a single double from an output Double DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get a single double from an output Double DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Double Value")) static double GetDoubleValue(UPARAM(Ref) const FFlowDataPinValue_Double& DoubleValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Get the full double array from an output Double DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get the full double array from an output Double DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Double Values")) static TArray GetDoubleValues(UPARAM(Ref) FFlowDataPinValue_Double& DoubleValue); - // Set a single FName on a Name DataPin Value (input or output pin). Replaces any existing values. + /* Set a single FName on a Name DataPin Value (input or output pin). + * Replaces any existing values. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Name Value")) static void SetNameValue(FName InValue, UPARAM(Ref) FFlowDataPinValue_Name& NameValue) { NameValue.Values = { InValue }; } - // Set an FName array on a Name DataPin Value (input or output pin). Replaces the entire array. + /* Set an FName array on a Name DataPin Value (input or output pin). + * Replaces the entire array. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Name Values")) static void SetNameValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_Name& NameValue) { NameValue.Values = InValues; } - // Get a single FName from an output Name DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get a single FName from an output Name DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Name Value")) static FName GetNameValue(UPARAM(Ref) const FFlowDataPinValue_Name& NameValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Get the full FName array from an output Name DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get the full FName array from an output Name DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Name Values")) static TArray GetNameValues(UPARAM(Ref) FFlowDataPinValue_Name& NameValue); - // Set a single FString on a String DataPin Value (input or output pin). Replaces any existing values. + /* Set a single FString on a String DataPin Value (input or output pin). + * Replaces any existing values. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set String Value")) static void SetStringValue(const FString& InValue, UPARAM(Ref) FFlowDataPinValue_String& StringValue) { StringValue.Values = { InValue }; } - // Set an FString array on a String DataPin Value (input or output pin). Replaces the entire array. + /* Set an FString array on a String DataPin Value (input or output pin). + * Replaces the entire array. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set String Values")) static void SetStringValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_String& StringValue) { StringValue.Values = InValues; } - // Get a single FString from an output String DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get a single FString from an output String DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get String Value")) static FString GetStringValue(UPARAM(Ref) const FFlowDataPinValue_String& StringValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Get the full FString array from an output String DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get the full FString array from an output String DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get String Values")) static TArray GetStringValues(UPARAM(Ref) FFlowDataPinValue_String& StringValue); - // Set a single FText on a Text DataPin Value (input or output pin). Replaces any existing values. + /* Set a single FText on a Text DataPin Value (input or output pin). + * Replaces any existing values. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Text Value")) static void SetTextValue(const FText& InValue, UPARAM(Ref) FFlowDataPinValue_Text& TextValue) { TextValue.Values = { InValue }; } - // Set an FText array on a Text DataPin Value (input or output pin). Replaces the entire array. + /* Set an FText array on a Text DataPin Value (input or output pin). + * Replaces the entire array. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Text Values")) static void SetTextValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_Text& TextValue) { TextValue.Values = InValues; } - // Get a single FText from an output Text DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get a single FText from an output Text DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Text Value")) static FText GetTextValue(UPARAM(Ref) const FFlowDataPinValue_Text& TextValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Get the full FText array from an output Text DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get the full FText array from an output Text DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Text Values")) static TArray GetTextValues(UPARAM(Ref) FFlowDataPinValue_Text& TextValue); - // Set a single enum value (as uint8) on an Enum DataPin Value (input or output pin). Requires EnumClass to be set on the struct. + /* Set a single enum value (as uint8) on an Enum DataPin Value (input or output pin). + * Requires EnumClass to be set on the struct. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Enum Value")) static void SetEnumValue(uint8 InValue, UPARAM(Ref) FFlowDataPinValue_Enum& EnumValue); - // Set an enum value array (as uint8) on an Enum DataPin Value (input or output pin). Requires EnumClass to be set on the struct. + /* Set an enum value array (as uint8) on an Enum DataPin Value (input or output pin). + * Requires EnumClass to be set on the struct. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Enum Values")) static void SetEnumValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_Enum& EnumValue); - // Get a single enum value (as uint8) from an output Enum DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get a single enum value (as uint8) from an output Enum DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Enum Value")) static uint8 GetEnumValue(UPARAM(Ref) const FFlowDataPinValue_Enum& EnumValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Get the full enum value array (as uint8) from an output Enum DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get the full enum value array (as uint8) from an output Enum DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Enum Values")) static TArray GetEnumValues(UPARAM(Ref) const FFlowDataPinValue_Enum& EnumValue); - // Set a single FVector on a Vector DataPin Value (input or output pin). Replaces any existing values. + /* Set a single FVector on a Vector DataPin Value (input or output pin). + * Replaces any existing values. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Vector Value")) static void SetVectorValue(const FVector& InValue, UPARAM(Ref) FFlowDataPinValue_Vector& VectorValue) { VectorValue.Values = { InValue }; } - // Set an FVector array on a Vector DataPin Value (input or output pin). Replaces the entire array. + /* Set an FVector array on a Vector DataPin Value (input or output pin). + * Replaces the entire array. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Vector Values")) static void SetVectorValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_Vector& VectorValue) { VectorValue.Values = InValues; } - // Get a single FVector from an output Vector DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get a single FVector from an output Vector DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Vector Value")) static FVector GetVectorValue(UPARAM(Ref) const FFlowDataPinValue_Vector& VectorValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Get the full FVector array from an output Vector DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get the full FVector array from an output Vector DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Vector Values")) static TArray GetVectorValues(UPARAM(Ref) FFlowDataPinValue_Vector& VectorValue); - // Set a single FRotator on a Rotator DataPin Value (input or output pin). Replaces any existing values. + /* Set a single FRotator on a Rotator DataPin Value (input or output pin). + * Replaces any existing values. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Rotator Value")) static void SetRotatorValue(const FRotator& InValue, UPARAM(Ref) FFlowDataPinValue_Rotator& RotatorValue) { RotatorValue.Values = { InValue }; } - // Set an FRotator array on a Rotator DataPin Value (input or output pin). Replaces the entire array. + /* Set an FRotator array on a Rotator DataPin Value (input or output pin). + * Replaces the entire array. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Rotator Values")) static void SetRotatorValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_Rotator& RotatorValue) { RotatorValue.Values = InValues; } - // Get a single FRotator from an output Rotator DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get a single FRotator from an output Rotator DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Rotator Value")) static FRotator GetRotatorValue(UPARAM(Ref) const FFlowDataPinValue_Rotator& RotatorValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Get the full FRotator array from an output Rotator DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get the full FRotator array from an output Rotator DataPin Value + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Rotator Values")) static TArray GetRotatorValues(UPARAM(Ref) FFlowDataPinValue_Rotator& RotatorValue); - // Set a single FTransform on a Transform DataPin Value (input or output pin). Replaces any existing values. + /* Set a single FTransform on a Transform DataPin Value (input or output pin). + * Replaces any existing values. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Transform Value")) static void SetTransformValue(const FTransform& InValue, UPARAM(Ref) FFlowDataPinValue_Transform& TransformValue) { TransformValue.Values = { InValue }; } - // Set an FTransform array on a Transform DataPin Value (input or output pin). Replaces the entire array. + /* Set an FTransform array on a Transform DataPin Value (input or output pin). + * Replaces the entire array. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Transform Values")) static void SetTransformValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_Transform& TransformValue) { TransformValue.Values = InValues; } - // Get a single FTransform from an output Transform DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get a single FTransform from an output Transform DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Transform Value")) static FTransform GetTransformValue(UPARAM(Ref) const FFlowDataPinValue_Transform& TransformValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Get the full FTransform array from an output Transform DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get the full FTransform array from an output Transform DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Transform Values")) static TArray GetTransformValues(UPARAM(Ref) FFlowDataPinValue_Transform& TransformValue); - // Set a single FGameplayTag on a GameplayTag DataPin Value (input or output pin). Replaces any existing values. + /* Set a single FGameplayTag on a GameplayTag DataPin Value (input or output pin). + * Replaces any existing values. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set GameplayTag Value")) static void SetGameplayTagValue(FGameplayTag InValue, UPARAM(Ref) FFlowDataPinValue_GameplayTag& GameplayTagValue) { GameplayTagValue.Values = { InValue }; } - // Set an FGameplayTag array on a GameplayTag DataPin Value (input or output pin). Replaces the entire array. + /* Set an FGameplayTag array on a GameplayTag DataPin Value (input or output pin). + * Replaces the entire array. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set GameplayTag Values")) static void SetGameplayTagValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_GameplayTag& GameplayTagValue) { GameplayTagValue.Values = InValues; } - // Get a single FGameplayTag from an output GameplayTag DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get a single FGameplayTag from an output GameplayTag DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get GameplayTag Value")) static FGameplayTag GetGameplayTagValue(UPARAM(Ref) const FFlowDataPinValue_GameplayTag& GameplayTagValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Get the full FGameplayTag array from an output GameplayTag DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get the full FGameplayTag array from an output GameplayTag DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get GameplayTag Values")) static TArray GetGameplayTagValues(UPARAM(Ref) FFlowDataPinValue_GameplayTag& GameplayTagValue); - // Set a GameplayTagContainer on a GameplayTagContainer DataPin Value (input or output pin, scalar only). + /* Set a GameplayTagContainer on a GameplayTagContainer DataPin Value (input or output pin, scalar only). */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set GameplayTagContainer Value")) static void SetGameplayTagContainerValue(const FGameplayTagContainer& InValue, UPARAM(Ref) FFlowDataPinValue_GameplayTagContainer& GameplayTagContainerValue) { GameplayTagContainerValue.Values = InValue; } - // Get the GameplayTagContainer from an output GameplayTagContainer DataPin Value (scalar only). DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get the GameplayTagContainer from an output GameplayTagContainer DataPin Value (scalar only). + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get GameplayTagContainer Value")) static FGameplayTagContainer GetGameplayTagContainerValue(UPARAM(Ref) const FFlowDataPinValue_GameplayTagContainer& GameplayTagContainerValue); - // Set a single FInstancedStruct on an InstancedStruct DataPin Value (input or output pin). Replaces any existing values. + /* Set a single FInstancedStruct on an InstancedStruct DataPin Value (input or output pin). + * Replaces any existing values. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set InstancedStruct Value")) static void SetInstancedStructValue(const FInstancedStruct& InValue, UPARAM(Ref) FFlowDataPinValue_InstancedStruct& InstancedStructValue) { InstancedStructValue.Values = { InValue }; } - // Set an FInstancedStruct array on an InstancedStruct DataPin Value (input or output pin). Replaces the entire array. + /* Set an FInstancedStruct array on an InstancedStruct DataPin Value (input or output pin). + * Replaces the entire array. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set InstancedStruct Values")) static void SetInstancedStructValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_InstancedStruct& InstancedStructValue) { InstancedStructValue.Values = InValues; } - // Get a single FInstancedStruct from an output InstancedStruct DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get a single FInstancedStruct from an output InstancedStruct DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get InstancedStruct Value")) static FInstancedStruct GetInstancedStructValue(UPARAM(Ref) const FFlowDataPinValue_InstancedStruct& InstancedStructValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Get the full FInstancedStruct array from an output InstancedStruct DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get the full FInstancedStruct array from an output InstancedStruct DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get InstancedStruct Values")) static TArray GetInstancedStructValues(UPARAM(Ref) FFlowDataPinValue_InstancedStruct& InstancedStructValue); - // Set a single UObject* on an Object DataPin Value (input or output pin). Replaces any existing values. + /* Set a single UObject* on an Object DataPin Value (input or output pin). + * Replaces any existing values. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Object Value")) static void SetObjectValue(UObject* InValue, UPARAM(Ref) FFlowDataPinValue_Object& ObjectValue) { ObjectValue.Values = { InValue }; } - // Set a UObject* array on an Object DataPin Value (input or output pin). Replaces the entire array. + /* Set a UObject* array on an Object DataPin Value (input or output pin). + * Replaces the entire array. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Object Values")) static void SetObjectValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_Object& ObjectValue) { ObjectValue.Values = InValues; } - // Get a single UObject* from an output Object DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get a single UObject* from an output Object DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Object Value")) static UObject* GetObjectValue(UPARAM(Ref) const FFlowDataPinValue_Object& ObjectValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Get the full UObject* array from an output Object DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get the full UObject* array from an output Object DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Object Values")) static TArray GetObjectValues(UPARAM(Ref) FFlowDataPinValue_Object& ObjectValue); - // Set a single FSoftClassPath on a Class DataPin Value (input or output pin). Replaces any existing values. + /* Set a single FSoftClassPath on a Class DataPin Value (input or output pin). + * Replaces any existing values. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Class Value")) static void SetClassValue(const FSoftClassPath& InValue, UPARAM(Ref) FFlowDataPinValue_Class& ClassValue) { ClassValue.Values = { InValue }; } - // Set an FSoftClassPath array on a Class DataPin Value (input or output pin). Replaces the entire array. + /* Set an FSoftClassPath array on a Class DataPin Value (input or output pin). + * Replaces the entire array. */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Set Class Values")) static void SetClassValues(const TArray& InValues, UPARAM(Ref) FFlowDataPinValue_Class& ClassValue) { ClassValue.Values = InValues; } - // Get a single FSoftClassPath from an output Class DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get a single FSoftClassPath from an output Class DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintPure, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Class Value")) static FSoftClassPath GetClassValue(UPARAM(Ref) const FFlowDataPinValue_Class& ClassValue, EFlowSingleFromArray SingleFromArray = EFlowSingleFromArray::LastValue); - // Get the full FSoftClassPath array from an output Class DataPin Value. DO NOT use on input pins — will error in editor, use Resolve As... functions instead! + /* Get the full FSoftClassPath array from an output Class DataPin Value. + * DO NOT use on input pins — will error in editor, use Resolve As... functions instead! */ UFUNCTION(BlueprintCallable, Category = DataPins, Meta = (BlueprintThreadSafe, DisplayName = "Get Class Values")) static TArray GetClassValues(UPARAM(Ref) FFlowDataPinValue_Class& ClassValue); diff --git a/Source/Flow/Public/Types/FlowDataPinProperties.h b/Source/Flow/Public/Types/FlowDataPinProperties.h index fb3811bfd..f97dabc1c 100644 --- a/Source/Flow/Public/Types/FlowDataPinProperties.h +++ b/Source/Flow/Public/Types/FlowDataPinProperties.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "GameplayTagContainer.h" @@ -9,7 +8,10 @@ #include "FlowDataPinProperties.generated.h" -// #FlowDataPinLegacy +/** + * #FlowDataPinLegacy + */ + USTRUCT(DisplayName = "Base - Flow DataPin Property", meta = (Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinProperty { @@ -20,126 +22,130 @@ struct FFlowDataPinProperty virtual ~FFlowDataPinProperty() { } }; -// Wrapper struct for a bool that will generate and link to a Data Pin with its same name +/** + * Wrapper struct for a bool that will generate and link to a Data Pin with its same name. + */ USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Bool - Output Flow Data Pin Property", meta = (FlowPinType = "Bool", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_Bool : public FFlowDataPinProperty { GENERATED_BODY() public: - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) bool Value = false; public: FFlowDataPinOutputProperty_Bool() { } - FFlowDataPinOutputProperty_Bool(bool InValue) : Value(InValue) { } + explicit FFlowDataPinOutputProperty_Bool(const bool InValue) : Value(InValue) { } }; -// Wrapper struct for an int64 that will generate and link to a Data Pin with its same name +/** + * Wrapper struct for an int64 that will generate and link to a Data Pin with its same name. + */ USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Int64 - Output Flow Data Pin Property", meta = (FlowPinType = "Int64", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_Int64 : public FFlowDataPinProperty { GENERATED_BODY() public: - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) int64 Value = 0; public: FFlowDataPinOutputProperty_Int64() { } - FFlowDataPinOutputProperty_Int64(int64 InValue) : Value(InValue) { } + explicit FFlowDataPinOutputProperty_Int64(const int64 InValue) : Value(InValue) { } }; -// Wrapper struct for an int32 that will generate and link to a Data Pin with its same name +/** + * Wrapper struct for an int32 that will generate and link to a Data Pin with its same name. + */ USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Int - Output Flow Data Pin Property", meta = (FlowPinType = "Int", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_Int32 : public FFlowDataPinProperty { GENERATED_BODY() public: - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) int32 Value = 0; public: - FFlowDataPinOutputProperty_Int32() { } - FFlowDataPinOutputProperty_Int32(int32 InValue) : Value(InValue) { } + explicit FFlowDataPinOutputProperty_Int32(const int32 InValue) : Value(InValue) { } }; -// Wrapper struct for a Double (64bit float) that will generate and link to a Data Pin with its same name +/** + * Wrapper struct for a Double (64bit float) that will generate and link to a Data Pin with its same name. + */ USTRUCT(BlueprintType, DisplayName = "Double (float64) - Output Flow Data Pin Property", meta = (FlowPinType = "Double", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_Double : public FFlowDataPinProperty { GENERATED_BODY() public: - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) double Value = 0; public: - FFlowDataPinOutputProperty_Double() { } - FFlowDataPinOutputProperty_Double(double InValue) : Value(InValue) { } + explicit FFlowDataPinOutputProperty_Double(const double InValue) : Value(InValue) { } }; -// Wrapper struct for a Float (32bit) that will generate and link to a Data Pin with its same name +/** + * Wrapper struct for a Float (32bit) that will generate and link to a Data Pin with its same name. + */ USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Float - Output Flow Data Pin Property", meta = (FlowPinType = "Float", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_Float : public FFlowDataPinProperty { GENERATED_BODY() public: - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) float Value = 0.0f; public: - FFlowDataPinOutputProperty_Float() { } - FFlowDataPinOutputProperty_Float(float InValue) : Value(InValue) { } + explicit FFlowDataPinOutputProperty_Float(const float InValue) : Value(InValue) { } }; -// Wrapper struct for a FName that will generate and link to a Data Pin with its same name +/** + * Wrapper struct for a FName that will generate and link to a Data Pin with its same name. + */ USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Name - Output Flow Data Pin Property", meta = (FlowPinType = "Name", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_Name : public FFlowDataPinProperty { GENERATED_BODY() public: - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) FName Value = NAME_None; public: - FFlowDataPinOutputProperty_Name() { } - FFlowDataPinOutputProperty_Name(const FName& InValue) : Value(InValue) { } + explicit FFlowDataPinOutputProperty_Name(const FName& InValue) : Value(InValue) { } }; -// Wrapper struct for a FString that will generate and link to a Data Pin with its same name +/** + * Wrapper struct for a FString that will generate and link to a Data Pin with its same name. + */ USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] String - Output Flow Data Pin Property", meta = (FlowPinType = "String", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_String : public FFlowDataPinProperty { GENERATED_BODY() public: - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) FString Value; public: - FFlowDataPinOutputProperty_String() { } - FFlowDataPinOutputProperty_String(const FString& InValue) : Value(InValue) { } + explicit FFlowDataPinOutputProperty_String(const FString& InValue) : Value(InValue) { } }; -// Wrapper struct for a FText that will generate and link to a Data Pin with its same name +/** + * Wrapper struct for a FText that will generate and link to a Data Pin with its same name. + */ USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Text - Output Flow Data Pin Property", meta = (FlowPinType = "Text", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_Text : public FFlowDataPinProperty { @@ -153,36 +159,35 @@ struct FFlowDataPinOutputProperty_Text : public FFlowDataPinProperty public: FFlowDataPinOutputProperty_Text() { } - FFlowDataPinOutputProperty_Text(const FText& InValue) : Value(InValue) { } + explicit FFlowDataPinOutputProperty_Text(const FText& InValue) : Value(InValue) { } }; -// Wrapper struct for an enum that will generate and link to a Data Pin with its same name +/** + * Wrapper struct for an enum that will generate and link to a Data Pin with its same name. + */ USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Enum - Output Flow Data Pin Property", meta = (FlowPinType = "Enum", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_Enum : public FFlowDataPinProperty { GENERATED_BODY() public: - - // The selected enum Value + /* The selected enum Value. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) FName Value = NAME_None; - // Class for this enum + /* Class for this enum. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) TObjectPtr EnumClass = nullptr; #if WITH_EDITORONLY_DATA - // name of enum defined in c++ code, will take priority over asset from EnumType property - // (this is a work-around because EnumClass cannot find C++ Enums, - // so you need to type the name of the enum in here, manually) - // See also: UBlackboardKeyType_Enum::PostEditChangeProperty() + /* Name of enum defined in c++ code, will take priority over asset from EnumType property. + * This is a work-around because EnumClass cannot find C++ Enums, so you need to type the name of the enum in here, manually. + * See also: UBlackboardKeyType_Enum::PostEditChangeProperty(). */ UPROPERTY(EditAnywhere, Category = Blackboard) FString EnumName; -#endif // WITH_EDITORONLY_DATA +#endif public: - FFlowDataPinOutputProperty_Enum() { } FFlowDataPinOutputProperty_Enum(const FName& InValue, UEnum* InEnumClass) : Value(InValue) @@ -191,109 +196,111 @@ struct FFlowDataPinOutputProperty_Enum : public FFlowDataPinProperty } }; -// Wrapper struct for a FVector that will generate and link to a Data Pin with its same name +/** + * Wrapper struct for a FVector that will generate and link to a Data Pin with its same name. + */ USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Vector - Output Flow Data Pin Property", meta = (FlowPinType = "Vector", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_Vector : public FFlowDataPinProperty { GENERATED_BODY() public: - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) FVector Value = FVector::ZeroVector; public: - FFlowDataPinOutputProperty_Vector() {} - FFlowDataPinOutputProperty_Vector(const FVector& InValue) : Value(InValue) { } + explicit FFlowDataPinOutputProperty_Vector(const FVector& InValue) : Value(InValue) { } }; -// Wrapper struct for a FRotator that will generate and link to a Data Pin with its same name +/** + * Wrapper struct for a FRotator that will generate and link to a Data Pin with its same name. + */ USTRUCT(BlueprintType, DisplayName = "Rotator - Output Flow Data Pin Property", meta = (FlowPinType = "Rotator", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_Rotator : public FFlowDataPinProperty { GENERATED_BODY() public: - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) FRotator Value = FRotator::ZeroRotator; public: - FFlowDataPinOutputProperty_Rotator() {} - FFlowDataPinOutputProperty_Rotator(const FRotator& InValue) : Value(InValue) { } + explicit FFlowDataPinOutputProperty_Rotator(const FRotator& InValue) : Value(InValue) { } }; -// Wrapper struct for a FTransform that will generate and link to a Data Pin with its same name +/** + * Wrapper struct for a FTransform that will generate and link to a Data Pin with its same name. + */ USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Transform - Output Flow Data Pin Property", meta = (FlowPinType = "Transform", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_Transform : public FFlowDataPinProperty { GENERATED_BODY() public: - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) FTransform Value; public: - FFlowDataPinOutputProperty_Transform() {} - FFlowDataPinOutputProperty_Transform(const FTransform& InValue) : Value(InValue) { } + explicit FFlowDataPinOutputProperty_Transform(const FTransform& InValue) : Value(InValue) { } }; -// Wrapper struct for a FGameplayTag that will generate and link to a Data Pin with its same name +/** + * Wrapper struct for a FGameplayTag that will generate and link to a Data Pin with its same name. + */ USTRUCT(BlueprintType, DisplayName = "GameplayTag - Output Flow Data Pin Property", meta = (FlowPinType = "GameplayTag", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_GameplayTag : public FFlowDataPinProperty { GENERATED_BODY() public: - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) FGameplayTag Value; public: - FFlowDataPinOutputProperty_GameplayTag() {} - FFlowDataPinOutputProperty_GameplayTag(const FGameplayTag& InValue) : Value(InValue) { } + explicit FFlowDataPinOutputProperty_GameplayTag(const FGameplayTag& InValue) : Value(InValue) { } }; -// Wrapper struct for a FGameplayTagContainer that will generate and link to a Data Pin with its same name +/** + * Wrapper struct for a FGameplayTagContainer that will generate and link to a Data Pin with its same name. + */ USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] GameplayTagContainer - Output Flow DataPin Property", meta = (FlowPinType = "GameplayTagContainer", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_GameplayTagContainer : public FFlowDataPinProperty { GENERATED_BODY() public: - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) FGameplayTagContainer Value; public: - FFlowDataPinOutputProperty_GameplayTagContainer() {} - FFlowDataPinOutputProperty_GameplayTagContainer(const FGameplayTagContainer& InValue) : Value(InValue) { } + explicit FFlowDataPinOutputProperty_GameplayTagContainer(const FGameplayTagContainer& InValue) : Value(InValue) { } }; -// Wrapper struct for a FInstancedStruct that will generate and link to a Data Pin with its same name +/** + * Wrapper struct for a FInstancedStruct that will generate and link to a Data Pin with its same name. + */ USTRUCT(BlueprintType, DisplayName = "InstancedStruct - Output Flow DataPin Property", meta = (FlowPinType = "InstancedStruct", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_InstancedStruct : public FFlowDataPinProperty { GENERATED_BODY() public: - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) FInstancedStruct Value; public: - FFlowDataPinOutputProperty_InstancedStruct() {} - FFlowDataPinOutputProperty_InstancedStruct(const FInstancedStruct& InValue) : Value(InValue) { } + explicit FFlowDataPinOutputProperty_InstancedStruct(const FInstancedStruct& InValue) : Value(InValue) { } }; -// Wrapper struct for a UObject that will generate and link to a Data Pin with its same name +/** + * Wrapper struct for a UObject that will generate and link to a Data Pin with its same name. + */ USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Object - Output Flow DataPin Property", meta = (FlowPinType = "Object", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_Object : public FFlowDataPinProperty { @@ -302,32 +309,34 @@ struct FFlowDataPinOutputProperty_Object : public FFlowDataPinProperty friend class FFlowDataPinProperty_ObjectCustomizationBase; public: + /** + * These pointers are separate so that the default value for the object can be configured + * in the editor according to the type of object that it is (instanced or not). + */ - // These pointers are separate so that the default value for the object can be configured - // in the editor according to the type of object that it is (instanced or not). - - // Object reference if the object is a non-instanced UObject type (ie, not EditInlineNew) + /* Object reference if the object is a non-instanced UObject type (ie, not EditInlineNew). */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins, DisplayName = "Value (reference)", meta = (EditCondition = "InlineValue == nullptr")) TObjectPtr ReferenceValue = nullptr; - // Ofject reference if the object is an instanced UObject type (ie, EditInlineNew) + /* Object reference if the object is an instanced UObject type (ie, EditInlineNew). */ UPROPERTY(EditAnywhere, Instanced, BlueprintReadWrite, Category = DataPins, DisplayName = "Value (inline)", meta = (EditCondition = "ReferenceValue == nullptr")) TObjectPtr InlineValue = nullptr; #if WITH_EDITORONLY_DATA UPROPERTY(EditAnywhere, Category = DataPins, meta = (AllowAbstract)) TObjectPtr ClassFilter = UObject::StaticClass(); -#endif // WITH_EDITORONLY_DATA +#endif public: - FFlowDataPinOutputProperty_Object() {} - FLOW_API FFlowDataPinOutputProperty_Object(UObject* InValue, UClass* InClassFilter = nullptr); + FLOW_API explicit FFlowDataPinOutputProperty_Object(UObject* InValue, UClass* InClassFilter = nullptr); UObject* GetObjectValue() const { return ReferenceValue ? ReferenceValue : InlineValue; } }; -// Wrapper struct for a UClass that will generate and link to a Data Pin with its same name +/** + * Wrapper struct for a UClass that will generate and link to a Data Pin with its same name. + */ USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Class - Output Flow DataPin Property", meta = (FlowPinType = "Class", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinOutputProperty_Class : public FFlowDataPinProperty { @@ -336,19 +345,18 @@ struct FFlowDataPinOutputProperty_Class : public FFlowDataPinProperty friend class FFlowDataPinProperty_ClassCustomizationBase; public: - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins) FSoftClassPath Value; #if WITH_EDITORONLY_DATA UPROPERTY(EditAnywhere, Category = DataPins, meta = (AllowAbstract)) TObjectPtr ClassFilter = UObject::StaticClass(); -#endif // WITH_EDITORONLY_DATA +#endif public: - FFlowDataPinOutputProperty_Class() {} - FFlowDataPinOutputProperty_Class(const FSoftClassPath& InValue, UClass* InClassFilter = nullptr) + + explicit FFlowDataPinOutputProperty_Class(const FSoftClassPath& InValue, UClass* InClassFilter = nullptr) : Value(InValue) #if WITH_EDITOR , ClassFilter(InClassFilter) @@ -359,16 +367,18 @@ struct FFlowDataPinOutputProperty_Class : public FFlowDataPinProperty UClass* GetObjectValue() const { return Value.ResolveClass(); } }; -// Wrapper-structs for a blueprint defaulted input pin types -// "Hidden" to keep them out of the TInstancedStruct selection list (but they can still be authored as properties in blueprint) -// "DefaultForInputFlowPin" to change them to a Defaulted-Input property (rather than an output property) +/** + * Wrapper-structs for a blueprint defaulted input pin types. + * "Hidden" to keep them out of the TInstancedStruct selection list (but they can still be authored as properties in blueprint) + * "DefaultForInputFlowPin" to change them to a Defaulted-Input property (rather than an output property) + */ USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Bool - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Bool", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) struct FFlowDataPinInputProperty_Bool : public FFlowDataPinOutputProperty_Bool { GENERATED_BODY() - FFlowDataPinInputProperty_Bool(bool InValue = false) : Super(InValue) { } + explicit FFlowDataPinInputProperty_Bool(const bool InValue = false) : Super(InValue) { } }; USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Int64 - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Int64", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) @@ -376,7 +386,7 @@ struct FFlowDataPinInputProperty_Int64 : public FFlowDataPinOutputProperty_Int64 { GENERATED_BODY() - FFlowDataPinInputProperty_Int64(int64 InValue = 0) : Super(InValue) { } + explicit FFlowDataPinInputProperty_Int64(const int64 InValue = 0) : Super(InValue) { } }; USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Int - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Int", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) @@ -384,7 +394,7 @@ struct FFlowDataPinInputProperty_Int32 : public FFlowDataPinOutputProperty_Int32 { GENERATED_BODY() - FFlowDataPinInputProperty_Int32(int32 InValue = 0) : Super(InValue) { } + explicit FFlowDataPinInputProperty_Int32(const int32 InValue = 0) : Super(InValue) { } }; USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Double (float64) - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Double", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) @@ -392,7 +402,7 @@ struct FFlowDataPinInputProperty_Double : public FFlowDataPinOutputProperty_Doub { GENERATED_BODY() - FFlowDataPinInputProperty_Double(double InValue = 0.0) : Super(InValue) { } + explicit FFlowDataPinInputProperty_Double(const double InValue = 0.0) : Super(InValue) { } }; USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Float - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Float", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) @@ -400,7 +410,7 @@ struct FFlowDataPinInputProperty_Float : public FFlowDataPinOutputProperty_Float { GENERATED_BODY() - FFlowDataPinInputProperty_Float(float InValue = 0.0f) : Super(InValue) { } + explicit FFlowDataPinInputProperty_Float(const float InValue = 0.0f) : Super(InValue) { } }; USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Name - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Name", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) @@ -409,7 +419,7 @@ struct FFlowDataPinInputProperty_Name : public FFlowDataPinOutputProperty_Name GENERATED_BODY() FFlowDataPinInputProperty_Name() : Super() { } - FFlowDataPinInputProperty_Name(const FName& InValue) : Super(InValue) { } + explicit FFlowDataPinInputProperty_Name(const FName& InValue) : Super(InValue) { } }; USTRUCT(BlueprintType, DisplayName = "String - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "String", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) @@ -418,7 +428,7 @@ struct FFlowDataPinInputProperty_String : public FFlowDataPinOutputProperty_Stri GENERATED_BODY() FFlowDataPinInputProperty_String() : Super() { } - FFlowDataPinInputProperty_String(const FString& InValue) : Super(InValue) { } + explicit FFlowDataPinInputProperty_String(const FString& InValue) : Super(InValue) { } }; USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Text - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Text", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) @@ -427,7 +437,7 @@ struct FFlowDataPinInputProperty_Text : public FFlowDataPinOutputProperty_Text GENERATED_BODY() FFlowDataPinInputProperty_Text() : Super() { } - FFlowDataPinInputProperty_Text(const FText& InValue) : Super(InValue) { } + explicit FFlowDataPinInputProperty_Text(const FText& InValue) : Super(InValue) { } }; USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Enum - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Enum", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) @@ -445,7 +455,7 @@ struct FFlowDataPinInputProperty_Vector : public FFlowDataPinOutputProperty_Vect GENERATED_BODY() FFlowDataPinInputProperty_Vector() : Super() { } - FFlowDataPinInputProperty_Vector(const FVector& InValue) : Super(InValue) { } + explicit FFlowDataPinInputProperty_Vector(const FVector& InValue) : Super(InValue) { } }; USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Rotator - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Rotator", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) @@ -454,7 +464,7 @@ struct FFlowDataPinInputProperty_Rotator : public FFlowDataPinOutputProperty_Rot GENERATED_BODY() FFlowDataPinInputProperty_Rotator() : Super() { } - FFlowDataPinInputProperty_Rotator(const FRotator& InValue) : Super(InValue) { } + explicit FFlowDataPinInputProperty_Rotator(const FRotator& InValue) : Super(InValue) { } }; USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Transform - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Transform", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) @@ -463,7 +473,7 @@ struct FFlowDataPinInputProperty_Transform : public FFlowDataPinOutputProperty_T GENERATED_BODY() FFlowDataPinInputProperty_Transform() : Super() { } - FFlowDataPinInputProperty_Transform(const FTransform& InValue) : Super(InValue) { } + explicit FFlowDataPinInputProperty_Transform(const FTransform& InValue) : Super(InValue) { } }; USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] GameplayTag - Input Flow Data Pin Property", meta = (DefaultForInputFlowPin, FlowPinType = "GameplayTag", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) @@ -472,7 +482,7 @@ struct FFlowDataPinInputProperty_GameplayTag : public FFlowDataPinOutputProperty GENERATED_BODY() FFlowDataPinInputProperty_GameplayTag() : Super() { } - FFlowDataPinInputProperty_GameplayTag(const FGameplayTag& InValue) : Super(InValue) { } + explicit FFlowDataPinInputProperty_GameplayTag(const FGameplayTag& InValue) : Super(InValue) { } }; USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] GameplayTagContainer - Input Flow DataPin Property", meta = (DefaultForInputFlowPin, FlowPinType = "GameplayTagContainer", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) @@ -481,7 +491,7 @@ struct FFlowDataPinInputProperty_GameplayTagContainer : public FFlowDataPinOutpu GENERATED_BODY() FFlowDataPinInputProperty_GameplayTagContainer() : Super() { } - FFlowDataPinInputProperty_GameplayTagContainer(const FGameplayTagContainer& InValue) : Super(InValue) { } + explicit FFlowDataPinInputProperty_GameplayTagContainer(const FGameplayTagContainer& InValue) : Super(InValue) { } }; USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] InstancedStruct - Input Flow DataPin Property", meta = (DefaultForInputFlowPin, FlowPinType = "InstancedStruct", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) @@ -490,7 +500,7 @@ struct FFlowDataPinInputProperty_InstancedStruct : public FFlowDataPinOutputProp GENERATED_BODY() FFlowDataPinInputProperty_InstancedStruct() : Super() { } - FFlowDataPinInputProperty_InstancedStruct(const FInstancedStruct& InValue) : Super(InValue) { } + explicit FFlowDataPinInputProperty_InstancedStruct(const FInstancedStruct& InValue) : Super(InValue) { } }; USTRUCT(BlueprintType, DisplayName = "[DEPRECATED] Object - Input Flow DataPin Property", meta = (DefaultForInputFlowPin, FlowPinType = "Object", Deprecated, DeprecationMessage = "Use FFlowDataPinValue* instead")) diff --git a/Source/Flow/Public/Types/FlowDataPinPropertyToValueMigration.h b/Source/Flow/Public/Types/FlowDataPinPropertyToValueMigration.h index 4233d2d83..636c3235c 100644 --- a/Source/Flow/Public/Types/FlowDataPinPropertyToValueMigration.h +++ b/Source/Flow/Public/Types/FlowDataPinPropertyToValueMigration.h @@ -1,16 +1,12 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once -#include "Types/FlowDataPinValuesStandard.h" - // #FlowDataPinLegacy -#include "Types/FlowDataPinProperties.h" -// -- -// #FlowDataPinLegacy +#include "Types/FlowDataPinValuesStandard.h" +#include "Types/FlowDataPinProperties.h" -// Templated helper to migrate simple types (scalar Value to TArray Values) +/* Templated helper to migrate simple types (scalar Value to TArray Values). */ template static bool MigrateSimpleType(const TInstancedStruct& Source, TInstancedStruct& Target) { @@ -30,9 +26,9 @@ static bool MigrateSimpleType(const TInstancedStruct& Sour return false; } -// Specialization for FGameplayTagContainer +/* Specialization for FGameplayTagContainer. */ template <> -bool MigrateSimpleType(const TInstancedStruct& Source, TInstancedStruct& Target) +inline bool MigrateSimpleType(const TInstancedStruct& Source, TInstancedStruct& Target) { if (!Source.IsValid() || !Source.GetPtr()) { @@ -50,14 +46,9 @@ bool MigrateSimpleType -bool MigrateSimpleType< - FFlowDataPinInputProperty_GameplayTagContainer, - FFlowDataPinValue_GameplayTagContainer, - FGameplayTagContainer>( - const TInstancedStruct& Source, - TInstancedStruct& Target) +inline bool MigrateSimpleType(const TInstancedStruct& Source, TInstancedStruct& Target) { if (!Source.IsValid() || !Source.GetPtr()) { @@ -79,9 +70,9 @@ bool MigrateSimpleType< return false; } -// Specialization for Enum (handles Value and EnumClass) +/* Specialization for Enum (handles Value and EnumClass). */ template <> -bool MigrateSimpleType(const TInstancedStruct& Source, TInstancedStruct& Target) +inline bool MigrateSimpleType(const TInstancedStruct& Source, TInstancedStruct& Target) { if (!Source.IsValid() || !Source.GetPtr()) { @@ -103,9 +94,9 @@ bool MigrateSimpleType -bool MigrateSimpleType(const TInstancedStruct& Source, TInstancedStruct& Target) +inline bool MigrateSimpleType(const TInstancedStruct& Source, TInstancedStruct& Target) { if (!Source.IsValid() || !Source.GetPtr()) { @@ -127,9 +118,9 @@ bool MigrateSimpleType -bool MigrateSimpleType(const TInstancedStruct& Source, TInstancedStruct& Target) +inline bool MigrateSimpleType(const TInstancedStruct& Source, TInstancedStruct& Target) { if (!Source.IsValid() || !Source.GetPtr()) { @@ -154,9 +145,9 @@ bool MigrateSimpleType -bool MigrateSimpleType(const TInstancedStruct& Source, TInstancedStruct& Target) +inline bool MigrateSimpleType(const TInstancedStruct& Source, TInstancedStruct& Target) { if (!Source.IsValid() || !Source.GetPtr()) { @@ -181,9 +172,9 @@ bool MigrateSimpleType -bool MigrateSimpleType(const TInstancedStruct& Source, TInstancedStruct& Target) +inline bool MigrateSimpleType(const TInstancedStruct& Source, TInstancedStruct& Target) { if (!Source.IsValid() || !Source.GetPtr()) { @@ -204,9 +195,9 @@ bool MigrateSimpleType -bool MigrateSimpleType(const TInstancedStruct& Source, TInstancedStruct& Target) +inline bool MigrateSimpleType(const TInstancedStruct& Source, TInstancedStruct& Target) { if (!Source.IsValid() || !Source.GetPtr()) { @@ -406,4 +397,5 @@ bool FFlowNamedDataPinProperty::FixupDataPinProperty() return bSuccess; } + // -- diff --git a/Source/Flow/Public/Types/FlowDataPinResults.h b/Source/Flow/Public/Types/FlowDataPinResults.h index 9b442604d..0ec8364bf 100644 --- a/Source/Flow/Public/Types/FlowDataPinResults.h +++ b/Source/Flow/Public/Types/FlowDataPinResults.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "GameplayTagContainer.h" @@ -22,8 +21,7 @@ struct FFlowDataPinResult GENERATED_BODY() public: - - // Result for the DataPin resolve attempt + /* Result for the DataPin resolve attempt. */ UPROPERTY(BlueprintReadWrite, Category = DataPins) EFlowDataPinResolveResult Result = EFlowDataPinResolveResult::FailedUnimplemented; @@ -47,15 +45,13 @@ struct FFlowDataPinResult_Bool : public FFlowDataPinResult GENERATED_BODY() public: - UPROPERTY(BlueprintReadWrite, Category = DataPins) bool Value = false; public: - FLOW_API FFlowDataPinResult_Bool() { } - FLOW_API FFlowDataPinResult_Bool(EFlowDataPinResolveResult InResult) : Super(InResult) { } - FLOW_API FFlowDataPinResult_Bool(bool InValue) + FLOW_API explicit FFlowDataPinResult_Bool(const EFlowDataPinResolveResult InResult) : Super(InResult) { } + FLOW_API explicit FFlowDataPinResult_Bool(const bool InValue) : Super(EFlowDataPinResolveResult::Success) , Value(InValue) { } @@ -74,8 +70,8 @@ struct FFlowDataPinResult_Int : public FFlowDataPinResult public: FLOW_API FFlowDataPinResult_Int() { } - FLOW_API FFlowDataPinResult_Int(EFlowDataPinResolveResult InResult) : Super(InResult) { } - FLOW_API FFlowDataPinResult_Int(int64 InValue) + FLOW_API explicit FFlowDataPinResult_Int(const EFlowDataPinResolveResult InResult) : Super(InResult) { } + FLOW_API explicit FFlowDataPinResult_Int(const int64 InValue) : Super(EFlowDataPinResolveResult::Success) , Value(InValue) { } @@ -94,8 +90,8 @@ struct FFlowDataPinResult_Float : public FFlowDataPinResult public: FLOW_API FFlowDataPinResult_Float() { } - FLOW_API FFlowDataPinResult_Float(EFlowDataPinResolveResult InResult) : Super(InResult) { } - FLOW_API FFlowDataPinResult_Float(double InValue) + FLOW_API explicit FFlowDataPinResult_Float(const EFlowDataPinResolveResult InResult) : Super(InResult) { } + FLOW_API explicit FFlowDataPinResult_Float(const double InValue) : Super(EFlowDataPinResolveResult::Success) , Value(InValue) { } @@ -107,15 +103,13 @@ struct FFlowDataPinResult_Name : public FFlowDataPinResult GENERATED_BODY() public: - UPROPERTY(BlueprintReadWrite, Category = DataPins) FName Value = NAME_None; public: - FLOW_API FFlowDataPinResult_Name() { } - FLOW_API FFlowDataPinResult_Name(EFlowDataPinResolveResult InResult) : Super(InResult) { } - FLOW_API FFlowDataPinResult_Name(const FName& InValue) + FLOW_API explicit FFlowDataPinResult_Name(const EFlowDataPinResolveResult InResult) : Super(InResult) { } + FLOW_API explicit FFlowDataPinResult_Name(const FName& InValue) : Super(EFlowDataPinResolveResult::Success) , Value(InValue) { } @@ -131,15 +125,13 @@ struct FFlowDataPinResult_String : public FFlowDataPinResult GENERATED_BODY() public: - UPROPERTY(BlueprintReadWrite, Category = DataPins) FString Value; public: - FLOW_API FFlowDataPinResult_String() { } - FLOW_API FFlowDataPinResult_String(EFlowDataPinResolveResult InResult) : Super(InResult) { } - FLOW_API FFlowDataPinResult_String(const FString& InValue) + FLOW_API explicit FFlowDataPinResult_String(const EFlowDataPinResolveResult InResult) : Super(InResult) { } + FLOW_API explicit FFlowDataPinResult_String(const FString& InValue) : Super(EFlowDataPinResolveResult::Success) , Value(InValue) { } @@ -155,15 +147,13 @@ struct FFlowDataPinResult_Text : public FFlowDataPinResult GENERATED_BODY() public: - UPROPERTY(BlueprintReadWrite, Category = DataPins) FText Value; public: - FLOW_API FFlowDataPinResult_Text() { } - FLOW_API FFlowDataPinResult_Text(EFlowDataPinResolveResult InResult) : Super(InResult) { } - FLOW_API FFlowDataPinResult_Text(const FText& InValue) + FLOW_API explicit FFlowDataPinResult_Text(const EFlowDataPinResolveResult InResult) : Super(InResult) { } + FLOW_API explicit FFlowDataPinResult_Text(const FText& InValue) : Super(EFlowDataPinResolveResult::Success) , Value(InValue) { } @@ -179,25 +169,23 @@ struct FFlowDataPinResult_Enum : public FFlowDataPinResult GENERATED_BODY() public: - - // The selected enum Value + /* The selected enum Value. */ UPROPERTY(BlueprintReadWrite, Category = DataPins) FName Value = NAME_None; - // Class for this enum + /* Class for this enum. */ UPROPERTY(BlueprintReadWrite, Category = DataPins) TObjectPtr EnumClass = nullptr; public: - FLOW_API FFlowDataPinResult_Enum() { } - FLOW_API FFlowDataPinResult_Enum(EFlowDataPinResolveResult InResult) : Super(InResult) { } + FLOW_API explicit FFlowDataPinResult_Enum(const EFlowDataPinResolveResult InResult) : Super(InResult) { } FLOW_API FFlowDataPinResult_Enum(const FName& InValue, UEnum* InEnumClass) : Super(EFlowDataPinResolveResult::Success) , Value(InValue) , EnumClass(InEnumClass) { } - FLOW_API explicit FFlowDataPinResult_Enum(uint8 InEnumAsIntValue, UEnum& InEnumClass) + FLOW_API explicit FFlowDataPinResult_Enum(const uint8 InEnumAsIntValue, UEnum& InEnumClass) : Super(EFlowDataPinResolveResult::Success) , Value() , EnumClass(&InEnumClass) @@ -231,7 +219,7 @@ struct FFlowDataPinResult_Enum : public FFlowDataPinResult } template - TUnrealNativeEnumType GetNativeEnumValue(EGetByNameFlags GetByNameFlags = EGetByNameFlags::None) const + TUnrealNativeEnumType GetNativeEnumValue(const EGetByNameFlags GetByNameFlags = EGetByNameFlags::None) const { if (!IsValid(EnumClass)) { @@ -261,8 +249,8 @@ struct FFlowDataPinResult_Vector : public FFlowDataPinResult public: FLOW_API FFlowDataPinResult_Vector() { } - FLOW_API FFlowDataPinResult_Vector(EFlowDataPinResolveResult InResult) : Super(InResult) { } - FLOW_API FFlowDataPinResult_Vector(const FVector& InValue) + FLOW_API explicit FFlowDataPinResult_Vector(const EFlowDataPinResolveResult InResult) : Super(InResult) { } + FLOW_API explicit FFlowDataPinResult_Vector(const FVector& InValue) : Super(EFlowDataPinResolveResult::Success) , Value(InValue) { } @@ -274,15 +262,13 @@ struct FFlowDataPinResult_Rotator : public FFlowDataPinResult GENERATED_BODY() public: - UPROPERTY(BlueprintReadWrite, Category = DataPins) FRotator Value = FRotator::ZeroRotator; public: - FLOW_API FFlowDataPinResult_Rotator() { } - FLOW_API FFlowDataPinResult_Rotator(EFlowDataPinResolveResult InResult) : Super(InResult) { } - FLOW_API FFlowDataPinResult_Rotator(const FRotator& InValue) + FLOW_API explicit FFlowDataPinResult_Rotator(const EFlowDataPinResolveResult InResult) : Super(InResult) { } + FLOW_API explicit FFlowDataPinResult_Rotator(const FRotator& InValue) : Super(EFlowDataPinResolveResult::Success) , Value(InValue) { } @@ -294,15 +280,13 @@ struct FFlowDataPinResult_Transform : public FFlowDataPinResult GENERATED_BODY() public: - UPROPERTY(BlueprintReadWrite, Category = DataPins) FTransform Value; public: - FLOW_API FFlowDataPinResult_Transform() { } - FLOW_API FFlowDataPinResult_Transform(EFlowDataPinResolveResult InResult) : Super(InResult) { } - FLOW_API FFlowDataPinResult_Transform(const FTransform& InValue) + FLOW_API explicit FFlowDataPinResult_Transform(const EFlowDataPinResolveResult InResult) : Super(InResult) { } + FLOW_API explicit FFlowDataPinResult_Transform(const FTransform& InValue) : Super(EFlowDataPinResolveResult::Success) , Value(InValue) { } @@ -314,15 +298,13 @@ struct FFlowDataPinResult_GameplayTag : public FFlowDataPinResult GENERATED_BODY() public: - UPROPERTY(BlueprintReadWrite, Category = DataPins) FGameplayTag Value; public: - FLOW_API FFlowDataPinResult_GameplayTag() { } - FLOW_API FFlowDataPinResult_GameplayTag(EFlowDataPinResolveResult InResult) : Super(InResult) { } - FLOW_API FFlowDataPinResult_GameplayTag(const FGameplayTag& InValue) + FLOW_API explicit FFlowDataPinResult_GameplayTag(const EFlowDataPinResolveResult InResult) : Super(InResult) { } + FLOW_API explicit FFlowDataPinResult_GameplayTag(const FGameplayTag& InValue) : Super(EFlowDataPinResolveResult::Success) , Value(InValue) { } @@ -334,15 +316,13 @@ struct FFlowDataPinResult_GameplayTagContainer : public FFlowDataPinResult GENERATED_BODY() public: - UPROPERTY(BlueprintReadWrite, Category = DataPins) FGameplayTagContainer Value; public: - FLOW_API FFlowDataPinResult_GameplayTagContainer() { } - FLOW_API FFlowDataPinResult_GameplayTagContainer(EFlowDataPinResolveResult InResult) : Super(InResult) { } - FLOW_API FFlowDataPinResult_GameplayTagContainer(const FGameplayTagContainer& InValue) + FLOW_API explicit FFlowDataPinResult_GameplayTagContainer(const EFlowDataPinResolveResult InResult) : Super(InResult) { } + FLOW_API explicit FFlowDataPinResult_GameplayTagContainer(const FGameplayTagContainer& InValue) : Super(EFlowDataPinResolveResult::Success) , Value(InValue) { } @@ -354,15 +334,13 @@ struct FFlowDataPinResult_InstancedStruct : public FFlowDataPinResult GENERATED_BODY() public: - UPROPERTY(BlueprintReadWrite, Category = DataPins) FInstancedStruct Value; public: - FLOW_API FFlowDataPinResult_InstancedStruct() { } - FLOW_API FFlowDataPinResult_InstancedStruct(EFlowDataPinResolveResult InResult) : Super(InResult) { } - FLOW_API FFlowDataPinResult_InstancedStruct(const FInstancedStruct& InValue) + FLOW_API explicit FFlowDataPinResult_InstancedStruct(const EFlowDataPinResolveResult InResult) : Super(InResult) { } + FLOW_API explicit FFlowDataPinResult_InstancedStruct(const FInstancedStruct& InValue) : Super(EFlowDataPinResolveResult::Success) , Value(InValue) { } @@ -374,15 +352,13 @@ struct FFlowDataPinResult_Object : public FFlowDataPinResult GENERATED_BODY() public: - UPROPERTY(BlueprintReadWrite, Category = DataPins) TObjectPtr Value; public: - FLOW_API FFlowDataPinResult_Object() { } - FLOW_API FFlowDataPinResult_Object(EFlowDataPinResolveResult InResult) : Super(InResult) { } - FLOW_API FFlowDataPinResult_Object(UObject* InValue); + FLOW_API explicit FFlowDataPinResult_Object(const EFlowDataPinResolveResult InResult) : Super(InResult) { } + FLOW_API explicit FFlowDataPinResult_Object(UObject* InValue); FLOW_API FORCEINLINE void SetValueFromSoftPath(const FSoftObjectPath& SoftPath) { Value = SoftPath.ResolveObject(); } FLOW_API FORCEINLINE void SetValueFromObjectPtr(UObject* ObjectPtr) { Value = ObjectPtr; } @@ -394,23 +370,21 @@ struct FFlowDataPinResult_Class : public FFlowDataPinResult GENERATED_BODY() protected: - - // SoftClassPath version of the result - // (both the SoftClassPath and the UClass (if available) will be set for the result) + /* SoftClassPath version of the result. + * Both the SoftClassPath and the UClass (if available) will be set for the result. */ UPROPERTY(BlueprintReadWrite, Category = DataPins) FSoftClassPath ValuePath; - // UClass version of the result - // (both the SoftClassPath and the UClass (if available) will be set for the result) + /* UClass version of the result. + * Both the SoftClassPath and the UClass (if available) will be set for the result. */ UPROPERTY(BlueprintReadWrite, Category = DataPins) TObjectPtr ValueClass = nullptr; public: - FLOW_API FFlowDataPinResult_Class() { } - FLOW_API FFlowDataPinResult_Class(EFlowDataPinResolveResult InResult) : Super(InResult) { } - FLOW_API FFlowDataPinResult_Class(const FSoftClassPath& InValuePath); - FLOW_API FFlowDataPinResult_Class(UClass* InValueClass); + FLOW_API explicit FFlowDataPinResult_Class(const EFlowDataPinResolveResult InResult) : Super(InResult) { } + FLOW_API explicit FFlowDataPinResult_Class(const FSoftClassPath& InValuePath); + FLOW_API explicit FFlowDataPinResult_Class(UClass* InValueClass); FLOW_API void SetValueSoftClassAndClassPtr(const FSoftClassPath& SoftPath, UClass* ObjectPtr); FLOW_API void SetValueFromSoftPath(const FSoftObjectPath& SoftObjectPath); diff --git a/Source/Flow/Public/Types/FlowDataPinValue.h b/Source/Flow/Public/Types/FlowDataPinValue.h index 737222faa..0453f72b4 100644 --- a/Source/Flow/Public/Types/FlowDataPinValue.h +++ b/Source/Flow/Public/Types/FlowDataPinValue.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "UObject/NameTypes.h" @@ -22,8 +21,8 @@ struct FFlowDataPinValue friend class FFlowDataPinValueCustomization; public: - // IF a pin was created from this property, this is the cached pin name that was used - // (which can be used in UFlowDataPinBlueprintLibrary::ResolveAs... functions to lookup the correct pin by name) + /* If a pin was created from this property, this is the cached pin name that was used. + * Which can be used in UFlowDataPinBlueprintLibrary::ResolveAs... functions to lookup the correct pin by name. */ UPROPERTY(VisibleAnywhere, Category = DataPins) mutable FName PropertyPinName; @@ -42,20 +41,20 @@ struct FFlowDataPinValue FLOW_API bool IsInputPin() const { return bIsInputPin; } FLOW_API bool IsArray() const { FLOW_ASSERT_ENUM_MAX(EFlowDataMultiType, 2); return MultiType == EFlowDataMultiType::Array; } - // Helper to get the Values property handle (implemented by subclasses or via type system) + /* Helper to get the Values property handle (implemented by subclasses or via type system). */ FLOW_API virtual TSharedPtr GetValuesPropertyHandle() const PURE_VIRTUAL(GetValuesPropertyHandle, return nullptr;); #endif - // Pin Type Name (identity) + /* Pin Type Name (identity). */ FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const PURE_VIRTUAL(GetPinTypeName, return FFlowPinType::PinTypeNameUnknown;); - // (optional) Get the field type if one exists (only used for UEnum For Now) + /* (optional) Get the field type if one exists (only used for UEnum For Now). */ FLOW_API virtual UField* GetFieldType() const { return nullptr; } - // (optional) + /* (optional) */ FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const { return false; } - // Resolve the registered data pin type + /* Resolve the registered data pin type. */ FLOW_API const FFlowPinType* LookupPinType() const; FLOW_API static const FString StringArraySeparator; diff --git a/Source/Flow/Public/Types/FlowDataPinValuesStandard.h b/Source/Flow/Public/Types/FlowDataPinValuesStandard.h index ff731e65a..34f523203 100644 --- a/Source/Flow/Public/Types/FlowDataPinValuesStandard.h +++ b/Source/Flow/Public/Types/FlowDataPinValuesStandard.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Types/FlowDataPinValue.h" @@ -32,8 +31,8 @@ struct FFlowDataPinValue_Bool : public FFlowDataPinValue TArray Values{ false }; FLOW_API FFlowDataPinValue_Bool() = default; - FLOW_API FFlowDataPinValue_Bool(ValueType InValue); - FLOW_API FFlowDataPinValue_Bool(const TArray& InValues); + FLOW_API explicit FFlowDataPinValue_Bool(ValueType InValue); + FLOW_API explicit FFlowDataPinValue_Bool(const TArray& InValues); FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; @@ -55,8 +54,8 @@ struct FFlowDataPinValue_Int : public FFlowDataPinValue TArray Values{ 0 }; FLOW_API FFlowDataPinValue_Int() = default; - FLOW_API FFlowDataPinValue_Int(ValueType InValue); - FLOW_API FFlowDataPinValue_Int(const TArray& InValues); + FLOW_API explicit FFlowDataPinValue_Int(ValueType InValue); + FLOW_API explicit FFlowDataPinValue_Int(const TArray& InValues); FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; @@ -78,8 +77,8 @@ struct FFlowDataPinValue_Int64 : public FFlowDataPinValue TArray Values{ 0 }; FLOW_API FFlowDataPinValue_Int64() = default; - FLOW_API FFlowDataPinValue_Int64(ValueType InValue); - FLOW_API FFlowDataPinValue_Int64(const TArray& InValues); + FLOW_API explicit FFlowDataPinValue_Int64(ValueType InValue); + FLOW_API explicit FFlowDataPinValue_Int64(const TArray& InValues); FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; @@ -101,8 +100,8 @@ struct FFlowDataPinValue_Float : public FFlowDataPinValue TArray Values{ 0.0f }; FLOW_API FFlowDataPinValue_Float() = default; - FLOW_API FFlowDataPinValue_Float(ValueType InValue); - FLOW_API FFlowDataPinValue_Float(const TArray& InValues); + FLOW_API explicit FFlowDataPinValue_Float(ValueType InValue); + FLOW_API explicit FFlowDataPinValue_Float(const TArray& InValues); FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; @@ -124,8 +123,8 @@ struct FFlowDataPinValue_Double : public FFlowDataPinValue TArray Values{ 0.0 }; FLOW_API FFlowDataPinValue_Double() = default; - FLOW_API FFlowDataPinValue_Double(ValueType InValue); - FLOW_API FFlowDataPinValue_Double(const TArray& InValues); + FLOW_API explicit FFlowDataPinValue_Double(ValueType InValue); + FLOW_API explicit FFlowDataPinValue_Double(const TArray& InValues); FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; @@ -147,8 +146,8 @@ struct FFlowDataPinValue_Name : public FFlowDataPinValue TArray Values{ NAME_None }; FLOW_API FFlowDataPinValue_Name() = default; - FLOW_API FFlowDataPinValue_Name(const ValueType& InValue); - FLOW_API FFlowDataPinValue_Name(const TArray& InValues); + FLOW_API explicit FFlowDataPinValue_Name(const ValueType& InValue); + FLOW_API explicit FFlowDataPinValue_Name(const TArray& InValues); FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; @@ -170,8 +169,8 @@ struct FFlowDataPinValue_String : public FFlowDataPinValue TArray Values; FLOW_API FFlowDataPinValue_String() = default; - FLOW_API FFlowDataPinValue_String(const ValueType& InValue); - FLOW_API FFlowDataPinValue_String(const TArray& InValues); + FLOW_API explicit FFlowDataPinValue_String(const ValueType& InValue); + FLOW_API explicit FFlowDataPinValue_String(const TArray& InValues); FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; @@ -193,8 +192,8 @@ struct FFlowDataPinValue_Text : public FFlowDataPinValue TArray Values; FLOW_API FFlowDataPinValue_Text() = default; - FLOW_API FFlowDataPinValue_Text(const ValueType& InValue); - FLOW_API FFlowDataPinValue_Text(const TArray& InValues); + FLOW_API explicit FFlowDataPinValue_Text(const ValueType& InValue); + FLOW_API explicit FFlowDataPinValue_Text(const TArray& InValues); FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; @@ -230,7 +229,6 @@ struct FFlowDataPinValue_Enum : public FFlowDataPinValue FLOW_API FFlowDataPinValue_Enum(const TSoftObjectPtr& InEnumClass, const TArray& InValues); FLOW_API FFlowDataPinValue_Enum(UEnum& InEnumClass, const TArray& InValues); - #if WITH_EDITOR FLOW_API void OnEnumNameChanged(); #endif @@ -241,7 +239,7 @@ struct FFlowDataPinValue_Enum : public FFlowDataPinValue // Helper templates template - static bool TryGetEnumValueByName(const UEnum* EnumClass, const FName& EnumValueName, TUnrealNativeEnumType& OutValue, EGetByNameFlags GetByNameFlags = EGetByNameFlags::ErrorIfNotFound) + static bool TryGetEnumValueByName(const UEnum* EnumClass, const FName& EnumValueName, TUnrealNativeEnumType& OutValue, const EGetByNameFlags GetByNameFlags = EGetByNameFlags::ErrorIfNotFound) { if (!IsValid(EnumClass)) { @@ -258,7 +256,7 @@ struct FFlowDataPinValue_Enum : public FFlowDataPinValue } template - EFlowDataPinResolveResult TryGetSingleEnumValue(TUnrealNativeEnumType& OutEnumValue, EFlowSingleFromArray SingleFromArray, EGetByNameFlags GetByNameFlags = EGetByNameFlags::ErrorIfNotFound) const + EFlowDataPinResolveResult TryGetSingleEnumValue(TUnrealNativeEnumType& OutEnumValue, const EFlowSingleFromArray SingleFromArray, EGetByNameFlags GetByNameFlags = EGetByNameFlags::ErrorIfNotFound) const { const int32 Index = EFlowSingleFromArray_Classifiers::ConvertToIndex(SingleFromArray, Values.Num()); if (!Values.IsValidIndex(Index)) @@ -314,8 +312,8 @@ struct FFlowDataPinValue_Vector : public FFlowDataPinValue TArray Values{ FVector::ZeroVector }; FLOW_API FFlowDataPinValue_Vector() = default; - FLOW_API FFlowDataPinValue_Vector(const ValueType& InValue); - FLOW_API FFlowDataPinValue_Vector(const TArray& InValues); + FLOW_API explicit FFlowDataPinValue_Vector(const ValueType& InValue); + FLOW_API explicit FFlowDataPinValue_Vector(const TArray& InValues); FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; @@ -337,8 +335,8 @@ struct FFlowDataPinValue_Rotator : public FFlowDataPinValue TArray Values{ FRotator::ZeroRotator }; FLOW_API FFlowDataPinValue_Rotator() = default; - FLOW_API FFlowDataPinValue_Rotator(const ValueType& InValue); - FLOW_API FFlowDataPinValue_Rotator(const TArray& InValues); + FLOW_API explicit FFlowDataPinValue_Rotator(const ValueType& InValue); + FLOW_API explicit FFlowDataPinValue_Rotator(const TArray& InValues); FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; @@ -360,8 +358,8 @@ struct FFlowDataPinValue_Transform : public FFlowDataPinValue TArray Values{ FTransform::Identity }; FLOW_API FFlowDataPinValue_Transform() = default; - FLOW_API FFlowDataPinValue_Transform(const ValueType& InValue); - FLOW_API FFlowDataPinValue_Transform(const TArray& InValues); + FLOW_API explicit FFlowDataPinValue_Transform(const ValueType& InValue); + FLOW_API explicit FFlowDataPinValue_Transform(const TArray& InValues); FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; @@ -383,8 +381,8 @@ struct FFlowDataPinValue_GameplayTag : public FFlowDataPinValue TArray Values; FLOW_API FFlowDataPinValue_GameplayTag() = default; - FLOW_API FFlowDataPinValue_GameplayTag(const ValueType& InValue); - FLOW_API FFlowDataPinValue_GameplayTag(const TArray& InValues); + FLOW_API explicit FFlowDataPinValue_GameplayTag(const ValueType& InValue); + FLOW_API explicit FFlowDataPinValue_GameplayTag(const TArray& InValues); FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; @@ -406,10 +404,10 @@ struct FFlowDataPinValue_GameplayTagContainer : public FFlowDataPinValue FGameplayTagContainer Values; FLOW_API FFlowDataPinValue_GameplayTagContainer() = default; - FLOW_API FFlowDataPinValue_GameplayTagContainer(const FGameplayTag& InValue); - FLOW_API FFlowDataPinValue_GameplayTagContainer(const FGameplayTagContainer& InValues); - FLOW_API FFlowDataPinValue_GameplayTagContainer(const TArray& InValues); - FLOW_API FFlowDataPinValue_GameplayTagContainer(const TArray& InValues); + FLOW_API explicit FFlowDataPinValue_GameplayTagContainer(const FGameplayTag& InValue); + FLOW_API explicit FFlowDataPinValue_GameplayTagContainer(const FGameplayTagContainer& InValues); + FLOW_API explicit FFlowDataPinValue_GameplayTagContainer(const TArray& InValues); + FLOW_API explicit FFlowDataPinValue_GameplayTagContainer(const TArray& InValues); FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; @@ -431,8 +429,8 @@ struct FFlowDataPinValue_InstancedStruct : public FFlowDataPinValue TArray Values; FLOW_API FFlowDataPinValue_InstancedStruct() = default; - FLOW_API FFlowDataPinValue_InstancedStruct(const ValueType& InValue); - FLOW_API FFlowDataPinValue_InstancedStruct(const TArray& InValues); + FLOW_API explicit FFlowDataPinValue_InstancedStruct(const ValueType& InValue); + FLOW_API explicit FFlowDataPinValue_InstancedStruct(const TArray& InValues); FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; @@ -459,11 +457,11 @@ struct FFlowDataPinValue_Object : public FFlowDataPinValue #endif FLOW_API FFlowDataPinValue_Object() = default; - FLOW_API FFlowDataPinValue_Object(TObjectPtr InObject, UClass* InClassFilter = UObject::StaticClass()); - FLOW_API FFlowDataPinValue_Object(const TArray>& InObjects, UClass* InClassFilter = UObject::StaticClass()); - FLOW_API FFlowDataPinValue_Object(const TArray& InObjects, UClass* InClassFilter = UObject::StaticClass()); - FLOW_API FFlowDataPinValue_Object(AActor* InActor, UClass* InClassFilter = nullptr /* nullptr here defaults to AActor::StaticClass() */ ); - FLOW_API FFlowDataPinValue_Object(const TArray& InActors, UClass* InClassFilter = nullptr /* nullptr here defaults to AActor::StaticClass() */); + FLOW_API explicit FFlowDataPinValue_Object(TObjectPtr InObject, UClass* InClassFilter = UObject::StaticClass()); + FLOW_API explicit FFlowDataPinValue_Object(const TArray>& InObjects, UClass* InClassFilter = UObject::StaticClass()); + FLOW_API explicit FFlowDataPinValue_Object(const TArray& InObjects, UClass* InClassFilter = UObject::StaticClass()); + FLOW_API explicit FFlowDataPinValue_Object(AActor* InActor, UClass* InClassFilter = nullptr /* nullptr here defaults to AActor::StaticClass() */ ); + FLOW_API explicit FFlowDataPinValue_Object(const TArray& InActors, UClass* InClassFilter = nullptr /* nullptr here defaults to AActor::StaticClass() */); FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; @@ -490,10 +488,10 @@ struct FFlowDataPinValue_Class : public FFlowDataPinValue #endif FLOW_API FFlowDataPinValue_Class() = default; - FLOW_API FFlowDataPinValue_Class(const FSoftClassPath& InPath, UClass* InClassFilter = UObject::StaticClass()); - FLOW_API FFlowDataPinValue_Class(const TArray& InPaths, UClass* InClassFilter = UObject::StaticClass()); - FLOW_API FFlowDataPinValue_Class(const UClass* InClass, UClass* InClassFilter = UObject::StaticClass()); - FLOW_API FFlowDataPinValue_Class(const TArray& InClasses, UClass* InClassFilter = UObject::StaticClass()); + FLOW_API explicit FFlowDataPinValue_Class(const FSoftClassPath& InPath, UClass* InClassFilter = UObject::StaticClass()); + FLOW_API explicit FFlowDataPinValue_Class(const TArray& InPaths, UClass* InClassFilter = UObject::StaticClass()); + FLOW_API explicit FFlowDataPinValue_Class(const UClass* InClass, UClass* InClassFilter = UObject::StaticClass()); + FLOW_API explicit FFlowDataPinValue_Class(const TArray& InClasses, UClass* InClassFilter = UObject::StaticClass()); FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const override { return PinType::GetPinTypeNameStatic(); } FLOW_API virtual bool TryConvertValuesToString(FString& OutString) const override; diff --git a/Source/Flow/Public/Types/FlowEnumUtils.h b/Source/Flow/Public/Types/FlowEnumUtils.h index 079a6f1a8..87eeb209a 100644 --- a/Source/Flow/Public/Types/FlowEnumUtils.h +++ b/Source/Flow/Public/Types/FlowEnumUtils.h @@ -1,9 +1,7 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Misc/EnumRange.h" - #include // Extensions to EnumRange.h @@ -13,14 +11,14 @@ namespace FlowEnum template constexpr auto MinOf() { return 0; } template constexpr auto MaxOf() { return 0; } - // NOTE (gtaylor) In this context, a "Valid" enum value is one that is within ::Min to ::Max - 1 - // Invalid values (like ::Invalid) should fall outside of this range + /* NOTE (gtaylor) In this context, a "Valid" enum value is one that is within ::Min to ::Max - 1. + * Invalid values (like ::Invalid) should fall outside of this range. */ template constexpr bool IsValidEnumValue(const TEnum EnumValue) { return false; } - // NOTE (gtaylor) In this context, a subrange is First..Last (where last is an inclusive bound) + /* NOTE (gtaylor) In this context, a subrange is First..Last (where last is an inclusive bound). */ template constexpr bool IsEnumValueInSubrange(const TEnum EnumValue, const TEnum SubrangeFirst, const TEnum SubrangeLast) { return false; } - // Utility templates for Enums + /* Utility templates for Enums. */ template ::type> struct safe_underlying_type { using type = void; diff --git a/Source/Flow/Public/Types/FlowGameplayTagMapUtils.h b/Source/Flow/Public/Types/FlowGameplayTagMapUtils.h index 445e4d511..d2578eb3c 100644 --- a/Source/Flow/Public/Types/FlowGameplayTagMapUtils.h +++ b/Source/Flow/Public/Types/FlowGameplayTagMapUtils.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "GameplayTagContainer.h" @@ -8,11 +7,13 @@ #include "Types/FlowEnumUtils.h" #include "Types/FlowArray.h" -// NOTE (gtaylor) The choice of which EFlowGameplayTagMapExpandPolicy to use will be informed by the map's tolerance -// for memory vs. lookup performance. If speed is not a concern, then fully expanding with AllSubtags can -// make for a single-tech lookup. If memory is more of a concern, then NoExpand will store the minimal information -// in the map keys (potentially requiring multiple parent searches in TryLookupGameplayTagKey). If only the leaf tags -// will be used for lookup, then LeafSubtags expansion policy is a good option. +/** + * NOTE (gtaylor) The choice of which EFlowGameplayTagMapExpandPolicy to use will be informed by the map's tolerance + * for memory vs. lookup performance. If speed is not a concern, then fully expanding with AllSubtags can + * make for a single-tech lookup. If memory is more of a concern, then NoExpand will store the minimal information + * in the map keys (potentially requiring multiple parent searches in TryLookupGameplayTagKey). If only the leaf tags + * will be used for lookup, then LeafSubtags expansion policy is a good option. + */ UENUM() enum class EFlowGameplayTagMapExpandPolicy : int8 @@ -30,8 +31,10 @@ FLOW_ENUM_RANGE_VALUES(EFlowGameplayTagMapExpandPolicy); namespace FlowMap { - // Utility functions for utilizing FGameplayTags as a key in a TMap. - // Expected to be wrapped by the client code to hide some of the details in these function signatures. + /** + * Utility functions for utilizing FGameplayTags as a key in a TMap. + * Expected to be wrapped by the client code to hide some of the details in these function signatures. + */ template void PatchGameplayTagMap( @@ -110,8 +113,8 @@ namespace FlowMap } } - // (const) Lookup function, which works on a gameplaytag-keyed map - // it can crawl up the tag ancestry chain to allow general keys to apply to sub-tags + /* (const) Lookup function, which works on a GameplayTag-keyed map. + * It can crawl up the tag ancestry chain to allow general keys to apply to sub-tags. */ template const TPayload* TryLookupGameplayTagKey( const FGameplayTag& KeyTag, @@ -142,8 +145,8 @@ namespace FlowMap return FoundPayload; } - // (mutable) Lookup function, which works on a gameplaytag-keyed map - // it can crawl up the tag ancestry chain to allow general keys to apply to sub-tags + /* (mutable) Lookup function, which works on a GameplayTag-keyed map. + * It can crawl up the tag ancestry chain to allow general keys to apply to sub-tags. */ template TPayload* TryLookupGameplayTagKey( const FGameplayTag& KeyTag, @@ -162,7 +165,7 @@ namespace FlowMap ParentTagSearchDepthMax)); } - // Extracts the key/value pairs from a gameplaytag-keyed map into a sorted array + /* Extracts the key/value pairs from a GameplayTag-keyed map into a sorted array. */ template TArray> BuildSortedGameplayTagMapPairs(const TMap& GameplayTagToPayloadMap) { diff --git a/Source/Flow/Public/Types/FlowInjectComponentsHelper.h b/Source/Flow/Public/Types/FlowInjectComponentsHelper.h index fdfac3de4..f26b42761 100644 --- a/Source/Flow/Public/Types/FlowInjectComponentsHelper.h +++ b/Source/Flow/Public/Types/FlowInjectComponentsHelper.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "FlowInjectComponentsHelper.generated.h" @@ -7,33 +6,33 @@ class AActor; class UActorComponent; -// Configuration helper struct for injecting components onto actors +/** + * Configuration helper struct for injecting components onto actors. + */ USTRUCT() struct FFlowInjectComponentsHelper { GENERATED_BODY() public: - FLOW_API TArray CreateComponentInstancesForActor(AActor& Actor); - // Static functions to create a component for injection: + /* Static functions to create a component for injection. */ static FLOW_API UActorComponent* TryCreateComponentInstanceForActorFromTemplate(AActor& Actor, UActorComponent& ComponentTemplate); static FLOW_API UActorComponent* TryCreateComponentInstanceForActorFromClass(AActor& Actor, TSubclassOf ComponentClass, const FName& InstanceBaseName); - // After creating using one of the above two functions, inject into the actor: + /* After creating using one of the above two functions, inject into the actor. */ static FLOW_API void InjectCreatedComponent(AActor& Actor, UActorComponent& ComponentInstance); - // Remove & Destroy the injected component: + /* Remove & Destroy the injected component. */ static FLOW_API void DestroyInjectedComponent(AActor& Actor, UActorComponent& ComponentInstance); public: - - // Component (template) to inject on the spawned actor + /* Component (template) to inject on the spawned actor. */ UPROPERTY(EditAnywhere, Instanced, Category = Configuration) TArray> ComponentTemplates; - // Component (template) to inject on the spawned actor + /* Component (template) to inject on the spawned actor. */ UPROPERTY(EditAnywhere, Category = Configuration) TArray> ComponentClasses; }; diff --git a/Source/Flow/Public/Types/FlowInjectComponentsManager.h b/Source/Flow/Public/Types/FlowInjectComponentsManager.h index a70b788ac..50422c695 100644 --- a/Source/Flow/Public/Types/FlowInjectComponentsManager.h +++ b/Source/Flow/Public/Types/FlowInjectComponentsManager.h @@ -1,34 +1,34 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "UObject/Object.h" - #include "FlowInjectComponentsManager.generated.h" class UActorComponent; class UFlowNodeBase; -// Container for injected component instances +/** + * Container for injected component instances + */ USTRUCT() struct FLOW_API FFlowComponentInstances { GENERATED_BODY() public: - UPROPERTY(Transient) TArray> Components; }; -// Inject components onto actors and will remove them when they are destroyed (or this is shutdown) +/** + * Inject components onto actors and will remove them when they are destroyed (or this is shutdown). + */ UCLASS(MinimalAPI) class UFlowInjectComponentsManager : public UObject { GENERATED_BODY() public: - FLOW_API void InitializeRuntime(); FLOW_API void ShutdownRuntime(); @@ -38,7 +38,6 @@ class UFlowInjectComponentsManager : public UObject FLOW_API void RemoveAllInjectedComponentsAndStopMonitoringActor(AActor& Actor); protected: - FLOW_API void AddAndRegisterComponent(AActor& Actor, UActorComponent& ComponentInstance); FLOW_API void RemoveAndUnregisterComponent(AActor& Actor, UActorComponent& ComponentInstance); @@ -51,17 +50,16 @@ class UFlowInjectComponentsManager : public UObject FLOW_API void OnActorDestroyed(AActor* DestroyedActor); public: - DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FFlowBeforeOnActorRemoved, AActor*, SpawnedActor); UPROPERTY(BlueprintAssignable) FFlowBeforeOnActorRemoved BeforeActorRemovedDelegate; - // Remove the Injected Components from the Actors when Deinitialized + /* Remove the Injected Components from the Actors when Deinitialized. */ UPROPERTY() bool bRemoveInjectedComponentsWhenDeinitializing = true; - // Map of spawned components (if we are cleaning up) + /* Map of spawned components (if we are cleaning up). */ UPROPERTY(Transient) TMap, FFlowComponentInstances> ActorToComponentsMap; }; diff --git a/Source/Flow/Public/Types/FlowNamedDataPinProperty.h b/Source/Flow/Public/Types/FlowNamedDataPinProperty.h index 939b67fa1..607c048a5 100644 --- a/Source/Flow/Public/Types/FlowNamedDataPinProperty.h +++ b/Source/Flow/Public/Types/FlowNamedDataPinProperty.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "StructUtils/InstancedStruct.h" @@ -12,42 +11,44 @@ struct FFlowDataPinProperty; struct FFlowDataPinValue; -// Wrapper for FFlowDataPinProperty that is used for flow nodes that add -// dynamic properties, with associated data pins, on the flow node instance -// (as opposed to C++ or blueprint compile-time). +/** + * Wrapper for FFlowDataPinProperty that is used for flow nodes that add dynamic properties, + * with associated data pins, on the flow node instance. + * (as opposed to C++ or blueprint compile-time). + */ USTRUCT(BlueprintType, DisplayName = "Flow Named DataPin Property") struct FFlowNamedDataPinProperty { GENERATED_BODY() public: - // Name of this instanced property + /* Name of this instanced property. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DataPins, meta = (EditCondition = "bMayChangeNameAndType", HideEditConditionToggle)) FName Name = NAME_None; private: - // DataPinProperty payload + /* DataPinProperty payload. */ UPROPERTY(VisibleAnywhere, Category = DataPins, meta = (DeprecatedProperty)) TInstancedStruct DataPinProperty; public: - // DataPinProperty payload + /* DataPinProperty payload. */ UPROPERTY(EditAnywhere, Category = DataPins, meta = (ExcludeBaseStruct, NoClear)) TInstancedStruct DataPinValue; #if WITH_EDITORONLY_DATA - // Unique identifier for property tracking + /* Unique identifier for property tracking. */ UPROPERTY() FGuid Guid = FGuid::NewGuid(); - // Tracks if this property overrides its super (auto-clears if matches super) + /* Tracks if this property overrides its super (auto-clears if matches super). */ UPROPERTY() bool bIsOverride = false; - // TODO (gtaylor) Does not currently police the type, - // because that prevents the instanced struct contents being edited as well, - // which is not what we want from this feature. - // Will try to fix next pass on the details customization. + /* TODO (gtaylor) Does not currently police the type, + * because that prevents the instanced struct contents being edited as well, + * which is not what we want from this feature. + * Will try to fix next pass on the details customization. */ UPROPERTY() bool bMayChangeNameAndType = true; #endif @@ -66,7 +67,7 @@ struct FFlowNamedDataPinProperty FLOW_API FText BuildHeaderText() const; - void ConfigureForFlowAssetParams() + void ConfigureForFlowAssetParams() { bIsOverride = false; bMayChangeNameAndType = false; @@ -85,6 +86,7 @@ struct FFlowNamedDataPinProperty Property.ConfigureForFlowAssetParams(); } } + static void ConfigurePropertiesForFlowAssetStartNode(TArray& MutableProperties) { for (FFlowNamedDataPinProperty& Property : MutableProperties) @@ -95,4 +97,3 @@ struct FFlowNamedDataPinProperty #endif }; - diff --git a/Source/Flow/Public/Types/FlowPinEnums.h b/Source/Flow/Public/Types/FlowPinEnums.h index 8998bb7da..d0a31bb43 100644 --- a/Source/Flow/Public/Types/FlowPinEnums.h +++ b/Source/Flow/Public/Types/FlowPinEnums.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Types/FlowEnumUtils.h" @@ -63,7 +62,9 @@ enum class EFlowPinType : uint8 }; FLOW_ENUM_RANGE_VALUES(EFlowPinType) -// Result enum for TryResolveDataPin() +/** + * Result enum for TryResolveDataPin() + */ UENUM(BlueprintType) enum class EFlowDataPinResolveResult : uint8 { @@ -114,7 +115,7 @@ FLOW_ENUM_RANGE_VALUES(EFlowDataPinResolveSimpleResult) namespace EFlowDataPinResolveResult_Classifiers { - FORCEINLINE bool IsSuccess(EFlowDataPinResolveResult Result) { return Result == EFlowDataPinResolveResult::Success; } + FORCEINLINE bool IsSuccess(const EFlowDataPinResolveResult Result) { return Result == EFlowDataPinResolveResult::Success; } FORCEINLINE EFlowDataPinResolveSimpleResult ConvertToSimpleResult(EFlowDataPinResolveResult ResultEnum) { return IsSuccess(ResultEnum) ? EFlowDataPinResolveSimpleResult::Succeeded : EFlowDataPinResolveSimpleResult::Failed; } }; diff --git a/Source/Flow/Public/Types/FlowPinType.h b/Source/Flow/Public/Types/FlowPinType.h index 03f217702..8ab9a5045 100644 --- a/Source/Flow/Public/Types/FlowPinType.h +++ b/Source/Flow/Public/Types/FlowPinType.h @@ -1,12 +1,11 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once -#include "FlowPinEnums.h" - #include "Math/Color.h" #include "UObject/NameTypes.h" +#include "FlowPinEnums.h" + #if WITH_EDITOR #include "GraphEditorSettings.h" #endif @@ -31,24 +30,24 @@ struct FFlowPinType public: virtual ~FFlowPinType() {} - // Lookup a registered type by name + /* Lookup a registered type by name. */ FLOW_API static const FFlowPinType* LookupPinType(const FFlowPinTypeName& FlowPinTypeName); - // Identity + /* Identity. */ FLOW_API virtual const FFlowPinTypeName& GetPinTypeName() const PURE_VIRTUAL(GetPinTypeName, return PinTypeNameUnknown;); - // Value resolution + /* Value resolution. */ FLOW_API virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const; FLOW_API virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const; #if WITH_EDITOR - // Editor visualization + /* Editor visualization. */ FLOW_API virtual FLinearColor GetPinColor() const { return GetDefault()->DefaultPinTypeColor; } FLOW_API virtual TSharedPtr GetValuesHandle(const TSharedRef& FlowDataPinValuePropertyHandle) const; FLOW_API virtual bool SupportsMultiType(EFlowDataMultiType Mode) const { return true; } FLOW_API virtual UObject* GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const { return nullptr; } - // Pin creation + /* Pin creation. */ FLOW_API FFlowPin CreateFlowPinFromProperty(const FProperty& Property, void const* InContainer) const; FLOW_API FFlowPin CreateFlowPinFromValueWrapper(const FName& PinName, const FFlowDataPinValue& Wrapper) const; #endif diff --git a/Source/Flow/Public/Types/FlowPinTypeName.h b/Source/Flow/Public/Types/FlowPinTypeName.h index ace66576e..b71e7a287 100644 --- a/Source/Flow/Public/Types/FlowPinTypeName.h +++ b/Source/Flow/Public/Types/FlowPinTypeName.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "UObject/NameTypes.h" diff --git a/Source/Flow/Public/Types/FlowPinTypeNamesStandard.h b/Source/Flow/Public/Types/FlowPinTypeNamesStandard.h index b3f824d23..ecdd41d43 100644 --- a/Source/Flow/Public/Types/FlowPinTypeNamesStandard.h +++ b/Source/Flow/Public/Types/FlowPinTypeNamesStandard.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "UObject/NameTypes.h" @@ -7,11 +6,11 @@ struct FFlowPinTypeNamesStandard { - // Other Standard Pin Types + /* Other Standard Pin Types. */ FLOW_API static constexpr const TCHAR* PinTypeNameUnknown = TEXT("Unknown"); FLOW_API static constexpr const TCHAR* PinTypeNameExec = TEXT("Exec"); - // "Standard" Data Pin Types + /* "Standard" Data Pin Types. */ FLOW_API static constexpr const TCHAR* PinTypeNameBool = TEXT("Bool"); FLOW_API static constexpr const TCHAR* PinTypeNameInt = TEXT("Int"); FLOW_API static constexpr const TCHAR* PinTypeNameInt64 = TEXT("Int64"); @@ -31,8 +30,8 @@ struct FFlowPinTypeNamesStandard FLOW_API static constexpr const TCHAR* PinTypeNameClass = TEXT("Class"); #if WITH_EDITOR - // These are the default pin match policies for input pin connections - // in the UFlowGraphSchema. Schema subclasses can modify this map + /* These are the default pin match policies for input pin connections in the UFlowGraphSchema. + * Schema subclasses can modify this map. . */ FLOW_API static const TMap PinTypeMatchPolicies; #endif -}; \ No newline at end of file +}; diff --git a/Source/Flow/Public/Types/FlowPinTypeNodeTemplates.h b/Source/Flow/Public/Types/FlowPinTypeNodeTemplates.h index 0c32158a0..196893039 100644 --- a/Source/Flow/Public/Types/FlowPinTypeNodeTemplates.h +++ b/Source/Flow/Public/Types/FlowPinTypeNodeTemplates.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Types/FlowDataPinResults.h" @@ -8,7 +7,9 @@ #include "Types/FlowArray.h" #include "Nodes/FlowNode.h" -// Additional FlowPinType templates that require FlowNode.h include +/** + * Additional FlowPinType templates that require FlowNode.h include + */ namespace FlowPinType { template @@ -46,7 +47,6 @@ namespace FlowPinType return false; } - // ResolveAndFormatArray template bool ResolveAndFormatArray( const UFlowNodeBase& Node, diff --git a/Source/Flow/Public/Types/FlowPinTypeTemplates.h b/Source/Flow/Public/Types/FlowPinTypeTemplates.h index f3b0660dd..9e1bed958 100644 --- a/Source/Flow/Public/Types/FlowPinTypeTemplates.h +++ b/Source/Flow/Public/Types/FlowPinTypeTemplates.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "UObject/NameTypes.h" @@ -92,7 +91,7 @@ namespace FlowPinType // Array Conversion Helper // ----------------------------------------------------------------------- - // Converts array with logging and clamping + /** Converts array with logging and clamping. */ template void ConvertArray(const TArray& Source, TArray& OutValues, TConverter Converter) { @@ -142,6 +141,7 @@ namespace FlowPinType // ----------------------------------------------------------------------- // Internal helper – applies the single-from-array policy after extraction // ----------------------------------------------------------------------- + template FORCEINLINE EFlowDataPinResolveResult ApplySinglePolicy( const TArray& Source, @@ -295,7 +295,7 @@ namespace FlowPinType // Property Traits // ----------------------------------------------------------------------- - // Base for simple scalar types + /* Base for simple scalar types. */ template struct FFlowSimplePropertyTraitsBase { @@ -369,7 +369,7 @@ namespace FlowPinType } }; - // Numeric cross-conversion + /* Numeric cross-conversion. */ template struct FFlowNumericTraitsBase : public FFlowSimplePropertyTraitsBase { @@ -433,7 +433,7 @@ namespace FlowPinType } }; - // String cross-conversion + /* String cross-conversion. */ template struct FFlowStringTraitsBase : public FFlowSimplePropertyTraitsBase { @@ -507,7 +507,7 @@ namespace FlowPinType } }; - // Struct types (Vector, Rotator, etc.) + /* Struct types: Vector, Rotator, etc. */ template struct FFlowStructTraitsBase : public FFlowSimplePropertyTraitsBase { @@ -653,7 +653,7 @@ namespace FlowPinType } }; - // GameplayTag + /* GameplayTag. */ template <> struct FFlowDataPinValueTraits : public FFlowStructTraitsBase { @@ -688,7 +688,7 @@ namespace FlowPinType } }; - // GameplayTagContainer + /* GameplayTagContainer. */ template <> struct FFlowDataPinValueTraits : public FFlowStructTraitsBase { @@ -772,7 +772,7 @@ namespace FlowPinType } }; - // Base for Object, Class + /* Base for Object, Class. */ template struct FFlowObjectTraitsBase { @@ -969,7 +969,7 @@ namespace FlowPinType return FFlowDataPinValueTraits::ExtractValues(DataPinResult, OutValues, EFlowSingleFromArray::EntireArray); } - // Special-case single-value extractor for enums (FName + EnumClass) + /* Special-case single-value extractor for enums (FName + EnumClass). */ template static EFlowDataPinResolveResult TryExtractValue(const FFlowDataPinResult& DataPinResult, typename TPinType::ValueType& OutValue, typename TPinType::FieldType*& OutField, EFlowSingleFromArray SingleFromArray) { @@ -983,7 +983,7 @@ namespace FlowPinType return TryExtractValue(DataPinResult, OutValue, SingleFromArray); } - // Special-case array-value extractor for enums (TArray + EnumClass) + /* Special-case array-value extractor for enums (TArray + EnumClass). */ template static EFlowDataPinResolveResult TryExtractValues(const FFlowDataPinResult& DataPinResult, TArray& OutValues, typename TPinType::FieldType*& OutField) { @@ -997,7 +997,7 @@ namespace FlowPinType return TryExtractValues(DataPinResult, OutValues); } - // Special-case single-value extractor for enums (Native enum value) + /* Special-case single-value extractor for enums (Native enum value). */ template requires std::is_enum_v static EFlowDataPinResolveResult TryExtractValue(const FFlowDataPinResult& DataPinResult, TEnumType& OutValue, EFlowSingleFromArray SingleFromArray) { @@ -1010,7 +1010,7 @@ namespace FlowPinType return Wrapper.TryGetSingleEnumValue(OutValue, SingleFromArray); } - // Special-case array-value extractor for enums (Native enum values) + /* Special-case array-value extractor for enums (Native enum values). */ template requires std::is_enum_v static EFlowDataPinResolveResult TryExtractValues(const FFlowDataPinResult& DataPinResult, TArray& OutValues) { diff --git a/Source/Flow/Public/Types/FlowPinTypesStandard.h b/Source/Flow/Public/Types/FlowPinTypesStandard.h index 7828c5dc0..ac4b70292 100644 --- a/Source/Flow/Public/Types/FlowPinTypesStandard.h +++ b/Source/Flow/Public/Types/FlowPinTypesStandard.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Types/FlowPinType.h" @@ -17,7 +16,6 @@ #include "FlowPinTypesStandard.generated.h" -// Forward declarations struct FFlowDataPinValue_Bool; struct FFlowDataPinValue_Int; struct FFlowDataPinValue_Int64; @@ -56,7 +54,9 @@ struct FFlowDataPinOutputProperty_Object; struct FFlowDataPinOutputProperty_Class; // -- -// Exec +/** + * Exec + */ USTRUCT(BlueprintType) struct FLOW_API FFlowPinType_Exec : public FFlowPinType { @@ -79,7 +79,9 @@ struct FLOW_API FFlowPinType_Exec : public FFlowPinType #endif }; -// Bool +/** + * Bool + */ USTRUCT(BlueprintType) struct FLOW_API FFlowPinType_Bool : public FFlowPinType { @@ -104,7 +106,9 @@ struct FLOW_API FFlowPinType_Bool : public FFlowPinType virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; -// Int +/** + * Int + */ USTRUCT(BlueprintType) struct FLOW_API FFlowPinType_Int : public FFlowPinType { @@ -129,7 +133,9 @@ struct FLOW_API FFlowPinType_Int : public FFlowPinType virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; -// Int64 +/** + * Int64 + */ USTRUCT(BlueprintType) struct FLOW_API FFlowPinType_Int64 : public FFlowPinType { @@ -154,7 +160,9 @@ struct FLOW_API FFlowPinType_Int64 : public FFlowPinType virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; -// Float +/** + * Float + */ USTRUCT(BlueprintType) struct FLOW_API FFlowPinType_Float : public FFlowPinType { @@ -179,7 +187,9 @@ struct FLOW_API FFlowPinType_Float : public FFlowPinType virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; -// Double +/** + * Double + */ USTRUCT(BlueprintType) struct FLOW_API FFlowPinType_Double : public FFlowPinType { @@ -204,7 +214,9 @@ struct FLOW_API FFlowPinType_Double : public FFlowPinType virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; -// Name +/** + * Name + */ USTRUCT(BlueprintType) struct FLOW_API FFlowPinType_Name : public FFlowPinType { @@ -229,7 +241,9 @@ struct FLOW_API FFlowPinType_Name : public FFlowPinType virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; -// String +/** + * String + */ USTRUCT(BlueprintType) struct FLOW_API FFlowPinType_String : public FFlowPinType { @@ -254,7 +268,9 @@ struct FLOW_API FFlowPinType_String : public FFlowPinType virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; -// Text +/** + * Text + */ USTRUCT(BlueprintType) struct FLOW_API FFlowPinType_Text : public FFlowPinType { @@ -279,7 +295,9 @@ struct FLOW_API FFlowPinType_Text : public FFlowPinType virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; -// Enum +/** + * Enum + */ USTRUCT(BlueprintType) struct FLOW_API FFlowPinType_Enum : public FFlowPinType { @@ -306,7 +324,9 @@ struct FLOW_API FFlowPinType_Enum : public FFlowPinType virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; -// Vector +/** + * Vector + */ USTRUCT(BlueprintType) struct FLOW_API FFlowPinType_Vector : public FFlowPinType { @@ -332,7 +352,9 @@ struct FLOW_API FFlowPinType_Vector : public FFlowPinType virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; -// Rotator +/** + * Rotator + */ USTRUCT(BlueprintType) struct FLOW_API FFlowPinType_Rotator : public FFlowPinType { @@ -358,7 +380,9 @@ struct FLOW_API FFlowPinType_Rotator : public FFlowPinType virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; -// Transform +/** + * Transform + */ USTRUCT(BlueprintType) struct FLOW_API FFlowPinType_Transform : public FFlowPinType { @@ -384,7 +408,9 @@ struct FLOW_API FFlowPinType_Transform : public FFlowPinType virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; -// GameplayTag +/** + * GameplayTag + */ USTRUCT(BlueprintType) struct FLOW_API FFlowPinType_GameplayTag : public FFlowPinType { @@ -410,7 +436,9 @@ struct FLOW_API FFlowPinType_GameplayTag : public FFlowPinType virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; -// GameplayTagContainer +/** + * GameplayTagContainer + */ USTRUCT(BlueprintType) struct FLOW_API FFlowPinType_GameplayTagContainer : public FFlowPinType { @@ -429,7 +457,7 @@ struct FLOW_API FFlowPinType_GameplayTagContainer : public FFlowPinType #if WITH_EDITOR virtual FLinearColor GetPinColor() const override { return GetDefault()->DefaultPinTypeColor; } - virtual bool SupportsMultiType(EFlowDataMultiType Mode) const { FLOW_ASSERT_ENUM_MAX(EFlowDataMultiType, 2); return (Mode == EFlowDataMultiType::Single); } + virtual bool SupportsMultiType(EFlowDataMultiType Mode) const override { FLOW_ASSERT_ENUM_MAX(EFlowDataMultiType, 2); return (Mode == EFlowDataMultiType::Single); } virtual bool ResolveAndFormatPinValue(const UFlowNodeBase& Node, const FName& PinName, FFormatArgumentValue& OutValue) const override; virtual UObject* GetPinSubCategoryObjectFromProperty(const FProperty* Property, void const* InContainer, const FFlowDataPinValue* Wrapper) const override; #endif @@ -437,7 +465,9 @@ struct FLOW_API FFlowPinType_GameplayTagContainer : public FFlowPinType virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; -// InstancedStruct +/** + * InstancedStruct + */ USTRUCT(BlueprintType) struct FLOW_API FFlowPinType_InstancedStruct : public FFlowPinType { @@ -463,7 +493,9 @@ struct FLOW_API FFlowPinType_InstancedStruct : public FFlowPinType virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; -// Object +/** + * Object + */ USTRUCT(BlueprintType) struct FLOW_API FFlowPinType_Object : public FFlowPinType { @@ -492,7 +524,9 @@ struct FLOW_API FFlowPinType_Object : public FFlowPinType virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; }; -// Class +/** + * Class + */ USTRUCT(BlueprintType) struct FLOW_API FFlowPinType_Class : public FFlowPinType { @@ -516,4 +550,4 @@ struct FLOW_API FFlowPinType_Class : public FFlowPinType #endif virtual bool PopulateResult(const UObject& PropertyOwnerObject, const UFlowNode& Node, const FName& PropertyName, FFlowDataPinResult& OutResult) const override; -}; \ No newline at end of file +}; diff --git a/Source/Flow/Public/Types/FlowStructUtils.h b/Source/Flow/Public/Types/FlowStructUtils.h index c6054fe73..846c08490 100644 --- a/Source/Flow/Public/Types/FlowStructUtils.h +++ b/Source/Flow/Public/Types/FlowStructUtils.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "UObject/Field.h" @@ -77,7 +76,7 @@ namespace FlowStructUtils return static_cast(ValueMem); } - // Pointer overload (const) + /* Pointer overload (const). */ template FORCEINLINE const TStruct* CastStructValue(const FProperty* Prop, const void* Container) { @@ -86,7 +85,7 @@ namespace FlowStructUtils const_cast(Container)); } - // Reference overloads for convenience + /* Reference overloads for convenience. */ template FORCEINLINE TStruct* CastStructValue(FProperty& Prop, void* Container) { diff --git a/Source/FlowDebugger/FlowDebugger.Build.cs b/Source/FlowDebugger/FlowDebugger.Build.cs index 15450d9c4..06e489725 100644 --- a/Source/FlowDebugger/FlowDebugger.Build.cs +++ b/Source/FlowDebugger/FlowDebugger.Build.cs @@ -1,26 +1,25 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - using UnrealBuildTool; public class FlowDebugger : ModuleRules { public FlowDebugger(ReadOnlyTargetRules target) : base(target) { - PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - PublicDependencyModuleNames.AddRange(new[] - { + PublicDependencyModuleNames.AddRange( + [ "Flow" - }); + ]); - PrivateDependencyModuleNames.AddRange(new[] - { + PrivateDependencyModuleNames.AddRange( + [ "Core", "CoreUObject", "DeveloperSettings", "Engine", "Slate", "SlateCore", - }); + ]); } } \ No newline at end of file diff --git a/Source/FlowDebugger/Public/Debugger/FlowDebuggerSettings.h b/Source/FlowDebugger/Public/Debugger/FlowDebuggerSettings.h index b09f249ad..514badd40 100644 --- a/Source/FlowDebugger/Public/Debugger/FlowDebuggerSettings.h +++ b/Source/FlowDebugger/Public/Debugger/FlowDebuggerSettings.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Engine/DeveloperSettings.h" diff --git a/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h b/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h index ef454af6d..89346303d 100644 --- a/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h +++ b/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Subsystems/EngineSubsystem.h" @@ -86,7 +85,7 @@ class FLOWDEBUGGER_API UFlowDebuggerSubsystem : public UEngineSubsystem, public virtual void RemovePinBreakpoint(const FGuid& NodeGuid, const FName& PinName); #if WITH_EDITOR - /** Removes obsolete pin breakpoints for provided. Pin list can be changed during node reconstruction. */ + /* Removes obsolete pin breakpoints for provided. Pin list can be changed during node reconstruction. */ virtual void RemoveObsoletePinBreakpoints(const UEdGraphNode* Node); #endif @@ -115,13 +114,11 @@ class FLOWDEBUGGER_API UFlowDebuggerSubsystem : public UEngineSubsystem, public virtual void ResumeSession(UFlowAsset& FlowAssetInstance); virtual void StopSession(); - /** - * Clears the "currently hit" breakpoint only (node or pin). - * This avoids races where blanket-clearing all hit flags can erase a newly-hit breakpoint during resume/flush. - */ + /* Clears the "currently hit" breakpoint only (node or pin). + * This avoids races where blanket-clearing all hit flags can erase a newly-hit breakpoint during resume/flush. */ void ClearLastHitBreakpoint(); - /** Clears hit state for all breakpoints. Prefer ClearLastHitBreakpoint() for resume/step logic. */ + /* Clears hit state for all breakpoints. Prefer ClearLastHitBreakpoint() for resume/step logic. */ virtual void ClearHitBreakpoints(); private: @@ -148,6 +145,6 @@ class FLOWDEBUGGER_API UFlowDebuggerSubsystem : public UEngineSubsystem, public FGuid LastHitNodeGuid; FName LastHitPinName; - /** Saves any modifications made to breakpoints */ + /* Saves any modifications made to breakpoints. */ virtual void SaveSettings(); }; \ No newline at end of file diff --git a/Source/FlowDebugger/Public/Debugger/FlowDebuggerTypes.h b/Source/FlowDebugger/Public/Debugger/FlowDebuggerTypes.h index ec5435784..12eb45e1b 100644 --- a/Source/FlowDebugger/Public/Debugger/FlowDebuggerTypes.h +++ b/Source/FlowDebugger/Public/Debugger/FlowDebuggerTypes.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "FlowDebuggerTypes.generated.h" @@ -10,8 +9,8 @@ struct FLOWDEBUGGER_API FFlowBreakpoint GENERATED_BODY() protected: - // Applies only to node breakpoint - // Pin breakpoints are deactivated by removing element from FNodeBreakpoint::PinBreakpoints + /* Applies only to node breakpoint. + /* Pin breakpoints are deactivated by removing element from FNodeBreakpoint::PinBreakpoints. */ UPROPERTY() bool bActive; diff --git a/Source/FlowDebugger/Public/FlowDebuggerModule.h b/Source/FlowDebugger/Public/FlowDebuggerModule.h index d52112483..4a489b04a 100644 --- a/Source/FlowDebugger/Public/FlowDebuggerModule.h +++ b/Source/FlowDebugger/Public/FlowDebuggerModule.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Modules/ModuleInterface.h" diff --git a/Source/FlowEditor/FlowEditor.Build.cs b/Source/FlowEditor/FlowEditor.Build.cs index 86bf67a09..8ba49d2ac 100644 --- a/Source/FlowEditor/FlowEditor.Build.cs +++ b/Source/FlowEditor/FlowEditor.Build.cs @@ -1,29 +1,28 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - using UnrealBuildTool; public class FlowEditor : ModuleRules { public FlowEditor(ReadOnlyTargetRules target) : base(target) { - if (CppStandard is null || CppStandard != CppStandardVersion.Cpp20) + if (CppStandard is null || CppStandard <= CppStandardVersion.Cpp20) { CppStandard = CppStandardVersion.Cpp20; } PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - PublicDependencyModuleNames.AddRange(new[] - { + PublicDependencyModuleNames.AddRange( + [ "AssetSearch", "EditorSubsystem", "Flow", "FlowDebugger", "MessageLog" - }); + ]); - PrivateDependencyModuleNames.AddRange(new[] - { + PrivateDependencyModuleNames.AddRange( + [ "AIModule", // For BlueprintNodeHelpers::DescribeProperty (could be copy/pasted out to remove editor-only dependency) "ApplicationCore", "AssetDefinition", @@ -63,6 +62,6 @@ public FlowEditor(ReadOnlyTargetRules target) : base(target) "SourceControl", "ToolMenus", "UnrealEd" - }); + ]); } } \ No newline at end of file diff --git a/Source/FlowEditor/Private/FlowEditorLogChannels.cpp b/Source/FlowEditor/Private/FlowEditorLogChannels.cpp index f41232f16..414286936 100644 --- a/Source/FlowEditor/Private/FlowEditorLogChannels.cpp +++ b/Source/FlowEditor/Private/FlowEditorLogChannels.cpp @@ -1,3 +1,5 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + #include "FlowEditorLogChannels.h" DEFINE_LOG_CATEGORY(LogFlowEditor); diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp index 47c7f7a52..9c20e9b00 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditorSettings.cpp @@ -14,7 +14,6 @@ UFlowGraphEditorSettings::UFlowGraphEditorSettings() , bShowSubGraphPreview(true) , bShowSubGraphPath(true) , SubGraphPreviewSize(FVector2D(640.f, 360.f)) - , bHotReloadNativeNodes(false) , bHighlightInputWiresOfSelectedNodes(false) , bHighlightOutputWiresOfSelectedNodes(false) { diff --git a/Source/FlowEditor/Public/Asset/AssetDefinition_FlowAsset.h b/Source/FlowEditor/Public/Asset/AssetDefinition_FlowAsset.h index e4aeb8c5f..b535e8070 100644 --- a/Source/FlowEditor/Public/Asset/AssetDefinition_FlowAsset.h +++ b/Source/FlowEditor/Public/Asset/AssetDefinition_FlowAsset.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "AssetDefinition.h" diff --git a/Source/FlowEditor/Public/Asset/AssetDefinition_FlowAssetParams.h b/Source/FlowEditor/Public/Asset/AssetDefinition_FlowAssetParams.h index 99c8aa74c..e39d13847 100644 --- a/Source/FlowEditor/Public/Asset/AssetDefinition_FlowAssetParams.h +++ b/Source/FlowEditor/Public/Asset/AssetDefinition_FlowAssetParams.h @@ -1,20 +1,19 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "AssetDefinitionDefault.h" #include "AssetDefinition_FlowAssetParams.generated.h" /** -* Asset Definition for Flow Asset Params, providing Content Browser integration. -*/ + * Asset Definition for Flow Asset Params, providing Content Browser integration. + */ UCLASS() class FLOWEDITOR_API UAssetDefinition_FlowAssetParams : public UAssetDefinitionDefault { GENERATED_BODY() public: - // UAssetDefinition interface + // UAssetDefinition virtual FText GetAssetDisplayName() const override; virtual FLinearColor GetAssetColor() const override; virtual TSoftClassPtr GetAssetClass() const override; diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index 9fd612580..fc269adc8 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "EditorUndoClient.h" @@ -24,10 +23,13 @@ struct FSlateBrush; struct FPropertyChangedEvent; struct Rect; +/** + * Based class for toolkits used to edit assets built around the Flow Graph. + */ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEditorUndoClient, public FGCObject, public FNotifyHook { public: - /** The tab ids for all the tabs used */ + /* The tab ids for all the tabs used. */ static const FName DetailsTab; static const FName GraphTab; static const FName PaletteTab; @@ -36,7 +38,7 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit static const FName ValidationLogTab; protected: - /** The Flow Asset being edited */ + /* The Flow Asset being edited. */ TObjectPtr FlowAsset; TSharedPtr AssetToolbar; @@ -51,16 +53,16 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit TSharedPtr SearchBrowser; #endif - /** Runtime message log, with the log listing that it reflects */ + /* Runtime message log, with the log listing that it reflects. */ TSharedPtr RuntimeLog; TSharedPtr RuntimeLogListing; - /** Asset Validation message log, with the log listing that it reflects */ + /* Asset Validation message log, with the log listing that it reflects. */ TSharedPtr ValidationLog; TSharedPtr ValidationLogListing; private: - /** The current UI selection state of this editor */ + /* The current UI selection state of this editor. */ FName CurrentUISelection; public: @@ -120,7 +122,7 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit void DoPresaveAssetUpdate(); public: - /** Edits the specified FlowAsset object */ + /* Edits the specified FlowAsset object. */ virtual void InitFlowAssetEditor(const EToolkitMode::Type Mode, const TSharedPtr& InitToolkitHost, UObject* ObjectToEdit); protected: @@ -161,6 +163,6 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit void OnLogTokenClicked(const TSharedRef& Token) const; public: - // Find in flow + /* Find in flow */ void JumpToNode(const UEdGraphNode* Node) const; }; diff --git a/Source/FlowEditor/Public/Asset/FlowAssetEditorContext.h b/Source/FlowEditor/Public/Asset/FlowAssetEditorContext.h index 89c628aa7..394b09ddf 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditorContext.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditorContext.h @@ -1,8 +1,7 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once -#include "CoreMinimal.h" +#include "UObject/Object.h" #include "FlowAssetEditorContext.generated.h" diff --git a/Source/FlowEditor/Public/Asset/FlowAssetFactory.h b/Source/FlowEditor/Public/Asset/FlowAssetFactory.h index 5e6fd011a..5a6df1739 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetFactory.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetFactory.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Factories/Factory.h" @@ -17,6 +16,6 @@ class FLOWEDITOR_API UFlowAssetFactory : public UFactory virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; protected: - // Parameterized guts of ConfigureProperties() + /* Parameterized guts of ConfigureProperties(). */ bool ConfigurePropertiesInternal(const FText& TitleText); }; diff --git a/Source/FlowEditor/Public/Asset/FlowAssetIndexer.h b/Source/FlowEditor/Public/Asset/FlowAssetIndexer.h index 12d6d316a..695a2bbe5 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetIndexer.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetIndexer.h @@ -1,9 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once -#include "CoreMinimal.h" - #include "IAssetIndexer.h" class UFlowAsset; @@ -20,6 +17,6 @@ class FLOWEDITOR_API FFlowAssetIndexer : public IAssetIndexer virtual void IndexAsset(const UObject* InAssetObject, FSearchSerializer& Serializer) const override; private: - // Variant of FBlueprintIndexer::IndexGraphs + /* Variant of FBlueprintIndexer::IndexGraphs. */ void IndexGraph(const UFlowAsset* InFlowAsset, FSearchSerializer& Serializer) const; }; diff --git a/Source/FlowEditor/Public/Asset/FlowAssetParamsFactory.h b/Source/FlowEditor/Public/Asset/FlowAssetParamsFactory.h index f31527681..1f8024533 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetParamsFactory.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetParamsFactory.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Factories/Factory.h" @@ -27,7 +26,7 @@ class FLOWEDITOR_API UFlowAssetParamsFactory : public UFactory // -- private: - // Required selection + /* Required selection. */ TSoftObjectPtr SelectedParentParams; bool ShowParentPickerDialog(); diff --git a/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h b/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h index 20fd406fd..f38ef8513 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Widgets/Input/SComboBox.h" @@ -11,9 +10,10 @@ class FFlowAssetEditor; class UFlowAssetEditorContext; class UToolMenu; -////////////////////////////////////////////////////////////////////////// -// Flow Asset Instance Context - +/** + * Gathers all instances of given Flow Asset per given context. + * Example: all instances for given client in the multiplayer game. + */ struct FFlowAssetInstanceContext { FText DisplayText; @@ -29,9 +29,9 @@ struct FFlowAssetInstanceContext } }; -////////////////////////////////////////////////////////////////////////// -// Flow Asset Instance List - +/** + * List of all instances of given Flow Asset. + */ class FLOWEDITOR_API SFlowAssetInstanceList : public SCompoundWidget { public: @@ -76,11 +76,8 @@ class FLOWEDITOR_API SFlowAssetInstanceList : public SCompoundWidget static FText NoInstanceSelectedText; }; -////////////////////////////////////////////////////////////////////////// -// Flow Asset Breadcrumb - /** - * The kind of breadcrumbs that Flow Debugger uses + * The kind of breadcrumbs that Flow Debugger uses. */ struct FLOWEDITOR_API FFlowBreadcrumb { @@ -100,6 +97,9 @@ struct FLOWEDITOR_API FFlowBreadcrumb } }; +/** + * Widget displaying chain of breadcrumbs. + */ class FLOWEDITOR_API SFlowAssetBreadcrumb : public SCompoundWidget { public: @@ -120,9 +120,9 @@ class FLOWEDITOR_API SFlowAssetBreadcrumb : public SCompoundWidget TSharedPtr> BreadcrumbTrail; }; -////////////////////////////////////////////////////////////////////////// -// Flow Asset Toolbar - +/** + * Flow-specific implementation of the asset editor toolbar. + */ class FLOWEDITOR_API FFlowAssetToolbar : public TSharedFromThis { public: diff --git a/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h b/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h index 6833c0570..966dad4db 100644 --- a/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h +++ b/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Logging/TokenizedMessage.h" diff --git a/Source/FlowEditor/Public/Asset/FlowDiffControl.h b/Source/FlowEditor/Public/Asset/FlowDiffControl.h index 770f3a996..55f1747a8 100644 --- a/Source/FlowEditor/Public/Asset/FlowDiffControl.h +++ b/Source/FlowEditor/Public/Asset/FlowDiffControl.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Asset/FlowObjectDiff.h" @@ -20,8 +19,6 @@ class UFlowAsset; struct FDiffResultItem; struct FEdGraphEditAction; -///////////////////////////////////////////////////////////////////////////// -/// FFlowAssetDiffControl class FLOWEDITOR_API FFlowAssetDiffControl : public FDetailsDiffControl { public: @@ -30,14 +27,15 @@ class FLOWEDITOR_API FFlowAssetDiffControl : public FDetailsDiffControl virtual void GenerateTreeEntries(TArray>& OutTreeEntries, TArray>& OutRealDifferences) override; }; -///////////////////////////////////////////////////////////////////////////// -/// FFlowGraphToDiff: engine's FGraphToDiff customized to Flow Graph +/** + * FFlowGraphToDiff: engine's FGraphToDiff customized to Flow Graph. + */ struct FLOWEDITOR_API FFlowGraphToDiff : public TSharedFromThis, IDiffControl { FFlowGraphToDiff(SFlowDiff* DiffWidget, UEdGraph* GraphOld, UEdGraph* GraphNew, const FRevisionInfo& RevisionOld, const FRevisionInfo& RevisionNew); virtual ~FFlowGraphToDiff() override; - /** Add widgets to the differences tree */ + /* Add widgets to the differences tree. */ virtual void GenerateTreeEntries(TArray>& OutTreeEntries, TArray>& OutRealDifferences) override; UEdGraph* GetGraphOld() const { return GraphOld; }; @@ -47,18 +45,18 @@ struct FLOWEDITOR_API FFlowGraphToDiff : public TSharedFromThis GetFlowObjectDiff(const FDiffResultItem& DiffResultItem); - /** Source for list view */ + /* Source for list view. */ TArray> DiffListSource; TSharedPtr> FoundDiffs; - /** Index of the first item in RealDifferences that was generated by this graph */ + /* Index of the first item in RealDifferences that was generated by this graph. */ int32 RealDifferencesStartIndex = INDEX_NONE; private: FText GetToolTip() const; TSharedRef GenerateCategoryWidget() const; - /** Called when the Newer Graph is modified*/ + /* Called when the Newer Graph is modified. */ void OnGraphChanged(const FEdGraphEditAction& Action) const; void BuildDiffSourceArray(); @@ -73,7 +71,7 @@ struct FLOWEDITOR_API FFlowGraphToDiff : public TSharedFromThis InFlowNodeDiff, const FSingleObjectDiffEntry& InPropertyDiff); @@ -33,36 +33,37 @@ struct FLOWEDITOR_API FFlowObjectDiffArgs FSingleObjectDiffEntry PropertyDiff; }; -///////////////////////////////////////////////////////////////////////////// -/// FFlowObjectDiff: represents diff data for a particular node or pin. +/** + * FFlowObjectDiff: represents diff data for a particular node or pin. + */ class FLOWEDITOR_API FFlowObjectDiff : public TSharedFromThis { public: FFlowObjectDiff(TSharedPtr InDiffResult, const FFlowGraphToDiff& GraphToDiff); void OnSelectDiff(const FSingleObjectDiffEntry& Property) const; - void DiffProperties(TArray& OutPropertyDiffsArray) const; private: void InitializeDetailsDiffFromNode(UEdGraphNode* Node, const UObject* Object, const FFlowGraphToDiff& GraphToDiff); public: - //the tree entry for this diff object, which can be the parent tree node to other changes such as property changes, - //added/removed add-ons, moves or comments. + /* The tree entry for this diff object, which can be the parent tree node to other changes such as property changes, + * added/removed add-ons, moves or comments. */ TSharedPtr DiffTreeEntry; TSharedPtr DiffResult; - //parent of this diff. Certain nodes like Add-Ons are displayed inside the DiffTreeEntry for the node they're attached to. + /* Parent of this diff. + * Certain nodes like Add-Ons are displayed inside the DiffTreeEntry for the node they're attached to. */ TWeakPtr ParentNodeDiff; TSharedPtr OldDetailsView; TSharedPtr NewDetailsView; - //arguments used for FOnDiffEntryFocused. + /* Arguments used for FOnDiffEntryFocused. */ TSharedPtr DiffEntryFocusArg; TArray> PropertyDiffArgList; - //a list for deferring creation of DiffTreeEntries that are lower priority. + //* A list for deferring creation of DiffTreeEntries that are lower priority. */ TArray> LowPriorityChildDiffResult; }; diff --git a/Source/FlowEditor/Public/Asset/SAssetRevisionMenu.h b/Source/FlowEditor/Public/Asset/SAssetRevisionMenu.h index 4bbbe3cc1..bbd6f4350 100644 --- a/Source/FlowEditor/Public/Asset/SAssetRevisionMenu.h +++ b/Source/FlowEditor/Public/Asset/SAssetRevisionMenu.h @@ -1,8 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once -#include "Components/VerticalBox.h" #include "ISourceControlProvider.h" #include "Widgets/SCompoundWidget.h" @@ -10,7 +8,9 @@ class FUpdateStatus; class SVerticalBox; struct FRevisionInfo; -// Forced to make a variant of SBlueprintRevisionMenu, only to replace to UBlueprint* parameter +/** + * Forced to make a variant of SBlueprintRevisionMenu, only to replace to UBlueprint* parameter + */ class FLOWEDITOR_API SAssetRevisionMenu : public SCompoundWidget { DECLARE_DELEGATE_TwoParams(FOnRevisionSelected, FRevisionInfo const& RevisionInfo, const FString& InFilename) @@ -30,26 +30,30 @@ class FLOWEDITOR_API SAssetRevisionMenu : public SCompoundWidget void Construct(const FArguments& InArgs, const FString& InFilename); private: - /** Delegate used to determine the visibility 'in progress' widgets */ + /* Delegate used to determine the visibility 'in progress' widgets. */ EVisibility GetInProgressVisibility() const; - /** Delegate used to determine the visibility of the cancel button */ + + /* Delegate used to determine the visibility of the cancel button. */ EVisibility GetCancelButtonVisibility() const; - /** Delegate used to cancel a source control operation in progress */ + /* Delegate used to cancel a source control operation in progress. */ FReply OnCancelButtonClicked() const; - /** Callback for when the source control operation is complete */ + + /* Callback for when the source control operation is complete. */ void OnSourceControlQueryComplete(const FSourceControlOperationRef& InOperation, ECommandResult::Type InResult); - /** */ bool bIncludeLocalRevision = false; - /** */ FOnRevisionSelected OnRevisionSelected; - /** The name of the file we want revision info for */ + + /* The name of the file we want revision info for. */ FString Filename; - /** The box we are using to display our menu */ + + /* The box we are using to display our menu. */ TSharedPtr MenuBox; - /** The source control operation in progress */ + + /* The source control operation in progress. */ TSharedPtr SourceControlQueryOp; - /** The state of the SCC query */ + + /* The state of the SCC query. */ uint32 SourceControlQueryState = 0; }; diff --git a/Source/FlowEditor/Public/Asset/SFlowDiff.h b/Source/FlowEditor/Public/Asset/SFlowDiff.h index bf23eacb8..2c916d51b 100644 --- a/Source/FlowEditor/Public/Asset/SFlowDiff.h +++ b/Source/FlowEditor/Public/Asset/SFlowDiff.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "IDetailsView.h" @@ -24,64 +23,66 @@ namespace FlowDiffUtils FLOWEDITOR_API bool HasPrevDifference(const SListView>& ListView, const TArray>& ListViewSource); } -/** Panel used to display the asset */ +/** + * Panel used to display the asset. + */ struct FLOWEDITOR_API FFlowDiffPanel { FFlowDiffPanel(); - /** Generate a panel for NewGraph diffed against OldGraph */ + /* Generate a panel for NewGraph diffed against OldGraph. */ void GeneratePanel(UEdGraph* NewGraph, UEdGraph* OldGraph); - /** Generate a panel that displays the Graph and reflects the items in the DiffResults */ + /* Generate a panel that displays the Graph and reflects the items in the DiffResults. */ void GeneratePanel(UEdGraph* Graph, TSharedPtr> DiffResults, TAttribute FocusedDiffResult); - /** Called when user hits keyboard shortcut to copy nodes */ + /* Called when user hits keyboard shortcut to copy nodes. */ void CopySelectedNodes() const; - /** Gets whatever nodes are selected in the Graph Editor */ + /* Gets whatever nodes are selected in the Graph Editor. */ FGraphPanelSelectionSet GetSelectedNodes() const; - /** Can user copy any of the selected nodes? */ + /* Can user copy any of the selected nodes?. */ bool CanCopyNodes() const; - /** Functions used to focus/find a particular change in a diff result */ + /* Functions used to focus/find a particular change in a diff result. */ void FocusDiff(const UEdGraphPin& Pin) const; void FocusDiff(const UEdGraphNode& Node) const; void OnNodeClicked(UObject* ClickedNode ); - /** The Flow Asset that owns the graph we are showing */ + /* The Flow Asset that owns the graph we are showing. */ const UFlowAsset* FlowAsset; - /** The box around the graph editor, used to change the content when new graphs are set */ + /* The box around the graph editor, used to change the content when new graphs are set. */ TSharedPtr GraphEditorBox; - /** using SNullWidget::NullNullWidget can only work for a single widget, since widget instances can only be + /* using SNullWidget::NullNullWidget can only work for a single widget, since widget instances can only be * used one at a time. PanelDefaultDetailsView is used for displaying an empty details panel instead, as well * as if the user selects a node in the graph view. */ TSharedPtr PanelDefaultDetailsView; - /** The graph editor which does the work of displaying the graph */ + /* The graph editor which does the work of displaying the graph. */ TWeakPtr GraphEditor; - /** Revision information for this asset */ + /* Revision information for this asset. */ FRevisionInfo RevisionInfo; - /** True if we should show a name identifying which asset this panel is displaying */ + /* True if we should show a name identifying which asset this panel is displaying. */ bool bShowAssetName; - /** The widget that contains the revision info in graph mode */ + /* The widget that contains the revision info in graph mode. */ TSharedPtr OverlayGraphRevisionInfo; TWeakPtr GraphDiffSplitter = nullptr; bool bIsOldPanel = false; private: - /** Command list for this diff panel */ + /* Command list for this diff panel. */ TSharedPtr GraphEditorCommands; }; -/* Visual Diff between two Flow Assets */ +/* Visual Diff between two Flow Assets. */ class FLOWEDITOR_API SFlowDiff : public SCompoundWidget { public: @@ -102,66 +103,66 @@ class FLOWEDITOR_API SFlowDiff : public SCompoundWidget void Construct(const FArguments& InArgs); virtual ~SFlowDiff() override; - /** Called when a new Graph is clicked on by user */ + /* Called when a new Graph is clicked on by user. */ void OnGraphChanged(const FFlowGraphToDiff* Diff); - /** Called when user clicks on a new graph list item */ + /* Called when user clicks on a new graph list item. */ void OnGraphSelectionChanged(const TSharedPtr Item, ESelectInfo::Type SelectionType); - /** Called when user clicks on an entry in the listview of differences */ + /* Called when user clicks on an entry in the listview of differences. */ void OnDiffListSelectionChanged(TSharedPtr FlowObjectDiffArgs); - /** Helper function for generating an empty widget */ + /* Helper function for generating an empty widget. */ static TSharedRef DefaultEmptyPanel(); - /** Helper function to create a window that holds a diff widget */ + /* Helper function to create a window that holds a diff widget. */ static TSharedPtr CreateDiffWindow(const FText WindowTitle, const UFlowAsset* OldFlow, const UFlowAsset* NewFlow, const struct FRevisionInfo& OldRevision, const struct FRevisionInfo& NewRevision); protected: - /** Called when user clicks button to go to next difference */ + /* Called when user clicks button to go to next difference. */ void NextDiff() const; - /** Called when user clicks button to go to prev difference */ + /* Called when user clicks button to go to prev difference. */ void PrevDiff() const; - /** Called to determine whether we have a list of differences to cycle through */ + /* Called to determine whether we have a list of differences to cycle through. */ bool HasNextDiff() const; bool HasPrevDiff() const; - /** Find the FGraphToDiff that displays the graph with GraphPath relative path */ + /* Find the FGraphToDiff that displays the graph with GraphPath relative path. */ FFlowGraphToDiff* FindGraphToDiffEntry(const FString& GraphPath) const; - /** Bring these revisions of graph into focus on main display*/ + /* Bring these revisions of graph into focus on main display*/ void FocusOnGraphRevisions(const FFlowGraphToDiff* Diff); - /** User toggles the option to lock the views between the two assets */ + /* User toggles the option to lock the views between the two assets. */ void OnToggleLockView(); - /** User toggles the option to change the split view mode between vertical and horizontal */ + /* User toggles the option to change the split view mode between vertical and horizontal. */ void OnToggleSplitViewMode(); - /** Reset the graph editor, called when user switches graphs to display*/ + /* Reset the graph editor, called when user switches graphs to display*/ void ResetGraphEditors() const; - /** Get the image to show for the toggle lock option*/ + /* Get the image to show for the toggle lock option*/ FSlateIcon GetLockViewImage() const; - /** Get the image to show for the toggle split view mode option*/ + /* Get the image to show for the toggle split view mode option*/ FSlateIcon GetSplitViewModeImage() const; - /** List of graphs to diff, are added to panel last */ + /* List of graphs to diff, are added to panel last. */ TSharedPtr GraphToDiff; - /** Get Graph editor associated with this Graph */ + /* Get Graph editor associated with this Graph. */ FFlowDiffPanel& GetDiffPanelForNode(const UEdGraphNode& Node); - /** Event handler that updates the graph view when user selects a new graph */ + /* Event handler that updates the graph view when user selects a new graph. */ void HandleGraphChanged(const FString& GraphPath); - /** Function used to generate the list of differences and the widgets needed to calculate that list */ + /* Function used to generate the list of differences and the widgets needed to calculate that list. */ void GenerateDifferencesList(); - /** Called when editor may need to be closed */ + /* Called when editor may need to be closed. */ void OnCloseAssetEditor(UObject* Asset, const EAssetEditorCloseReason CloseReason); struct FDiffControl @@ -182,7 +183,7 @@ class FLOWEDITOR_API SFlowDiff : public SCompoundWidget TSharedRef GenerateGraphWidgetForPanel(FFlowDiffPanel& OutDiffPanel) const; TSharedRef GenerateRevisionInfoWidgetForPanel(TSharedPtr& OutGeneratedWidget, const FText& InRevisionText) const; - /** Accessor and event handler for toggling between diff view modes (defaults, components, graph view, interface, macro): */ + /* Accessor and event handler for toggling between diff view modes (defaults, components, graph view, interface, macro). */ void SetCurrentMode(FName NewMode); FName GetCurrentMode() const { return CurrentMode; } void OnModeChanged(const FName& InNewViewMode) const; @@ -191,16 +192,16 @@ class FLOWEDITOR_API SFlowDiff : public SCompoundWidget FName CurrentMode; - /*The two panels used to show the old & new revision*/ + /* The two panels used to show the old & new revision. */ FFlowDiffPanel PanelOld, PanelNew; - /** If the two views should be locked */ + /* If the two views should be locked. */ bool bLockViews; - /** If the view on Graph Mode should be divided vertically */ + /* If the view on Graph Mode should be divided vertically. */ bool bVerticalSplitGraphMode = true; - /** Contents widget that we swap when mode changes (defaults, components, etc) */ + /* Contents widget that we swap when mode changes (defaults, components, etc). */ TSharedPtr ModeContents; TSharedPtr TopRevisionInfoWidget; @@ -209,22 +210,22 @@ class FLOWEDITOR_API SFlowDiff : public SCompoundWidget friend struct FListItemGraphToDiff; - /** We can't use the global tab manager because we need to instance the diff control, so we have our own tab manager: */ + /* We can't use the global tab manager because we need to instance the diff control, so we have our own tab manager. */ TSharedPtr TabManager; - /** Tree of differences collected across all panels: */ + /* Tree of differences collected across all panels. */ TArray> PrimaryDifferencesList; - /** List of all differences, cached so that we can iterate only the differences and not labels, etc: */ + /* List of all differences, cached so that we can iterate only the differences and not labels, etc. */ TArray> RealDifferences; - /** Tree view that displays the differences, cached for the buttons that iterate the differences: */ + /* Tree view that displays the differences, cached for the buttons that iterate the differences. */ TSharedPtr>> DifferencesTreeView; - /** Stored references to widgets used to display various parts of asset, from the mode name */ + /* Stored references to widgets used to display various parts of asset, from the mode name. */ TMap ModePanels; - /** A pointer to the window holding this */ + /* A pointer to the window holding this. */ TWeakPtr WeakParentWindow; TSharedPtr GraphDiffSplitter = nullptr; diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowActorOwnerComponentRefCustomization.h b/Source/FlowEditor/Public/DetailCustomizations/FlowActorOwnerComponentRefCustomization.h index 02cba2ce9..4e1acb4d9 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowActorOwnerComponentRefCustomization.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowActorOwnerComponentRefCustomization.h @@ -1,18 +1,17 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "UnrealExtensions/IFlowCuratedNamePropertyCustomization.h" - #include "Types/FlowActorOwnerComponentRef.h" -// Forward Declaration class UFlowAsset; class UFlowNode; class UObject; class UClass; -// Details customization for FFlowActorOwnerComponentRef +/** + * Details customization for FFlowActorOwnerComponentRef. + */ class FFlowActorOwnerComponentRefCustomization : public IFlowCuratedNamePropertyCustomization { private: @@ -34,7 +33,7 @@ class FFlowActorOwnerComponentRefCustomization : public IFlowCuratedNameProperty virtual TArray GetCuratedNameOptions() const override; // -- - // Accessor to return the actual struct being edited + /* Accessor to return the actual struct being edited. */ FORCEINLINE FFlowActorOwnerComponentRef* GetFlowActorOwnerComponentRef() const { return IFlowExtendedPropertyTypeCustomization::TryGetTypedStructValue(StructPropertyHandle); } diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowAssetDetails.h b/Source/FlowEditor/Public/DetailCustomizations/FlowAssetDetails.h index 56894f30a..092d7d9fc 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowAssetDetails.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowAssetDetails.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "IDetailCustomization.h" diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowAssetParamsPtrCustomization.h b/Source/FlowEditor/Public/DetailCustomizations/FlowAssetParamsPtrCustomization.h index 2c979633f..fb22fbe26 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowAssetParamsPtrCustomization.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowAssetParamsPtrCustomization.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "IPropertyTypeCustomization.h" @@ -7,7 +6,9 @@ class IPropertyHandle; -// Customizes the FFlowAssetParamsPtr property in the Details panel. +/** + * Customizes the FFlowAssetParamsPtr property in the Details panel. + */ class FFlowAssetParamsPtrCustomization : public IPropertyTypeCustomization { public: diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization.h b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization.h index 7e0fb1404..0753fde6e 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Types/FlowPinType.h" @@ -46,7 +45,7 @@ class FLOWEDITOR_API FFlowDataPinValueCustomization : public IFlowExtendedProper TSharedPtr SelectedMultiType; TSharedPtr>> MultiTypeComboBox; - // Cached flag whether this pin type supports Array mode + /* Cached flag whether this pin type supports Array mode. */ bool bArraySupported = true; public: @@ -126,7 +125,7 @@ class FLOWEDITOR_API FFlowDataPinValueCustomization : public IFlowExtendedProper static FText GetInputPinTooltip(); }; -// Template customization for simple scalar value structs +/* Template customization for simple scalar value structs. */ template class TFlowDataPinValueCustomization : public FFlowDataPinValueCustomization { diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization_Class.h b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization_Class.h index 32728efb8..e8ffabc29 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization_Class.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization_Class.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "DetailCustomizations/FlowDataPinValueCustomization.h" diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization_Enum.h b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization_Enum.h index c6bdecaac..fc283b809 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization_Enum.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization_Enum.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "DetailCustomizations/FlowDataPinValueCustomization.h" diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization_Object.h b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization_Object.h index a680d2e9a..e18422740 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization_Object.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueCustomization_Object.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "DetailCustomizations/FlowDataPinValueCustomization.h" diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueOwnerCustomization.h b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueOwnerCustomization.h index 62943e8bc..c783ac60a 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueOwnerCustomization.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueOwnerCustomization.h @@ -1,7 +1,6 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #pragma once -#if WITH_EDITOR - #include "IDetailCustomization.h" #include "DetailLayoutBuilder.h" #include "Delegates/Delegate.h" @@ -58,5 +57,3 @@ class TFlowDataPinValueOwnerCustomization : public IDetailCustomization } } }; - -#endif // WITH_EDITOR \ No newline at end of file diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueOwnerCustomizations.h b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueOwnerCustomizations.h index 9cf99f9e8..437de9a59 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueOwnerCustomizations.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueOwnerCustomizations.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "FlowDataPinValueOwnerCustomization.h" diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueStandardCustomizations.h b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueStandardCustomizations.h index 0d8767e61..e920827b2 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueStandardCustomizations.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowDataPinValueStandardCustomizations.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "FlowDataPinValueCustomization.h" diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowNamedDataPinPropertyCustomization.h b/Source/FlowEditor/Public/DetailCustomizations/FlowNamedDataPinPropertyCustomization.h index c6a642bd6..41ee4914b 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowNamedDataPinPropertyCustomization.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowNamedDataPinPropertyCustomization.h @@ -1,10 +1,11 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h" -// Details customization for FFlowPin +/** + * Details customization for FFlowPin. + */ class FFlowNamedDataPinPropertyCustomization : public IFlowExtendedPropertyTypeCustomization { typedef IFlowExtendedPropertyTypeCustomization Super; diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowNodeAddOn_Details.h b/Source/FlowEditor/Public/DetailCustomizations/FlowNodeAddOn_Details.h index 423820ec3..9d69651ab 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowNodeAddOn_Details.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowNodeAddOn_Details.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "FlowDataPinValueOwnerCustomization.h" diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowNode_ComponentObserverDetails.h b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_ComponentObserverDetails.h index c3450294f..c56ec54ee 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowNode_ComponentObserverDetails.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_ComponentObserverDetails.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "IDetailCustomization.h" diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomEventBaseDetails.h b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomEventBaseDetails.h index ce5f7d4f1..ddda1485b 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomEventBaseDetails.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomEventBaseDetails.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "IDetailCustomization.h" diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomInputDetails.h b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomInputDetails.h index 7fe0c4a6e..ad948c5fb 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomInputDetails.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomInputDetails.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "FlowNode_CustomEventBaseDetails.h" diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomOutputDetails.h b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomOutputDetails.h index 6e90889f7..9640d013c 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomOutputDetails.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_CustomOutputDetails.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "FlowNode_CustomEventBaseDetails.h" diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowNode_Details.h b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_Details.h index 297d81b5f..7d4148f48 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowNode_Details.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_Details.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "FlowDataPinValueOwnerCustomization.h" diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowNode_PlayLevelSequenceDetails.h b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_PlayLevelSequenceDetails.h index efb935389..e5fe2f25c 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowNode_PlayLevelSequenceDetails.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_PlayLevelSequenceDetails.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "IDetailCustomization.h" diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowNode_SubGraphDetails.h b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_SubGraphDetails.h index 323e0b8d1..4195316d7 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowNode_SubGraphDetails.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowNode_SubGraphDetails.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "IDetailCustomization.h" diff --git a/Source/FlowEditor/Public/DetailCustomizations/FlowPinCustomization.h b/Source/FlowEditor/Public/DetailCustomizations/FlowPinCustomization.h index d73b8c7c7..7810ad279 100644 --- a/Source/FlowEditor/Public/DetailCustomizations/FlowPinCustomization.h +++ b/Source/FlowEditor/Public/DetailCustomizations/FlowPinCustomization.h @@ -1,12 +1,13 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h" struct FFlowPin; -// Details customization for FFlowPin +/** + * Details customization for FFlowPin. + */ class FFlowPinCustomization : public IFlowExtendedPropertyTypeCustomization { typedef IFlowExtendedPropertyTypeCustomization Super; @@ -17,8 +18,7 @@ class FFlowPinCustomization : public IFlowExtendedPropertyTypeCustomization virtual void CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; protected: - - // Accessor to return the actual struct being edited + /* Accessor to return the actual struct being edited. */ FORCEINLINE FFlowPin* GetFlowPin() const { return IFlowExtendedPropertyTypeCustomization::TryGetTypedStructValue(StructPropertyHandle); diff --git a/Source/FlowEditor/Public/Find/FindInFlow.h b/Source/FlowEditor/Public/Find/FindInFlow.h index 9c8321416..2c0357024 100644 --- a/Source/FlowEditor/Public/Find/FindInFlow.h +++ b/Source/FlowEditor/Public/Find/FindInFlow.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Containers/Array.h" @@ -24,7 +23,6 @@ #include "Widgets/Views/STableViewBase.h" #include "Widgets/Views/STreeView.h" -#include "Types/FlowEnumUtils.h" #include "FindInFlowEnums.h" class ITableRow; @@ -34,74 +32,76 @@ class UEdGraphNode; class UFlowAsset; class UFlowNodeBase; -/** Item that matched the search results */ +/** + * Item that matched the search results. + */ class FFindInFlowResult { public: - /** Create a root (or only text) result */ + /*Create a root (or only text) result. */ FFindInFlowResult(const FString& InValue, UFlowAsset* InOwningFlowAsset = nullptr); - /** Create a flow node result */ + /* Create a flow node result. */ FFindInFlowResult(const FString& InValue, TSharedPtr InParent, UEdGraphNode* InNode, bool bInIsSubGraphNode = false, UFlowAsset* InOwningFlowAsset = nullptr); - /** Called when user clicks on the search item */ + /* Called when user clicks on the search item. */ FReply OnClick(TWeakPtr FlowAssetEditorPtr); - /** Called when user double clicks on the search item */ + /* Called when user double clicks on the search item. */ FReply OnDoubleClick() const; - /** Create an icon to represent the result */ + /* Create an icon to represent the result. */ TSharedRef CreateIcon() const; - /** Gets the description on flow node if any */ + /* Gets the description on flow node if any. */ FString GetDescriptionText() const; - /** Gets the comment on this node if any */ + /* Gets the comment on this node if any. */ FString GetCommentText() const; - /** Gets the node type */ + /* Gets the node type. */ FString GetNodeTypeText() const; - /** Gets the node tool tip */ + /* Gets the node tool tip. */ FText GetToolTipText() const; - /** Returns a snippet of the matched property/value for tooltip */ + /* Returns a snippet of the matched property/value for tooltip. */ FText GetMatchedSnippet() const; - /** Human-readable list of categories this result matched in */ + /* Human-readable list of categories this result matched in. */ FText GetMatchedCategoriesText() const; - /** Any children listed under this flow node (decorators, services, addons, subnodes) */ + /* Any children listed under this flow node (decorators, services, addons, subnodes). */ TArray< TSharedPtr > Children; - /** The string value for this result */ + /* The string value for this result. */ FString Value; - /** Stores a snippet of the matched property/value (e.g. "Damage:50") */ + /* Stores a snippet of the matched property/value (e.g. "Damage:50"). */ FString MatchedPropertySnippet; - /** Which search categories actually produced a hit for this item */ + /* Which search categories actually produced a hit for this item. */ EFlowSearchFlags MatchedFlags = EFlowSearchFlags::None; - /** The graph node that this search result refers to */ + /* The graph node that this search result refers to. */ TWeakObjectPtr GraphNode; - /** The owning flow asset for this result */ + /* The owning flow asset for this result. */ TWeakObjectPtr OwningFlowAsset; - /** Search result parent */ + /* Search result parent. */ TWeakPtr Parent; - /** Whether this item is a subgraph node */ + /* Whether this item is a subgraph node. */ bool bIsSubGraphNode = false; }; struct FFindInFlowCache { - /** Removes all cached data for the changed flow asset */ + /* Removes all cached data for the changed flow asset. */ static void OnFlowAssetChanged(UFlowAsset& ChangedFlowAsset); - /** Cache searchable strings per node (for repeat searches) */ + /* Cache searchable strings per node (for repeat searches). */ static TMap, TMap>> CategoryStringCache; }; @@ -109,13 +109,13 @@ struct FFindInFlowAllResults { typedef TSharedPtr FSearchResult; - /** we need to keep a handle on the root result, because it won't show up in the tree */ + /* we need to keep a handle on the root result, because it won't show up in the tree. */ FSearchResult RootSearchResult; - /** This buffer stores the currently displayed results */ + /* This buffer stores the currently displayed results. */ TArray ItemsFound; - /** Visited assets to prevent cycles in subgraph recursion */ + /* Visited assets to prevent cycles in subgraph recursion. */ TSet VisitedAssets; void Setup() @@ -131,7 +131,9 @@ struct FFindInFlowAllResults } }; -/** Widget for searching for (Flow nodes) across focused FlowNodes */ +/** + * Widget for searching for (Flow nodes) across focused FlowNodes. + */ class SFindInFlow : public SCompoundWidget { public: @@ -140,7 +142,7 @@ class SFindInFlow : public SCompoundWidget void Construct(const FArguments& InArgs, TSharedPtr InFlowAssetEditor); - /** Focuses this widget's search box */ + /* Focuses this widget's search box. */ void FocusForUse() const; protected: @@ -148,47 +150,47 @@ class SFindInFlow : public SCompoundWidget typedef TSharedPtr FSearchResult; typedef STreeView STreeViewType; - /** Called when user changes the text they are searching for */ + /* Called when user changes the text they are searching for. */ void OnSearchTextChanged(const FText& Text); - /** Called when user commits text */ + /* Called when user commits text. */ void OnSearchTextCommitted(const FText& Text, ETextCommit::Type CommitType); - /** Called when search button is clicked */ + /* Called when search button is clicked. */ FReply OnSearchButtonClicked(); - /** Get the children of a row */ + /* Get the children of a row. */ void OnGetChildren(FSearchResult InItem, TArray& OutChildren); - /** Called when user clicks on a new result */ + /* Called when user clicks on a new result. */ void OnTreeSelectionChanged(FSearchResult Item, ESelectInfo::Type SelectInfo); - /* Called when user double clicks on a new result */ + /* Called when user double clicks on a new result. */ void OnTreeSelectionDoubleClicked(FSearchResult Item); - /** Called when scope selection changed */ + /* Called when scope selection changed. */ void OnScopeChanged(TSharedPtr NewSelection, ESelectInfo::Type SelectInfo); - /** Called when max depth changed */ + /* Called when max depth changed. */ void OnMaxDepthChanged(int32 NewDepth); - /** Called when a new row is being generated */ + /* Called when a new row is being generated. */ TSharedRef OnGenerateRow(FSearchResult InItem, const TSharedRef& OwnerTable); - /** Begins the search based on the SearchValue */ + /* Begins the search based on the SearchValue. */ void InitiateSearch(); - /** Build searchable string from node and its FlowNodeBase + AddOns */ + /* Build searchable string from node and its FlowNodeBase + AddOns. */ const TMap>* BuildCategoryStrings(UEdGraphNode* Node, int32 Depth) const; - /** Determines if a string matches the search tokens */ + /* Determines if a string matches the search tokens. */ static bool StringMatchesSearchTokens(const TArray& Tokens, const FString& ComparisonString); static bool StringSetMatchesSearchTokens(const TArray& Tokens, const TSet& StringSet); - /** Generate widget for scope combo */ + /* Generate widget for scope combo. */ TSharedRef GenerateScopeWidget(TSharedPtr Item) const; - /** Get current scope display text */ + /* Get current scope display text. */ FText GetCurrentScopeText() const; bool ProcessAsset(UFlowAsset* Asset, FSearchResult ParentResult, const TArray& Tokens, int32 Depth); @@ -200,37 +202,37 @@ class SFindInFlow : public SCompoundWidget void AppendPropertyValues(const void* Container, const UStruct* Struct, const UObject* ParentObject, TMap>& SearchFlagToStringMap, int32 Depth) const; protected: - /** Pointer back to the flow editor that owns us */ + /* Pointer back to the flow editor that owns us. */ TWeakPtr FlowAssetEditorPtr; - /** The tree view displays the results */ + /* The tree view displays the results. */ TSharedPtr TreeView; - /** The search text box */ + /* The search text box. */ TSharedPtr SearchTextField; - /** The search button */ + /* The search button. */ TSharedPtr SearchButton; - /** Struct with all of the search results */ + /* Struct with all of the search results. */ FFindInFlowAllResults SearchResults; - /** Repeat Search Caching */ + /* Repeat Search Caching. */ FFindInFlowCache SearchCache; - /** The string to highlight in the results */ + /* The string to highlight in the results. */ FText HighlightText; - /** The string to search for */ + /* The string to search for. */ FString SearchValue; - /** Search configuration */ + /* Search configuration. */ EFlowSearchFlags SearchFlags = EFlowSearchFlags::DefaultSearchFlags; TSharedPtr> MaxDepthSpinBox; int32 MaxSearchDepth = 3; - /** Scope selection */ + /* Scope selection. */ TArray> ScopeOptionList; TSharedPtr SelectedScopeOption; EFlowSearchScope SearchScope = EFlowSearchScope::ThisAssetOnly; diff --git a/Source/FlowEditor/Public/Find/FindInFlowEnums.h b/Source/FlowEditor/Public/Find/FindInFlowEnums.h index e0c7a6887..27d88dda8 100644 --- a/Source/FlowEditor/Public/Find/FindInFlowEnums.h +++ b/Source/FlowEditor/Public/Find/FindInFlowEnums.h @@ -1,12 +1,12 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Types/FlowEnumUtils.h" - #include "FindInFlowEnums.generated.h" -/** Bitflags controlling what parts of a Flow node are included in search */ +/** + * Bitflags controlling what parts of a Flow node are included in search. + */ UENUM(Meta = (Bitflags, UseEnumValuesAsMaskValuesInEditor = "true")) enum class EFlowSearchFlags : uint32 { @@ -33,7 +33,9 @@ enum class EFlowSearchFlags : uint32 }; ENUM_CLASS_FLAGS(EFlowSearchFlags); -/** Search scope — intentionally minimal */ +/** + * Search scope — intentionally minimal. + */ UENUM() enum class EFlowSearchScope : uint8 { diff --git a/Source/FlowEditor/Public/Find/SFindInFlowFilterPopup.h b/Source/FlowEditor/Public/Find/SFindInFlowFilterPopup.h index 49843b068..819661686 100644 --- a/Source/FlowEditor/Public/Find/SFindInFlowFilterPopup.h +++ b/Source/FlowEditor/Public/Find/SFindInFlowFilterPopup.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Widgets/DeclarativeSyntaxSupport.h" diff --git a/Source/FlowEditor/Public/FlowEditorCommands.h b/Source/FlowEditor/Public/FlowEditorCommands.h index b7d3aa95c..5a530e44e 100644 --- a/Source/FlowEditor/Public/FlowEditorCommands.h +++ b/Source/FlowEditor/Public/FlowEditorCommands.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "EdGraph/EdGraphSchema.h" @@ -21,46 +20,50 @@ class FLOWEDITOR_API FFlowToolbarCommands : public TCommands { public: FFlowGraphCommands(); - /** Context Pins */ + // Context Pins TSharedPtr ReconstructNode; - /** Pins */ + // Pins TSharedPtr AddInput; TSharedPtr AddOutput; TSharedPtr RemovePin; - /** Pin Breakpoints */ + // Pin Breakpoints TSharedPtr AddPinBreakpoint; TSharedPtr RemovePinBreakpoint; TSharedPtr EnablePinBreakpoint; TSharedPtr DisablePinBreakpoint; TSharedPtr TogglePinBreakpoint; - /** Breakpoints */ + // Breakpoints TSharedPtr EnableAllBreakpoints; TSharedPtr DisableAllBreakpoints; TSharedPtr RemoveAllBreakpoints; - /** Execution Override */ + // Execution Override TSharedPtr EnableNode; TSharedPtr DisableNode; TSharedPtr SetPassThrough; TSharedPtr ForcePinActivation; - /** Jumps */ + // Jumps TSharedPtr FocusViewport; TSharedPtr JumpToNodeDefinition; virtual void RegisterCommands() override; }; -/** Handles spawning nodes by keyboard shortcut */ +/** + * Handles spawning nodes by keyboard shortcut. + */ class FLOWEDITOR_API FFlowSpawnNodeCommands : public TCommands { public: diff --git a/Source/FlowEditor/Public/FlowEditorDefines.h b/Source/FlowEditor/Public/FlowEditorDefines.h index 00fb15e43..182ce8da5 100644 --- a/Source/FlowEditor/Public/FlowEditorDefines.h +++ b/Source/FlowEditor/Public/FlowEditorDefines.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once /** diff --git a/Source/FlowEditor/Public/FlowEditorLogChannels.h b/Source/FlowEditor/Public/FlowEditorLogChannels.h index 684d14206..104256f12 100644 --- a/Source/FlowEditor/Public/FlowEditorLogChannels.h +++ b/Source/FlowEditor/Public/FlowEditorLogChannels.h @@ -1,3 +1,4 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #pragma once #include "Logging/LogMacros.h" diff --git a/Source/FlowEditor/Public/FlowEditorModule.h b/Source/FlowEditor/Public/FlowEditorModule.h index b75187d91..df7940396 100644 --- a/Source/FlowEditor/Public/FlowEditorModule.h +++ b/Source/FlowEditor/Public/FlowEditorModule.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "AssetTypeCategories.h" diff --git a/Source/FlowEditor/Public/FlowEditorStyle.h b/Source/FlowEditor/Public/FlowEditorStyle.h index 93b45ec05..b38efc9b4 100644 --- a/Source/FlowEditor/Public/FlowEditorStyle.h +++ b/Source/FlowEditor/Public/FlowEditorStyle.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Styling/SlateStyle.h" @@ -13,7 +12,7 @@ class FLOWEDITOR_API FFlowEditorStyle static void Initialize(); static void Shutdown(); - static const FSlateBrush* GetBrush(FName PropertyName, const ANSICHAR* Specifier = nullptr) + static const FSlateBrush* GetBrush(const FName PropertyName, const ANSICHAR* Specifier = nullptr) { return Get()->GetBrush(PropertyName, Specifier); } diff --git a/Source/FlowEditor/Public/Graph/FlowGraph.h b/Source/FlowEditor/Public/Graph/FlowGraph.h index 0fe54ae72..1b576926c 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraph.h +++ b/Source/FlowEditor/Public/Graph/FlowGraph.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "EdGraph/EdGraph.h" @@ -11,24 +10,26 @@ class SFlowGraphEditor; class UFlowGraphNode; class UFlowGraphSchema; +/** + * Flow-specific implementation of engine's EdGraph. + */ UCLASS() class FLOWEDITOR_API UFlowGraph : public UEdGraph { GENERATED_UCLASS_BODY() protected: - /** Graph version number */ + /* Graph version number. */ UPROPERTY() int32 GraphVersion; static constexpr int32 CurrentGraphVersion = 2; - /** if set, graph modifications won't cause updates in internal tree structure - * flag allows freezing update during heavy changes like pasting new nodes - */ + /* If set, graph modifications won't cause updates in internal tree structure. + * Flag allows freezing update during heavy changes like pasting new nodes. */ uint32 bLockUpdates : 1; - // is currently loading the Flow Graph (used to suppress some work during load) + /* Is currently loading the Flow Graph (used to suppress some work during load)? */ uint32 bIsLoadingGraph : 1; bool bIsSavingGraph = false; @@ -90,6 +91,5 @@ class FLOWEDITOR_API UFlowGraph : public UEdGraph void UnlockUpdates(); bool IsLoadingGraph() const { return bIsLoadingGraph; } - bool IsSavingGraph() const { return bIsSavingGraph; } }; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphConnectionDrawingPolicy.h b/Source/FlowEditor/Public/Graph/FlowGraphConnectionDrawingPolicy.h index 93dd074bd..911beb7ba 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphConnectionDrawingPolicy.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphConnectionDrawingPolicy.h @@ -1,11 +1,13 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "ConnectionDrawingPolicy.h" #include "EdGraphUtilities.h" #include "Runtime/Launch/Resources/Version.h" +class FSlateWindowElementList; +class UEdGraph; + UENUM() enum class EFlowConnectionDrawType : uint8 { @@ -22,10 +24,9 @@ struct FLOWEDITOR_API FFlowGraphConnectionDrawingPolicyFactory : public FGraphPa virtual class FConnectionDrawingPolicy* CreateConnectionPolicy(const class UEdGraphSchema* Schema, int32 InBackLayerID, int32 InFrontLayerID, float ZoomFactor, const class FSlateRect& InClippingRect, class FSlateWindowElementList& InDrawElements, class UEdGraph* InGraphObj) const override; }; -class FSlateWindowElementList; -class UEdGraph; - -// This class draws the connections between nodes +/** + * This class draws the connections between nodes. + */ class FLOWEDITOR_API FFlowGraphConnectionDrawingPolicy : public FConnectionDrawingPolicy { float RecentWireDuration; @@ -40,13 +41,13 @@ class FLOWEDITOR_API FFlowGraphConnectionDrawingPolicy : public FConnectionDrawi float RecordedWireThickness; float SelectedWireThickness; - // runtime values + // Runtime values UEdGraph* GraphObj; TMap RecentPaths; TMap RecordedPaths; TMap SelectedPaths; - //Used to help reversing pins on nodes that go backwards + /* Used to help reversing pins on nodes that go backwards. */ TMap RerouteToReversedDirectionMap; public: @@ -54,7 +55,7 @@ class FLOWEDITOR_API FFlowGraphConnectionDrawingPolicy : public FConnectionDrawi void BuildPaths(); - // FConnectionDrawingPolicy interface + // FConnectionDrawingPolicy #if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 6 virtual void DrawConnection(int32 LayerId, const FVector2D& Start, const FVector2D& End, const FConnectionParams& Params) override; #else @@ -62,7 +63,7 @@ class FLOWEDITOR_API FFlowGraphConnectionDrawingPolicy : public FConnectionDrawi #endif virtual void DetermineWiringStyle(UEdGraphPin* OutputPin, UEdGraphPin* InputPin, FConnectionParams& Params) override; virtual void Draw(TMap, FArrangedWidget>& PinGeometries, FArrangedChildren& ArrangedNodes) override; - // End of FConnectionDrawingPolicy interface + // -- protected: void DrawCircuitSpline(const int32& LayerId, const FVector2f& Start, const FVector2f& End, const FConnectionParams& Params) const; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h index 8c292ece1..67789253f 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "GraphEditor.h" @@ -15,7 +14,7 @@ class UFlowDebuggerSubsystem; struct FFlowBreakpoint; /** - * + * Flow-specific implementation of engine's Graph Editor. */ class FLOWEDITOR_API SFlowGraphEditor : public SGraphEditor { diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h index 047f75bcd..e1dbdea74 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Engine/DeveloperSettings.h" @@ -33,27 +32,27 @@ class FLOWEDITOR_API UFlowGraphEditorSettings : public UDeveloperSettings virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; #endif - // Double-clicking a Flow Node might open relevant asset/code editor + /* Double-clicking a Flow Node might open relevant asset/code editor. */ UPROPERTY(config, EditAnywhere, Category = "Nodes") EFlowNodeDoubleClickTarget NodeDoubleClickTarget; - // Displays information on the graph node, either C++ class name or path to blueprint asset + /* Displays information on the graph node, either C++ class name or path to blueprint asset. */ UPROPERTY(config, EditAnywhere, Category = "Nodes") bool bShowNodeClass; - // Shows the node description when you play in editor + /* Shows the node description when you play in editor. */ UPROPERTY(config, EditAnywhere, Category = "Nodes") bool bShowNodeDescriptionWhilePlaying; - // Display descriptions from attached addons in node descriptions + /* Display descriptions from attached addons in node descriptions. */ UPROPERTY(EditAnywhere, config, Category = "Nodes") bool bShowAddonDescriptions; - // Pin names will be displayed in a format that is easier to read, even if PinFriendlyName wasn't set + /* Pin names will be displayed in a format that is easier to read, even if PinFriendlyName wasn't set. */ UPROPERTY(EditAnywhere, config, Category = "Nodes") bool bEnforceFriendlyPinNames; - // Renders preview of entire graph while hovering over + /* Renders preview of entire graph while hovering over. */ UPROPERTY(config, EditAnywhere, Category = "Nodes") bool bShowSubGraphPreview; @@ -63,22 +62,17 @@ class FLOWEDITOR_API UFlowGraphEditorSettings : public UDeveloperSettings UPROPERTY(config, EditAnywhere, Category = "Nodes", meta = (EditCondition = "bShowSubGraphPreview")) FVector2D SubGraphPreviewSize; - /** Enable hot reload for native flow nodes? - * WARNING: hot reload can easily crash the editor and you can lose progress */ - UPROPERTY(EditAnywhere, Config, Category = "Nodes", AdvancedDisplay) - bool bHotReloadNativeNodes; - UPROPERTY(EditAnywhere, config, Category = "Wires") bool bHighlightInputWiresOfSelectedNodes; UPROPERTY(EditAnywhere, config, Category = "Wires") bool bHighlightOutputWiresOfSelectedNodes; - // Default search filter flags for the Flow Editor + /* Default search filter flags for the Flow Editor. */ UPROPERTY(VisibleAnywhere, config, Category = "Search", meta = (Bitmask, BitmaskEnum = "/Script/Flow.EFlowSearchFlags")) uint32 DefaultSearchFlags = uint32(EFlowSearchFlags::DefaultSearchFlags); - // Max search depth for inline objects in the Flow Editor + /* Max search depth for inline objects in the Flow Editor. */ UPROPERTY(EditAnywhere, config, Category = "Search", meta = (ClampMin = 1)) int32 DefaultMaxSearchDepth = 1; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphNodesPolicy.h b/Source/FlowEditor/Public/Graph/FlowGraphNodesPolicy.h index 1b40db35f..c6178264f 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphNodesPolicy.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphNodesPolicy.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "FlowGraphNodesPolicy.generated.h" diff --git a/Source/FlowEditor/Public/Graph/FlowGraphPinFactory.h b/Source/FlowEditor/Public/Graph/FlowGraphPinFactory.h index 67b2f7204..cac23e1b0 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphPinFactory.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphPinFactory.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "EdGraphSchema_K2.h" @@ -10,7 +9,7 @@ struct FFlowPin; class FFlowGraphPinFactory : public FGraphPanelPinFactory { public: - // FGraphPanelPinFactory interface + // FGraphPanelPinFactory virtual TSharedPtr CreatePin(class UEdGraphPin* InPin) const override; // -- diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h index 78918f786..a13f9234a 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema.h @@ -1,10 +1,6 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once -#include "Asset/FlowPinTypeMatchPolicy.h" -#include "Types/FlowPinTypeNamesStandard.h" - #include "EdGraph/EdGraphSchema.h" #include "Runtime/Launch/Resources/Version.h" #include "Templates/SubclassOf.h" @@ -21,6 +17,9 @@ struct FFlowPinType; DECLARE_MULTICAST_DELEGATE(FFlowGraphSchemaRefresh); +/** + * Flow-specific implementation of engine's Graph Schema. + */ UCLASS() class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema { @@ -72,8 +71,6 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema virtual bool CanShowDataTooltipForPin(const UEdGraphPin& Pin) const override; // -- - // FlowGraphSchema - static const FFlowPinType* LookupDataPinTypeForPinCategory(const FName& PinCategory); void EnsurePinTypesInitialized(); @@ -128,12 +125,12 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema protected: - // These are the policies for matching data pin types + /* These are the policies for matching data pin types. */ UPROPERTY(Transient) TMap PinTypeMatchPolicies; - // TODO (gtaylor) The mechanism for customizing PinTypeMatchPolicies will need some revision. - // I am going with a simple virtual method on schema For Now(tm) but expect a revision in how this is done, in the future. + /* TODO (gtaylor) The mechanism for customizing PinTypeMatchPolicies will need some revision. + * I am going with a simple virtual method on schema For Now(tm) but expect a revision in how this is done, in the future. */ virtual void InitializedPinTypes(); static UFlowGraphNode* CreateDefaultNode(UEdGraph& Graph, const TSubclassOf& NodeClass, const FVector2D& Offset, bool bPlacedAsGhostNode); @@ -168,6 +165,6 @@ class FLOWEDITOR_API UFlowGraphSchema : public UEdGraphSchema static const UFlowAsset* GetEditedAssetOrClassDefault(const UEdGraph* Graph); private: - // ID for checking dirty status of node titles against + /* ID for checking dirty status of node titles against. */ static int32 CurrentCacheRefreshID; }; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h b/Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h index f57a715dc..e41f08d98 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSchema_Actions.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "EdGraph/EdGraphSchema.h" @@ -10,7 +9,9 @@ class UFlowGraphSettings; -/** Action to add a node to the graph */ +/** + * Action to add a node to the graph. + */ USTRUCT() struct FLOWEDITOR_API FFlowGraphSchemaAction_NewNode : public FEdGraphSchemaAction { @@ -55,17 +56,19 @@ struct FLOWEDITOR_API FFlowGraphSchemaAction_NewNode : public FEdGraphSchemaActi static FText GetNodeCategory(const UFlowNodeBase* Node, const UFlowGraphSettings& GraphSettings); }; -/** Action to add a subnode to the selected node */ +/** + * Action to add a subnode to the selected node. + */ USTRUCT() struct FLOWEDITOR_API FFlowSchemaAction_NewSubNode : public FEdGraphSchemaAction { GENERATED_USTRUCT_BODY(); - /** Template of node we want to create */ + /* Template of node we want to create. */ UPROPERTY() TObjectPtr NodeTemplate; - /** parent node */ + /* Parent node. */ UPROPERTY() TObjectPtr ParentNode; @@ -94,7 +97,9 @@ struct FLOWEDITOR_API FFlowSchemaAction_NewSubNode : public FEdGraphSchemaAction static TSharedPtr AddNewSubNodeAction(FGraphActionListBuilderBase& ContextMenuBuilder, const FText& Category, const FText& MenuDesc, const FText& Tooltip); }; -/** Action to paste clipboard contents into the graph */ +/** + * Action to paste clipboard contents into the graph. + */ USTRUCT() struct FLOWEDITOR_API FFlowGraphSchemaAction_Paste : public FEdGraphSchemaAction { @@ -115,13 +120,14 @@ struct FLOWEDITOR_API FFlowGraphSchemaAction_Paste : public FEdGraphSchemaAction // -- }; -/** Action to create new comment */ +/** + * Action to create new comment. + */ USTRUCT() struct FLOWEDITOR_API FFlowGraphSchemaAction_NewComment : public FEdGraphSchemaAction { GENERATED_USTRUCT_BODY() - // Simple type info static FName StaticGetTypeId() { static FName Type("FFlowGraphSchemaAction_NewComment"); diff --git a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h index faafca568..ff4fdea83 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphSettings.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "FlowGraphConnectionDrawingPolicy.h" @@ -53,7 +52,7 @@ struct FFlowNodeDisplayStyleConfig }; /** - * + * Editor-only graph settings. */ UCLASS(Config = Editor, defaultconfig, meta = (DisplayName = "Flow Graph")) class FLOWEDITOR_API UFlowGraphSettings : public UDeveloperSettings @@ -69,64 +68,64 @@ class FLOWEDITOR_API UFlowGraphSettings : public UDeveloperSettings virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; #endif - /** Show Flow Asset in Flow category of "Create Asset" menu? - * Requires restart after making a change. */ + /* Show Flow Asset in Flow category of "Create Asset" menu? + * Requires restart after making a change. */ UPROPERTY(EditAnywhere, config, Category = "Default UI", meta = (ConfigRestartRequired = true)) bool bExposeFlowAssetCreation; - /** Show Flow Node blueprint in Flow category of "Create Asset" menu? - * Requires restart after making a change. */ + /* Show Flow Node blueprint in Flow category of "Create Asset" menu? + * Requires restart after making a change. */ UPROPERTY(EditAnywhere, config, Category = "Default UI", meta = (ConfigRestartRequired = true)) bool bExposeFlowNodeCreation; - /** Show Flow Asset toolbar? - * Requires restart after making a change. */ + /* Show Flow Asset toolbar? + * Requires restart after making a change. */ UPROPERTY(EditAnywhere, config, Category = "Default UI", meta = (ConfigRestartRequired = true)) bool bShowAssetToolbarAboveLevelEditor; UPROPERTY(EditAnywhere, config, Category = "Default UI", meta = (ConfigRestartRequired = true)) FText FlowAssetCategoryName; - /** Use this class to create new assets. Class picker will show up if None */ + /* Use this class to create new assets. Class picker will show up if None. */ UPROPERTY(EditAnywhere, config, Category = "Default UI") TSubclassOf DefaultFlowAssetClass; - /** Flow Asset class allowed to be assigned via Level Editor toolbar*/ + /* Flow Asset class allowed to be assigned via Level Editor toolbar. */ UPROPERTY(EditAnywhere, config, Category = "Default UI", meta = (EditCondition = "bShowAssetToolbarAboveLevelEditor")) TSubclassOf WorldAssetClass; - /** Hide specific nodes from the Flow Palette without changing the source code. - * Requires restart after making a change. */ + /* Hide specific nodes from the Flow Palette without changing the source code. + * Requires restart after making a change. */ UPROPERTY(EditAnywhere, config, Category = "Nodes", meta = (ConfigRestartRequired = true)) TArray> NodesHiddenFromPalette; - /** Configurable map of FlowAsset subclasses to the FlowAssetNodePolicy for that subclass */ + /* Configurable map of FlowAsset subclasses to the FlowAssetNodePolicy for that subclass. */ UPROPERTY(EditAnywhere, Config, Category = "Nodes", meta = (ConfigRestartRequired = true, AllowedClasses = "/Script/Flow.FlowAsset")) TMap PerAssetSubclassFlowNodePolicies; - /** Allows anyone to override Flow Palette category for specific nodes without modifying source code.*/ + /* Allows anyone to override Flow Palette category for specific nodes without modifying source code. */ UPROPERTY(EditAnywhere, config, Category = "Nodes") TMap, FString> OverridenNodeCategories; - /** Hide default pin names on simple nodes, reduces UI clutter */ + /* Hide default pin names on simple nodes, reduces UI clutter. */ UPROPERTY(EditAnywhere, config, Category = "Nodes") bool bShowDefaultPinNames; - /** List of prefixes to hide on node titles and palette without need to add custom DisplayName. - * If node class has meta = (DisplayName = ... ) or BlueprintDisplayName, those texts will be displayed */ + /* List of prefixes to hide on node titles and palette without need to add custom DisplayName. + * If node class has meta = (DisplayName = ... ) or BlueprintDisplayName, those texts will be displayed. */ UPROPERTY(EditAnywhere, config, Category = "Nodes") TArray NodePrefixesToRemove; - // Display Styles for nodes, keyed by Gameplay Tag + /* Display Styles for nodes, keyed by Gameplay Tag. */ UPROPERTY(EditAnywhere, config, Category = "Nodes", meta = (TitleProperty = "{Tag}}")) TArray NodeDisplayStyles; #if WITH_EDITORONLY_DATA - // Tags in the NodeDisplayStylesMap, used to detect when the map needs updating + /* Tags in the NodeDisplayStylesMap, used to detect when the map needs updating. */ UPROPERTY(Transient) FGameplayTagContainer NodeDisplayStylesAuthoredTags; - // Cached map of the data in NodeDisplayStyles for GameplayTag-keyed lookup + /* Cached map of the data in NodeDisplayStyles for GameplayTag-keyed lookup. */ UPROPERTY(Transient) TMap NodeDisplayStylesMap; #endif @@ -167,7 +166,7 @@ class FLOWEDITOR_API UFlowGraphSettings : public UDeveloperSettings UPROPERTY(EditAnywhere, config, Category = "Wires", meta = (ClampMin = 1.0f)) float RecentWireDuration; - /** The color to display execution wires that were just executed */ + /* The color to display execution wires that were just executed. */ UPROPERTY(EditAnywhere, config, Category = "Wires") FLinearColor RecentWireColor; @@ -190,7 +189,7 @@ class FLOWEDITOR_API UFlowGraphSettings : public UDeveloperSettings virtual FName GetCategoryName() const override { return FName("Flow Graph"); } virtual FText GetSectionText() const override { return INVTEXT("Graph Settings"); } - // Override-safe category query for flow node + /* Override-safe category query for Flow Node. */ static FString GetNodeCategoryForNode(const UFlowNodeBase& FlowNodeBase); #if WITH_EDITOR diff --git a/Source/FlowEditor/Public/Graph/FlowGraphUtils.h b/Source/FlowEditor/Public/Graph/FlowGraphUtils.h index fecb73ee5..b643d41d0 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphUtils.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphUtils.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "CoreMinimal.h" diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h index c7a985ad3..3bd390a23 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "EdGraph/EdGraphNode.h" @@ -21,7 +20,7 @@ class FFlowMessageLog; DECLARE_DELEGATE(FFlowGraphNodeEvent); /** - * Graph representation of the Flow Node + * Graph representation of the Flow Node. */ UCLASS() class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode @@ -32,7 +31,7 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode // Flow node protected: - // The FlowNode or FlowNodeAddOn runtime instance that is being edited by this UFlowGraphNode + /* The FlowNode or FlowNodeAddOn runtime instance that is being edited by this UFlowGraphNode. */ UPROPERTY(Instanced) TObjectPtr NodeInstance; @@ -43,8 +42,8 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode static bool bFlowAssetsLoaded; public: - // It would be intuitive to assign a custom Graph Node class in Flow Node class - // However, we shouldn't assign class from editor module to runtime module class + /* It would be intuitive to assign a custom Graph Node class in Flow Node class. + * However, we shouldn't assign class from editor module to runtime module class. */ UPROPERTY() TArray> AssignedNodeClasses; @@ -129,27 +128,27 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode // Utils public: - // Short summary of node's content + /* Short summary of node's content. */ FString GetNodeDescription() const; - // Get flow node for the inspected asset instance + /* Get flow node for the inspected asset instance. */ UFlowNode* GetInspectedNodeInstance() const; UFlowAsset* GetFlowAsset() const; - // Used for highlighting active nodes of the inspected asset instance + /* Used for highlighting active nodes of the inspected asset instance. */ EFlowNodeState GetActivationState() const; - // Information displayed while node is active + /* Information displayed while node is active. */ FString GetStatusString() const; FLinearColor GetStatusBackgroundColor() const; - // Check this to display information while node is preloaded + /* Check this to display information while node is preloaded. */ bool IsContentPreloaded() const; bool CanFocusViewport() const; - // Index properties that are not indexed by default + /* Index properties that are not indexed by default. */ virtual void AdditionalNodeIndexing(FSearchSerializer& Serializer) const {} // UEdGraphNode @@ -161,7 +160,7 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode virtual void OnNodeDoubleClicked() const; virtual void OnNodeDoubleClickedInPIE() const {} - /** check if node has any errors, used for assigning colors on graph */ + /* Check if node has any errors, used for assigning colors on graph. */ virtual bool HasErrors() const; void ValidateGraphNode(FFlowMessageLog& MessageLog) const; @@ -196,10 +195,10 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode void AddUserInput(); void AddUserOutput(); - // Add pin only on this instance of node, under default pins + /* Add pin only on this instance of node, under default pins. */ void AddInstancePin(const EEdGraphPinDirection Direction, const uint8 NumberedPinsAmount); - // Call node and graph updates manually, if using bBatchRemoval + /* Call node and graph updates manually, if using bBatchRemoval. */ void RemoveInstancePin(UEdGraphPin* Pin); public: @@ -207,7 +206,7 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode virtual void GetPinHoverText(const UEdGraphPin& Pin, FString& HoverTextOut) const override; // -- - // @return true, if pins cannot be connected due to node's inner logic, put message for user in OutReason + /* Returns true, if pins cannot be connected due to node's inner logic, put message for user in OutReason. */ virtual bool IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const { return false; } ////////////////////////////////////////////////////////////////////////// @@ -217,10 +216,10 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode FFlowGraphNodeEvent OnSignalModeChanged; FFlowGraphNodeEvent OnReconstructNodeCompleted; - // Pin activation forced by user during PIE + /* Pin activation forced by user during PIE. */ virtual void ForcePinActivation(const FEdGraphPinReference PinReference) const; - // Pass-through forced by designer, set per node instance + /* Pass-through forced by designer, set per node instance. */ virtual void SetSignalMode(const EFlowSignalMode Mode); virtual EFlowSignalMode GetSignalMode() const; @@ -229,13 +228,13 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode ////////////////////////////////////////////////////////////////////////// // SubNode Support - //~ Begin UEdGraphNode Interface + // UEdGraphNode UFlowGraph* GetFlowGraph() const; virtual void DestroyNode() override; virtual void NodeConnectionListChanged() override; virtual void FindDiffs(class UEdGraphNode* OtherNode, struct FDiffResults& Results) override; virtual FString GetPropertyNameAndValueForDiff(const FProperty* Prop, const uint8* PropertyAddr) const override; - //~ End UEdGraphNode Interface + // -- void SetParentNodeForSubNode(UFlowGraphNode* InParentNode); UFlowGraphNode* GetParentNode() const { return ParentNode; } @@ -245,11 +244,11 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode static void DiffSubNodes(const FText& NodeTypeDisplayName, const TArray& LhsSubNodes, const TArray& RhsSubNodes, FDiffResults& Results); - //~ Begin UObject Interface + // UObject #if WITH_EDITOR virtual void PostEditUndo() override; #endif - // End UObject + // -- virtual UEdGraphPin* GetInputPin(int32 InputIndex = 0) const; virtual UEdGraphPin* GetOutputPin(int32 InputIndex = 0) const; @@ -265,20 +264,14 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode virtual int32 FindSubNodeDropIndex(UFlowGraphNode* SubNode) const; virtual void InsertSubNodeAt(UFlowGraphNode* SubNode, const int32 DropIndex); - - /** check if node is subnode */ + virtual bool IsSubNode() const; - - /** initialize instance object */ + virtual void InitializeInstance(); - - /** reinitialize node instance */ virtual bool RefreshNodeClass(); - - /** updates ClassData from node instance */ virtual void UpdateNodeClassData(); - /** Check if node instance uses blueprint for its implementation */ + /* Check if node instance uses blueprint for its implementation. */ bool UsesBlueprint() const; protected: @@ -287,34 +280,32 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode void LogError(const FString& MessageToLog, const UFlowNodeBase* FlowNodeBase) const; public: - /** instance class */ UPROPERTY() TSoftClassPtr NodeInstanceClass; - /** SubNodes that are owned by this UFlowGraphNode */ + /* SubNodes that are owned by this UFlowGraphNode. */ UPROPERTY() TArray> SubNodes; - /** subnode's parent index assigned during copy operation to connect nodes again on paste */ + /* Subnode's parent index assigned during copy operation to connect nodes again on paste. */ UPROPERTY() int32 CopySubNodeParentIndex = INDEX_NONE; - /** subnode index assigned during copy operation to connect nodes again on paste */ + /* Subnode index assigned during copy operation to connect nodes again on paste. */ UPROPERTY() int32 CopySubNodeIndex = INDEX_NONE; - /** if set, this node will be always considered as subnode */ + /* If set, this node will always be considered as subnode. */ UPROPERTY() bool bIsSubNode = false; - /** error message for node */ UPROPERTY() FString ErrorMessage; private: - /** parent UFlowGraphNode for this node, - * note, this is not saved, and is restored in when the graph is opened in the editor via - * UFlowGraph::RecursivelySetParentNodeForAllSubNodes */ + /* Parent UFlowGraphNode for this node + * Note: this is not saved, and is restored in when the graph is opened in the editor via + * UFlowGraph::RecursivelySetParentNodeForAllSubNodes. */ UPROPERTY(Transient) TObjectPtr ParentNode; }; diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_Branch.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_Branch.h index ff5836bc6..48d441a76 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_Branch.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_Branch.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Graph/Nodes/FlowGraphNode.h" diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_ExecutionSequence.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_ExecutionSequence.h index 82105197f..8a523a82f 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_ExecutionSequence.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_ExecutionSequence.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Graph/Nodes/FlowGraphNode.h" diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_Finish.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_Finish.h index c8e2ac632..9fafb852d 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_Finish.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_Finish.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Graph/Nodes/FlowGraphNode.h" diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_Reroute.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_Reroute.h index 53d1f9fa7..654be1501 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_Reroute.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_Reroute.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Graph/Nodes/FlowGraphNode.h" diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_Start.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_Start.h index c69e23433..9f929c8be 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_Start.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_Start.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Graph/Nodes/FlowGraphNode.h" diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_SubGraph.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_SubGraph.h index eac30e74e..d39744d3f 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_SubGraph.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode_SubGraph.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Graph/Nodes/FlowGraphNode.h" diff --git a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h index 140633284..5f9dd12de 100644 --- a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "SGraphNode.h" @@ -55,10 +54,8 @@ class FLOWEDITOR_API SFlowGraphNode : public SGraphNode virtual const FSlateBrush* GetNameIcon() const; virtual FSlateColor GetBorderBackgroundColor() const; - virtual FSlateColor GetConfigBoxBackgroundColor() const; - /** adds subnode widget inside current node */ virtual void AddSubNode(TSharedPtr SubNodeWidget); // -- @@ -90,38 +87,36 @@ class FLOWEDITOR_API SFlowGraphNode : public SGraphNode virtual FReply OnMouseButtonDown(const FGeometry& SenderGeometry, const FPointerEvent& MouseEvent) override; // -- - // purposely overriden non-virtual methods, added PR #9791 to made these methods virtual: https://github.com/EpicGames/UnrealEngine/pull/9791 FSlateColor GetNodeTitleColor() const; FSlateColor GetNodeBodyColor() const; FSlateColor GetNodeTitleIconColor() const; FLinearColor GetNodeTitleTextColor() const; TSharedPtr GetEnabledStateWidget() const; - // -- - - // Variant of SGraphNode::AddPinButtonContent + + /* Variant of SGraphNode::AddPinButtonContent. */ virtual void AddPinButton(TSharedPtr OutputBox, TSharedRef ButtonContent, const EEdGraphPinDirection Direction, FString DocumentationExcerpt = FString(), TSharedPtr CustomTooltip = nullptr); - // Variant of SGraphNode::OnAddPin + /* Variant of SGraphNode::OnAddPin. */ virtual FReply OnAddFlowPin(const EEdGraphPinDirection Direction); protected: - /** adds a sub node widget inside current node */ + /* Adds a sub node widget inside current node. */ void AddSubNodeWidget(const TSharedPtr& NewSubNodeWidget); - /** removes dragged subnodes from the current node, - * bInOutReorderOperation reports if this is a simple "reorder" internally within the node or - * if one or more of the removed SubNodes will be removed from the node completely */ + /* Removes dragged subnodes from the current node, + * bInOutReorderOperation reports if this is a simple "reorder" internally within the node or + * if one or more of the removed SubNodes will be removed from the node completely. */ void RemoveDraggedSubNodes(const TArray< TSharedRef >& DraggedNodes, bool& bInOutReorderOperation) const; static bool ShouldDropDraggedNodesAsSubNodes(const TArray>& DraggedNodes, const UFlowGraphNode* DropTargetNode); - /** gets decorator or service node if one is found under mouse cursor */ + /* Gets decorator or service node if one is found under mouse cursor. */ TSharedPtr GetSubNodeUnderCursor(const FGeometry& WidgetGeometry, const FPointerEvent& MouseEvent); - /** gets drag over marker visibility */ + /* Gets drag over marker visibility. */ EVisibility GetDragOverMarkerVisibility() const; - /** sets drag marker visible or collapsed on this node */ + /* Sets drag marker visible or collapsed on this node. */ void SetDragMarker(bool bEnabled); FMargin ComputeSubNodeChildIndentPaddingMargin() const; @@ -135,10 +130,10 @@ class FLOWEDITOR_API SFlowGraphNode : public SGraphNode bool IsFlowGraphNodeSelected(UFlowGraphNode* Node) const; protected: - // The graph node this slate widget is representing + /* The graph node this slate widget is representing. */ UFlowGraphNode* FlowGraphNode = nullptr; - // Subsystem pointer cached to avoid retrieving it every frame + /* Subsystem pointer cached to avoid retrieving it every frame. */ TWeakObjectPtr DebuggerSubsystem; bool bDragMarkerVisible = false; diff --git a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode_Finish.h b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode_Finish.h index d054d4a37..3265d6336 100644 --- a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode_Finish.h +++ b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode_Finish.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Graph/Widgets/SFlowGraphNode.h" diff --git a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode_Start.h b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode_Start.h index 52a959239..cd7b47d64 100644 --- a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode_Start.h +++ b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode_Start.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Graph/Widgets/SFlowGraphNode.h" diff --git a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode_SubGraph.h b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode_SubGraph.h index 9a97dd3de..99bdf0ed8 100644 --- a/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode_SubGraph.h +++ b/Source/FlowEditor/Public/Graph/Widgets/SFlowGraphNode_SubGraph.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Graph/Widgets/SFlowGraphNode.h" diff --git a/Source/FlowEditor/Public/Graph/Widgets/SFlowPalette.h b/Source/FlowEditor/Public/Graph/Widgets/SFlowPalette.h index dff23d85f..ac7d786f5 100644 --- a/Source/FlowEditor/Public/Graph/Widgets/SFlowPalette.h +++ b/Source/FlowEditor/Public/Graph/Widgets/SFlowPalette.h @@ -1,12 +1,13 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "SGraphPalette.h" class FFlowAssetEditor; -/** Widget displaying a single item */ +/** + * Widget displaying a single Palette item. + */ class FLOWEDITOR_API SFlowPaletteItem : public SGraphPaletteItem { public: @@ -20,7 +21,9 @@ class FLOWEDITOR_API SFlowPaletteItem : public SGraphPaletteItem virtual FText GetItemTooltip() const override; }; -/** Flow Palette */ +/** + * Flow-specific implementation of engine's Graph Palette, a list of nodes to place in the graph. + */ class FLOWEDITOR_API SFlowPalette : public SGraphPalette { public: diff --git a/Source/FlowEditor/Public/Graph/Widgets/SGraphEditorActionMenuFlow.h b/Source/FlowEditor/Public/Graph/Widgets/SGraphEditorActionMenuFlow.h index 7355ab842..d47d16316 100644 --- a/Source/FlowEditor/Public/Graph/Widgets/SGraphEditorActionMenuFlow.h +++ b/Source/FlowEditor/Public/Graph/Widgets/SGraphEditorActionMenuFlow.h @@ -1,7 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -// Adapted from SGraphEditorActionMenuAI, changing UAIGraphNode to UEdGraphNode, and using UFlowGraphSchema - #pragma once #include "Containers/Array.h" @@ -23,8 +20,9 @@ class UEdGraphPin; struct FEdGraphSchemaAction; struct FGraphActionListBuilderBase; -///////////////////////////////////////////////////////////////////////////////////////////////// - +/** + * Adapted from SGraphEditorActionMenuAI, changing UAIGraphNode to UEdGraphNode, and using UFlowGraphSchema. + */ class FLOWEDITOR_API SGraphEditorActionMenuFlow : public SBorder { public: @@ -48,23 +46,23 @@ class FLOWEDITOR_API SGraphEditorActionMenuFlow : public SBorder void Construct(const FArguments& InArgs); - ~SGraphEditorActionMenuFlow(); + virtual ~SGraphEditorActionMenuFlow() override; TSharedRef GetFilterTextBox(); protected: - UEdGraph* GraphObj; - UEdGraphNode* GraphNode; + UEdGraph* GraphObj = nullptr; + UEdGraphNode* GraphNode = nullptr; TArray DraggedFromPins; FVector2f NewNodePosition; - bool AutoExpandActionMenu; - int32 SubNodeFlags; + bool AutoExpandActionMenu = false; + int32 SubNodeFlags = 0; SGraphEditor::FActionMenuClosed OnClosedCallback; TSharedPtr GraphActionMenu; void OnActionSelected(const TArray>& SelectedAction, ESelectInfo::Type InSelectionType); - /** Callback used to populate all actions list in SGraphActionMenu */ + /* Callback used to populate all actions list in SGraphActionMenu. */ void CollectAllActions(FGraphActionListBuilderBase& OutAllActions); }; diff --git a/Source/FlowEditor/Public/MovieScene/FlowSection.h b/Source/FlowEditor/Public/MovieScene/FlowSection.h index b630ca77c..f805b323f 100644 --- a/Source/FlowEditor/Public/MovieScene/FlowSection.h +++ b/Source/FlowEditor/Public/MovieScene/FlowSection.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "ISequencerSection.h" @@ -10,7 +9,7 @@ class FSequencerSectionPainter; class FLOWEDITOR_API FFlowSectionBase : public FSequencerSection { public: - FFlowSectionBase(UMovieSceneSection& InSectionObject, TWeakPtr InSequencer) + FFlowSectionBase(UMovieSceneSection& InSectionObject, const TWeakPtr& InSequencer) : FSequencerSection(InSectionObject) , Sequencer(InSequencer) { @@ -29,7 +28,7 @@ class FLOWEDITOR_API FFlowSectionBase : public FSequencerSection class FLOWEDITOR_API FFlowSection : public FFlowSectionBase { public: - FFlowSection(UMovieSceneSection& InSectionObject, TWeakPtr InSequencer) + FFlowSection(UMovieSceneSection& InSectionObject, const TWeakPtr& InSequencer) : FFlowSectionBase(InSectionObject, InSequencer) { } @@ -40,7 +39,7 @@ class FLOWEDITOR_API FFlowSection : public FFlowSectionBase class FLOWEDITOR_API FFlowTriggerSection : public FFlowSectionBase { public: - FFlowTriggerSection(UMovieSceneSection& InSectionObject, TWeakPtr InSequencer) + FFlowTriggerSection(UMovieSceneSection& InSectionObject, const TWeakPtr& InSequencer) : FFlowSectionBase(InSectionObject, InSequencer) { } @@ -51,7 +50,7 @@ class FLOWEDITOR_API FFlowTriggerSection : public FFlowSectionBase class FLOWEDITOR_API FFlowRepeaterSection : public FFlowSectionBase { public: - FFlowRepeaterSection(UMovieSceneSection& InSectionObject, TWeakPtr InSequencer) + FFlowRepeaterSection(UMovieSceneSection& InSectionObject, const TWeakPtr& InSequencer) : FFlowSectionBase(InSectionObject, InSequencer) { } diff --git a/Source/FlowEditor/Public/MovieScene/FlowTrackEditor.h b/Source/FlowEditor/Public/MovieScene/FlowTrackEditor.h index 2316db26b..1724fa84e 100644 --- a/Source/FlowEditor/Public/MovieScene/FlowTrackEditor.h +++ b/Source/FlowEditor/Public/MovieScene/FlowTrackEditor.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Editor/Sequencer/Public/ISequencer.h" @@ -29,23 +28,24 @@ class FLOWEDITOR_API FFlowTrackEditor : public FMovieSceneTrackEditor * * @param InSequencer The sequencer instance to be used by this tool. */ - FFlowTrackEditor(TSharedRef InSequencer); - - // ISequencerTrackEditor interface + explicit FFlowTrackEditor(TSharedRef InSequencer); + // ISequencerTrackEditor virtual void BuildAddTrackMenu(FMenuBuilder& MenuBuilder) override; virtual bool SupportsType(TSubclassOf Type) const override; virtual bool SupportsSequence(UMovieSceneSequence* InSequence) const override; virtual const FSlateBrush* GetIconBrush() const override; virtual TSharedPtr BuildOutlinerEditWidget(const FGuid& ObjectBinding, UMovieSceneTrack* Track, const FBuildEditWidgetParams& Params) override; + // -- - //~ FPropertyTrackEditor interface + // FPropertyTrackEditor virtual TSharedRef MakeSectionInterface(UMovieSceneSection& SectionObject, UMovieSceneTrack& Track, FGuid ObjectBinding) override; + // -- private: void AddFlowSubMenu(FMenuBuilder& MenuBuilder); - /** Callback for executing the "Add Event Track" menu entry. */ + /* Callback for executing the "Add Event Track" menu entry. */ void HandleAddFlowTrackMenuEntryExecute(UClass* SectionType) const; void CreateNewSection(UMovieSceneTrack* Track, int32 RowIndex, UClass* SectionType, bool bSelect) const; diff --git a/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.h b/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.h index 08c40a9ec..a22f4a8fd 100644 --- a/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.h +++ b/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeAddOnBlueprint.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "AssetTypeActions/AssetTypeActions_Blueprint.h" diff --git a/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeBlueprint.h b/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeBlueprint.h index e8243cf42..a9b3a2316 100644 --- a/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeBlueprint.h +++ b/Source/FlowEditor/Public/Nodes/AssetTypeActions_FlowNodeBlueprint.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "AssetTypeActions/AssetTypeActions_Blueprint.h" diff --git a/Source/FlowEditor/Public/Nodes/FlowNodeBlueprintFactory.h b/Source/FlowEditor/Public/Nodes/FlowNodeBlueprintFactory.h index e3752525e..ba96b29fd 100644 --- a/Source/FlowEditor/Public/Nodes/FlowNodeBlueprintFactory.h +++ b/Source/FlowEditor/Public/Nodes/FlowNodeBlueprintFactory.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Factories/Factory.h" @@ -10,11 +9,11 @@ class UFlowNodeBaseBlueprintFactory : public UFactory { GENERATED_UCLASS_BODY() - // The Default parent class of the created blueprint (set by subclasses) + /* The Default parent class of the created blueprint (set by subclasses). */ UPROPERTY() TSubclassOf DefaultParentClass; - // The parent class of the created blueprint + /* The parent class of the created blueprint. */ UPROPERTY(EditAnywhere, Category = "FlowNodeBlueprintFactory") TSubclassOf ParentClass; @@ -27,14 +26,18 @@ class UFlowNodeBaseBlueprintFactory : public UFactory // -- }; -// Specialization of UFlowNodeBaseBlueprintFactory for UFlowNode blueprints +/** + * Specialization of UFlowNodeBaseBlueprintFactory for UFlowNode blueprints. + */ UCLASS(hidecategories = Object) class FLOWEDITOR_API UFlowNodeBlueprintFactory : public UFlowNodeBaseBlueprintFactory { GENERATED_UCLASS_BODY() }; -// Specialization of UFlowNodeBaseBlueprintFactory for UFlowNodeAddOn blueprints +/** + * Specialization of UFlowNodeBaseBlueprintFactory for UFlowNodeAddOn blueprints. + */ UCLASS(hidecategories = Object) class FLOWEDITOR_API UFlowNodeAddOnBlueprintFactory : public UFlowNodeBaseBlueprintFactory { diff --git a/Source/FlowEditor/Public/Pins/SFlowInputPinHandle.h b/Source/FlowEditor/Public/Pins/SFlowInputPinHandle.h index 5355265bb..d42e75fdb 100644 --- a/Source/FlowEditor/Public/Pins/SFlowInputPinHandle.h +++ b/Source/FlowEditor/Public/Pins/SFlowInputPinHandle.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "EdGraphUtilities.h" diff --git a/Source/FlowEditor/Public/Pins/SFlowOutputPinHandle.h b/Source/FlowEditor/Public/Pins/SFlowOutputPinHandle.h index a291ac83c..0dfe78f05 100644 --- a/Source/FlowEditor/Public/Pins/SFlowOutputPinHandle.h +++ b/Source/FlowEditor/Public/Pins/SFlowOutputPinHandle.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "EdGraphUtilities.h" diff --git a/Source/FlowEditor/Public/Pins/SFlowPinHandle.h b/Source/FlowEditor/Public/Pins/SFlowPinHandle.h index 141ba3c1a..e82d9d143 100644 --- a/Source/FlowEditor/Public/Pins/SFlowPinHandle.h +++ b/Source/FlowEditor/Public/Pins/SFlowPinHandle.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "SGraphPin.h" diff --git a/Source/FlowEditor/Public/UnrealExtensions/IFlowCuratedNamePropertyCustomization.h b/Source/FlowEditor/Public/UnrealExtensions/IFlowCuratedNamePropertyCustomization.h index 6bd95250d..84da03609 100644 --- a/Source/FlowEditor/Public/UnrealExtensions/IFlowCuratedNamePropertyCustomization.h +++ b/Source/FlowEditor/Public/UnrealExtensions/IFlowCuratedNamePropertyCustomization.h @@ -1,14 +1,17 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -// NOTE (gtaylor) This class is planned for submission to Epic to include in baseline UE. -// If/when that happens, we will want to remove this version and update to the latest one in the PropertyModule - #pragma once #include "IFlowExtendedPropertyTypeCustomization.h" #include "Widgets/Input/SComboBox.h" -// A base-class to do property Customization for a struct that presents a curated list of FNames for selection +/** + * NOTE (gtaylor) This class is planned for submission to Epic to include in baseline UE. + * If/when that happens, we will want to remove this version and update to the latest one in the PropertyModule. + */ + +/** + * A base-class to do property Customization for a struct that presents a curated list of FNames for selection. + */ class FLOWEDITOR_API IFlowCuratedNamePropertyCustomization : public IFlowExtendedPropertyTypeCustomization { protected: @@ -18,8 +21,7 @@ class FLOWEDITOR_API IFlowCuratedNamePropertyCustomization : public IFlowExtende void Initialize(); - // Helper function to set the property to a specified value - // (and handle all of the side-effects) + /* Helper function to set the property to a specified value, and handle all the side effects. */ bool TrySetCuratedNameWithSideEffects(const FName& NewName); // Callbacks for the TextListWidget (see CreateHeaderRowWidget) @@ -50,19 +52,15 @@ class FLOWEDITOR_API IFlowCuratedNamePropertyCustomization : public IFlowExtende // --- public: - // Cached property handle for the Curated Name property that is being customized TSharedPtr CachedNameHandle; - - // Cached PropertyUtils TSharedPtr CachedPropertyUtils; - // Cache FTexts for the ComboBox dropdown & current selected + /* Cache FTexts for the ComboBox dropdown & current selected. */ TArray> CachedTextList; TSharedPtr CachedTextSelected; - // Preallocated NAME_None as FText static TSharedPtr NoneAsText; - // Combo Box widget for displaying the curated list of Names + /* Combo Box widget for displaying the curated list of Names */ TSharedPtr>> TextListWidget; }; diff --git a/Source/FlowEditor/Public/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h b/Source/FlowEditor/Public/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h index 512cab399..4451656b9 100644 --- a/Source/FlowEditor/Public/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h +++ b/Source/FlowEditor/Public/UnrealExtensions/IFlowExtendedPropertyTypeCustomization.h @@ -1,16 +1,10 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - -// NOTE (gtaylor) This class is planned for submission to Epic to include in baseline UE. -// If/when that happens, we will want to remove this version and update to the latest one in the PropertyModule - #pragma once #include "IPropertyTypeCustomization.h" #include "PropertyHandle.h" #include "Templates/SharedPointer.h" -#include "IPropertyTypeCustomization.h" - class STextBlock; class FDetailWidgetRow; class IDetailChildrenBuilder; @@ -18,8 +12,14 @@ class IPropertyTypeCustomizationUtils; class IDetailPropertyRow; class IPropertyHandle; -// An extension of IPropertyTypeCustomization -// which adds some quality-of-life improvements for subclasses +/** + * NOTE (gtaylor) This class is planned for submission to Epic to include in baseline UE. + * If/when that happens, we will want to remove this version and update to the latest one in the PropertyModule. + */ + +/** + * An extension of IPropertyTypeCustomization which adds some quality-of-life improvements for subclasses. + */ class FLOWEDITOR_API IFlowExtendedPropertyTypeCustomization : public IPropertyTypeCustomization { public: @@ -43,14 +43,14 @@ class FLOWEDITOR_API IFlowExtendedPropertyTypeCustomization : public IPropertyTy virtual void CreateHeaderRowWidget(FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils); virtual FText BuildHeaderText() const; - // Callbacks for property editor delegates + /* Callbacks for property editor delegates. */ void OnAnyChildPropertyChanged() const; protected: - // Cached struct property + /* Cached struct property. */ TSharedPtr StructPropertyHandle; - // Header property text block, (re-)built in RefreshHeader + /* Header property text block, (re-)built in RefreshHeader. */ TSharedPtr HeaderTextBlock; }; diff --git a/Source/FlowEditor/Public/Utils/SLevelEditorFlow.h b/Source/FlowEditor/Public/Utils/SLevelEditorFlow.h index 5d842eaa3..e76ca499c 100644 --- a/Source/FlowEditor/Public/Utils/SLevelEditorFlow.h +++ b/Source/FlowEditor/Public/Utils/SLevelEditorFlow.h @@ -1,5 +1,4 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors - #pragma once #include "Widgets/DeclarativeSyntaxSupport.h" From 8510e423fcc6576f71a2df96887e0337d92a132d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Sun, 15 Feb 2026 18:51:49 +0100 Subject: [PATCH 411/485] Removed enforcing cpp20 in the plugin, it's confusing for general users. --- Source/FlowEditor/FlowEditor.Build.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Source/FlowEditor/FlowEditor.Build.cs b/Source/FlowEditor/FlowEditor.Build.cs index 8ba49d2ac..bf270ec93 100644 --- a/Source/FlowEditor/FlowEditor.Build.cs +++ b/Source/FlowEditor/FlowEditor.Build.cs @@ -5,11 +5,6 @@ public class FlowEditor : ModuleRules { public FlowEditor(ReadOnlyTargetRules target) : base(target) { - if (CppStandard is null || CppStandard <= CppStandardVersion.Cpp20) - { - CppStandard = CppStandardVersion.Cpp20; - } - PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; PublicDependencyModuleNames.AddRange( From 38e630ed8b41dcb0771d269cff2c627a05af724b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Mon, 16 Feb 2026 20:50:50 +0100 Subject: [PATCH 412/485] Fixed: SubGraphs are marked as dirty on every graph load #344 --- Source/Flow/Private/FlowAsset.cpp | 82 ++++--------------- Source/Flow/Private/Nodes/FlowNode.cpp | 81 +++++++++++------- .../Nodes/Graph/FlowNode_DefineProperties.cpp | 53 ++++++------ .../Private/Nodes/Graph/FlowNode_SubGraph.cpp | 62 +++++++------- .../Types/FlowAutoDataPinsWorkingData.cpp | 5 +- Source/Flow/Public/FlowAsset.h | 3 - .../FlowDataPinGeneratorInterface.h | 27 ------ Source/Flow/Public/Nodes/FlowNode.h | 35 +++----- .../Nodes/Graph/FlowNode_DefineProperties.h | 8 +- .../Public/Nodes/Graph/FlowNode_SubGraph.h | 22 +++-- Source/FlowEditor/Private/Graph/FlowGraph.cpp | 13 ++- .../Private/Graph/Nodes/FlowGraphNode.cpp | 54 +++++------- .../Public/Graph/Nodes/FlowGraphNode.h | 2 - 13 files changed, 184 insertions(+), 263 deletions(-) delete mode 100644 Source/Flow/Public/Interfaces/FlowDataPinGeneratorInterface.h diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index afbeae7f0..0294a9a07 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -274,7 +274,7 @@ EDataValidationResult UFlowAsset::ValidateAsset(FFlowMessageLog& MessageLog) } else { - const FString ErrorMsg = FString::Format(*ValidationError_NullAddOnNodeInstance, { *Node.Key.ToString() }); + const FString ErrorMsg = FString::Format(*ValidationError_NullAddOnNodeInstance, {*Node.Key.ToString()}); MessageLog.Error(*ErrorMsg, this); } } @@ -409,8 +409,8 @@ void UFlowAsset::ValidateAddOnTree(UFlowNodeAddOn& AddOn, FFlowMessageLog& Messa { const FString ErrorMsg = FailureReason.IsEmpty() - ? FString::Format(*ValidationError_AddOnNodeClassNotAllowed, { *AddOn.GetClass()->GetName() }) - : FailureReason.ToString(); + ? FString::Format(*ValidationError_AddOnNodeClassNotAllowed, {*AddOn.GetClass()->GetName()}) + : FailureReason.ToString(); MessageLog.Error(*ErrorMsg, AddOn.GetFlowNodeSelfOrOwner()); } @@ -495,7 +495,7 @@ void UFlowAsset::RegisterNode(const FGuid& NewGuid, UFlowNode* NewNode) HarvestNodeConnections(); - if (TryUpdateManagedFlowPinsForNode(*NewNode)) + if (NewNode->TryUpdateAutoDataPins()) { (void)NewNode->OnReconstructionRequested.ExecuteIfBound(); } @@ -638,52 +638,6 @@ bool UFlowAsset::TryGetDefaultForInputPinName(const FStructProperty& StructPrope return false; } -bool UFlowAsset::TryUpdateManagedFlowPinsForNode(UFlowNode& FlowNode) -{ - // Set up the working data struct - FFlowAutoDataPinsWorkingData WorkingData = - FFlowAutoDataPinsWorkingData( - FlowNode.GetAutoInputDataPins(), - FlowNode.GetAutoOutputDataPins()); - - bool bAutoInputDataPinsChanged = false; - bool bAutoOutputDataPinsChanged = false; - - if (WorkingData.AutoGenerateDataPinsForFlowNode(FlowNode, bAutoInputDataPinsChanged, bAutoOutputDataPinsChanged)) - { - FlowNode.SetFlags(RF_Transactional); - FlowNode.Modify(); - - // Lock-in the data that changed. - TArray FlowPinsNext; - const int32 LargestPinNum = FMath::Max(WorkingData.AutoInputDataPinsNext.Num(), WorkingData.AutoOutputDataPinsNext.Num()); - - if (bAutoInputDataPinsChanged) - { - FlowPinsNext.Reserve(LargestPinNum); - - WorkingData.BuildNextFlowPinArray(WorkingData.AutoInputDataPinsNext, FlowPinsNext); - - FlowNode.SetAutoInputDataPins(FlowPinsNext); - } - - if (bAutoOutputDataPinsChanged) - { - FlowPinsNext.Reserve(LargestPinNum); - - WorkingData.BuildNextFlowPinArray(WorkingData.AutoOutputDataPinsNext, FlowPinsNext); - - FlowNode.SetAutoOutputDataPins(FlowPinsNext); - } - - FlowNode.PostEditChange(); - - return true; - } - - return false; -} - #endif UFlowNode* UFlowAsset::GetDefaultEntryNode() const @@ -1087,8 +1041,8 @@ void UFlowAsset::CancelAndWarnForUnflushedDeferredTriggers() if (TotalDroppedTriggers == 0 && !Triggers.IsEmpty()) { UE_LOG(LogFlow, Warning, TEXT("FlowAsset '%s' is finishing with %d lingering deferred transition scope(s) — dropping them. " - "This is usually unexpected and may indicate a bug or abnormal termination."), - *GetName(), DeferredTransitionScopes.Num()); + "This is usually unexpected and may indicate a bug or abnormal termination."), + *GetName(), DeferredTransitionScopes.Num()); } TotalDroppedTriggers += Triggers.Num(); @@ -1099,17 +1053,17 @@ void UFlowAsset::CancelAndWarnForUnflushedDeferredTriggers() const UFlowNode* FromNode = Trigger.FromPin.NodeGuid.IsValid() ? GetNode(Trigger.FromPin.NodeGuid) : nullptr; UE_LOG(LogFlow, Error, - TEXT(" → Dropped deferred trigger:\n") - TEXT(" To Node: %s (%s)\n") - TEXT(" To Pin: %s\n") - TEXT(" From Node: %s (%s)\n") - TEXT(" From Pin: %s"), - *ToNode->GetName(), - *Trigger.NodeGuid.ToString(), - *Trigger.PinName.ToString(), - *FromNode->GetName(), - *Trigger.FromPin.NodeGuid.ToString(), - *Trigger.FromPin.PinName.ToString() + TEXT(" → Dropped deferred trigger:\n") + TEXT(" To Node: %s (%s)\n") + TEXT(" To Pin: %s\n") + TEXT(" From Node: %s (%s)\n") + TEXT(" From Pin: %s"), + *ToNode->GetName(), + *Trigger.NodeGuid.ToString(), + *Trigger.PinName.ToString(), + *FromNode->GetName(), + *Trigger.FromPin.NodeGuid.ToString(), + *Trigger.FromPin.PinName.ToString() ); } } @@ -1276,7 +1230,7 @@ void UFlowAsset::EnqueueDeferredTrigger(const FGuid& NodeGuid, const FName& PinN } // Always enqueue to the current innermost (top) scope - DeferredTransitionScopes.Top()->EnqueueDeferredTrigger(FFlowDeferredTriggerInput{ NodeGuid, PinName, FromPin }); + DeferredTransitionScopes.Top()->EnqueueDeferredTrigger(FFlowDeferredTriggerInput{NodeGuid, PinName, FromPin}); } bool UFlowAsset::TryFlushAllDeferredTriggerScopes() diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 4c168fb92..e24d8f530 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -297,7 +297,7 @@ bool UFlowNode::SupportsContextPins() const return true; } - if (!GetAutoInputDataPins().IsEmpty() || !GetAutoOutputDataPins().IsEmpty()) + if (!AutoInputDataPins.IsEmpty() || !AutoOutputDataPins.IsEmpty()) { return true; } @@ -318,7 +318,7 @@ TArray UFlowNode::GetContextInputs() const TArray ContextOutputs = Super::GetContextInputs(); // Add the Auto-Generated DataPins as GetContextInputs - for (const FFlowPin& AutoGeneratedDataPin : GetAutoInputDataPins()) + for (const FFlowPin& AutoGeneratedDataPin : AutoInputDataPins) { ContextOutputs.AddUnique(AutoGeneratedDataPin); } @@ -331,7 +331,7 @@ TArray UFlowNode::GetContextOutputs() const TArray ContextOutputs = Super::GetContextOutputs(); // Add the Auto-Generated DataPins as ContextOutputs - for (const FFlowPin& AutoGeneratedDataPin : GetAutoOutputDataPins()) + for (const FFlowPin& AutoGeneratedDataPin : AutoOutputDataPins) { ContextOutputs.AddUnique(AutoGeneratedDataPin); } @@ -405,14 +405,58 @@ void UFlowNode::RemoveUserOutput(const FName& PinName) } } -void UFlowNode::SetAutoInputDataPins(const TArray& AutoInputPins) +bool UFlowNode::TryUpdateAutoDataPins() { - AutoInputDataPins = AutoInputPins; + // Generate data pins, feed list to working data struct + FFlowAutoDataPinsWorkingData WorkingData = FFlowAutoDataPinsWorkingData(AutoInputDataPins, AutoOutputDataPins); + AutoGenerateDataPins(WorkingData); + + bool bAutoInputDataPinsChanged = false; + bool bAutoOutputDataPinsChanged = false; + if (WorkingData.AutoGenerateDataPinsForFlowNode(*this, bAutoInputDataPinsChanged, bAutoOutputDataPinsChanged)) + { + SetFlags(RF_Transactional); + Modify(); + + // Lock-in the data that changed. + TArray NewDataPins; + const int32 LargestPinNum = FMath::Max(WorkingData.AutoInputDataPinsNext.Num(), WorkingData.AutoOutputDataPinsNext.Num()); + + if (bAutoInputDataPinsChanged) + { + NewDataPins.Reserve(LargestPinNum); + WorkingData.BuildNextFlowPinArray(WorkingData.AutoInputDataPinsNext, NewDataPins); + AutoInputDataPins = MoveTemp(NewDataPins); + } + + if (bAutoOutputDataPinsChanged) + { + NewDataPins.Reserve(LargestPinNum); + WorkingData.BuildNextFlowPinArray(WorkingData.AutoOutputDataPinsNext, NewDataPins); + AutoOutputDataPins = MoveTemp(NewDataPins); + } + + PostEditChange(); + return true; + } + + return false; } -void UFlowNode::SetAutoOutputDataPins(const TArray& AutoOutputPins) +void UFlowNode::AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const { - AutoOutputDataPins = AutoOutputPins; + // Gather all the potential providers for this DataPin + TArray PropertyOwnerObjects; + GatherPotentialPropertyOwnersForDataPins(PropertyOwnerObjects); + + // GenerateDataPins for all the potential providers + for (int32 PropertyOwnerIndex = 0; PropertyOwnerIndex < PropertyOwnerObjects.Num(); ++PropertyOwnerIndex) + { + const UObject* PropertyOwnerObject = PropertyOwnerObjects[PropertyOwnerIndex]; + checkf(IsValid(PropertyOwnerObject), TEXT("Every UObject provided by GatherPotentialPropertyOwnersForDataPins must be valid")); + + InOutWorkingData.AddFlowDataPinsForClassProperties(*PropertyOwnerObject, PropertyOwnerIndex); + } } #endif // WITH_EDITOR @@ -487,7 +531,7 @@ void UFlowNode::GatherPotentialPropertyOwnersForDataPins(TArray& InOutOwners.Add(this); - // Give all of the AddOns a chance to supply data pins as well + // Give all the AddOns a chance to supply data pins as well (void) ForEachAddOnConst( [&](const UFlowNodeAddOn& AddOn) { @@ -519,7 +563,7 @@ bool UFlowNode::TryGatherPropertyOwnersAndPopulateResult( return false; } - const UObject* PropertyOwnerObject = nullptr; + const UObject* PropertyOwnerObject; FName PropertyNameToLookup; // Look up explicit mapping (used for non-default owners or disambiguated pins) @@ -632,25 +676,6 @@ void UFlowNode::TryAddSupplierDataToArray(FFlowPinValueSupplierData& InOutSuppli } } -#if WITH_EDITOR -void UFlowNode::AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const -{ - // Gather all of the potential providers for this DataPin - TArray PropertyOwnerObjects; - GatherPotentialPropertyOwnersForDataPins(PropertyOwnerObjects); - - // GenerateDataPins for all of the potential providers - for (int32 PropertyOwnerIndex = 0; PropertyOwnerIndex < PropertyOwnerObjects.Num(); ++PropertyOwnerIndex) - { - const UObject* PropertyOwnerObject = PropertyOwnerObjects[PropertyOwnerIndex]; - - checkf(IsValid(PropertyOwnerObject), TEXT("Every UObject provided by GatherPotentialPropertyOwnersForDataPins must be valid")); - - InOutWorkingData.AddFlowDataPinsForClassProperties(*PropertyOwnerObject, PropertyOwnerIndex); - } -} -#endif - // #FlowDataPinLegacy void UFlowNode::FixupDataPinTypes() { diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_DefineProperties.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_DefineProperties.cpp index 6b116310c..064b72e45 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_DefineProperties.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_DefineProperties.cpp @@ -36,6 +36,35 @@ void UFlowNode_DefineProperties::PostLoad() } } +bool UFlowNode_DefineProperties::SupportsContextPins() const +{ + return Super::SupportsContextPins() || !NamedProperties.IsEmpty(); +} + +void UFlowNode_DefineProperties::AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const +{ + Super::AutoGenerateDataPins(InOutWorkingData); + + for (const FFlowNamedDataPinProperty& DataPinProperty : NamedProperties) + { + if (DataPinProperty.IsValid()) + { + const FFlowDataPinValue& DataPinValue = DataPinProperty.DataPinValue.Get(); + const FName PropertyOwnerObjectName = GetFName(); + constexpr int32 PropertyOwnerIndex = 0; + + if (DataPinValue.IsInputPin()) + { + InOutWorkingData.AutoInputDataPinsNext.Add(FFlowPinSourceData(DataPinProperty.CreateFlowPin(), PropertyOwnerObjectName, PropertyOwnerIndex, &DataPinValue)); + } + else + { + InOutWorkingData.AutoOutputDataPinsNext.Add(FFlowPinSourceData(DataPinProperty.CreateFlowPin(), PropertyOwnerObjectName, PropertyOwnerIndex, &DataPinValue)); + } + } + } +} + bool UFlowNode_DefineProperties::TryFindPropertyByPinName( const UObject& PropertyOwnerObject, const FName& PinName, @@ -74,30 +103,6 @@ void UFlowNode_DefineProperties::OnPostEditEnsureAllNamedPropertiesPinDirection( } } -void UFlowNode_DefineProperties::AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const -{ - Super::AutoGenerateDataPins(InOutWorkingData); - - for (const FFlowNamedDataPinProperty& DataPinProperty : NamedProperties) - { - if (DataPinProperty.IsValid()) - { - const FFlowDataPinValue& DataPinValue = DataPinProperty.DataPinValue.Get(); - const FName PropertyOwnerObjectName = GetFName(); - constexpr int32 PropertyOwnerIndex = 0; - - if (DataPinValue.IsInputPin()) - { - InOutWorkingData.AutoInputDataPinsNext.Add(FFlowPinSourceData(DataPinProperty.CreateFlowPin(), PropertyOwnerObjectName, PropertyOwnerIndex, &DataPinValue)); - } - else - { - InOutWorkingData.AutoOutputDataPinsNext.Add(FFlowPinSourceData(DataPinProperty.CreateFlowPin(), PropertyOwnerObjectName, PropertyOwnerIndex, &DataPinValue)); - } - } - } -} - void UFlowNode_DefineProperties::PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChainEvent) { Super::PostEditChangeChainProperty(PropertyChainEvent); diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp index 02fefd7f4..feedf5850 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp @@ -227,37 +227,6 @@ void UFlowNode_SubGraph::AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOu } } -void UFlowNode_SubGraph::PostLoad() -{ - Super::PostLoad(); - - SubscribeToAssetChanges(); -} - -void UFlowNode_SubGraph::PreEditChange(FProperty* PropertyAboutToChange) -{ - Super::PreEditChange(PropertyAboutToChange); - - if (PropertyAboutToChange->GetFName() == GET_MEMBER_NAME_CHECKED(UFlowNode_SubGraph, Asset)) - { - if (Asset) - { - Asset->OnSubGraphReconstructionRequested.Unbind(); - } - } -} - -void UFlowNode_SubGraph::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) -{ - Super::PostEditChangeProperty(PropertyChangedEvent); - - if (PropertyChangedEvent.Property && PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(UFlowNode_SubGraph, Asset)) - { - OnReconstructionRequested.ExecuteIfBound(); - SubscribeToAssetChanges(); - } -} - FFlowDataPinResult UFlowNode_SubGraph::TrySupplyDataPin(FName PinName) const { if (PinName == AssetParams_MemberName) @@ -297,6 +266,37 @@ FFlowDataPinResult UFlowNode_SubGraph::TrySupplyDataPin(FName PinName) const return Super::TrySupplyDataPin(PinName); } +void UFlowNode_SubGraph::PostLoad() +{ + Super::PostLoad(); + + SubscribeToAssetChanges(); +} + +void UFlowNode_SubGraph::PreEditChange(FProperty* PropertyAboutToChange) +{ + Super::PreEditChange(PropertyAboutToChange); + + if (PropertyAboutToChange->GetFName() == GET_MEMBER_NAME_CHECKED(UFlowNode_SubGraph, Asset)) + { + if (Asset) + { + Asset->OnSubGraphReconstructionRequested.Unbind(); + } + } +} + +void UFlowNode_SubGraph::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + if (PropertyChangedEvent.Property && PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(UFlowNode_SubGraph, Asset)) + { + OnReconstructionRequested.ExecuteIfBound(); + SubscribeToAssetChanges(); + } +} + void UFlowNode_SubGraph::SubscribeToAssetChanges() { if (Asset) diff --git a/Source/Flow/Private/Types/FlowAutoDataPinsWorkingData.cpp b/Source/Flow/Private/Types/FlowAutoDataPinsWorkingData.cpp index f003630f6..38360d169 100644 --- a/Source/Flow/Private/Types/FlowAutoDataPinsWorkingData.cpp +++ b/Source/Flow/Private/Types/FlowAutoDataPinsWorkingData.cpp @@ -10,9 +10,6 @@ bool FFlowAutoDataPinsWorkingData::AutoGenerateDataPinsForFlowNode(UFlowNode& FlowNode, bool& bAutoInputDataPinsChanged, bool& bAutoOutputDataPinsChanged) { - // Allow the node to auto-generate data pins - FlowNode.AutoGenerateDataPins(*this); - DisambiguateAndRebuildDataPinPropertySourceMap(FlowNode); bAutoInputDataPinsChanged = DidAutoInputDataPinsChange(); @@ -63,7 +60,7 @@ void FFlowAutoDataPinsWorkingData::BuildNextFlowPinArray(const TArray PropertyIt(Class); PropertyIt; ++PropertyIt) { AddFlowDataPinForProperty(*PropertyIt, ObjectContainer, PropertyOwnerIndex); diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index a34d545cf..2d206a9d0 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -152,9 +152,6 @@ class FLOW_API UFlowAsset : public UObject void HarvestNodeConnections(UFlowNode* TargetNode = nullptr); static bool TryGetDefaultForInputPinName(const FStructProperty& StructProperty, const void* Container, FString& OutString); - - /* Updates the auto-generated pins and bindings for a given FlowNode, returns true if any changes were made. */ - static bool TryUpdateManagedFlowPinsForNode(UFlowNode& FlowNode); #endif public: diff --git a/Source/Flow/Public/Interfaces/FlowDataPinGeneratorInterface.h b/Source/Flow/Public/Interfaces/FlowDataPinGeneratorInterface.h deleted file mode 100644 index ff9dfb92d..000000000 --- a/Source/Flow/Public/Interfaces/FlowDataPinGeneratorInterface.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors -#pragma once - -#include "UObject/Interface.h" - -#include "FlowDataPinGeneratorInterface.generated.h" - -struct FFlowAutoDataPinsWorkingData; - -/** - * Interface for Classes that can auto-generate DataPins. - */ -UINTERFACE(MinimalAPI, NotBlueprintable, DisplayName = "Flow Data Pin Generator Interface", meta = (CannotImplementInterfaceInBlueprint)) -class UFlowDataPinGeneratorInterface : public UInterface -{ - GENERATED_BODY() -}; - -class FLOW_API IFlowDataPinGeneratorInterface -{ - GENERATED_BODY() - -public: -#if WITH_EDITOR - virtual void AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const = 0; -#endif -}; diff --git a/Source/Flow/Public/Nodes/FlowNode.h b/Source/Flow/Public/Nodes/FlowNode.h index 82b675481..e19eb1ffe 100644 --- a/Source/Flow/Public/Nodes/FlowNode.h +++ b/Source/Flow/Public/Nodes/FlowNode.h @@ -8,13 +8,13 @@ #include "FlowNodeBase.h" #include "FlowTypes.h" -#include "Interfaces/FlowDataPinGeneratorInterface.h" #include "Interfaces/FlowDataPinValueSupplierInterface.h" #include "Nodes/FlowPin.h" #include "Types/FlowArray.h" - #include "FlowNode.generated.h" +struct FFlowAutoDataPinsWorkingData; + /** * Entry in MapDataPinNameToPropertySource for how to source a non-trivial pin mapping in TryGatherPropertyOwnersAndPopulateResult. */ @@ -38,7 +38,6 @@ struct FFlowPinPropertySource */ UCLASS(Abstract, Blueprintable, HideCategories = Object) class FLOW_API UFlowNode : public UFlowNodeBase - , public IFlowDataPinGeneratorInterface , public IFlowDataPinValueSupplierInterface , public IVisualLoggerDebugSnapshotInterface { @@ -239,7 +238,14 @@ class FLOW_API UFlowNode : public UFlowNodeBase public: using TFlowPinValueSupplierDataArray = FlowArray::TInlineArray; + /* Map for PinName to Property supplier for non-trivial data pin property lookups. + * Non-trivial means a different pin name from its property source, or a non-zero property owner object index. + * See TryGatherPropertyOwnersAndPopulateResult(). */ + UPROPERTY() + TMap MapDataPinNameToPropertySource; + #if WITH_EDITORONLY_DATA +protected: UPROPERTY(VisibleDefaultsOnly, AdvancedDisplay, Category = "FlowNode", meta = (GetByRef)) TArray AutoInputDataPins; @@ -247,20 +253,10 @@ class FLOW_API UFlowNode : public UFlowNodeBase TArray AutoOutputDataPins; #endif - /* Map for PinName to Property supplier for non-trivial data pin property lookups. - * Non-trivial means a different pin name from its property source, or a non-zero property owner object index. - * See TryGatherPropertyOwnersAndPopulateResult(). */ - UPROPERTY() - TMap MapDataPinNameToPropertySource; - #if WITH_EDITOR - void SetAutoInputDataPins(const TArray& AutoInputPins); - void SetAutoOutputDataPins(const TArray& AutoOutputPins); - const TArray& GetAutoInputDataPins() const { return AutoInputDataPins; } - const TArray& GetAutoOutputDataPins() const { return AutoOutputDataPins; } - - TArray& GetMutableAutoInputDataPins() { return AutoInputDataPins; } - TArray& GetMutableAutoOutputDataPins() { return AutoOutputDataPins; } +public: + bool TryUpdateAutoDataPins(); + virtual void AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const; #endif // IFlowDataPinValueSupplierInterface @@ -301,13 +297,6 @@ class FLOW_API UFlowNode : public UFlowNodeBase bool TryGetFlowDataPinSupplierDatasForPinName(const FName& PinName, TFlowPinValueSupplierDataArray& InOutPinValueSupplierDatas) const; - // IFlowDataPinGeneratorInterface -#if WITH_EDITOR -public: - virtual void AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const override; -#endif - // -- - // #FlowDataPinLegacy public: void FixupDataPinTypes(); diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h b/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h index 52e34ef11..9f919042b 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h @@ -28,17 +28,15 @@ class FLOW_API UFlowNode_DefineProperties #if WITH_EDITOR // IFlowContextPinSupplierInterface - virtual bool SupportsContextPins() const override { return Super::SupportsContextPins() || !NamedProperties.IsEmpty(); } + virtual bool SupportsContextPins() const override; // -- + virtual void AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const override; + // UObject virtual void PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent) override; // -- - // IFlowDataPinGeneratorInterface - virtual void AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const override; - // -- - // IFlowNamedPropertiesSupplierInterface virtual TArray& GetMutableNamedProperties() override { return NamedProperties; } // -- diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h b/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h index ddc5c15da..91d826a41 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h @@ -68,29 +68,27 @@ class FLOW_API UFlowNode_SubGraph : public UFlowNode #if WITH_EDITOR public: + virtual FText K2_GetNodeTitle_Implementation() const override; + virtual FString GetNodeDescription() const override; + virtual UObject* GetAssetToEdit() override; + virtual EDataValidationResult ValidateNode() override; + // IFlowContextPinSupplierInterface virtual bool SupportsContextPins() const override { return true; } virtual TArray GetContextInputs() const override; virtual TArray GetContextOutputs() const override; // -- - virtual FText K2_GetNodeTitle_Implementation() const override; - virtual FString GetNodeDescription() const override; - virtual UObject* GetAssetToEdit() override; - virtual EDataValidationResult ValidateNode() override; - - // UObject - virtual void PostLoad() override; - virtual void PreEditChange(FProperty* PropertyAboutToChange) override; - virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; - // -- + virtual void AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const override; // IFlowDataPinValueSupplierInterface virtual FFlowDataPinResult TrySupplyDataPin(FName PinName) const override; // -- - // IFlowDataPinGeneratorInterface - virtual void AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const override; + // UObject + virtual void PostLoad() override; + virtual void PreEditChange(FProperty* PropertyAboutToChange) override; + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; // -- private: diff --git a/Source/FlowEditor/Private/Graph/FlowGraph.cpp b/Source/FlowEditor/Private/Graph/FlowGraph.cpp index e039011db..e94ebf86b 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraph.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraph.cpp @@ -243,8 +243,7 @@ void UFlowGraph::UpgradeAllFlowNodePins() if (IsValid(FlowNode)) { FlowNode->FixupDataPinTypes(); - - FlowAsset->TryUpdateManagedFlowPinsForNode(*FlowNode); + FlowNode->TryUpdateAutoDataPins(); } } } @@ -290,15 +289,15 @@ void UFlowGraph::UpdateAsset(const int32 UpdateFlags) return; } - // UpdateAsset is called to do any reconciliation from the editor-version of the - // graph to the runtime version of the graph data. - // In our case, it will copy the AddOns from their editor-side UFlowGraphNode containers to - // their runtime UFlowNode and/or UFlowNodeAddOn ::AddOn array entry (via OnUpdateAsset) + /* UpdateAsset is called to do any reconciliation from the editor-version of the + * graph to the runtime version of the graph data. + * In our case, it will copy the AddOns from their editor-side UFlowGraphNode containers to + * their runtime UFlowNode and/or UFlowNodeAddOn ::AddOn array entry. */ for (UEdGraphNode* Node : Nodes) { if (UFlowGraphNode* FlowGraphNode = Cast(Node)) { - FlowGraphNode->OnUpdateAsset(UpdateFlags); + FlowGraphNode->RebuildRuntimeAddOnsFromEditorSubNodes(); } } } diff --git a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp index 0b4a20c79..b87e6fcca 100644 --- a/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp +++ b/Source/FlowEditor/Private/Graph/Nodes/FlowGraphNode.cpp @@ -322,12 +322,11 @@ void UFlowGraphNode::ReconstructNode() bIsReconstructingNode = true; FScopedTransaction Transaction(LOCTEXT("ReconstructNode", "Reconstruct Node"), !GUndo); - const bool bNodeDataPinsUpdated = TryUpdateAutoDataPins(); // This must be called first, it updates the underlying data for data pins of the Flow Node - const bool bNodeExecPinsUpdated = TryUpdateNodePins(); // Updates all pins of the Flow Node (native pins, meta auto pins, and context pins which include data pins for now) + const bool bAnyPinsUpdated = TryUpdateNodePins(); // Updates all pins of the Flow Node (native pins, meta auto pins, and context pins which include data pins for now) const bool bAreGraphPinsMismatched = !CheckGraphPinsMatchNodePins(); // This must be called last since it checks the existing graph node against the cleaned up Flow Node instance - const bool bGraphNodeRequiresReconstruction = bNeedsFullReconstruction || bNodeDataPinsUpdated || bNodeExecPinsUpdated || bAreGraphPinsMismatched; - if (bGraphNodeRequiresReconstruction) + // Does Graph Node requires reconstruction? + if (bNeedsFullReconstruction || bAnyPinsUpdated || bAreGraphPinsMismatched) { Modify(); @@ -348,7 +347,7 @@ void UFlowGraphNode::ReconstructNode() DestroyPin(OldPin); } - // clear breakpoints for destroyed pins + // Clear breakpoints for destroyed pins if (UFlowDebuggerSubsystem* DebuggerSubsystem = GEngine->GetEngineSubsystem()) { DebuggerSubsystem->RemoveObsoletePinBreakpoints(this); @@ -1847,20 +1846,25 @@ bool UFlowGraphNode::TryUpdateNodePins() const return true; } - bool bIsLoad = false; - if (const UFlowGraph* FlowGraph = GetFlowGraph()) - { - bIsLoad = FlowGraph->IsLoadingGraph(); - } + // Attempt to update auto-generated pins + // This must be called first, it updates the underlying data for data pins of the Flow Node + const bool bAutoDataPinsChanged = FlowNodeInstance->TryUpdateAutoDataPins(); - // Confirm that we should be refreshing context pins - const bool bIsAllowedToRefreshPins = !bIsLoad || NodeInstance->CanRefreshContextPinsOnLoad(); - const bool bShouldConsiderRefreshingContextPins = bIsAllowedToRefreshPins && (SupportsContextPins()); - const bool bShouldRefreshContextPins = bShouldConsiderRefreshingContextPins || bNeedsFullReconstruction; - - if (!bShouldRefreshContextPins) + // these check would be all ignored if a full reconstruction has been requested + if (!bNeedsFullReconstruction) { - return false; + bool bLoadingGraph = false; + if (const UFlowGraph* FlowGraph = GetFlowGraph()) + { + bLoadingGraph = FlowGraph->IsLoadingGraph(); + } + + // Confirm that we should be refreshing context pins + const bool bShouldRefreshContextPins = SupportsContextPins() && (!bLoadingGraph || NodeInstance->CanRefreshContextPinsOnLoad() || bAutoDataPinsChanged); + if (!bShouldRefreshContextPins) + { + return false; + } } // ------------ @@ -1919,22 +1923,6 @@ bool UFlowGraphNode::TryUpdateNodePins() const return bPinsChanged; } -bool UFlowGraphNode::TryUpdateAutoDataPins() const -{ - // Attempt to update the manged / auto-generated pins - UFlowAsset* FlowAsset = NodeInstance->GetFlowAsset(); - UFlowNode* FlowNodeInstance = Cast(NodeInstance); - if (FlowAsset && FlowNodeInstance) - { - if (FlowAsset->TryUpdateManagedFlowPinsForNode(*FlowNodeInstance)) - { - return true; - } - } - - return false; -} - bool UFlowGraphNode::CheckGraphPinsMatchNodePins() const { const UFlowNode* FlowNodeInstance = Cast(NodeInstance); diff --git a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h index 3bd390a23..69c527483 100644 --- a/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h +++ b/Source/FlowEditor/Public/Graph/Nodes/FlowGraphNode.h @@ -169,7 +169,6 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode bool CanReconstructNode() const; bool TryUpdateNodePins() const; - bool TryUpdateAutoDataPins() const; bool CheckGraphPinsMatchNodePins() const; ////////////////////////////////////////////////////////////////////////// @@ -239,7 +238,6 @@ class FLOWEDITOR_API UFlowGraphNode : public UEdGraphNode void SetParentNodeForSubNode(UFlowGraphNode* InParentNode); UFlowGraphNode* GetParentNode() const { return ParentNode; } - void OnUpdateAsset(int32 UpdateFlags) { RebuildRuntimeAddOnsFromEditorSubNodes(); } void RebuildRuntimeAddOnsFromEditorSubNodes(); static void DiffSubNodes(const FText& NodeTypeDisplayName, const TArray& LhsSubNodes, const TArray& RhsSubNodes, FDiffResults& Results); From 84845a0b8ee566c08e9b1527b8957a4d38fbc1ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Mon, 16 Feb 2026 21:23:47 +0100 Subject: [PATCH 413/485] fixed class layout --- .../Nodes/Graph/FlowNode_DefineProperties.cpp | 127 +++++++++--------- .../Nodes/Graph/FlowNode_DefineProperties.h | 4 +- 2 files changed, 67 insertions(+), 64 deletions(-) diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_DefineProperties.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_DefineProperties.cpp index 064b72e45..a7980a27c 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_DefineProperties.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_DefineProperties.cpp @@ -36,73 +36,12 @@ void UFlowNode_DefineProperties::PostLoad() } } +#if WITH_EDITOR bool UFlowNode_DefineProperties::SupportsContextPins() const { return Super::SupportsContextPins() || !NamedProperties.IsEmpty(); } -void UFlowNode_DefineProperties::AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const -{ - Super::AutoGenerateDataPins(InOutWorkingData); - - for (const FFlowNamedDataPinProperty& DataPinProperty : NamedProperties) - { - if (DataPinProperty.IsValid()) - { - const FFlowDataPinValue& DataPinValue = DataPinProperty.DataPinValue.Get(); - const FName PropertyOwnerObjectName = GetFName(); - constexpr int32 PropertyOwnerIndex = 0; - - if (DataPinValue.IsInputPin()) - { - InOutWorkingData.AutoInputDataPinsNext.Add(FFlowPinSourceData(DataPinProperty.CreateFlowPin(), PropertyOwnerObjectName, PropertyOwnerIndex, &DataPinValue)); - } - else - { - InOutWorkingData.AutoOutputDataPinsNext.Add(FFlowPinSourceData(DataPinProperty.CreateFlowPin(), PropertyOwnerObjectName, PropertyOwnerIndex, &DataPinValue)); - } - } - } -} - -bool UFlowNode_DefineProperties::TryFindPropertyByPinName( - const UObject& PropertyOwnerObject, - const FName& PinName, - const FProperty*& OutFoundProperty, - TInstancedStruct& OutFoundInstancedStruct) const -{ - // The start node stores its properties in instanced structs in an array, so look there first - - for (const FFlowNamedDataPinProperty& NamedProperty : NamedProperties) - { - if (NamedProperty.Name == PinName && NamedProperty.IsValid()) - { - OutFoundInstancedStruct = NamedProperty.DataPinValue; - - return true; - } - } - - return Super::TryFindPropertyByPinName(PropertyOwnerObject, PinName, OutFoundProperty, OutFoundInstancedStruct); -} - -#if WITH_EDITOR -void UFlowNode_DefineProperties::OnPostEditEnsureAllNamedPropertiesPinDirection(const FProperty& Property, bool bIsInput) -{ - if (Property.GetFName() == GET_MEMBER_NAME_CHECKED(ThisClass, NamedProperties)) - { - for (FFlowNamedDataPinProperty& NamedProperty : NamedProperties) - { - const UScriptStruct* ScriptStruct = NamedProperty.DataPinValue.GetScriptStruct(); - if (IsValid(ScriptStruct) && ScriptStruct->IsChildOf()) - { - FFlowDataPinValue& Value = NamedProperty.DataPinValue.GetMutable(); - Value.bIsInputPin = bIsInput; - } - } - } -} - void UFlowNode_DefineProperties::PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChainEvent) { Super::PostEditChangeChainProperty(PropertyChainEvent); @@ -153,6 +92,31 @@ void UFlowNode_DefineProperties::PostEditChangeChainProperty(FPropertyChangedCha OnReconstructionRequested.ExecuteIfBound(); } } + + +void UFlowNode_DefineProperties::AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const +{ + Super::AutoGenerateDataPins(InOutWorkingData); + + for (const FFlowNamedDataPinProperty& DataPinProperty : NamedProperties) + { + if (DataPinProperty.IsValid()) + { + const FFlowDataPinValue& DataPinValue = DataPinProperty.DataPinValue.Get(); + const FName PropertyOwnerObjectName = GetFName(); + constexpr int32 PropertyOwnerIndex = 0; + + if (DataPinValue.IsInputPin()) + { + InOutWorkingData.AutoInputDataPinsNext.Add(FFlowPinSourceData(DataPinProperty.CreateFlowPin(), PropertyOwnerObjectName, PropertyOwnerIndex, &DataPinValue)); + } + else + { + InOutWorkingData.AutoOutputDataPinsNext.Add(FFlowPinSourceData(DataPinProperty.CreateFlowPin(), PropertyOwnerObjectName, PropertyOwnerIndex, &DataPinValue)); + } + } + } +} #endif // WITH_EDITOR bool UFlowNode_DefineProperties::TryFormatTextWithNamedPropertiesAsParameters(const FText& FormatText, FText& OutFormattedText) const @@ -179,3 +143,42 @@ bool UFlowNode_DefineProperties::TryFormatTextWithNamedPropertiesAsParameters(co return true; } + +bool UFlowNode_DefineProperties::TryFindPropertyByPinName( + const UObject& PropertyOwnerObject, + const FName& PinName, + const FProperty*& OutFoundProperty, + TInstancedStruct& OutFoundInstancedStruct) const +{ + // The start node stores its properties in instanced structs in an array, so look there first + + for (const FFlowNamedDataPinProperty& NamedProperty : NamedProperties) + { + if (NamedProperty.Name == PinName && NamedProperty.IsValid()) + { + OutFoundInstancedStruct = NamedProperty.DataPinValue; + + return true; + } + } + + return Super::TryFindPropertyByPinName(PropertyOwnerObject, PinName, OutFoundProperty, OutFoundInstancedStruct); +} + +#if WITH_EDITOR +void UFlowNode_DefineProperties::OnPostEditEnsureAllNamedPropertiesPinDirection(const FProperty& Property, bool bIsInput) +{ + if (Property.GetFName() == GET_MEMBER_NAME_CHECKED(ThisClass, NamedProperties)) + { + for (FFlowNamedDataPinProperty& NamedProperty : NamedProperties) + { + const UScriptStruct* ScriptStruct = NamedProperty.DataPinValue.GetScriptStruct(); + if (IsValid(ScriptStruct) && ScriptStruct->IsChildOf()) + { + FFlowDataPinValue& Value = NamedProperty.DataPinValue.GetMutable(); + Value.bIsInputPin = bIsInput; + } + } + } +} +#endif \ No newline at end of file diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h b/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h index 9f919042b..7f606e3b7 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_DefineProperties.h @@ -31,12 +31,12 @@ class FLOW_API UFlowNode_DefineProperties virtual bool SupportsContextPins() const override; // -- - virtual void AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const override; - // UObject virtual void PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent) override; // -- + virtual void AutoGenerateDataPins(FFlowAutoDataPinsWorkingData& InOutWorkingData) const override; + // IFlowNamedPropertiesSupplierInterface virtual TArray& GetMutableNamedProperties() override { return NamedProperties; } // -- From ef42e3e45a99f5b2b8f6791ae5592fac4bd50dca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Tue, 17 Feb 2026 20:14:40 +0100 Subject: [PATCH 414/485] Quick fix: Copy Paste Issue With Comment Sections In Graph #278 The issue was that copied nodes are temporarily created to test, it came with concept of SubNodes. Testing isn't needed for Comment nodes, but there are still somehow "pre-copied". I simply made a quick change to allow for deleting such rogue nodes. --- Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index 8f4656fa9..a4cb51585 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -993,7 +993,7 @@ bool SFlowGraphEditor::CanPasteNodes() const // We need to clean up the nodes we built to test the paste operation for (TSet::TConstIterator It(NodesToPaste); It; ++It) { - UFlowGraphNode* NodeToPaste = Cast(*It); + UEdGraphNode* NodeToPaste = *It; if (IsValid(NodeToPaste)) { NodeToPaste->ClearFlags(RF_Public); From 3b8a9885ae82a1d214b75f154330cc92a03c7f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Wed, 18 Feb 2026 18:01:39 +0100 Subject: [PATCH 415/485] fixed adding empty line for every AddOn added to node happened if AddOn didn't provide a description string --- Source/Flow/Private/Nodes/FlowNodeBase.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Source/Flow/Private/Nodes/FlowNodeBase.cpp b/Source/Flow/Private/Nodes/FlowNodeBase.cpp index 94a134b06..e41056660 100644 --- a/Source/Flow/Private/Nodes/FlowNodeBase.cpp +++ b/Source/Flow/Private/Nodes/FlowNodeBase.cpp @@ -732,10 +732,18 @@ FString UFlowNodeBase::GetNodeDescription() const FString UFlowNodeBase::GetAddOnDescriptions() const { - return FString::JoinBy(AddOns, LINE_TERMINATOR, [](const UFlowNodeBase* Addon) + FString Result; + + for (const UFlowNodeBase* Addon : AddOns) { - return Addon->GetNodeDescription(); - }); + const FString& Description = Addon->GetNodeDescription(); + if (!Description.IsEmpty()) + { + Result.Append(Description).Append(LINE_TERMINATOR); + } + } + + return Result; } bool UFlowNodeBase::CanModifyFlowDataPinType() const From 82afa6a85cdf8d9698048af58ea36a088cfc1ab8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Wed, 18 Feb 2026 18:29:42 +0100 Subject: [PATCH 416/485] creating documentation webiste --- docs/Features/AddingNodeSpawnShortcut.md | 22 ++++++++ docs/Features/AssetSearch.md | 18 +++++++ docs/Features/ForcePinActivation.md | 7 +++ docs/Features/GenericGameplayTagEvents.md | 56 +++++++++++++++++++ docs/Features/SaveGameSupport.md | 22 ++++++++ docs/Features/SignalModes.md | 18 +++++++ docs/Overview/FAQ.md | 26 +++++++++ docs/Overview/GettingStarted.md | 21 ++++++++ docs/Overview/SupportedlEngineVersions.md | 17 ++++++ docs/README.md | 66 +++++++++++++++++++++++ docs/_config.yml | 3 ++ docs/_data/navigation.yml | 23 ++++++++ docs/_includes/head-custom.html | 60 +++++++++++++++++++++ docs/_layouts/default.html | 62 +++++++++++++++++++++ docs/index.html | 7 +++ 15 files changed, 428 insertions(+) create mode 100644 docs/Features/AddingNodeSpawnShortcut.md create mode 100644 docs/Features/AssetSearch.md create mode 100644 docs/Features/ForcePinActivation.md create mode 100644 docs/Features/GenericGameplayTagEvents.md create mode 100644 docs/Features/SaveGameSupport.md create mode 100644 docs/Features/SignalModes.md create mode 100644 docs/Overview/FAQ.md create mode 100644 docs/Overview/GettingStarted.md create mode 100644 docs/Overview/SupportedlEngineVersions.md create mode 100644 docs/README.md create mode 100644 docs/_config.yml create mode 100644 docs/_data/navigation.yml create mode 100644 docs/_includes/head-custom.html create mode 100644 docs/_layouts/default.html create mode 100644 docs/index.html diff --git a/docs/Features/AddingNodeSpawnShortcut.md b/docs/Features/AddingNodeSpawnShortcut.md new file mode 100644 index 000000000..08490f85f --- /dev/null +++ b/docs/Features/AddingNodeSpawnShortcut.md @@ -0,0 +1,22 @@ +1. Create `DefaultEditorPerProjectUserSettings.ini` in your project's Config folder, if you don't have this .ini yet. +2. Add this section to the .ini +``` +[FlowSpawnNodes] +; Flow ++Node=(Class=FlowNode_OnNotifyFromActor Key=A Shift=false Ctrl=false Alt=false) ++Node=(Class=FlowNode_Finish Key=F Shift=false Ctrl=false Alt=false) ++Node=(Class=FlowNode_SubGraph Key=G Shift=false Ctrl=false Alt=false) ++Node=(Class=FlowNode_CustomInput Key=I Shift=false Ctrl=false Alt=false) ++Node=(Class=FlowNode_Log Key=L Shift=false Ctrl=false Alt=false) ++Node=(Class=FlowNode_ExecutionMultiGate Key=M Shift=false Ctrl=false Alt=false) ++Node=(Class=FlowNode_NotifyActor Key=N Shift=false Ctrl=false Alt=false) ++Node=(Class=FlowNode_CustomOutput Key=O Shift=false Ctrl=false Alt=false) ++Node=(Class=FlowNode_Reroute Key=R Shift=false Ctrl=false Alt=false) ++Node=(Class=FlowNode_ExecutionSequence Key=S Shift=false Ctrl=false Alt=false) ++Node=(Class=FlowNode_Timer Key=T Shift=false Ctrl=false Alt=false) +; Comment ++Node=(Name=Comment Key=C Shift=false Ctrl=false Alt=false) +``` +3. Restart the editor. You should see the assigned shortcut in the Palette. + +![BKCt3pDj9s](https://user-images.githubusercontent.com/5065057/114264581-062ee400-99ec-11eb-9ffc-ba3e6d901b3d.png) diff --git a/docs/Features/AssetSearch.md b/docs/Features/AssetSearch.md new file mode 100644 index 000000000..74bfa4fb8 --- /dev/null +++ b/docs/Features/AssetSearch.md @@ -0,0 +1,18 @@ +Feature based on the engine plugin added in UE 4.26. The plugin is marked as beta, probably because of the search performance. + +## Using the feature +- Make sure the `Asset Search` plugin is enabled in your project. +- Open the `Search` tab. You can do this via the `Tools` menu in UE5, or the `Window` menu in UE4. +- If you never used this Search yet, check the status of "missing indexed assets" in the bottom right corner. Click it, and it gonna index all assets supported by the Asset Search. It can take many minutes, depending on your project size. +- Wait until indexing finishes and the status in the bottom left corner is `Ready` again. + +## Limiting Search by asset type +If you'd merge the provided engine modification, you can exclude specific asset types from Asset Search indexing. On the project level (via Project Settings) or user level (Editor Preferences). +* [Added Search Roles to Asset Search settings as a huge time-saver](https://github.com/EpicGames/UnrealEngine/pull/9332) + +Here's the list of asset types supported out of the box, hardcoded in the engine's code. (as of UE 5.0) +![image](https://user-images.githubusercontent.com/5065057/175774889-bf4b3ed4-ba6b-47e2-af17-10320e3da8c1.png) + +## Useful engine modifications +* [Jump from Asset Search result to the node in any graph editor!](https://github.com/EpicGames/UnrealEngine/pull/9882) - set the `ENABLE_JUMP_TO_INNER_OBJECT` value to 1 after integrating this change. +* [Asset Search: added option to run search on single asset, with Search Browser opened as asset editor tab](https://github.com/EpicGames/UnrealEngine/pull/9943) \ No newline at end of file diff --git a/docs/Features/ForcePinActivation.md b/docs/Features/ForcePinActivation.md new file mode 100644 index 000000000..4110a4c9a --- /dev/null +++ b/docs/Features/ForcePinActivation.md @@ -0,0 +1,7 @@ +* It's a debugging feature available from Pin's context menu during PIE. +* Allows pushing the graph execution in case of blockers, i.e. specific node doesn't work for whatever reason and we want to continue playtesting. +* It works both on Input pins and Output pins. You can even trigger unconnected Input pins this way. + +Here's a link to [**a short video presenting how this works**](https://user-images.githubusercontent.com/5065057/198881114-1bdb5a2b-3d08-4e99-b5b4-ad1d226f1a8a.mp4). + +![firefox_bbitKtMkwt](https://user-images.githubusercontent.com/5065057/198881818-87c558c0-fe46-45a2-a540-9b1541cb9505.png) diff --git a/docs/Features/GenericGameplayTagEvents.md b/docs/Features/GenericGameplayTagEvents.md new file mode 100644 index 000000000..6b9350e4f --- /dev/null +++ b/docs/Features/GenericGameplayTagEvents.md @@ -0,0 +1,56 @@ +Nodes available in the Flow plugin support finding actors and communicating with them by Gameplay Tags. +- This can have many advantages over using soft references to actor instances, i.e. changing actor instance name won't break reference. +- This also allows communicating with actors spawned in runtime, i.e. all characters. +- You can call multiple actors identified by the same Gameplay Tag, which may be useful in many cases. + +Obviously, you can use soft references to actors in your project-specific Flow nodes. Although the Flow plugin comes with a mechanism to call events in a generic way, without the need to know the actor's class and functions. + +# Adding Flow Component to Actor +* Add `Flow Component` to the actor. +* Assign a unique gameplay tag on the `Identity Tags` list. + +![image](https://user-images.githubusercontent.com/5065057/176913188-077dc82a-2c7d-4af1-ac30-42d7fba869d5.png) + +# Calling Actor events from the Flow Graph +* Add the `Receive Notify` event from the Flow Component. + +![image](https://user-images.githubusercontent.com/5065057/176913685-180f9f6c-a17e-4097-8e8f-2984a89efaae.png) +* Place the `Notify Actor` node in your Flow Graph. + * `Identity Tags` should include the Identity Tag assigned in the Flow Component. + * `Notify Tag` is an optional tag that would be sent through the `Receive Notify` event to your actor. It allows you to call different events in the same actor. + +![image](https://user-images.githubusercontent.com/5065057/176920012-98c34c6c-fa7c-43b7-a5ce-be928eae24a3.png) + +# Calling Flow Graph event from the Actor +* Call `Notify Graph` from the Flow Component. + +![image](https://user-images.githubusercontent.com/5065057/176914462-d91f43a3-722d-4c77-bc83-48ee69d0525b.png) +* Place the `On Notify From Actor` node in your Flow Graph. + * `Identity Tags` should include the Identity Tag assigned in the Flow Component. + * `Notify Tag` is optional. If added, it needs to match the tag selected on Notify Graph in the step above. Otherwise, the `Success` output won't be triggered. + * If `Notify Tag` will be empty, the Notify node won't check what tag has been sent from the actor. The `Success` output will always be triggered. + +![image](https://user-images.githubusercontent.com/5065057/176920480-9341ac92-b96d-448d-976a-5590e14ae20b.png) + +# Calling events between Actors +* Source Actor: call `Notify Actor` from the Flow Component. + * `Notify Tag` is optional. It allows you to call different events in the target actor. + +![image](https://user-images.githubusercontent.com/5065057/176920761-d7f10c83-e11f-4955-85d5-3caf70f20c49.png) +* Target Actor: add the `Receive Notify` event from the Flow Component. + +![image](https://user-images.githubusercontent.com/5065057/176921316-bc021dcc-a024-4a56-923e-c2a19889c58e.png) + +# Calling Flow Graph events from Sequencer +* Add the `Flow Events` track to the Level Sequence. + * Simply place a key on the section and give it a name. + +![image](https://user-images.githubusercontent.com/5065057/176922388-60e42447-a6aa-4cd8-a67d-5e2ac1be5280.png) + +* Include this Level Sequence on any `Play Level Sequence` node. + * Either use the `Refresh Asset` button on the toolbar or the `Refresh Context Pins` option on the node. You need to use it after every change of event names, adding or removing events from the timeline. + +![image](https://user-images.githubusercontent.com/5065057/176922906-a0a8b327-8227-4ba0-a088-0e1d33370d2d.png) + +![image](https://user-images.githubusercontent.com/5065057/176923030-1d906f00-44fa-4812-8e04-2b8550828aff.png) + diff --git a/docs/Features/SaveGameSupport.md b/docs/Features/SaveGameSupport.md new file mode 100644 index 000000000..de332e932 --- /dev/null +++ b/docs/Features/SaveGameSupport.md @@ -0,0 +1,22 @@ +Flow Graph plugs into Unreal's `SaveGame` system. If you haven't used it yet, read ["Saving and Loading Your Game" docs](https://dev.epicgames.com/documentation/en-us/unreal-engine/saving-and-loading-your-game-in-unreal-engine?application_version=5.0). + +You control which properties are included in SaveGame by marking C++ properties with the `SaveGame` specifier. Or by ticking the `SaveGame` checkbox in the blueprint editor. + +## What to look for? +* `FlowSave.h`. Active graphs are serialized to the `UFlowSaveGame` object, which extends the engine's `USaveGame`. That allows you to integrate Flow Graph into your SaveGame setup. +* `UFlowSubsystem` keeps a registry of all active Flow Graphs at the given moment. That's why it also contains methods providing SaveGame support. To support Flow Graph in your save system, you need to call `OnGameSaved` and `OnGameLoaded` methods. These are accessible from blueprints. +* `UFlowNode` class provides overridable events `OnSave` and `OnLoad`, so you can add custom SaveGame logic to any node, like restoring Timer with "RemainingTime" value read from SaveGame. Check the `UFlowNode_Timer` class for reference. +* `UFlowNode_Checkpoint` node is a built-in example of implementing autosave called from quests. +* `UFlowAsset` and `UFlowComponent` expose similar `OnSave` and `OnLoad` events, so you should be able to customize SaveGame logic in every plugin's class that's involved in SaveGame operations. + +[Signal Modes](https://github.com/MothCocoon/FlowGraph/wiki/Signal-Modes) features provide a solution for modifying Flow Graphs post-launch, once players already have SaveGames with serialized graph state. + +## Quick sample +You can find a quick example of integrating Flow into your SaveGame setup in the `FlowSolo` demo project. Here are [simple C++ classes related to this](https://github.com/MothCocoon/FlowSolo/tree/main/Source/FlowSolo/Public/Core). + +Note: You might need to call `UFlowSubsystem::LoadRootFlow` manually on your Root Flow owners if you're loading a game while the world is already active. Flow Component automatically calls `LoadRootFlow` only on BeginPlay! Supporting in-game loading is up to you. + +## Support for graphs not instantiated from the Flow Component +It's possible to create Root Flow for any UObject owner, like Player Controller or a subsystem. If these objects don't include the Flow Component, supporting Save/Load logic requires a bit more work. +* You need to call `UFlowSubsystem::LoadRootFlow` on this custom owner after deserializing the SaveGame with `UFlowSubsystem::OnGameLoaded`. Look at the sample code linked above. You need to iterate on owners if they don't include the Flow Component's logic. +* If your Root Flow is created on an UObject owner that doesn't belong to the world (Game Instance or its subsystem), you need to set the `bWorldBound` property on your Flow Asset to False. \ No newline at end of file diff --git a/docs/Features/SignalModes.md b/docs/Features/SignalModes.md new file mode 100644 index 000000000..359881b87 --- /dev/null +++ b/docs/Features/SignalModes.md @@ -0,0 +1,18 @@ +The Signal Modes concept was born from the need to support patching already released games. Flow Graphs serialize the state of their active nodes to a SaveGame. This works well, but there's one inherent limitation. If a player has a SaveGame with a given Flow Node active and serialized to a SaveGame, we cannot remove this node from the graph post-launch. Hence, we shouldn't remove any node post-launch to be perfectly safe. + +### Basics + +Yet we sometimes need to change the logic of our game in patches. Signal Modes provide a solution for that. It allows designers to mark nodes as: +* **Enabled** - Default state, node is fully executed. +* **Disabled** - No logic executed, any Input Pin activation is ignored. Node instantly enters a deactivated state. +* **PassThrough** - Internal node logic not executed. All connected outputs are triggered, node finishes its work. + +This way, we can deactivate nodes without removing them from the graph, so we can properly continue graph execution from a legacy SaveGame. Pin connections aren't serialized to a SaveGame, so we can safely change connections on pass-through nodes anyway we need. + +![image](https://user-images.githubusercontent.com/5065057/201344208-2020cdad-ae0d-4df6-bd06-25dddc1c88aa.png) + +### Allowed Signal Modes +The node author can limit a list of available signal nodes for a given Flow Node class. +Some nodes are already acting like pass-through nodes by design, like Reroute or Sequence. It would be redundant or confusing to mark them as PassThrough, so we removed that from the list of available signal modes. + +`AllowedSignalModes = {EFlowSignalMode::Enabled, EFlowSignalMode::Disabled};` \ No newline at end of file diff --git a/docs/Overview/FAQ.md b/docs/Overview/FAQ.md new file mode 100644 index 000000000..0eb6f0f31 --- /dev/null +++ b/docs/Overview/FAQ.md @@ -0,0 +1,26 @@ +## How to separate the logic between many graphs? +The basic and recommended way is to use the `Sub Graph` node to start graph B from graph A. This way we could have very complex logic, i.e. separate graphs for every quest or event. + +![image](https://user-images.githubusercontent.com/5065057/204156931-8f21eb1a-d438-4430-8e8a-f1365e4528ea.png) + +## Is it possible to have a Flow Graph independent from the world? +Yes, it's an everyday use case, i.e. if you want to have a global "meta-quest" (controlling the game's story) or graph that is tracking achievements. You can create a Flow Asset instance from anywhere in the game by calling `UFlowSubsystem::StartRootFlow`. + +![image](https://user-images.githubusercontent.com/5065057/150850841-3827e785-dd71-4c08-9ab7-aa1a94631052.png) + +## Are there plans to support variables similar to blueprints? +Personally, I have no plans to implement it. The community is free to tackle this challenge. You can check [Discord channel](https://discord.com/channels/742802606874820619/905414688093904956) where people discuss their implementations. + +It's not obvious which way to choose as a proper solution for all (at least most) projects. It could be +- blueprint-based variables +- blackboard-style +- blueprint-style, but using a method similar to @ryanjon2040 pull request: https://github.com/MothCocoon/FlowGraph/pull/86 +- also might take inspiration from StateTree implementation in UE5 + +Flow Graph solution would need to include support for UObject references. + +## My Flow Node blueprint isn't visible in the Palette? +This happens if a given blueprint was created with a standard blueprint creation menu. You have to create blueprint nodes via a dedicated Content Browser menu item. (Similar inconvenience applies to creating Editor Blutilites, which won't run if created via the standard blueprint menu). + +![image](https://user-images.githubusercontent.com/5065057/204156900-87fe32cd-daa9-4bd4-9e7f-a42ead0d0443.png) + diff --git a/docs/Overview/GettingStarted.md b/docs/Overview/GettingStarted.md new file mode 100644 index 000000000..afb975888 --- /dev/null +++ b/docs/Overview/GettingStarted.md @@ -0,0 +1,21 @@ +## Sample projects +There's a separate repository including [a sample single-player project called FlowGame](https://github.com/MothCocoon/FlowSolo), so you can easily check how this plug-in works. It includes +* Flow plugin +* Additional C++ Flow nodes, as `FlowQuest` plugin. +* Simple map with a few Flow Graphs. + +## Adding Flow Graph to your project +1. Unpack the plugin to the Plugins folder in your project folder. If you don't have such a folder yet, simply create it. +2. First of all, open Project Settings in the editor. Change `World Settings` to the `Flow World Settings` class. Restart the editor. That class starts the Flow assigned to the map, just when starting the game. So it serves as a replacement for BeginPlay in the level blueprint. +3. You can assign a Flow Asset to the map via the Flow toolbar above the main viewport. Assigning it directly via the World Settings editor window also works. + +It's crucial to perform Step 3 after Step 2. Otherwise, a reference to the Flow Asset might be improperly saved in the map and wouldn't work. If that happens, just try to revert your map. And assign the Flow Asset again. + +## Including GitHub repository in your repository +You can include this plugin repository as a dependency. It can be done by using Git submodules. +``` +git submodule add -b 5.0 https://github.com/MothCocoon/FlowGraph.git Plugins/Flow +``` + +## Useful tools +* [Subsystem Browser plugin](https://github.com/aquanox/SubsystemBrowserPlugin) works really well with Flow Graph, as it displays runtime data of subsystems like Flow Subsystem. \ No newline at end of file diff --git a/docs/Overview/SupportedlEngineVersions.md b/docs/Overview/SupportedlEngineVersions.md new file mode 100644 index 000000000..ee374d99c --- /dev/null +++ b/docs/Overview/SupportedlEngineVersions.md @@ -0,0 +1,17 @@ +| Unreal Engine | Flow Graph | +| :-----------: | :--------: | +| 5.7 | 2.2+ | +| 5.6 | 2.1+ | +| 5.5 | 2.0-2.2 | +| 5.4 | 1.6-2.1 | +| 5.3 | 1.5-2.0 | +| 5.2 | 1.4-1.6 | +| 5.1 | 1.3-1.6 | +| 5.0 | 1.2-1.5 | +| 5 EA | 1.1 | +| 4.27 | 1.1-1.3 | +| 4.26 | 1.0-1.3 | +| 4.25 | 1.0 - 1.2 | +| 4.24 | 1.0 - 1.1 | +| 4.23 | 1.0 - 1.1 | +| 4.22 | 1.0 - 1.1 | \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..7313b09b2 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,66 @@ +## Concept +Flow plug-in for Unreal Engine provides a graph editor tailored for scripting flow of events in virtual worlds. It's based on a decade of experience with designing and implementing narrative in video games. All we need here is simplicity. + +It's a design-agnostic event node editor. + +![Flow101](https://user-images.githubusercontent.com/5065057/103543817-6d924080-4e9f-11eb-87d9-15ab092c3875.png) + +* A single node in this graph is a simple UObject, not a function like in blueprints. This allows you to encapsulate the entire gameplay element (logic with its data) within a single Flow Node. The idea is to write a reusable "event script" only once for the entire game! +* Unlike blueprints, Flow Node is async/latent by design. Active nodes usually subscribe to delegates, so they can react to an event by triggering the output pin (or whatever you choose). +* Every node defines its own set of input/output pins. It's dead simple to design the flow of the game - connect nodes representing features. +* Developers creating a Flow Node can call the execution of pins in any way they need. API is extremely simple. +* Editor supports conveniently displaying debug information on nodes and wires while playing a game. You provide what kind of message would be displayed over active Flow Nodes. You can't have that with blueprint functions. +* Works well with the World Partition introduced with UE 5.0 - a truly open-world way of building maps where every actor instance is saved separately to disk. In this model, there are no sublevels anymore, so there are no level blueprints anymore! + +## Base for your own systems and tools +* It's up to you to add game-specific functionalities by writing your nodes and editor customizations. It's not like a marketplace providing the very specific implementation of systems. It's a convenient base for building systems tailored to fit your needs. +* Quickly build your own Quest system, Dialogue system, or any other custom system that would control the flow of events in the game. +* Expand it, build Articy:Draft equivalent right in the Unreal Engine. + +## In-depth video presentation +This 24-minute presentation breaks down the concept of the Flow Graph. It goes through everything written here, but in greater detail. + +[![Introducing Flow Graph for Unreal Engine](https://img.youtube.com/vi/Rj76JP1f-I4/0.jpg)](https://www.youtube.com/watch?v=BAqhccgKx_k) + +## Simplicity is a key +* It's all about simplifying the cooperation between gameplay programmers and content designers by providing a clean interface between "code of systems" and "using systems". +* The code of gameplay mechanics wouldn't ever be mixed. Usually, system X shouldn't even know about the existence of system Y. Flow Graph is a place to combine features by connecting nodes. +* Every mechanic is exposed to content designers once, in one way only - as the Flow Node. It greatly reduces the number of bugs. Refactoring mechanics is easy since you don't have to update dozens of level blueprints by directly calling system functions. +* Systems based on such an editor are simple to use for the least technical team members, like narrative designers, writers, QA. Every time I ask designers why they love working with such a system, they usually reply: "It's so simple to understand and make a game with it". +* Even a complex game might end up with a few dozen Flow Nodes. It's easy to manage the game's complexity - a lot of mechanics, mission scripting, narrative events. It makes it very efficient to develop lengthy campaigns and multiplayer games. + +## Blueprints +* A programmer writing a new gameplay feature can quickly expose it to content creators by creating a new Flow Node. A given C++ feature doesn't have to be exposed to blueprints at all. +* However, Flow Nodes can be created in blueprints by anyone. Personally, I would recommend using blueprint nodes mostly for prototyping and rarely used custom actions, if you have a gameplay programmer in your team. If not, sure, you can implement your systems in blueprints entirely. + +## Performance +* Performance loss in blueprint graphs comes from executing a large network of nodes, processing pins and connections between them. Moving away from overcomplicated level blueprints and messy "system blueprints" to simple Flow Graphs might improve framerate and memory management. +* As Flow Nodes are designed to be event-based, executing graph connections might happen only a few times per minute or so. (heavily depends on your logic and event mechanics). Finally, Flow Graph has its own execution logic, doesn't utilize blueprint VM. +* Flow-based event systems are generally more performant than blueprint counterparts. Especially if frequently used nodes are implemented in C++. + +## Flexibility of the system design +Flow Graph communicates with actors in the world by using [Gameplay Tags](https://docs.unrealengine.com/en-US/Gameplay/Tags/index.html). No direct references to actors are used in this variant of scripting. That brings a lot of new possibilities. +* Simply add a Flow Component to every "event actor", and assign Gameplay Tags identifying this actor. Flow Component registers itself with the Flow Subsystem (or any derived system) when it appears in the world. It's easy to find any event actor this way, just ask the Flow Subsystem for actors registered with a given Gameplay Tag. +* It allows for reusing entire Flow Graphs in different maps. Unlike level blueprints, Flow Graphs aren't bound to levels. +* It's possible to place actors used by the single Flow Graph in different sublevels or even worlds. This removes one of the workflow limitations related to the level design. +* Flow Graph could live as long as the game session, not even bound to a specific world. You can have a meta Flow Graph waiting for events happening anywhere during the game. +* Using Gameplay Tags allows scripting an action on any actor spawned in runtime, typically NPCs. +* In some cases actor with a given Gameplay Tag doesn't even have to exist when starting a related action! Example: On Trigger Enter in the image above would pick up the required trigger after loading a sublevel with this trigger. + +## Recommended workflow +* Flow Graph is meant to entirely replace the need to use Level Blueprints (also known as Flying Spaghetti Monster) in production maps. The flow of the game - the connection between consecutive events and actors - should be scripted by using Flow Graphs only. Otherwise, you end up creating a mess, using multiple tools for the same job. +* This graph also entirely replaces another way of doing things: referencing different actors directly, i.e. hooking up Spawner actor directly to the Trigger actor. Technically, it works fine, but it's impossible to read the designed flow of events scripted this way. Debugging can be very cumbersome and time-consuming. +* Actor blueprints are supposed to be used only to script the inner logic of actors, not connections between actors belonging to different systems. +* Flow Nodes can send and receive blueprint events via the Flow Component. This recommended way of communicating between Flow Graphs and blueprints. +* Technically, it's always possible to call custom blueprint events directly from a blueprint Flow Node, but this would require creating a new Flow Node for every custom blueprint actor. Effectively, you would throw the simplicity of Flow Graph out of the window. + +## Related resources +* [Introduction to Gameplay Tags](https://docs.unrealengine.com/en-US/ProgrammingAndScripting/Tags/index.html) +* [Behind the Scenes of the Cinematic Dialogues in The Witcher 3: Wild Hunt](https://www.youtube.com/watch?v=chf3REzAjgI) +* [Story of Choices: the quest system in Dying Light 2](https://www.youtube.com/watch?v=DPcz_-m3SwQ) +* [Sinking City - story scripting for the open-world game](https://youtu.be/W_yiopwoXt0?t=929) as part of their talk on Sinking City development +* [Large worlds in UE5: A whole new (open) world](https://www.youtube.com/watch?v=ZxJ5DG8Ytog) - describes World Partition and related features +* [Blueprints In-depth - Part 1 | Unreal Fest Europe 2019](https://youtu.be/j6mskTgL7kU?t=1048) - great talk on blueprint system, the timestamp at the Performance part. +* [Blueprints In-depth - Part 2 | Unreal Fest Europe 2019](https://www.youtube.com/watch?v=0YMS2wnykbc) +* [The Visual Logger: For All Your Gameplay Needs!](https://www.youtube.com/watch?v=hWpbco3F4L4) +* [Gamedec exemplifies how to incorporate complex branching pathways using Unreal Engine](https://www.unrealengine.com/en-US/tech-blog/gamedec-exemplifies-how-to-incorporate-complex-branching-pathways-using-unreal-engine) - example of how the integration of Articy:Draft with Unreal Engine looks like. diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 000000000..097f5101d --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1,3 @@ +theme: jekyll-theme-midnight +title: Flow Graph +description: Design-agnostic node system for scripting game’s flow in Unreal Engine. diff --git a/docs/_data/navigation.yml b/docs/_data/navigation.yml new file mode 100644 index 000000000..80589eae7 --- /dev/null +++ b/docs/_data/navigation.yml @@ -0,0 +1,23 @@ +- title: Overview + docs: + - title: FAQ + url: /Overview/FAQ + - title: Getting Started + url: /Overview/GettingStarted + - title: Supported Engine Versions + url: /Overview/SupportedlEngineVersions + +- title: Features + docs: + - title: Adding Node Spawn Shortcut + url: /Features/AddingNodeSpawnShortcut + - title: Asset Search + url: /Features/AssetSearch + - title: Force Pin Activation + url: /Features/ForcePinActivation + - title: Generic Gameplay Tag Events + url: /Features/GenericGameplayTagEvents + - title: Save Game Support + url: /Features/SaveGameSupport + - title: Signal Modes + url: /Features/SignalModes diff --git a/docs/_includes/head-custom.html b/docs/_includes/head-custom.html new file mode 100644 index 000000000..bb9670fa5 --- /dev/null +++ b/docs/_includes/head-custom.html @@ -0,0 +1,60 @@ + diff --git a/docs/_layouts/default.html b/docs/_layouts/default.html new file mode 100644 index 000000000..43d0c7db1 --- /dev/null +++ b/docs/_layouts/default.html @@ -0,0 +1,62 @@ + + + + + + +{% seo %} + + + + + + + {% include head-custom.html %} + + + + +
+ + + +
+
+

{{ site.title | default: site.github.repository_name }}

+

{{ site.tagline | default: site.description | default: site.github.project_tagline }}

+
+ Project maintained by {{ site.github.owner_name }} + Hosted on GitHub Pages — Theme by mattgraham +
+ + {{ content }} + +
+ +
+ + diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 000000000..74e042c63 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,7 @@ +--- +layout: default +title: Flow Graph Docs +--- + +{% capture readme %}{% include_relative README.md %}{% endcapture %} +{{ readme | markdownify }} From 0becc63bd947754042f1e2b90f6026f5e0153dcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Justy=C5=84ski?= Date: Wed, 18 Feb 2026 20:00:13 +0100 Subject: [PATCH 417/485] basic header --- docs/Features/AddingNodeSpawnShortcut.md | 5 ++ docs/Features/AssetSearch.md | 5 ++ docs/Features/ForcePinActivation.md | 5 ++ docs/Features/GenericGameplayTagEvents.md | 24 ++++++--- docs/Features/SaveGameSupport.md | 5 ++ docs/Features/SignalModes.md | 10 +++- docs/Overview/FAQ.md | 5 ++ docs/Overview/GettingStarted.md | 5 ++ docs/Overview/SupportedlEngineVersions.md | 5 ++ docs/_includes/head-custom.html | 65 ++++++++++++++++++++++- docs/_layouts/default.html | 12 +++-- docs/index.html | 2 +- 12 files changed, 132 insertions(+), 16 deletions(-) diff --git a/docs/Features/AddingNodeSpawnShortcut.md b/docs/Features/AddingNodeSpawnShortcut.md index 08490f85f..20bfbdfa0 100644 --- a/docs/Features/AddingNodeSpawnShortcut.md +++ b/docs/Features/AddingNodeSpawnShortcut.md @@ -1,3 +1,8 @@ +--- +title: Adding Node Spawn Shortcut +layout: default +--- + 1. Create `DefaultEditorPerProjectUserSettings.ini` in your project's Config folder, if you don't have this .ini yet. 2. Add this section to the .ini ``` diff --git a/docs/Features/AssetSearch.md b/docs/Features/AssetSearch.md index 74bfa4fb8..516405c40 100644 --- a/docs/Features/AssetSearch.md +++ b/docs/Features/AssetSearch.md @@ -1,3 +1,8 @@ +--- +title: Asset Search +layout: default +--- + Feature based on the engine plugin added in UE 4.26. The plugin is marked as beta, probably because of the search performance. ## Using the feature diff --git a/docs/Features/ForcePinActivation.md b/docs/Features/ForcePinActivation.md index 4110a4c9a..547c23fd0 100644 --- a/docs/Features/ForcePinActivation.md +++ b/docs/Features/ForcePinActivation.md @@ -1,3 +1,8 @@ +--- +title: Force Pin Activation +layout: default +--- + * It's a debugging feature available from Pin's context menu during PIE. * Allows pushing the graph execution in case of blockers, i.e. specific node doesn't work for whatever reason and we want to continue playtesting. * It works both on Input pins and Output pins. You can even trigger unconnected Input pins this way. diff --git a/docs/Features/GenericGameplayTagEvents.md b/docs/Features/GenericGameplayTagEvents.md index 6b9350e4f..a642bb1bb 100644 --- a/docs/Features/GenericGameplayTagEvents.md +++ b/docs/Features/GenericGameplayTagEvents.md @@ -1,3 +1,8 @@ +--- +title: Generic Gameplay Tag Events +layout: default +--- + Nodes available in the Flow plugin support finding actors and communicating with them by Gameplay Tags. - This can have many advantages over using soft references to actor instances, i.e. changing actor instance name won't break reference. - This also allows communicating with actors spawned in runtime, i.e. all characters. @@ -5,13 +10,15 @@ Nodes available in the Flow plugin support finding actors and communicating with Obviously, you can use soft references to actors in your project-specific Flow nodes. Although the Flow plugin comes with a mechanism to call events in a generic way, without the need to know the actor's class and functions. -# Adding Flow Component to Actor -* Add `Flow Component` to the actor. +## Adding Flow Component to Actor + +* Add `Flow Component` to the actor. * Assign a unique gameplay tag on the `Identity Tags` list. ![image](https://user-images.githubusercontent.com/5065057/176913188-077dc82a-2c7d-4af1-ac30-42d7fba869d5.png) -# Calling Actor events from the Flow Graph +## Calling Actor events from the Flow Graph + * Add the `Receive Notify` event from the Flow Component. ![image](https://user-images.githubusercontent.com/5065057/176913685-180f9f6c-a17e-4097-8e8f-2984a89efaae.png) @@ -21,18 +28,20 @@ Obviously, you can use soft references to actors in your project-specific Flow n ![image](https://user-images.githubusercontent.com/5065057/176920012-98c34c6c-fa7c-43b7-a5ce-be928eae24a3.png) -# Calling Flow Graph event from the Actor +## Calling Flow Graph event from the Actor + * Call `Notify Graph` from the Flow Component. ![image](https://user-images.githubusercontent.com/5065057/176914462-d91f43a3-722d-4c77-bc83-48ee69d0525b.png) * Place the `On Notify From Actor` node in your Flow Graph. * `Identity Tags` should include the Identity Tag assigned in the Flow Component. - * `Notify Tag` is optional. If added, it needs to match the tag selected on Notify Graph in the step above. Otherwise, the `Success` output won't be triggered. + * `Notify Tag` is optional. If added, it needs to match the tag selected on Notify Graph in the step above. Otherwise, the `Success` output won't be triggered. * If `Notify Tag` will be empty, the Notify node won't check what tag has been sent from the actor. The `Success` output will always be triggered. ![image](https://user-images.githubusercontent.com/5065057/176920480-9341ac92-b96d-448d-976a-5590e14ae20b.png) -# Calling events between Actors +## Calling events between Actors + * Source Actor: call `Notify Actor` from the Flow Component. * `Notify Tag` is optional. It allows you to call different events in the target actor. @@ -41,7 +50,8 @@ Obviously, you can use soft references to actors in your project-specific Flow n ![image](https://user-images.githubusercontent.com/5065057/176921316-bc021dcc-a024-4a56-923e-c2a19889c58e.png) -# Calling Flow Graph events from Sequencer +## Calling Flow Graph events from Sequencer + * Add the `Flow Events` track to the Level Sequence. * Simply place a key on the section and give it a name. diff --git a/docs/Features/SaveGameSupport.md b/docs/Features/SaveGameSupport.md index de332e932..25c0818fa 100644 --- a/docs/Features/SaveGameSupport.md +++ b/docs/Features/SaveGameSupport.md @@ -1,3 +1,8 @@ +--- +title: Save Game Support +layout: default +--- + Flow Graph plugs into Unreal's `SaveGame` system. If you haven't used it yet, read ["Saving and Loading Your Game" docs](https://dev.epicgames.com/documentation/en-us/unreal-engine/saving-and-loading-your-game-in-unreal-engine?application_version=5.0). You control which properties are included in SaveGame by marking C++ properties with the `SaveGame` specifier. Or by ticking the `SaveGame` checkbox in the blueprint editor. diff --git a/docs/Features/SignalModes.md b/docs/Features/SignalModes.md index 359881b87..c8d954190 100644 --- a/docs/Features/SignalModes.md +++ b/docs/Features/SignalModes.md @@ -1,6 +1,11 @@ +--- +title: Signal Modes +layout: default +--- + The Signal Modes concept was born from the need to support patching already released games. Flow Graphs serialize the state of their active nodes to a SaveGame. This works well, but there's one inherent limitation. If a player has a SaveGame with a given Flow Node active and serialized to a SaveGame, we cannot remove this node from the graph post-launch. Hence, we shouldn't remove any node post-launch to be perfectly safe. -### Basics +## Basics Yet we sometimes need to change the logic of our game in patches. Signal Modes provide a solution for that. It allows designers to mark nodes as: * **Enabled** - Default state, node is fully executed. @@ -11,7 +16,8 @@ This way, we can deactivate nodes without removing them from the graph, so we ca ![image](https://user-images.githubusercontent.com/5065057/201344208-2020cdad-ae0d-4df6-bd06-25dddc1c88aa.png) -### Allowed Signal Modes +## Allowed Signal Modes + The node author can limit a list of available signal nodes for a given Flow Node class. Some nodes are already acting like pass-through nodes by design, like Reroute or Sequence. It would be redundant or confusing to mark them as PassThrough, so we removed that from the list of available signal modes. diff --git a/docs/Overview/FAQ.md b/docs/Overview/FAQ.md index 0eb6f0f31..c6a472eb7 100644 --- a/docs/Overview/FAQ.md +++ b/docs/Overview/FAQ.md @@ -1,3 +1,8 @@ +--- +title: FAQ +layout: default +--- + ## How to separate the logic between many graphs? The basic and recommended way is to use the `Sub Graph` node to start graph B from graph A. This way we could have very complex logic, i.e. separate graphs for every quest or event. diff --git a/docs/Overview/GettingStarted.md b/docs/Overview/GettingStarted.md index afb975888..854dc5798 100644 --- a/docs/Overview/GettingStarted.md +++ b/docs/Overview/GettingStarted.md @@ -1,3 +1,8 @@ +--- +title: Getting Started +layout: default +--- + ## Sample projects There's a separate repository including [a sample single-player project called FlowGame](https://github.com/MothCocoon/FlowSolo), so you can easily check how this plug-in works. It includes * Flow plugin diff --git a/docs/Overview/SupportedlEngineVersions.md b/docs/Overview/SupportedlEngineVersions.md index ee374d99c..fde8097bb 100644 --- a/docs/Overview/SupportedlEngineVersions.md +++ b/docs/Overview/SupportedlEngineVersions.md @@ -1,3 +1,8 @@ +--- +title: Supported Engine Versions +layout: default +--- + | Unreal Engine | Flow Graph | | :-----------: | :--------: | | 5.7 | 2.2+ | diff --git a/docs/_includes/head-custom.html b/docs/_includes/head-custom.html index bb9670fa5..ab41cdc37 100644 --- a/docs/_includes/head-custom.html +++ b/docs/_includes/head-custom.html @@ -6,6 +6,38 @@ max-width: 960px; } + #header nav { + float: none; + width: 100%; + height: 100%; + display: flex; + align-items: center; + padding: 0; + background: none; + } + + #header nav ul { + display: flex; + align-items: center; + max-width: 960px; + width: 100%; + margin: 0 auto; + padding: 0 16px; + height: 100%; + } + + #header nav ul li.fork { + float: none; + margin: 0 8px 0 0; + padding: 0; + } + + #header nav ul li.discord { + float: none; + margin-left: auto; + padding: 0; + } + section { max-width: none; float: none; @@ -36,7 +68,7 @@ aside.sidebar ul { list-style: none; padding: 0; - margin: 0; + margin: 0 0 0.6em; } aside.sidebar ul li a { @@ -57,4 +89,35 @@ flex: 1; min-width: 0; } + + .title-bar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + } + + a.discord-link { + display: flex; + align-items: center; + gap: 7px; + padding: 7px 14px; + border-radius: 4px; + background: #5865F2; + color: #fff; + text-decoration: none; + font-size: 0.85rem; + font-weight: bold; + white-space: nowrap; + flex-shrink: 0; + fill: currentColor; + } + + a.discord-link:hover { + background: #4752c4; + } + + a.discord-link svg { + fill: currentColor; + } diff --git a/docs/_layouts/default.html b/docs/_layouts/default.html index 43d0c7db1..a752ff4eb 100644 --- a/docs/_layouts/default.html +++ b/docs/_layouts/default.html @@ -21,7 +21,9 @@