Files
UnrealEngine/Engine/Source/Editor/MassEntityDebugger/Private/SMassFragmentsView.cpp
2025-05-18 13:04:45 +08:00

270 lines
8.6 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SMassFragmentsView.h"
#include "MassDebuggerModel.h"
#include "MassEntityManager.h"
#include "MassArchetypeData.h"
#include "MassProcessingTypes.h"
#include "MassProcessor.h"
#include "Styling/AppStyle.h"
#include "Widgets/Input/SButton.h"
#include "Widgets/Views/SHeaderRow.h"
#include "Widgets/Text/STextBlock.h"
#include "Framework/Views/TableViewMetadata.h"
#include "Misc/TextFilter.h"
#define LOCTEXT_NAMESPACE "SMassDebugger"
//----------------------------------------------------------------------//
// SFragmentsView
//----------------------------------------------------------------------//
namespace UE::MassDebugger::FragmentsView
{
namespace Private
{
static const FLazyName ColumnName = TEXT("Name");
static const FLazyName ColumnDebugControls = TEXT("DebugControls");
static const FLazyName ColumnNumArchetypes = TEXT("Archetypes");
static const FLazyName ColumnNumEntities = TEXT("Entities");
}
void SFragmentsView::Construct(const FArguments& InArgs, TSharedRef<FMassDebuggerModel> InDebuggerModel)
{
#if WITH_MASSENTITY_DEBUG
Initialize(InDebuggerModel);
TSharedRef<SHeaderRow> HeaderRowWidget =
SNew(SHeaderRow)
+ SHeaderRow::Column(Private::ColumnName)
.DefaultLabel(LOCTEXT("FragmentColumnName", "Fragment Type"))
.FillWidth(0.5f)
.SortMode(this, &SFragmentsView::GetColumnSortMode, Private::ColumnName.Resolve())
.OnSort(this, &SFragmentsView::OnColumnSortModeChanged)
+ SHeaderRow::Column(Private::ColumnDebugControls)
.DefaultLabel(LOCTEXT("FragmentColumnDebugControls", "Debug Controls"))
.FixedWidth(30.f)
.HAlignHeader(HAlign_Center)
.HAlignCell(HAlign_Center)
.VAlignCell(VAlign_Center)
.SortMode(EColumnSortMode::None)
+ SHeaderRow::Column(Private::ColumnNumArchetypes)
.DefaultLabel(LOCTEXT("FragmentColumnArchetypes", "Archetypes"))
.FillWidth(0.25f)
.SortMode(this, &SFragmentsView::GetColumnSortMode, Private::ColumnNumArchetypes.Resolve())
.OnSort(this, &SFragmentsView::OnColumnSortModeChanged)
+ SHeaderRow::Column(Private::ColumnNumEntities)
.DefaultLabel(LOCTEXT("FragmentColumnEntities", "Entities"))
.FillWidth(0.25f)
.SortMode(this, &SFragmentsView::GetColumnSortMode, Private::ColumnNumEntities.Resolve())
.OnSort(this, &SFragmentsView::OnColumnSortModeChanged);
SAssignNew(FragmentsList, SListView<FFragmentsTableRowPtr>)
.ListItemsSource(&FragmentListItemsSource)
.OnGenerateRow(this, &SFragmentsView::OnGenerateFragmentRow)
.HeaderRow(HeaderRowWidget);
ChildSlot
[
FragmentsList.ToSharedRef()
];
RefreshFragmentList();
#else // WITH_MASSENTITY_DEBUG
ChildSlot
[
SNew(STextBlock)
.Text(LOCTEXT("MassEntityDebuggingNotEnabled", "Mass Entity Debugging Not Enabled for this configuration"))
.Justification(ETextJustify::Center)
];
#endif // WITH_MASSENTITY_DEBUG
}
TSharedRef<ITableRow> SFragmentsView::OnGenerateFragmentRow(FFragmentsTableRowPtr InItem, const TSharedRef<STableViewBase>& OwnerTable)
{
return SNew(SFragmentsTableRow, OwnerTable, InItem);
}
EColumnSortMode::Type SFragmentsView::GetColumnSortMode(FName ColumnId) const
{
if (SortByColumnId != ColumnId)
{
return EColumnSortMode::None;
}
return bSortAscending ? EColumnSortMode::Ascending : EColumnSortMode::Descending;
}
void SFragmentsView::OnColumnSortModeChanged(const EColumnSortPriority::Type SortPriority, const FName& ColumnId, const EColumnSortMode::Type InSortMode)
{
if (ColumnId == Private::ColumnDebugControls)
{
return;
}
if (SortPriority == EColumnSortPriority::Primary)
{
SortByColumnId = ColumnId;
bSortAscending = InSortMode == EColumnSortMode::Ascending ? true : false;
}
RefreshFragmentList();
}
void SFragmentsView::OnRefresh()
{
#if WITH_MASSENTITY_DEBUG
RefreshFragmentList();
#endif // WITH_MASSENTITY_DEBUG
}
void SFragmentsView::RefreshFragmentList()
{
FragmentListItemsSource.Reset();
#if WITH_MASSENTITY_DEBUG
if (!DebuggerModel.IsValid())
{
return;
}
FragmentListItemsSource.Reserve(DebuggerModel->CachedFragmentData.Num());
for (TSharedPtr<FMassDebuggerFragmentData>& FragmentData : DebuggerModel->CachedFragmentData)
{
// filter out unused fragments
// @todo: add checkbox to "Show Unused Fragments"
if (FragmentData->Archetypes.Num() > 0)
{
FragmentListItemsSource.Add(FragmentData);
}
}
if (SortByColumnId == Private::ColumnName)
{
auto SortName = [bAscending = bSortAscending](const TSharedPtr<FMassDebuggerFragmentData>& A, const TSharedPtr<FMassDebuggerFragmentData>& B)
{
const int32 Compare = A->Name.CompareTo(B->Name) <= 0;
return bAscending ? Compare > 0 : Compare <= 0;
};
FragmentListItemsSource.StableSort(SortName);
}
else if (SortByColumnId == Private::ColumnNumArchetypes)
{
auto SortArchetypes = [bAscending = bSortAscending](const TSharedPtr<FMassDebuggerFragmentData>& A, const TSharedPtr<FMassDebuggerFragmentData>& B)
{
return bAscending ? A->Archetypes.Num() < B->Archetypes.Num() : A->Archetypes.Num() > B->Archetypes.Num();
};
FragmentListItemsSource.StableSort(SortArchetypes);
}
else if (SortByColumnId == Private::ColumnNumEntities)
{
auto SortEntities = [bAscending = bSortAscending](const TSharedPtr<FMassDebuggerFragmentData>& A, const TSharedPtr<FMassDebuggerFragmentData>& B)
{
return bAscending ? A->NumEntities < B->NumEntities : A->NumEntities > B->NumEntities;
};
FragmentListItemsSource.StableSort(SortEntities);
}
if (FragmentsList.IsValid())
{
FragmentsList->RequestListRefresh();
}
#endif // WITH_MASSENTITY_DEBUG
}
//----------------------------------------------------------------------//
// SFragmentsTableRow - Represents a single row in the list
//----------------------------------------------------------------------//
void SFragmentsTableRow::Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView, FFragmentsTableRowPtr InFragmentDataPtr)
{
#if WITH_MASSENTITY_DEBUG
FragmentDataPtr = InFragmentDataPtr;
SMultiColumnTableRow<FFragmentsTableRowPtr>::Construct(
SMultiColumnTableRow<FFragmentsTableRowPtr>::FArguments()
.ShowSelection(false)
, InOwnerTableView);
#endif //WITH_MASSENTITY_DEBUG
}
TSharedRef<SWidget> SFragmentsTableRow::GenerateWidgetForColumn(const FName& InColumnName)
{
TSharedRef<SWidget> CellWidget = SNullWidget::NullWidget;
#if WITH_MASSENTITY_DEBUG
if (!FragmentDataPtr.IsValid())
{
return CellWidget;
}
if (InColumnName == Private::ColumnName)
{
SAssignNew(CellWidget, STextBlock)
.Text(FragmentDataPtr->Name)
.ToolTipText(FragmentDataPtr->Fragment.IsValid() ? FText::FromString(FragmentDataPtr->Fragment.Pin()->GetPathName()) : LOCTEXT("InvalidFragmentPath", "Invalid Fragment Struct"));
}
else if (InColumnName == Private::ColumnNumArchetypes)
{
SAssignNew(CellWidget, STextBlock)
.Text(FText::AsNumber(FragmentDataPtr->Archetypes.Num()))
.Justification(ETextJustify::Right);
}
else if (InColumnName == Private::ColumnNumEntities)
{
SAssignNew(CellWidget, STextBlock)
.Text(FText::AsNumber(FragmentDataPtr->NumEntities))
.Justification(ETextJustify::Right);
}
else if (InColumnName == Private::ColumnDebugControls)
{
SAssignNew(CellWidget, SButton)
.ToolTipText(LOCTEXT("BreakpointTooltip", "Break on writes for selected entity."))
.ButtonStyle(FAppStyle::Get(), "NoBorder")
.ContentPadding(0)
.OnClicked_Lambda([WeakEntityManager = FragmentDataPtr->EntityManager, Fragment = FragmentDataPtr->Fragment]()
{
TSharedPtr<const FMassEntityManager> StrongEntityManager = WeakEntityManager.Pin();
TStrongObjectPtr<const UScriptStruct> StrongFragment = Fragment.Pin();
if (StrongEntityManager && StrongFragment)
{
FMassDebugger::ToggleFragmentWriteBreakForSelectedEntity(*StrongEntityManager, StrongFragment.Get());
}
return FReply::Handled();
})
[
SNew(SImage)
.ColorAndOpacity_Lambda([WeakEntityManager = FragmentDataPtr->EntityManager, Fragment = FragmentDataPtr->Fragment]()
{
TSharedPtr<const FMassEntityManager> StrongEntityManager = WeakEntityManager.Pin();
TStrongObjectPtr<const UScriptStruct> StrongFragment = Fragment.Pin();
if (StrongEntityManager && StrongFragment)
{
if(FMassDebugger::IsFragmentWriteBreakForSelectedEntitySet(*StrongEntityManager, StrongFragment.Get()))
{
return FSlateColor(FLinearColor(1.f, 0.f, 0.f, 1.f));
}
}
return FSlateColor(FLinearColor(1.f, 1.f, 1.f, 0.8f));
})
.Image(FAppStyle::GetBrush("Icons.FilledCircle"))
];
}
#endif //WITH_MASSENTITY_DEBUG
return SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.Padding(FMargin(4.f, 0.f))
.VAlign(VAlign_Center)
[
CellWidget
];
}
} // UE::MassDebugger::FragmentsView
#undef LOCTEXT_NAMESPACE