Files
UnrealEngine/Engine/Source/Editor/Kismet/Private/SBlueprintActionMenu.cpp
2025-05-18 13:04:45 +08:00

786 lines
27 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SBlueprintActionMenu.h"
#include "BlueprintActionFilter.h"
#include "BlueprintActionMenuBuilder.h"
#include "BlueprintActionMenuUtils.h"
#include "BlueprintEditor.h"
#include "BlueprintEditorSettings.h"
#include "BlueprintNamespaceUtilities.h"
#include "BlueprintPaletteFavorites.h"
#include "Containers/EnumAsByte.h"
#include "Containers/SparseArray.h"
#include "CoreGlobals.h"
#include "EdGraph/EdGraph.h"
#include "EdGraph/EdGraphNode.h"
#include "EdGraph/EdGraphPin.h"
#include "EdGraph/EdGraphSchema.h"
#include "EdGraphSchema_K2.h"
#include "EdGraphSchema_K2_Actions.h"
#include "Editor/EditorPerProjectUserSettings.h"
#include "Engine/Blueprint.h"
#include "Fonts/SlateFontInfo.h"
#include "Framework/Application/SlateApplication.h"
#include "HAL/PlatformCrt.h"
#include "IDocumentation.h"
#include "Internationalization/Internationalization.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Layout/Children.h"
#include "Misc/AssertionMacros.h"
#include "Misc/ConfigCacheIni.h"
#include "Misc/Optional.h"
#include "SBlueprintContextTargetMenu.h"
#include "SBlueprintNamespaceEntry.h"
#include "SBlueprintPalette.h"
#include "SGraphActionMenu.h"
#include "SMyBlueprint.h"
#include "SSubobjectEditor.h"
#include "SlotBase.h"
#include "Styling/AppStyle.h"
#include "Styling/SlateColor.h"
#include "Templates/Casts.h"
#include "Templates/SubclassOf.h"
#include "Templates/UnrealTemplate.h"
#include "Types/SlateStructs.h"
#include "UObject/Class.h"
#include "UObject/Field.h"
#include "UObject/GarbageCollection.h"
#include "UObject/NameTypes.h"
#include "UObject/Object.h"
#include "UObject/ObjectMacros.h"
#include "UObject/ObjectPtr.h"
#include "UObject/UObjectGlobals.h"
#include "UObject/UnrealType.h"
#include "Widgets/Images/SImage.h"
#include "Widgets/Input/SCheckBox.h"
#include "Widgets/Input/SComboButton.h"
#include "Widgets/Layout/SBox.h"
#include "Widgets/Notifications/SProgressBar.h"
#include "Widgets/SBoxPanel.h"
#include "Widgets/SCompoundWidget.h"
#include "Widgets/SNullWidget.h"
#include "Widgets/SPanel.h"
#include "Widgets/SToolTip.h"
#include "Widgets/Text/STextBlock.h"
class SWidget;
struct FSlateBrush;
#define LOCTEXT_NAMESPACE "SBlueprintGraphContextMenu"
/** Action to promote a pin to a variable */
USTRUCT()
struct FBlueprintAction_PromoteVariable : public FEdGraphSchemaAction
{
FBlueprintAction_PromoteVariable(bool bInToMemberVariable)
: FEdGraphSchemaAction( FText(),
bInToMemberVariable? LOCTEXT("PromoteToVariable", "Promote to variable") : LOCTEXT("PromoteToLocalVariable", "Promote to local variable"),
bInToMemberVariable ? LOCTEXT("PromoteToVariable", "Promote to variable") : LOCTEXT("PromoteToLocalVariable", "Promote to local variable"),
1)
, bToMemberVariable(bInToMemberVariable)
{
}
// FEdGraphSchemaAction interface
virtual UEdGraphNode* PerformAction( class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2f& Location, bool bSelectNewNode = true) override
{
if( ( ParentGraph != NULL ) && ( FromPin != NULL ) )
{
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(ParentGraph);
if( ( MyBlueprintEditor.IsValid() == true ) && ( Blueprint != NULL ) )
{
MyBlueprintEditor.Pin()->DoPromoteToVariable2f( Blueprint, FromPin, bToMemberVariable, Location);
}
}
return NULL;
}
// End of FEdGraphSchemaAction interface
/* Pointer to the blueprint editor containing the blueprint in which we will promote the variable. */
TWeakPtr<class FBlueprintEditor> MyBlueprintEditor;
/* TRUE if promoting to member variable, FALSE if promoting to local variable */
bool bToMemberVariable;
};
/**
* Static method for binding with delegates. Spawns an instance of the custom
* expander.
*
* @param ActionMenuData A set of useful data for detailing the specific action menu row this is for.
* @return A new widget, intended to lead entries in an SGraphActionMenu.
*/
static TSharedRef<SExpanderArrow> CreateCustomBlueprintActionExpander(const FCustomExpanderData& ActionMenuData)
{
return SNew(SBlueprintActionMenuExpander, ActionMenuData);
}
/*******************************************************************************
* SBlueprintActionFavoriteToggle
*******************************************************************************/
class SBlueprintActionFavoriteToggle : public SCompoundWidget
{
SLATE_BEGIN_ARGS( SBlueprintActionFavoriteToggle ) {}
SLATE_END_ARGS()
public:
/**
* Constructs a favorite-toggle widget (so that user can easily modify the
* item's favorited state).
*
* @param InArgs A set of slate arguments, defined above.
* @param ActionPtrIn The FEdGraphSchemaAction that the parent item represents.
* @param BlueprintEdPtrIn A pointer to the blueprint editor that the palette belongs to.
*/
void Construct(const FArguments& InArgs, const FCustomExpanderData& CustomExpanderData)
{
Container = CustomExpanderData.WidgetContainer;
ActionPtr = CustomExpanderData.RowAction;
ChildSlot
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.VAlign(VAlign_Fill)
.HAlign(HAlign_Center)
.FillWidth(1.0)
[
SNew( SCheckBox )
.Visibility(this, &SBlueprintActionFavoriteToggle::IsVisible)
.ToolTipText(this, &SBlueprintActionFavoriteToggle::GetToolTipText)
.IsChecked(this, &SBlueprintActionFavoriteToggle::GetFavoritedState)
.OnCheckStateChanged(this, &SBlueprintActionFavoriteToggle::OnFavoriteToggled)
.Style(FAppStyle::Get(), "Kismet.Palette.FavoriteToggleStyle")
]
];
}
private:
/**
* Used to determine the toggle's visibility (this is only visible when the
* owning item is being hovered over, and the associated action can be favorited).
*
* @return True if this toggle switch should be showing, false if not.
*/
EVisibility IsVisible() const
{
bool bNoFavorites = false;
GConfig->GetBool(TEXT("BlueprintEditor.Palette"), TEXT("bUseLegacyLayout"), bNoFavorites, GEditorIni);
UBlueprintPaletteFavorites const* const BlueprintFavorites = GetDefault<UEditorPerProjectUserSettings>()->BlueprintFavorites;
EVisibility CurrentVisibility = EVisibility::Hidden;
if (!bNoFavorites && BlueprintFavorites && BlueprintFavorites->CanBeFavorited(ActionPtr.Pin()))
{
if (BlueprintFavorites->IsFavorited(ActionPtr.Pin()) || Container->IsHovered())
{
CurrentVisibility = EVisibility::Visible;
}
}
return CurrentVisibility;
}
/**
* Retrieves tooltip that describes the current favorited state of the
* associated action.
*
* @return Text describing what this toggle will do when you click on it.
*/
FText GetToolTipText() const
{
if (GetFavoritedState() == ECheckBoxState::Checked)
{
return LOCTEXT("Unfavorite", "Click to remove this item from your favorites.");
}
return LOCTEXT("Favorite", "Click to add this item to your favorites.");
}
/**
* Checks on the associated action's favorite state, and returns a
* corresponding checkbox state to match.
*
* @return ECheckBoxState::Checked if the associated action is already favorited, ECheckBoxState::Unchecked if not.
*/
ECheckBoxState GetFavoritedState() const
{
ECheckBoxState FavoriteState = ECheckBoxState::Unchecked;
if (ActionPtr.IsValid())
{
const UEditorPerProjectUserSettings& EditorSettings = *GetDefault<UEditorPerProjectUserSettings>();
if (UBlueprintPaletteFavorites* BlueprintFavorites = EditorSettings.BlueprintFavorites)
{
FavoriteState = BlueprintFavorites->IsFavorited(ActionPtr.Pin()) ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
}
return FavoriteState;
}
/**
* Triggers when the user clicks this toggle, adds or removes the associated
* action to the user's favorites.
*
* @param InNewState The new state that the user set the checkbox to.
*/
void OnFavoriteToggled(ECheckBoxState InNewState)
{
if (InNewState == ECheckBoxState::Checked)
{
GetMutableDefault<UEditorPerProjectUserSettings>()->BlueprintFavorites->AddFavorite(ActionPtr.Pin());
}
else
{
GetMutableDefault<UEditorPerProjectUserSettings>()->BlueprintFavorites->RemoveFavorite(ActionPtr.Pin());
}
}
private:
/** The action that the owning palette entry represents */
TWeakPtr<FEdGraphSchemaAction> ActionPtr;
/** The widget that this widget is nested inside */
TSharedPtr<SPanel> Container;
};
/*******************************************************************************
* SBlueprintActionMenu
*******************************************************************************/
SBlueprintActionMenu::~SBlueprintActionMenu()
{
OnClosedCallback.ExecuteIfBound();
OnCloseReasonCallback.ExecuteIfBound(bActionExecuted, ContextToggleIsChecked() == ECheckBoxState::Checked, DraggedFromPins.Num() > 0);
}
void SBlueprintActionMenu::Construct( const FArguments& InArgs, TSharedPtr<FBlueprintEditor> InEditor )
{
bActionExecuted = false;
this->GraphObj = InArgs._GraphObj;
this->DraggedFromPins = InArgs._DraggedFromPins;
this->NewNodePosition = InArgs._NewNodePosition;
this->OnClosedCallback = InArgs._OnClosedCallback;
this->bAutoExpandActionMenu = InArgs._AutoExpandActionMenu;
this->EditorPtr = InEditor;
this->OnCloseReasonCallback = InArgs._OnCloseReason;
// Generate the context display; showing the user what they're picking something for
//@TODO: Should probably be somewhere more schema-sensitive than the graph panel!
FSlateColor TypeColor;
FString TypeOfDisplay;
const FSlateBrush* ContextIcon = nullptr;
if (DraggedFromPins.Num() == 1)
{
UEdGraphPin* OnePin = DraggedFromPins[0];
const UEdGraphSchema* Schema = OnePin->GetSchema();
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
if (!Schema->IsA(UEdGraphSchema_K2::StaticClass()) || !K2Schema->IsExecPin(*OnePin))
{
// Get the type color and icon
TypeColor = Schema->GetPinTypeColor(OnePin->PinType);
ContextIcon = FAppStyle::GetBrush( OnePin->PinType.IsArray() ? TEXT("Graph.ArrayPin.Connected") : TEXT("Graph.Pin.Connected") );
}
}
FBlueprintActionContext MenuContext;
ConstructActionContext(MenuContext);
TSharedPtr<SWidget> AddImportTargetContent = SNullWidget::NullWidget;
if (GetDefault<UBlueprintEditorSettings>()->bEnableNamespaceImportingFeatures)
{
SAssignNew(AddImportTargetContent, SBox)
.ToolTipText(LOCTEXT("ImportActionLabelTooltip", "Choose a namespace to import and load additional actions."))
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(LOCTEXT("ImportActionButtonLabel", "Import Actions From:"))
]
+SHorizontalBox::Slot()
.AutoWidth()
.Padding(4.f, 0.f)
[
SNew(SBlueprintNamespaceEntry)
.AllowTextEntry(false)
.OnNamespaceSelected(this, &SBlueprintActionMenu::OnNamespaceSelectedForImport)
.OnGetNamespacesToExclude(this, &SBlueprintActionMenu::OnGetNamespacesToExcludeFromImportMenu)
.ExcludedNamespaceTooltipText(LOCTEXT("CannotSelectNamespaceForImport", "This namespace has already been imported by this Blueprint."))
]
];
}
TSharedPtr<SComboButton> TargetContextSubMenuButton;
// @TODO: would be nice if we could use a checkbox style for this, and have a different state for open/closed
SAssignNew(TargetContextSubMenuButton, SComboButton)
.MenuPlacement(MenuPlacement_MenuRight)
.HasDownArrow(false)
.ButtonStyle(FAppStyle::Get(), "BlueprintEditor.ContextMenu.TargetsButton")
.ContentPadding(FMargin(5))
.MenuContent()
[
SAssignNew(ContextTargetSubMenu, SBlueprintContextTargetMenu, MenuContext)
.OnTargetMaskChanged(this, &SBlueprintActionMenu::OnContextTargetsChanged)
.CustomTargetContent()
[
AddImportTargetContent.ToSharedRef()
]
];
// Build the widget layout
SBorder::Construct( SBorder::FArguments()
.BorderImage( FAppStyle::GetBrush("Menu.Background") )
.Padding(5.0f)
[
// Achieving fixed width by nesting items within a fixed width box.
SNew(SBox)
.WidthOverride(400.0f)
.HeightOverride(400.0f)
[
SNew(SVerticalBox)
// TYPE OF SEARCH INDICATOR
+SVerticalBox::Slot()
.AutoHeight()
.Padding(2, 2, 2, 5)
[
SNew(SHorizontalBox)
// Type pill
+SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
.Padding(0.0f, 0.0f, (ContextIcon != nullptr) ? 5.0f : 0.0f, 0.0f)
[
SNew(SImage)
.ColorAndOpacity(TypeColor)
.Visibility(this, &SBlueprintActionMenu::GetTypeImageVisibility)
.Image(ContextIcon)
]
// Search context description
+SHorizontalBox::Slot()
.FillWidth(1.f)
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(this, &SBlueprintActionMenu::GetSearchContextDesc)
.Font(FAppStyle::GetFontStyle(FName("BlueprintEditor.ActionMenu.ContextDescriptionFont")))
.ToolTip(IDocumentation::Get()->CreateToolTip(
LOCTEXT("BlueprintActionMenuContextTextTooltip", "Describes the current context of the action list"),
NULL,
TEXT("Shared/Editors/BlueprintEditor"),
TEXT("BlueprintActionMenuContextText")))
.AutoWrapText(true)
]
// Context Toggle
+SHorizontalBox::Slot()
.HAlign(HAlign_Right)
.VAlign(VAlign_Center)
.AutoWidth()
[
SNew(SCheckBox)
.OnCheckStateChanged(this, &SBlueprintActionMenu::OnContextToggleChanged)
.IsChecked(this, &SBlueprintActionMenu::ContextToggleIsChecked)
.ToolTip(IDocumentation::Get()->CreateToolTip(
LOCTEXT("BlueprintActionMenuContextToggleTooltip", "Should the list be filtered to only actions that make sense in the current context?"),
NULL,
TEXT("Shared/Editors/BlueprintEditor"),
TEXT("BlueprintActionMenuContextToggle")))
[
SNew(STextBlock)
.Text(LOCTEXT("BlueprintActionMenuContextToggle", "Context Sensitive"))
]
]
+SHorizontalBox::Slot()
.HAlign(HAlign_Right)
.VAlign(VAlign_Center)
.AutoWidth()
.Padding(3.f, 0.f, 0.f, 0.f)
[
TargetContextSubMenuButton.ToSharedRef()
]
]
// ACTION LIST
+SVerticalBox::Slot()
[
SAssignNew(GraphActionMenu, SGraphActionMenu)
.OnActionSelected(this, &SBlueprintActionMenu::OnActionSelected)
.OnCreateWidgetForAction(SGraphActionMenu::FOnCreateWidgetForAction::CreateSP(this, &SBlueprintActionMenu::OnCreateWidgetForAction))
.OnGetActionList(this, &SBlueprintActionMenu::OnGetActionList)
.OnCreateCustomRowExpander_Static(&CreateCustomBlueprintActionExpander)
.DraggedFromPins(DraggedFromPins)
.GraphObj(GraphObj)
]
// PROGRESS BAR
+SVerticalBox::Slot()
.AutoHeight()
[
SNew(SBox)
.HeightOverride(2)
.Visibility_Lambda([this]()
{
return ContextMenuBuilder.IsValid() && ContextMenuBuilder->GetNumPendingActions() > 0 ? EVisibility::SelfHitTestInvisible : EVisibility::Collapsed;
})
[
SNew(SProgressBar)
.BorderPadding(FVector2D(0, 0))
.Percent_Lambda([this]()
{
return ContextMenuBuilder.IsValid() ? ContextMenuBuilder->GetPendingActionsProgress() : 0.0f;
})
]
]
]
]
);
}
EVisibility SBlueprintActionMenu::GetTypeImageVisibility() const
{
if (DraggedFromPins.Num() == 1 && EditorPtr.Pin()->GetIsContextSensitive())
{
UEdGraphPin* OnePin = DraggedFromPins[0];
const UEdGraphSchema* Schema = OnePin->GetSchema();
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
if (!Schema->IsA(UEdGraphSchema_K2::StaticClass()) || !K2Schema->IsExecPin(*OnePin))
{
return EVisibility::Visible;
}
}
return EVisibility::Collapsed;
}
FText SBlueprintActionMenu::GetSearchContextDesc() const
{
bool bIsContextSensitive = EditorPtr.Pin()->GetIsContextSensitive();
bool bHasPins = DraggedFromPins.Num() > 0;
if (!bIsContextSensitive)
{
return LOCTEXT("MenuPrompt_AllPins", "All Possible Actions");
}
else if (!bHasPins)
{
return LOCTEXT("MenuPrompt_BlueprintActions", "All Actions for this Blueprint");
}
else if (DraggedFromPins.Num() == 1)
{
UEdGraphPin* OnePin = DraggedFromPins[0];
const UEdGraphSchema* Schema = OnePin->GetSchema();
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
if (Schema->IsA(UEdGraphSchema_K2::StaticClass()) && K2Schema->IsExecPin(*OnePin))
{
return LOCTEXT("MenuPrompt_ExecPin", "Executable actions");
}
else
{
// Get the type string
const FString TypeStringRaw = UEdGraphSchema_K2::TypeToText(OnePin->PinType).ToString();
//@TODO: Add a parameter to TypeToText indicating the kind of formating requested
const FString TypeString = (TypeStringRaw.Replace(TEXT("'"), TEXT(" "))).TrimEnd();
if (OnePin->Direction == EGPD_Input)
{
return FText::Format(LOCTEXT("MenuPrompt_InputPin", "Actions providing a(n) {0}"), FText::FromString(TypeString));
}
else
{
return FText::Format(LOCTEXT("MenuPrompt_OutputPin", "Actions taking a(n) {0}"), FText::FromString(TypeString));
}
}
}
else
{
return FText::Format(LOCTEXT("MenuPrompt_ManyPins", "Actions for {0} pins"), FText::AsNumber(DraggedFromPins.Num()));
}
}
void SBlueprintActionMenu::OnContextToggleChanged(ECheckBoxState CheckState)
{
EditorPtr.Pin()->GetIsContextSensitive() = CheckState == ECheckBoxState::Checked;
GraphActionMenu->RefreshAllActions(true, false);
}
void SBlueprintActionMenu::OnContextTargetsChanged(uint32 /*ContextTargetMask*/)
{
GraphActionMenu->RefreshAllActions(/*bPreserveExpansion =*/true, /*bHandleOnSelectionEvent =*/false);
}
ECheckBoxState SBlueprintActionMenu::ContextToggleIsChecked() const
{
return EditorPtr.Pin()->GetIsContextSensitive() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
TSharedRef<FGraphActionListBuilderBase> SBlueprintActionMenu::OnGetActionList()
{
check(EditorPtr.IsValid());
TSharedPtr<FBlueprintEditor> BlueprintEditor = EditorPtr.Pin();
bool const bIsContextSensitive = BlueprintEditor->GetIsContextSensitive();
uint32 ContextTargetMask = 0;
if (bIsContextSensitive && ContextTargetSubMenu.IsValid())
{
ContextTargetMask = ContextTargetSubMenu->GetContextTargetMask();
}
FBlueprintActionContext FilterContext;
ConstructActionContext(FilterContext);
FBlueprintActionMenuBuilder::EConfigFlags ConfigFlags = FBlueprintActionMenuBuilder::DefaultConfig;
if (GetDefault<UBlueprintEditorSettings>()->bEnableContextMenuTimeSlicing)
{
ConfigFlags |= FBlueprintActionMenuBuilder::UseTimeSlicing;
}
ContextMenuBuilder = MakeShared<FBlueprintActionMenuBuilder>(ConfigFlags);
// NOTE: cannot call GetGraphContextActions() during serialization and GC due to its use of FindObject()
if (!GIsSavingPackage && !IsGarbageCollecting() && FilterContext.Blueprints.Num() > 0)
{
FBlueprintActionMenuUtils::MakeContextMenu(FilterContext, bIsContextSensitive, ContextTargetMask, *ContextMenuBuilder);
}
// also try adding promote to variable if we can do so.
TryInsertPromoteToVariable(FilterContext, *ContextMenuBuilder);
// give the schema the opportunity to add another action
if (const UEdGraphSchema* Schema = Cast<const UEdGraphSchema>(GraphObj->GetSchema()))
{
Schema->InsertAdditionalActions(FilterContext.Blueprints, FilterContext.Graphs, FilterContext.Pins, *ContextMenuBuilder);
}
return ContextMenuBuilder.ToSharedRef();
}
void SBlueprintActionMenu::ConstructActionContext(FBlueprintActionContext& ContextDescOut)
{
check(EditorPtr.IsValid());
TSharedPtr<FBlueprintEditor> BlueprintEditor = EditorPtr.Pin();
bool const bIsContextSensitive = BlueprintEditor->GetIsContextSensitive();
// we still want context from the graph (even if the user has unchecked
// "Context Sensitive"), otherwise the user would be presented with nodes
// that can't be placed in the graph... if the user isn't being presented
// with a valid node, then fix it up in filtering
ContextDescOut.Graphs.Add(GraphObj);
UBlueprint* Blueprint = BlueprintEditor->GetBlueprintObj();
const bool bBlueprintIsValid = IsValid(Blueprint) && Blueprint->GeneratedClass && (Blueprint->GeneratedClass->ClassGeneratedBy == Blueprint);
if (!ensure(bBlueprintIsValid)) // to track UE-11597 and UE-11595
{
return;
}
ContextDescOut.EditorPtr = EditorPtr;
ContextDescOut.Blueprints.Add(Blueprint);
if (bIsContextSensitive)
{
ContextDescOut.Pins = DraggedFromPins;
// Get selection from the "My Blueprint" view.
FEdGraphSchemaAction_K2Var* SelectedVar = BlueprintEditor->GetMyBlueprintWidget()->SelectionAsVar();
if ((SelectedVar != nullptr) && (SelectedVar->GetProperty() != nullptr))
{
ContextDescOut.SelectedObjects.Add(SelectedVar->GetProperty());
}
// If the selection come from the SCS editor, add it to the filter context.
else if (Blueprint->SkeletonGeneratedClass && BlueprintEditor->GetSubobjectEditor().IsValid())
{
TArray<FSubobjectEditorTreeNodePtrType> Nodes = BlueprintEditor->GetSubobjectEditor()->GetSelectedNodes();
if (Nodes.Num() == 1 && Nodes[0]->IsComponentNode())
{
FName PropertyName = Nodes[0]->GetVariableName();
FObjectProperty* VariableProperty = FindFProperty<FObjectProperty>(Blueprint->SkeletonGeneratedClass, PropertyName);
ContextDescOut.SelectedObjects.Add(VariableProperty);
}
}
}
}
TSharedRef<SEditableTextBox> SBlueprintActionMenu::GetFilterTextBox()
{
return GraphActionMenu->GetFilterTextBox();
}
TSharedRef<SWidget> SBlueprintActionMenu::OnCreateWidgetForAction(FCreateWidgetForActionData* const InCreateData)
{
InCreateData->bHandleMouseButtonDown = true;
return SNew(SBlueprintPaletteItem, InCreateData, EditorPtr.Pin());
}
void SBlueprintActionMenu::OnActionSelected( const TArray< TSharedPtr<FEdGraphSchemaAction> >& SelectedAction, ESelectInfo::Type InSelectionType )
{
if (InSelectionType == ESelectInfo::OnMouseClick || InSelectionType == ESelectInfo::OnKeyPress || SelectedAction.Num() == 0)
{
for ( int32 ActionIndex = 0; ActionIndex < SelectedAction.Num(); ActionIndex++ )
{
if ( SelectedAction[ActionIndex].IsValid() && GraphObj != nullptr )
{
// Don't dismiss when clicking on dummy action
if ( !bActionExecuted && (SelectedAction[ActionIndex]->GetTypeId() != FEdGraphSchemaAction_Dummy::StaticGetTypeId()))
{
FSlateApplication::Get().DismissAllMenus();
bActionExecuted = true;
}
UEdGraphNode* ResultNode = SelectedAction[ActionIndex]->PerformAction(GraphObj, DraggedFromPins, NewNodePosition);
if ( ResultNode != nullptr )
{
NewNodePosition.Y += UEdGraphSchema_K2::EstimateNodeHeight( ResultNode );
TSharedPtr<FBlueprintEditor> BlueprintEditorPtr = EditorPtr.Pin();
if (BlueprintEditorPtr.IsValid())
{
// Determine which namespace(s) to import, based on the node's external dependencies.
TSet<FString> NamespacesToImport;
TArray<UStruct*> ExternalDependencies;
if (ResultNode->HasExternalDependencies(&ExternalDependencies))
{
for (const UStruct* ExternalDependency : ExternalDependencies)
{
FBlueprintNamespaceUtilities::GetDefaultImportsForObject(ExternalDependency, NamespacesToImport);
}
}
if (NamespacesToImport.Num() > 0)
{
// Auto-import the namespace(s) gathered above. Additional type objects within the imported scope may be loaded here.
FBlueprintEditor::FImportNamespaceExParameters Params;
Params.NamespacesToImport = MoveTemp(NamespacesToImport);
BlueprintEditorPtr->ImportNamespaceEx(Params);
}
}
}
}
}
}
}
void SBlueprintActionMenu::TryInsertPromoteToVariable(FBlueprintActionContext const& MenuContext, FGraphActionListBuilderBase& OutAllActions)
{
// If we can promote this to a variable add a menu entry to do so.
const UEdGraphSchema_K2* K2Schema = Cast<const UEdGraphSchema_K2>(GraphObj->GetSchema());
if ((K2Schema != nullptr) && (MenuContext.Pins.Num() > 0))
{
if (K2Schema->CanPromotePinToVariable(*MenuContext.Pins[0], true))
{
TSharedPtr<FBlueprintAction_PromoteVariable> PromoteAction = TSharedPtr<FBlueprintAction_PromoteVariable>(new FBlueprintAction_PromoteVariable(true));
PromoteAction->MyBlueprintEditor = EditorPtr;
OutAllActions.AddAction(PromoteAction);
}
if (MenuContext.Graphs.Num() == 1 && FBlueprintEditorUtils::DoesSupportLocalVariables(MenuContext.Graphs[0]) && K2Schema->CanPromotePinToVariable(*MenuContext.Pins[0], false))
{
TSharedPtr<FBlueprintAction_PromoteVariable> LocalPromoteAction = TSharedPtr<FBlueprintAction_PromoteVariable>(new FBlueprintAction_PromoteVariable(false));
LocalPromoteAction->MyBlueprintEditor = EditorPtr;
OutAllActions.AddAction( LocalPromoteAction );
}
}
}
void SBlueprintActionMenu::OnGetNamespacesToExcludeFromImportMenu(TSet<FString>& OutNamespacesToExclude)
{
FBlueprintActionContext MenuContext;
ConstructActionContext(MenuContext);
FBlueprintNamespaceUtilities::GetSharedGlobalImports(OutNamespacesToExclude);
for (const UBlueprint* Blueprint : MenuContext.Blueprints)
{
FBlueprintNamespaceUtilities::GetDefaultImportsForObject(Blueprint, OutNamespacesToExclude);
OutNamespacesToExclude.Append(Blueprint->ImportedNamespaces);
}
}
void SBlueprintActionMenu::OnNamespaceSelectedForImport(const FString& InNamespace)
{
TSharedPtr<FBlueprintEditor> BlueprintEditorPtr = EditorPtr.Pin();
if (BlueprintEditorPtr.IsValid())
{
FBlueprintEditor::FImportNamespaceExParameters Params;
Params.NamespacesToImport.Add(InNamespace);
Params.OnPostImportCallback = FSimpleDelegate::CreateLambda([GraphActionMenu = this->GraphActionMenu]()
{
// Now that additional types have been loaded/imported, update the menu to include any additional action(s).
const bool bPreserveExpansion = true;
const bool bHandleOnSelectionEvent = false;
GraphActionMenu->RefreshAllActions(bPreserveExpansion, bHandleOnSelectionEvent);
});
// Auto-import the namespace into the current editor context. This may load additional type assets.
BlueprintEditorPtr->ImportNamespaceEx(Params);
}
}
void SBlueprintActionMenu::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
{
SBorder::Tick(AllottedGeometry, InCurrentTime, InDeltaTime);
int32 NewIdxStart = ContextMenuBuilder->GetNumActions();
if (ContextMenuBuilder.IsValid() && ContextMenuBuilder->ProcessPendingActions())
{
GraphActionMenu->UpdateForNewActions(NewIdxStart);
}
}
/*******************************************************************************
* SBlueprintActionMenuExpander
*******************************************************************************/
void SBlueprintActionMenuExpander::Construct(const FArguments& InArgs, const FCustomExpanderData& ActionMenuData)
{
OwnerRowPtr = ActionMenuData.TableRow;
IndentAmount = InArgs._IndentAmount;
ActionPtr = ActionMenuData.RowAction;
if (!ActionPtr.IsValid())
{
SExpanderArrow::FArguments SuperArgs;
SuperArgs._IndentAmount = InArgs._IndentAmount;
SExpanderArrow::Construct(SuperArgs, ActionMenuData.TableRow);
}
else
{
ChildSlot
.Padding(TAttribute<FMargin>(this, &SBlueprintActionMenuExpander::GetCustomIndentPadding))
[
SNew(SBlueprintActionFavoriteToggle, ActionMenuData)
];
}
}
FMargin SBlueprintActionMenuExpander::GetCustomIndentPadding() const
{
FMargin CustomPadding = SExpanderArrow::GetExpanderPadding();
// if this is a action row (not a category or separator)
if (ActionPtr.IsValid())
{
// flip the left/right margins (we want the favorite toggle aligned to the far left)
//CustomPadding = FMargin(CustomPadding.Right, CustomPadding.Top, CustomPadding.Left, CustomPadding.Bottom);
}
return CustomPadding;
}
#undef LOCTEXT_NAMESPACE