// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "AnimPreviewInstance.h" #include "Animation/DebugSkelMeshComponent.h" #include "Editor.h" #include "IAnimationEditor.h" #include "IPersonaToolkit.h" #include "PoseSearchDatabaseEditor.h" #include "PoseSearchDatabaseViewModel.h" #include "PoseSearchDebuggerDatabaseRowData.h" #include "PoseSearchDebuggerViewModel.h" #include "Fonts/FontMeasure.h" #include "Framework/Application/SlateApplication.h" #include "Preferences/PersonaOptions.h" #include "Styling/AppStyle.h" #include "Subsystems/AssetEditorSubsystem.h" #include "Widgets/Input/SHyperlink.h" #include "Widgets/Text/STextBlock.h" #define LOCTEXT_NAMESPACE "PoseSearchDebugger" namespace UE::PoseSearch::DebuggerDatabaseColumns { constexpr float ColumnWidthPadding = 10.f; constexpr float ColumnSortWidthPadding = 20.f; /** Column struct to represent each column in the debugger database */ struct IColumn : TSharedFromThis { explicit IColumn(int32 InSortIndex, bool InEnabled = true) : SortIndex(InSortIndex) , bEnabled(InEnabled) { ColumnId = FName(FString::Printf(TEXT("Column %d"), SortIndex)); } virtual ~IColumn() = default; FName ColumnId; /** Sorted left to right based on this index */ int32 SortIndex = 0; /** Current width, starts at 1 to be evenly spaced between all columns */ float Width = 1.0f; /** Disabled selectively with view options */ bool bEnabled = false; virtual FText GetLabel() const = 0; virtual FText GetLabelTooltip() const { return GetLabel(); } using FRowDataRef = TSharedRef; using FSortPredicate = TFunction; virtual FSortPredicate GetSortPredicate() const = 0; virtual TSharedRef GenerateWidget(const FRowDataRef& RowData) const = 0; virtual float ComputeColumnWidth(TConstArrayView Rows) const = 0; }; /** Column struct to represent each column in the debugger database */ struct ITextColumn : IColumn { explicit ITextColumn(int32 InSortIndex, bool InEnabled = true) : IColumn(InSortIndex, InEnabled) { } virtual ~ITextColumn() = default; virtual TSharedRef GenerateWidget(const FRowDataRef& RowData) const override { static FSlateFontInfo RowFont = FAppStyle::Get().GetFontStyle("DetailsView.CategoryTextStyle"); return SNew(STextBlock) .Font(RowFont) .Text_Lambda([this, RowData]() -> FText { return GetRowText(RowData); }) .ToolTipText_Lambda([this, RowData]() -> FText { return GetRowToolTipText(RowData); }) .Justification(ETextJustify::Center) .ColorAndOpacity_Lambda([this, RowData] { return GetColorAndOpacity(RowData); }); } protected: virtual FText GetRowText(const FRowDataRef& Row) const = 0; virtual FText GetRowToolTipText(const FRowDataRef& Row) const { return FText::GetEmpty(); } virtual FSlateColor GetColorAndOpacity(const FRowDataRef& Row) const { return FSlateColor(FLinearColor::White); } virtual float ComputeColumnWidth(TConstArrayView> Rows) const override { const TSharedRef FontMeasure = FSlateApplication::Get().GetRenderer()->GetFontMeasureService(); const FSlateFontInfo & CellTextStyle = FAppStyle::GetFontStyle("SmallText"); float MinWidth = FontMeasure->Measure(GetLabel(), CellTextStyle).X + ColumnWidthPadding; for (const TSharedRef& Row : Rows) { MinWidth = FMath::Max((FontMeasure->Measure(GetRowText(Row), CellTextStyle).X + ColumnWidthPadding), MinWidth); } return MinWidth; } }; struct FPoseIdx : ITextColumn { using ITextColumn::ITextColumn; virtual FText GetLabel() const override { return LOCTEXT("ColumnLabelPoseIndex", "Pose Id"); } virtual FText GetLabelTooltip() const override { return LOCTEXT("ColumnLabelTooltipPoseIndex", "Index of the Pose in the Database"); } virtual FSortPredicate GetSortPredicate() const override { return [](const FRowDataRef& Row0, const FRowDataRef& Row1) -> bool { return Row0->PoseIdx < Row1->PoseIdx; }; } virtual FText GetRowText(const FRowDataRef& Row) const override { return FText::AsNumber(Row->PoseIdx, &FNumberFormattingOptions::DefaultNoGrouping()); } }; struct FAssetIdx : ITextColumn { using ITextColumn::ITextColumn; virtual FText GetLabel() const override { return LOCTEXT("ColumnLabelAssetIndex", "Asset Id"); } virtual FText GetLabelTooltip() const override { return LOCTEXT("ColumnLabelTooltipAssetIndex", "Index of the Asset in the Database"); } virtual FSortPredicate GetSortPredicate() const override { return [](const FRowDataRef& Row0, const FRowDataRef& Row1) -> bool { return Row0->DbAssetIdx < Row1->DbAssetIdx; }; } virtual FText GetRowText(const FRowDataRef& Row) const override { return FText::AsNumber(Row->DbAssetIdx, &FNumberFormattingOptions::DefaultNoGrouping()); } }; struct FDatabaseName : IColumn { TSharedPtr DebuggerViewModel; FDatabaseName(int32 InSortIndex, TSharedPtr InDebuggerViewModel) : IColumn(InSortIndex) , DebuggerViewModel(InDebuggerViewModel) { } virtual FText GetLabel() const override { return LOCTEXT("ColumnLabelDatabaseName", "Database"); } virtual FText GetLabelTooltip() const override { return LOCTEXT("ColumnLabelTooltipDatabaseName", "Database Name"); } virtual FSortPredicate GetSortPredicate() const override { return [](const FRowDataRef& Row0, const FRowDataRef& Row1) -> bool { return Row0->SharedData->DatabaseName < Row1->SharedData->DatabaseName; }; } virtual TSharedRef GenerateWidget(const FRowDataRef& RowData) const override { return SNew(SHyperlink) .Text_Lambda([RowData]() -> FText { return FText::FromString(RowData->SharedData->DatabaseName); }) .TextStyle(&FCoreStyle::Get().GetWidgetStyle("SmallText")) .ToolTipText_Lambda([RowData]() -> FText { return FText::Format( LOCTEXT("DatabaseHyperlinkTooltipFormat", "Open database '{0}'"), FText::FromString(RowData->SharedData->DatabasePath)); }) .OnNavigate_Lambda([this, RowData]() { UObject* Asset = nullptr; // Load asset if (UPackage* Package = LoadPackage(NULL, *RowData->SharedData->DatabasePath, LOAD_NoRedirects)) { Package->FullyLoad(); const FString AssetName = FPaths::GetBaseFilename(RowData->SharedData->DatabasePath); Asset = FindObject(Package, *AssetName); } else { // Fallback for unsaved assets Asset = FindObject(nullptr, *RowData->SharedData->DatabasePath); } // Open editor if (Asset != nullptr) { if (UAssetEditorSubsystem* AssetEditorSS = GEditor->GetEditorSubsystem()) { AssetEditorSS->OpenEditorForAsset(Asset); if (IAssetEditorInstance* Editor = AssetEditorSS->FindEditorForAsset(Asset, true)) { if (Editor->GetEditorName() == FName("PoseSearchDatabaseEditor")) { FDatabaseEditor* DatabaseEditor = static_cast(Editor); // Open asset paused and at specific time as seen on the pose search debugger. DatabaseEditor->SetSelectedPoseIdx(RowData->PoseIdx, DebuggerViewModel->GetDrawQuery(), RowData->SharedData->QueryVector); } } } } }); } virtual float ComputeColumnWidth(TConstArrayView Rows) const override { const TSharedRef FontMeasure = FSlateApplication::Get().GetRenderer()->GetFontMeasureService(); const FSlateFontInfo & CellTextStyle = FAppStyle::GetFontStyle("SmallText"); float MinWidth = FontMeasure->Measure(GetLabel(), CellTextStyle).X + ColumnWidthPadding; for (const TSharedRef& Row : Rows) { MinWidth = FMath::Max(FontMeasure->Measure(Row->SharedData->DatabaseName, CellTextStyle).X + ColumnWidthPadding, MinWidth); } return MinWidth; } }; struct FAssetName : IColumn { using IColumn::IColumn; virtual FText GetLabel() const override { return LOCTEXT("ColumnLabelAssetName", "Asset"); } virtual FText GetLabelTooltip() const override { return LOCTEXT("ColumnLabelTooltipAssetName", "Animation Asset Name"); } virtual FSortPredicate GetSortPredicate() const override { return [](const FRowDataRef& Row0, const FRowDataRef& Row1) -> bool { return Row0->AssetName < Row1->AssetName; }; } virtual TSharedRef GenerateWidget(const FRowDataRef& RowData) const override { return SNew(SHyperlink) .Text_Lambda([RowData]() -> FText { return FText::FromString(RowData->AssetName); }) .TextStyle(&FCoreStyle::Get().GetWidgetStyle("SmallText")) .ToolTipText_Lambda([RowData]() -> FText { return FText::Format( LOCTEXT("AssetHyperlinkTooltipFormat", "Open asset '{0}'"), FText::FromString(RowData->AssetPath)); }) .OnNavigate_Lambda([RowData]() { UObject* Asset = nullptr; // Load asset if (UPackage* Package = LoadPackage(NULL, *RowData->AssetPath, LOAD_NoRedirects)) { Package->FullyLoad(); const FString AssetName = FPaths::GetBaseFilename(RowData->AssetPath); Asset = FindObject(Package, *AssetName); } else { // Fallback for unsaved assets Asset = FindObject(nullptr, *RowData->AssetPath); } // Open editor if (Asset != nullptr) { if (UAssetEditorSubsystem* AssetEditorSS = GEditor->GetEditorSubsystem()) { AssetEditorSS->OpenEditorForAsset(Asset); if (IAssetEditorInstance* Editor = AssetEditorSS->FindEditorForAsset(Asset, true)) { if (Editor->GetEditorName() == "AnimationEditor") { const IAnimationEditor* AnimationEditor = static_cast(Editor); const UDebugSkelMeshComponent* PreviewComponent = AnimationEditor->GetPersonaToolkit()->GetPreviewMeshComponent(); // Open asset paused and at specific time as seen on the pose search debugger. PreviewComponent->PreviewInstance->SetPosition(RowData->AssetTime); PreviewComponent->PreviewInstance->SetPlaying(false); PreviewComponent->PreviewInstance->SetBlendSpacePosition(RowData->BlendParameters); } } } } }); } virtual float ComputeColumnWidth(TConstArrayView Rows) const override { const TSharedRef FontMeasure = FSlateApplication::Get().GetRenderer()->GetFontMeasureService(); const FSlateFontInfo & CellTextStyle = FAppStyle::GetFontStyle("SmallText"); float MinWidth = FontMeasure->Measure(GetLabel(), CellTextStyle).X + ColumnWidthPadding; for (const TSharedRef& Row : Rows) { MinWidth = FMath::Max(FontMeasure->Measure(Row->AssetName, CellTextStyle).X, MinWidth); } return MinWidth; } }; struct FFrame : ITextColumn { using ITextColumn::ITextColumn; virtual FText GetLabel() const override { return LOCTEXT("ColumnLabelFrame", "Frame"); } virtual FText GetLabelTooltip() const override { return LOCTEXT("ColumnLabelTooltipFrame", "Frame number from the start of the Animation Asset"); } virtual FSortPredicate GetSortPredicate() const override { return [](const FRowDataRef& Row0, const FRowDataRef& Row1) -> bool { return Row0->AnimFrame < Row1->AnimFrame; }; } virtual FText GetRowText(const FRowDataRef& Row) const override { return FText::AsNumber(Row->AnimFrame, &FNumberFormattingOptions::DefaultNoGrouping()); } }; struct FTime : ITextColumn { using ITextColumn::ITextColumn; virtual FText GetLabel() const override { return LOCTEXT("ColumnLabelTime", "Time"); } virtual FText GetLabelTooltip() const override { return LOCTEXT("ColumnLabelTooltipTime", "Time in seconds from the start of the Animation Asset"); } virtual FSortPredicate GetSortPredicate() const override { return [](const FRowDataRef& Row0, const FRowDataRef& Row1) -> bool { return Row0->AssetTime < Row1->AssetTime; }; } virtual FText GetRowText(const FRowDataRef& Row) const override { return FText::AsNumber(Row->AssetTime, &FNumberFormattingOptions().SetUseGrouping(false).SetMaximumFractionalDigits(2)); } }; struct FPercentage : ITextColumn { using ITextColumn::ITextColumn; virtual FText GetLabel() const override { return LOCTEXT("ColumnLabelPercentage", "Percent"); } virtual FText GetLabelTooltip() const override { return LOCTEXT("ColumnLabelTooltipPercentage", "Time in percentage from the start of the Animation Asset"); } virtual FSortPredicate GetSortPredicate() const override { return [](const FRowDataRef& Row0, const FRowDataRef& Row1) -> bool { return Row0->AnimPercentage < Row1->AnimPercentage; }; } virtual FText GetRowText(const FRowDataRef& Row) const override { return FText::AsPercent(Row->AnimPercentage, &FNumberFormattingOptions().SetMaximumFractionalDigits(2)); } }; struct FCost : ITextColumn { using ITextColumn::ITextColumn; virtual FText GetLabel() const override { return LOCTEXT("ColumnLabelCost", "Cost"); } virtual FText GetLabelTooltip() const override { return LOCTEXT("ColumnLabelTooltipCost", "Total Cost of the associated Pose"); } virtual FSortPredicate GetSortPredicate() const override { return [](const FRowDataRef& Row0, const FRowDataRef& Row1) -> bool { return Row0->PoseCost < Row1->PoseCost; }; } virtual FText GetRowText(const FRowDataRef& Row) const override { if (Row->PoseCost.IsValid()) { return FText::AsNumber(Row->PoseCost); } else { return FText::FromString(TEXT("-")); } } virtual FSlateColor GetColorAndOpacity(const FRowDataRef& Row) const override { return Row->CostColor; } }; struct FPCACost : ITextColumn { using ITextColumn::ITextColumn; virtual FText GetLabel() const override { return LOCTEXT("ColumnLabelPCACost", "PCA Cost"); } virtual FText GetLabelTooltip() const override { return LOCTEXT("ColumnLabelTooltipPCACost", "Total PCA Cost of the associated Pose"); } virtual FSortPredicate GetSortPredicate() const override { return [](const FRowDataRef& Row0, const FRowDataRef& Row1) -> bool { return Row0->PosePCACost < Row1->PosePCACost; }; } virtual FText GetRowText(const FRowDataRef& Row) const override { return FText::AsNumber(Row->PosePCACost); } virtual FSlateColor GetColorAndOpacity(const FRowDataRef& Row) const override { return Row->PCACostColor; } }; struct FChannelBreakdownCostColumn : ITextColumn { FChannelBreakdownCostColumn(int32 SortIndex, int32 InBreakdownCostIndex, const FText& InLabel) : ITextColumn(SortIndex) , Label(InLabel) , BreakdownCostIndex(InBreakdownCostIndex) { } virtual FText GetLabel() const override { return Label; } virtual FText GetLabelTooltip() const override { return FText::Format(LOCTEXT("ColumnLabelTooltipChannelBreakdownCost", "Breakdown Cost for the Channel '{0}'"), Label); } virtual FSortPredicate GetSortPredicate() const override { return [this](const FRowDataRef& Row0, const FRowDataRef& Row1) -> bool { return Row0->CostBreakdowns[BreakdownCostIndex] < Row1->CostBreakdowns[BreakdownCostIndex]; }; } virtual FText GetRowText(const FRowDataRef& Row) const override { const float LabelCost = Row->CostBreakdowns[BreakdownCostIndex]; if (LabelCost != UE_MAX_FLT) { return FText::AsNumber(LabelCost); } return FText::FromString(TEXT("-")); } virtual FSlateColor GetColorAndOpacity(const FRowDataRef& Row) const override { return Row->CostBreakdownsColors[BreakdownCostIndex]; } FText Label; int32 BreakdownCostIndex = INDEX_NONE; }; struct FCostModifier : ITextColumn { using ITextColumn::ITextColumn; virtual FText GetLabel() const override { return LOCTEXT("ColumnLabelCostModifier", "Bias"); } virtual FText GetLabelTooltip() const override { return LOCTEXT("ColumnLabelTooltipCostModifier", "Total Cost for all the Bias contributions"); } virtual FSortPredicate GetSortPredicate() const override { return [](const FRowDataRef& Row0, const FRowDataRef& Row1) -> bool { return Row0->PoseCost.GetCostAddend() < Row1->PoseCost.GetCostAddend(); }; } virtual FText GetRowText(const FRowDataRef& Row) const override { return FText::AsNumber(Row->PoseCost.GetCostAddend()); } }; struct FContinuingPoseCost : ITextColumn { using ITextColumn::ITextColumn; virtual FText GetLabel() const override { return LOCTEXT("ColumnLabelContinuingPoseCost", "ContinuingPoseCost"); } virtual FText GetLabelTooltip() const override { return LOCTEXT("ColumnLabelTooltipContinuingPoseCost", "Continuing Pose Cost Bias contribution"); } virtual FSortPredicate GetSortPredicate() const override { return [](const FRowDataRef& Row0, const FRowDataRef& Row1) -> bool { return Row0->PoseCost.GetContinuingPoseCostAddend() < Row1->PoseCost.GetContinuingPoseCostAddend(); }; } virtual FText GetRowText(const FRowDataRef& Row) const override { return FText::AsNumber(Row->PoseCost.GetContinuingPoseCostAddend()); } }; struct FContinuingInteractionCost : ITextColumn { using ITextColumn::ITextColumn; virtual FText GetLabel() const override { return LOCTEXT("ColumnLabelContinuingInteractionCost", "ContinuingInteractionCost"); } virtual FText GetLabelTooltip() const override { return LOCTEXT("ColumnLabelTooltipContinuingInteractionCost", "Continuing Interaction Cost Bias contribution"); } virtual FSortPredicate GetSortPredicate() const override { return [](const FRowDataRef& Row0, const FRowDataRef& Row1) -> bool { return Row0->PoseCost.GetContinuingInteractionCostAddend() < Row1->PoseCost.GetContinuingInteractionCostAddend(); }; } virtual FText GetRowText(const FRowDataRef& Row) const override { return FText::AsNumber(Row->PoseCost.GetContinuingInteractionCostAddend()); } }; struct FNotifyCost : ITextColumn { using ITextColumn::ITextColumn; virtual FText GetLabel() const override { return LOCTEXT("ColumnLabelNotifyCost", "NotifyCost"); } virtual FText GetLabelTooltip() const override { return LOCTEXT("ColumnLabelTooltipNotifyCost", "Notify Cost Bias contribution"); } virtual FSortPredicate GetSortPredicate() const override { return [](const FRowDataRef& Row0, const FRowDataRef& Row1) -> bool { return Row0->PoseCost.GetNotifyCostAddend() < Row1->PoseCost.GetNotifyCostAddend(); }; } virtual FText GetRowText(const FRowDataRef& Row) const override { return FText::AsNumber(Row->PoseCost.GetNotifyCostAddend()); } }; struct FMirrored : ITextColumn { using ITextColumn::ITextColumn; virtual FText GetLabel() const override { return LOCTEXT("ColumnLabelMirrored", "Mirror"); } virtual FText GetLabelTooltip() const override { return LOCTEXT("ColumnLabelTooltipMirrored", "Mirror state of the associated Pose"); } virtual FSortPredicate GetSortPredicate() const override { return [](const FRowDataRef& Row0, const FRowDataRef& Row1) -> bool { return Row0->bMirrored < Row1->bMirrored; }; } virtual FText GetRowText(const FRowDataRef& Row) const override { return FText::Format(LOCTEXT("Mirrored", "{0}"), { Row->bMirrored } ); } }; struct FLooping : ITextColumn { using ITextColumn::ITextColumn; virtual FText GetLabel() const override { return LOCTEXT("ColumnLabelLooping", "Loop"); } virtual FText GetLabelTooltip() const override { return LOCTEXT("ColumnLabelTooltipLooping", "Loop state of the associated Pose"); } virtual FSortPredicate GetSortPredicate() const override { return [](const FRowDataRef& Row0, const FRowDataRef& Row1) -> bool { return Row0->bLooping < Row1->bLooping; }; } virtual FText GetRowText(const FRowDataRef& Row) const override { return FText::Format(LOCTEXT("Looping", "{0}"), { Row->bLooping }); } }; struct FBlendParameters : ITextColumn { using ITextColumn::ITextColumn; virtual FText GetLabel() const override { return LOCTEXT("ColumnLabelBlendParams", "Blend Params"); } virtual FText GetLabelTooltip() const override { return LOCTEXT("ColumnLabelBlendTooltipParams", "Blend Params used to sample the associated BlendSpace asset"); } virtual FSortPredicate GetSortPredicate() const override { return [](const FRowDataRef& Row0, const FRowDataRef& Row1) -> bool { return ( Row0->BlendParameters[0] < Row1->BlendParameters[0] || Row0->BlendParameters[1] < Row1->BlendParameters[1]); }; } virtual FText GetRowText(const FRowDataRef& Row) const override { return FText::Format(LOCTEXT("Blend Parameters", "{0}, {1}"), FText::AsNumber(Row->BlendParameters[0]), FText::AsNumber(Row->BlendParameters[1])); } }; struct FPoseCandidateFlags : ITextColumn { using ITextColumn::ITextColumn; virtual FText GetLabel() const override { return LOCTEXT("ColumnLabelPoseCandidateFlags", "Flags"); } virtual FText GetLabelTooltip() const override { return LOCTEXT("ColumnLabelTooltipPoseCandidateFlags", "Flags indicating why a Pose has been discarded"); } virtual FSortPredicate GetSortPredicate() const override { return [](const FRowDataRef& Row0, const FRowDataRef& Row1) -> bool { return Row0->PoseCandidateFlags < Row1->PoseCandidateFlags; }; } virtual FText GetRowText(const FRowDataRef& Row) const override { if (EnumHasAnyFlags(Row->PoseCandidateFlags, EPoseCandidateFlags::AnyDiscardedMask)) { FString Sring; if (EnumHasAnyFlags(Row->PoseCandidateFlags, EPoseCandidateFlags::DiscardedBy_PoseJumpThresholdTime)) { Sring.Append("J "); } if (EnumHasAnyFlags(Row->PoseCandidateFlags, EPoseCandidateFlags::DiscardedBy_PoseReselectHistory)) { Sring.Append("H "); } if (EnumHasAnyFlags(Row->PoseCandidateFlags, EPoseCandidateFlags::DiscardedBy_BlockTransition)) { Sring.Append("B "); } if (EnumHasAnyFlags(Row->PoseCandidateFlags, EPoseCandidateFlags::DiscardedBy_PoseFilter)) { Sring.Append("F "); } if (EnumHasAnyFlags(Row->PoseCandidateFlags, EPoseCandidateFlags::DiscardedBy_AssetIdxFilter)) { Sring.Append("A "); } if (EnumHasAnyFlags(Row->PoseCandidateFlags, EPoseCandidateFlags::DiscardedBy_Search)) { Sring.Append("S "); } if (EnumHasAnyFlags(Row->PoseCandidateFlags, EPoseCandidateFlags::DiscardedBy_AssetReselection)) { Sring.Append("R "); } return FText::FromString(Sring); } return FText::GetEmpty(); } virtual FText GetRowToolTipText(const FRowDataRef& Row) const override { if (EnumHasAnyFlags(Row->PoseCandidateFlags, EPoseCandidateFlags::AnyDiscardedMask)) { FTextBuilder TextBuilder; TextBuilder.AppendLine(LOCTEXT("DiscardedBy_Reason_Tooltip", "Pose discarded because of:")); if (EnumHasAnyFlags(Row->PoseCandidateFlags, EPoseCandidateFlags::DiscardedBy_PoseJumpThresholdTime)) { TextBuilder.AppendLine(LOCTEXT("DiscardedBy_PoseJumpThresholdTime_Tooltip", "(J) Pose Jump Threshold Time")); } if (EnumHasAnyFlags(Row->PoseCandidateFlags, EPoseCandidateFlags::DiscardedBy_PoseReselectHistory)) { TextBuilder.AppendLine(LOCTEXT("DiscardedBy_PoseReselectHistory_Tooltip", "(H) Pose Reselect History")); } if (EnumHasAnyFlags(Row->PoseCandidateFlags, EPoseCandidateFlags::DiscardedBy_BlockTransition)) { TextBuilder.AppendLine(LOCTEXT("DiscardedBy_BlockTransition_Tooltip", "(B) Block Transition")); } if (EnumHasAnyFlags(Row->PoseCandidateFlags, EPoseCandidateFlags::DiscardedBy_PoseFilter)) { TextBuilder.AppendLine(LOCTEXT("DiscardedBy_PoseFilter_Tooltip", "(F) Filter")); } if (EnumHasAnyFlags(Row->PoseCandidateFlags, EPoseCandidateFlags::DiscardedBy_AssetIdxFilter)) { TextBuilder.AppendLine(LOCTEXT("DiscardedBy_AssetIdxFilter_Tooltip", "(A) Asset Idx Filter")); } if (EnumHasAnyFlags(Row->PoseCandidateFlags, EPoseCandidateFlags::DiscardedBy_Search)) { TextBuilder.AppendLine(LOCTEXT("DiscardedBy_Search_Tooltip", "(S) Search")); } if (EnumHasAnyFlags(Row->PoseCandidateFlags, EPoseCandidateFlags::DiscardedBy_AssetReselection)) { TextBuilder.AppendLine(LOCTEXT("DiscardedBy_AssetReselection_Tooltip", "(R) Disable Reselection")); } return TextBuilder.ToText(); } return FText::GetEmpty(); } }; } // namespace UE::PoseSearch::DebuggerDatabaseColumns #undef LOCTEXT_NAMESPACE