4292 lines
152 KiB
C++
4292 lines
152 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
#include "SMyBlueprint.h"
|
|
|
|
#include "Animation/AnimClassInterface.h"
|
|
#include "AnimationGraph.h"
|
|
#include "AnimationStateMachineGraph.h"
|
|
#include "AnimationStateMachineSchema.h"
|
|
#include "AnimGraphNode_StateMachineBase.h"
|
|
#include "BPDelegateDragDropAction.h"
|
|
#include "BPFunctionDragDropAction.h"
|
|
#include "BPGraphClipboardData.h"
|
|
#include "BPVariableDragDropAction.h"
|
|
#include "BlueprintEditor.h"
|
|
#include "BlueprintEditorCommands.h"
|
|
#include "BlueprintEditorModule.h"
|
|
#include "BlueprintEditorSettings.h"
|
|
#include "Components/ActorComponent.h"
|
|
#include "Components/TimelineComponent.h"
|
|
#include "Containers/EnumAsByte.h"
|
|
#include "Containers/Map.h"
|
|
#include "Containers/UnrealString.h"
|
|
#include "Delegates/Delegate.h"
|
|
#include "DetailLayoutBuilder.h"
|
|
#include "Dialogs/Dialogs.h"
|
|
#include "EdGraph/EdGraph.h"
|
|
#include "EdGraph/EdGraphNode.h"
|
|
#include "EdGraph/EdGraphSchema.h"
|
|
#include "EdGraphSchema_K2.h"
|
|
#include "Editor.h"
|
|
#include "Editor/EditorEngine.h"
|
|
#include "EditorCategoryUtils.h"
|
|
#include "Engine/Blueprint.h"
|
|
#include "Engine/BlueprintGeneratedClass.h"
|
|
#include "Engine/MemberReference.h"
|
|
#include "Engine/TimelineTemplate.h"
|
|
#include "Fonts/SlateFontInfo.h"
|
|
#include "Framework/Application/SlateApplication.h"
|
|
#include "Framework/Commands/GenericCommands.h"
|
|
#include "Framework/Commands/InputChord.h"
|
|
#include "Framework/Commands/UIAction.h"
|
|
#include "Framework/Commands/UICommandInfo.h"
|
|
#include "Framework/Commands/UICommandList.h"
|
|
#include "Framework/MultiBox/MultiBoxBuilder.h"
|
|
#include "Framework/Notifications/NotificationManager.h"
|
|
#include "GenericPlatform/GenericApplication.h"
|
|
#include "GraphActionNode.h"
|
|
#include "GraphEditorActions.h"
|
|
#include "GraphEditorDragDropAction.h"
|
|
#include "HAL/PlatformApplicationMisc.h"
|
|
#include "HAL/PlatformCrt.h"
|
|
#include "Input/DragAndDrop.h"
|
|
#include "Input/Events.h"
|
|
#include "InputCoreTypes.h"
|
|
#include "K2Node.h"
|
|
#include "K2Node_AddComponent.h"
|
|
#include "K2Node_CallFunction.h"
|
|
#include "K2Node_Composite.h"
|
|
#include "K2Node_CreateDelegate.h"
|
|
#include "K2Node_EditablePinBase.h"
|
|
#include "K2Node_Event.h"
|
|
#include "K2Node_EventNodeInterface.h"
|
|
#include "K2Node_ExternalGraphInterface.h"
|
|
#include "K2Node_FunctionEntry.h"
|
|
#include "K2Node_Tunnel.h"
|
|
#include "Kismet2/BlueprintEditorUtils.h"
|
|
#include "Kismet2/KismetEditorUtilities.h"
|
|
#include "Layout/Children.h"
|
|
#include "Layout/Margin.h"
|
|
#include "Math/Color.h"
|
|
#include "Misc/AssertionMacros.h"
|
|
#include "Misc/Attribute.h"
|
|
#include "Misc/CString.h"
|
|
#include "Misc/Guid.h"
|
|
#include "ObjectEditorUtils.h"
|
|
#include "ObjectTools.h"
|
|
#include "SBlueprintPalette.h"
|
|
#include "SGraphActionMenu.h"
|
|
#include "SKismetInspector.h"
|
|
#include "SPositiveActionButton.h"
|
|
#include "SReplaceNodeReferences.h"
|
|
#include "SSubobjectBlueprintEditor.h"
|
|
#include "ScopedTransaction.h"
|
|
#include "SlotBase.h"
|
|
#include "SourceCodeNavigation.h"
|
|
#include "Styling/ISlateStyle.h"
|
|
#include "Styling/SlateColor.h"
|
|
#include "Styling/SlateTypes.h"
|
|
#include "Subsystems/AssetEditorSubsystem.h"
|
|
#include "Templates/Casts.h"
|
|
#include "Templates/Less.h"
|
|
#include "Templates/SubclassOf.h"
|
|
#include "Templates/Tuple.h"
|
|
#include "Templates/UnrealTemplate.h"
|
|
#include "Textures/SlateIcon.h"
|
|
#include "Types/ISlateMetaData.h"
|
|
#include "UObject/Class.h"
|
|
#include "UObject/Field.h"
|
|
#include "UObject/Object.h"
|
|
#include "UObject/ObjectMacros.h"
|
|
#include "UObject/ObjectPtr.h"
|
|
#include "UObject/PropertyPortFlags.h"
|
|
#include "UObject/Script.h"
|
|
#include "UObject/UObjectBaseUtility.h"
|
|
#include "UObject/UObjectGlobals.h"
|
|
#include "UObject/UObjectIterator.h"
|
|
#include "UObject/UnrealType.h"
|
|
#include "UObject/WeakObjectPtr.h"
|
|
#include "Widgets/Images/SImage.h"
|
|
#include "Widgets/Input/SButton.h"
|
|
#include "Widgets/Input/SComboButton.h"
|
|
#include "Widgets/Input/SSearchBox.h"
|
|
#include "Widgets/Layout/SBorder.h"
|
|
#include "Widgets/Layout/SBox.h"
|
|
#include "Widgets/Notifications/SNotificationList.h"
|
|
#include "Widgets/SBoxPanel.h"
|
|
#include "Widgets/SNullWidget.h"
|
|
#include "Widgets/SWidget.h"
|
|
#include "Widgets/Text/STextBlock.h"
|
|
|
|
struct FGeometry;
|
|
struct FSlateBrush;
|
|
|
|
#define LOCTEXT_NAMESPACE "MyBlueprint"
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Magic values to differentiate Variables and Graphs on the clipboard
|
|
static const TCHAR* VAR_PREFIX = TEXT("BPVar");
|
|
static const TCHAR* GRAPH_PREFIX = TEXT("BPGraph");
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
void FMyBlueprintCommands::RegisterCommands()
|
|
{
|
|
UI_COMMAND( OpenGraph, "Open Graph", "Opens up this function, macro, or event graph's graph panel up.", EUserInterfaceActionType::Button, FInputChord() );
|
|
UI_COMMAND( OpenGraphInNewTab, "Open in New Tab", "Opens up this function, macro, or event graph's graph panel up in a new tab. Hold down Ctrl and double click for shortcut.", EUserInterfaceActionType::Button, FInputChord() );
|
|
UI_COMMAND( OpenExternalGraph, "Open External Graph", "Opens up this external graph's graph panel in its own asset editor", EUserInterfaceActionType::Button, FInputChord() );
|
|
UI_COMMAND( FocusNode, "Focus", "Focuses on the associated node", EUserInterfaceActionType::Button, FInputChord() );
|
|
UI_COMMAND( FocusNodeInNewTab, "Focus in New Tab", "Focuses on the associated node in a new tab", EUserInterfaceActionType::Button, FInputChord() );
|
|
UI_COMMAND( ImplementFunction, "Implement event", "Implements this overridable function as a new event.", EUserInterfaceActionType::Button, FInputChord() );
|
|
UI_COMMAND( DeleteEntry, "Delete", "Deletes this function or variable from this blueprint.", EUserInterfaceActionType::Button, FInputChord(EKeys::Delete), FInputChord(EKeys::BackSpace));
|
|
UI_COMMAND( PasteVariable, "Paste Variable", "Pastes the variable to this blueprint.", EUserInterfaceActionType::Button, FInputChord());
|
|
UI_COMMAND( PasteLocalVariable, "Paste Local Variable", "Pastes the variable to this scope.", EUserInterfaceActionType::Button, FInputChord());
|
|
UI_COMMAND( PasteFunction, "Paste Function", "Pastes the function to this blueprint.", EUserInterfaceActionType::Button, FInputChord());
|
|
UI_COMMAND( PasteMacro, "Paste Macro", "Pastes the macro to this blueprint.", EUserInterfaceActionType::Button, FInputChord());
|
|
UI_COMMAND( GotoNativeVarDefinition, "Goto Code Definition", "Goto the native code definition of this variable", EUserInterfaceActionType::Button, FInputChord() );
|
|
UI_COMMAND( MoveVariableToParent, "Move to Parent Class", "Moves the variable to its parent class", EUserInterfaceActionType::Button, FInputChord() );
|
|
UI_COMMAND( MoveFunctionToParent, "Move to Parent Class", "Moves the function to its parent class", EUserInterfaceActionType::Button, FInputChord() );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
class FMyBlueprintCategoryDragDropAction : public FGraphEditorDragDropAction
|
|
{
|
|
public:
|
|
DRAG_DROP_OPERATOR_TYPE(FMyBlueprintCategoryDragDropAction, FGraphEditorDragDropAction)
|
|
|
|
virtual void HoverTargetChanged() override
|
|
{
|
|
const FSlateBrush* StatusSymbol = FAppStyle::GetBrush(TEXT("NoBrush"));
|
|
FText Message = DraggedCategory;
|
|
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("DraggedCategory"), DraggedCategory);
|
|
|
|
if (!HoveredCategoryName.IsEmpty())
|
|
{
|
|
if(HoveredCategoryName.EqualTo(DraggedCategory))
|
|
{
|
|
StatusSymbol = FAppStyle::GetBrush(TEXT("Graph.ConnectorFeedback.Error"));
|
|
|
|
|
|
Message = FText::Format( LOCTEXT("MoveCatOverSelf", "Cannot insert category '{DraggedCategory}' before itself."), Args );
|
|
}
|
|
else
|
|
{
|
|
StatusSymbol = FAppStyle::GetBrush(TEXT("Graph.ConnectorFeedback.OK"));
|
|
Args.Add(TEXT("HoveredCategory"), HoveredCategoryName);
|
|
Message = FText::Format( LOCTEXT("MoveCatOK", "Move category '{DraggedCategory}' before '{HoveredCategory}'"), Args );
|
|
}
|
|
}
|
|
else if (HoveredAction.IsValid())
|
|
{
|
|
StatusSymbol = FAppStyle::GetBrush(TEXT("Graph.ConnectorFeedback.Error"));
|
|
Message = LOCTEXT("MoveCatOverAction", "Can only insert before another category.");
|
|
}
|
|
else
|
|
{
|
|
StatusSymbol = FAppStyle::GetBrush(TEXT("Graph.ConnectorFeedback.Error"));
|
|
Message = FText::Format(LOCTEXT("MoveCatAction", "Moving category '{DraggedCategory}'"), Args);
|
|
}
|
|
|
|
SetSimpleFeedbackMessage(StatusSymbol, FLinearColor::White, Message);
|
|
}
|
|
|
|
virtual FReply DroppedOnCategory(FText OnCategory) override
|
|
{
|
|
// Get MyBlueprint via MyBlueprintPtr
|
|
TSharedPtr<SMyBlueprint> MyBlueprint = MyBlueprintPtr.Pin();
|
|
if(MyBlueprint.IsValid())
|
|
{
|
|
// Move the category in the blueprint category sort list
|
|
MyBlueprint->MoveCategoryBeforeCategory( DraggedCategory, OnCategory );
|
|
}
|
|
|
|
return FReply::Handled();
|
|
}
|
|
|
|
static TSharedRef<FMyBlueprintCategoryDragDropAction> New(const FText& InCategory, TSharedPtr<SMyBlueprint> InMyBlueprint)
|
|
{
|
|
TSharedRef<FMyBlueprintCategoryDragDropAction> Operation = MakeShareable(new FMyBlueprintCategoryDragDropAction);
|
|
Operation->DraggedCategory = InCategory;
|
|
Operation->MyBlueprintPtr = InMyBlueprint;
|
|
Operation->Construct();
|
|
return Operation;
|
|
}
|
|
|
|
/** Category we were dragging */
|
|
FText DraggedCategory;
|
|
/** MyBlueprint widget we dragged from */
|
|
TWeakPtr<SMyBlueprint> MyBlueprintPtr;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// FGraphActionSort
|
|
|
|
// Helper structure to aid category sorting
|
|
struct FGraphActionSort
|
|
{
|
|
public:
|
|
FGraphActionSort(TArray<FName>& BlueprintCategorySorting)
|
|
: bCategoriesModified(false)
|
|
, CategorySortIndices(BlueprintCategorySorting)
|
|
{
|
|
CategoryUsage.Init(0, CategorySortIndices.Num());
|
|
}
|
|
|
|
void AddAction(const FString& Category, TSharedPtr<FEdGraphSchemaAction> Action)
|
|
{
|
|
// Find root category
|
|
int32 RootCategoryDelim = Category.Find(TEXT("|"));
|
|
FName RootCategory = RootCategoryDelim == INDEX_NONE ? *Category : *Category.Left(RootCategoryDelim);
|
|
// Get root sort index
|
|
const int32 SortIndex = GetSortIndex(RootCategory) + Action->GetSectionID();
|
|
|
|
SortedActions.Add(SortIndex, Action);
|
|
}
|
|
|
|
void AddAction(TSharedPtr<FEdGraphSchemaAction> Action)
|
|
{
|
|
const FString UserCategoryName = FEditorCategoryUtils::GetCategoryDisplayString(Action->GetCategory().ToString());
|
|
AddAction(UserCategoryName, Action);
|
|
}
|
|
|
|
void GetAllActions(FGraphActionListBuilderBase& OutActions)
|
|
{
|
|
SortedActions.KeySort(TLess<int32>());
|
|
|
|
for (const auto& Iter : SortedActions)
|
|
{
|
|
OutActions.AddAction(Iter.Value);
|
|
}
|
|
}
|
|
|
|
void CleanupCategories()
|
|
{
|
|
// Scrub unused categories from the blueprint
|
|
if (bCategoriesModified)
|
|
{
|
|
for (int32 CategoryIdx = CategoryUsage.Num() - 1; CategoryIdx >= 0; CategoryIdx--)
|
|
{
|
|
if (CategoryUsage[CategoryIdx] == 0)
|
|
{
|
|
CategorySortIndices.RemoveAt(CategoryIdx);
|
|
}
|
|
}
|
|
bCategoriesModified = false;
|
|
}
|
|
}
|
|
|
|
private:
|
|
const int32 GetSortIndex(FName Category)
|
|
{
|
|
int32 SortIndex = CategorySortIndices.Find(Category);
|
|
|
|
if (SortIndex == INDEX_NONE)
|
|
{
|
|
bCategoriesModified = true;
|
|
SortIndex = CategorySortIndices.Add(Category);
|
|
CategoryUsage.Add(0);
|
|
}
|
|
CategoryUsage[SortIndex]++;
|
|
// Spread the sort values so we can fine tune sorting
|
|
SortIndex *= 1000;
|
|
|
|
return SortIndex + SortedActions.Num();
|
|
}
|
|
|
|
private:
|
|
/** Signals if the blueprint categories have been modified and require cleanup */
|
|
bool bCategoriesModified;
|
|
/** Tracks category usage to aid removal of unused categories */
|
|
TArray<int32> CategoryUsage;
|
|
/** Reference to the category sorting in the blueprint */
|
|
TArray<FName>& CategorySortIndices;
|
|
/** Map used to sort Graph actions */
|
|
TMultiMap<int32, TSharedPtr<FEdGraphSchemaAction>> SortedActions;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
void SMyBlueprint::Construct(const FArguments& InArgs, TWeakPtr<FBlueprintEditor> InBlueprintEditor, const UBlueprint* InBlueprint )
|
|
{
|
|
bNeedsRefresh = false;
|
|
bShowReplicatedVariablesOnly = false;
|
|
|
|
BlueprintEditorPtr = InBlueprintEditor;
|
|
EdGraph = nullptr;
|
|
|
|
TSharedPtr<SWidget> ToolbarBuilderWidget = TSharedPtr<SWidget>();
|
|
|
|
if( InBlueprintEditor.IsValid() )
|
|
{
|
|
Blueprint = BlueprintEditorPtr.Pin()->GetBlueprintObj();
|
|
|
|
CommandList = MakeShareable(new FUICommandList);
|
|
|
|
CommandList->Append(InBlueprintEditor.Pin()->GetToolkitCommands());
|
|
|
|
CommandList->MapAction( FMyBlueprintCommands::Get().OpenGraph,
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::OnOpenGraph),
|
|
FCanExecuteAction(), FIsActionChecked(),
|
|
FIsActionButtonVisible::CreateSP(this, &SMyBlueprint::CanOpenGraph) );
|
|
|
|
CommandList->MapAction( FMyBlueprintCommands::Get().OpenGraphInNewTab,
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::OnOpenGraphInNewTab),
|
|
FCanExecuteAction(), FIsActionChecked(),
|
|
FIsActionButtonVisible::CreateSP(this, &SMyBlueprint::CanOpenGraph) );
|
|
|
|
CommandList->MapAction( FMyBlueprintCommands::Get().OpenExternalGraph,
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::OnOpenExternalGraph),
|
|
FCanExecuteAction(), FIsActionChecked(),
|
|
FIsActionButtonVisible::CreateSP(this, &SMyBlueprint::CanOpenExternalGraph) );
|
|
|
|
CommandList->MapAction( FMyBlueprintCommands::Get().FocusNode,
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::OnFocusNode),
|
|
FCanExecuteAction(), FIsActionChecked(),
|
|
FIsActionButtonVisible::CreateSP(this, &SMyBlueprint::CanFocusOnNode) );
|
|
|
|
CommandList->MapAction( FMyBlueprintCommands::Get().FocusNodeInNewTab,
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::OnFocusNodeInNewTab),
|
|
FCanExecuteAction(), FIsActionChecked(),
|
|
FIsActionButtonVisible::CreateSP(this, &SMyBlueprint::CanFocusOnNode) );
|
|
|
|
CommandList->MapAction( FMyBlueprintCommands::Get().ImplementFunction,
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::OnImplementFunction),
|
|
FCanExecuteAction(), FIsActionChecked(),
|
|
FIsActionButtonVisible::CreateSP(this, &SMyBlueprint::CanImplementFunction) );
|
|
|
|
CommandList->MapAction( FGraphEditorCommands::Get().FindReferences,
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::OnFindReference, /*bSearchAllBlueprints=*/false, EGetFindReferenceSearchStringFlags::Legacy),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked(),
|
|
FIsActionButtonVisible::CreateSP(this, &SMyBlueprint::CanFindReference) );
|
|
|
|
CommandList->MapAction( FGraphEditorCommands::Get().FindReferencesByNameLocal,
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::OnFindReference, /*bSearchAllBlueprints=*/false, EGetFindReferenceSearchStringFlags::None),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked(),
|
|
FIsActionButtonVisible::CreateSP(this, &SMyBlueprint::CanFindReference) );
|
|
|
|
|
|
CommandList->MapAction( FGraphEditorCommands::Get().FindReferencesByNameGlobal,
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::OnFindReference, /*bSearchAllBlueprints=*/true, EGetFindReferenceSearchStringFlags::None),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked(),
|
|
FIsActionButtonVisible::CreateSP(this, &SMyBlueprint::CanFindReference) );
|
|
|
|
CommandList->MapAction( FGraphEditorCommands::Get().FindReferencesByClassMemberLocal,
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::OnFindReference, /*bSearchAllBlueprints=*/false, EGetFindReferenceSearchStringFlags::UseSearchSyntax),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked(),
|
|
FIsActionButtonVisible::CreateSP(this, &SMyBlueprint::CanFindReference) );
|
|
|
|
CommandList->MapAction( FGraphEditorCommands::Get().FindReferencesByClassMemberGlobal,
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::OnFindReference, /*bSearchAllBlueprints=*/true, EGetFindReferenceSearchStringFlags::UseSearchSyntax),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked(),
|
|
FIsActionButtonVisible::CreateSP(this, &SMyBlueprint::CanFindReference) );
|
|
|
|
CommandList->MapAction( FGraphEditorCommands::Get().FindAndReplaceReferences,
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::OnFindAndReplaceReference),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked(),
|
|
FIsActionButtonVisible::CreateSP(this, &SMyBlueprint::CanFindAndReplaceReference) );
|
|
|
|
CommandList->MapAction( FMyBlueprintCommands::Get().DeleteEntry,
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::OnDeleteEntry),
|
|
FCanExecuteAction::CreateSP(this, &SMyBlueprint::CanDeleteEntry) );
|
|
|
|
CommandList->MapAction( FGenericCommands::Get().Duplicate,
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::OnDuplicateAction),
|
|
FCanExecuteAction::CreateSP(this, &SMyBlueprint::CanDuplicateAction),
|
|
FIsActionChecked(),
|
|
FIsActionButtonVisible::CreateSP(this, &SMyBlueprint::IsDuplicateActionVisible) );
|
|
|
|
CommandList->MapAction( FMyBlueprintCommands::Get().MoveVariableToParent,
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::OnMoveToParent),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked(),
|
|
FIsActionButtonVisible::CreateSP(this, &SMyBlueprint::CanMoveVariableToParent) );
|
|
|
|
CommandList->MapAction( FMyBlueprintCommands::Get().MoveFunctionToParent,
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::OnMoveToParent),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked(),
|
|
FIsActionButtonVisible::CreateSP(this, &SMyBlueprint::CanMoveFunctionToParent) );
|
|
|
|
CommandList->MapAction( FMyBlueprintCommands::Get().GotoNativeVarDefinition,
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::GotoNativeCodeVarDefinition),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked(),
|
|
FIsActionButtonVisible::CreateSP(this, &SMyBlueprint::IsNativeVariable) );
|
|
ToolbarBuilderWidget = SNullWidget::NullWidget;
|
|
|
|
CommandList->MapAction(FGenericCommands::Get().Rename,
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::OnRequestRenameOnActionNode),
|
|
FCanExecuteAction::CreateSP(this, &SMyBlueprint::CanRequestRenameOnActionNode));
|
|
|
|
CommandList->MapAction(FGenericCommands::Get().Copy,
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::OnCopy),
|
|
FCanExecuteAction::CreateSP(this, &SMyBlueprint::CanCopy));
|
|
|
|
CommandList->MapAction(FGenericCommands::Get().Cut,
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::OnCut),
|
|
FCanExecuteAction::CreateSP(this, &SMyBlueprint::CanCut));
|
|
|
|
CommandList->MapAction(FGenericCommands::Get().Paste,
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::OnPasteGeneric),
|
|
FCanExecuteAction(), FIsActionChecked(),
|
|
FIsActionButtonVisible::CreateSP(this, &SMyBlueprint::CanPasteGeneric));
|
|
|
|
CommandList->MapAction(FMyBlueprintCommands::Get().PasteVariable,
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::OnPasteVariable),
|
|
FCanExecuteAction(), FIsActionChecked(),
|
|
FIsActionButtonVisible::CreateSP(this, &SMyBlueprint::CanPasteVariable));
|
|
|
|
CommandList->MapAction(FMyBlueprintCommands::Get().PasteLocalVariable,
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::OnPasteLocalVariable),
|
|
FCanExecuteAction(), FIsActionChecked(),
|
|
FIsActionButtonVisible::CreateSP(this, &SMyBlueprint::CanPasteLocalVariable));
|
|
|
|
CommandList->MapAction(FMyBlueprintCommands::Get().PasteFunction,
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::OnPasteFunction),
|
|
FCanExecuteAction(), FIsActionChecked(),
|
|
FIsActionButtonVisible::CreateSP(this, &SMyBlueprint::CanPasteFunction));
|
|
|
|
CommandList->MapAction(FMyBlueprintCommands::Get().PasteMacro,
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::OnPasteMacro),
|
|
FCanExecuteAction(), FIsActionChecked(),
|
|
FIsActionButtonVisible::CreateSP(this, &SMyBlueprint::CanPasteMacro));
|
|
}
|
|
else
|
|
{
|
|
// we're in read only mode when there's no blueprint editor:
|
|
Blueprint = const_cast<UBlueprint*>(InBlueprint);
|
|
check(Blueprint);
|
|
ToolbarBuilderWidget = SNew(SBox);
|
|
}
|
|
|
|
TSharedPtr<SWidget> AddNewMenu = SNew(SPositiveActionButton)
|
|
.AddMetaData<FTagMetaData>(FTagMetaData(TEXT("MyBlueprintAddNewCombo")))
|
|
.Icon(FAppStyle::Get().GetBrush("Icons.Plus"))
|
|
.Text(LOCTEXT("AddNewLabel", "Add"))
|
|
.ToolTipText(LOCTEXT("AddNewToolTip", "Add a new Variable, Graph, Function, Macro, or Event Dispatcher."))
|
|
.IsEnabled(this, &SMyBlueprint::IsEditingMode)
|
|
.OnGetMenuContent(this, &SMyBlueprint::CreateAddNewMenuWidget);
|
|
|
|
FMenuBuilder ViewOptions(true, nullptr);
|
|
|
|
ViewOptions.AddMenuEntry(
|
|
LOCTEXT("ShowInheritedVariables", "Show Inherited Variables"),
|
|
LOCTEXT("ShowInheritedVariablesTooltip", "Should inherited variables from parent classes and blueprints be shown in the tree?"),
|
|
FSlateIcon(),
|
|
FUIAction(
|
|
FExecuteAction::CreateSP( this, &SMyBlueprint::OnToggleShowInheritedVariables ),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP( this, &SMyBlueprint::IsShowingInheritedVariables )
|
|
),
|
|
NAME_None,
|
|
EUserInterfaceActionType::ToggleButton,
|
|
TEXT("MyBlueprint_ShowInheritedVariables")
|
|
);
|
|
|
|
ViewOptions.AddMenuEntry(
|
|
LOCTEXT("ShowEmptySections", "Show Empty Sections"),
|
|
LOCTEXT("ShowEmptySectionsTooltip", "Should we show empty sections? eg. Graphs, Functions...etc."),
|
|
FSlateIcon(),
|
|
FUIAction(
|
|
FExecuteAction::CreateSP( this, &SMyBlueprint::OnToggleShowEmptySections ),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP(this, &SMyBlueprint::IsShowingEmptySections)
|
|
),
|
|
NAME_None,
|
|
EUserInterfaceActionType::ToggleButton,
|
|
TEXT("MyBlueprint_ShowEmptySections")
|
|
);
|
|
|
|
ViewOptions.AddMenuEntry(
|
|
LOCTEXT("ShowReplicatedVariablesOnly", "Show Replicated Variables Only"),
|
|
LOCTEXT("ShowReplicatedVariablesOnlyTooltip", "Should we only show variables that are replicated?"),
|
|
FSlateIcon(),
|
|
FUIAction(
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::OnToggleShowReplicatedVariablesOnly),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP(this, &SMyBlueprint::IsShowingReplicatedVariablesOnly)
|
|
),
|
|
NAME_None,
|
|
EUserInterfaceActionType::ToggleButton,
|
|
TEXT("MyBlueprint_ShowReplicatedVariablesOnly")
|
|
);
|
|
|
|
ViewOptions.AddMenuEntry(
|
|
LOCTEXT("AlwaysShowInterfacesInOverrides", "Show interfaces in the function override menu"),
|
|
LOCTEXT("AlwaysShowInterfacesInOverridesTooltip", "Should we always display interface functions/events in the override menu?"),
|
|
FSlateIcon(),
|
|
FUIAction(
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::OnToggleAlwaysShowInterfacesInOverrides),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP(this, &SMyBlueprint::GetAlwaysShowInterfacesInOverrides)
|
|
),
|
|
NAME_None,
|
|
EUserInterfaceActionType::ToggleButton,
|
|
TEXT("MyBlueprint_AlwaysShowInterfacesInOverrides")
|
|
);
|
|
|
|
ViewOptions.AddMenuEntry(
|
|
LOCTEXT("AlwaysShowAccessSpecifier", "Show access specifier in the My Blueprint View"),
|
|
LOCTEXT("AlwaysShowAccessSpecifierTooltip", "Should we always display the access specifier of functions in the function menu?"),
|
|
FSlateIcon(),
|
|
FUIAction(
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::OnToggleShowAccessSpecifier),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP(this, &SMyBlueprint::GetShowAccessSpecifier)
|
|
),
|
|
NAME_None,
|
|
EUserInterfaceActionType::ToggleButton,
|
|
TEXT("MyBlueprint_AlwaysShowAccessSpecifier")
|
|
);
|
|
|
|
SAssignNew(FilterBox, SSearchBox)
|
|
.OnTextChanged( this, &SMyBlueprint::OnFilterTextChanged );
|
|
|
|
// create the main action list piece of this widget
|
|
SAssignNew(GraphActionMenu, SGraphActionMenu, false)
|
|
.OnGetFilterText(this, &SMyBlueprint::GetFilterText)
|
|
.OnCreateWidgetForAction(this, &SMyBlueprint::OnCreateWidgetForAction)
|
|
.OnCollectAllActions(this, &SMyBlueprint::CollectAllActions)
|
|
.OnCollectStaticSections(this, &SMyBlueprint::CollectStaticSections)
|
|
.OnActionDragged(this, &SMyBlueprint::OnActionDragged)
|
|
.OnCategoryDragged(this, &SMyBlueprint::OnCategoryDragged)
|
|
.OnActionSelected(this, &SMyBlueprint::OnGlobalActionSelected)
|
|
.OnActionDoubleClicked(this, &SMyBlueprint::OnActionDoubleClicked)
|
|
.OnContextMenuOpening(this, &SMyBlueprint::OnContextMenuOpening)
|
|
.OnCategoryTextCommitted(this, &SMyBlueprint::OnCategoryNameCommitted)
|
|
.OnCanRenameSelectedAction(this, &SMyBlueprint::CanRequestRenameOnActionNode)
|
|
.OnGetSectionTitle(this, &SMyBlueprint::OnGetSectionTitle)
|
|
.OnGetSectionWidget(this, &SMyBlueprint::OnGetSectionWidget)
|
|
.OnActionMatchesName(this, &SMyBlueprint::HandleActionMatchesName)
|
|
.DefaultRowExpanderBaseIndentLevel(1)
|
|
.AlphaSortItems(false)
|
|
.UseSectionStyling(true);
|
|
|
|
|
|
// now piece together all the content for this widget
|
|
ChildSlot
|
|
[
|
|
SNew(SVerticalBox)
|
|
|
|
+ SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
[
|
|
SNew(SBorder)
|
|
.Padding(4.0f)
|
|
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
|
|
.AddMetaData<FTagMetaData>(FTagMetaData(TEXT("MyBlueprintPanel")))
|
|
[
|
|
SNew(SVerticalBox)
|
|
|
|
+ SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
[
|
|
ToolbarBuilderWidget.ToSharedRef()
|
|
]
|
|
|
|
+ SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
|
|
+ SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.VAlign(VAlign_Center)
|
|
.Padding(0, 0, 4, 0)
|
|
[
|
|
AddNewMenu.ToSharedRef()
|
|
]
|
|
|
|
+ SHorizontalBox::Slot()
|
|
.FillWidth(1.0f)
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
FilterBox.ToSharedRef()
|
|
]
|
|
|
|
+ SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.Padding(2, 0, 0, 0)
|
|
[
|
|
SNew(SComboButton)
|
|
.ContentPadding(0.0f)
|
|
.ComboButtonStyle(&FAppStyle::Get().GetWidgetStyle<FComboButtonStyle>("SimpleComboButton"))
|
|
.HasDownArrow(false)
|
|
.AddMetaData<FTagMetaData>(FTagMetaData(TEXT("ViewOptions")))
|
|
.ButtonContent()
|
|
[
|
|
SNew(SImage)
|
|
.ColorAndOpacity(FSlateColor::UseForeground())
|
|
.Image(FAppStyle::Get().GetBrush("Icons.Settings"))
|
|
]
|
|
.MenuContent()
|
|
[
|
|
ViewOptions.MakeWidget()
|
|
]
|
|
]
|
|
]
|
|
]
|
|
]
|
|
|
|
+ SVerticalBox::Slot()
|
|
.FillHeight(1.0f)
|
|
[
|
|
GraphActionMenu.ToSharedRef()
|
|
]
|
|
];
|
|
|
|
ResetLastPinType();
|
|
|
|
if( !BlueprintEditorPtr.IsValid() )
|
|
{
|
|
Refresh();
|
|
}
|
|
|
|
TMap<int32, bool> ExpandedSections;
|
|
ExpandedSections.Add(NodeSectionID::VARIABLE, true);
|
|
ExpandedSections.Add(NodeSectionID::FUNCTION, true);
|
|
ExpandedSections.Add(NodeSectionID::MACRO, true);
|
|
ExpandedSections.Add(NodeSectionID::DELEGATE, true);
|
|
ExpandedSections.Add(NodeSectionID::GRAPH, true);
|
|
ExpandedSections.Add(NodeSectionID::ANIMGRAPH, true);
|
|
ExpandedSections.Add(NodeSectionID::ANIMLAYER, true);
|
|
ExpandedSections.Add(NodeSectionID::LOCAL_VARIABLE, true);
|
|
|
|
GraphActionMenu->SetSectionExpansion(ExpandedSections);
|
|
|
|
FCoreUObjectDelegates::OnObjectPropertyChanged.AddRaw(this, &SMyBlueprint::OnObjectPropertyChanged);
|
|
}
|
|
|
|
SMyBlueprint::~SMyBlueprint()
|
|
{
|
|
FCoreUObjectDelegates::OnObjectPropertyChanged.RemoveAll(this);
|
|
}
|
|
|
|
void SMyBlueprint::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
|
|
{
|
|
SCompoundWidget::Tick(AllottedGeometry, InCurrentTime, InDeltaTime);
|
|
|
|
if(bNeedsRefresh)
|
|
{
|
|
Refresh();
|
|
}
|
|
}
|
|
|
|
FReply SMyBlueprint::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent)
|
|
{
|
|
if (CommandList.IsValid() && CommandList->ProcessCommandBindings(InKeyEvent))
|
|
{
|
|
return FReply::Handled();
|
|
}
|
|
return FReply::Unhandled();
|
|
}
|
|
|
|
void SMyBlueprint::OnCategoryNameCommitted(const FText& InNewText, ETextCommit::Type InTextCommit, TWeakPtr< FGraphActionNode > InAction )
|
|
{
|
|
// Remove excess whitespace and prevent categories with just spaces
|
|
FText CategoryName = FText::TrimPrecedingAndTrailing(InNewText);
|
|
|
|
TArray<TSharedPtr<FEdGraphSchemaAction>> Actions;
|
|
GraphActionMenu->GetCategorySubActions(InAction, Actions);
|
|
|
|
if (Actions.Num())
|
|
{
|
|
const FScopedTransaction Transaction( LOCTEXT( "RenameCategory", "Rename Category" ) );
|
|
|
|
GetBlueprintObj()->Modify();
|
|
|
|
for (int32 i = 0; i < Actions.Num(); ++i)
|
|
{
|
|
if (Actions[i]->GetTypeId() == FEdGraphSchemaAction_K2Var::StaticGetTypeId())
|
|
{
|
|
FEdGraphSchemaAction_K2Var* VarAction = (FEdGraphSchemaAction_K2Var*)Actions[i].Get();
|
|
|
|
if(FProperty* TargetProperty = VarAction->GetProperty())
|
|
{
|
|
UClass* OuterClass = VarAction->GetProperty()->GetOwnerChecked<UClass>();
|
|
const bool bIsNativeVar = (OuterClass->ClassGeneratedBy == NULL);
|
|
|
|
// If the variable is not native and it's outer is the skeleton generated class, we can rename the category
|
|
if(!bIsNativeVar && OuterClass == GetBlueprintObj()->SkeletonGeneratedClass)
|
|
{
|
|
FBlueprintEditorUtils::SetBlueprintVariableCategory(GetBlueprintObj(), VarAction->GetVariableName(), NULL, CategoryName, true);
|
|
}
|
|
}
|
|
}
|
|
else if (Actions[i]->GetTypeId() == FEdGraphSchemaAction_K2LocalVar::StaticGetTypeId())
|
|
{
|
|
FEdGraphSchemaAction_K2LocalVar* LocalVarAction = (FEdGraphSchemaAction_K2LocalVar*)Actions[i].Get();
|
|
|
|
FBlueprintEditorUtils::SetBlueprintVariableCategory(GetBlueprintObj(), LocalVarAction->GetVariableName(), CastChecked<UStruct>(LocalVarAction->GetVariableScope()), CategoryName, true);
|
|
}
|
|
else if (Actions[i]->GetTypeId() == FEdGraphSchemaAction_K2Delegate::StaticGetTypeId())
|
|
{
|
|
FEdGraphSchemaAction_K2Delegate* DelegateAction = (FEdGraphSchemaAction_K2Delegate*)Actions[i].Get();
|
|
FBlueprintEditorUtils::SetBlueprintVariableCategory(GetBlueprintObj(), DelegateAction->GetDelegateProperty()->GetFName(), NULL, CategoryName, true);
|
|
}
|
|
else if (Actions[i]->GetTypeId() == FEdGraphSchemaAction_K2Graph::StaticGetTypeId())
|
|
{
|
|
// Do not allow renaming of any graph actions outside of the following
|
|
if(Actions[i]->GetSectionID() == NodeSectionID::FUNCTION || Actions[i]->GetSectionID() == NodeSectionID::MACRO || Actions[i]->GetSectionID() == NodeSectionID::ANIMLAYER)
|
|
{
|
|
FEdGraphSchemaAction_K2Graph* GraphAction = (FEdGraphSchemaAction_K2Graph*)Actions[i].Get();
|
|
|
|
// Don't allow changing the category of a graph who's parent is not the current Blueprint
|
|
if(GraphAction && !FBlueprintEditorUtils::IsPaletteActionReadOnly(Actions[i], BlueprintEditorPtr.Pin()) && FBlueprintEditorUtils::FindBlueprintForGraph(GraphAction->EdGraph) == GetBlueprintObj())
|
|
{
|
|
FReply ReplyBySchema = FReply::Unhandled();
|
|
if (GraphAction->EdGraph)
|
|
{
|
|
if (UEdGraphSchema* Schema = (UEdGraphSchema*)GraphAction->EdGraph->GetSchema())
|
|
{
|
|
TArray<FString> CategoryParts;
|
|
TWeakPtr< FGraphActionNode > CurrentActionNode = InAction;
|
|
while (CurrentActionNode.IsValid())
|
|
{
|
|
FString CurrentDisplayName = CurrentActionNode.Pin()->GetDisplayName().ToString();
|
|
if (!CurrentDisplayName.IsEmpty())
|
|
{
|
|
CategoryParts.Insert(CurrentDisplayName, 0);
|
|
}
|
|
CurrentActionNode = CurrentActionNode.Pin()->GetParentNode();
|
|
}
|
|
|
|
FString OldCategoryPath = FString::Join(CategoryParts, TEXT("|"));
|
|
CategoryParts.Last() = CategoryName.ToString();
|
|
FString NewCategoryPath = FString::Join(CategoryParts, TEXT("|"));
|
|
|
|
FString CurrentCategoryPath = GraphAction->GetCategory().ToString();
|
|
if (CurrentCategoryPath == OldCategoryPath || CurrentCategoryPath.StartsWith(OldCategoryPath + TEXT("|"), ESearchCase::CaseSensitive))
|
|
{
|
|
NewCategoryPath = NewCategoryPath + CurrentCategoryPath.RightChop(OldCategoryPath.Len());
|
|
}
|
|
|
|
ReplyBySchema = Schema->TrySetGraphCategory(GraphAction->EdGraph, FText::FromString(NewCategoryPath));
|
|
}
|
|
}
|
|
|
|
if (!ReplyBySchema.IsEventHandled())
|
|
{
|
|
GraphAction->MovePersistentItemToCategory(CategoryName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Refresh();
|
|
FBlueprintEditorUtils::MarkBlueprintAsModified(GetBlueprintObj());
|
|
SelectItemByName(FName(*CategoryName.ToString()), ESelectInfo::OnMouseClick, InAction.Pin()->SectionID, true);
|
|
}
|
|
}
|
|
|
|
FText SMyBlueprint::OnGetSectionTitle( int32 InSectionID )
|
|
{
|
|
FText SeparatorTitle;
|
|
/* Setup an appropriate name for the section for this node */
|
|
switch( InSectionID )
|
|
{
|
|
case NodeSectionID::VARIABLE:
|
|
SeparatorTitle = NSLOCTEXT("GraphActionNode", "Variables", "Variables");
|
|
break;
|
|
case NodeSectionID::COMPONENT:
|
|
SeparatorTitle = NSLOCTEXT("GraphActionNode", "Components", "Components");
|
|
break;
|
|
case NodeSectionID::FUNCTION:
|
|
if ( OverridableFunctionActions.Num() > 0 )
|
|
{
|
|
SeparatorTitle = FText::Format(NSLOCTEXT("GraphActionNode", "FunctionsOverridableFormat", "Functions <TinyText.Subdued>({0} Overridable)</>"), FText::AsNumber(OverridableFunctionActions.Num()));
|
|
}
|
|
else
|
|
{
|
|
SeparatorTitle = NSLOCTEXT("GraphActionNode", "Functions", "Functions");
|
|
}
|
|
|
|
break;
|
|
case NodeSectionID::FUNCTION_OVERRIDABLE:
|
|
SeparatorTitle = NSLOCTEXT("GraphActionNode", "OverridableFunctions", "Overridable Functions");
|
|
break;
|
|
case NodeSectionID::MACRO:
|
|
SeparatorTitle = NSLOCTEXT("GraphActionNode", "Macros", "Macros");
|
|
break;
|
|
case NodeSectionID::INTERFACE:
|
|
SeparatorTitle = NSLOCTEXT("GraphActionNode", "Interfaces", "Interfaces");
|
|
break;
|
|
case NodeSectionID::DELEGATE:
|
|
SeparatorTitle = NSLOCTEXT("GraphActionNode", "EventDispatchers", "Event Dispatchers");
|
|
break;
|
|
case NodeSectionID::GRAPH:
|
|
SeparatorTitle = NSLOCTEXT("GraphActionNode", "Graphs", "Graphs");
|
|
break;
|
|
case NodeSectionID::ANIMGRAPH:
|
|
SeparatorTitle = NSLOCTEXT("GraphActionNode", "AnimationGraphs", "Animation Graphs");
|
|
break;
|
|
case NodeSectionID::ANIMLAYER:
|
|
SeparatorTitle = NSLOCTEXT("GraphActionNode", "AnimationLayers", "Animation Layers");
|
|
break;
|
|
case NodeSectionID::USER_ENUM:
|
|
SeparatorTitle = NSLOCTEXT("GraphActionNode", "Userenums", "User Enums");
|
|
break;
|
|
case NodeSectionID::LOCAL_VARIABLE:
|
|
if ( GetFocusedGraph() )
|
|
{
|
|
SeparatorTitle = FText::Format(NSLOCTEXT("GraphActionNode", "LocalVariables_Focused", "Local Variables <TinyText.Subdued>({0})</>"), FText::FromName(GetFocusedGraph()->GetFName()));
|
|
}
|
|
else
|
|
{
|
|
SeparatorTitle = NSLOCTEXT("GraphActionNode", "LocalVariables", "Local Variables");
|
|
}
|
|
break;
|
|
case NodeSectionID::USER_STRUCT:
|
|
SeparatorTitle = NSLOCTEXT("GraphActionNode", "Userstructs", "User Structs");
|
|
break;
|
|
default:
|
|
case NodeSectionID::NONE:
|
|
SeparatorTitle = FText::GetEmpty();
|
|
break;
|
|
}
|
|
return SeparatorTitle;
|
|
}
|
|
|
|
TSharedRef<SWidget> SMyBlueprint::OnGetSectionWidget(TSharedRef<SWidget> RowWidget, int32 InSectionID)
|
|
{
|
|
TWeakPtr<SWidget> WeakRowWidget = RowWidget;
|
|
|
|
FText AddNewText;
|
|
FName MetaDataTag;
|
|
|
|
switch ( InSectionID )
|
|
{
|
|
case NodeSectionID::VARIABLE:
|
|
AddNewText = LOCTEXT("AddNewVariable", "Variable");
|
|
MetaDataTag = TEXT("AddNewVariable");
|
|
break;
|
|
case NodeSectionID::FUNCTION:
|
|
AddNewText = LOCTEXT("AddNewFunction", "Function");
|
|
MetaDataTag = TEXT("AddNewFunction");
|
|
|
|
if ( OverridableFunctionActions.Num() > 0 )
|
|
{
|
|
return SNew(SHorizontalBox)
|
|
+ SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
[
|
|
SAssignNew(FunctionSectionButton, SComboButton)
|
|
.IsEnabled(this, &SMyBlueprint::IsEditingMode)
|
|
.Visibility(EVisibility::SelfHitTestInvisible)
|
|
.ForegroundColor(FAppStyle::GetSlateColor("DefaultForeground"))
|
|
.OnGetMenuContent(this, &SMyBlueprint::OnGetFunctionListMenu)
|
|
.ContentPadding(0.0f)
|
|
.HasDownArrow(true)
|
|
.ButtonContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Font(IDetailLayoutBuilder::GetDetailFontBold())
|
|
.Text(LOCTEXT("Override", "Override"))
|
|
]
|
|
]
|
|
|
|
+ SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.VAlign(VAlign_Center)
|
|
.Padding(2,0,0,0)
|
|
[
|
|
CreateAddToSectionButton(InSectionID, WeakRowWidget, AddNewText, MetaDataTag)
|
|
];
|
|
}
|
|
|
|
break;
|
|
case NodeSectionID::MACRO:
|
|
AddNewText = LOCTEXT("AddNewMacro", "Macro");
|
|
MetaDataTag = TEXT("AddNewMacro");
|
|
break;
|
|
case NodeSectionID::DELEGATE:
|
|
AddNewText = LOCTEXT("AddNewDelegate", "Event Dispatcher");
|
|
MetaDataTag = TEXT("AddNewDelegate");
|
|
break;
|
|
case NodeSectionID::GRAPH:
|
|
AddNewText = LOCTEXT("AddNewGraph", "New Graph");
|
|
MetaDataTag = TEXT("AddNewGraph");
|
|
break;
|
|
case NodeSectionID::ANIMLAYER:
|
|
AddNewText = LOCTEXT("AddNewAnimLayer", "New Animation Layer");
|
|
MetaDataTag = TEXT("AddNewAnimLayer");
|
|
break;
|
|
case NodeSectionID::LOCAL_VARIABLE:
|
|
AddNewText = LOCTEXT("AddNewLocalVariable", "Local Variable");
|
|
MetaDataTag = TEXT("AddNewLocalVariable");
|
|
break;
|
|
default:
|
|
return SNullWidget::NullWidget;
|
|
}
|
|
|
|
return CreateAddToSectionButton(InSectionID, WeakRowWidget, AddNewText, MetaDataTag);
|
|
}
|
|
|
|
TSharedRef<SWidget> SMyBlueprint::CreateAddToSectionButton(int32 InSectionID, TWeakPtr<SWidget> WeakRowWidget, FText AddNewText, FName MetaDataTag)
|
|
{
|
|
return
|
|
SNew(SButton)
|
|
.ButtonStyle(FAppStyle::Get(), "SimpleButton")
|
|
.OnClicked(this, &SMyBlueprint::OnAddButtonClickedOnSection, InSectionID)
|
|
.IsEnabled(this, &SMyBlueprint::CanAddNewElementToSection, InSectionID)
|
|
.ContentPadding(FMargin(1, 0))
|
|
.AddMetaData<FTagMetaData>(FTagMetaData(MetaDataTag))
|
|
.ToolTipText(AddNewText)
|
|
[
|
|
SNew(SImage)
|
|
.Image(FAppStyle::Get().GetBrush("Icons.PlusCircle"))
|
|
.ColorAndOpacity(FSlateColor::UseForeground())
|
|
];
|
|
}
|
|
|
|
FReply SMyBlueprint::OnAddButtonClickedOnSection(int32 InSectionID)
|
|
{
|
|
TSharedPtr<FBlueprintEditor> BlueprintEditor = BlueprintEditorPtr.Pin();
|
|
|
|
switch ( InSectionID )
|
|
{
|
|
case NodeSectionID::VARIABLE:
|
|
CommandList->ExecuteAction(FBlueprintEditorCommands::Get().AddNewVariable.ToSharedRef());
|
|
break;
|
|
case NodeSectionID::FUNCTION:
|
|
CommandList->ExecuteAction(FBlueprintEditorCommands::Get().AddNewFunction.ToSharedRef());
|
|
break;
|
|
case NodeSectionID::MACRO:
|
|
CommandList->ExecuteAction(FBlueprintEditorCommands::Get().AddNewMacroDeclaration.ToSharedRef());
|
|
break;
|
|
case NodeSectionID::DELEGATE:
|
|
CommandList->ExecuteAction(FBlueprintEditorCommands::Get().AddNewDelegate.ToSharedRef());
|
|
break;
|
|
case NodeSectionID::GRAPH:
|
|
CommandList->ExecuteAction(FBlueprintEditorCommands::Get().AddNewEventGraph.ToSharedRef());
|
|
break;
|
|
case NodeSectionID::ANIMLAYER:
|
|
CommandList->ExecuteAction(FBlueprintEditorCommands::Get().AddNewAnimationLayer.ToSharedRef());
|
|
break;
|
|
case NodeSectionID::LOCAL_VARIABLE:
|
|
OnAddNewLocalVariable();
|
|
break;
|
|
}
|
|
|
|
return FReply::Handled();
|
|
}
|
|
|
|
bool SMyBlueprint::CanAddNewElementToSection(int32 InSectionID) const
|
|
{
|
|
if (!IsEditingMode())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (UBlueprint* CurrentBlueprint = GetBlueprintObj())
|
|
{
|
|
switch (InSectionID)
|
|
{
|
|
case NodeSectionID::VARIABLE:
|
|
return CurrentBlueprint->SupportsGlobalVariables();
|
|
case NodeSectionID::FUNCTION:
|
|
return CurrentBlueprint->SupportsFunctions();
|
|
case NodeSectionID::MACRO:
|
|
return CurrentBlueprint->SupportsMacros();
|
|
case NodeSectionID::DELEGATE:
|
|
return CurrentBlueprint->SupportsDelegates();
|
|
case NodeSectionID::GRAPH:
|
|
return CurrentBlueprint->SupportsEventGraphs();
|
|
case NodeSectionID::ANIMLAYER:
|
|
return CurrentBlueprint->SupportsAnimLayers();
|
|
case NodeSectionID::LOCAL_VARIABLE:
|
|
return CurrentBlueprint->SupportsLocalVariables();
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool SMyBlueprint::HandleActionMatchesName(FEdGraphSchemaAction* InAction, const FName& InName) const
|
|
{
|
|
if (BlueprintEditorPtr.IsValid())
|
|
{
|
|
return BlueprintEditorPtr.Pin()->OnActionMatchesName(InAction, InName);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
TSharedRef<SWidget> SMyBlueprint::OnGetFunctionListMenu()
|
|
{
|
|
const bool bShouldCloseWindowAfterMenuSelection = true;
|
|
FMenuBuilder MenuBuilder(bShouldCloseWindowAfterMenuSelection, CommandList);
|
|
|
|
BuildOverridableFunctionsMenu(MenuBuilder);
|
|
|
|
TSharedRef<SWidget> MenuWidget = MenuBuilder.MakeWidget();
|
|
|
|
// force user focus onto the menu widget:
|
|
if(FunctionSectionButton.IsValid())
|
|
{
|
|
FunctionSectionButton->SetMenuContentWidgetToFocus(MenuWidget);
|
|
}
|
|
|
|
return MenuWidget;
|
|
}
|
|
|
|
void SMyBlueprint::BuildOverridableFunctionsMenu(FMenuBuilder& MenuBuilder)
|
|
{
|
|
// Sort by function name so that it's easier for users to find the function they're looking for:
|
|
OverridableFunctionActions.Sort([](const TSharedPtr<FEdGraphSchemaAction_K2Graph> &LHS, const TSharedPtr<FEdGraphSchemaAction_K2Graph> &RHS)
|
|
{
|
|
return LHS->GetMenuDescription().CompareToCaseIgnored(RHS->GetMenuDescription()) < 0;
|
|
});
|
|
|
|
MenuBuilder.BeginSection("OverrideFunction", LOCTEXT("OverrideFunction", "Override Function"));
|
|
{
|
|
for (TSharedPtr<FEdGraphSchemaAction_K2Graph>& OverrideAction : OverridableFunctionActions)
|
|
{
|
|
// Check if function data is valid and skip this entry if it's a private function
|
|
if (OverrideAction->FuncName != NAME_None)
|
|
{
|
|
if (const UFunction* OverrideActionFunction = FindUField<UFunction>(Blueprint->SkeletonGeneratedClass, OverrideAction->FuncName))
|
|
{
|
|
if (OverrideActionFunction->HasAnyFunctionFlags(FUNC_Private))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
UClass* const OverrideFuncClass = FBlueprintEditorUtils::GetOverrideFunctionClass(GetBlueprintObj(), OverrideAction->FuncName);
|
|
|
|
// Add the function name and tooltip
|
|
TSharedRef<SHorizontalBox> FunctionBox = SNew(SHorizontalBox)
|
|
+ SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.VAlign(VAlign_Center)
|
|
.HAlign(HAlign_Left)
|
|
.Padding(FMargin(2.0f, 0.0f, 20.0f, 0.0f))
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(OverrideAction->GetMenuDescription())
|
|
.ToolTipText(OverrideAction->GetTooltipDescription())
|
|
]
|
|
// Where the function came from function came from
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.HAlign(HAlign_Right)
|
|
.Padding(FMargin(1.0f, 0.0f, 0.0f, 0.0f))
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(OverrideFuncClass ? OverrideFuncClass->GetDisplayNameText() : FText::GetEmpty())
|
|
.ToolTipText(OverrideAction->GetTooltipDescription())
|
|
.ColorAndOpacity(FSlateColor::UseSubduedForeground())
|
|
];
|
|
|
|
MenuBuilder.AddMenuEntry(
|
|
FUIAction(
|
|
FExecuteAction::CreateSP(this, &SMyBlueprint::ImplementFunction, OverrideAction),
|
|
FCanExecuteAction::CreateSP(this, &SMyBlueprint::IsEditingMode)),
|
|
FunctionBox,
|
|
NAME_None,
|
|
OverrideAction->GetTooltipDescription(),
|
|
EUserInterfaceActionType::Button
|
|
);
|
|
}
|
|
}
|
|
MenuBuilder.EndSection();
|
|
}
|
|
|
|
bool SMyBlueprint::CanRequestRenameOnActionNode(TWeakPtr<FGraphActionNode> InSelectedNode) const
|
|
{
|
|
bool bIsReadOnly = true;
|
|
|
|
// If checking if renaming is available on a category node, the category must have a non-native entry
|
|
if (InSelectedNode.Pin()->IsCategoryNode())
|
|
{
|
|
TArray<TSharedPtr<FEdGraphSchemaAction>> Actions;
|
|
GraphActionMenu->GetCategorySubActions(InSelectedNode, Actions);
|
|
|
|
for (TSharedPtr<FEdGraphSchemaAction> Action : Actions)
|
|
{
|
|
if (Action->GetPersistentItemDefiningObject().IsPotentiallyEditable())
|
|
{
|
|
bIsReadOnly = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (InSelectedNode.Pin()->IsActionNode())
|
|
{
|
|
bIsReadOnly = FBlueprintEditorUtils::IsPaletteActionReadOnly(InSelectedNode.Pin()->Action, BlueprintEditorPtr.Pin());
|
|
if(!bIsReadOnly)
|
|
{
|
|
bIsReadOnly = !InSelectedNode.Pin()->Action->CanBeRenamed();
|
|
}
|
|
}
|
|
|
|
return IsEditingMode() && !bIsReadOnly;
|
|
}
|
|
|
|
void SMyBlueprint::Refresh()
|
|
{
|
|
bNeedsRefresh = false;
|
|
|
|
// Conform to our interfaces here to ensure we catch any newly added functions
|
|
FBlueprintEditorUtils::ConformImplementedInterfaces(GetBlueprintObj());
|
|
|
|
GraphActionMenu->RefreshAllActions(/*bPreserveExpansion=*/ true);
|
|
}
|
|
|
|
TSharedRef<SWidget> SMyBlueprint::OnCreateWidgetForAction(FCreateWidgetForActionData* const InCreateData)
|
|
{
|
|
return BlueprintEditorPtr.IsValid() ? SNew(SBlueprintPaletteItem, InCreateData, BlueprintEditorPtr.Pin()) : SNew(SBlueprintPaletteItem, InCreateData, GetBlueprintObj());
|
|
}
|
|
|
|
void SMyBlueprint::GetChildGraphs(UEdGraph* InEdGraph, int32 const SectionId, FGraphActionSort& SortList, const FText& ParentCategory) const
|
|
{
|
|
check(InEdGraph);
|
|
|
|
// Grab display info
|
|
FGraphDisplayInfo EdGraphDisplayInfo;
|
|
if (const UEdGraphSchema* Schema = InEdGraph->GetSchema())
|
|
{
|
|
Schema->GetGraphDisplayInformation(*InEdGraph, EdGraphDisplayInfo);
|
|
}
|
|
const FText EdGraphDisplayName = EdGraphDisplayInfo.DisplayName;
|
|
|
|
// Grab children graphs
|
|
for (UEdGraph* Graph : InEdGraph->SubGraphs)
|
|
{
|
|
if (Graph == nullptr)
|
|
{
|
|
ensureMsgf(Graph != nullptr, TEXT("A subgraph of %s was null"), *GetPathNameSafe(InEdGraph));
|
|
continue;
|
|
}
|
|
|
|
FGraphDisplayInfo ChildGraphDisplayInfo;
|
|
if (const UEdGraphSchema* ChildSchema = Graph->GetSchema())
|
|
{
|
|
ChildSchema->GetGraphDisplayInformation(*Graph, ChildGraphDisplayInfo);
|
|
}
|
|
|
|
FText DisplayText = ChildGraphDisplayInfo.DisplayName;
|
|
|
|
FText Category;
|
|
if (!ParentCategory.IsEmpty())
|
|
{
|
|
Category = FText::Format(FText::FromString(TEXT("{0}|{1}")), ParentCategory, EdGraphDisplayName);
|
|
}
|
|
else
|
|
{
|
|
Category = EdGraphDisplayName;
|
|
}
|
|
|
|
const FName DisplayName = FName(*DisplayText.ToString());
|
|
FText ChildTooltip = DisplayText;
|
|
FText ChildDesc = MoveTemp(DisplayText);
|
|
|
|
TSharedPtr<FEdGraphSchemaAction_K2Graph> NewChildAction = MakeShareable(new FEdGraphSchemaAction_K2Graph(EEdGraphSchemaAction_K2Graph::Subgraph, Category, MoveTemp(ChildDesc), MoveTemp(ChildTooltip), 1, SectionId));
|
|
NewChildAction->FuncName = DisplayName;
|
|
NewChildAction->EdGraph = Graph;
|
|
SortList.AddAction(NewChildAction);
|
|
|
|
GetChildGraphs(Graph, SectionId, SortList, Category);
|
|
GetChildEvents(Graph, SectionId, SortList, Category);
|
|
}
|
|
}
|
|
|
|
void SMyBlueprint::GetChildEvents(UEdGraph const* InEdGraph, int32 const SectionId, FGraphActionSort& SortList, const FText& ParentCategory, bool bInAddChildGraphs) const
|
|
{
|
|
if (!ensure(InEdGraph != NULL))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// ask the schema first to provide the child events
|
|
if (UEdGraphSchema const* Schema = InEdGraph->GetSchema())
|
|
{
|
|
TArray<TSharedPtr<FEdGraphSchemaAction>> ActionsFromSchema;
|
|
if(Schema->TryToGetChildEvents(InEdGraph, SectionId, ActionsFromSchema, ParentCategory))
|
|
{
|
|
for(TSharedPtr<FEdGraphSchemaAction> ActionFromSchema : ActionsFromSchema)
|
|
{
|
|
SortList.AddAction(ActionFromSchema);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
// grab the parent graph's name
|
|
FGraphDisplayInfo EdGraphDisplayInfo;
|
|
if (UEdGraphSchema const* Schema = InEdGraph->GetSchema())
|
|
{
|
|
Schema->GetGraphDisplayInformation(*InEdGraph, EdGraphDisplayInfo);
|
|
}
|
|
FText EdGraphDisplayName = EdGraphDisplayInfo.DisplayName;
|
|
FText ActionCategory;
|
|
if (!ParentCategory.IsEmpty())
|
|
{
|
|
ActionCategory = FText::Format(FText::FromString(TEXT("{0}|{1}")), ParentCategory, EdGraphDisplayName);
|
|
}
|
|
else
|
|
{
|
|
ActionCategory = MoveTemp(EdGraphDisplayName);
|
|
}
|
|
|
|
for (UEdGraphNode* GraphNode : InEdGraph->Nodes)
|
|
{
|
|
if (GraphNode)
|
|
{
|
|
TSharedPtr<FEdGraphSchemaAction> EventNodeAction;
|
|
if(GraphNode->GetClass()->ImplementsInterface(UK2Node_EventNodeInterface::StaticClass()))
|
|
{
|
|
EventNodeAction = CastChecked<IK2Node_EventNodeInterface>(GraphNode)->GetEventNodeAction(ActionCategory);
|
|
EventNodeAction->SectionID = SectionId;
|
|
SortList.AddAction(EventNodeAction);
|
|
}
|
|
|
|
if(bInAddChildGraphs && GraphNode->GetClass()->ImplementsInterface(UK2Node_ExternalGraphInterface::StaticClass()))
|
|
{
|
|
TArray<UEdGraph*> ExternalGraphs = CastChecked<IK2Node_ExternalGraphInterface>(GraphNode)->GetExternalGraphs();
|
|
for(UEdGraph* ExternalGraph : ExternalGraphs)
|
|
{
|
|
FText ExternalGraphCategory;
|
|
if(EventNodeAction.IsValid())
|
|
{
|
|
ExternalGraphCategory = FText::Format(FText::FromString(TEXT("{0}|{1}")), ActionCategory, EventNodeAction->GetMenuDescription());
|
|
}
|
|
else
|
|
{
|
|
ExternalGraphCategory = ActionCategory;
|
|
}
|
|
|
|
// Dont add child graphs, to avoid circular references creating infinite recursion
|
|
const bool bAddChildGraphs = false;
|
|
AddEventForFunctionGraph(ExternalGraph, SectionId, SortList, ExternalGraphCategory, bAddChildGraphs);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SMyBlueprint::GetLocalVariables(FGraphActionSort& SortList) const
|
|
{
|
|
// We want to pull local variables from the top level function graphs
|
|
UEdGraph* TopLevelGraph = FBlueprintEditorUtils::GetTopLevelGraph(GetFocusedGraph());
|
|
if( TopLevelGraph )
|
|
{
|
|
TArray<FBPVariableDescription> LocalVariables;
|
|
bool bSchemaImplementsGetLocalVariables = false;
|
|
|
|
// grab the parent graph's name
|
|
FGraphDisplayInfo EdGraphDisplayInfo;
|
|
if (UEdGraphSchema const* Schema = TopLevelGraph->GetSchema())
|
|
{
|
|
Schema->GetGraphDisplayInformation(*TopLevelGraph, EdGraphDisplayInfo);
|
|
|
|
// Try to get the local variables from the schema
|
|
bSchemaImplementsGetLocalVariables = Schema->GetLocalVariables(GetFocusedGraph(), LocalVariables);
|
|
for (const FBPVariableDescription& Variable : LocalVariables)
|
|
{
|
|
FText Category = Variable.Category;
|
|
if (Variable.Category.EqualTo(UEdGraphSchema_K2::VR_DefaultCategory))
|
|
{
|
|
Category = FText::GetEmpty();
|
|
}
|
|
|
|
TSharedPtr<FEdGraphSchemaAction> Action = Schema->MakeActionFromVariableDescription(GetFocusedGraph(), Variable);
|
|
if (Action.IsValid())
|
|
{
|
|
SortList.AddAction(Action);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the schema did not return any local variables, try to get them from the function entry
|
|
if (!bSchemaImplementsGetLocalVariables)
|
|
{
|
|
TArray<UK2Node_FunctionEntry*> FunctionEntryNodes;
|
|
TopLevelGraph->GetNodesOfClass<UK2Node_FunctionEntry>(FunctionEntryNodes);
|
|
|
|
// Search in all FunctionEntry nodes for their local variables
|
|
FText ActionCategory;
|
|
for (UK2Node_FunctionEntry* const FunctionEntry : FunctionEntryNodes)
|
|
{
|
|
for (const FBPVariableDescription& Variable : FunctionEntry->LocalVariables)
|
|
{
|
|
FText Category = Variable.Category;
|
|
if (Variable.Category.EqualTo(UEdGraphSchema_K2::VR_DefaultCategory))
|
|
{
|
|
Category = FText::GetEmpty();
|
|
}
|
|
|
|
UFunction* Func = FindUField<UFunction>(GetBlueprintObj()->SkeletonGeneratedClass, TopLevelGraph->GetFName());
|
|
if (Func)
|
|
{
|
|
TSharedPtr<FEdGraphSchemaAction_K2LocalVar> NewVarAction = MakeShareable(new FEdGraphSchemaAction_K2LocalVar(Category, FText::FromName(Variable.VarName), FText::GetEmpty(), 0, NodeSectionID::LOCAL_VARIABLE));
|
|
NewVarAction->SetVariableInfo(Variable.VarName, Func, Variable.VarType.PinCategory == UEdGraphSchema_K2::PC_Boolean);
|
|
SortList.AddAction(NewVarAction);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
EVisibility SMyBlueprint::GetLocalActionsListVisibility() const
|
|
{
|
|
if( !BlueprintEditorPtr.IsValid())
|
|
{
|
|
return EVisibility::Visible;
|
|
}
|
|
|
|
if( BlueprintEditorPtr.IsValid() && BlueprintEditorPtr.Pin()->NewDocument_IsVisibleForType(FBlueprintEditor::CGT_NewLocalVariable))
|
|
{
|
|
return EVisibility::Visible;
|
|
}
|
|
return EVisibility::Collapsed;
|
|
}
|
|
|
|
void SMyBlueprint::AddEventForFunctionGraph(UEdGraph* InEdGraph, int32 const SectionId, FGraphActionSort& SortList, const FText& ParentCategory, bool bAddChildGraphs) const
|
|
{
|
|
UBlueprint* BlueprintObj = GetBlueprintObj();
|
|
check(BlueprintObj);
|
|
|
|
FGraphDisplayInfo DisplayInfo;
|
|
InEdGraph->GetSchema()->GetGraphDisplayInformation(*InEdGraph, DisplayInfo);
|
|
|
|
FText FunctionCategory = InEdGraph->GetSchema()->GetGraphCategory(InEdGraph);
|
|
if (FunctionCategory.IsEmpty() && BlueprintObj->SkeletonGeneratedClass != nullptr)
|
|
{
|
|
UFunction* Function = BlueprintObj->SkeletonGeneratedClass->FindFunctionByName(InEdGraph->GetFName());
|
|
if (Function != nullptr)
|
|
{
|
|
FunctionCategory = FObjectEditorUtils::GetCategoryText(Function);
|
|
}
|
|
}
|
|
|
|
// Default, so place in 'non' category
|
|
if (FunctionCategory.EqualTo(FText::FromString(BlueprintObj->GetName())) || FunctionCategory.EqualTo(UEdGraphSchema_K2::VR_DefaultCategory))
|
|
{
|
|
FunctionCategory = FText::GetEmpty();
|
|
}
|
|
|
|
FText ActionCategory;
|
|
if (!ParentCategory.IsEmpty())
|
|
{
|
|
ActionCategory = FText::Format(FText::FromString(TEXT("{0}|{1}")), ParentCategory, FunctionCategory);
|
|
}
|
|
else
|
|
{
|
|
ActionCategory = MoveTemp(FunctionCategory);
|
|
}
|
|
|
|
//@TODO: Should be a bit more generic (or the AnimGraph shouldn't be stored as a FunctionGraph...)
|
|
const bool bIsConstructionScript = InEdGraph->GetFName() == UEdGraphSchema_K2::FN_UserConstructionScript;
|
|
|
|
TSharedPtr<FEdGraphSchemaAction_K2Graph> NewFuncAction = MakeShareable(new FEdGraphSchemaAction_K2Graph(EEdGraphSchemaAction_K2Graph::Function, ActionCategory, DisplayInfo.DisplayName, DisplayInfo.Tooltip, bIsConstructionScript ? 2 : 1, SectionId));
|
|
NewFuncAction->FuncName = InEdGraph->GetFName();
|
|
NewFuncAction->EdGraph = InEdGraph;
|
|
|
|
const FString UserCategoryName = FEditorCategoryUtils::GetCategoryDisplayString(ActionCategory.ToString());
|
|
SortList.AddAction(UserCategoryName, NewFuncAction);
|
|
|
|
if(bAddChildGraphs)
|
|
{
|
|
GetChildGraphs(InEdGraph, NewFuncAction->GetSectionID(), SortList, ActionCategory);
|
|
}
|
|
GetChildEvents(InEdGraph, NewFuncAction->GetSectionID(), SortList, ActionCategory, bAddChildGraphs);
|
|
}
|
|
|
|
void SMyBlueprint::CollectAllActions(FGraphActionListBuilderBase& OutAllActions)
|
|
{
|
|
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
|
|
|
|
UBlueprint* BlueprintObj = GetBlueprintObj();
|
|
check(BlueprintObj);
|
|
|
|
TSharedPtr<FBlueprintEditor> BlueprintEditor = BlueprintEditorPtr.Pin();
|
|
|
|
EFieldIteratorFlags::SuperClassFlags FieldIteratorSuperFlag = EFieldIteratorFlags::IncludeSuper;
|
|
if ( ShowUserVarsOnly() )
|
|
{
|
|
FieldIteratorSuperFlag = EFieldIteratorFlags::ExcludeSuper;
|
|
}
|
|
|
|
bool bShowReplicatedOnly = IsShowingReplicatedVariablesOnly();
|
|
|
|
// Initialise action sorting instance
|
|
FGraphActionSort SortList( BlueprintObj->CategorySorting );
|
|
// List of names of functions we implement
|
|
ImplementedFunctionCache.Empty();
|
|
|
|
// Fill with functions names we've already collected for rename, to ensure we do not add the same function multiple times.
|
|
TArray<FName> OverridableFunctionNames;
|
|
|
|
// Grab Variables
|
|
for (TFieldIterator<FProperty> PropertyIt(BlueprintObj->SkeletonGeneratedClass, FieldIteratorSuperFlag); PropertyIt; ++PropertyIt)
|
|
{
|
|
FProperty* Property = *PropertyIt;
|
|
FName PropName = Property->GetFName();
|
|
|
|
// If we're showing only replicated, ignore the rest
|
|
if (bShowReplicatedOnly && (!Property->HasAnyPropertyFlags(CPF_Net | CPF_RepNotify) || Property->HasAnyPropertyFlags(CPF_RepSkip)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Don't show delegate properties, there is special handling for these
|
|
const bool bMulticastDelegateProp = Property->IsA(FMulticastDelegateProperty::StaticClass());
|
|
const bool bDelegateProp = (Property->IsA(FDelegateProperty::StaticClass()) || bMulticastDelegateProp);
|
|
const bool bShouldShowAsVar = (!Property->HasAnyPropertyFlags(CPF_Parm) && Property->HasAllPropertyFlags(CPF_BlueprintVisible)) && !bDelegateProp;
|
|
const bool bShouldShowAsDelegate = !Property->HasAnyPropertyFlags(CPF_Parm) && bMulticastDelegateProp
|
|
&& Property->HasAnyPropertyFlags(CPF_BlueprintAssignable | CPF_BlueprintCallable);
|
|
FObjectPropertyBase* Obj = CastField<FObjectPropertyBase>(Property);
|
|
if(!bShouldShowAsVar && !bShouldShowAsDelegate)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const FText PropertyTooltip = Property->GetToolTipText();
|
|
const FName PropertyName = Property->GetFName();
|
|
const FText PropertyDesc = Property->IsNative() ? Property->GetDisplayNameText() : FText::FromName(PropertyName);
|
|
|
|
FText CategoryName = FObjectEditorUtils::GetCategoryText(Property);
|
|
FText PropertyCategory = FObjectEditorUtils::GetCategoryText(Property);
|
|
const FString UserCategoryName = FEditorCategoryUtils::GetCategoryDisplayString( PropertyCategory.ToString() );
|
|
|
|
if (CategoryName.EqualTo(FText::FromString(BlueprintObj->GetName())) || CategoryName.EqualTo(UEdGraphSchema_K2::VR_DefaultCategory))
|
|
{
|
|
CategoryName = FText::GetEmpty(); // default, so place in 'non' category
|
|
PropertyCategory = FText::GetEmpty();
|
|
}
|
|
|
|
if (bShouldShowAsVar)
|
|
{
|
|
const bool bComponentProperty = Obj && Obj->PropertyClass ? Obj->PropertyClass->IsChildOf<UActorComponent>() : false;
|
|
|
|
// By default components go into the variable section under the component category unless a custom category is specified.
|
|
if ( bComponentProperty && CategoryName.IsEmpty() )
|
|
{
|
|
PropertyCategory = LOCTEXT("Components", "Components");
|
|
}
|
|
|
|
TSharedPtr<FEdGraphSchemaAction_K2Var> NewVarAction = MakeShareable(new FEdGraphSchemaAction_K2Var(PropertyCategory, PropertyDesc, PropertyTooltip, 0, NodeSectionID::VARIABLE));
|
|
const FArrayProperty* ArrayProperty = CastField<const FArrayProperty>(Property);
|
|
const FProperty* TestProperty = ArrayProperty ? ArrayProperty->Inner : Property;
|
|
NewVarAction->SetVariableInfo(PropertyName, BlueprintObj->SkeletonGeneratedClass, CastField<FBoolProperty>(TestProperty) != nullptr);
|
|
SortList.AddAction( UserCategoryName, NewVarAction );
|
|
}
|
|
else if (bShouldShowAsDelegate && BlueprintEditor.IsValid() && BlueprintEditor->AreDelegatesAllowed())
|
|
{
|
|
TSharedPtr<FEdGraphSchemaAction_K2Delegate> NewDelegateAction;
|
|
// Delegate is visible in MyBlueprint when not-native or its category name is not empty.
|
|
if (Property->HasAllPropertyFlags(CPF_Edit) || !PropertyCategory.IsEmpty())
|
|
{
|
|
NewDelegateAction = MakeShareable(new FEdGraphSchemaAction_K2Delegate(PropertyCategory, PropertyDesc, PropertyTooltip, 0, NodeSectionID::DELEGATE));
|
|
NewDelegateAction->SetVariableInfo(PropertyName, BlueprintObj->SkeletonGeneratedClass, false);
|
|
SortList.AddAction( UserCategoryName, NewDelegateAction );
|
|
}
|
|
|
|
UClass* OwnerClass = Property->GetOwnerChecked<UClass>();
|
|
UEdGraph* Graph = FBlueprintEditorUtils::GetDelegateSignatureGraphByName(BlueprintObj, PropertyName);
|
|
if (Graph && OwnerClass && (BlueprintObj == OwnerClass->ClassGeneratedBy))
|
|
{
|
|
if (NewDelegateAction.IsValid())
|
|
{
|
|
NewDelegateAction->EdGraph = Graph;
|
|
}
|
|
ImplementedFunctionCache.Add(PropertyName);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Grab what events are implemented in the event graphs so they don't show up in the menu if they are already implemented
|
|
for (UEdGraph* const Graph : BlueprintObj->EventGraphs)
|
|
{
|
|
if (Graph && !Graph->IsUnreachable())
|
|
{
|
|
FName GraphName = Graph->GetFName();
|
|
ImplementedFunctionCache.Add(GraphName);
|
|
OverridableFunctionNames.Add(GraphName);
|
|
}
|
|
}
|
|
|
|
// Grab functions implemented by the blueprint
|
|
for (UEdGraph* Graph : BlueprintObj->FunctionGraphs)
|
|
{
|
|
check(Graph);
|
|
|
|
int32 SectionID = NodeSectionID::FUNCTION;
|
|
|
|
if(Graph->IsA<UAnimationGraph>())
|
|
{
|
|
const bool bIsDefaultAnimGraph = Graph->GetFName() == UEdGraphSchema_K2::GN_AnimGraph;
|
|
if(bIsDefaultAnimGraph)
|
|
{
|
|
SectionID = NodeSectionID::ANIMGRAPH;
|
|
}
|
|
else
|
|
{
|
|
SectionID = NodeSectionID::ANIMLAYER;
|
|
}
|
|
}
|
|
|
|
AddEventForFunctionGraph(Graph, SectionID, SortList, FText::GetEmpty(), true);
|
|
|
|
ImplementedFunctionCache.Add(Graph->GetFName());
|
|
}
|
|
|
|
if(BlueprintEditor.IsValid() && BlueprintEditor->AreMacrosAllowed())
|
|
{
|
|
// Grab macros implemented by the blueprint
|
|
for (int32 i = 0; i < BlueprintObj->MacroGraphs.Num(); i++)
|
|
{
|
|
UEdGraph* Graph = BlueprintObj->MacroGraphs[i];
|
|
check(Graph);
|
|
|
|
const FName MacroName = Graph->GetFName();
|
|
|
|
FGraphDisplayInfo DisplayInfo;
|
|
Graph->GetSchema()->GetGraphDisplayInformation(*Graph, DisplayInfo);
|
|
|
|
FText MacroCategory = GetGraphCategory(Graph);
|
|
|
|
TSharedPtr<FEdGraphSchemaAction_K2Graph> NewMacroAction = MakeShareable(new FEdGraphSchemaAction_K2Graph(EEdGraphSchemaAction_K2Graph::Macro, MacroCategory, DisplayInfo.PlainName, DisplayInfo.Tooltip, 1, NodeSectionID::MACRO));
|
|
NewMacroAction->FuncName = MacroName;
|
|
NewMacroAction->EdGraph = Graph;
|
|
|
|
const FString UserCategoryName = FEditorCategoryUtils::GetCategoryDisplayString(MacroCategory.ToString());
|
|
SortList.AddAction(UserCategoryName, NewMacroAction);
|
|
|
|
GetChildGraphs(Graph, NewMacroAction->GetSectionID(), SortList, MacroCategory);
|
|
GetChildEvents(Graph, NewMacroAction->GetSectionID(), SortList, MacroCategory);
|
|
|
|
ImplementedFunctionCache.Add(MacroName);
|
|
}
|
|
}
|
|
|
|
OverridableFunctionActions.Reset();
|
|
|
|
// Cache potentially overridable functions
|
|
UClass* ParentClass = BlueprintObj->SkeletonGeneratedClass ? BlueprintObj->SkeletonGeneratedClass->GetSuperClass() : *BlueprintObj->ParentClass;
|
|
for ( TFieldIterator<UFunction> FunctionIt(ParentClass, EFieldIteratorFlags::IncludeSuper); FunctionIt; ++FunctionIt )
|
|
{
|
|
const UFunction* Function = *FunctionIt;
|
|
const FName FunctionName = Function->GetFName();
|
|
|
|
UClass *OuterClass = CastChecked<UClass>(Function->GetOuter());
|
|
// ignore skeleton classes and convert them into their "authoritative" types so they
|
|
// can be found in the graph
|
|
if(UBlueprintGeneratedClass *GeneratedOuterClass = Cast<UBlueprintGeneratedClass>(OuterClass))
|
|
{
|
|
OuterClass = GeneratedOuterClass->GetAuthoritativeClass();
|
|
}
|
|
|
|
if ( UEdGraphSchema_K2::CanKismetOverrideFunction(Function)
|
|
&& !OverridableFunctionNames.Contains(FunctionName)
|
|
&& !ImplementedFunctionCache.Contains(FunctionName)
|
|
&& !FObjectEditorUtils::IsFunctionHiddenFromClass(Function, ParentClass)
|
|
&& !FBlueprintEditorUtils::FindOverrideForFunction(BlueprintObj, OuterClass, Function->GetFName())
|
|
&& Blueprint->AllowFunctionOverride(Function)
|
|
)
|
|
{
|
|
FText FunctionTooltip = FText::FromString(ObjectTools::GetDefaultTooltipForFunction(Function));
|
|
FText FunctionDesc = K2Schema->GetFriendlySignatureName(Function);
|
|
if ( FunctionDesc.IsEmpty() )
|
|
{
|
|
FunctionDesc = FText::FromString(Function->GetName());
|
|
}
|
|
|
|
if (Function->HasMetaData(FBlueprintMetadata::MD_DeprecatedFunction))
|
|
{
|
|
FunctionDesc = FBlueprintEditorUtils::GetDeprecatedMemberMenuItemName(FunctionDesc);
|
|
}
|
|
|
|
FText FunctionCategory = FObjectEditorUtils::GetCategoryText(Function);
|
|
|
|
TSharedPtr<FEdGraphSchemaAction_K2Graph> NewFuncAction = MakeShareable(new FEdGraphSchemaAction_K2Graph(EEdGraphSchemaAction_K2Graph::Function, FunctionCategory, FunctionDesc, FunctionTooltip, 1, NodeSectionID::FUNCTION_OVERRIDABLE));
|
|
NewFuncAction->FuncName = FunctionName;
|
|
|
|
OverridableFunctionActions.Add(NewFuncAction);
|
|
OverridableFunctionNames.Add(FunctionName);
|
|
}
|
|
}
|
|
|
|
auto IsInAnimBPLambda = [&BlueprintObj](const FName FunctionName, FText& FunctionCategory) -> bool
|
|
{
|
|
if (BlueprintObj->SkeletonGeneratedClass != nullptr)
|
|
{
|
|
if (UFunction * Function = BlueprintObj->SkeletonGeneratedClass->FindFunctionByName(FunctionName))
|
|
{
|
|
FunctionCategory = FObjectEditorUtils::GetCategoryText(Function);
|
|
|
|
if (IAnimClassInterface * AnimClassInterface = IAnimClassInterface::GetFromClass(BlueprintObj->SkeletonGeneratedClass))
|
|
{
|
|
if (IAnimClassInterface::IsAnimBlueprintFunction(AnimClassInterface, Function))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
// Also functions implemented from interfaces
|
|
for (int32 i=0; i < BlueprintObj->ImplementedInterfaces.Num(); i++)
|
|
{
|
|
FBPInterfaceDescription& InterfaceDesc = BlueprintObj->ImplementedInterfaces[i];
|
|
UClass* InterfaceClass = InterfaceDesc.Interface.Get();
|
|
if (InterfaceClass)
|
|
{
|
|
for (TFieldIterator<UFunction> FunctionIt(InterfaceClass, EFieldIteratorFlags::IncludeSuper); FunctionIt; ++FunctionIt)
|
|
{
|
|
const UFunction* Function = *FunctionIt;
|
|
const FName FunctionName = Function->GetFName();
|
|
|
|
if (FunctionName != UEdGraphSchema_K2::FN_ExecuteUbergraphBase)
|
|
{
|
|
FText FunctionTooltip = Function->GetToolTipText();
|
|
FText FunctionDesc = K2Schema->GetFriendlySignatureName(Function);
|
|
|
|
FText FunctionCategory;
|
|
bool bIsAnimFunction = IsInAnimBPLambda(FunctionName, FunctionCategory);
|
|
|
|
TSharedPtr<FEdGraphSchemaAction_K2Graph> NewFuncAction = MakeShareable(new FEdGraphSchemaAction_K2Graph(EEdGraphSchemaAction_K2Graph::Interface, FunctionCategory, FunctionDesc, FunctionTooltip, 1, bIsAnimFunction ? NodeSectionID::ANIMLAYER : NodeSectionID::INTERFACE));
|
|
|
|
NewFuncAction->FuncName = FunctionName;
|
|
OutAllActions.AddAction(NewFuncAction);
|
|
|
|
// Find the graph that this function is on so the user can double click and open it from the interfaces menu
|
|
for (UEdGraph* const Graph : InterfaceDesc.Graphs)
|
|
{
|
|
if (Graph && Graph->GetFName() == FunctionName)
|
|
{
|
|
NewFuncAction->EdGraph = Graph;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if this function is not in the interfaces menu, then allow it to be put in the override function menu
|
|
if (GetAlwaysShowInterfacesInOverrides())
|
|
{
|
|
OverridableFunctionActions.Add(NewFuncAction);
|
|
OverridableFunctionNames.Add(FunctionName);
|
|
}
|
|
|
|
if(bIsAnimFunction && NewFuncAction->EdGraph)
|
|
{
|
|
GetChildGraphs(NewFuncAction->EdGraph, NewFuncAction->GetSectionID(), SortList, FunctionCategory);
|
|
GetChildEvents(NewFuncAction->EdGraph, NewFuncAction->GetSectionID(), SortList, FunctionCategory);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// also walk up the class chain to look for overridable functions in natively implemented interfaces
|
|
for (UClass* TempClass = BlueprintObj->ParentClass; TempClass; TempClass = TempClass->GetSuperClass())
|
|
{
|
|
for (int32 Idx = 0; Idx < TempClass->Interfaces.Num(); ++Idx)
|
|
{
|
|
FImplementedInterface const& I = TempClass->Interfaces[Idx];
|
|
|
|
// same as above, make a function?
|
|
for (TFieldIterator<UFunction> FunctionIt(I.Class, EFieldIteratorFlags::IncludeSuper); FunctionIt; ++FunctionIt)
|
|
{
|
|
const UFunction* Function = *FunctionIt;
|
|
const FName FunctionName = Function->GetFName();
|
|
|
|
if (UEdGraphSchema_K2::CanKismetOverrideFunction(Function) && !ImplementedFunctionCache.Contains(FunctionName))
|
|
{
|
|
FText FunctionTooltip = Function->GetToolTipText();
|
|
FText FunctionDesc = K2Schema->GetFriendlySignatureName(Function);
|
|
|
|
FText FunctionCategory = FObjectEditorUtils::GetCategoryText(Function);
|
|
bool bIsAnimFunction = IsInAnimBPLambda(FunctionName, FunctionCategory);
|
|
|
|
TSharedPtr<FEdGraphSchemaAction_K2Graph> NewFuncAction = MakeShareable(new FEdGraphSchemaAction_K2Graph(EEdGraphSchemaAction_K2Graph::Interface, FunctionCategory, FunctionDesc, FunctionTooltip, 1, bIsAnimFunction ? NodeSectionID::ANIMLAYER : NodeSectionID::INTERFACE));
|
|
NewFuncAction->FuncName = FunctionName;
|
|
|
|
if (!OverridableFunctionNames.Contains(FunctionName))
|
|
{
|
|
OverridableFunctionActions.Add(NewFuncAction);
|
|
OverridableFunctionNames.Add(FunctionName);
|
|
}
|
|
|
|
OutAllActions.AddAction(NewFuncAction);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if(BlueprintEditor.IsValid() && BlueprintEditor->AreEventGraphsAllowed())
|
|
{
|
|
// Grab ubergraph pages
|
|
for (int32 i = 0; i < BlueprintObj->UbergraphPages.Num(); i++)
|
|
{
|
|
UEdGraph* Graph = BlueprintObj->UbergraphPages[i];
|
|
check(Graph);
|
|
|
|
FGraphDisplayInfo DisplayInfo;
|
|
Graph->GetSchema()->GetGraphDisplayInformation(*Graph, DisplayInfo);
|
|
|
|
TSharedPtr<FEdGraphSchemaAction_K2Graph> NeUbergraphAction = MakeShareable(new FEdGraphSchemaAction_K2Graph(EEdGraphSchemaAction_K2Graph::Graph, FText::GetEmpty(), DisplayInfo.PlainName, DisplayInfo.Tooltip, 2, NodeSectionID::GRAPH));
|
|
NeUbergraphAction->FuncName = Graph->GetFName();
|
|
NeUbergraphAction->EdGraph = Graph;
|
|
OutAllActions.AddAction(NeUbergraphAction);
|
|
|
|
GetChildGraphs(Graph, NeUbergraphAction->GetSectionID(), SortList);
|
|
GetChildEvents(Graph, NeUbergraphAction->GetSectionID(), SortList);
|
|
}
|
|
}
|
|
|
|
// Grab intermediate pages
|
|
for (int32 i = 0; i < BlueprintObj->IntermediateGeneratedGraphs.Num(); i++)
|
|
{
|
|
UEdGraph* Graph = BlueprintObj->IntermediateGeneratedGraphs[i];
|
|
check(Graph);
|
|
|
|
const FName IntermediateName(*(FString(TEXT("$INTERMEDIATE$_")) + Graph->GetName()));
|
|
FString IntermediateTooltip = IntermediateName.ToString();
|
|
FString IntermediateDesc = IntermediateName.ToString();
|
|
TSharedPtr<FEdGraphSchemaAction_K2Graph> NewIntermediateAction = MakeShareable(new FEdGraphSchemaAction_K2Graph(EEdGraphSchemaAction_K2Graph::Graph, FText::GetEmpty(), FText::FromString(IntermediateDesc), FText::FromString(IntermediateTooltip), 1));
|
|
NewIntermediateAction->FuncName = IntermediateName;
|
|
NewIntermediateAction->EdGraph = Graph;
|
|
OutAllActions.AddAction(NewIntermediateAction);
|
|
|
|
GetChildGraphs(Graph, NewIntermediateAction->GetSectionID(), SortList);
|
|
GetChildEvents(Graph, NewIntermediateAction->GetSectionID(), SortList);
|
|
}
|
|
|
|
if (GetLocalActionsListVisibility().IsVisible())
|
|
{
|
|
GetLocalVariables(SortList);
|
|
}
|
|
|
|
// Add all the sorted variables, components, functions, etc...
|
|
SortList.CleanupCategories();
|
|
SortList.GetAllActions(OutAllActions);
|
|
}
|
|
|
|
void SMyBlueprint::CollectStaticSections(TArray<int32>& StaticSectionIDs)
|
|
{
|
|
TSharedPtr<FBlueprintEditor> BlueprintEditor = BlueprintEditorPtr.Pin();
|
|
const bool bIsEditor = BlueprintEditor.IsValid();
|
|
|
|
if ( IsShowingEmptySections() )
|
|
{
|
|
if (!bIsEditor || (BlueprintEditor->NewDocument_IsVisibleForType(FBlueprintEditor::CGT_NewEventGraph) && BlueprintEditor->IsSectionVisible(NodeSectionID::GRAPH)))
|
|
{
|
|
StaticSectionIDs.Add(NodeSectionID::GRAPH);
|
|
}
|
|
if (!bIsEditor || (BlueprintEditor->NewDocument_IsVisibleForType(FBlueprintEditor::CGT_NewAnimationLayer) && BlueprintEditor->IsSectionVisible(NodeSectionID::ANIMLAYER)))
|
|
{
|
|
StaticSectionIDs.Add(NodeSectionID::ANIMLAYER);
|
|
}
|
|
if (!bIsEditor || (BlueprintEditor->NewDocument_IsVisibleForType(FBlueprintEditor::CGT_NewMacroGraph) && BlueprintEditor->IsSectionVisible(NodeSectionID::MACRO)))
|
|
{
|
|
StaticSectionIDs.Add(NodeSectionID::MACRO);
|
|
}
|
|
if (!bIsEditor || (BlueprintEditor->NewDocument_IsVisibleForType(FBlueprintEditor::CGT_NewFunctionGraph) && BlueprintEditor->IsSectionVisible(NodeSectionID::FUNCTION)))
|
|
{
|
|
StaticSectionIDs.Add(NodeSectionID::FUNCTION);
|
|
}
|
|
if (!bIsEditor || (BlueprintEditor->NewDocument_IsVisibleForType(FBlueprintEditor::CGT_NewVariable) && BlueprintEditor->IsSectionVisible(NodeSectionID::VARIABLE)))
|
|
{
|
|
StaticSectionIDs.Add(NodeSectionID::VARIABLE);
|
|
}
|
|
if (!bIsEditor || (BlueprintEditor->FBlueprintEditor::AddNewDelegateIsVisible() && BlueprintEditor->IsSectionVisible(NodeSectionID::DELEGATE)))
|
|
{
|
|
StaticSectionIDs.Add(NodeSectionID::DELEGATE);
|
|
}
|
|
}
|
|
|
|
if ( GetLocalActionsListVisibility().IsVisible() && (!bIsEditor || BlueprintEditor->IsSectionVisible(NodeSectionID::LOCAL_VARIABLE)))
|
|
{
|
|
StaticSectionIDs.Add(NodeSectionID::LOCAL_VARIABLE);
|
|
}
|
|
}
|
|
|
|
bool SMyBlueprint::IsShowingInheritedVariables() const
|
|
{
|
|
return GetMutableDefault<UBlueprintEditorSettings>()->bShowInheritedVariables;
|
|
}
|
|
|
|
void SMyBlueprint::OnToggleShowInheritedVariables()
|
|
{
|
|
UBlueprintEditorSettings* Settings = GetMutableDefault<UBlueprintEditorSettings>();
|
|
Settings->bShowInheritedVariables = !Settings->bShowInheritedVariables;
|
|
Settings->PostEditChange();
|
|
Settings->SaveConfig();
|
|
|
|
Refresh();
|
|
}
|
|
|
|
void SMyBlueprint::OnToggleShowEmptySections()
|
|
{
|
|
UBlueprintEditorSettings* Settings = GetMutableDefault<UBlueprintEditorSettings>();
|
|
Settings->bShowEmptySections = !Settings->bShowEmptySections;
|
|
Settings->PostEditChange();
|
|
Settings->SaveConfig();
|
|
|
|
Refresh();
|
|
}
|
|
|
|
bool SMyBlueprint::IsShowingEmptySections() const
|
|
{
|
|
return GetMutableDefault<UBlueprintEditorSettings>()->bShowEmptySections;
|
|
}
|
|
|
|
void SMyBlueprint::OnToggleShowReplicatedVariablesOnly()
|
|
{
|
|
bShowReplicatedVariablesOnly = !bShowReplicatedVariablesOnly;
|
|
Refresh();
|
|
}
|
|
|
|
void SMyBlueprint::OnToggleAlwaysShowInterfacesInOverrides()
|
|
{
|
|
UBlueprintEditorSettings* Settings = GetMutableDefault<UBlueprintEditorSettings>();
|
|
Settings->bAlwaysShowInterfacesInOverrides = !Settings->bAlwaysShowInterfacesInOverrides;
|
|
Settings->PostEditChange();
|
|
Settings->SaveConfig();
|
|
Refresh();
|
|
}
|
|
|
|
bool SMyBlueprint::GetAlwaysShowInterfacesInOverrides() const
|
|
{
|
|
return GetMutableDefault<UBlueprintEditorSettings>()->bAlwaysShowInterfacesInOverrides;
|
|
}
|
|
|
|
void SMyBlueprint::OnToggleShowParentClassInOverrides()
|
|
{
|
|
UBlueprintEditorSettings* Settings = GetMutableDefault<UBlueprintEditorSettings>();
|
|
Settings->bShowParentClassInOverrides = !Settings->bShowParentClassInOverrides;
|
|
Settings->PostEditChange();
|
|
Settings->SaveConfig();
|
|
Refresh();
|
|
}
|
|
|
|
bool SMyBlueprint::GetShowParentClassInOverrides() const
|
|
{
|
|
return GetMutableDefault<UBlueprintEditorSettings>()->bShowParentClassInOverrides;
|
|
}
|
|
|
|
void SMyBlueprint::OnToggleShowAccessSpecifier()
|
|
{
|
|
UBlueprintEditorSettings* Settings = GetMutableDefault<UBlueprintEditorSettings>();
|
|
Settings->bShowAccessSpecifier = !Settings->bShowAccessSpecifier;
|
|
Settings->PostEditChange();
|
|
Settings->SaveConfig();
|
|
Refresh();
|
|
}
|
|
|
|
bool SMyBlueprint::GetShowAccessSpecifier() const
|
|
{
|
|
return GetMutableDefault<UBlueprintEditorSettings>()->bShowAccessSpecifier;
|
|
}
|
|
|
|
bool SMyBlueprint::IsShowingReplicatedVariablesOnly() const
|
|
{
|
|
return bShowReplicatedVariablesOnly;
|
|
}
|
|
|
|
FReply SMyBlueprint::OnActionDragged( const TArray< TSharedPtr<FEdGraphSchemaAction> >& InActions, const FPointerEvent& MouseEvent )
|
|
{
|
|
if (!BlueprintEditorPtr.IsValid())
|
|
{
|
|
return FReply::Unhandled();
|
|
}
|
|
|
|
TSharedPtr<FEdGraphSchemaAction> InAction( InActions.Num() > 0 ? InActions[0] : nullptr );
|
|
if(InAction.IsValid())
|
|
{
|
|
auto AnalyticsDelegate = FNodeCreationAnalytic::CreateSP( this, &SMyBlueprint::UpdateNodeCreation );
|
|
|
|
if(InAction->GetTypeId() == FEdGraphSchemaAction_K2Graph::StaticGetTypeId())
|
|
{
|
|
FEdGraphSchemaAction_K2Graph* FuncAction = (FEdGraphSchemaAction_K2Graph*)InAction.Get();
|
|
|
|
if (FuncAction->EdGraph)
|
|
{
|
|
if (FuncAction->EdGraph->GetSchema()->CanGraphBeDropped(InAction))
|
|
{
|
|
return FuncAction->EdGraph->GetSchema()->BeginGraphDragAction(InAction, FPointerEvent());
|
|
}
|
|
}
|
|
|
|
if (FuncAction->GraphType == EEdGraphSchemaAction_K2Graph::Function ||FuncAction->GraphType == EEdGraphSchemaAction_K2Graph::Interface)
|
|
{
|
|
// Callback function to report that the user cannot drop this function in the graph
|
|
auto CanDragDropAction = [](TSharedPtr<FEdGraphSchemaAction> /*DropAction*/, UEdGraph* HoveredGraphIn, FText& ImpededReasonOut, int32 FunctionFlags)->bool //bool bIsBlueprintCallableFunction, bool bIsPureFunction)->bool
|
|
{
|
|
if ((FunctionFlags & (FUNC_BlueprintCallable | FUNC_BlueprintPure)) == 0)
|
|
{
|
|
ImpededReasonOut = LOCTEXT("NonBlueprintCallable", "This function was not marked as Blueprint Callable and cannot be placed in a graph!");
|
|
return false;
|
|
}
|
|
|
|
if (const UEdGraphSchema_K2* K2Schema = Cast<UEdGraphSchema_K2>(HoveredGraphIn->GetSchema()))
|
|
{
|
|
if ((FunctionFlags & (FUNC_BlueprintPure)) == 0)
|
|
{
|
|
if (K2Schema->DoesGraphSupportImpureFunctions(HoveredGraphIn) == false)
|
|
{
|
|
ImpededReasonOut = LOCTEXT("GraphNoSupportImpureF", "The target graph does not support Impure Functions.");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// TODO : Check if this else has to block everything or just explicit schemas
|
|
if (const UAnimationStateMachineSchema* AnimationStateMachineSchema = Cast<UAnimationStateMachineSchema>(HoveredGraphIn->GetSchema()))
|
|
{
|
|
ImpededReasonOut = LOCTEXT("GraphNoSupportFunctions", "The target graph does not support Functions.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
int32 FunctionFlags = 0;
|
|
|
|
if (FuncAction->EdGraph)
|
|
{
|
|
for (UEdGraphNode* GraphNode : FuncAction->EdGraph->Nodes)
|
|
{
|
|
if (UK2Node_FunctionEntry* Node = Cast<UK2Node_FunctionEntry>(GraphNode))
|
|
{
|
|
FunctionFlags |= Node->GetFunctionFlags();
|
|
}
|
|
}
|
|
}
|
|
|
|
return FReply::Handled().BeginDragDrop(FKismetFunctionDragDropAction::New(
|
|
InAction,
|
|
FuncAction->FuncName,
|
|
GetBlueprintObj()->SkeletonGeneratedClass,
|
|
FMemberReference(),
|
|
AnalyticsDelegate,
|
|
FKismetDragDropAction::FCanBeDroppedDelegate::CreateLambda(CanDragDropAction, FunctionFlags)));
|
|
}
|
|
else if (FuncAction->GraphType == EEdGraphSchemaAction_K2Graph::Macro)
|
|
{
|
|
if ((FuncAction->EdGraph != NULL) && GetBlueprintObj()->BlueprintType != BPTYPE_MacroLibrary)
|
|
{
|
|
return FReply::Handled().BeginDragDrop(FKismetMacroDragDropAction::New(InAction, FuncAction->FuncName, GetBlueprintObj(), FuncAction->EdGraph, AnalyticsDelegate));
|
|
}
|
|
}
|
|
}
|
|
else if(InAction->GetTypeId() == FEdGraphSchemaAction_K2Delegate::StaticGetTypeId())
|
|
{
|
|
FEdGraphSchemaAction_K2Delegate* DelegateAction = (FEdGraphSchemaAction_K2Delegate*)InAction.Get();
|
|
check(DelegateAction->GetDelegateName() != NAME_None);
|
|
if (UClass* VarClass = DelegateAction->GetDelegateClass())
|
|
{
|
|
const bool bIsAltDown = MouseEvent.IsAltDown();
|
|
const bool bIsCtrlDown = MouseEvent.IsLeftControlDown() || MouseEvent.IsRightControlDown();
|
|
|
|
TSharedRef<FKismetVariableDragDropAction> DragOperation = FKismetDelegateDragDropAction::New(InAction, DelegateAction->GetDelegateName(), VarClass, AnalyticsDelegate);
|
|
DragOperation->SetAltDrag(bIsAltDown);
|
|
DragOperation->SetCtrlDrag(bIsCtrlDown);
|
|
return FReply::Handled().BeginDragDrop(DragOperation);
|
|
}
|
|
}
|
|
else if( InAction->GetTypeId() == FEdGraphSchemaAction_K2LocalVar::StaticGetTypeId())
|
|
{
|
|
FEdGraphSchemaAction_K2LocalVar* VarAction = (FEdGraphSchemaAction_K2LocalVar*)InAction.Get();
|
|
if (UStruct* VariableScope = Cast<UStruct>(VarAction->GetVariableScope()))
|
|
{
|
|
TSharedRef<FKismetVariableDragDropAction> DragOperation = FKismetVariableDragDropAction::New(InAction, VarAction->GetVariableName(), VariableScope, AnalyticsDelegate);
|
|
DragOperation->SetAltDrag(MouseEvent.IsAltDown());
|
|
DragOperation->SetCtrlDrag(MouseEvent.IsLeftControlDown() || MouseEvent.IsRightControlDown());
|
|
return FReply::Handled().BeginDragDrop(DragOperation);
|
|
}
|
|
}
|
|
else if(InAction->GetTypeId() == FEdGraphSchemaAction_K2Var::StaticGetTypeId())
|
|
{
|
|
FEdGraphSchemaAction_K2Var* VarAction = (FEdGraphSchemaAction_K2Var*)InAction.Get();
|
|
if (UClass* VarClass = VarAction->GetVariableClass())
|
|
{
|
|
TSharedRef<FKismetVariableDragDropAction> DragOperation = FKismetVariableDragDropAction::New(InAction, VarAction->GetVariableName(), VarClass, AnalyticsDelegate);
|
|
DragOperation->SetAltDrag(MouseEvent.IsAltDown());
|
|
DragOperation->SetCtrlDrag(MouseEvent.IsLeftControlDown() || MouseEvent.IsRightControlDown());
|
|
return FReply::Handled().BeginDragDrop(DragOperation);
|
|
}
|
|
}
|
|
else if( InAction->IsA(FEdGraphSchemaAction_BlueprintVariableBase::StaticGetTypeId()))
|
|
{
|
|
FEdGraphSchemaAction_BlueprintVariableBase* VarAction = (FEdGraphSchemaAction_BlueprintVariableBase*)InAction.Get();
|
|
|
|
if (GetFocusedGraph()->GetSchema()->CanGraphBeDropped(InAction))
|
|
{
|
|
return GetFocusedGraph()->GetSchema()->BeginGraphDragAction(InAction, MouseEvent);
|
|
}
|
|
}
|
|
else if (InAction->GetTypeId() == FEdGraphSchemaAction_K2Event::StaticGetTypeId())
|
|
{
|
|
// Check if it's a custom event, it is preferable to drop a call function for custom events than to focus on the node
|
|
FEdGraphSchemaAction_K2Event* FuncAction = (FEdGraphSchemaAction_K2Event*)InAction.Get();
|
|
if (UK2Node_Event* Event = Cast<UK2Node_Event>(FuncAction->NodeTemplate))
|
|
{
|
|
UFunction* const Function = FFunctionFromNodeHelper::FunctionFromNode(Event);
|
|
|
|
// Callback function to report that the user cannot drop this function in the graph
|
|
auto CanDragDropAction = [](TSharedPtr<FEdGraphSchemaAction> /*DropAction*/, UEdGraph* /*HoveredGraphIn*/, FText& ImpededReasonOut, UFunction* Func)->bool
|
|
{
|
|
// If this function is not BP callable then don't let it be dropped
|
|
if (Func && !(Func->FunctionFlags & (FUNC_BlueprintCallable | FUNC_BlueprintPure)))
|
|
{
|
|
ImpededReasonOut = LOCTEXT("NonBlueprintCallableEvent", "This event was not marked as Blueprint Callable and cannot be placed in a graph!");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
TSharedRef< FKismetFunctionDragDropAction> DragOperation =
|
|
FKismetFunctionDragDropAction::New(
|
|
InAction, (Function ? Function->GetFName() : Event->GetFName()),
|
|
GetBlueprintObj()->SkeletonGeneratedClass,
|
|
FMemberReference(),
|
|
AnalyticsDelegate,
|
|
FKismetDragDropAction::FCanBeDroppedDelegate::CreateLambda(CanDragDropAction, Function)
|
|
);
|
|
return FReply::Handled().BeginDragDrop(DragOperation);
|
|
}
|
|
}
|
|
}
|
|
|
|
return FReply::Unhandled();
|
|
}
|
|
|
|
FReply SMyBlueprint::OnCategoryDragged(const FText& InCategory, const FPointerEvent& MouseEvent)
|
|
{
|
|
TSharedRef<FMyBlueprintCategoryDragDropAction> DragOperation = FMyBlueprintCategoryDragDropAction::New(InCategory, SharedThis(this));
|
|
return FReply::Handled().BeginDragDrop(DragOperation);
|
|
}
|
|
|
|
void SMyBlueprint::OnGlobalActionSelected(const TArray< TSharedPtr<FEdGraphSchemaAction> >& InActions, ESelectInfo::Type InSelectionType)
|
|
{
|
|
if (InSelectionType == ESelectInfo::OnMouseClick || InSelectionType == ESelectInfo::OnKeyPress || InSelectionType == ESelectInfo::OnNavigation || InActions.Num() == 0)
|
|
{
|
|
OnActionSelected(InActions);
|
|
}
|
|
}
|
|
|
|
void SMyBlueprint::OnActionSelected( const TArray< TSharedPtr<FEdGraphSchemaAction> >& InActions )
|
|
{
|
|
TSharedPtr<FEdGraphSchemaAction> InAction(InActions.Num() > 0 ? InActions[0] : NULL);
|
|
UBlueprint* CurrentBlueprint = Blueprint;
|
|
TSharedPtr<SKismetInspector> CurrentInspector = Inspector.Pin();
|
|
|
|
TSharedPtr<FBlueprintEditor> BlueprintEditor = BlueprintEditorPtr.Pin();
|
|
|
|
if (BlueprintEditor.IsValid())
|
|
{
|
|
BlueprintEditor->SetUISelectionState(FBlueprintEditor::SelectionState_MyBlueprint);
|
|
|
|
CurrentBlueprint = BlueprintEditor->GetBlueprintObj();
|
|
CurrentInspector = BlueprintEditor->GetInspector();
|
|
}
|
|
OnActionSelectedHelper(InAction, BlueprintEditorPtr, Blueprint, CurrentInspector.ToSharedRef());
|
|
}
|
|
|
|
void SMyBlueprint::OnActionSelectedHelper(TSharedPtr<FEdGraphSchemaAction> InAction, TWeakPtr< FBlueprintEditor > InBlueprintEditor, UBlueprint* CurrentBlueprint, TSharedRef<SKismetInspector> CurrentInspector)
|
|
{
|
|
if (InAction.IsValid())
|
|
{
|
|
if (InAction->GetTypeId() == FEdGraphSchemaAction_K2Graph::StaticGetTypeId())
|
|
{
|
|
FEdGraphSchemaAction_K2Graph* GraphAction = (FEdGraphSchemaAction_K2Graph*)InAction.Get();
|
|
|
|
if (GraphAction->EdGraph)
|
|
{
|
|
FGraphDisplayInfo DisplayInfo;
|
|
const UEdGraphSchema* Schema = GraphAction->EdGraph->GetSchema();
|
|
check(Schema != nullptr);
|
|
Schema->GetGraphDisplayInformation(*GraphAction->EdGraph, DisplayInfo);
|
|
CurrentInspector->ShowDetailsForSingleObject(GraphAction->EdGraph, SKismetInspector::FShowDetailsOptions(DisplayInfo.PlainName));
|
|
}
|
|
}
|
|
else if (InAction->GetTypeId() == FEdGraphSchemaAction_K2Delegate::StaticGetTypeId())
|
|
{
|
|
FEdGraphSchemaAction_K2Delegate* DelegateAction = (FEdGraphSchemaAction_K2Delegate*)InAction.Get();
|
|
if (FMulticastDelegateProperty* Property = DelegateAction->GetDelegateProperty())
|
|
{
|
|
CurrentInspector->ShowDetailsForSingleObject(Property->GetUPropertyWrapper(), SKismetInspector::FShowDetailsOptions(FText::FromString(Property->GetName())));
|
|
}
|
|
}
|
|
else if (InAction->GetTypeId() == FEdGraphSchemaAction_K2Var::StaticGetTypeId())
|
|
{
|
|
FEdGraphSchemaAction_K2Var* VarAction = (FEdGraphSchemaAction_K2Var*)InAction.Get();
|
|
|
|
SKismetInspector::FShowDetailsOptions Options(FText::FromName(VarAction->GetVariableName()));
|
|
Options.bForceRefresh = true;
|
|
|
|
FProperty* Prop = VarAction->GetProperty();
|
|
UPropertyWrapper* PropWrap = (Prop ? Prop->GetUPropertyWrapper() : nullptr);
|
|
CurrentInspector->ShowDetailsForSingleObject(PropWrap, Options);
|
|
}
|
|
else if (InAction->GetTypeId() == FEdGraphSchemaAction_K2LocalVar::StaticGetTypeId())
|
|
{
|
|
FEdGraphSchemaAction_K2LocalVar* VarAction = (FEdGraphSchemaAction_K2LocalVar*)InAction.Get();
|
|
|
|
SKismetInspector::FShowDetailsOptions Options(FText::FromName(VarAction->GetVariableName()));
|
|
|
|
FProperty* Prop = VarAction->GetProperty();
|
|
UPropertyWrapper* PropWrap = (Prop ? Prop->GetUPropertyWrapper() : nullptr);
|
|
CurrentInspector->ShowDetailsForSingleObject(PropWrap, Options);
|
|
}
|
|
else if (InAction->IsAVariable())
|
|
{
|
|
FEdGraphSchemaAction_BlueprintVariableBase* VarAction = (FEdGraphSchemaAction_BlueprintVariableBase*)InAction.Get();
|
|
if (UEdGraph* Graph = GetFocusedGraph())
|
|
{
|
|
if (BlueprintEditorPtr.IsValid())
|
|
{
|
|
BlueprintEditorPtr.Pin()->SelectLocalVariable(Graph, VarAction->GetVariableName());
|
|
}
|
|
}
|
|
}
|
|
else if (InAction->GetTypeId() == FEdGraphSchemaAction_K2Enum::StaticGetTypeId())
|
|
{
|
|
FEdGraphSchemaAction_K2Enum* EnumAction = (FEdGraphSchemaAction_K2Enum*)InAction.Get();
|
|
|
|
SKismetInspector::FShowDetailsOptions Options(FText::FromName(EnumAction->GetPathName()));
|
|
Options.bForceRefresh = true;
|
|
|
|
CurrentInspector->ShowDetailsForSingleObject(EnumAction->Enum, Options);
|
|
}
|
|
else if (InAction->GetTypeId() == FEdGraphSchemaAction_K2Struct::StaticGetTypeId())
|
|
{
|
|
FEdGraphSchemaAction_K2Struct* StructAction = (FEdGraphSchemaAction_K2Struct*)InAction.Get();
|
|
|
|
SKismetInspector::FShowDetailsOptions Options(FText::FromName(StructAction->GetPathName()));
|
|
Options.bForceRefresh = true;
|
|
|
|
CurrentInspector->ShowDetailsForSingleObject(StructAction->Struct, Options);
|
|
}
|
|
else if (InAction->GetTypeId() == FEdGraphSchemaAction_K2TargetNode::StaticGetTypeId() ||
|
|
InAction->GetTypeId() == FEdGraphSchemaAction_K2Event::StaticGetTypeId() ||
|
|
InAction->GetTypeId() == FEdGraphSchemaAction_K2InputAction::StaticGetTypeId())
|
|
{
|
|
FEdGraphSchemaAction_K2TargetNode* TargetNodeAction = (FEdGraphSchemaAction_K2TargetNode*)InAction.Get();
|
|
|
|
UK2Node* NodeTemplate = TargetNodeAction->NodeTemplate.Get();
|
|
check(NodeTemplate != nullptr)
|
|
|
|
SKismetInspector::FShowDetailsOptions Options(NodeTemplate->GetNodeTitle(ENodeTitleType::EditableTitle));
|
|
CurrentInspector->ShowDetailsForSingleObject(NodeTemplate, Options);
|
|
}
|
|
else
|
|
{
|
|
CurrentInspector->ShowDetailsForObjects(TArray<UObject*>());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CurrentInspector->ShowDetailsForObjects(TArray<UObject*>());
|
|
}
|
|
}
|
|
|
|
void SMyBlueprint::OnActionDoubleClicked(const TArray< TSharedPtr<FEdGraphSchemaAction> >& InActions)
|
|
{
|
|
if ( !BlueprintEditorPtr.IsValid() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
TSharedPtr<FEdGraphSchemaAction> InAction(InActions.Num() > 0 ? InActions[0] : NULL);
|
|
ExecuteAction(InAction);
|
|
}
|
|
|
|
void SMyBlueprint::ExecuteAction(TSharedPtr<FEdGraphSchemaAction> InAction)
|
|
{
|
|
// Force it to open in a new document if shift is pressed
|
|
const bool bIsShiftPressed = FSlateApplication::Get().GetModifierKeys().IsShiftDown();
|
|
FDocumentTracker::EOpenDocumentCause OpenMode = bIsShiftPressed ? FDocumentTracker::ForceOpenNewDocument : FDocumentTracker::OpenNewDocument;
|
|
|
|
UBlueprint* BlueprintObj = BlueprintEditorPtr.Pin()->GetBlueprintObj();
|
|
if(InAction.IsValid())
|
|
{
|
|
if(InAction->GetTypeId() == FEdGraphSchemaAction_K2Graph::StaticGetTypeId())
|
|
{
|
|
FEdGraphSchemaAction_K2Graph* GraphAction = (FEdGraphSchemaAction_K2Graph*)InAction.Get();
|
|
|
|
if (GraphAction->EdGraph)
|
|
{
|
|
BlueprintEditorPtr.Pin()->JumpToHyperlink(GraphAction->EdGraph);
|
|
}
|
|
else if(IsAnInterfaceEvent(GraphAction))
|
|
{
|
|
// Focus it's node in the event graph
|
|
UFunction* OverrideFunc = nullptr;
|
|
UClass* const OverrideFuncClass = FBlueprintEditorUtils::GetOverrideFunctionClass(BlueprintObj, GraphAction->FuncName, &OverrideFunc);
|
|
if (OverrideFunc)
|
|
{
|
|
FName EventName = OverrideFunc->GetFName();
|
|
// check if event has been implemented
|
|
if (UK2Node_Event* ExistingNode = FBlueprintEditorUtils::FindOverrideForFunction(BlueprintObj, OverrideFuncClass, EventName))
|
|
{
|
|
FKismetEditorUtilities::BringKismetToFocusAttentionOnObject(ExistingNode);
|
|
}
|
|
else
|
|
{
|
|
// if there isn't an associated node, make one and focus it
|
|
ImplementFunction(GraphAction);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (InAction->GetTypeId() == FEdGraphSchemaAction_K2Delegate::StaticGetTypeId())
|
|
{
|
|
FEdGraphSchemaAction_K2Delegate* DelegateAction = (FEdGraphSchemaAction_K2Delegate*)InAction.Get();
|
|
|
|
if (DelegateAction->EdGraph)
|
|
{
|
|
BlueprintEditorPtr.Pin()->JumpToHyperlink(DelegateAction->EdGraph);
|
|
}
|
|
}
|
|
else if(InAction->GetTypeId() == FEdGraphSchemaAction_K2Var::StaticGetTypeId())
|
|
{
|
|
FEdGraphSchemaAction_K2Var* VarAction = (FEdGraphSchemaAction_K2Var*)InAction.Get();
|
|
|
|
// timeline variables
|
|
const FObjectPropertyBase* ObjectProperty = CastField<const FObjectPropertyBase>(VarAction->GetProperty());
|
|
if (ObjectProperty &&
|
|
ObjectProperty->PropertyClass &&
|
|
ObjectProperty->PropertyClass->IsChildOf(UTimelineComponent::StaticClass()))
|
|
{
|
|
for (int32 i=0; i<BlueprintObj->Timelines.Num(); i++)
|
|
{
|
|
// Convert the Timeline's name to a variable name before comparing it to the variable
|
|
if (BlueprintObj->Timelines[i]->GetVariableName() == VarAction->GetVariableName())
|
|
{
|
|
BlueprintEditorPtr.Pin()->JumpToHyperlink(BlueprintObj->Timelines[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (InAction->GetTypeId() == FEdGraphSchemaAction_K2Event::StaticGetTypeId())
|
|
{
|
|
FEdGraphSchemaAction_K2Event* EventNodeAction = (FEdGraphSchemaAction_K2Event*)InAction.Get();
|
|
FKismetEditorUtilities::BringKismetToFocusAttentionOnObject(EventNodeAction->NodeTemplate);
|
|
}
|
|
else if (InAction->GetTypeId() == FEdGraphSchemaAction_K2TargetNode::StaticGetTypeId() ||
|
|
InAction->GetTypeId() == FEdGraphSchemaAction_K2InputAction::StaticGetTypeId())
|
|
{
|
|
FEdGraphSchemaAction_K2TargetNode* TargetNodeAction = (FEdGraphSchemaAction_K2TargetNode*)InAction.Get();
|
|
FKismetEditorUtilities::BringKismetToFocusAttentionOnObject(TargetNodeAction->NodeTemplate);
|
|
}
|
|
else
|
|
{
|
|
InAction->OnDoubleClick(Blueprint);
|
|
}
|
|
}
|
|
}
|
|
|
|
template<class SchemaActionType> SchemaActionType* SelectionAsType( const TSharedPtr< SGraphActionMenu >& GraphActionMenu )
|
|
{
|
|
TArray<TSharedPtr<FEdGraphSchemaAction> > SelectedActions;
|
|
GraphActionMenu->GetSelectedActions(SelectedActions);
|
|
|
|
SchemaActionType* Selection = NULL;
|
|
|
|
TSharedPtr<FEdGraphSchemaAction> SelectedAction( SelectedActions.Num() > 0 ? SelectedActions[0] : NULL );
|
|
if ( SelectedAction.IsValid() &&
|
|
SelectedAction->GetTypeId() == SchemaActionType::StaticGetTypeId() )
|
|
{
|
|
// TODO Why not? StaticCastSharedPtr<>()
|
|
|
|
Selection = (SchemaActionType*)SelectedActions[0].Get();
|
|
}
|
|
|
|
return Selection;
|
|
}
|
|
|
|
FEdGraphSchemaAction_K2Enum* SMyBlueprint::SelectionAsEnum() const
|
|
{
|
|
return SelectionAsType<FEdGraphSchemaAction_K2Enum>( GraphActionMenu );
|
|
}
|
|
|
|
|
|
FEdGraphSchemaAction_K2Struct* SMyBlueprint::SelectionAsStruct() const
|
|
{
|
|
return SelectionAsType<FEdGraphSchemaAction_K2Struct>( GraphActionMenu );
|
|
}
|
|
|
|
FEdGraphSchemaAction_K2Graph* SMyBlueprint::SelectionAsGraph() const
|
|
{
|
|
return SelectionAsType<FEdGraphSchemaAction_K2Graph>( GraphActionMenu );
|
|
}
|
|
|
|
FEdGraphSchemaAction_K2Var* SMyBlueprint::SelectionAsVar() const
|
|
{
|
|
return SelectionAsType<FEdGraphSchemaAction_K2Var>( GraphActionMenu );
|
|
}
|
|
|
|
FEdGraphSchemaAction_K2LocalVar* SMyBlueprint::SelectionAsLocalVar() const
|
|
{
|
|
return SelectionAsType<FEdGraphSchemaAction_K2LocalVar>(GraphActionMenu);
|
|
}
|
|
|
|
FEdGraphSchemaAction_BlueprintVariableBase* SMyBlueprint::SelectionAsBlueprintVariable() const
|
|
{
|
|
TArray<TSharedPtr<FEdGraphSchemaAction> > SelectedActions;
|
|
GraphActionMenu->GetSelectedActions(SelectedActions);
|
|
|
|
FEdGraphSchemaAction_BlueprintVariableBase* Selection = nullptr;
|
|
|
|
TSharedPtr<FEdGraphSchemaAction> SelectedAction( SelectedActions.Num() > 0 ? SelectedActions[0] : nullptr );
|
|
if ( SelectedAction.IsValid() && SelectedAction->IsA(FEdGraphSchemaAction_BlueprintVariableBase::StaticGetTypeId()))
|
|
{
|
|
Selection = (FEdGraphSchemaAction_BlueprintVariableBase*)SelectedAction.Get();
|
|
}
|
|
|
|
return Selection;
|
|
}
|
|
|
|
FEdGraphSchemaAction_K2Delegate* SMyBlueprint::SelectionAsDelegate() const
|
|
{
|
|
return SelectionAsType<FEdGraphSchemaAction_K2Delegate>( GraphActionMenu );
|
|
}
|
|
|
|
FEdGraphSchemaAction_K2Event* SMyBlueprint::SelectionAsEvent() const
|
|
{
|
|
return SelectionAsType<FEdGraphSchemaAction_K2Event>( GraphActionMenu );
|
|
}
|
|
|
|
FEdGraphSchemaAction_K2InputAction* SMyBlueprint::SelectionAsInputAction() const
|
|
{
|
|
return SelectionAsType<FEdGraphSchemaAction_K2InputAction>(GraphActionMenu);
|
|
}
|
|
|
|
bool SMyBlueprint::SelectionIsCategory() const
|
|
{
|
|
return !SelectionHasContextMenu();
|
|
}
|
|
|
|
bool SMyBlueprint::SelectionHasContextMenu() const
|
|
{
|
|
TArray<TSharedPtr<FEdGraphSchemaAction> > SelectedActions;
|
|
GraphActionMenu->GetSelectedActions(SelectedActions);
|
|
return SelectedActions.Num() > 0;
|
|
}
|
|
|
|
FText SMyBlueprint::GetGraphCategory(UEdGraph* InGraph) const
|
|
{
|
|
FText ReturnCategory;
|
|
|
|
// Pull the category from the required metadata based on the types of nodes we can discover in the graph
|
|
UK2Node_EditablePinBase* EntryNode = FBlueprintEditorUtils::GetEntryNode(InGraph);
|
|
if (UK2Node_FunctionEntry* FunctionEntryNode = Cast<UK2Node_FunctionEntry>(EntryNode))
|
|
{
|
|
ReturnCategory = FunctionEntryNode->MetaData.Category;
|
|
}
|
|
else if (UK2Node_Tunnel* TypedEntryNode = ExactCast<UK2Node_Tunnel>(EntryNode))
|
|
{
|
|
ReturnCategory = TypedEntryNode->MetaData.Category;
|
|
}
|
|
|
|
// Empty the category if it's default, we don't want to display the "default" category and items will just appear without a category
|
|
if(ReturnCategory.EqualTo(UEdGraphSchema_K2::VR_DefaultCategory))
|
|
{
|
|
ReturnCategory = FText::GetEmpty();
|
|
}
|
|
|
|
return ReturnCategory;
|
|
}
|
|
|
|
void SMyBlueprint::GetSelectedItemsForContextMenu(TArray<FComponentEventConstructionData>& OutSelectedItems) const
|
|
{
|
|
FEdGraphSchemaAction_K2Var* Var = SelectionAsVar();
|
|
if ( Var != NULL )
|
|
{
|
|
FObjectProperty* ComponentProperty = CastField<FObjectProperty>(Var->GetProperty());
|
|
|
|
if ( ComponentProperty != NULL &&
|
|
ComponentProperty->PropertyClass != NULL &&
|
|
ComponentProperty->PropertyClass->IsChildOf( UActorComponent::StaticClass() ) )
|
|
{
|
|
FComponentEventConstructionData NewItem;
|
|
NewItem.VariableName = Var->GetVariableName();
|
|
NewItem.Component = Cast<UActorComponent>(ComponentProperty->PropertyClass->GetDefaultObject());
|
|
|
|
OutSelectedItems.Add( NewItem );
|
|
}
|
|
}
|
|
}
|
|
|
|
TSharedPtr<SWidget> SMyBlueprint::OnContextMenuOpening()
|
|
{
|
|
if( !BlueprintEditorPtr.IsValid() )
|
|
{
|
|
return TSharedPtr<SWidget>();
|
|
}
|
|
|
|
const bool bShouldCloseWindowAfterMenuSelection = true;
|
|
FMenuBuilder MenuBuilder( bShouldCloseWindowAfterMenuSelection, CommandList);
|
|
|
|
// Check if the selected action is valid for a context menu
|
|
if (SelectionHasContextMenu())
|
|
{
|
|
FEdGraphSchemaAction_K2Var* Var = SelectionAsVar();
|
|
FEdGraphSchemaAction_K2Graph* Graph = SelectionAsGraph();
|
|
FEdGraphSchemaAction_K2Event* Event = SelectionAsEvent();
|
|
const bool bExpandFindReferences = Graph || Event || Var;
|
|
|
|
MenuBuilder.BeginSection("BasicOperations");
|
|
{
|
|
MenuBuilder.AddMenuEntry(FMyBlueprintCommands::Get().OpenGraph);
|
|
MenuBuilder.AddMenuEntry(FMyBlueprintCommands::Get().OpenGraphInNewTab);
|
|
MenuBuilder.AddMenuEntry(FMyBlueprintCommands::Get().OpenExternalGraph);
|
|
MenuBuilder.AddMenuEntry(FMyBlueprintCommands::Get().FocusNode);
|
|
MenuBuilder.AddMenuEntry(FMyBlueprintCommands::Get().FocusNodeInNewTab);
|
|
MenuBuilder.AddMenuEntry(FGenericCommands::Get().Rename, NAME_None, LOCTEXT("Rename", "Rename"), LOCTEXT("Rename_Tooltip", "Renames this function or variable from blueprint.") );
|
|
MenuBuilder.AddMenuEntry(FMyBlueprintCommands::Get().ImplementFunction);
|
|
|
|
// Depending on context, FindReferences can be a button or an expandable menu. For example, the context menu
|
|
// for functions now lets you choose whether to do search by-name (fast) or by-function (smart).
|
|
if (!bExpandFindReferences)
|
|
{
|
|
// No expandable menu: display the simple 'Find References' action
|
|
MenuBuilder.AddMenuEntry(FGraphEditorCommands::Get().FindReferences);
|
|
}
|
|
else
|
|
{
|
|
// Insert "Find References" sub-menu here
|
|
MenuBuilder.AddSubMenu(
|
|
LOCTEXT("FindReferences_Label", "Find References"),
|
|
LOCTEXT("FindReferences_Tooltip", "Options for finding references to class members"),
|
|
FNewMenuDelegate::CreateStatic(&FGraphEditorCommands::BuildFindReferencesMenu),
|
|
false,
|
|
FSlateIcon()
|
|
);
|
|
}
|
|
|
|
MenuBuilder.AddMenuEntry(FGraphEditorCommands::Get().FindAndReplaceReferences);
|
|
MenuBuilder.AddMenuEntry(FMyBlueprintCommands::Get().GotoNativeVarDefinition);
|
|
MenuBuilder.AddMenuEntry(FGenericCommands::Get().Cut);
|
|
MenuBuilder.AddMenuEntry(FGenericCommands::Get().Copy);
|
|
MenuBuilder.AddMenuEntry(FGenericCommands::Get().Duplicate);
|
|
MenuBuilder.AddMenuEntry(FMyBlueprintCommands::Get().MoveVariableToParent);
|
|
MenuBuilder.AddMenuEntry(FMyBlueprintCommands::Get().MoveFunctionToParent);
|
|
MenuBuilder.AddMenuEntry(FMyBlueprintCommands::Get().DeleteEntry);
|
|
}
|
|
MenuBuilder.EndSection();
|
|
|
|
if ( Var && BlueprintEditorPtr.IsValid() && FBlueprintEditorUtils::DoesSupportEventGraphs(GetBlueprintObj()) )
|
|
{
|
|
FObjectProperty* ComponentProperty = CastField<FObjectProperty>(Var->GetProperty());
|
|
|
|
if ( ComponentProperty && ComponentProperty->PropertyClass &&
|
|
ComponentProperty->PropertyClass->IsChildOf( UActorComponent::StaticClass() ) )
|
|
{
|
|
if( FBlueprintEditorUtils::CanClassGenerateEvents( ComponentProperty->PropertyClass ))
|
|
{
|
|
TSharedPtr<FBlueprintEditor> BlueprintEditor(BlueprintEditorPtr.Pin());
|
|
|
|
// If the selected item is valid, and is a component of some sort, build a context menu
|
|
// of events appropriate to the component.
|
|
MenuBuilder.AddSubMenu( LOCTEXT("AddEventSubMenu", "Add Event"),
|
|
LOCTEXT("AddEventSubMenu_ToolTip", "Add Event"),
|
|
FNewMenuDelegate::CreateStatic( &SSubobjectBlueprintEditor::BuildMenuEventsSection,
|
|
BlueprintEditor->GetBlueprintObj(), ComponentProperty->PropertyClass.Get(),
|
|
FCanExecuteAction::CreateRaw(this, &SMyBlueprint::IsEditingMode),
|
|
FGetSelectedObjectsDelegate::CreateSP(this, &SMyBlueprint::GetSelectedItemsForContextMenu)));
|
|
}
|
|
}
|
|
}
|
|
// If this is a function graph than we should add the option to convert it to an event if possible
|
|
else if( Graph && Graph->EdGraph )
|
|
{
|
|
// The first function entry node will have all the information that the conversion needs
|
|
// (the interface method entry in the tree might not have a real graph though, if it comes from a parent unchanged or is an event that hasn't been implemented yet)
|
|
UK2Node_FunctionEntry* EntryNode = nullptr;
|
|
if (Graph->EdGraph != nullptr)
|
|
{
|
|
for( UEdGraphNode* Node : Graph->EdGraph->Nodes)
|
|
{
|
|
if (UK2Node_FunctionEntry* TypedNode = Cast<UK2Node_FunctionEntry>(Node))
|
|
{
|
|
EntryNode = TypedNode;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
TSharedPtr<FBlueprintEditor> BlueprintEditor(BlueprintEditorPtr.Pin());
|
|
if( EntryNode && BlueprintEditor.IsValid() &&
|
|
FBlueprintEditorUtils::IsFunctionConvertableToEvent(BlueprintEditor->GetBlueprintObj(), EntryNode->FindSignatureFunction()) )
|
|
{
|
|
MenuBuilder.AddMenuEntry(
|
|
LOCTEXT("MyBlueprint_Conversion_Func", "Convert function to event"), FText(), FSlateIcon(),
|
|
FExecuteAction::CreateLambda([BlueprintEditor, EntryNode]()
|
|
{
|
|
// ConvertFunctionIfValid handles any bad state, so no need for additional messaging
|
|
BlueprintEditor->ConvertFunctionIfValid(EntryNode);
|
|
})
|
|
);
|
|
}
|
|
|
|
if (const UEdGraphSchema* Schema = Graph->EdGraph->GetSchema())
|
|
{
|
|
if (Schema->AllowsFunctionVariants())
|
|
{
|
|
MenuBuilder.AddMenuEntry(
|
|
LOCTEXT("MyBlueprint_Add_Func_Variant", "Add Variant"), FText(), FSlateIcon(),
|
|
FExecuteAction::CreateLambda([BlueprintEditor, Graph]()
|
|
{
|
|
BlueprintEditor->AddNewFunctionVariant(Graph->EdGraph);
|
|
})
|
|
);
|
|
}
|
|
}
|
|
}
|
|
// If this is an event, allow us to convert it to a function graph if possible
|
|
else if( Event )
|
|
{
|
|
TSharedPtr<FBlueprintEditor> BlueprintEditor(BlueprintEditorPtr.Pin());
|
|
UK2Node_Event* EventNode = Cast<UK2Node_Event>(Event->NodeTemplate);
|
|
|
|
if( BlueprintEditor.IsValid() && EventNode )
|
|
{
|
|
MenuBuilder.AddMenuEntry(
|
|
LOCTEXT("MyBlueprint_Conversion_Event", "Convert event to function"), FText(), FSlateIcon(),
|
|
FExecuteAction::CreateLambda([BlueprintEditor, EventNode]()
|
|
{
|
|
// The ConvertEventIfValid function handles all bad states, so there's no need for further validation
|
|
BlueprintEditor->ConvertEventIfValid(EventNode);
|
|
})
|
|
);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BuildAddNewMenu(MenuBuilder);
|
|
}
|
|
|
|
return MenuBuilder.MakeWidget();
|
|
}
|
|
|
|
TSharedRef<SWidget> SMyBlueprint::CreateAddNewMenuWidget()
|
|
{
|
|
const bool bShouldCloseWindowAfterMenuSelection = true;
|
|
FMenuBuilder MenuBuilder(bShouldCloseWindowAfterMenuSelection, CommandList);
|
|
|
|
BuildAddNewMenu(MenuBuilder);
|
|
|
|
return MenuBuilder.MakeWidget();
|
|
}
|
|
|
|
void SMyBlueprint::BuildAddNewMenu(FMenuBuilder& MenuBuilder)
|
|
{
|
|
MenuBuilder.BeginSection("AddNewItem", LOCTEXT("AddOperations", "Add New"));
|
|
|
|
if(UBlueprint* CurrentBlueprint = GetBlueprintObj())
|
|
{
|
|
if (CurrentBlueprint->SupportsGlobalVariables())
|
|
{
|
|
MenuBuilder.AddMenuEntry(FBlueprintEditorCommands::Get().AddNewVariable);
|
|
MenuBuilder.AddMenuEntry(FMyBlueprintCommands::Get().PasteVariable);
|
|
}
|
|
if (CurrentBlueprint->SupportsLocalVariables())
|
|
{
|
|
MenuBuilder.AddMenuEntry(FBlueprintEditorCommands::Get().AddNewLocalVariable);
|
|
MenuBuilder.AddMenuEntry(FMyBlueprintCommands::Get().PasteLocalVariable);
|
|
}
|
|
if (CurrentBlueprint->SupportsFunctions())
|
|
{
|
|
MenuBuilder.AddMenuEntry(FBlueprintEditorCommands::Get().AddNewFunction);
|
|
MenuBuilder.AddMenuEntry(FMyBlueprintCommands::Get().PasteFunction);
|
|
|
|
// If we cannot handle Function Graphs, we cannot handle function overrides
|
|
if (OverridableFunctionActions.Num() > 0 && BlueprintEditorPtr.Pin()->NewDocument_IsVisibleForType(FBlueprintEditor::CGT_NewFunctionGraph))
|
|
{
|
|
MenuBuilder.AddSubMenu(
|
|
LOCTEXT("OverrideFunction", "Override Function"),
|
|
FText::GetEmpty(),
|
|
FNewMenuDelegate::CreateSP(this, &SMyBlueprint::BuildOverridableFunctionsMenu),
|
|
false,
|
|
FSlateIcon(FAppStyle::GetAppStyleSetName(), "BlueprintEditor.AddNewFunction.Small"));
|
|
}
|
|
}
|
|
|
|
if (CurrentBlueprint->SupportsMacros())
|
|
{
|
|
MenuBuilder.AddMenuEntry(FBlueprintEditorCommands::Get().AddNewMacroDeclaration);
|
|
MenuBuilder.AddMenuEntry(FMyBlueprintCommands::Get().PasteMacro);
|
|
}
|
|
if (CurrentBlueprint->SupportsEventGraphs())
|
|
{
|
|
MenuBuilder.AddMenuEntry(FBlueprintEditorCommands::Get().AddNewEventGraph);
|
|
}
|
|
if (CurrentBlueprint->SupportsDelegates())
|
|
{
|
|
MenuBuilder.AddMenuEntry(FBlueprintEditorCommands::Get().AddNewDelegate);
|
|
}
|
|
if (CurrentBlueprint->SupportsAnimLayers())
|
|
{
|
|
MenuBuilder.AddMenuEntry(FBlueprintEditorCommands::Get().AddNewAnimationLayer);
|
|
}
|
|
}
|
|
MenuBuilder.EndSection();
|
|
}
|
|
|
|
bool SMyBlueprint::CanOpenGraph() const
|
|
{
|
|
const FEdGraphSchemaAction_K2Graph* GraphAction = SelectionAsGraph();
|
|
const bool bGraph = GraphAction && GraphAction->EdGraph;
|
|
const FEdGraphSchemaAction_K2Delegate* DelegateAction = SelectionAsDelegate();
|
|
const bool bDelegate = DelegateAction && DelegateAction->EdGraph;
|
|
return (bGraph || bDelegate) && BlueprintEditorPtr.IsValid();
|
|
}
|
|
|
|
bool SMyBlueprint::CanOpenExternalGraph() const
|
|
{
|
|
const FEdGraphSchemaAction_K2Graph* GraphAction = SelectionAsGraph();
|
|
const bool bGraph = GraphAction && GraphAction->EdGraph;
|
|
return CanOpenGraph() && bGraph && !BlueprintEditorPtr.Pin()->IsGraphInCurrentBlueprint(GraphAction->EdGraph);
|
|
}
|
|
|
|
void SMyBlueprint::OpenGraph(FDocumentTracker::EOpenDocumentCause InCause, bool bOpenExternalGraphInNewEditor)
|
|
{
|
|
UEdGraph* GraphToOpen = nullptr;
|
|
|
|
if (FEdGraphSchemaAction_K2Graph* GraphAction = SelectionAsGraph())
|
|
{
|
|
GraphToOpen = GraphAction->EdGraph;
|
|
// If we have no graph then this is an interface event, so focus on the event graph
|
|
if (!GraphToOpen)
|
|
{
|
|
GraphToOpen = FBlueprintEditorUtils::FindEventGraph(GetBlueprintObj());
|
|
}
|
|
}
|
|
else if (FEdGraphSchemaAction_K2Delegate* DelegateAction = SelectionAsDelegate())
|
|
{
|
|
GraphToOpen = DelegateAction->EdGraph;
|
|
}
|
|
else if (FEdGraphSchemaAction_K2Event* EventAction = SelectionAsEvent())
|
|
{
|
|
GraphToOpen = EventAction->NodeTemplate->GetGraph();
|
|
}
|
|
else if (FEdGraphSchemaAction_K2InputAction* InputAction = SelectionAsInputAction())
|
|
{
|
|
GraphToOpen = InputAction->NodeTemplate->GetGraph();
|
|
}
|
|
|
|
if (GraphToOpen)
|
|
{
|
|
if(bOpenExternalGraphInNewEditor && !BlueprintEditorPtr.Pin()->IsGraphInCurrentBlueprint(GraphToOpen))
|
|
{
|
|
if(UBlueprint* OtherBlueprint = FBlueprintEditorUtils::FindBlueprintForGraph(GraphToOpen))
|
|
{
|
|
GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->OpenEditorForAsset(OtherBlueprint);
|
|
if(IBlueprintEditor* OtherBlueprintEditor = static_cast<IBlueprintEditor*>(GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->FindEditorForAsset(OtherBlueprint, true)))
|
|
{
|
|
OtherBlueprintEditor->JumpToHyperlink(GraphToOpen, false);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BlueprintEditorPtr.Pin()->OpenDocument(GraphToOpen, InCause);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void SMyBlueprint::OnOpenGraph()
|
|
{
|
|
OpenGraph(FDocumentTracker::OpenNewDocument);
|
|
}
|
|
|
|
void SMyBlueprint::OnOpenGraphInNewTab()
|
|
{
|
|
OpenGraph(FDocumentTracker::ForceOpenNewDocument);
|
|
}
|
|
|
|
void SMyBlueprint::OnOpenExternalGraph()
|
|
{
|
|
OpenGraph(FDocumentTracker::OpenNewDocument, true);
|
|
}
|
|
|
|
bool SMyBlueprint::CanFocusOnNode() const
|
|
{
|
|
FEdGraphSchemaAction_K2Event const* const EventAction = SelectionAsEvent();
|
|
FEdGraphSchemaAction_K2InputAction const* const InputAction = SelectionAsInputAction();
|
|
UK2Node_Event* ExistingNode = nullptr;
|
|
|
|
if (FEdGraphSchemaAction_K2Graph* GraphAction = SelectionAsGraph())
|
|
{
|
|
// Is this an event implemented from an interface?
|
|
UBlueprint* BlueprintObj = GetBlueprintObj();
|
|
UFunction* OverrideFunc = nullptr;
|
|
UClass* const OverrideFuncClass = FBlueprintEditorUtils::GetOverrideFunctionClass(BlueprintObj, GraphAction->FuncName, &OverrideFunc);
|
|
|
|
if (OverrideFunc)
|
|
{
|
|
// Add to event graph
|
|
FName EventName = OverrideFunc->GetFName();
|
|
ExistingNode = FBlueprintEditorUtils::FindOverrideForFunction(BlueprintObj, OverrideFuncClass, EventName);
|
|
}
|
|
}
|
|
|
|
return (EventAction && EventAction->NodeTemplate) || (InputAction && InputAction->NodeTemplate) || ExistingNode;
|
|
}
|
|
|
|
void SMyBlueprint::OnFocusNode()
|
|
{
|
|
FEdGraphSchemaAction_K2Event* EventAction = SelectionAsEvent();
|
|
FEdGraphSchemaAction_K2InputAction* InputAction = SelectionAsInputAction();
|
|
if (EventAction || InputAction)
|
|
{
|
|
UK2Node* Node = EventAction ? EventAction->NodeTemplate : InputAction->NodeTemplate;
|
|
FKismetEditorUtilities::BringKismetToFocusAttentionOnObject(Node);
|
|
}
|
|
else if (FEdGraphSchemaAction_K2Graph* GraphAction = SelectionAsGraph())
|
|
{
|
|
// Is this an event implemented from an interface?
|
|
UBlueprint* BlueprintObj = GetBlueprintObj();
|
|
UFunction* OverrideFunc = nullptr;
|
|
UClass* const OverrideFuncClass = FBlueprintEditorUtils::GetOverrideFunctionClass(BlueprintObj, GraphAction->FuncName, &OverrideFunc);
|
|
|
|
if (OverrideFunc)
|
|
{
|
|
// Add to event graph
|
|
FName EventName = OverrideFunc->GetFName();
|
|
if (UK2Node_Event* ExistingNode = FBlueprintEditorUtils::FindOverrideForFunction(BlueprintObj, OverrideFuncClass, EventName))
|
|
{
|
|
FKismetEditorUtilities::BringKismetToFocusAttentionOnObject(ExistingNode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SMyBlueprint::OnFocusNodeInNewTab()
|
|
{
|
|
OpenGraph(FDocumentTracker::ForceOpenNewDocument);
|
|
OnFocusNode();
|
|
}
|
|
|
|
bool SMyBlueprint::CanImplementFunction() const
|
|
{
|
|
FEdGraphSchemaAction_K2Graph* GraphAction = SelectionAsGraph();
|
|
return GraphAction && GraphAction->EdGraph == nullptr && !CanFocusOnNode();
|
|
}
|
|
|
|
void SMyBlueprint::OnImplementFunction()
|
|
{
|
|
if ( FEdGraphSchemaAction_K2Graph* GraphAction = SelectionAsGraph() )
|
|
{
|
|
ImplementFunction(GraphAction);
|
|
}
|
|
}
|
|
|
|
void SMyBlueprint::ImplementFunction(TSharedPtr<FEdGraphSchemaAction_K2Graph> GraphAction)
|
|
{
|
|
ImplementFunction(GraphAction.Get());
|
|
}
|
|
|
|
void SMyBlueprint::ImplementFunction(FEdGraphSchemaAction_K2Graph* GraphAction)
|
|
{
|
|
UBlueprint* BlueprintObj = GetBlueprintObj();
|
|
check(BlueprintObj && BlueprintObj->SkeletonGeneratedClass);
|
|
if(BlueprintObj->BlueprintType == BPTYPE_MacroLibrary)
|
|
{
|
|
// macro libraries cannot have functions, attempting to create a function in
|
|
// one would trigger an assertion failure
|
|
FNotificationInfo Info( LOCTEXT("NoFunctionsInMacro",
|
|
"Blueprint Macro Libraries cannot implement functions"));
|
|
Info.bUseLargeFont = false;
|
|
Info.ExpireDuration = 5.0f;
|
|
Info.bFireAndForget = true;
|
|
FSlateNotificationManager::Get().AddNotification(Info);
|
|
return;
|
|
}
|
|
|
|
// Ensure that we are conforming to all current interfaces so that if there has been an additional
|
|
// interface function added we just focus to it instead of creating a new one
|
|
FBlueprintEditorUtils::ConformImplementedInterfaces(BlueprintObj);
|
|
|
|
UFunction* OverrideFunc = nullptr;
|
|
UClass* const OverrideFuncClass = FBlueprintEditorUtils::GetOverrideFunctionClass(BlueprintObj, GraphAction->FuncName, &OverrideFunc);
|
|
check(OverrideFunc);
|
|
// Some types of blueprints don't have an event graph (IE gameplay ability blueprints), in that case just make a new graph, even
|
|
// for events:
|
|
UEdGraph* EventGraph = FBlueprintEditorUtils::FindEventGraph(BlueprintObj);
|
|
if (UEdGraphSchema_K2::FunctionCanBePlacedAsEvent(OverrideFunc) && !IsImplementationDesiredAsFunction(OverrideFunc) && EventGraph)
|
|
{
|
|
// Add to event graph
|
|
FName EventName = OverrideFunc->GetFName();
|
|
UK2Node_Event* ExistingNode = FBlueprintEditorUtils::FindOverrideForFunction(BlueprintObj, OverrideFuncClass, EventName);
|
|
|
|
if (ExistingNode)
|
|
{
|
|
FKismetEditorUtilities::BringKismetToFocusAttentionOnObject(ExistingNode);
|
|
}
|
|
else
|
|
{
|
|
UK2Node_Event* NewEventNode = FEdGraphSchemaAction_K2NewNode::SpawnNode<UK2Node_Event>(
|
|
EventGraph,
|
|
EventGraph->GetGoodPlaceForNewNode(),
|
|
EK2NewNodeFlags::SelectNewNode,
|
|
[EventName, OverrideFuncClass](UK2Node_Event* NewInstance)
|
|
{
|
|
NewInstance->EventReference.SetExternalMember(EventName, OverrideFuncClass);
|
|
NewInstance->bOverrideFunction = true;
|
|
}
|
|
);
|
|
if (NewEventNode)
|
|
{
|
|
FKismetEditorUtilities::BringKismetToFocusAttentionOnObject(NewEventNode);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If there is an already existing graph of this function than just open that
|
|
// Needed for implementing interface functions on the base class through the override menu
|
|
UEdGraph* const ExistingGraph = FindObject<UEdGraph>(BlueprintObj, *GraphAction->FuncName.ToString());
|
|
if (ExistingGraph)
|
|
{
|
|
FKismetEditorUtilities::BringKismetToFocusAttentionOnObject(ExistingGraph);
|
|
}
|
|
else
|
|
{
|
|
const FScopedTransaction Transaction(LOCTEXT("CreateOverrideFunctionGraph", "Create Override Function Graph"));
|
|
BlueprintObj->Modify();
|
|
// Implement the function graph
|
|
UEdGraph* const NewGraph = FBlueprintEditorUtils::CreateNewGraph(BlueprintObj, GraphAction->FuncName, UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass());
|
|
FBlueprintEditorUtils::AddFunctionGraph(BlueprintObj, NewGraph, /*bIsUserCreated=*/ false, OverrideFuncClass);
|
|
NewGraph->Modify();
|
|
BlueprintEditorPtr.Pin()->OpenDocument(NewGraph, FDocumentTracker::OpenNewDocument);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SMyBlueprint::IsImplementationDesiredAsFunction(const UFunction* OverrideFunc) const
|
|
{
|
|
// If the original function was created in a parent blueprint, then prefer a BP function
|
|
if (OverrideFunc)
|
|
{
|
|
FName OverrideName = *OverrideFunc->GetName();
|
|
TSet<FName> GraphNames;
|
|
FBlueprintEditorUtils::GetAllGraphNames(GetBlueprintObj(), GraphNames);
|
|
for (const FName & Name : GraphNames)
|
|
{
|
|
if (Name == OverrideName)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Otherwise, we would prefer an event
|
|
return false;
|
|
}
|
|
|
|
void SMyBlueprint::OnFindReference(bool bSearchAllBlueprints, const EGetFindReferenceSearchStringFlags Flags)
|
|
{
|
|
FString SearchTerm;
|
|
for(const UEdGraph* UberGraph : GetBlueprintObj()->UbergraphPages)
|
|
{
|
|
if(const UEdGraphSchema* Schema = UberGraph->GetSchema())
|
|
{
|
|
TArray<TSharedPtr<FEdGraphSchemaAction> > SelectedActions;
|
|
GraphActionMenu->GetSelectedActions(SelectedActions);
|
|
if(SelectedActions.Num() == 1)
|
|
{
|
|
if (const FEdGraphSchemaAction* GraphAction = SelectedActions[0].Get())
|
|
{
|
|
SearchTerm = Schema->GetFindReferenceSearchTerm(GraphAction);
|
|
if(!SearchTerm.IsEmpty())
|
|
{
|
|
BlueprintEditorPtr.Pin()->SummonSearchUI(true, SearchTerm);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool bUseQuotes = true;
|
|
|
|
if (FEdGraphSchemaAction_K2Graph* GraphAction = SelectionAsGraph())
|
|
{
|
|
bool bSearchTermGenerated = false;
|
|
if (EnumHasAnyFlags(Flags, EGetFindReferenceSearchStringFlags::UseSearchSyntax))
|
|
{
|
|
// Attempt to resolve function, then attempt to generate search term from it
|
|
if (const UFunction* Function = GraphAction->GetFunction())
|
|
{
|
|
if (FindInBlueprintsHelpers::ConstructSearchTermFromFunction(Function, SearchTerm))
|
|
{
|
|
bSearchTermGenerated = true;
|
|
bUseQuotes = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If a search term was not generated yet, just search by name. Either the graph doesn't represent a
|
|
// function or we failed to resolve a dependency for generating a query.
|
|
if (!bSearchTermGenerated)
|
|
{
|
|
SearchTerm = GraphAction->FuncName.ToString();
|
|
}
|
|
}
|
|
else if (FEdGraphSchemaAction_K2Var* VarAction = SelectionAsVar())
|
|
{
|
|
bool bSearchTermGenerated = false;
|
|
if (EnumHasAnyFlags(Flags, EGetFindReferenceSearchStringFlags::UseSearchSyntax))
|
|
{
|
|
// Attempt to resolve property, then generate search term from it
|
|
if (const FProperty* Property = VarAction->GetProperty())
|
|
{
|
|
FMemberReference MemberReference;
|
|
MemberReference.SetFromField<FProperty>(Property, true, Property->GetOwnerClass());
|
|
SearchTerm = MemberReference.GetReferenceSearchString(Property->GetOwnerClass());
|
|
bSearchTermGenerated = true;
|
|
bUseQuotes = false;
|
|
}
|
|
}
|
|
|
|
// If a search term was not generated yet, just search by variable name
|
|
if (!bSearchTermGenerated)
|
|
{
|
|
SearchTerm = VarAction->GetVariableName().ToString();
|
|
}
|
|
}
|
|
else if (FEdGraphSchemaAction_K2LocalVar* LocalVarAction = SelectionAsLocalVar())
|
|
{
|
|
SearchTerm = FString::Printf(TEXT("Nodes(VariableReference(MemberName=+\"%s\" && MemberScope=+\"%s\"))"), *LocalVarAction->GetVariableName().ToString(), *LocalVarAction->GetVariableScope()->GetName());
|
|
bUseQuotes = false;
|
|
}
|
|
else if (FEdGraphSchemaAction_K2Delegate* DelegateAction = SelectionAsDelegate())
|
|
{
|
|
SearchTerm = DelegateAction->GetDelegateName().ToString();
|
|
}
|
|
else if (FEdGraphSchemaAction_K2Enum* EnumAction = SelectionAsEnum())
|
|
{
|
|
SearchTerm = EnumAction->Enum->GetName();
|
|
}
|
|
else if (FEdGraphSchemaAction_K2Struct* StructAction = SelectionAsStruct())
|
|
{
|
|
SearchTerm = StructAction->Struct->GetName();
|
|
}
|
|
else if (FEdGraphSchemaAction_K2Event* EventAction = SelectionAsEvent())
|
|
{
|
|
bool bSearchTermGenerated = false;
|
|
if (EnumHasAnyFlags(Flags, EGetFindReferenceSearchStringFlags::UseSearchSyntax))
|
|
{
|
|
if (const UK2Node_Event* EventNode = Cast<UK2Node_Event>(EventAction->NodeTemplate))
|
|
{
|
|
SearchTerm = EventNode->GetFindReferenceSearchString(Flags);
|
|
bSearchTermGenerated = true;
|
|
bUseQuotes = false;
|
|
}
|
|
}
|
|
|
|
if (!bSearchTermGenerated)
|
|
{
|
|
SearchTerm = EventAction->NodeTemplate->GetFindReferenceSearchString(EGetFindReferenceSearchStringFlags::None);
|
|
bUseQuotes = false;
|
|
}
|
|
}
|
|
else if (FEdGraphSchemaAction_K2InputAction* InputAction = SelectionAsInputAction())
|
|
{
|
|
SearchTerm = InputAction->NodeTemplate ?
|
|
InputAction->NodeTemplate->GetNodeTitle(ENodeTitleType::FullTitle).ToString() :
|
|
InputAction->GetMenuDescription().ToString();
|
|
}
|
|
|
|
if (!SearchTerm.IsEmpty())
|
|
{
|
|
if (bUseQuotes)
|
|
{
|
|
SearchTerm = FString::Printf(TEXT("\"%s\""), *SearchTerm);
|
|
}
|
|
|
|
const bool bSetFindWithinBlueprint = !bSearchAllBlueprints;
|
|
BlueprintEditorPtr.Pin()->SummonSearchUI(bSetFindWithinBlueprint, SearchTerm);
|
|
}
|
|
}
|
|
|
|
bool SMyBlueprint::CanFindReference() const
|
|
{
|
|
// Nothing relevant to the category will ever be found, unless the name of the category overlaps with another item
|
|
if (SelectionIsCategory())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void SMyBlueprint::OnFindAndReplaceReference()
|
|
{
|
|
TSharedPtr<FBlueprintEditor> PinnedEditor = BlueprintEditorPtr.Pin();
|
|
if (PinnedEditor.IsValid())
|
|
{
|
|
if (FEdGraphSchemaAction_K2Var* VarAction = SelectionAsVar())
|
|
{
|
|
PinnedEditor->SummonFindAndReplaceUI();
|
|
if (PinnedEditor->GetReplaceReferencesWidget().IsValid())
|
|
{
|
|
PinnedEditor->GetReplaceReferencesWidget()->SetSourceVariable(VarAction->GetProperty());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SMyBlueprint::CanFindAndReplaceReference() const
|
|
{
|
|
return SelectionAsVar() != nullptr;
|
|
}
|
|
|
|
void SMyBlueprint::OnDeleteGraph(UEdGraph* InGraph, EEdGraphSchemaAction_K2Graph::Type InGraphType)
|
|
{
|
|
if (InGraph && InGraph->bAllowDeletion)
|
|
{
|
|
if (const UEdGraphSchema* Schema = InGraph->GetSchema())
|
|
{
|
|
if (Schema->TryDeleteGraph(InGraph))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
const FScopedTransaction Transaction( LOCTEXT("RemoveGraph", "Remove Graph") );
|
|
GetBlueprintObj()->Modify();
|
|
|
|
InGraph->Modify();
|
|
|
|
if (InGraphType == EEdGraphSchemaAction_K2Graph::Subgraph)
|
|
{
|
|
// Remove any composite nodes bound to this graph
|
|
TArray<UK2Node_Composite*> AllCompositeNodes;
|
|
FBlueprintEditorUtils::GetAllNodesOfClass<UK2Node_Composite>(GetBlueprintObj(), AllCompositeNodes);
|
|
|
|
const bool bDontRecompile = true;
|
|
for (UK2Node_Composite* CompNode : AllCompositeNodes)
|
|
{
|
|
if (CompNode->BoundGraph == InGraph)
|
|
{
|
|
FBlueprintEditorUtils::RemoveNode(GetBlueprintObj(), CompNode, bDontRecompile);
|
|
}
|
|
}
|
|
}
|
|
|
|
FBlueprintEditorUtils::RemoveGraph(GetBlueprintObj(), InGraph, EGraphRemoveFlags::Recompile);
|
|
BlueprintEditorPtr.Pin()->CloseDocumentTab(InGraph);
|
|
|
|
for (TObjectIterator<UK2Node_CreateDelegate> It(RF_ClassDefaultObject, /** bIncludeDerivedClasses */ true, /** InternalExcludeFlags */ EInternalObjectFlags::Garbage); It; ++It)
|
|
{
|
|
if (It->GetGraph() != InGraph)
|
|
{
|
|
if (IsValid(*It) && IsValid(It->GetGraph()))
|
|
{
|
|
It->HandleAnyChange();
|
|
}
|
|
}
|
|
}
|
|
|
|
InGraph = NULL;
|
|
}
|
|
}
|
|
|
|
UEdGraph* SMyBlueprint::GetFocusedGraph() const
|
|
{
|
|
TSharedPtr<FBlueprintEditor> BlueprintEditorPtrPinned = BlueprintEditorPtr.Pin();
|
|
if( BlueprintEditorPtrPinned.IsValid() )
|
|
{
|
|
return BlueprintEditorPtrPinned->GetFocusedGraph();
|
|
}
|
|
|
|
return EdGraph;
|
|
}
|
|
|
|
void SMyBlueprint::OnObjectPropertyChanged(UObject* InObject, FPropertyChangedEvent& InPropertyChangedEvent)
|
|
{
|
|
if (InObject == Blueprint && (InPropertyChangedEvent.ChangeType != EPropertyChangeType::ValueSet && InPropertyChangedEvent.ChangeType != EPropertyChangeType::ArrayClear))
|
|
{
|
|
bNeedsRefresh = true;
|
|
}
|
|
}
|
|
|
|
bool SMyBlueprint::IsEditingMode() const
|
|
{
|
|
TSharedPtr<FBlueprintEditor> BlueprintEditorSPtr = BlueprintEditorPtr.Pin();
|
|
return BlueprintEditorSPtr.IsValid() && BlueprintEditorSPtr->InEditingMode();
|
|
}
|
|
|
|
bool SMyBlueprint::IsAnInterfaceEvent(FEdGraphSchemaAction_K2Graph* InAction)
|
|
{
|
|
return InAction->GraphType == EEdGraphSchemaAction_K2Graph::Interface && !InAction->EdGraph;
|
|
}
|
|
|
|
void SMyBlueprint::OnDeleteDelegate(FEdGraphSchemaAction_K2Delegate* InDelegateAction)
|
|
{
|
|
UEdGraph* GraphToActOn = InDelegateAction->EdGraph;
|
|
UBlueprint* BlueprintObj = GetBlueprintObj();
|
|
if (GraphToActOn && BlueprintObj)
|
|
{
|
|
const FScopedTransaction Transaction( LOCTEXT("RemoveDelegate", "Remove Event Dispatcher") );
|
|
BlueprintObj->Modify();
|
|
|
|
BlueprintEditorPtr.Pin()->CloseDocumentTab(GraphToActOn);
|
|
GraphToActOn->Modify();
|
|
|
|
FBlueprintEditorUtils::RemoveMemberVariable(BlueprintObj, GraphToActOn->GetFName());
|
|
FBlueprintEditorUtils::RemoveGraph(BlueprintObj, GraphToActOn, EGraphRemoveFlags::Recompile);
|
|
|
|
for (TObjectIterator<UK2Node_CreateDelegate> It(RF_ClassDefaultObject, /** bIncludeDerivedClasses */ true, /** InternalExcludeFlags */ EInternalObjectFlags::Garbage); It; ++It)
|
|
{
|
|
if (IsValid(*It) && IsValid(It->GetGraph()))
|
|
{
|
|
It->HandleAnyChange();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace UE::Blueprint::Private
|
|
{
|
|
// Given a type and value name, display a deletion confirmation warning.
|
|
// Returns true if the user 'cancelled' the action, interpreted as an early exit prior to deletion.
|
|
static bool DisplayInUseWarningAndEarlyExit(const FName& DisplayTypeName, const FName& DisplayValueName)
|
|
{
|
|
const FText DeleteConfirmationPrompt = FText::Format(LOCTEXT("DeleteConfirmationPrompt", "{0} {1} is in use! Do you really want to delete it?")
|
|
, { FText::FromName(DisplayTypeName), FText::FromName(DisplayValueName) }
|
|
);
|
|
const FText DeleteConfirmationTitle = FText::Format(LOCTEXT("DeleteConfirmationTitle", "Delete {0}")
|
|
, { FText::FromName(DisplayTypeName) }
|
|
);
|
|
const FString DeleteConfirmationIniSetting = FString::Format(TEXT("DeleteConfirmation{0}_Warning"), { DisplayTypeName.ToString() });
|
|
|
|
// Warn the user that this may result in data loss
|
|
FSuppressableWarningDialog::FSetupInfo Info(DeleteConfirmationPrompt, DeleteConfirmationTitle, DeleteConfirmationIniSetting);
|
|
Info.ConfirmText = LOCTEXT("DeleteConfirmation_Yes", "Yes");
|
|
Info.CancelText = LOCTEXT("DeleteConfirmation_No", "No");
|
|
|
|
FSuppressableWarningDialog DeleteFunctionInUse(Info);
|
|
return DeleteFunctionInUse.ShowModal() == FSuppressableWarningDialog::Cancel;
|
|
}
|
|
}
|
|
|
|
void SMyBlueprint::OnDeleteEntry()
|
|
{
|
|
if (FEdGraphSchemaAction_K2Graph* GraphAction = SelectionAsGraph())
|
|
{
|
|
// Currently only function graphs are supported for in-use detection and deletion warnings
|
|
if (GraphAction->GraphType == EEdGraphSchemaAction_K2Graph::Function)
|
|
{
|
|
if (FBlueprintEditorUtils::IsFunctionUsed(GetBlueprintObj(), GraphAction->FuncName))
|
|
{
|
|
if (UE::Blueprint::Private::DisplayInUseWarningAndEarlyExit("Function", GraphAction->FuncName))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
OnDeleteGraph(GraphAction->EdGraph, GraphAction->GraphType);
|
|
}
|
|
else if (FEdGraphSchemaAction_K2Delegate* DelegateAction = SelectionAsDelegate())
|
|
{
|
|
OnDeleteDelegate(DelegateAction);
|
|
}
|
|
else if (FEdGraphSchemaAction_K2Var* VarAction = SelectionAsVar())
|
|
{
|
|
if(FBlueprintEditorUtils::IsVariableUsed(GetBlueprintObj(), VarAction->GetVariableName()))
|
|
{
|
|
if (UE::Blueprint::Private::DisplayInUseWarningAndEarlyExit("Variable", VarAction->GetVariableName()))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
const FScopedTransaction Transaction( LOCTEXT( "RemoveVariable", "Remove Variable" ) );
|
|
|
|
GetBlueprintObj()->Modify();
|
|
FBlueprintEditorUtils::RemoveMemberVariable(GetBlueprintObj(), VarAction->GetVariableName());
|
|
}
|
|
else if (FEdGraphSchemaAction_K2LocalVar* LocalVarAction = SelectionAsLocalVar())
|
|
{
|
|
if (FBlueprintEditorUtils::IsVariableUsed(GetBlueprintObj(), LocalVarAction->GetVariableName(), FBlueprintEditorUtils::FindScopeGraph(GetBlueprintObj(), CastChecked<UStruct>(LocalVarAction->GetVariableScope()))))
|
|
{
|
|
if (UE::Blueprint::Private::DisplayInUseWarningAndEarlyExit("Local Variable", LocalVarAction->GetVariableName()))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
const FScopedTransaction Transaction( LOCTEXT( "RemoveLocalVariable", "Remove Local Variable" ) );
|
|
|
|
GetBlueprintObj()->Modify();
|
|
|
|
UEdGraph* FunctionGraph = FBlueprintEditorUtils::GetTopLevelGraph(GetFocusedGraph());
|
|
TArray<UK2Node_FunctionEntry*> FunctionEntryNodes;
|
|
FunctionGraph->GetNodesOfClass<UK2Node_FunctionEntry>(FunctionEntryNodes);
|
|
check(FunctionEntryNodes.Num() == 1);
|
|
FunctionEntryNodes[0]->Modify();
|
|
|
|
FBlueprintEditorUtils::RemoveLocalVariable(GetBlueprintObj(), CastChecked<UStruct>(LocalVarAction->GetVariableScope()), LocalVarAction->GetVariableName());
|
|
}
|
|
else if (FEdGraphSchemaAction_BlueprintVariableBase* BPVarAction = SelectionAsBlueprintVariable())
|
|
{
|
|
if (BPVarAction->IsVariableUsed())
|
|
{
|
|
if (UE::Blueprint::Private::DisplayInUseWarningAndEarlyExit("Variable", BPVarAction->GetVariableName()))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
const FScopedTransaction Transaction( LOCTEXT( "RemoveLocalVariable", "Remove Local Variable" ) );
|
|
|
|
GetBlueprintObj()->Modify();
|
|
BPVarAction->DeleteVariable();
|
|
}
|
|
else if (FEdGraphSchemaAction_K2Event* EventAction = SelectionAsEvent())
|
|
{
|
|
const FScopedTransaction Transaction(LOCTEXT( "RemoveEventNode", "Remove EventNode"));
|
|
|
|
GetBlueprintObj()->Modify();
|
|
FBlueprintEditorUtils::RemoveNode(GetBlueprintObj(), EventAction->NodeTemplate);
|
|
}
|
|
else if (SelectionIsCategory())
|
|
{
|
|
TArray<TSharedPtr<FEdGraphSchemaAction>> Actions;
|
|
GraphActionMenu->GetSelectedCategorySubActions(Actions);
|
|
if (Actions.Num())
|
|
{
|
|
FText TransactionTitle;
|
|
|
|
switch((NodeSectionID::Type)Actions[0]->GetSectionID())
|
|
{
|
|
case NodeSectionID::VARIABLE:
|
|
case NodeSectionID::LOCAL_VARIABLE:
|
|
{
|
|
TransactionTitle = LOCTEXT( "BulkRemoveVariables", "Bulk Remove Variables" );
|
|
break;
|
|
}
|
|
case NodeSectionID::DELEGATE:
|
|
{
|
|
TransactionTitle = LOCTEXT( "BulkRemoveDelegates", "Bulk Remove Delegates" );
|
|
break;
|
|
}
|
|
case NodeSectionID::FUNCTION:
|
|
{
|
|
TransactionTitle = LOCTEXT( "BulkRemoveFunctions", "Bulk Remove Functions" );
|
|
break;
|
|
}
|
|
case NodeSectionID::MACRO:
|
|
{
|
|
TransactionTitle = LOCTEXT( "BulkRemoveMacros", "Bulk Remove Macros" );
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
TransactionTitle = LOCTEXT( "BulkRemove", "Bulk Remove Items" );
|
|
}
|
|
}
|
|
|
|
FScopedTransaction Transaction( TransactionTitle);
|
|
|
|
bool bModified = false;
|
|
|
|
GetBlueprintObj()->Modify();
|
|
for (int32 i = 0; i < Actions.Num(); ++i)
|
|
{
|
|
if (Actions[i]->GetTypeId() == FEdGraphSchemaAction_K2Var::StaticGetTypeId())
|
|
{
|
|
FEdGraphSchemaAction_K2Var* Var = (FEdGraphSchemaAction_K2Var*)Actions[i].Get();
|
|
|
|
FBlueprintEditorUtils::RemoveMemberVariable(GetBlueprintObj(), Var->GetVariableName());
|
|
bModified = true;
|
|
}
|
|
else if (Actions[i]->GetTypeId() == FEdGraphSchemaAction_K2LocalVar::StaticGetTypeId())
|
|
{
|
|
FEdGraphSchemaAction_K2LocalVar* K2LocalVarAction = (FEdGraphSchemaAction_K2LocalVar*)Actions[i].Get();
|
|
|
|
FBlueprintEditorUtils::RemoveLocalVariable(GetBlueprintObj(), CastChecked<UStruct>(K2LocalVarAction->GetVariableScope()), K2LocalVarAction->GetVariableName());
|
|
bModified = true;
|
|
}
|
|
else if (Actions[i]->GetTypeId() == FEdGraphSchemaAction_K2Graph::StaticGetTypeId())
|
|
{
|
|
FEdGraphSchemaAction_K2Graph* K2GraphAction = (FEdGraphSchemaAction_K2Graph*)Actions[i].Get();
|
|
if(K2GraphAction->EdGraph->bAllowDeletion)
|
|
{
|
|
OnDeleteGraph(K2GraphAction->EdGraph, K2GraphAction->GraphType);
|
|
bModified = true;
|
|
}
|
|
}
|
|
else if (Actions[i]->GetTypeId() == FEdGraphSchemaAction_K2Delegate::StaticGetTypeId())
|
|
{
|
|
OnDeleteDelegate((FEdGraphSchemaAction_K2Delegate*)Actions[i].Get());
|
|
bModified = true;
|
|
}
|
|
}
|
|
|
|
if(!bModified)
|
|
{
|
|
Transaction.Cancel();
|
|
}
|
|
}
|
|
}
|
|
|
|
Refresh();
|
|
BlueprintEditorPtr.Pin()->GetInspector()->ShowDetailsForObjects(TArray<UObject*>());
|
|
}
|
|
|
|
struct FDeleteEntryHelper
|
|
{
|
|
static bool CanDeleteVariable(const UBlueprint* Blueprint, FName VarName)
|
|
{
|
|
check(NULL != Blueprint);
|
|
|
|
const FProperty* VariableProperty = FindFProperty<FProperty>(Blueprint->SkeletonGeneratedClass, VarName);
|
|
const UClass* VarSourceClass = VariableProperty->GetOwnerChecked<const UClass>();
|
|
const bool bIsBlueprintVariable = (VarSourceClass == Blueprint->SkeletonGeneratedClass);
|
|
const int32 VarInfoIndex = FBlueprintEditorUtils::FindNewVariableIndex(Blueprint, VariableProperty->GetFName());
|
|
const bool bHasVarInfo = (VarInfoIndex != INDEX_NONE);
|
|
|
|
return bIsBlueprintVariable && bHasVarInfo;
|
|
}
|
|
};
|
|
|
|
bool SMyBlueprint::CanDeleteEntry() const
|
|
{
|
|
// Cannot delete entries while not in editing mode
|
|
if(!IsEditingMode())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (FEdGraphSchemaAction_K2Graph* GraphAction = SelectionAsGraph())
|
|
{
|
|
return (GraphAction->EdGraph ? GraphAction->EdGraph->bAllowDeletion : false);
|
|
}
|
|
else if (FEdGraphSchemaAction_K2Delegate* DelegateAction = SelectionAsDelegate())
|
|
{
|
|
return (DelegateAction->EdGraph != nullptr) && (DelegateAction->EdGraph->bAllowDeletion) &&
|
|
FDeleteEntryHelper::CanDeleteVariable(GetBlueprintObj(), DelegateAction->GetDelegateName());
|
|
}
|
|
else if (FEdGraphSchemaAction_K2Var* VarAction = SelectionAsVar())
|
|
{
|
|
return FDeleteEntryHelper::CanDeleteVariable(GetBlueprintObj(), VarAction->GetVariableName());
|
|
}
|
|
else if (FEdGraphSchemaAction_K2Event* EventAction = SelectionAsEvent())
|
|
{
|
|
return EventAction->NodeTemplate != nullptr;
|
|
}
|
|
else if (FEdGraphSchemaAction_K2LocalVar* LocalVariable = SelectionAsLocalVar())
|
|
{
|
|
return true;
|
|
}
|
|
else if (FEdGraphSchemaAction_BlueprintVariableBase* BPVariable = SelectionAsBlueprintVariable())
|
|
{
|
|
return true;
|
|
}
|
|
else if (SelectionIsCategory())
|
|
{
|
|
// Can't delete categories if they can't be renamed, that means they are native
|
|
if(GraphActionMenu->CanRequestRenameOnActionNode())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
else if (FEdGraphSchemaAction* Action = SelectionAsType<FEdGraphSchemaAction>(GraphActionMenu))
|
|
{
|
|
return Action->CanBeDeleted();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool SMyBlueprint::IsDuplicateActionVisible() const
|
|
{
|
|
if (FEdGraphSchemaAction_K2Graph* GraphAction = SelectionAsGraph())
|
|
{
|
|
// Functions in interface Blueprints cannot be duplicated
|
|
if(GetBlueprintObj()->BlueprintType != BPTYPE_Interface)
|
|
{
|
|
// Only display it for valid function graphs
|
|
return GraphAction->EdGraph && GraphAction->EdGraph->GetSchema()->CanDuplicateGraph(GraphAction->EdGraph);
|
|
}
|
|
}
|
|
else if (SelectionAsVar() || SelectionAsLocalVar())
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool SMyBlueprint::CanDuplicateAction() const
|
|
{
|
|
// Cannot delete entries while not in editing mode
|
|
if (!IsEditingMode())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (FEdGraphSchemaAction_K2Graph* GraphAction = SelectionAsGraph())
|
|
{
|
|
// Only support function graph duplication
|
|
if(GraphAction->EdGraph)
|
|
{
|
|
return GraphAction->EdGraph->GetSchema()->CanDuplicateGraph(GraphAction->EdGraph);
|
|
}
|
|
}
|
|
else if(FEdGraphSchemaAction_K2Var* VarAction = SelectionAsVar())
|
|
{
|
|
// if the property is not an allowable Blueprint variable type, do not allow the variable to be duplicated.
|
|
// Some actions (timelines) exist as variables but cannot be used in a user-defined variable.
|
|
const FObjectPropertyBase* ObjectProperty = CastField<const FObjectPropertyBase>(VarAction->GetProperty());
|
|
if (ObjectProperty &&
|
|
ObjectProperty->PropertyClass &&
|
|
!UEdGraphSchema_K2::IsAllowableBlueprintVariableType(ObjectProperty->PropertyClass))
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
else if(SelectionAsBlueprintVariable())
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void SMyBlueprint::OnDuplicateAction()
|
|
{
|
|
FName DuplicateActionName = NAME_None;
|
|
|
|
if (FEdGraphSchemaAction_K2Graph* GraphAction = SelectionAsGraph())
|
|
{
|
|
// Only StateMachine, function, anim graph and macro duplication is supported
|
|
EGraphType GraphType = GraphAction->EdGraph->GetSchema()->GetGraphType(GraphAction->EdGraph);
|
|
check(GraphType == GT_StateMachine || GraphType == GT_Function || GraphType == GT_Macro || GraphType == GT_Animation);
|
|
|
|
if (GraphType == GT_StateMachine)
|
|
{
|
|
// StateMachine is handled using the BlueprintEditor copy / paste functionality
|
|
if (const UAnimationStateMachineGraph* AnimationStateMachineGraph = Cast<UAnimationStateMachineGraph>(GraphAction->EdGraph))
|
|
{
|
|
BlueprintEditorPtr.Pin()->SelectAndDuplicateNode(AnimationStateMachineGraph->OwnerAnimGraphNode.Get());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const FScopedTransaction Transaction(LOCTEXT("DuplicateGraph", "Duplicate Graph"));
|
|
UBlueprint* BlueprintObj = GetBlueprintObj();
|
|
UEdGraph* GraphToDuplicate = GraphAction->EdGraph;
|
|
|
|
BlueprintObj->Modify();
|
|
|
|
UEdGraph* DuplicatedGraph = GraphToDuplicate->GetSchema()->DuplicateGraph(GraphToDuplicate);
|
|
check(DuplicatedGraph);
|
|
|
|
DuplicatedGraph->Modify();
|
|
|
|
// Generate new Guids and component templates for all relevant nodes in the graph
|
|
// *NOTE* this cannot occur during PostDuplicate, node Guids and component templates need to remain static during duplication for Blueprint compilation
|
|
for (UEdGraphNode* EdGraphNode : DuplicatedGraph->Nodes)
|
|
{
|
|
if (EdGraphNode)
|
|
{
|
|
EdGraphNode->CreateNewGuid();
|
|
|
|
if (UK2Node_AddComponent* AddComponentNode = Cast<UK2Node_AddComponent>(EdGraphNode))
|
|
{
|
|
AddComponentNode->MakeNewComponentTemplate();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (GraphType == GT_Function || GraphType == GT_Animation)
|
|
{
|
|
BlueprintObj->FunctionGraphs.Add(DuplicatedGraph);
|
|
}
|
|
else if (GraphType == GT_Macro)
|
|
{
|
|
BlueprintObj->MacroGraphs.Add(DuplicatedGraph);
|
|
}
|
|
|
|
// Now that we've taken ownership of the graph (assigned it into macrographs
|
|
// or functiongraphs, as appropriate) it will be properly categorized
|
|
// and we can run post rename logic reliably:
|
|
FName NewGraphName = FBlueprintEditorUtils::FindUniqueKismetName(BlueprintObj, GraphToDuplicate->GetFName().GetPlainNameString());
|
|
FBlueprintEditorUtils::RenameGraph(DuplicatedGraph, NewGraphName.ToString());
|
|
|
|
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(BlueprintObj);
|
|
|
|
BlueprintEditorPtr.Pin()->OpenDocument(DuplicatedGraph, FDocumentTracker::ForceOpenNewDocument);
|
|
DuplicateActionName = DuplicatedGraph->GetFName();
|
|
}
|
|
}
|
|
else if (FEdGraphSchemaAction_K2Var* VarAction = SelectionAsVar())
|
|
{
|
|
const FScopedTransaction Transaction( LOCTEXT( "DuplicateVariable", "Duplicate Variable" ) );
|
|
GetBlueprintObj()->Modify();
|
|
|
|
DuplicateActionName = FBlueprintEditorUtils::DuplicateVariable(GetBlueprintObj(), nullptr, VarAction->GetVariableName());
|
|
if(DuplicateActionName == NAME_None)
|
|
{
|
|
// the variable was probably inherited from a C++ class
|
|
|
|
FEdGraphPinType VarPinType;
|
|
GetDefault<UEdGraphSchema_K2>()->ConvertPropertyToPinType(VarAction->GetProperty(), VarPinType);
|
|
FBlueprintEditorUtils::AddMemberVariable(GetBlueprintObj(), FBlueprintEditorUtils::FindUniqueKismetName(Blueprint, VarAction->GetVariableName().ToString()), VarPinType);
|
|
}
|
|
}
|
|
else if (FEdGraphSchemaAction_K2LocalVar* LocalVarAction = SelectionAsLocalVar())
|
|
{
|
|
const FScopedTransaction Transaction( LOCTEXT( "Duplicate Local Variable", "Duplicate Local Variable" ) );
|
|
GetBlueprintObj()->Modify();
|
|
|
|
DuplicateActionName = FBlueprintEditorUtils::DuplicateVariable(GetBlueprintObj(), Cast<UStruct>(LocalVarAction->GetVariableScope()), LocalVarAction->GetVariableName());
|
|
}
|
|
|
|
// Select and rename the duplicated action
|
|
if(DuplicateActionName != NAME_None)
|
|
{
|
|
SelectItemByName(DuplicateActionName);
|
|
Refresh();
|
|
OnRequestRenameOnActionNode();
|
|
}
|
|
}
|
|
|
|
void SMyBlueprint::GotoNativeCodeVarDefinition()
|
|
{
|
|
if( FEdGraphSchemaAction_K2Var* VarAction = SelectionAsVar() )
|
|
{
|
|
if( FProperty* VarProperty = VarAction->GetProperty() )
|
|
{
|
|
FSourceCodeNavigation::NavigateToProperty( VarProperty );
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SMyBlueprint::IsNativeVariable() const
|
|
{
|
|
if( FEdGraphSchemaAction_K2Var* VarAction = SelectionAsVar() )
|
|
{
|
|
FProperty* VarProperty = VarAction->GetProperty();
|
|
|
|
if( VarProperty && VarProperty->IsNative())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void SMyBlueprint::OnMoveToParent()
|
|
{
|
|
if (UBlueprint* ParentBlueprint = UBlueprint::GetBlueprintFromClass(Blueprint->ParentClass))
|
|
{
|
|
if (FEdGraphSchemaAction_K2Var* VarAction = SelectionAsVar())
|
|
{
|
|
int32 VarIndex = FBlueprintEditorUtils::FindNewVariableIndex(Blueprint, VarAction->GetVariableName());
|
|
if (VarIndex != INDEX_NONE)
|
|
{
|
|
FScopedTransaction Transaction(LOCTEXT("MoveVarToParent", "Move Variable To Parent"));
|
|
Blueprint->Modify();
|
|
ParentBlueprint->Modify();
|
|
|
|
FBPVariableDescription VarDesc = Blueprint->NewVariables[VarIndex];
|
|
Blueprint->NewVariables.RemoveAt(VarIndex);
|
|
|
|
// We need to manually pull the DefaultValue from the FProperty to set it on the new parent class variable
|
|
{
|
|
// Grab property off blueprint's current CDO
|
|
UClass* GeneratedClass = Blueprint->GeneratedClass;
|
|
UObject* GeneratedCDO = GeneratedClass->GetDefaultObject();
|
|
|
|
if (FProperty* TargetProperty = FindFProperty<FProperty>(GeneratedClass, VarDesc.VarName))
|
|
{
|
|
if (void* OldPropertyAddr = TargetProperty->ContainerPtrToValuePtr<void>(GeneratedCDO))
|
|
{
|
|
// If there is a property for variable, it means the original default value was already copied, so it can be safely overridden
|
|
VarDesc.DefaultValue.Empty();
|
|
TargetProperty->ExportTextItem_Direct(VarDesc.DefaultValue, OldPropertyAddr, OldPropertyAddr, nullptr, PPF_SerializedAsImportText);
|
|
}
|
|
}
|
|
}
|
|
|
|
ParentBlueprint->NewVariables.Add(VarDesc);
|
|
|
|
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Blueprint);
|
|
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(ParentBlueprint);
|
|
FBlueprintEditorUtils::ValidateBlueprintChildVariables(ParentBlueprint, VarDesc.VarName);
|
|
}
|
|
}
|
|
else if (FEdGraphSchemaAction_K2Graph* GraphAction = SelectionAsGraph())
|
|
{
|
|
TSharedPtr<FBlueprintEditor> PinnedEditor = BlueprintEditorPtr.Pin();
|
|
if (PinnedEditor.IsValid())
|
|
{
|
|
FScopedTransaction Transaction(LOCTEXT("MoveFuncToParent", "Move Function To Parent"));
|
|
Blueprint->Modify();
|
|
ParentBlueprint->Modify();
|
|
|
|
FBPGraphClipboardData CopiedGraph(GraphAction->EdGraph);
|
|
if (CopiedGraph.CreateAndPopulateGraph(ParentBlueprint, CopiedGraph.GetOriginalBlueprint(), PinnedEditor.Get(), GraphAction->GetCategory()))
|
|
{
|
|
PinnedEditor->CloseDocumentTab(GraphAction->EdGraph);
|
|
OnDeleteEntry();
|
|
}
|
|
}
|
|
}
|
|
|
|
// open editor for parent blueprint
|
|
if (UObject* ParentClass = Blueprint->ParentClass)
|
|
{
|
|
UBlueprintGeneratedClass* ParentBlueprintGeneratedClass = Cast<UBlueprintGeneratedClass>(ParentClass);
|
|
if (ParentBlueprintGeneratedClass != NULL)
|
|
{
|
|
GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->OpenEditorForAsset(ParentBlueprintGeneratedClass->ClassGeneratedBy);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SMyBlueprint::CanMoveVariableToParent() const
|
|
{
|
|
bool bCanMove = false;
|
|
|
|
TSharedPtr<FBlueprintEditor> PinnedEditor = BlueprintEditorPtr.Pin();
|
|
if (PinnedEditor.IsValid() && PinnedEditor->IsParentClassABlueprint())
|
|
{
|
|
if (FEdGraphSchemaAction_K2Var* VarAction = SelectionAsVar())
|
|
{
|
|
// If this variable is new to this class
|
|
UBlueprint* SourceBlueprint;
|
|
int32 VarIndex = FBlueprintEditorUtils::FindNewVariableIndexAndBlueprint(Blueprint, VarAction->GetVariableName(), SourceBlueprint);
|
|
bCanMove = (VarIndex != INDEX_NONE) && (SourceBlueprint == Blueprint);
|
|
}
|
|
}
|
|
|
|
return bCanMove;
|
|
}
|
|
|
|
bool SMyBlueprint::CanMoveFunctionToParent() const
|
|
{
|
|
bool bCanMove = false;
|
|
|
|
TSharedPtr<FBlueprintEditor> PinnedEditor = BlueprintEditorPtr.Pin();
|
|
if (PinnedEditor.IsValid() && PinnedEditor->IsParentClassABlueprint())
|
|
{
|
|
if (FEdGraphSchemaAction_K2Graph* GraphAction = SelectionAsGraph())
|
|
{
|
|
if (CanDeleteEntry())
|
|
{
|
|
return PinnedEditor->IsGraphInCurrentBlueprint(GraphAction->EdGraph) && GraphAction->GraphType == EEdGraphSchemaAction_K2Graph::Function;
|
|
}
|
|
}
|
|
}
|
|
|
|
return bCanMove;
|
|
}
|
|
|
|
void SMyBlueprint::OnCopy()
|
|
{
|
|
FString OutputString;
|
|
|
|
if (FEdGraphSchemaAction_K2Var* VarAction = SelectionAsVar())
|
|
{
|
|
UBlueprint* SourceBlueprint;
|
|
int32 VarIndex = FBlueprintEditorUtils::FindNewVariableIndexAndBlueprint(Blueprint, VarAction->GetVariableName(), SourceBlueprint);
|
|
if (VarIndex != INDEX_NONE)
|
|
{
|
|
// make a copy of the Variable description so we can set the default value
|
|
FBPVariableDescription Description = SourceBlueprint->NewVariables[VarIndex];
|
|
|
|
//Grab property of blueprint's current CDO
|
|
UClass* GeneratedClass = SourceBlueprint->GeneratedClass;
|
|
UObject* GeneratedCDO = GeneratedClass->GetDefaultObject();
|
|
FProperty* TargetProperty = FindFProperty<FProperty>(GeneratedClass, Description.VarName);
|
|
|
|
if (TargetProperty)
|
|
{
|
|
// Grab the address of where the property is actually stored (UObject* base, plus the offset defined in the property)
|
|
void* OldPropertyAddr = TargetProperty->ContainerPtrToValuePtr<void>(GeneratedCDO);
|
|
if (OldPropertyAddr)
|
|
{
|
|
TargetProperty->ExportTextItem_Direct(Description.DefaultValue, OldPropertyAddr, OldPropertyAddr, nullptr, PPF_SerializedAsImportText);
|
|
}
|
|
}
|
|
|
|
FBPVariableDescription::StaticStruct()->ExportText(OutputString, &Description, &Description, nullptr, 0, nullptr, false);
|
|
OutputString = VAR_PREFIX + OutputString;
|
|
}
|
|
}
|
|
else if (FEdGraphSchemaAction_K2LocalVar* LocalVarAction = SelectionAsLocalVar())
|
|
{
|
|
FBPVariableDescription* Description = FBlueprintEditorUtils::FindLocalVariable(Blueprint, CastChecked<UStruct>(LocalVarAction->GetVariableScope()), LocalVarAction->GetVariableName());
|
|
|
|
if (Description)
|
|
{
|
|
FBPVariableDescription::StaticStruct()->ExportText(OutputString, Description, Description, nullptr, 0, nullptr, false);
|
|
OutputString = VAR_PREFIX + OutputString;
|
|
}
|
|
}
|
|
else if (FEdGraphSchemaAction_BlueprintVariableBase* BPVariable = SelectionAsBlueprintVariable())
|
|
{
|
|
if (const UEdGraph* FocusedGraph = Cast<UEdGraph>(BPVariable->GetVariableScope()))
|
|
{
|
|
if (const UEdGraphSchema* Schema = FocusedGraph->GetSchema())
|
|
{
|
|
TArray<FBPVariableDescription> LocalVariables;
|
|
Schema->GetLocalVariables(FocusedGraph, LocalVariables);
|
|
for (const FBPVariableDescription& VariableDescription : LocalVariables)
|
|
{
|
|
if (VariableDescription.VarName == BPVariable->GetVariableName())
|
|
{
|
|
FBPVariableDescription::StaticStruct()->ExportText(OutputString, &VariableDescription, &VariableDescription, nullptr, 0, nullptr, false);
|
|
OutputString = VAR_PREFIX + OutputString;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (FEdGraphSchemaAction_K2Graph* GraphAction = SelectionAsGraph())
|
|
{
|
|
if (!Blueprint->ExportGraphToText(GraphAction->EdGraph, OutputString))
|
|
{
|
|
FBPGraphClipboardData FuncData(GraphAction->EdGraph);
|
|
FBPGraphClipboardData::StaticStruct()->ExportText(OutputString, &FuncData, &FuncData, nullptr, 0, nullptr, false);
|
|
OutputString = GRAPH_PREFIX + OutputString;
|
|
}
|
|
}
|
|
|
|
if (!OutputString.IsEmpty())
|
|
{
|
|
FPlatformApplicationMisc::ClipboardCopy(OutputString.GetCharArray().GetData());
|
|
}
|
|
}
|
|
|
|
bool SMyBlueprint::CanCopy() const
|
|
{
|
|
if (FEdGraphSchemaAction_K2Var* VarAction = SelectionAsVar())
|
|
{
|
|
return FBlueprintEditorUtils::FindNewVariableIndex(Blueprint, VarAction->GetVariableName()) != INDEX_NONE;
|
|
}
|
|
else if (FEdGraphSchemaAction_K2LocalVar* LocalVarAction = SelectionAsLocalVar())
|
|
{
|
|
return FBlueprintEditorUtils::FindLocalVariable(Blueprint, Cast<UStruct>(LocalVarAction->GetVariableScope()), LocalVarAction->GetVariableName()) != nullptr;
|
|
}
|
|
else if (FEdGraphSchemaAction_BlueprintVariableBase* BPVariable = SelectionAsBlueprintVariable())
|
|
{
|
|
if (const UEdGraph* FocusedGraph = Cast<UEdGraph>(BPVariable->GetVariableScope()))
|
|
{
|
|
if (const UEdGraphSchema* Schema = FocusedGraph->GetSchema())
|
|
{
|
|
TArray<FBPVariableDescription> LocalVariables;
|
|
Schema->GetLocalVariables(FocusedGraph, LocalVariables);
|
|
|
|
for (const FBPVariableDescription& VariableDescription : LocalVariables)
|
|
{
|
|
if (VariableDescription.VarName == BPVariable->GetVariableName())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
else if (FEdGraphSchemaAction_K2Graph* GraphAction = SelectionAsGraph())
|
|
{
|
|
if (GraphAction->GraphType == EEdGraphSchemaAction_K2Graph::Function ||
|
|
GraphAction->GraphType == EEdGraphSchemaAction_K2Graph::Macro)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void SMyBlueprint::OnCut()
|
|
{
|
|
OnCopy();
|
|
OnDeleteEntry();
|
|
}
|
|
|
|
bool SMyBlueprint::CanCut() const
|
|
{
|
|
return CanCopy() && CanDeleteEntry();
|
|
}
|
|
|
|
void SMyBlueprint::OnPasteGeneric()
|
|
{
|
|
// prioritize pasting as a member variable if possible
|
|
if (CanPasteVariable())
|
|
{
|
|
OnPasteVariable();
|
|
}
|
|
else if (CanPasteLocalVariable())
|
|
{
|
|
OnPasteLocalVariable();
|
|
}
|
|
else if (CanPasteFunction())
|
|
{
|
|
OnPasteFunction();
|
|
}
|
|
else if (CanPasteMacro())
|
|
{
|
|
OnPasteMacro();
|
|
}
|
|
}
|
|
|
|
bool SMyBlueprint::CanPasteGeneric()
|
|
{
|
|
return CanPasteVariable() || CanPasteLocalVariable() || CanPasteFunction() || CanPasteMacro();
|
|
}
|
|
|
|
void SMyBlueprint::OnPasteVariable()
|
|
{
|
|
FString ClipboardText;
|
|
FPlatformApplicationMisc::ClipboardPaste(ClipboardText);
|
|
if (!ensure(ClipboardText.StartsWith(VAR_PREFIX, ESearchCase::CaseSensitive)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
FBPVariableDescription Description;
|
|
FStringOutputDevice Errors;
|
|
const TCHAR* Import = ClipboardText.GetCharArray().GetData() + FCString::Strlen(VAR_PREFIX);
|
|
FBPVariableDescription::StaticStruct()->ImportText(Import, &Description, nullptr, PPF_None, &Errors, FBPVariableDescription::StaticStruct()->GetName());
|
|
if (Errors.IsEmpty())
|
|
{
|
|
FBPVariableDescription NewVar = FBlueprintEditorUtils::DuplicateVariableDescription(Blueprint, Description);
|
|
if (NewVar.VarGuid.IsValid())
|
|
{
|
|
FScopedTransaction Transaction(FText::Format(LOCTEXT("PasteVariable", "Paste Variable: {0}"), FText::FromName(NewVar.VarName)));
|
|
Blueprint->Modify();
|
|
|
|
NewVar.Category = GetPasteCategory();
|
|
|
|
Blueprint->NewVariables.Add(NewVar);
|
|
|
|
// Potentially adjust variable names for any child blueprints
|
|
FBlueprintEditorUtils::ValidateBlueprintChildVariables(Blueprint, NewVar.VarName);
|
|
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Blueprint);
|
|
|
|
SelectItemByName(NewVar.VarName);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SMyBlueprint::OnPasteLocalVariable()
|
|
{
|
|
TSharedPtr<FBlueprintEditor> PinnedEditor = BlueprintEditorPtr.Pin();
|
|
if (PinnedEditor.IsValid())
|
|
{
|
|
UEdGraph* FocusedGraph = PinnedEditor->GetFocusedGraph();
|
|
if (FocusedGraph)
|
|
{
|
|
TArray<UK2Node_FunctionEntry*> FunctionEntry;
|
|
FocusedGraph->GetNodesOfClass<UK2Node_FunctionEntry>(FunctionEntry);
|
|
|
|
FString ClipboardText;
|
|
FPlatformApplicationMisc::ClipboardPaste(ClipboardText);
|
|
if (!ensure(ClipboardText.StartsWith(VAR_PREFIX, ESearchCase::CaseSensitive)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
FBPVariableDescription Description;
|
|
FStringOutputDevice Errors;
|
|
const TCHAR* Import = ClipboardText.GetCharArray().GetData() + FCString::Strlen(VAR_PREFIX);
|
|
FBPVariableDescription::StaticStruct()->ImportText(Import, &Description, nullptr, 0, &Errors, FBPVariableDescription::StaticStruct()->GetName());
|
|
if (Errors.IsEmpty())
|
|
{
|
|
FBPVariableDescription NewVar = FBlueprintEditorUtils::DuplicateVariableDescription(Blueprint, Description);
|
|
if (NewVar.VarGuid.IsValid())
|
|
{
|
|
FScopedTransaction Transaction(FText::Format(LOCTEXT("PasteLocalVariable", "Paste Local Variable: {0}"), FText::FromName(NewVar.VarName)));
|
|
|
|
NewVar.Category = GetPasteCategory();
|
|
|
|
|
|
if (FunctionEntry.Num() == 1)
|
|
{
|
|
FunctionEntry[0]->Modify();
|
|
FunctionEntry[0]->LocalVariables.Add(NewVar);
|
|
}
|
|
else
|
|
{
|
|
BlueprintEditorPtr.Pin()->OnPasteNewLocalVariable(NewVar);
|
|
}
|
|
|
|
// Potentially adjust variable names for any child blueprints
|
|
FBlueprintEditorUtils::ValidateBlueprintChildVariables(Blueprint, NewVar.VarName);
|
|
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Blueprint);
|
|
|
|
SelectItemByName(NewVar.VarName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SMyBlueprint::CanPasteVariable() const
|
|
{
|
|
TSharedPtr<FBlueprintEditor> PinnedEditor = BlueprintEditorPtr.Pin();
|
|
if (PinnedEditor.IsValid() && !PinnedEditor->NewDocument_IsVisibleForType(FBlueprintEditor::CGT_NewVariable))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
FString ClipboardText;
|
|
FPlatformApplicationMisc::ClipboardPaste(ClipboardText);
|
|
if (ClipboardText.StartsWith(VAR_PREFIX, ESearchCase::CaseSensitive))
|
|
{
|
|
FBPVariableDescription Description;
|
|
FStringOutputDevice Errors;
|
|
const TCHAR* Import = ClipboardText.GetCharArray().GetData() + FCString::Strlen(VAR_PREFIX);
|
|
FBPVariableDescription::StaticStruct()->ImportText(Import, &Description, nullptr, 0, &Errors, FBPVariableDescription::StaticStruct()->GetName());
|
|
|
|
return Errors.IsEmpty();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool SMyBlueprint::CanPasteLocalVariable() const
|
|
{
|
|
TSharedPtr<FBlueprintEditor> PinnedEditor = BlueprintEditorPtr.Pin();
|
|
if (PinnedEditor.IsValid() && !PinnedEditor->NewDocument_IsVisibleForType(FBlueprintEditor::CGT_NewLocalVariable))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
FString ClipboardText;
|
|
FPlatformApplicationMisc::ClipboardPaste(ClipboardText);
|
|
if (ClipboardText.StartsWith(VAR_PREFIX, ESearchCase::CaseSensitive))
|
|
{
|
|
FBPVariableDescription Description;
|
|
FStringOutputDevice Errors;
|
|
const TCHAR* Import = ClipboardText.GetCharArray().GetData() + FCString::Strlen(VAR_PREFIX);
|
|
FBPVariableDescription::StaticStruct()->ImportText(Import, &Description, nullptr, 0, &Errors, FBPVariableDescription::StaticStruct()->GetName());
|
|
|
|
return Errors.IsEmpty();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void SMyBlueprint::OnPasteFunction()
|
|
{
|
|
FString ClipboardText;
|
|
FPlatformApplicationMisc::ClipboardPaste(ClipboardText);
|
|
|
|
if (Blueprint->TryImportGraphFromText(ClipboardText))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!ensure(ClipboardText.StartsWith(GRAPH_PREFIX, ESearchCase::CaseSensitive)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
FBPGraphClipboardData FuncData;
|
|
FStringOutputDevice Errors;
|
|
const TCHAR* Import = ClipboardText.GetCharArray().GetData() + FCString::Strlen(GRAPH_PREFIX);
|
|
FBPGraphClipboardData::StaticStruct()->ImportText(Import, &FuncData, nullptr, 0, &Errors, FBPGraphClipboardData::StaticStruct()->GetName());
|
|
if (Errors.IsEmpty() && FuncData.IsValid())
|
|
{
|
|
FScopedTransaction Transaction(LOCTEXT("PasteFunction", "Paste Function"));
|
|
|
|
TSharedPtr<FBlueprintEditor> PinnedEditor = BlueprintEditorPtr.Pin();
|
|
if (PinnedEditor.IsValid())
|
|
{
|
|
Blueprint->Modify();
|
|
UEdGraph* Graph = FuncData.CreateAndPopulateGraph(Blueprint, FuncData.GetOriginalBlueprint(), PinnedEditor.Get(), GetPasteCategory());
|
|
|
|
if (Graph)
|
|
{
|
|
PinnedEditor->OpenDocument(Graph, FDocumentTracker::OpenNewDocument);
|
|
SelectItemByName(Graph->GetFName());
|
|
Refresh();
|
|
OnRequestRenameOnActionNode();
|
|
}
|
|
else
|
|
{
|
|
Transaction.Cancel();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SMyBlueprint::CanPasteFunction() const
|
|
{
|
|
TSharedPtr<FBlueprintEditor> PinnedEditor = BlueprintEditorPtr.Pin();
|
|
if (PinnedEditor.IsValid() && !PinnedEditor->NewDocument_IsVisibleForType(FBlueprintEditor::CGT_NewFunctionGraph))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
FString ClipboardText;
|
|
FPlatformApplicationMisc::ClipboardPaste(ClipboardText);
|
|
|
|
if (Blueprint->CanImportGraphFromText(ClipboardText))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (ClipboardText.StartsWith(GRAPH_PREFIX, ESearchCase::CaseSensitive))
|
|
{
|
|
FBPGraphClipboardData FuncData;
|
|
FStringOutputDevice Errors;
|
|
const TCHAR* Import = ClipboardText.GetCharArray().GetData() + FCString::Strlen(GRAPH_PREFIX);
|
|
FBPGraphClipboardData::StaticStruct()->ImportText(Import, &FuncData, nullptr, 0, &Errors, FBPGraphClipboardData::StaticStruct()->GetName());
|
|
|
|
return Errors.IsEmpty() && FuncData.IsFunction();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void SMyBlueprint::OnPasteMacro()
|
|
{
|
|
FString ClipboardText;
|
|
FPlatformApplicationMisc::ClipboardPaste(ClipboardText);
|
|
if (!ensure(ClipboardText.StartsWith(GRAPH_PREFIX, ESearchCase::CaseSensitive)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
FBPGraphClipboardData FuncData;
|
|
FStringOutputDevice Errors;
|
|
const TCHAR* Import = ClipboardText.GetCharArray().GetData() + FCString::Strlen(GRAPH_PREFIX);
|
|
FBPGraphClipboardData::StaticStruct()->ImportText(Import, &FuncData, nullptr, 0, &Errors, FBPGraphClipboardData::StaticStruct()->GetName());
|
|
if (Errors.IsEmpty() && FuncData.IsValid())
|
|
{
|
|
FScopedTransaction Transaction(LOCTEXT("PasteMacro", "Paste Macro"));
|
|
|
|
TSharedPtr<FBlueprintEditor> PinnedEditor = BlueprintEditorPtr.Pin();
|
|
if (PinnedEditor.IsValid())
|
|
{
|
|
Blueprint->Modify();
|
|
UEdGraph* Graph = FuncData.CreateAndPopulateGraph(Blueprint, FuncData.GetOriginalBlueprint(), PinnedEditor.Get(), GetPasteCategory());
|
|
|
|
if (Graph)
|
|
{
|
|
PinnedEditor->OpenDocument(Graph, FDocumentTracker::OpenNewDocument);
|
|
SelectItemByName(Graph->GetFName());
|
|
OnRequestRenameOnActionNode();
|
|
}
|
|
else
|
|
{
|
|
Transaction.Cancel();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SMyBlueprint::CanPasteMacro() const
|
|
{
|
|
TSharedPtr<FBlueprintEditor> PinnedEditor = BlueprintEditorPtr.Pin();
|
|
if (PinnedEditor.IsValid() && !PinnedEditor->NewDocument_IsVisibleForType(FBlueprintEditor::CGT_NewMacroGraph))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
FString ClipboardText;
|
|
FPlatformApplicationMisc::ClipboardPaste(ClipboardText);
|
|
if (ClipboardText.StartsWith(GRAPH_PREFIX, ESearchCase::CaseSensitive))
|
|
{
|
|
FBPGraphClipboardData MacroData;
|
|
FStringOutputDevice Errors;
|
|
const TCHAR* Import = ClipboardText.GetCharArray().GetData() + FCString::Strlen(GRAPH_PREFIX);
|
|
FBPGraphClipboardData::StaticStruct()->ImportText(Import, &MacroData, nullptr, 0, &Errors, FBPGraphClipboardData::StaticStruct()->GetName());
|
|
|
|
return Errors.IsEmpty() && MacroData.IsMacro();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
FText SMyBlueprint::GetPasteCategory() const
|
|
{
|
|
if (SelectionIsCategory() && GraphActionMenu.IsValid())
|
|
{
|
|
FString CategoryName = GraphActionMenu->GetSelectedCategoryName();
|
|
if (!CategoryName.IsEmpty())
|
|
{
|
|
return FText::FromString(GraphActionMenu->GetSelectedCategoryName());
|
|
}
|
|
}
|
|
|
|
return UEdGraphSchema_K2::VR_DefaultCategory;
|
|
}
|
|
|
|
void SMyBlueprint::OnResetItemFilter()
|
|
{
|
|
FilterBox->SetText(FText::GetEmpty());
|
|
}
|
|
|
|
void SMyBlueprint::EnsureLastPinTypeValid()
|
|
{
|
|
LastPinType.bIsWeakPointer = false;
|
|
LastFunctionPinType.bIsWeakPointer = false;
|
|
|
|
const bool bLastPinTypeValid = (UEdGraphSchema_K2::PC_Struct != LastPinType.PinCategory) || LastPinType.PinSubCategoryObject.IsValid();
|
|
const bool bLastFunctionPinTypeValid = (UEdGraphSchema_K2::PC_Struct != LastFunctionPinType.PinCategory) || LastFunctionPinType.PinSubCategoryObject.IsValid();
|
|
const bool bConstType = LastPinType.bIsConst || LastFunctionPinType.bIsConst;
|
|
if (!bLastPinTypeValid || !bLastFunctionPinTypeValid || bConstType)
|
|
{
|
|
ResetLastPinType();
|
|
}
|
|
}
|
|
|
|
void SMyBlueprint::ResetLastPinType()
|
|
{
|
|
LastPinType.ResetToDefaults();
|
|
LastPinType.PinCategory = UEdGraphSchema_K2::PC_Boolean;
|
|
LastFunctionPinType = LastPinType;
|
|
}
|
|
|
|
void SMyBlueprint::UpdateNodeCreation()
|
|
{
|
|
if( BlueprintEditorPtr.IsValid() )
|
|
{
|
|
BlueprintEditorPtr.Pin()->UpdateNodeCreationStats( ENodeCreateAction::MyBlueprintDragPlacement );
|
|
}
|
|
}
|
|
|
|
FReply SMyBlueprint::OnAddNewLocalVariable()
|
|
{
|
|
if( BlueprintEditorPtr.IsValid() )
|
|
{
|
|
BlueprintEditorPtr.Pin()->OnAddNewLocalVariable();
|
|
}
|
|
|
|
return FReply::Handled();
|
|
}
|
|
|
|
void SMyBlueprint::OnFilterTextChanged( const FText& InFilterText )
|
|
{
|
|
GraphActionMenu->GenerateFilteredItems(false);
|
|
}
|
|
|
|
FText SMyBlueprint::GetFilterText() const
|
|
{
|
|
return FilterBox->GetText();
|
|
}
|
|
|
|
void SMyBlueprint::OnRequestRenameOnActionNode()
|
|
{
|
|
// Attempt to rename in both menus, only one of them will have anything selected
|
|
GraphActionMenu->OnRequestRenameOnActionNode();
|
|
}
|
|
|
|
bool SMyBlueprint::CanRequestRenameOnActionNode() const
|
|
{
|
|
TArray<TSharedPtr<FEdGraphSchemaAction> > SelectedActions;
|
|
GraphActionMenu->GetSelectedActions(SelectedActions);
|
|
|
|
// If there is anything selected in the GraphActionMenu, check the item for if it can be renamed.
|
|
if (SelectedActions.Num() || SelectionIsCategory())
|
|
{
|
|
return GraphActionMenu->CanRequestRenameOnActionNode();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void SMyBlueprint::SelectItemByName(const FName& ItemName, ESelectInfo::Type SelectInfo, int32 SectionId/* = INDEX_NONE*/, bool bIsCategory/* = false*/)
|
|
{
|
|
// Check if the graph action menu is being told to clear
|
|
if(ItemName == NAME_None)
|
|
{
|
|
ClearGraphActionMenuSelection();
|
|
}
|
|
else
|
|
{
|
|
// Attempt to select the item in the main graph action menu
|
|
const bool bSucceededAtSelecting = GraphActionMenu->SelectItemByName(ItemName, SelectInfo, SectionId, bIsCategory);
|
|
if (!bSucceededAtSelecting)
|
|
{
|
|
// We failed to select the item, maybe because it was filtered out?
|
|
// Reset the item filter and try again (we don't do this first because someone went to the effort of typing
|
|
// a filter and probably wants to keep it unless it is getting in the way, as it just has)
|
|
OnResetItemFilter();
|
|
GraphActionMenu->SelectItemByName(ItemName, SelectInfo, SectionId, bIsCategory);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SMyBlueprint::ClearGraphActionMenuSelection()
|
|
{
|
|
GraphActionMenu->SelectItemByName(NAME_None);
|
|
}
|
|
|
|
void SMyBlueprint::ExpandCategory(const FText& CategoryName)
|
|
{
|
|
GraphActionMenu->ExpandCategory(CategoryName);
|
|
}
|
|
|
|
bool SMyBlueprint::MoveCategoryBeforeCategory(const FText& InCategoryToMove, const FText& InTargetCategory)
|
|
{
|
|
bool bResult = false;
|
|
|
|
FString CategoryToMoveString = InCategoryToMove.ToString();
|
|
FString TargetCategoryString = InTargetCategory.ToString();
|
|
if (UBlueprint* BlueprintObj = BlueprintEditorPtr.Pin()->GetBlueprintObj())
|
|
{
|
|
FScopedTransaction Transaction(LOCTEXT("ReorderCategories", "Reorder Categories"));
|
|
BlueprintObj->Modify();
|
|
|
|
// Find root categories
|
|
int32 RootCategoryDelim = CategoryToMoveString.Find(TEXT("|"), ESearchCase::CaseSensitive);
|
|
FName CategoryToMove = RootCategoryDelim == INDEX_NONE ? *CategoryToMoveString : *CategoryToMoveString.Left(RootCategoryDelim);
|
|
RootCategoryDelim = TargetCategoryString.Find(TEXT("|"), ESearchCase::CaseSensitive);
|
|
FName TargetCategory = RootCategoryDelim == INDEX_NONE ? *TargetCategoryString : *TargetCategoryString.Left(RootCategoryDelim);
|
|
|
|
TArray<FName>& CategorySort = BlueprintObj->CategorySorting;
|
|
|
|
// Remove existing sort index
|
|
const int32 RemovalIndex = CategorySort.Find(CategoryToMove);
|
|
if (RemovalIndex != INDEX_NONE)
|
|
{
|
|
CategorySort.RemoveAt(RemovalIndex);
|
|
}
|
|
|
|
// Update the Category sort order and refresh ( if the target category has an entry )
|
|
const int32 InsertIndex = CategorySort.Find(TargetCategory);
|
|
if (InsertIndex != INDEX_NONE)
|
|
{
|
|
CategorySort.Insert(CategoryToMove, InsertIndex);
|
|
Refresh();
|
|
bResult = true;
|
|
}
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|