diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 14b971f4f..d3ad5cbf2 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -5,6 +5,7 @@ #include "FlowLogChannels.h" #include "FlowSettings.h" #include "FlowSubsystem.h" +#include "FlowUserSettings.h" #include "AddOns/FlowNodeAddOn.h" #include "Interfaces/FlowDataPinGeneratorNodeInterface.h" @@ -17,6 +18,7 @@ #include "Engine/World.h" #include "Serialization/MemoryReader.h" #include "Serialization/MemoryWriter.h" +#include "Algo/AnyOf.h" #if WITH_EDITOR #include "Editor.h" @@ -1061,7 +1063,16 @@ int32 UFlowAsset::RemoveInstance(UFlowAsset* Instance) #if WITH_EDITOR if (InspectedInstance.IsValid() && InspectedInstance.Get() == Instance) { - SetInspectedInstance(NAME_None); + if (UFlowUserSettings::Get()->bKeepLastInspectedInstance) + { + FString LastPath = LastInspectedInstanceName; + SetInspectedInstance(nullptr, false); + LastInspectedInstanceName = LastPath; + } + else + { + SetInspectedInstance(nullptr); + } } #endif @@ -1074,7 +1085,16 @@ void UFlowAsset::ClearInstances() #if WITH_EDITOR if (InspectedInstance.IsValid()) { - SetInspectedInstance(NAME_None); + if (UFlowUserSettings::Get()->bKeepLastInspectedInstance) + { + FString LastPath = LastInspectedInstanceName; + SetInspectedInstance(nullptr, false); + LastInspectedInstanceName = LastPath; + } + else + { + SetInspectedInstance(nullptr); + } } #endif @@ -1090,36 +1110,78 @@ void UFlowAsset::ClearInstances() } #if WITH_EDITOR -void UFlowAsset::GetInstanceDisplayNames(TArray>& OutDisplayNames) const +FString UFlowAsset::GetDebugName() const { - for (const UFlowAsset* Instance : ActiveInstances) + auto GetNumLocalWorlds = []() { - OutDisplayNames.Emplace(MakeShareable(new FName(Instance->GetDisplayName()))); + int32 LocalWorldCount = 0; + for (const FWorldContext& Context : GEngine->GetWorldContexts()) + { + if (Context.WorldType == EWorldType::PIE && Context.World() != nullptr) + { + ++LocalWorldCount; + } + } + return LocalWorldCount; + }; + + FString Name = GetDisplayName().ToString(); + + if (GetNumLocalWorlds() > 1 || GetWorld()->GetNetMode() == NM_ListenServer) + { + FString Context = GetDebugStringForWorld(GetWorld()); + if (!Context.IsEmpty()) + { + Name = FString::Printf(TEXT("%s (%s)"), *Name, *Context); + } + + return Name; } + + return Name; } -void UFlowAsset::SetInspectedInstance(const FName& NewInspectedInstanceName) +void UFlowAsset::SetInspectedInstance(TWeakObjectPtr NewInspectedInstance, bool bRefreshDebugger) { - if (NewInspectedInstanceName.IsNone()) + if (NewInspectedInstance.IsValid()) + { + if (InspectedInstance == NewInspectedInstance) + { + // Nothing changed + return; + } + + bool bIsNewInstancePresent = Algo::AnyOf(ActiveInstances, [NewInspectedInstance](const UFlowAsset* ActiveInstance) + { + 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; + + if (InspectedInstance.IsValid()) { - InspectedInstance = nullptr; + LastInspectedInstanceName = NewInspectedInstance->GetDebugName(); } else { - for (UFlowAsset* ActiveInstance : ActiveInstances) - { - if (ActiveInstance && ActiveInstance->GetDisplayName() == NewInspectedInstanceName) - { - if (!InspectedInstance.IsValid() || InspectedInstance != ActiveInstance) - { - InspectedInstance = ActiveInstance; - } - break; - } - } + LastInspectedInstanceName = FString(); } - BroadcastDebuggerRefresh(); + if (bRefreshDebugger) + { + BroadcastDebuggerRefresh(); + } +} + +void UFlowAsset::SetWorldBeingDebugged(const TWeakObjectPtr NewWorld) +{ + CurrentWorldBeingDebugged = NewWorld; } void UFlowAsset::BroadcastDebuggerRefresh() const @@ -1186,16 +1248,16 @@ void UFlowAsset::PreStartFlow() #if WITH_EDITOR check(IsInstanceInitialized()); - if (TemplateAsset->ActiveInstances.Num() == 1) + bool bCanSetInstanceAsInspected = UFlowUserSettings::Get()->bSetFirstAssetInstanceAsInspected && TemplateAsset->ActiveInstances.Num() == 1; + bool bKeepLastInstance = UFlowUserSettings::Get()->bKeepLastInspectedInstance && TemplateAsset->GetLastInspectedInstanceName().IsEmpty(); + if (bCanSetInstanceAsInspected && !bKeepLastInstance) { // this instance is the only active one, set it directly as Inspected Instance - TemplateAsset->SetInspectedInstance(GetDisplayName()); - } - else - { - // request to refresh list to show newly created instance - TemplateAsset->BroadcastDebuggerRefresh(); + TemplateAsset->SetInspectedInstance(this, false); } + + // request to refresh list to show newly created instance + TemplateAsset->BroadcastDebuggerRefresh(); #endif } diff --git a/Source/Flow/Private/FlowUserSettings.cpp b/Source/Flow/Private/FlowUserSettings.cpp new file mode 100644 index 000000000..7127e261a --- /dev/null +++ b/Source/Flow/Private/FlowUserSettings.cpp @@ -0,0 +1,10 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#include "FlowUserSettings.h" + +UFlowUserSettings::UFlowUserSettings(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) + , bSetFirstAssetInstanceAsInspected(true) + , bKeepLastInspectedInstance(false) +{ +} \ No newline at end of file diff --git a/Source/Flow/Private/Nodes/FlowNode.cpp b/Source/Flow/Private/Nodes/FlowNode.cpp index 048339277..bede5dfc5 100644 --- a/Source/Flow/Private/Nodes/FlowNode.cpp +++ b/Source/Flow/Private/Nodes/FlowNode.cpp @@ -809,7 +809,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(GetFlowAsset(), NodeGuid, PinName); } #endif } @@ -875,7 +875,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(GetFlowAsset(), NodeGuid, PinName); } } else diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index 38b9e206d..eb50027f5 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -22,9 +22,11 @@ class UEdGraph; class UEdGraphNode; class UFlowAsset; +class UWorld; + #if !UE_BUILD_SHIPPING DECLARE_DELEGATE(FFlowGraphEvent); -DECLARE_DELEGATE_TwoParams(FFlowSignalEvent, const FGuid& /*NodeGuid*/, const FName& /*PinName*/); +DECLARE_DELEGATE_ThreeParams(FFlowSignalEvent, const UFlowAsset* /*Instance*/, const FGuid& /*NodeGuid*/, const FName& /*PinName*/); #endif // Working Data struct for the Harvest Data Pins operation @@ -279,7 +281,12 @@ class FLOW_API UFlowAsset : public UObject TArray> ActiveInstances; #if WITH_EDITORONLY_DATA - TWeakObjectPtr InspectedInstance; + TWeakObjectPtr InspectedInstance; + + FString LastInspectedInstanceName; + + /** Current world being debugged for this asset */ + TWeakObjectPtr CurrentWorldBeingDebugged; // 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 @@ -289,15 +296,25 @@ 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; + FString GetDebugName() const; + + void SetInspectedInstance(TWeakObjectPtr NewInspectedInstance, bool bRefreshDebugger = true); + const UFlowAsset* GetInspectedInstance() const { return InspectedInstance.IsValid() ? InspectedInstance.Get() : nullptr; } - void SetInspectedInstance(const FName& NewInspectedInstanceName); - UFlowAsset* GetInspectedInstance() const { return InspectedInstance.IsValid() ? InspectedInstance.Get() : nullptr; } + /** @return debug name of instance that should be debugged, may be from previous PIE session */ + const FStringView GetLastInspectedInstanceName() const + { + return LastInspectedInstanceName; + } + + void SetWorldBeingDebugged(const TWeakObjectPtr NewWorld); + const TWeakObjectPtr GetWorldBeingDebugged() const { return CurrentWorldBeingDebugged; } DECLARE_EVENT(UFlowAsset, FRefreshDebuggerEvent); diff --git a/Source/Flow/Public/FlowUserSettings.h b/Source/Flow/Public/FlowUserSettings.h new file mode 100644 index 000000000..b9f7023ae --- /dev/null +++ b/Source/Flow/Public/FlowUserSettings.h @@ -0,0 +1,29 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "Engine/DeveloperSettings.h" +#include "FlowUserSettings.generated.h" + +/** + * + */ +UCLASS(Config = EditorPerProjectUserSettings, meta = (DisplayName = "Flow")) +class FLOW_API UFlowUserSettings : public UDeveloperSettings +{ + GENERATED_UCLASS_BODY() + + static UFlowUserSettings* Get() { return CastChecked(UFlowUserSettings::StaticClass()->GetDefaultObject()); } + + UPROPERTY(EditDefaultsOnly, config, Category = "Debug") + bool bSetFirstAssetInstanceAsInspected; + + // Keep last inspected instance of this FlowAsset between PIE sessions + UPROPERTY(EditDefaultsOnly, config, Category = "Debug") + bool bKeepLastInspectedInstance; + + virtual FName GetCategoryName() const override { return FName("Flow Graph"); } +#if WITH_EDITORONLY_DATA + virtual FText GetSectionText() const override { return INVTEXT("User Settings"); } +#endif +}; diff --git a/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp b/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp index b9543a4aa..537bce41c 100644 --- a/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp +++ b/Source/FlowDebugger/Private/Debugger/FlowDebuggerSubsystem.cpp @@ -44,15 +44,13 @@ void UFlowDebuggerSubsystem::OnInstancedTemplateRemoved(UFlowAsset* AssetTemplat AssetTemplate->OnPinTriggered.Unbind(); } -void UFlowDebuggerSubsystem::OnPinTriggered(const FGuid& NodeGuid, const FName& PinName) +void UFlowDebuggerSubsystem::OnPinTriggered(const UFlowAsset* Instance, const FGuid& NodeGuid, const FName& PinName) { - if (FindBreakpoint(NodeGuid, PinName)) + if (!TryMarkAsHit(NodeGuid, PinName)) { - MarkAsHit(NodeGuid, PinName); + // Node breakpoints waits on any pin triggered, but check it only if there is no hit pin breakpoint + TryMarkAsHit(NodeGuid); } - - // Node breakpoints waits on any pin triggered - MarkAsHit(NodeGuid); } void UFlowDebuggerSubsystem::AddBreakpoint(const FGuid& NodeGuid) @@ -74,6 +72,19 @@ void UFlowDebuggerSubsystem::AddBreakpoint(const FGuid& NodeGuid, const FName& P SaveSettings(); } +void UFlowDebuggerSubsystem::RemoveAllBreakpoints(const UFlowAsset* Asset) +{ + UFlowDebuggerSettings* Settings = GetMutableDefault(); + for (auto& [NodeGuid, Node] : Asset->GetNodes()) + { + if (Settings->NodeBreakpoints.Contains(NodeGuid)) + { + Settings->NodeBreakpoints.Remove(NodeGuid); + } + } + SaveSettings(); +} + void UFlowDebuggerSubsystem::RemoveAllBreakpoints(const FGuid& NodeGuid) { UFlowDebuggerSettings* Settings = GetMutableDefault(); @@ -187,6 +198,20 @@ void UFlowDebuggerSubsystem::ToggleBreakpoint(const FGuid& NodeGuid, const FName } } +bool UFlowDebuggerSubsystem::HasAnyBreakpoints(const UFlowAsset* Asset) const +{ + UFlowDebuggerSettings* Settings = GetMutableDefault(); + for (auto& [NodeGuid, Node] : Asset->GetNodes()) + { + if (Settings->NodeBreakpoints.Find(NodeGuid)) + { + return true; + } + } + + return false; +} + FFlowBreakpoint* UFlowDebuggerSubsystem::FindBreakpoint(const FGuid& NodeGuid) { UFlowDebuggerSettings* Settings = GetMutableDefault(); @@ -206,6 +231,27 @@ FFlowBreakpoint* UFlowDebuggerSubsystem::FindBreakpoint(const FGuid& NodeGuid, c return NodeBreakpoint ? NodeBreakpoint->PinBreakpoints.Find(PinName) : nullptr; } +void UFlowDebuggerSubsystem::SetAllBreakpointsEnabled(const UFlowAsset* Asset, bool bEnabled) +{ + UFlowDebuggerSettings* Settings = GetMutableDefault(); + for (auto& [NodeGuid, Node] : Asset->GetNodes()) + { + if (FNodeBreakpoint* NodeBreakpoint = Settings->NodeBreakpoints.Find(NodeGuid)) + { + if (NodeBreakpoint->Breakpoint.IsActive()) + { + NodeBreakpoint->Breakpoint.SetEnabled(bEnabled); + } + + for (auto& [Name, PinBreakpoint] : NodeBreakpoint->PinBreakpoints) + { + PinBreakpoint.SetEnabled(bEnabled); + } + } + } + SaveSettings(); +} + void UFlowDebuggerSubsystem::SetBreakpointEnabled(const FGuid& NodeGuid, const bool bEnabled) { if (FFlowBreakpoint* NodeBreakpoint = FindBreakpoint(NodeGuid)) @@ -224,6 +270,56 @@ void UFlowDebuggerSubsystem::SetBreakpointEnabled(const FGuid& NodeGuid, const F } } +bool UFlowDebuggerSubsystem::HasAnyBreakpointsDisabled(const UFlowAsset* Asset) const +{ + UFlowDebuggerSettings* Settings = GetMutableDefault(); + for (auto& [NodeGuid, Node] : Asset->GetNodes()) + { + if (FNodeBreakpoint* NodeBreakpoint = Settings->NodeBreakpoints.Find(NodeGuid)) + { + if (NodeBreakpoint->Breakpoint.IsActive() && !NodeBreakpoint->Breakpoint.IsEnabled()) + { + return true; + } + + for (auto& [Name, PinBreakpoint] : NodeBreakpoint->PinBreakpoints) + { + if (!PinBreakpoint.IsEnabled()) + { + return true; + } + } + } + } + + return false; +} + +bool UFlowDebuggerSubsystem::HasAnyBreakpointsEnabled(const UFlowAsset* Asset) const +{ + UFlowDebuggerSettings* Settings = GetMutableDefault(); + for (auto& [NodeGuid, Node] : Asset->GetNodes()) + { + if (FNodeBreakpoint* NodeBreakpoint = Settings->NodeBreakpoints.Find(NodeGuid)) + { + if (NodeBreakpoint->Breakpoint.IsActive() && NodeBreakpoint->Breakpoint.IsEnabled()) + { + return true; + } + + for (auto& [Name, PinBreakpoint] : NodeBreakpoint->PinBreakpoints) + { + if (PinBreakpoint.IsEnabled()) + { + return true; + } + } + } + } + + return false; +} + bool UFlowDebuggerSubsystem::IsBreakpointEnabled(const FGuid& NodeGuid) { if (const FFlowBreakpoint* PinBreakpoint = FindBreakpoint(NodeGuid)) @@ -244,31 +340,37 @@ bool UFlowDebuggerSubsystem::IsBreakpointEnabled(const FGuid& NodeGuid, const FN return false; } -void UFlowDebuggerSubsystem::MarkAsHit(const FGuid& NodeGuid) +bool UFlowDebuggerSubsystem::TryMarkAsHit(const FGuid& NodeGuid) { if (FFlowBreakpoint* NodeBreakpoint = FindBreakpoint(NodeGuid)) { if (NodeBreakpoint->IsEnabled()) { NodeBreakpoint->MarkAsHit(true); - PauseSession(); + PauseSession(NodeGuid); + return true; } } + + return false; } -void UFlowDebuggerSubsystem::MarkAsHit(const FGuid& NodeGuid, const FName& PinName) +bool UFlowDebuggerSubsystem::TryMarkAsHit(const FGuid& NodeGuid, const FName& PinName) { if (FFlowBreakpoint* PinBreakpoint = FindBreakpoint(NodeGuid, PinName)) { if (PinBreakpoint->IsEnabled()) { PinBreakpoint->MarkAsHit(true); - PauseSession(); + PauseSession(NodeGuid); + return true; } } + + return false; } -void UFlowDebuggerSubsystem::PauseSession() +void UFlowDebuggerSubsystem::PauseSession(const FGuid& FromNode) { SetPause(true); } diff --git a/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h b/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h index c83e004cd..88413ee9b 100644 --- a/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h +++ b/Source/FlowDebugger/Public/Debugger/FlowDebuggerSubsystem.h @@ -28,12 +28,13 @@ class FLOWDEBUGGER_API UFlowDebuggerSubsystem : public UEngineSubsystem virtual void OnInstancedTemplateAdded(UFlowAsset* AssetTemplate); virtual void OnInstancedTemplateRemoved(UFlowAsset* AssetTemplate) const; - virtual void OnPinTriggered(const FGuid& NodeGuid, const FName& PinName); + virtual void OnPinTriggered(const UFlowAsset* Instance, const FGuid& NodeGuid, const FName& PinName); public: virtual void AddBreakpoint(const FGuid& NodeGuid); virtual void AddBreakpoint(const FGuid& NodeGuid, const FName& PinName); + virtual void RemoveAllBreakpoints(const UFlowAsset* Asset); virtual void RemoveAllBreakpoints(const FGuid& NodeGuid); virtual void RemoveNodeBreakpoint(const FGuid& NodeGuid); virtual void RemovePinBreakpoint(const FGuid& NodeGuid, const FName& PinName); @@ -46,20 +47,24 @@ class FLOWDEBUGGER_API UFlowDebuggerSubsystem : public UEngineSubsystem virtual void ToggleBreakpoint(const FGuid& NodeGuid); virtual void ToggleBreakpoint(const FGuid& NodeGuid, const FName& PinName); + bool HasAnyBreakpoints(const UFlowAsset* Asset) const; virtual FFlowBreakpoint* FindBreakpoint(const FGuid& NodeGuid); virtual FFlowBreakpoint* FindBreakpoint(const FGuid& NodeGuid, const FName& PinName); + virtual void SetAllBreakpointsEnabled(const UFlowAsset* Asset, bool bEnabled); virtual void SetBreakpointEnabled(const FGuid& NodeGuid, bool bEnabled); virtual void SetBreakpointEnabled(const FGuid& NodeGuid, const FName& PinName, bool bEnabled); + bool HasAnyBreakpointsDisabled(const UFlowAsset* Asset) const; + bool HasAnyBreakpointsEnabled(const UFlowAsset* Asset) const; virtual bool IsBreakpointEnabled(const FGuid& NodeGuid); virtual bool IsBreakpointEnabled(const FGuid& NodeGuid, const FName& PinName); protected: - virtual void MarkAsHit(const FGuid& NodeGuid); - virtual void MarkAsHit(const FGuid& NodeGuid, const FName& PinName); + virtual bool TryMarkAsHit(const FGuid& NodeGuid); + virtual bool TryMarkAsHit(const FGuid& NodeGuid, const FName& PinName); - virtual void PauseSession(); + virtual void PauseSession(const FGuid& FromNode); 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..5ff7f816c 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetEditor.cpp @@ -11,6 +11,7 @@ #include "Graph/FlowGraphEditor.h" #include "Graph/FlowGraphSchema.h" #include "Graph/Widgets/SFlowPalette.h" +#include "Debugger/FlowDebuggerSubsystem.h" #include "FlowAsset.h" @@ -47,6 +48,7 @@ const FName FFlowAssetEditor::ValidationLogTab(TEXT("ValidationLog")); FFlowAssetEditor::FFlowAssetEditor() : FlowAsset(nullptr) { + DebuggerSubsystem = GEngine->GetEngineSubsystem(); } FFlowAssetEditor::~FFlowAssetEditor() @@ -309,7 +311,8 @@ void FFlowAssetEditor::InitFlowAssetEditor(const EToolkitMode::Type Mode, const UFlowGraphSchema::SubscribeToAssetChanges(); FlowAsset->OnDetailsRefreshRequested.BindThreadSafeSP(this, &FFlowAssetEditor::RefreshDetails); - BindToolbarCommands(); + BindEditorCommands(); + RegisterMenus(); CreateToolbar(); CreateWidgets(); @@ -372,6 +375,31 @@ void FFlowAssetEditor::InitFlowAssetEditor(const EToolkitMode::Type Mode, const RegenerateMenusAndToolbars(); } +void FFlowAssetEditor::RegisterMenus() +{ + const FName MainMenuName = GetToolMenuName(); + + FToolMenuSection& Section = UToolMenus::Get()->ExtendMenu(MainMenuName)->FindOrAddSection(NAME_None); + + if (!Section.FindEntry("Debug")) + { + Section.AddSubMenu( + "Debug", + LOCTEXT("DebugMenu", "Debug"), + LOCTEXT("DebugMenu_ToolTip", "Open the debug menu"), + FNewToolMenuDelegate::CreateLambda([](UToolMenu* InMenu) + { + { + FToolMenuSection& Section = InMenu->AddSection("DebugBreakpoints", LOCTEXT("DebugMenu_BreakpointHeading", "Breakpoints")); + Section.AddMenuEntry( FFlowEditorCommands::Get().EnableAllBreakpoints ); + Section.AddMenuEntry( FFlowEditorCommands::Get().DisableAllBreakpoints ); + Section.AddMenuEntry( FFlowEditorCommands::Get().RemoveAllBreakpoints ); + } + }) + ).InsertPosition = FToolMenuInsert("Edit", EToolMenuInsertType::After); + } +} + void FFlowAssetEditor::CreateToolbar() { FName ParentToolbarName; @@ -390,37 +418,55 @@ void FFlowAssetEditor::CreateToolbar() } } -void FFlowAssetEditor::BindToolbarCommands() +void FFlowAssetEditor::BindEditorCommands() { - FFlowToolbarCommands::Register(); - const FFlowToolbarCommands& ToolbarCommands = FFlowToolbarCommands::Get(); - - // Editing - ToolkitCommands->MapAction(ToolbarCommands.RefreshAsset, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::RefreshAsset), - FCanExecuteAction::CreateStatic(&FFlowAssetEditor::CanEdit)); + FFlowEditorCommands::Register(); + const FFlowEditorCommands& ToolbarCommands = FFlowEditorCommands::Get(); - ToolkitCommands->MapAction(ToolbarCommands.ValidateAsset, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::ValidateAsset_Internal), - FCanExecuteAction()); + // Toolbar + { + // Editing + ToolkitCommands->MapAction(ToolbarCommands.RefreshAsset, + FExecuteAction::CreateSP(this, &FFlowAssetEditor::RefreshAsset), + FCanExecuteAction::CreateStatic(&FFlowAssetEditor::CanEdit)); + + ToolkitCommands->MapAction(ToolbarCommands.ValidateAsset, + FExecuteAction::CreateSP(this, &FFlowAssetEditor::ValidateAsset_Internal), + FCanExecuteAction()); - ToolkitCommands->MapAction(ToolbarCommands.SearchInAsset, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::SearchInAsset), - FCanExecuteAction()); - - ToolkitCommands->MapAction(ToolbarCommands.EditAssetDefaults, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::EditAssetDefaults_Clicked), - FCanExecuteAction()); - - // Engine's Play commands - ToolkitCommands->Append(FPlayWorldCommands::GlobalPlayWorldActions.ToSharedRef()); + ToolkitCommands->MapAction(ToolbarCommands.SearchInAsset, + FExecuteAction::CreateSP(this, &FFlowAssetEditor::SearchInAsset), + FCanExecuteAction()); + + ToolkitCommands->MapAction(ToolbarCommands.EditAssetDefaults, + 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)); + } - // Debugging - ToolkitCommands->MapAction(ToolbarCommands.GoToParentInstance, - FExecuteAction::CreateSP(this, &FFlowAssetEditor::GoToParentInstance), - FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::CanGoToParentInstance), - FIsActionChecked(), - FIsActionButtonVisible::CreateSP(this, &FFlowAssetEditor::CanGoToParentInstance)); + // Debug menu + { + ToolkitCommands->MapAction(ToolbarCommands.DisableAllBreakpoints, + FExecuteAction::CreateSP(this, &FFlowAssetEditor::DisableAllBreakpoints), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::HasAnyEnabledBreakpoints)); + + ToolkitCommands->MapAction(ToolbarCommands.EnableAllBreakpoints, + FExecuteAction::CreateSP(this, &FFlowAssetEditor::EnableAllBreakpoints), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::HasAnyDisabledBreakpoints)); + + ToolkitCommands->MapAction(ToolbarCommands.RemoveAllBreakpoints, + FExecuteAction::CreateSP(this, &FFlowAssetEditor::ClearAllBreakpoints), + FCanExecuteAction::CreateSP(this, &FFlowAssetEditor::HasAnyBreakpoints)); + } } void FFlowAssetEditor::InitalizeExtenders() @@ -487,7 +533,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() @@ -495,6 +541,42 @@ bool FFlowAssetEditor::CanGoToParentInstance() return FlowAsset->GetInspectedInstance() && FlowAsset->GetInspectedInstance()->GetNodeOwningThisAssetInstance() != nullptr; } +void FFlowAssetEditor::EnableAllBreakpoints() +{ + check(DebuggerSubsystem.IsValid()); + DebuggerSubsystem->SetAllBreakpointsEnabled(FlowAsset, true); +} + +bool FFlowAssetEditor::HasAnyDisabledBreakpoints() +{ + check(DebuggerSubsystem.IsValid()); + return DebuggerSubsystem->HasAnyBreakpointsDisabled(FlowAsset); +} + +void FFlowAssetEditor::DisableAllBreakpoints() +{ + check(DebuggerSubsystem.IsValid()); + DebuggerSubsystem->SetAllBreakpointsEnabled(FlowAsset, false); +} + +bool FFlowAssetEditor::HasAnyEnabledBreakpoints() +{ + check(DebuggerSubsystem.IsValid()); + return DebuggerSubsystem->HasAnyBreakpointsEnabled(FlowAsset); +} + +void FFlowAssetEditor::ClearAllBreakpoints() +{ + check(DebuggerSubsystem.IsValid()); + DebuggerSubsystem->RemoveAllBreakpoints(FlowAsset); +} + +bool FFlowAssetEditor::HasAnyBreakpoints() +{ + check(DebuggerSubsystem.IsValid()); + return DebuggerSubsystem->HasAnyBreakpoints(FlowAsset); +} + void FFlowAssetEditor::CreateWidgets() { // Details View diff --git a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp index c794ad0e5..d6550aec6 100644 --- a/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp +++ b/Source/FlowEditor/Private/Asset/FlowAssetToolbar.cpp @@ -2,12 +2,15 @@ #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 "FlowUserSettings.h" #include "Kismet2/DebuggerCommands.h" #include "Misc/Attribute.h" @@ -30,22 +33,30 @@ // Flow Asset Instance List FText SFlowAssetInstanceList::NoInstanceSelectedText = LOCTEXT("NoInstanceSelected", "No instance selected"); +FText SFlowAssetInstanceList::AllWorldsText = LOCTEXT("AllWorlds", "All Worlds"); 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) + DebugWorldsComboBox = SNew(SComboBox>) + .OptionsSource(&DebugWorlds) + .Visibility_Static(&SFlowAssetInstanceList::GetWorldComboVisibility) + .OnComboBoxOpening(this, &SFlowAssetInstanceList::GenerateDebugWorldNames) + .OnGenerateWidget(this, &SFlowAssetInstanceList::GenerateWorldItemWidget) + .OnSelectionChanged(this, &SFlowAssetInstanceList::DebugWorldSelectionChanged) + .ContentPadding(FMargin(0.f, 2.f)) + [ + SNew(STextBlock) + .Text(this, &SFlowAssetInstanceList::GetSelectedWorldName) + ]; + + DebugInstancesComboBox = SNew(SComboBox>) + .OptionsSource(&DebugInstances) + .OnComboBoxOpening(this, &SFlowAssetInstanceList::GenerateDebugInstances) + .OnGenerateWidget(this, &SFlowAssetInstanceList::GenerateInstanceItemWidget) + .OnSelectionChanged(this, &SFlowAssetInstanceList::DebugInstanceSelectionChanged) + .ContentPadding(FMargin(0.f, 2.f)) [ SNew(STextBlock) .Text(this, &SFlowAssetInstanceList::GetSelectedInstanceName) @@ -53,8 +64,27 @@ void SFlowAssetInstanceList::Construct(const FArguments& InArgs, const TWeakObje ChildSlot [ - Dropdown.ToSharedRef() + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + [ + DebugWorldsComboBox.ToSharedRef() + ] + + SHorizontalBox::Slot() + .Padding(8.0, 0.f, 4.f, 0.f) + .AutoWidth() + [ + DebugInstancesComboBox.ToSharedRef() + ] ]; + + if (TemplateAsset.IsValid()) + { + TemplateAsset->OnDebuggerRefresh().AddSP(this, &SFlowAssetInstanceList::GenerateDebugWorldNames); + TemplateAsset->OnDebuggerRefresh().AddSP(this, &SFlowAssetInstanceList::GenerateDebugInstances); + GenerateDebugWorldNames(); + GenerateDebugInstances(); + } } SFlowAssetInstanceList::~SFlowAssetInstanceList() @@ -65,59 +95,188 @@ SFlowAssetInstanceList::~SFlowAssetInstanceList() } } -void SFlowAssetInstanceList::RefreshInstances() +EVisibility SFlowAssetInstanceList::GetWorldComboVisibility() { - // collect instance names of this Flow Asset - InstanceNames = {MakeShareable(new FName(*NoInstanceSelectedText.ToString()))}; - TemplateAsset->GetInstanceDisplayNames(InstanceNames); - - // select instance - if (const UFlowAsset* InspectedInstance = TemplateAsset->GetInspectedInstance()) + if (GEditor->PlayWorld != nullptr) { - const FName& InspectedInstanceName = InspectedInstance->GetDisplayName(); - for (const TSharedPtr& Instance : InstanceNames) + auto GetNumLocalWorlds = []() { - if (*Instance == InspectedInstanceName) + int32 LocalWorldCount = 0; + for (const FWorldContext& Context : GEngine->GetWorldContexts()) { - SelectedInstance = Instance; - break; + if (Context.WorldType == EWorldType::PIE && Context.World() != nullptr) + { + ++LocalWorldCount; + } } + return LocalWorldCount; + }; + + if (GetNumLocalWorlds() > 1) + { + return EVisibility::Visible; + } + } + + return EVisibility::Collapsed; +} + +void SFlowAssetInstanceList::GenerateDebugWorldNames() +{ + DebugWorlds.Empty(); + DebugWorlds.Add(MakeShareable(new FFlowDebugWorld(nullptr, AllWorldsText.ToString()))); + + for (const FWorldContext& PieContext : GEngine->GetWorldContexts()) + { + UWorld* PlayWorld = PieContext.World(); + if (PlayWorld && PlayWorld->IsGameWorld()) + { + FString WorldName = GetDebugStringForWorld(PlayWorld); + DebugWorlds.Add(MakeShareable(new FFlowDebugWorld(PlayWorld, WorldName))); } } - else + + TSharedPtr LastSelection = GetDebugWorld(); + DebugWorldsComboBox->SetSelectedItem(LastSelection); +} + +TSharedRef SFlowAssetInstanceList::GenerateWorldItemWidget(TSharedPtr Item) const +{ + return SNew(STextBlock) + .Text(FText::FromString(Item->WorldLabel)); +} + +void SFlowAssetInstanceList::DebugWorldSelectionChanged(TSharedPtr SelectedItem, ESelectInfo::Type SelectionType) +{ + check(TemplateAsset.IsValid()); + if (SelectionType != ESelectInfo::Direct) { - // default object is always available - SelectedInstance = InstanceNames[0]; + check(SelectedItem.IsValid()); + TemplateAsset->SetWorldBeingDebugged(SelectedItem->WorldPtr); + TemplateAsset->SetInspectedInstance(nullptr); } } -EVisibility SFlowAssetInstanceList::GetDebuggerVisibility() +FText SFlowAssetInstanceList::GetSelectedWorldName() const { - return GEditor->PlayWorld ? EVisibility::Visible : EVisibility::Collapsed; + return FText::FromString(DebugWorldsComboBox->GetSelectedItem()->WorldLabel); } -TSharedRef SFlowAssetInstanceList::OnGenerateWidget(const TSharedPtr Item) const +TSharedPtr SFlowAssetInstanceList::GetDebugWorld() const { - return SNew(STextBlock).Text(FText::FromName(*Item.Get())); + check(TemplateAsset.IsValid()); + TWeakObjectPtr World = TemplateAsset->GetWorldBeingDebugged(); + if (!World.IsExplicitlyNull()) + { + for (const TSharedPtr& DebugWorld : DebugWorlds) + { + if (ensure(DebugWorld.IsValid()) && DebugWorld->WorldPtr == World) + { + return DebugWorld; + } + } + } + + check(DebugWorlds.Num() > 0); + return DebugWorlds[0]; } -void SFlowAssetInstanceList::OnSelectionChanged(const TSharedPtr SelectedItem, const ESelectInfo::Type SelectionType) +void SFlowAssetInstanceList::GenerateDebugInstances() { - if (SelectionType != ESelectInfo::Direct) + check(TemplateAsset.IsValid()); + + TSharedPtr LastSelection; + if (UFlowUserSettings::Get()->bKeepLastInspectedInstance) { - SelectedInstance = SelectedItem; + LastSelection = GetDebugInstance(); + } + + DebugInstances.Empty(); + DebugInstances.Add(MakeShareable(new FFlowDebugInstance(nullptr, *NoInstanceSelectedText.ToString()))); - if (TemplateAsset.IsValid()) + TWeakObjectPtr DebugWorld = DebugWorldsComboBox->GetSelectedItem()->WorldPtr; + + // collect active instances of this Flow Asset + for (const UFlowAsset* ActiveInstance: TemplateAsset->GetActiveInstances()) + { + if (DebugWorld.IsValid() && DebugWorld.Get() != ActiveInstance->GetWorld()) { - const FName NewSelectedInstanceName = (SelectedInstance.IsValid() && *SelectedInstance != *InstanceNames[0]) ? *SelectedInstance : NAME_None; - TemplateAsset->SetInspectedInstance(NewSelectedInstanceName); + continue; } + + TSharedPtr NewInstance = MakeShareable(new FFlowDebugInstance(ActiveInstance, ActiveInstance->GetDebugName())); + DebugInstances.Add(NewInstance); + } + + TSharedPtr Selection = GetDebugInstance(); + if (Selection.IsValid() && !Selection->IsEmptyObject()) + { + // If our new selection matches the actual debug instance, set it + if (LastSelection.IsValid() && LastSelection->InstanceLabel == Selection->InstanceLabel) + { + // new selection is the same as our selected instance from previous PIE session, set it as inspected + TemplateAsset->SetInspectedInstance(Selection->InstancePtr); + } + DebugInstancesComboBox->SetSelectedItem(Selection); + } + else if (LastSelection.IsValid() && !LastSelection->IsEmptyObject()) + { + // Re-add the desired runtime instance, even though it is currently null + DebugInstances.Add(LastSelection); + DebugInstancesComboBox->SetSelectedItem(LastSelection); + } + + // Finally ensure we have a valid selection, this will set to all objects as a backup + TSharedPtr CurrentSelection = DebugInstancesComboBox->GetSelectedItem(); + if (DebugInstances.Find(CurrentSelection) == INDEX_NONE) + { + check(DebugInstances.Num() > 0); + DebugInstancesComboBox->SetSelectedItem(DebugInstances[0]); + } +} + +TSharedRef SFlowAssetInstanceList::GenerateInstanceItemWidget(const TSharedPtr Item) const +{ + return SNew(STextBlock) + .Text(FText::FromString(Item->InstanceLabel)); +} + +void SFlowAssetInstanceList::DebugInstanceSelectionChanged(const TSharedPtr SelectedItem, const ESelectInfo::Type SelectionType) +{ + check(TemplateAsset.IsValid()); + if (SelectionType != ESelectInfo::Direct) + { + check(SelectedItem.IsValid()); + TWeakObjectPtr Instance = SelectedItem->InstancePtr; + TemplateAsset->SetInspectedInstance(Instance); } } FText SFlowAssetInstanceList::GetSelectedInstanceName() const { - return SelectedInstance.IsValid() ? FText::FromName(*SelectedInstance) : NoInstanceSelectedText; + return FText::FromString(DebugInstancesComboBox->GetSelectedItem()->InstanceLabel); +} + +TSharedPtr SFlowAssetInstanceList::GetDebugInstance() const +{ + check(TemplateAsset.IsValid()); + const FStringView DebugName = TemplateAsset->GetLastInspectedInstanceName(); + if (!DebugName.IsEmpty()) + { + for (int32 ObjectIndex = 0; ObjectIndex < DebugInstances.Num(); ++ObjectIndex) + { + if (ensure(DebugInstances[ObjectIndex].IsValid()) && DebugName.Equals(DebugInstances[ObjectIndex]->InstanceLabel)) + { + return DebugInstances[ObjectIndex]; + } + } + } + + if (DebugInstances.Num() > 0) + { + return DebugInstances[0]; + } + return nullptr; } ////////////////////////////////////////////////////////////////////////// @@ -130,30 +289,43 @@ 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"); + .ButtonStyle(FAppStyle::Get(), "SimpleButton") + .TextStyle(FAppStyle::Get(), "NormalText") + .ButtonContentPadding( FMargin(2.f, 4.f) ) + .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 + check(TemplateAsset.IsValid()); + TemplateAsset->OnDebuggerRefresh().AddSP(this, &SFlowAssetBreadcrumb::FillBreadcrumb); + FillBreadcrumb(); +} + +EVisibility SFlowAssetBreadcrumb::GetBreadcrumbVisibility() const +{ + return GEditor->PlayWorld && TemplateAsset->GetInspectedInstance() ? EVisibility::Visible : EVisibility::Collapsed; +} + +void SFlowAssetBreadcrumb::FillBreadcrumb() +{ 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 +334,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 +347,19 @@ 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)) + { + FlowAssetEditor->JumpToNode(Item.ChildInstance->GetNodeOwningThisAssetInstance()->GetGraphNode()); + } + } } } @@ -199,9 +380,9 @@ void FFlowAssetToolbar::BuildAssetToolbar(UToolMenu* ToolbarMenu) const Section.InsertPosition = FToolMenuInsert("Asset", EToolMenuInsertType::After); // add buttons - Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().RefreshAsset)); - Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().ValidateAsset)); - Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().EditAssetDefaults)); + Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowEditorCommands::Get().RefreshAsset)); + Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowEditorCommands::Get().ValidateAsset)); + Section.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowEditorCommands::Get().EditAssetDefaults)); } { @@ -229,7 +410,7 @@ void FFlowAssetToolbar::BuildAssetToolbar(UToolMenu* ToolbarMenu) const })); Section.AddEntry(FToolMenuEntry::InitToolBarButton( - FFlowToolbarCommands::Get().SearchInAsset, + FFlowEditorCommands::Get().SearchInAsset, TAttribute(), TAttribute(), FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.Tabs.FindResults") @@ -325,7 +506,8 @@ void FFlowAssetToolbar::BuildDebuggerToolbar(UToolMenu* ToolbarMenu) const InSection.AddEntry(FToolMenuEntry::InitWidget("AssetInstances", SNew(SFlowAssetInstanceList, Context->GetFlowAsset()), FText(), true)); - InSection.AddEntry(FToolMenuEntry::InitToolBarButton(FFlowToolbarCommands::Get().GoToParentInstance)); + InSection.AddSeparator(NAME_None).StyleNameOverride = FName("Toolbar.BackplateRight"); + 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..f46754ba2 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" @@ -17,6 +18,8 @@ #define LOCTEXT_NAMESPACE "FlowDebugEditorSubsystem" UFlowDebugEditorSubsystem::UFlowDebugEditorSubsystem() + : bOverrideInspectedInstance(false) + , bPausedAtFlowBreakpoint(false) { FEditorDelegates::BeginPIE.AddUObject(this, &ThisClass::OnBeginPIE); FEditorDelegates::ResumePIE.AddUObject(this, &ThisClass::OnResumePIE); @@ -41,6 +44,48 @@ void UFlowDebugEditorSubsystem::OnInstancedTemplateRemoved(UFlowAsset* AssetTemp Super::OnInstancedTemplateRemoved(AssetTemplate); } +void UFlowDebugEditorSubsystem::OnPinTriggered(const UFlowAsset* Instance, const FGuid& NodeGuid, const FName& PinName) +{ + if (bPausedAtFlowBreakpoint) + { + return; + } + + if (ensure(Instance)) + { + const UFlowAsset* InspectedInstance = Instance->GetTemplateAsset()->GetInspectedInstance(); + const FStringView InstanceName = Instance->GetTemplateAsset()->GetLastInspectedInstanceName(); + TWeakObjectPtr DebugWorld = Instance->GetTemplateAsset()->GetWorldBeingDebugged(); + + bOverrideInspectedInstance = false; + + if (InspectedInstance) + { + if (InspectedInstance != Instance) + { + return; + } + } + else if (!InstanceName.IsEmpty() && InstanceName != Instance->GetDebugName()) + { + // there is other inspected asset, from previous PIE session. Wait for it + return; + } + else if (DebugWorld.IsValid() && DebugWorld.Get() != Instance->GetWorld()) + { + return; + } + else + { + bOverrideInspectedInstance = true; + } + + OverrideInstancePtr = Instance; + + Super::OnPinTriggered(Instance, NodeGuid, PinName); + } +} + void UFlowDebugEditorSubsystem::OnRuntimeMessageAdded(const UFlowAsset* AssetTemplate, const TSharedRef& Message) const { const TSharedPtr Log = RuntimeLogs.FindRef(AssetTemplate); @@ -59,12 +104,12 @@ void UFlowDebugEditorSubsystem::OnBeginPIE(const bool bIsSimulating) void UFlowDebugEditorSubsystem::OnResumePIE(const bool bIsSimulating) { - ClearHitBreakpoints(); + ClearPausedState(); } void UFlowDebugEditorSubsystem::OnEndPIE(const bool bIsSimulating) { - ClearHitBreakpoints(); + ClearPausedState(); for (const TPair, TSharedPtr>& Log : RuntimeLogs) { @@ -92,12 +137,57 @@ void UFlowDebugEditorSubsystem::OnEndPIE(const bool bIsSimulating) } } -void UFlowDebugEditorSubsystem::PauseSession() +void UFlowDebugEditorSubsystem::PauseSession(const FGuid& FromNode) { - if (!GUnrealEd->SetPIEWorldsPaused(true)) + if (GEditor->ShouldEndPlayMap()) + { + return; + } + + if (GUnrealEd->SetPIEWorldsPaused(true)) { + bPausedAtFlowBreakpoint = true; + + check(OverrideInstancePtr.IsValid()); + UFlowAsset* TemplateInstance = OverrideInstancePtr->GetTemplateAsset(); + + if (bOverrideInspectedInstance) + { + TemplateInstance->SetInspectedInstance(OverrideInstancePtr); + } + + UFlowNode* FlowNode = TemplateInstance->GetNode(FromNode); + check(FlowNode); + + UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem(); + if (AssetEditorSubsystem->OpenEditorForAsset(TemplateInstance)) + { + if (const TSharedPtr FlowAssetEditor = FFlowGraphUtils::GetFlowAssetEditor(TemplateInstance)) + { + FlowAssetEditor->JumpToNode(FlowNode->GetGraphNode()); + } + } + GUnrealEd->PlaySessionPaused(); } } +void UFlowDebugEditorSubsystem::ClearPausedState() +{ + ClearHitBreakpoints(); + + if (bPausedAtFlowBreakpoint && bOverrideInspectedInstance) + { + bOverrideInspectedInstance = false; + + // do not reset inspected instance if it does not match temporary OverrideInstance. It means user selected other instance when editor was in debug mode + if (ensure(OverrideInstancePtr.IsValid()) && OverrideInstancePtr == OverrideInstancePtr->GetTemplateAsset()->GetInspectedInstance()) + { + OverrideInstancePtr->GetTemplateAsset()->SetInspectedInstance(nullptr); + } + } + + bPausedAtFlowBreakpoint = false; +} + #undef LOCTEXT_NAMESPACE diff --git a/Source/FlowEditor/Private/FlowEditorCommands.cpp b/Source/FlowEditor/Private/FlowEditorCommands.cpp index 8e681edec..cd9af270c 100644 --- a/Source/FlowEditor/Private/FlowEditorCommands.cpp +++ b/Source/FlowEditor/Private/FlowEditorCommands.cpp @@ -11,12 +11,12 @@ #define LOCTEXT_NAMESPACE "FlowGraphCommands" -FFlowToolbarCommands::FFlowToolbarCommands() - : TCommands("FlowToolbar", LOCTEXT("FlowToolbar", "Flow Toobar"), NAME_None, FFlowEditorStyle::GetStyleSetName()) +FFlowEditorCommands::FFlowEditorCommands() + : TCommands("FlowEditor", LOCTEXT("FlowEditor", "Flow Editor"), NAME_None, FFlowEditorStyle::GetStyleSetName()) { } -void FFlowToolbarCommands::RegisterCommands() +void FFlowEditorCommands::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()); @@ -25,6 +25,11 @@ void FFlowToolbarCommands::RegisterCommands() 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()); + + 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)); + } FFlowGraphCommands::FFlowGraphCommands() diff --git a/Source/FlowEditor/Private/FlowEditorStyle.cpp b/Source/FlowEditor/Private/FlowEditorStyle.cpp index 23c067776..d9544bd66 100644 --- a/Source/FlowEditor/Private/FlowEditorStyle.cpp +++ b/Source/FlowEditor/Private/FlowEditorStyle.cpp @@ -31,13 +31,11 @@ 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.ValidateAsset", new IMAGE_BRUSH_SVG( "Starship/Common/Debug", Icon20)); + StyleSet->Set("FlowEditor.RefreshAsset", new IMAGE_BRUSH_SVG( "Starship/Common/Apply", Icon20)); + StyleSet->Set("FlowEditor.ValidateAsset", new IMAGE_BRUSH_SVG( "Starship/Common/Debug", 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("FlowEditor.SearchInAsset", new IMAGE_BRUSH_SVG( "Starship/Common/Search", Icon20)); + StyleSet->Set("FlowEditor.EditAssetDefaults", new IMAGE_BRUSH_SVG("Starship/Common/Details", 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/Private/Graph/FlowGraphEditor.cpp b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp index ccdd093fb..5931c31cf 100644 --- a/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp +++ b/Source/FlowEditor/Private/Graph/FlowGraphEditor.cpp @@ -37,7 +37,7 @@ void SFlowGraphEditor::Construct(const FArguments& InArgs, const TSharedPtr::CreateSP(this, &SFlowGraphEditor::GetGraphAppearanceInfo); Arguments._GraphToEdit = FlowAsset->GetGraph(); Arguments._GraphEvents = InArgs._GraphEvents; Arguments._AutoExpandActionMenu = true; @@ -264,11 +264,7 @@ FGraphAppearanceInfo SFlowGraphEditor::GetGraphAppearanceInfo() const { FGraphAppearanceInfo AppearanceInfo; AppearanceInfo.CornerText = GetCornerText(); - - if (IsPlaySessionPaused()) - { - AppearanceInfo.PIENotifyText = LOCTEXT("PausedLabel", "PAUSED"); - } + AppearanceInfo.PIENotifyText = GetPIEStatus(); return AppearanceInfo; } @@ -278,6 +274,45 @@ FText SFlowGraphEditor::GetCornerText() const return LOCTEXT("AppearanceCornerText_FlowAsset", "FLOW"); } +FText SFlowGraphEditor::GetPIEStatus() const +{ + ENetMode NetMode = NM_Standalone; + const UWorld* World = nullptr; + if (FlowAsset.IsValid()) + { + TWeakObjectPtr DebugWorld = FlowAsset->GetWorldBeingDebugged(); + if (DebugWorld.IsValid()) + { + World = DebugWorld.Get(); + NetMode = DebugWorld->GetNetMode(); + } + else + { + TWeakObjectPtr InspectedInstance = FlowAsset->GetInspectedInstance(); + if (InspectedInstance.IsValid()) + { + World = InspectedInstance->GetWorld(); + if (World) // not sure if it is okay if we don't have a valid world for InspectedInstance + { + NetMode = World->GetNetMode(); + } + } + } + } + + if (NetMode == NM_ListenServer || NetMode == NM_DedicatedServer) + { + return LOCTEXT("PIEStatusServerSimulating", "SERVER - SIMULATING"); + } + else if (NetMode == NM_Client) + { + FWorldContext* PIEContext = GEngine->GetWorldContextFromWorld(World); + return FText::Format(LOCTEXT("PIEStatusClientSimulatingFormat", "CLIENT {0} - SIMULATING"), FText::AsNumber(PIEContext->PIEInstance)); + } + + return LOCTEXT("PIEStatusSimulating", "SIMULATING"); +} + void SFlowGraphEditor::UndoGraphAction() { GEditor->UndoTransaction(); @@ -370,7 +405,7 @@ void SFlowGraphEditor::OnSelectedNodesChanged(const TSet& Nodes) { if (const UFlowGraphNode* GraphNode = Cast(*SetIt)) { - SelectedObjects.Add(Cast(GraphNode->GetFlowNodeBase())); + SelectedObjects.Add(GraphNode->GetFlowNodeBase()); } else { 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/Public/Asset/FlowAssetEditor.h b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h index 8c479b8e0..fd430fc67 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetEditor.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetEditor.h @@ -15,6 +15,7 @@ class SFlowGraphEditor; class SFlowPalette; class UFlowAsset; class UFlowGraphNode; +class UFlowDebuggerSubsystem; class IDetailsView; class SDockableTab; @@ -59,6 +60,8 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit TSharedPtr ValidationLog; TSharedPtr ValidationLogListing; + TWeakObjectPtr DebuggerSubsystem; + private: /** The current UI selection state of this editor */ FName CurrentUISelection; @@ -124,8 +127,10 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit void InitFlowAssetEditor(const EToolkitMode::Type Mode, const TSharedPtr& InitToolkitHost, UObject* ObjectToEdit); protected: + virtual void RegisterMenus(); + virtual void CreateToolbar(); - virtual void BindToolbarCommands(); + virtual void BindEditorCommands(); virtual void InitalizeExtenders(); virtual void RefreshAsset(); @@ -143,6 +148,15 @@ class FLOWEDITOR_API FFlowAssetEditor : public FAssetEditorToolkit, public FEdit virtual void GoToParentInstance(); virtual bool CanGoToParentInstance(); + void EnableAllBreakpoints(); + bool HasAnyDisabledBreakpoints(); + + void DisableAllBreakpoints(); + bool HasAnyEnabledBreakpoints(); + + void ClearAllBreakpoints(); + bool HasAnyBreakpoints(); + virtual void CreateWidgets(); virtual void CreateGraphWidget(); diff --git a/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h b/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h index 0a867e672..495872d6f 100644 --- a/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h +++ b/Source/FlowEditor/Public/Asset/FlowAssetToolbar.h @@ -7,10 +7,53 @@ #include "FlowAsset.h" +class UFlowNode_SubGraph; class FFlowAssetEditor; class UFlowAssetEditorContext; class UToolMenu; +struct FFlowDebugWorld +{ + /** Actual World object */ + TWeakObjectPtr WorldPtr; + + /** Friendly label for debug world */ + FString WorldLabel; + + FFlowDebugWorld(const TWeakObjectPtr& InWorldPtr, const FString& InWorldLabel) + : WorldPtr(InWorldPtr) + , WorldLabel(InWorldLabel) + { + } + + /** Returns true if this is the special entry for no specific world */ + bool IsEmptyObject() const + { + return WorldPtr.IsExplicitlyNull(); + } +}; + +struct FFlowDebugInstance +{ + /** Actual FlowAsset instance */ + TWeakObjectPtr InstancePtr; + + /** Friendly label for debug instance */ + FString InstanceLabel; + + FFlowDebugInstance(const TWeakObjectPtr& InInstancePtr, const FString& InInstanceLabel) + : InstancePtr(InInstancePtr) + , InstanceLabel(InInstanceLabel) + { + } + + /** Returns true if this is the special entry for no specific instance */ + bool IsEmptyObject() const + { + return InstancePtr.IsExplicitlyNull(); + } +}; + ////////////////////////////////////////////////////////////////////////// // Flow Asset Instance List @@ -23,22 +66,32 @@ class FLOWEDITOR_API SFlowAssetInstanceList : public SCompoundWidget void Construct(const FArguments& InArgs, const TWeakObjectPtr InTemplateAsset); virtual ~SFlowAssetInstanceList() override; - static EVisibility GetDebuggerVisibility(); - private: - void RefreshInstances(); + static EVisibility GetWorldComboVisibility(); + + void GenerateDebugWorldNames(); + TSharedRef GenerateWorldItemWidget(TSharedPtr Item) const; + void DebugWorldSelectionChanged(TSharedPtr SelectedItem, ESelectInfo::Type SelectionType); + FText GetSelectedWorldName() const; + TSharedPtr GetDebugWorld() const; - TSharedRef OnGenerateWidget(TSharedPtr Item) const; - void OnSelectionChanged(TSharedPtr SelectedItem, ESelectInfo::Type SelectionType); + void GenerateDebugInstances(); + TSharedRef GenerateInstanceItemWidget(TSharedPtr Item) const; + void DebugInstanceSelectionChanged(TSharedPtr SelectedItem, ESelectInfo::Type SelectionType); FText GetSelectedInstanceName() const; + TSharedPtr GetDebugInstance() const; + TWeakObjectPtr TemplateAsset; - TSharedPtr>> Dropdown; + + TSharedPtr>> DebugWorldsComboBox; + TSharedPtr>> DebugInstancesComboBox; - TArray> InstanceNames; - TSharedPtr SelectedInstance; + TArray> DebugWorlds; + TArray> DebugInstances; static FText NoInstanceSelectedText; + static FText AllWorldsText; }; ////////////////////////////////////////////////////////////////////////// @@ -49,17 +102,17 @@ 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) + : CurrentInstance(nullptr) + , ChildInstance(nullptr) {} - explicit FFlowBreadcrumb(const TWeakObjectPtr FlowAsset) - : AssetPathName(FlowAsset->GetTemplateAsset()->GetPathName()) - , InstanceName(FlowAsset->GetDisplayName()) + explicit FFlowBreadcrumb(const TWeakObjectPtr InCurrentInstance, const TWeakObjectPtr InChildInstance) + : CurrentInstance(InCurrentInstance) + , ChildInstance(InChildInstance) {} }; @@ -72,6 +125,8 @@ class FLOWEDITOR_API SFlowAssetBreadcrumb : public SCompoundWidget void Construct(const FArguments& InArgs, const TWeakObjectPtr InTemplateAsset); private: + EVisibility GetBreadcrumbVisibility() const; + void FillBreadcrumb(); void OnCrumbClicked(const FFlowBreadcrumb& Item) const; TWeakObjectPtr TemplateAsset; diff --git a/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h b/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h index 3cbe13fa8..8ef7229e7 100644 --- a/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h +++ b/Source/FlowEditor/Public/Asset/FlowDebugEditorSubsystem.h @@ -27,11 +27,18 @@ class FLOWEDITOR_API UFlowDebugEditorSubsystem : public UFlowDebuggerSubsystem virtual void OnInstancedTemplateAdded(UFlowAsset* AssetTemplate) override; virtual void OnInstancedTemplateRemoved(UFlowAsset* AssetTemplate) const override; + virtual void OnPinTriggered(const UFlowAsset* Instance, const FGuid& NodeGuid, const FName& PinName) override; + void OnRuntimeMessageAdded(const UFlowAsset* AssetTemplate, const TSharedRef& Message) const; virtual void OnBeginPIE(const bool bIsSimulating); virtual void OnResumePIE(const bool bIsSimulating); virtual void OnEndPIE(const bool bIsSimulating); - virtual void PauseSession() override; + virtual void PauseSession(const FGuid& FromNode) override; + void ClearPausedState(); +private: + bool bOverrideInspectedInstance; + bool bPausedAtFlowBreakpoint; + TWeakObjectPtr OverrideInstancePtr; }; diff --git a/Source/FlowEditor/Public/FlowEditorCommands.h b/Source/FlowEditor/Public/FlowEditorCommands.h index d83f84d5b..e26ef38b4 100644 --- a/Source/FlowEditor/Public/FlowEditorCommands.h +++ b/Source/FlowEditor/Public/FlowEditorCommands.h @@ -7,10 +7,10 @@ #include "Framework/Commands/UICommandInfo.h" #include "Templates/SharedPointer.h" -class FLOWEDITOR_API FFlowToolbarCommands : public TCommands +class FLOWEDITOR_API FFlowEditorCommands : public TCommands { public: - FFlowToolbarCommands(); + FFlowEditorCommands(); TSharedPtr RefreshAsset; TSharedPtr ValidateAsset; @@ -20,6 +20,10 @@ class FLOWEDITOR_API FFlowToolbarCommands : public TCommands GoToParentInstance; + TSharedPtr EnableAllBreakpoints; + TSharedPtr DisableAllBreakpoints; + TSharedPtr RemoveAllBreakpoints; + virtual void RegisterCommands() override; }; diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h index 331ebb1e1..4104f7cea 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditor.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditor.h @@ -42,6 +42,7 @@ class FLOWEDITOR_API SFlowGraphEditor : public SGraphEditor virtual FGraphAppearanceInfo GetGraphAppearanceInfo() const; virtual FText GetCornerText() const; + virtual FText GetPIEStatus() const; private: static void UndoGraphAction(); diff --git a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h index f19656734..5a33fb576 100644 --- a/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h +++ b/Source/FlowEditor/Public/Graph/FlowGraphEditorSettings.h @@ -69,5 +69,5 @@ class FLOWEDITOR_API UFlowGraphEditorSettings : public UDeveloperSettings public: virtual FName GetCategoryName() const override { return FName("Flow Graph"); } - virtual FText GetSectionText() const override { return INVTEXT("User Settings"); } + virtual FText GetSectionText() const override { return INVTEXT("Graph User Settings"); } }; 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);