Files
UnrealEngine/Engine/Source/Editor/GraphEditor/Public/SGraphActionMenu.h
2025-05-18 13:04:45 +08:00

412 lines
19 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Containers/Array.h"
#include "Containers/BitArray.h"
#include "Containers/Map.h"
#include "Containers/Set.h"
#include "Containers/SparseArray.h"
#include "Containers/UnrealString.h"
#include "CoreMinimal.h"
#include "CoreTypes.h"
#include "Delegates/Delegate.h"
#include "Framework/SlateDelegates.h"
#include "HAL/PlatformCrt.h"
#include "Input/Reply.h"
#include "Internationalization/Text.h"
#include "Misc/Attribute.h"
#include "Misc/Optional.h"
#include "SlateFwd.h"
#include "Templates/SharedPointer.h"
#include "Templates/TypeHash.h"
#include "Templates/UnrealTemplate.h"
#include "Types/SlateConstants.h"
#include "Types/SlateEnums.h"
#include "UObject/GCObject.h"
#include "UObject/NameTypes.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Widgets/SCompoundWidget.h"
#include "Widgets/SWidget.h"
#include "Widgets/Views/SExpanderArrow.h"
#include "Widgets/Views/STableRow.h"
#include "Widgets/Views/STableViewBase.h"
#include "Widgets/Views/STreeView.h"
class FReferenceCollector;
class ITableRow;
class IToolTip;
class SEditableTextBox;
class SPanel;
class SSearchBox;
class SWidget;
class UEdGraph;
class UEdGraphPin;
struct FCreateWidgetForActionData;
struct FGeometry;
struct FGraphActionNode;
struct FKeyEvent;
struct FPointerEvent;
struct FEdGraphSchemaAction;
struct FGraphActionListBuilderBase;
/** Delegate for hooking up an inline editable text block to be notified that a rename is requested. */
DECLARE_DELEGATE( FOnRenameRequestActionNode );
/** Delegate executed when the mouse button goes down */
DECLARE_DELEGATE_RetVal_OneParam( bool, FCreateWidgetMouseButtonDown, TWeakPtr<FEdGraphSchemaAction> );
/** Default widget for GraphActionMenu */
class GRAPHEDITOR_API SDefaultGraphActionWidget : public SCompoundWidget
{
SLATE_BEGIN_ARGS( SDefaultGraphActionWidget ) {}
SLATE_ATTRIBUTE(FText, HighlightText)
SLATE_END_ARGS()
void Construct(const FArguments& InArgs, const FCreateWidgetForActionData* InCreateData);
virtual FReply OnMouseButtonDown( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) override;
/** The item that we want to display with this widget */
TWeakPtr<FEdGraphSchemaAction> ActionPtr;
/** Delegate executed when mouse button goes down */
FCreateWidgetMouseButtonDown MouseButtonDownDelegate;
};
struct FCreateWidgetForActionData
{
/** True if we want to use the mouse delegate */
bool bHandleMouseButtonDown;
/** Delegate for mouse button going down */
FCreateWidgetMouseButtonDown MouseButtonDownDelegate;
/** The action being used for the widget */
TSharedPtr< FEdGraphSchemaAction > Action;
/** The delegate to determine if the current action is selected in the row */
FIsSelected IsRowSelectedDelegate;
/** This will be returned, hooked up to request a rename */
FOnRenameRequestActionNode* const OnRenameRequest;
/** The text to highlight */
TAttribute<FText> HighlightText;
/** True if the widget should be read only - no renaming allowed */
bool bIsReadOnly;
FCreateWidgetForActionData(FOnRenameRequestActionNode* const InOnRenameRequest)
: OnRenameRequest(InOnRenameRequest)
, bIsReadOnly(false)
{
}
};
struct FCustomExpanderData
{
/** The menu row associated with the widget being customized */
TSharedPtr<ITableRow> TableRow;
/** The action associated with the menu row being customized */
TSharedPtr<FEdGraphSchemaAction> RowAction;
/** The widget container that the custom expander will belong to */
TSharedPtr<SPanel> WidgetContainer;
};
/** Class that displays a list of graph actions and them to be searched and selected */
class GRAPHEDITOR_API SGraphActionMenu : public SCompoundWidget, public FGCObject
{
public:
/** Delegate that can be used to create a widget for a particular action */
DECLARE_DELEGATE_RetVal_OneParam( TSharedRef<SWidget>, FOnCreateWidgetForAction, FCreateWidgetForActionData* const );
/** Delegate that can be used to create a custom "expander" widget for a particular row */
DECLARE_DELEGATE_RetVal_OneParam( TSharedRef<SExpanderArrow>, FOnCreateCustomRowExpander, FCustomExpanderData const& );
/** Delegate executed when an action is selected */
DECLARE_DELEGATE_TwoParams( FOnActionSelected, const TArray< TSharedPtr<FEdGraphSchemaAction> >&, ESelectInfo::Type );
/** Delegate executed when an action is double clicked */
DECLARE_DELEGATE_OneParam( FOnActionDoubleClicked, const TArray< TSharedPtr<FEdGraphSchemaAction> >& );
/** Delegate executed when an action is dragged */
DECLARE_DELEGATE_RetVal_TwoParams( FReply, FOnActionDragged, const TArray< TSharedPtr<FEdGraphSchemaAction> >&, const FPointerEvent& );
/** Delegate executed when a category is dragged */
DECLARE_DELEGATE_RetVal_TwoParams( FReply, FOnCategoryDragged, const FText&, const FPointerEvent& );
/** Delegate executed when the list of all actions needs to be refreshed */
DECLARE_DELEGATE_OneParam( FOnCollectAllActions, FGraphActionListBuilderBase& );
/** Delegate executed when the list of all actions needs to be refreshed, should return any sections that should always be visible, even if they don't have children. */
DECLARE_DELEGATE_OneParam( FOnCollectStaticSections, TArray<int32>& )
/** Delegate executed when a category is being renamed so any post-rename actions can be handled */
DECLARE_DELEGATE_ThreeParams( FOnCategoryTextCommitted, const FText&, ETextCommit::Type, TWeakPtr< FGraphActionNode >);
/** Delegate executed to check if the selected action is valid for renaming */
DECLARE_DELEGATE_RetVal_OneParam( bool, FCanRenameSelectedAction, TWeakPtr< FGraphActionNode > );
/** Delegate to get the name of a section if the widget is a section separator. */
DECLARE_DELEGATE_RetVal_OneParam( FText, FGetSectionTitle, int32 );
/** Delegate to get the tooltip of a section if the widget is a section separator. */
DECLARE_DELEGATE_RetVal_OneParam( TSharedPtr<IToolTip>, FGetSectionToolTip, int32 );
/** Delegate to get the widget that appears on the section bar in the section separator. */
DECLARE_DELEGATE_RetVal_TwoParams( TSharedRef<SWidget>, FGetSectionWidget, TSharedRef<SWidget>, int32 );
/** Delegate to get the filter text */
DECLARE_DELEGATE_RetVal( FText, FGetFilterText);
/** Delegate to check if an action matches a specified name (used for renaming items etc.) */
DECLARE_DELEGATE_RetVal_TwoParams( bool, FOnActionMatchesName, FEdGraphSchemaAction*, const FName& );
/** Delegate that can be used to create and/or get a custom action list. */
DECLARE_DELEGATE_RetVal(TSharedRef<FGraphActionListBuilderBase>, FOnGetActionList);
SLATE_BEGIN_ARGS(SGraphActionMenu)
: _AutoExpandActionMenu(false)
, _AlphaSortItems(true)
, _SortItemsRecursively(true)
, _ShowFilterTextBox(true)
, _UseSectionStyling(false)
, _bAllowPreselectedItemActivation(false)
, _DefaultRowExpanderBaseIndentLevel(0)
, _GraphObj(nullptr)
, _bAutomaticallySelectSingleAction(false)
{ }
SLATE_EVENT( FOnActionSelected, OnActionSelected )
SLATE_EVENT( FOnActionDoubleClicked, OnActionDoubleClicked )
SLATE_EVENT( FOnActionDragged, OnActionDragged )
SLATE_EVENT( FOnCategoryDragged, OnCategoryDragged )
SLATE_EVENT( FOnContextMenuOpening, OnContextMenuOpening )
SLATE_EVENT( FOnCreateWidgetForAction, OnCreateWidgetForAction )
SLATE_EVENT( FOnCreateCustomRowExpander, OnCreateCustomRowExpander )
SLATE_EVENT( FOnGetActionList, OnGetActionList )
SLATE_EVENT( FOnCollectAllActions, OnCollectAllActions )
SLATE_EVENT( FOnCollectStaticSections, OnCollectStaticSections )
SLATE_EVENT( FOnCategoryTextCommitted, OnCategoryTextCommitted )
SLATE_EVENT( FCanRenameSelectedAction, OnCanRenameSelectedAction )
SLATE_EVENT( FGetSectionTitle, OnGetSectionTitle )
SLATE_EVENT( FGetSectionToolTip, OnGetSectionToolTip )
SLATE_EVENT( FGetSectionWidget, OnGetSectionWidget )
SLATE_EVENT( FGetFilterText, OnGetFilterText )
SLATE_EVENT( FOnActionMatchesName, OnActionMatchesName )
SLATE_ARGUMENT( bool, AutoExpandActionMenu )
SLATE_ARGUMENT( bool, AlphaSortItems )
SLATE_ARGUMENT( bool, SortItemsRecursively )
SLATE_ARGUMENT( bool, ShowFilterTextBox )
SLATE_ARGUMENT( bool, UseSectionStyling )
SLATE_ARGUMENT( bool, bAllowPreselectedItemActivation )
SLATE_ARGUMENT( int32, DefaultRowExpanderBaseIndentLevel )
SLATE_ARGUMENT( TArray<UEdGraphPin*>, DraggedFromPins )
SLATE_ARGUMENT( UEdGraph*, GraphObj )
SLATE_ARGUMENT( bool, bAutomaticallySelectSingleAction )
SLATE_END_ARGS()
void Construct( const FArguments& InArgs, bool bIsReadOnly = true );
// FGCObject override
virtual void AddReferencedObjects( FReferenceCollector& Collector ) override;
virtual FString GetReferencerName() const override;
/**
* Refreshes the actions that this widget should display
*
* @param bPreserveExpansion TRUE if the expansion state of the tree should be preserved
* @param bHandleOnSelectionEvent TRUE if the item should be selected and any actions that occur with selection will be handled. FALSE and only selection will occur */
void RefreshAllActions(bool bPreserveExpansion, bool bHandleOnSelectionEvent = true);
/** Returns a map of all top level sections and their current expansion state. */
void GetSectionExpansion(TMap<int32, bool>& SectionExpansion) const;
/** Sets the sections to be expanded of all top level sections. */
void SetSectionExpansion(const TMap<int32, bool>& SectionExpansion);
protected:
/** Tree view for showing actions */
TSharedPtr< STreeView< TSharedPtr<FGraphActionNode> > > TreeView;
/** Text box used for searching for actions */
TSharedPtr<SSearchBox> FilterTextBox;
/** List of all actions we can browser */
TSharedPtr<FGraphActionListBuilderBase> AllActions;
/** Flattened list of all actions passing the filter - access via GetFilteredActionNodes to ensure it's up to date */
TArray< TSharedPtr<FGraphActionNode> > FilteredActionNodes;
/** Root of filtered actions tree */
TSharedPtr<FGraphActionNode> FilteredRootAction;
/** Stored score for our current selection, so that we can quickly maintain selection when building the list asynchronously */
float SelectedSuggestionScore;
/** Stored index in the AllActions list builder that we think is the best fit */
int32 SelectedSuggestionSourceIndex;
/** The actual item selected - possibly redundant to selection information in the treeview, but the tree is rebuilt requently */
TSharedPtr<FGraphActionNode> SelectedAction;
/** Allows us to set selection (via keyboard) without triggering action */
bool bIgnoreUIUpdate;
/** Should we auto-expand categories */
bool bAutoExpandActionMenu;
/** Should we display the filter text box */
bool bShowFilterTextBox;
/** Don't sort items alphabetically */
bool bAlphaSortItems;
/** If only the top entries should be sorted or subentries as well */
bool bSortItemsRecursively;
/** Should the rows and sections be styled like the details panel? */
bool bUseSectionStyling;
/** True if the user is using the keyboard to navigatethe list, halting scoring of newly added entries so that selection is stable */
bool bIsKeyboardNavigating;
/** Whether we allow pre-selected items to be activated with a left-click */
bool bAllowPreselectedItemActivation;
/** Whether to automatically proceed with an action if it's the only one in the list. */
bool bAutomaticallySelectSingleAction;
/** The BaseIndentLevel of the default-created row expander. Not used with OnCreateCustomRowExpander. */
int32 DefaultRowExpanderBaseIndentLevel;
/** The slot that the selected item should be displayed at */
int32 DisplayIndex;
/** Delegate to call when action is selected */
FOnActionSelected OnActionSelected;
/** Delegate to call when action is double clicked */
FOnActionDoubleClicked OnActionDoubleClicked;
/** Delegate to call when an action is dragged. */
FOnActionDragged OnActionDragged;
/** Delegate to call when a category is dragged. */
FOnCategoryDragged OnCategoryDragged;
/** Delegate to call to create widget for an action */
FOnCreateWidgetForAction OnCreateWidgetForAction;
/** Delegate to call for creating a custom "expander" widget for indenting a menu row with */
FOnCreateCustomRowExpander OnCreateCustomRowExpander;
/** Delegate to call to get a custom action list */
FOnGetActionList OnGetActionList;
/** Delegate to call to collect all actions */
FOnCollectAllActions OnCollectAllActions;
/** Delegate to call to collect all always visible sections */
FOnCollectStaticSections OnCollectStaticSections;
/** Delegate to call to handle any post-category rename events */
FOnCategoryTextCommitted OnCategoryTextCommitted;
/** Delegate to call to check if a selected action is valid for renaming */
FCanRenameSelectedAction OnCanRenameSelectedAction;
/** Delegate to get the name of a section separator. */
FGetSectionTitle OnGetSectionTitle;
/** Delegate to get the tooltip of a section separator. */
FGetSectionToolTip OnGetSectionToolTip;
/** Delegate to get the widgets of a section separator. */
FGetSectionWidget OnGetSectionWidget;
/** Delegate to get the filter text if supplied from an external source */
FGetFilterText OnGetFilterText;
/** Delegate to check if an action matches a specified name (used for renaming items etc.) */
FOnActionMatchesName OnActionMatchesName;
public:
// SWidget interface
virtual FReply OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent) override;
// End of SWidget interface
/** Get filter text box widget */
TSharedRef<SEditableTextBox> GetFilterTextBox();
/** Get action that is currently selected */
void GetSelectedActions(TArray< TSharedPtr<FEdGraphSchemaAction> >& OutSelectedActions) const;
/** Initiates a rename on the selected action node, if possible */
void OnRequestRenameOnActionNode();
/** Queries if a rename on the selected action node is possible */
bool CanRequestRenameOnActionNode() const;
/** Get category that is currently selected */
FString GetSelectedCategoryName() const;
/** Get category child actions that is currently selected */
void GetSelectedCategorySubActions(TArray<TSharedPtr<FEdGraphSchemaAction>>& OutActions) const;
/** Get category child actions for the passed in action */
void GetCategorySubActions(TWeakPtr<FGraphActionNode> InAction, TArray<TSharedPtr<FEdGraphSchemaAction>>& OutActions) const;
/**
* Selects an non-creation item in the list, searching by FName, deselects if name is none
*
* @param ItemName The name of the item to select
* @param SelectInfo The selection type
* @param SectionId If known, the section Id to restrict the selection to, useful in the case of categories where they can exist multiple times
* @param bIsCategory TRUE if the selection is a category, categories obey different rules and it's hard to re-select properly without this knowledge
* @return TRUE if the item was successfully selected or the tree cleared, FALSE if unsuccessful
*/
bool SelectItemByName(const FName& ItemName, ESelectInfo::Type SelectInfo = ESelectInfo::Direct, int32 SectionId = INDEX_NONE, bool bIsCategory = false );
/** Expands any category with the associated name */
void ExpandCategory(const FText& CategoryName);
/* Handler for mouse button going down */
bool OnMouseButtonDownEvent( TWeakPtr<FEdGraphSchemaAction> InAction );
/** Updates the displayed list starting from IdxStart, useful for async building the display list of actions */
void UpdateForNewActions(int32 IdxStart);
/** Regenerated filtered results (FilteredRootAction) based on filter text */
void GenerateFilteredItems(bool bPreserveExpansion);
/** The last typed action within the graph action menu */
static FString LastUsedFilterText;
protected:
/** Get current filter text */
FText GetFilterText() const;
/** Change the selection to reflect the active suggestion */
void MarkActiveSuggestion();
/** Try to spawn the node reflected by the active suggestion */
bool TryToSpawnActiveSuggestion();
/** Returns true if the tree should be autoexpanded */
bool ShouldExpandNodes() const;
/** Checks if the passed in node is safe for renaming */
bool CanRenameNode(TWeakPtr<FGraphActionNode> InNode) const;
// Delegates
/** Called when filter text changes */
void OnFilterTextChanged( const FText& InFilterText );
/** Called when enter is hit in search box */
void OnFilterTextCommitted(const FText& InText, ETextCommit::Type CommitInfo);
/** Get children */
void OnGetChildrenForCategory( TSharedPtr<FGraphActionNode> InItem, TArray< TSharedPtr<FGraphActionNode> >& OutChildren );
/** Create widget for the supplied node */
TSharedRef<ITableRow> MakeWidget( TSharedPtr<FGraphActionNode> InItem, const TSharedRef<STableViewBase>& OwnerTable, bool bIsReadOnly );
/**
* Called when tree item is selected
*
* @param InSelectedItem The action node that is being selected
* @param SelectInfo Selection type - Only OnMouseClick and OnKeyPress will trigger a call to HandleSelection
*/
void OnItemSelected( TSharedPtr< FGraphActionNode > InSelectedItem, ESelectInfo::Type SelectInfo );
/**
* Executes the selection delegate providing it has been bound, and the provided action node given is valid and is an action node
*
* @param InselectedItem The graph action node selected
*
* @return true if item selection delegate was executed
*/
bool HandleSelection( TSharedPtr< FGraphActionNode > &InSelectedItem, ESelectInfo::Type InSelectionType );
/** Called when tree item is double clicked */
void OnItemDoubleClicked( TSharedPtr< FGraphActionNode > InClickedItem );
/** Called when tree item dragged */
FReply OnItemDragDetected( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent );
/** Callback when rename text is committed */
void OnNameTextCommitted(const FText& NewText, ETextCommit::Type InTextCommit, TWeakPtr< FGraphActionNode > InAction);
/** Handler for when an item has scrolled into view after having been requested to do so */
void OnItemScrolledIntoView( TSharedPtr<FGraphActionNode> InActionNode, const TSharedPtr<ITableRow>& InWidget );
/** Callback for expanding tree items recursively */
void OnSetExpansionRecursive(TSharedPtr<FGraphActionNode> InTreeNode, bool bInIsItemExpanded);
/** Helper function for adding and scoring actions from our builder */
void ScoreAndAddActions(int32 StartingIndex = INDEX_NONE);
/** Helper functions for keyboard interaction */
void SelectPreviousAction(int32 Num = 1);
void SelectNextAction(int32 Num = 1);
void SelectFirstAction();
void SelectLastAction();
/** General helper functions for accessing the filtered tree */
TSharedPtr<FGraphActionNode> GetFirstAction();
const TArray< TSharedPtr<FGraphActionNode> >& GetFilteredActionNodes(int32* OutSelectedIndex = nullptr);
int32 GetTotalLeafNodes() const;
private:
/** The pins that have been dragged off of to prompt the creation of this action menu. */
TArray<UEdGraphPin*> DraggedFromPins;
/** The graph that this menu is being constructed in */
UEdGraph* GraphObj;
};