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

335 lines
12 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Containers/Array.h"
#include "Containers/Map.h"
#include "Containers/Set.h"
#include "Containers/UnrealString.h"
#include "EdGraph/EdGraphSchema.h"
#include "HAL/Platform.h"
#include "Internationalization/Text.h"
#include "SGraphActionMenu.h"
#include "Templates/SharedPointer.h"
#include "Templates/TypeHash.h"
template <typename ItemType> class STreeView;
// Utility class for building menus of graph actions
struct GRAPHEDITOR_API FGraphActionNode : TSharedFromThis<FGraphActionNode>
{
public:
// We need to declare our copy constructors so that we can disable
// deprecation warnings around them for ClangEditor - when all of
// the deprecated members are deleted we can remove these:
PRAGMA_DISABLE_DEPRECATION_WARNINGS
FGraphActionNode(const FGraphActionNode& Node) = delete;
FGraphActionNode& operator=(const FGraphActionNode& Node) = delete;
FGraphActionNode(FGraphActionNode&& Node) = delete;
FGraphActionNode& operator=(FGraphActionNode&& Node) = delete;
~FGraphActionNode() = default;
PRAGMA_ENABLE_DEPRECATION_WARNINGS
/** */
static const int32 INVALID_SECTION_ID = 0;
/** Identifies the named section that this node belongs to, if any (defaults to INVALID_SECTION_ID) */
int32 const SectionID;
/** Identifies the menu group that this node belongs to (defaults to zero) */
int32 const Grouping;
/** An action to execute when this node is picked from a menu */
TSharedPtr<FEdGraphSchemaAction> const Action;
UE_DEPRECATED(5.5, "!! WARNING: This array is no longer populated!! FGraphActionNode::Actions array only functioned with a single Action (GetPrimaryAction), access via Action")
TArray< TSharedPtr<FEdGraphSchemaAction> > const Actions;
/** */
TArray< TSharedPtr<FGraphActionNode> > Children;
/** Lookup table for category nodes, used to speed up menu construction */
TMap< FString, TSharedPtr<FGraphActionNode> > CategoryNodes;
public:
/**
* Static allocator for a new root node (so external users have a starting
* point to build graph action trees from).
*
* @return A newly allocated root node (should not be displayed in the tree view).
*/
static TSharedPtr<FGraphActionNode> NewRootNode();
/**
* Inserts a new action node (and any accompanying category nodes) based off
* the provided Action.
*
* NOTE: This does NOT insert the node in a sorted manner. Call SortChildren()
* separately or use AddChildAlphabetical
*
* @param Action An action that you want the node to execute when picked.
* @return The new action node.
*/
TSharedPtr<FGraphActionNode> AddChild(const TSharedPtr<FEdGraphSchemaAction>& Action);
PRAGMA_DISABLE_DEPRECATION_WARNINGS
UE_DEPRECATED(5.5, "FGraphActionListBuilderBase::ActionGroup has been deprecated, use TSharedPtr<FEdGraphSchemaAction> directly")
TSharedPtr<FGraphActionNode> AddChild(FGraphActionListBuilderBase::ActionGroup const& ActionSet);
PRAGMA_ENABLE_DEPRECATION_WARNINGS
/**
* Inserts a new action node (and any required category nodes) based off
* the provided Action. Inserts in alphabetical order.
*
* @param Action An action that you want the node to execute when picked.
* @return The new action node.
*/
TSharedPtr<FGraphActionNode> AddChildAlphabetical(const TSharedPtr<FEdGraphSchemaAction>& Action);
PRAGMA_DISABLE_DEPRECATION_WARNINGS
UE_DEPRECATED(5.5, "FGraphActionListBuilderBase::ActionGroup has been deprecated, use TSharedPtr<FEdGraphSchemaAction> directly")
TSharedPtr<FGraphActionNode> AddChildAlphabetical(FGraphActionListBuilderBase::ActionGroup const& ActionSet);
PRAGMA_ENABLE_DEPRECATION_WARNINGS
TSharedPtr<FGraphActionNode> AddSection(int32 Grouping, int32 InSectionID);
/**
* Sorts all child nodes by section, group, and type (additionally, can
* sort alphabetically if wanted).
*
* @param bAlphabetically Determines if we sort alphabetically on top of section/group/type.
* @param bRecursive Determines if we should sort all decendent nodes' children ass well.
*/
void SortChildren(bool bAlphabetically = true, bool bRecursive = true);
/**
* Returns a WeakPtr to the Parent Node
*/
TWeakPtr<FGraphActionNode> GetParentNode() const{ return ParentNode; }
/**
* Recursively collects all child/grandchild/decendent nodes.
*
* @param OutNodeArray The array to fill out with decendent nodes.
*/
void GetAllNodes(TArray< TSharedPtr<FGraphActionNode> >& OutNodeArray) const;
/**
* Recursively collects all child/grandchild/decendent action nodes.
*
* @param OutNodeArray The array to fill out with decendent nodes.
*/
void GetAllActionNodes(TArray<TSharedPtr<FGraphActionNode>>& OutNodeArray) const;
/**
* Recursively collects all decendent action/separator nodes (leaves out
* branching category-nodes).
*
* @param OutLeafArray The array to fill out with decendent leaf nodes.
*/
void GetLeafNodes(TArray< TSharedPtr<FGraphActionNode> >& OutLeafArray) const;
/** Returns the number of leaf nodes */
int32 GetTotalLeafNodes() const;
/**
* Takes the tree view and expands its elements for each child.
*
* @param TreeView The tree responsible for visualizing this node hierarchy.
* @param bRecursive Determines if you want children/decendents to expand their children as well.
*/
void ExpandAllChildren(TSharedPtr< STreeView< TSharedPtr<FGraphActionNode> > > TreeView, bool bRecursive = true);
/**
* Clears all children (not recursively... the TSharedPtrs should clean up
* appropriately).
*/
void ClearChildren();
/**
* Query to determine this node's type (there are five distinguishable node
* types: root, section heading, category, action, & group-divider).
*
* @return True if this is the type your queried about, otherwise false.
*/
bool IsRootNode() const;
bool IsSectionHeadingNode() const;
bool IsCategoryNode() const;
bool IsActionNode() const;
bool IsGroupDividerNode() const;
/**
* Determines if this node is a menu separator of some kind (either a
* "group-divider" or a "section heading").
*
* @return True if this is a menu divider, otherwise false.
*/
bool IsSeparator() const;
/**
* Retrieves this node's display name (for category and action nodes). The
* text string will be empty for separator and root nodes.
*
* @return The name to present this node with in the tree view (will be an empty text string if this is a separator node)
*/
FText const& GetDisplayName() const;
/**
* Walks the node chain backwards, constructing a category path (delimited
* by '|' characters). This includes this node's category (if it is a
* category node).
*
* @return A category path string, denoting the category hierarchy up to this node.
*/
FText GetCategoryPath() const;
/**
* Checks to see if this node contains at least one valid action.
*
* @return True is the Actions array contains a valid entry, otherwise false.
*/
bool HasValidAction() const;
/**
* Looks through this node's Actions array, and returns the first valid
* action it finds.
*
* @return This node's first valid action (will be an empty pointer if this is not an action node).
*/
TSharedPtr<FEdGraphSchemaAction> GetPrimaryAction() const;
/**
* Accessor to the node's RenameRequestEvent (for binding purposes). Do not
* Execute() the delegate from this function, instead call
* BroadcastRenameRequest() on the node.
*
* @return The node's internal RenameRequestEvent.
*/
FOnRenameRequestActionNode& OnRenameRequest() { return RenameRequestEvent; }
/**
* Executes the node's RenameRequestEvent if it is bound. Otherwise, it will
* mark the node as having a pending rename request.
*
* @return True if the broadcast went through, false if the "pending rename request" flag was set.
*/
bool BroadcastRenameRequest();
/**
* Sometimes a call to BroadcastRenameRequest() is made before the
* RenameRequestEvent has been bound. When that happens, this node is
* marked with a pending rename request. This method determines if that is
* the case for this node.
*
* @return True if a call to BroadcastRenameRequest() was made without a valid RenameRequestEvent.
*/
bool IsRenameRequestPending() const;
/** Returns the 'linearized' index of the node, including category nodes, useful for getting displayed position */
int32 GetLinearizedIndex(TSharedPtr<FGraphActionNode> Node) const;
private:
/**
*
*
* @param Grouping
* @param SectionID
*/
FGraphActionNode(int32 Grouping, int32 SectionID);
/**
* Constructor for action nodes. Private so that users go through AddChild().
*
* @param InAction
* @param Grouping
* @param SectionID
*/
FGraphActionNode(const TSharedPtr<FEdGraphSchemaAction>& InAction, int32 InGrouping, int32 InSectionID);
/**
*
*
* @param Parent
* @param Grouping
* @param SectionID
* @return
*/
static TSharedPtr<FGraphActionNode> NewSectionHeadingNode(TWeakPtr<FGraphActionNode> Parent, int32 Grouping, int32 SectionID);
/**
*
*
* @param Category
* @param Grouping
* @param SectionID
* @return
*/
static TSharedPtr<FGraphActionNode> NewCategoryNode(FString const& Category, int32 Grouping, int32 SectionID);
/**
*
*
* @param ActionNode
* @return
*/
static TSharedPtr<FGraphActionNode> NewActionNode(TSharedPtr<FEdGraphSchemaAction> const& ActionNode);
/**
*
*
* @param Parent
* @param Grouping
* @return
*/
static TSharedPtr<FGraphActionNode> NewGroupDividerNode(TWeakPtr<FGraphActionNode> Parent, int32 Grouping);
/**
* Iterates the CategoryStack, adding category-nodes as needed. The
* last category is what the node will be inserted under.
*
* @param CategoryStack A list of categories denoting where to nest the new node (the first element is the highest category)
* @param Idx Current point in the category stack that we have iterated to
* @param NodeToAdd The node you want inserted.
*/
void AddChildRecursively(const TArray<FString>& CategoryStack, int32 Idx, TSharedPtr<FGraphActionNode> NodeToAdd);
/**
* Looks through this node's children to see if a there already exists a
* node matching one we'd have to spawn (to parent the supplied NodeToAdd).
*
* @param ParentName The name of the category NodeToAdd wants to nest under.
* @param NodeToAdd The node that we'll be adding to this child.
* @return A child node matching the supplied parameters (will be empty if no match was found).
*/
TSharedPtr<FGraphActionNode> FindMatchingParent(FString const& ParentName, TSharedPtr<FGraphActionNode> NodeToAdd);
/**
* Adds the specified node directly to this node's Children array. Will
* create and insert separators if needed (if the node has a new group or
* section).
*
* @param NodeToAdd The node you want inserted.
*/
void InsertChild(TSharedPtr<FGraphActionNode> NodeToAdd);
void AddChildGrouping(TSharedPtr<FGraphActionNode> ActionNode, TWeakPtr<FGraphActionNode> Parent, bool bInsertAlphabetically);
void InsertChildAlphabetical(TSharedPtr<FGraphActionNode> NodeToAdd);
/** Recursive implementation helper for GetLinearizedIndex */
int32 GetLinearizedIndex(TSharedPtr<FGraphActionNode> Node, int32& Iter) const;
private:
/** The category or action name (depends on what type of node this is) */
FText DisplayText;
/** The node that this is a direct child of (empty if this is a root node) */
TWeakPtr<FGraphActionNode> ParentNode;
/** Tracks what groups have already been added (so we can easily determine what group-dividers we need) */
TSet<int32> ChildGroupings;
/** Tracks what sections have already been added (so we can easily determine what heading we need) */
TSet<int32> ChildSections;
/** When the item is first created, a rename request may occur before everything is setup for it. This toggles to true in those cases */
bool bPendingRenameRequest;
/** Delegate to trigger when a rename was requested on this node */
FOnRenameRequestActionNode RenameRequestEvent;
friend struct FGraphActionNodeImpl;
/** For sorting, when we don't alphabetically sort (so menu items don't jump around). */
int32 InsertOrder;
/** Root entry only, counts the total leaf entries in this tree */
int32 TotalLeafs;
};