Files
UnrealEngine/Engine/Plugins/Developer/NamingTokens/Source/NamingTokensUI/Private/SNamingTokensDataTreeView.h
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

273 lines
8.6 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "NamingTokenData.h"
#include "Utils/NamingTokenUtils.h"
#include "Widgets/Views/STreeView.h"
/**
* Represents naming token items in our tree view.
*/
struct FNamingTokenDataTreeItem : TSharedFromThis<FNamingTokenDataTreeItem>
{
/** The token data, null on namespace entries. */
TSharedPtr<FNamingTokenData> NamingTokenData;
/** The namespace for this token. May be set regardless of entry type. */
FString Namespace;
/** The friendly display name for the namespace. */
FText NamespaceDisplayName;
/** Children of a namespace. */
TArray<TSharedPtr<FNamingTokenDataTreeItem>> ChildItems;
/** The parent owning this entry. */
TWeakPtr<FNamingTokenDataTreeItem> Parent;
/** The flattened item index in the tree. */
int32 ItemIndex = INDEX_NONE;
/** If this entry is globally registered in the naming tokens subsystem. */
bool bIsGlobal = false;
bool ShouldItemBeExpanded() const { return true; }
/** Checks if this item matches a filter returning a simple percentage score on the match. */
float MatchesFilter(const FString& FilterText, bool bIsExactNamespaceMatch = false) const
{
if (FilterText.IsEmpty())
{
return 0.5f;
}
if (IsNamespace())
{
if (Namespace == FilterText) // Exact match requires true namespace being typed out...
{
return 1.f;
}
if (!bIsExactNamespaceMatch &&
(Namespace.StartsWith(FilterText, ESearchCase::IgnoreCase) // ...Otherwise, we can check partial match and the display name.
|| NamespaceDisplayName.ToString().StartsWith(FilterText, ESearchCase::IgnoreCase)))
{
return 0.75f;
}
return 0.f;
}
check(NamingTokenData.IsValid());
if (NamingTokenData->TokenKey.Equals(FilterText, ESearchCase::CaseSensitive))
{
return 1.f;
}
if (NamingTokenData->TokenKey.Equals(FilterText, ESearchCase::IgnoreCase))
{
return 0.9f;
}
if (NamingTokenData->TokenKey.StartsWith(FilterText, ESearchCase::IgnoreCase)
|| NamingTokenData->DisplayName.ToString().StartsWith(FilterText, ESearchCase::IgnoreCase))
{
return 0.75f;
}
return 0.f;
}
/** True iff this is considered a namespace entry. */
bool IsNamespace() const
{
return !NamingTokenData.IsValid();
}
/** Convert the item to the best text representation. */
FString ToString(bool bFullyQualified) const
{
if (NamingTokenData.IsValid())
{
return bFullyQualified ?
UE::NamingTokens::Utils::CombineNamespaceAndTokenKey(Namespace, NamingTokenData->TokenKey)
: NamingTokenData->TokenKey;
}
return Namespace;
}
bool operator==(const FNamingTokenDataTreeItem& Other) const
{
return NamingTokenData == Other.NamingTokenData && Namespace == Other.Namespace;
}
};
typedef TSharedPtr<FNamingTokenDataTreeItem> FNamingTokenDataTreeItemPtr;
/**
* A tree view for naming token filtering.
*/
class SNamingTokenDataTreeView : public STreeView<FNamingTokenDataTreeItemPtr>
{
public:
void Construct(const FArguments& InArgs, const TSharedPtr<class SNamingTokenDataTreeViewWidget>& InTreeViewWidget);
/** Recursively set expansion state of tree view to match items. */
void SetExpansionStateFromItems(const TArray<FNamingTokenDataTreeItemPtr>& InTreeItems);
/** Pointer to our owning widget. */
const TWeakPtr<SNamingTokenDataTreeViewWidget>& GetOwningTreeViewWidget() const { return OwningTreeViewWidget; }
// ~Begin STreeView
virtual bool HasKeyboardFocus() const override { return false; }
virtual FReply OnFocusReceived(const FGeometry& MyGeometry, const FFocusEvent& InFocusEvent) override;
// ~End STreeView
private:
/** The owning tree view widget. */
TWeakPtr<SNamingTokenDataTreeViewWidget> OwningTreeViewWidget;
};
/**
* Each row of the tree view.
*/
class SNamingTokenDataTreeViewRow final : public SMultiColumnTableRow<FNamingTokenDataTreeItemPtr>
{
public:
SLATE_BEGIN_ARGS(SNamingTokenDataTreeViewRow) {}
/** The list item for this row. */
SLATE_ARGUMENT(FNamingTokenDataTreeItemPtr, Item)
SLATE_END_ARGS()
void Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTable);
// ~Begin SMultiColumnTableRow interface
virtual TSharedRef<SWidget> GenerateWidgetForColumn(const FName& ColumnName) override;
// ~End SMultiColumnTableRow interface
private:
static const FEditableTextBoxStyle& GetCustomEditableTextBoxStyle();
private:
/** The item associated with this row of data. */
TWeakPtr<FNamingTokenDataTreeItem> Item;
};
/**
* Widget for displaying namespaces and tokens to choose from.
*/
class SNamingTokenDataTreeViewWidget final : public SCompoundWidget
{
using FOnSelectionChanged = STreeView<FNamingTokenDataTreeItemPtr>::FOnSelectionChanged;
DECLARE_DELEGATE_OneParam(FOnItemSelected, FNamingTokenDataTreeItemPtr);
public:
SLATE_BEGIN_ARGS(SNamingTokenDataTreeViewWidget) {}
/** If a selection is changed, such as arrow keys or a single click. */
SLATE_EVENT(FOnSelectionChanged, OnSelectionChanged)
/** If an item is formally selected, such as by pressing enter or a double click. */
SLATE_EVENT(FOnItemSelected, OnItemSelected)
/** Event if this widget or its children receives focus. */
SLATE_EVENT(FSimpleDelegate, OnFocused)
SLATE_END_ARGS()
virtual ~SNamingTokenDataTreeViewWidget() override;
void Construct(const FArguments& InArgs);
/** Populate all possible tree items */
void PopulateTreeItems();
/** Filter down to specific tokens and namespaces. */
void FilterTreeItems(const FString& InFilterText);
/**
* Provide a key event from the user to determine a navigation path, highlighting an item.
* @return True if the key event contained a recognized navigation key.
*/
bool ForwardKeyEventForNavigation(const FKeyEvent& InKeyEvent);
/** Retrieve the currently selected item. */
FNamingTokenDataTreeItemPtr GetSelectedItem() const;
/** Retrieve an item given its absolute index. */
FNamingTokenDataTreeItemPtr GetItemFromIndex(int32 InIndex) const;
/** Get the token filter text applied. */
const FString& GetTokenFilterText() const { return TokenFilterText; }
/** Get the namespace filter text applied. */
const FString& GetNamespaceFilterText() const { return NamespaceFilterText; }
/** Informs us we're receiving focus, such as from a child of the treeview. */
void NotifyFocusReceived();
// ~Begin SCompoundWidget
virtual FReply OnFocusReceived(const FGeometry& MyGeometry, const FFocusEvent& InFocusEvent) override;
// ~End SCompoundWidget
private:
/** Find an item given its index. */
FNamingTokenDataTreeItemPtr GetItemFromIndex(int32 InIndex, const TArray<FNamingTokenDataTreeItemPtr>& InItems) const;
/** Set the currently selected item given an absolute index. */
void SetItemFromIndex(int32 InIndex);
/** Clears the currently selected item. */
void ClearCurrentSelection();
/** Officially complete and report the selection. Called from tab or double-click. */
void FinalizeSelection(FNamingTokenDataTreeItemPtr SelectedItem);
public:
static FName PrimaryColumnName() { return "Primary"; }
private:
/** Called by STreeView for each row being generated. */
TSharedRef<ITableRow> OnGenerateRowForTree(FNamingTokenDataTreeItemPtr Item, const TSharedRef<STableViewBase>& OwnerTable);
/** Called by STreeView to get child items for the specified parent item. */
void OnGetChildrenForTree(FNamingTokenDataTreeItemPtr InParent, TArray<FNamingTokenDataTreeItemPtr>& OutChildren);
/** Called when an item in the tree has been collapsed or expanded. */
void OnItemExpansionChanged(FNamingTokenDataTreeItemPtr TreeItem, bool bIsExpanded) const;
/** Called when the tree view changes selection. */
void OnTreeViewItemSelectionChanged(FNamingTokenDataTreeItemPtr SelectedItem, ESelectInfo::Type SelectInfo);
/** Called when an item in the tree view is double-clicked. */
void OnTreeViewItemDoubleClicked(FNamingTokenDataTreeItemPtr SelectedItem);
private:
/** Our tree view widget. */
TSharedPtr<SNamingTokenDataTreeView> TreeView;
/** The top most items on the tree view without a filter. */
TArray<FNamingTokenDataTreeItemPtr> RootTreeItems;
/** Items filtered in a search. */
TArray<FNamingTokenDataTreeItemPtr> FilteredRootTreeItems;
/** The last filter applied. */
FString LastFilterText;
/** Last token filter text applied. */
FString TokenFilterText;
/** Last namespace filter text applied. */
FString NamespaceFilterText;
/** Index of tree item currently selected. */
int32 CurrentSelectionIndex = 0;
/** Delegate for when selection changes. */
FOnSelectionChanged ItemSelectionChangedDelegate;
/** Delegate for when an item is fully selected. */
FOnItemSelected ItemSelectedDelegate;
/** Delegate if we have received focus. */
FSimpleDelegate OnFocusedDelegate;
};