// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "UObject/Object.h" #include "EditorUndoClient.h" #include "Framework/Commands/UICommandList.h" #include "Framework/Views/ITypedTableView.h" #include "Input/Reply.h" #include "Layout/Visibility.h" #include "Misc/Attribute.h" #include "Misc/TextFilter.h" #include "SlateFwd.h" #include "Styling/SlateColor.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "Widgets/SWidget.h" #include "Widgets/Views/STableRow.h" #include "Widgets/Views/STableViewBase.h" #include "Delegates/DelegateCombinations.h" #include "Framework/Views/ITypedTableView.h" #include "Types/SlateEnums.h" #include "BlueprintEditor.h" #include "Engine/PoseWatch.h" #include "PoseWatchManagerFolderTreeItem.h" #include "IPoseWatchManager.h" #include "PoseWatchManagerFwd.h" #include "SPoseWatchManagerTreeView.h" #include "PoseWatchManagerPublicTypes.h" #include "PoseWatchManagerStandaloneTypes.h" #include "PoseWatchManagerDragDrop.h" class FMenuBuilder; class UToolMenu; class IPoseWatchManagerColumn; class FPoseWatchManagerDefaultHierarchy; class SComboButton; template class STreeView; namespace PoseWatchManager { DECLARE_EVENT_OneParam(SPoseWatchManager, FTreeItemPtrEvent, FPoseWatchManagerTreeItemPtr); DECLARE_EVENT_TwoParams(SPoseWatchManager, FOnItemSelectionChanged, FPoseWatchManagerTreeItemPtr, ESelectInfo::Type); typedef TTextFilter TreeItemTextFilter; /** Structure that defines an operation that should be applied to the tree */ struct FPendingTreeOperation { enum EType { Added, Removed, Moved }; FPendingTreeOperation(EType InType, TSharedRef InItem) : Type(InType), Item(InItem) { } /** The type of operation that is to be applied */ EType Type; /** The tree item to which this operation relates */ FPoseWatchManagerTreeItemRef Item; }; /** Set of actions to apply to new tree items */ namespace ENewItemAction { enum Type { /** Do nothing when it is created */ None = 0, /** Select the item when it is created */ Select = 1 << 0, /** Scroll the item into view when it is created */ ScrollIntoView = 1 << 1, /** Interactively rename the item when it is created (implies the above) */ Rename = 1 << 2, }; } } class SPoseWatchManager : public IPoseWatchManager, public FGCObject { public: SLATE_BEGIN_ARGS(SPoseWatchManager) {} SLATE_END_ARGS() void Construct(const FArguments& InArgs, const FPoseWatchManagerInitializationOptions& InitOptions); SPoseWatchManager(); virtual ~SPoseWatchManager(); // SWidget interface virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override; virtual bool SupportsKeyboardFocus() const override; virtual FReply OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) override; /** Sends a requests to refresh the next chance it gets */ virtual void Refresh() override; void RefreshSelection(); /** Get a const reference to the actual tree hierarchy */ virtual const STreeView& GetTree() const override { return *PoseWatchManagerTreeView; } virtual const TSharedPtr& GetTreeView() const { return PoseWatchManagerTreeView; } /** @return Returns a string to use for highlighting results in the list */ virtual TAttribute GetFilterHighlightText() const; /** Set the keyboard focus to the manager */ virtual void SetKeyboardFocus() override; void SetColumnVisibility(FName ColumnId, bool bIsVisible); /** Returns the current sort mode of the specified column */ virtual EColumnSortMode::Type GetColumnSortMode(const FName ColumnId) const; /** Request that the tree be sorted at a convenient time */ virtual void RequestSort(); /** Requests a full refresh, which will clear the entire tree and rebuild it from scratch */ virtual void FullRefresh() override; public: /** Event to react to a user double click on a item */ PoseWatchManager::FTreeItemPtrEvent& GetDoubleClickEvent() { return OnDoubleClickOnTreeEvent; } PoseWatchManager::FOnItemSelectionChanged& GetOnItemSelectionChanged() { return OnItemSelectionChanged; } /** Set the item selection of the manager based on a selector function. Any items which return true will be added */ virtual void SetSelection(const TFunctionRef Selector) override; /** Set the selection status of a set of items in the scene manager */ void SetItemSelection(const TArray& InItems, bool bSelected, ESelectInfo::Type SelectInfo = ESelectInfo::Direct); /** Set the selection status of a single item in the scene manager */ void SetItemSelection(const FPoseWatchManagerTreeItemPtr& InItem, bool bSelected, ESelectInfo::Type SelectInfo = ESelectInfo::Direct); virtual FPoseWatchManagerTreeItemPtr GetSelection() const { return PoseWatchManagerTreeView->GetSelectedItems().Num() == 1 ? PoseWatchManagerTreeView->GetSelectedItems()[0] : nullptr; } FPoseWatchManagerTreeItemPtr FindParent(const IPoseWatchManagerTreeItem& InItem) const; /** Deselect all selected items */ void ClearSelection(); /** Sets the next item to rename */ void SetPendingRenameItem(const FPoseWatchManagerTreeItemPtr& InItem) { PendingRenameItem = InItem; Refresh(); } /** Retrieve an IPoseWatchManagerTreeItem by its ID if it exists in the tree */ FPoseWatchManagerTreeItemPtr GetTreeItem(FObjectKey, bool bIncludePending = false); /** Create a drag drop operation */ TSharedPtr CreateDragDropOperation(const TArray& InTreeItems) const; /** Parse a drag drop operation into a payload */ bool ParseDragDrop(FPoseWatchManagerDragDropPayload& OutPayload, const FDragDropOperation& Operation) const; /** Validate a drag drop operation on a drop target */ FPoseWatchManagerDragValidationInfo ValidateDrop(const IPoseWatchManagerTreeItem& DropTarget, const FPoseWatchManagerDragDropPayload& Payload) const; /** Called when a payload is dropped onto a target */ void OnDropPayload(IPoseWatchManagerTreeItem& DropTarget, const FPoseWatchManagerDragDropPayload& Payload, const FPoseWatchManagerDragValidationInfo& ValidationInfo) const; /** Called when a payload is dragged over an item */ FReply OnDragOverItem(const FDragDropEvent& Event, const IPoseWatchManagerTreeItem& Item) const; virtual uint32 GetTypeSortPriority(const IPoseWatchManagerTreeItem& Item) const; /** Used to test if manager related selection changes have already been handled */ bool GetIsReentrant() const { return bIsReentrant; } private: /** Empty all the tree item containers maintained by this manager */ void EmptyTreeItems(); /** Apply incremental changes to, or a complete repopulation of the tree */ void Populate(); /** Repopulates the entire tree */ void RepopulateEntireTree(); /** Adds a single new item to the pending map and creates an add operation for it */ void AddPendingItem(FPoseWatchManagerTreeItemPtr Item); /** Adds a new item and all of its children to the pending items. */ void AddPendingItemAndChildren(FPoseWatchManagerTreeItemPtr Item); /** Attempts to add a pending item to the current tree. Will add any parents if required. */ bool AddItemToTree(FPoseWatchManagerTreeItemRef InItem); /** Add an item to the tree, even if it doesn't match the filter terms. Used to add parent's that would otherwise be filtered out */ void AddUnfilteredItemToTree(FPoseWatchManagerTreeItemRef Item); /** Ensure that the specified item's parent is added to the tree, if applicable */ FPoseWatchManagerTreeItemPtr EnsureParentForItem(FPoseWatchManagerTreeItemRef Item); public: // Test the filters using stack-allocated data to prevent unnecessary heap allocations template FPoseWatchManagerTreeItemPtr CreateItemFor(const TreeItemData& Data, bool bForce = false) { const TreeItemType Temporary(Data); if (bForce || SearchBoxFilter->PassesFilter(Temporary)) { FPoseWatchManagerTreeItemPtr Result = MakeShareable(new TreeItemType(Data)); return Result; } return nullptr; } /** Instruct the manager to perform an action on the specified item when it is created */ void OnItemAdded(const FObjectKey& ItemID, uint8 ActionMask); void OnPoseWatchesChanged(UAnimBlueprint* InAnimBlueprint, UEdGraphNode* InNode); virtual void Rename_Execute() override; virtual void Delete_Execute(); /** Get the columns to be displayed in this manager */ const TMap>& GetColumns() const { return Columns; } /** @return Returns true if the text filter is currently active */ bool IsTextFilterActive() const; bool PassesTextFilter(const FPoseWatchManagerTreeItemPtr& Item) const { return SearchBoxFilter->PassesFilter(*Item); } bool HasSelectorFocus(FPoseWatchManagerTreeItemPtr Item) const { return PoseWatchManagerTreeView->Private_HasSelectorFocus(Item); } private: /** Map of columns that are shown on this manager. */ TMap> Columns; /** Set up the columns required for this manager */ void SetupColumns(SHeaderRow& HeaderRow); /** Refresh the scene manager for when a column was added or removed */ void RefreshColumns(); /** Populates OutSearchStrings with the strings associated with TreeItem that should be used in searching */ void PopulateSearchStrings(const IPoseWatchManagerTreeItem& Item, TArray< FString >& OutSearchStrings) const; public: /** Scroll the specified item into view */ void ScrollItemIntoView(const FPoseWatchManagerTreeItemPtr& Item); private: /** Called by STreeView to generate a table row for the specified item */ TSharedRef< ITableRow > OnGenerateRowForManagerTree(FPoseWatchManagerTreeItemPtr Item, const TSharedRef< STableViewBase >& OwnerTable); /** Called by STreeView to get child items for the specified parent item */ void OnGetChildrenForManagerTree(FPoseWatchManagerTreeItemPtr InParent, TArray< FPoseWatchManagerTreeItemPtr >& OutChildren); /** Called by STreeView when the tree's selection has changed */ void OnManagerTreeSelectionChanged(FPoseWatchManagerTreeItemPtr TreeItem, ESelectInfo::Type SelectInfo); /** Called by STreeView when an item is scrolled into view */ void OnManagerTreeItemScrolledIntoView(FPoseWatchManagerTreeItemPtr TreeItem, const TSharedPtr& Widget); void OnManagerTreeDoubleClick(FPoseWatchManagerTreeItemPtr TreeItem); /** Called when an item in the tree has been collapsed or expanded */ void OnItemExpansionChanged(FPoseWatchManagerTreeItemPtr TreeItem, bool bIsExpanded) const; private: /** Event required to keep the pose watch manager up to date */ void OnHierarchyChangedEvent(FPoseWatchManagerHierarchyChangedData Event); private: /** Called by the editable text control when the filter text is changed by the user */ void OnFilterTextChanged(const FText& InFilterText); /** Called by the filter button to get the image to display in the button */ const FSlateBrush* GetFilterButtonGlyph() const; /** @return The filter button tool-tip text */ FString GetFilterButtonToolTip() const; /** @return Returns whether the filter status line should be drawn */ EVisibility GetFilterStatusVisibility() const; /** Returns the current visibility of the Empty label */ EVisibility GetEmptyLabelVisibility() const; /** Returns the selection mode*/ ESelectionMode::Type GetSelectionMode() const; private: /** Called when the user right clicks in the tree view */ TSharedPtr OnOpenContextMenu(); /** Called when the user has clicked the button to add a new folder */ FReply OnCreateFolderClicked(); /** Requests a new folder be created, confirmation received with OnHierarchyChangedEvent */ void CreateFolder(); /** Binds our UI commands to delegates. */ void BindCommands(); private: /** Context menu opening delegate provided by the client */ FOnContextMenuOpening OnContextMenuOpening; /** List of pending operations to be applied to the tree */ TArray PendingOperations; /** Map of actions to apply to new tree items */ TMap NewItemActions; /** Our tree view */ TSharedPtr< SPoseWatchManagerTreeView > PoseWatchManagerTreeView; /** A map of all items we have in the tree */ FPoseWatchManagerTreeItemMap TreeItemMap; /** Pending tree items that are yet to be added the tree */ FPoseWatchManagerTreeItemMap PendingTreeItemMap; /** Root level tree items */ TArray RootTreeItems; TSharedPtr CommandList; private: /** true if the manager needs to be repopulated at the next appropriate opportunity */ uint8 bNeedsRefresh : 1; /** Processing a full refresh until pending items are processed */ uint8 bProcessingFullRefresh : 1; /** true if should do a full refresh */ uint8 bFullRefresh : 1; /** true if should refresh selection */ uint8 bSelectionDirty : 1; uint8 bNeedsColumnRefresh : 1; /** Reentrancy guard */ bool bIsReentrant; /* Widget containing the filtering text box */ TSharedPtr< SSearchBox > FilterTextBoxWidget; /** The header row of the manager */ TSharedPtr< SHeaderRow > HeaderRowWidget; /** The TextFilter attached to the SearchBox widget */ TSharedPtr< PoseWatchManager::TreeItemTextFilter > SearchBoxFilter; /** True if the search box will take keyboard focus next frame */ bool bPendingFocusNextFrame; /** The tree item that is currently pending a rename */ TWeakPtr PendingRenameItem; TUniquePtr Hierarchy; PoseWatchManager::FTreeItemPtrEvent OnDoubleClickOnTreeEvent; PoseWatchManager::FOnItemSelectionChanged OnItemSelectionChanged; private: virtual void AddReferencedObjects(FReferenceCollector& Collector) override {}; virtual FString GetReferencerName() const override { return TEXT("PoseWatchManager::SPoseWatchManager"); } bool bDisableIntermediateSorting; /** true if currently needs to be sorted */ bool bSortDirty; /** Specify which column to sort with */ FName SortByColumn; /** Currently selected sorting mode */ EColumnSortMode::Type SortMode; FBlueprintEditor* BlueprintEditor; /** Handles column sorting mode change */ void OnColumnSortModeChanged(const EColumnSortPriority::Type SortPriority, const FName& ColumnId, const EColumnSortMode::Type InSortMode); /** Sort the specified array of items based on the current sort column */ void SortItems(TArray& Items) const; /** Handler for recursively expanding/collapsing items */ void SetItemExpansionRecursive(FPoseWatchManagerTreeItemPtr Model, bool bInExpansionState); };