Files
UnrealEngine/Engine/Source/Developer/ProfileVisualizer/Private/SEventsTree.cpp
2025-05-18 13:04:45 +08:00

670 lines
21 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SEventsTree.h"
#include "SlateOptMacros.h"
#include "Textures/SlateIcon.h"
#include "Framework/Commands/UIAction.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "Widgets/Input/SSearchBox.h"
#define LOCTEXT_NAMESPACE "SEventsTree"
FName SEventsTree::NAME_NameColumn = FName(*NSLOCTEXT("ProfileVisualizer", "ColumnName", "Name").ToString());
FName SEventsTree::NAME_DurationColumn = FName( *NSLOCTEXT("ProfileVisualizer", "ColumnDuration", "Duration").ToString() );
void SEventItem::Construct( const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView )
{
EventName = InArgs._EventName;
EventDuration = InArgs._EventDuration;
SMultiColumnTableRow< TSharedPtr< FVisualizerEvent > >::Construct( FSuperRowType::FArguments(), InOwnerTableView );
}
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
TSharedRef< SWidget > SEventItem::GenerateWidgetForColumn( const FName& ColumnName )
{
if( ColumnName == SEventsTree::NAME_NameColumn )
{
return
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.AutoWidth()
[
SNew( SExpanderArrow, SharedThis(this) )
]
+SHorizontalBox::Slot()
.AutoWidth()
[
SNew( STextBlock )
.Text( FText::FromString(EventName) )
];
}
else if( ColumnName == SEventsTree::NAME_DurationColumn )
{
return SNew( STextBlock )
.Text( this, &SEventItem::GetDurationText );
}
else
{
return
SNew( STextBlock )
. Text(FText::Format( LOCTEXT("UnsupportedColumnFmt", "Unsupported Column: {0}"), FText::FromName(ColumnName) ) );
}
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SEventsTree::Construct( const FArguments& InArgs )
{
DurationUnits = EVisualizerTimeUnits::Milliseconds;
ViewMode = EVisualizerViewMode::Hierarchical;
bSuppressSelectionChangedEvent = false;
OnEventSelectionChangedDelegate = InArgs._OnEventSelectionChanged;
ProfileData = InArgs._ProfileData.Get();
// Duration column drop down menu
const bool bInShouldCloseWindowAfterMenuSelection = true;
FMenuBuilder MenuBuilder( bInShouldCloseWindowAfterMenuSelection, NULL );
{
FUIAction Action( FExecuteAction::CreateSP( this, &SEventsTree::SetDurationUnits, EVisualizerTimeUnits::Microseconds ),
FCanExecuteAction(),
FIsActionChecked::CreateSP( this, &SEventsTree::CheckDurationUnits, EVisualizerTimeUnits::Microseconds ) );
MenuBuilder.AddMenuEntry( LOCTEXT("Microseconds", "Microseconds"), FText(), FSlateIcon(), Action, NAME_None, EUserInterfaceActionType::Check );
}
{
FUIAction Action( FExecuteAction::CreateSP( this, &SEventsTree::SetDurationUnits, EVisualizerTimeUnits::Milliseconds ),
FCanExecuteAction(),
FIsActionChecked::CreateSP( this, &SEventsTree::CheckDurationUnits, EVisualizerTimeUnits::Milliseconds ) );
MenuBuilder.AddMenuEntry( LOCTEXT("Milliseconds", "Milliseconds"), FText(), FSlateIcon(), Action, NAME_None, EUserInterfaceActionType::Check );
}
{
FUIAction Action( FExecuteAction::CreateSP( this, &SEventsTree::SetDurationUnits, EVisualizerTimeUnits::Seconds ),
FCanExecuteAction(),
FIsActionChecked::CreateSP( this, &SEventsTree::CheckDurationUnits, EVisualizerTimeUnits::Seconds ) );
MenuBuilder.AddMenuEntry( LOCTEXT("Seconds", "Seconds"), FText(), FSlateIcon(), Action, NAME_None, EUserInterfaceActionType::Check );
}
// Name column drop down menu
FMenuBuilder NameMenuBuilder( bInShouldCloseWindowAfterMenuSelection, NULL );
{
FUIAction Action( FExecuteAction::CreateSP( this, &SEventsTree::SetViewMode, EVisualizerViewMode::Hierarchical ),
FCanExecuteAction(),
FIsActionChecked::CreateSP( this, &SEventsTree::CheckViewMode, EVisualizerViewMode::Hierarchical ) );
NameMenuBuilder.AddMenuEntry( LOCTEXT("Hierarchical", "Hierarchical"), FText(), FSlateIcon(), Action, NAME_None, EUserInterfaceActionType::Check );
}
{
FUIAction Action( FExecuteAction::CreateSP( this, &SEventsTree::SetViewMode, EVisualizerViewMode::Flat ),
FCanExecuteAction(),
FIsActionChecked::CreateSP( this, &SEventsTree::CheckViewMode, EVisualizerViewMode::Flat ) );
NameMenuBuilder.AddMenuEntry( LOCTEXT("Flat", "Flat"), FText(), FSlateIcon(), Action, NAME_None, EUserInterfaceActionType::Check );
}
{
FUIAction Action( FExecuteAction::CreateSP( this, &SEventsTree::SetViewMode, EVisualizerViewMode::Coalesced ),
FCanExecuteAction(),
FIsActionChecked::CreateSP( this, &SEventsTree::CheckViewMode, EVisualizerViewMode::Coalesced ) );
NameMenuBuilder.AddMenuEntry( LOCTEXT("Coalesced", "Coalesced"), FText(), FSlateIcon(), Action, NAME_None, EUserInterfaceActionType::Check );
}
{
FUIAction Action( FExecuteAction::CreateSP( this, &SEventsTree::SetViewMode, EVisualizerViewMode::FlatCoalesced ),
FCanExecuteAction(),
FIsActionChecked::CreateSP( this, &SEventsTree::CheckViewMode, EVisualizerViewMode::FlatCoalesced ) );
NameMenuBuilder.AddMenuEntry( LOCTEXT("FlatCoalesced", "Flat Coalesced"), FText(), FSlateIcon(), Action, NAME_None, EUserInterfaceActionType::Check );
}
this->ChildSlot
[
SNew( SVerticalBox )
+SVerticalBox::Slot().AutoHeight().Padding( 1.0f, 0.0f, 1.0f, 2.0f )
[
SNew( SSearchBox )
.ToolTipText( NSLOCTEXT("ProfileVisualizer", "FilterSearchHint", "Type here to search events.") )
.OnTextChanged( this, &SEventsTree::OnFilterTextChanged )
.OnTextCommitted( this, &SEventsTree::OnFilterTextCommitted )
]
+SVerticalBox::Slot().Padding( 2 ).FillHeight( 1 ).VAlign( VAlign_Fill )
[
SNew( SHorizontalBox )
+SHorizontalBox::Slot().Padding( 2 ).FillWidth( 1 ).HAlign( HAlign_Fill )
[
// List of all events for the selected thread
SAssignNew( EventsListView, STreeView< TSharedPtr< FVisualizerEvent > > )
// Tell the list view where to get its source data
.TreeItemsSource( &SelectedEventsView )
// When the list view needs to generate a widget for some data item, use this method
.OnGenerateRow( this, &SEventsTree::OnGenerateWidgetForEventsList )
// Given some DataItem, this is how we find out if it has any children and what they are.
.OnGetChildren( this, &SEventsTree::OnGetChildrenForEventsList )
// Selection mode
.SelectionMode( ESelectionMode::Single )
// Selection callback
.OnSelectionChanged( this, &SEventsTree::OnEventSelectionChanged )
.HeaderRow
(
SNew( SHeaderRow )
+ SHeaderRow::Column( NAME_NameColumn )
.DefaultLabel( NSLOCTEXT("ProfileVisualizer", "ColumnName", "Name") )
.SortMode( TAttribute< EColumnSortMode::Type >::Create( TAttribute< EColumnSortMode::Type >::FGetter::CreateSP( this, &SEventsTree::GetColumnSortMode, NAME_NameColumn ) ) )
.OnSort( FOnSortModeChanged::CreateSP( this, &SEventsTree::OnColumnSortModeChanged ) )
.FillWidth( 1.0f )
.MenuContent()
[
NameMenuBuilder.MakeWidget()
]
+ SHeaderRow::Column( NAME_DurationColumn )
.DefaultLabel( this, &SEventsTree::GetDurationColumnTitle )
.SortMode( TAttribute< EColumnSortMode::Type >::Create( TAttribute< EColumnSortMode::Type >::FGetter::CreateSP( this, &SEventsTree::GetColumnSortMode, NAME_DurationColumn ) ) )
.OnSort( FOnSortModeChanged::CreateSP( this, &SEventsTree::OnColumnSortModeChanged ) )
.FixedWidth( 128.0f )
.MenuContent()
[
MenuBuilder.MakeWidget()
]
)
]
]
];
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
EColumnSortMode::Type SEventsTree::GetColumnSortMode( const FName ColumnId )
{
if ( SortByColumn != ColumnId )
{
return EColumnSortMode::None;
}
return SortMode;
}
FText SEventsTree::GetDurationColumnTitle() const
{
static const FText Units[] = { NSLOCTEXT("ProfileVisualizer", "microseconds", "microseconds"), NSLOCTEXT("ProfileVisualizer", "milliseconds", "ms"), NSLOCTEXT("ProfileVisualizer", "seconds", "s") };
return FText::Format( NSLOCTEXT("ProfileVisualizer", "ColumnDurationValue", "Duration ({0})"), Units[ DurationUnits ] );
}
void ClearEventsSelection( TArray< TSharedPtr< FVisualizerEvent > >& Events )
{
for( int32 Index = 0; Index < Events.Num(); ++Index )
{
TSharedPtr< FVisualizerEvent > CurrentEvent = Events[ Index ];
CurrentEvent->IsSelected = false;
// Clear recursively
if( CurrentEvent->Children.Num() )
{
ClearEventsSelection( CurrentEvent->Children );
}
}
}
void SEventsTree::OnEventSelectionChanged( TSharedPtr< FVisualizerEvent > Selection, ESelectInfo::Type /*SelectInfo*/ )
{
if( Selection.IsValid() )
{
ClearEventsSelection( SelectedEventsView );
Selection->IsSelected = true;
// Mirror the selection in the source events list
TSharedPtr< FVisualizerEvent > MappedSelection = ViewToEventsMap.FindChecked( Selection );
MappedSelection->IsSelected = true;
if( !bSuppressSelectionChangedEvent )
{
OnEventSelectionChangedDelegate.ExecuteIfBound( MappedSelection );
}
}
else if( !bSuppressSelectionChangedEvent )
{
OnEventSelectionChangedDelegate.ExecuteIfBound( Selection );
}
}
TSharedRef< ITableRow > SEventsTree::OnGenerateWidgetForEventsList( TSharedPtr< FVisualizerEvent > InItem, const TSharedRef<STableViewBase>& OwnerTable )
{
return SNew( SEventItem, OwnerTable )
.EventName( InItem->EventName )
.EventDuration( this, &SEventsTree::GetEventDuration, InItem->DurationMs );
}
double SEventsTree::GetEventDuration( double InDurationMs ) const
{
switch( DurationUnits )
{
case EVisualizerTimeUnits::Microseconds:
return InDurationMs * 1000.0;
case EVisualizerTimeUnits::Milliseconds:
return InDurationMs;
case EVisualizerTimeUnits::Seconds:
return InDurationMs * 0.001;
}
return InDurationMs;
}
void SEventsTree::SetDurationUnits( EVisualizerTimeUnits::Type InUnits )
{
DurationUnits = InUnits;
EventsListView->RequestTreeRefresh();
}
void SEventsTree::SetViewMode( EVisualizerViewMode::Type InMode )
{
ViewMode = InMode;
CreateSelectedEventsView();
SortEventsList();
EventsListView->RequestTreeRefresh();
}
void SEventsTree::OnGetChildrenForEventsList( TSharedPtr<FVisualizerEvent> InItem, TArray<TSharedPtr<FVisualizerEvent> >& OutChildren )
{
OutChildren = InItem->Children;
}
void SEventsTree::OnColumnSortModeChanged( const EColumnSortPriority::Type SortPriority, const FName& ColumnId, const EColumnSortMode::Type InSortMode )
{
SortByColumn = ColumnId;
SortMode = InSortMode;
SortEventsList();
}
void SEventsTree::SortEventsList( TArray< TSharedPtr< FVisualizerEvent > >& Events )
{
// Sort taking the current settings into account
if( SortByColumn == NAME_NameColumn )
{
if( SortMode == EColumnSortMode::Ascending )
{
struct FCompareEventsByName
{
FORCEINLINE bool operator()( const TSharedPtr<FVisualizerEvent> A, const TSharedPtr<FVisualizerEvent> B ) const { return A->EventName < B->EventName; }
};
Events.Sort( FCompareEventsByName() );
}
else if( SortMode == EColumnSortMode::Descending )
{
struct FCompareEventsByNameDescending
{
FORCEINLINE bool operator()( const TSharedPtr<FVisualizerEvent> A, const TSharedPtr<FVisualizerEvent> B ) const { return B->EventName < A->EventName; }
};
Events.Sort( FCompareEventsByNameDescending() );
}
}
else if( SortByColumn == NAME_DurationColumn )
{
if( SortMode == EColumnSortMode::Ascending )
{
struct FCompareEventsByDuration
{
FORCEINLINE bool operator()( const TSharedPtr<FVisualizerEvent> A, const TSharedPtr<FVisualizerEvent> B ) const { return A->DurationMs < B->DurationMs; }
};
Events.Sort( FCompareEventsByDuration() );
}
else if( SortMode == EColumnSortMode::Descending )
{
struct FCompareEventsByDurationDescending
{
FORCEINLINE bool operator()( const TSharedPtr<FVisualizerEvent> A, const TSharedPtr<FVisualizerEvent> B ) const { return B->DurationMs < A->DurationMs; }
};
Events.Sort( FCompareEventsByDurationDescending() );
}
}
// Sort recursively
for( int32 Index = 0; Index < Events.Num(); Index++ )
{
if( Events[ Index ]->Children.Num() )
{
SortEventsList( Events[ Index ]->Children );
}
}
}
void SEventsTree::SortEventsList( void )
{
SortEventsList( SelectedEventsView );
EventsListView->RequestTreeRefresh();
RestoreEventSelection( SelectedEventsView );
}
bool SEventsTree::RestoreEventSelection( TArray< TSharedPtr< FVisualizerEvent > >& Events )
{
// Search for the selected event
for( int32 Index = 0; Index < Events.Num(); ++Index )
{
if( Events[ Index ]->IsSelected )
{
// Select it in the tree view widget
EventsListView->ClearSelection();
EventsListView->RequestTreeRefresh();
EventsListView->SetSelection( Events[ Index ] );
EventsListView->RequestScrollIntoView( Events[ Index ] );
return true;
}
// Search recursively
if( RestoreEventSelection( Events[ Index ]->Children ) )
{
return true;
}
}
return false;
}
void SEventsTree::HandleBarGraphSelectionChanged( TSharedPtr< FVisualizerEvent > Selection )
{
if ( Selection.IsValid() )
{
for( int32 EventIndex = 0; EventIndex < SelectedEvents.Num(); EventIndex++ )
{
TSharedPtr<FVisualizerEvent> Event = SelectedEvents[ EventIndex ];
if( Selection->EventName == Event->EventName )
{
HandleBarEventSelectionChanged( SelectedThread->Category, Event );
break;
}
}
}
}
void SEventsTree::HandleBarGraphExpansionChanged( TSharedPtr< FVisualizerEvent > Selection )
{
if ( Selection.IsValid() )
{
// We don't want to trigger selection changed event when the selection change is actually coming from the bar graph
bSuppressSelectionChangedEvent = true;
SelectedThread = Selection;
SelectedEvents = Selection.Get()->Children;
CreateSelectedEventsView();
SortEventsList();
bSuppressSelectionChangedEvent = false;
}
}
void SEventsTree::HandleBarEventSelectionChanged( int32 Thread, TSharedPtr<FVisualizerEvent> Selection )
{
const TSharedPtr< FVisualizerEvent >* MappedViewSelection = ViewToEventsMap.FindKey( Selection );
if( MappedViewSelection )
{
TSharedPtr< FVisualizerEvent > ViewSelection = *MappedViewSelection;
// Clear the current selection
EventsListView->ClearSelection();
EventsListView->RequestTreeRefresh();
// Expand all parents so that it's visible
for( TSharedPtr< FVisualizerEvent > ParentEvent = ViewSelection->ParentEvent; ParentEvent.IsValid(); ParentEvent = ParentEvent->ParentEvent )
{
EventsListView->SetItemExpansion( ParentEvent, true );
}
EventsListView->SetSelection( ViewSelection );
EventsListView->RequestScrollIntoView( ViewSelection );
}
else
{
EventsListView->ClearSelection();
}
}
FString SEventsTree::GetTabTitle() const
{
if ( SelectedThread.IsValid() )
{
return SelectedThread->EventName;
}
else
{
return NSLOCTEXT("ProfileVisualizer", "EventsVisualizerName", "Empty Events List").ToString();
}
}
int32 SEventsTree::CountEvents( TArray< TSharedPtr< FVisualizerEvent > >& Events )
{
int32 Count = Events.Num();
for( int32 Index = 0; Index < Events.Num(); Index++ )
{
Count += CountEvents( Events[ Index ]->Children );
}
return Count;
}
void SEventsTree::CreateSelectedEventsView()
{
const int32 EventsCount = CountEvents( SelectedEvents );
ViewToEventsMap.Empty( EventsCount );
SelectedEventsView.Empty( SelectedEvents.Num() );
// Create the selected events copy based on the current view mode
if( ViewMode == EVisualizerViewMode::Hierarchical )
{
SelectedEventsView.Empty( SelectedEvents.Num() );
for( int32 Index = 0; Index < SelectedEvents.Num(); Index++ )
{
if( FilterEvent( SelectedEvents[ Index ] ) )
{
TSharedPtr< FVisualizerEvent > EventCopy( CreateSelectedEventsViewRecursively( SelectedEvents[ Index ] ) );
if( EventCopy.IsValid() )
{
SelectedEventsView.Add( EventCopy );
}
}
}
}
else if( ViewMode == EVisualizerViewMode::Flat )
{
SelectedEventsView.Empty( EventsCount );
for( int32 Index = 0; Index < SelectedEvents.Num(); Index++ )
{
CreateSelectedEventsViewRecursivelyAndFlatten( SelectedEvents[ Index ] );
}
}
else if( ViewMode == EVisualizerViewMode::Coalesced )
{
SelectedEventsView.Empty( SelectedEvents.Num() );
CreateSelectedEventsViewRecursivelyCoalesced( SelectedEvents, SelectedEventsView, TSharedPtr< FVisualizerEvent >() );
}
else if( ViewMode == EVisualizerViewMode::FlatCoalesced )
{
SelectedEventsView.Empty( EventsCount );
CreateSelectedEventsViewRecursivelyFlatCoalesced( SelectedEvents );
}
}
TSharedPtr< FVisualizerEvent > SEventsTree::CreateSelectedEventsViewRecursively( TSharedPtr< FVisualizerEvent > SourceEvent )
{
TSharedPtr< FVisualizerEvent > EventCopy( new FVisualizerEvent( *SourceEvent ) );
EventCopy->Children.Empty( SourceEvent->Children.Num() );
for( int32 ChildIndex = 0; ChildIndex < SourceEvent->Children.Num(); ChildIndex++ )
{
if( FilterEvent( SourceEvent->Children[ ChildIndex ] ) )
{
TSharedPtr< FVisualizerEvent > ChildCopy = CreateSelectedEventsViewRecursively( SourceEvent->Children[ ChildIndex ] );
if( ChildCopy.IsValid() )
{
ChildCopy->ParentEvent = EventCopy;
EventCopy->Children.Add( ChildCopy );
}
}
}
// Add this event because it's a leaf or has valid children
if( EventCopy->Children.Num() > 0 || SourceEvent->Children.Num() == 0 )
{
ViewToEventsMap.Add( EventCopy, SourceEvent );
}
else
{
EventCopy.Reset();
}
return EventCopy;
}
void SEventsTree::CreateSelectedEventsViewRecursivelyAndFlatten( TSharedPtr< FVisualizerEvent > SourceEvent )
{
// Collect only children and store them directly into SelectedEventsView
if ( FilterEvent( SourceEvent ) && SourceEvent->Children.Num() == 0 )
{
TSharedPtr< FVisualizerEvent > EventCopy( new FVisualizerEvent( *SourceEvent ) );
EventCopy->ParentEvent.Reset();
ViewToEventsMap.Add( EventCopy, SourceEvent );
SelectedEventsView.Add( EventCopy );
}
for( int32 ChildIndex = 0; ChildIndex < SourceEvent->Children.Num(); ChildIndex++ )
{
CreateSelectedEventsViewRecursivelyAndFlatten( SourceEvent->Children[ ChildIndex ] );
}
}
void SEventsTree::CreateSelectedEventsViewRecursivelyCoalesced( TArray< TSharedPtr< FVisualizerEvent > >& SourceEvents, TArray< TSharedPtr< FVisualizerEvent > >& CopiedEvents, TSharedPtr< FVisualizerEvent > InParent )
{
for( int32 SourceIndex = 0; SourceIndex < SourceEvents.Num(); SourceIndex++ )
{
TSharedPtr< FVisualizerEvent > SourceEvent = SourceEvents[ SourceIndex ];
if( FilterEvent( SourceEvent ) )
{
if( SourceEvent->Children.Num() == 0 )
{
// Check if a child with the same name has already been added to the copied event
bool bEventExists = false;
for( int32 CopiedEventIndex = 0; CopiedEventIndex < CopiedEvents.Num(); CopiedEventIndex++ )
{
if( CopiedEvents[ CopiedEventIndex ]->EventName == SourceEvent->EventName )
{
bEventExists = true;
break;
}
}
if( !bEventExists )
{
TSharedPtr< FVisualizerEvent > EventCopy( new FVisualizerEvent( *SourceEvent ) );
EventCopy->ParentEvent = InParent;
ViewToEventsMap.Add( EventCopy, SourceEvent );
CopiedEvents.Add( EventCopy );
// Find other children with the same name and add their time to the copied one
for( int32 OtherIndex = SourceIndex + 1; OtherIndex < SourceEvents.Num(); OtherIndex++ )
{
TSharedPtr< FVisualizerEvent > OtherEvent = SourceEvents[ OtherIndex ];
if( OtherEvent->Children.Num() == 0 && OtherEvent->EventName == SourceEvent->EventName )
{
EventCopy->DurationMs += OtherEvent->DurationMs;
}
}
}
}
else
{
TSharedPtr< FVisualizerEvent > EventCopy( new FVisualizerEvent( *SourceEvent ) );
EventCopy->Children.Empty( SourceEvent->Children.Num() );
EventCopy->ParentEvent = InParent;
CreateSelectedEventsViewRecursivelyCoalesced( SourceEvent->Children, EventCopy->Children, EventCopy );
// Only add this event if its children haven't been filtered
if( EventCopy->Children.Num() )
{
ViewToEventsMap.Add( EventCopy, SourceEvent );
CopiedEvents.Add( EventCopy );
}
}
}
}
}
void SEventsTree::CreateSelectedEventsViewRecursivelyFlatCoalesced( TArray< TSharedPtr< FVisualizerEvent > >& SourceEvents )
{
for( int32 SourceIndex = 0; SourceIndex < SourceEvents.Num(); SourceIndex++ )
{
TSharedPtr< FVisualizerEvent > SourceEvent = SourceEvents[ SourceIndex ];
if( FilterEvent( SourceEvent ) )
{
if( SourceEvent->Children.Num() == 0 )
{
// Check if a child with the same name has already been added to the copied event
bool bEventExists = false;
for( int32 CopiedEventIndex = 0; CopiedEventIndex < SelectedEventsView.Num(); CopiedEventIndex++ )
{
if( SelectedEventsView[ CopiedEventIndex ]->EventName == SourceEvent->EventName )
{
bEventExists = true;
break;
}
}
if( !bEventExists )
{
TSharedPtr< FVisualizerEvent > EventCopy( new FVisualizerEvent( *SourceEvent ) );
EventCopy->ParentEvent.Reset();
ViewToEventsMap.Add( EventCopy, SourceEvent );
SelectedEventsView.Add( EventCopy );
// Find other children with the same name and add their time to the copied one
for( int32 OtherIndex = SourceIndex + 1; OtherIndex < SourceEvents.Num(); OtherIndex++ )
{
TSharedPtr< FVisualizerEvent > OtherEvent = SourceEvents[ OtherIndex ];
if( OtherEvent->Children.Num() == 0 && OtherEvent->EventName == SourceEvent->EventName )
{
EventCopy->DurationMs += OtherEvent->DurationMs;
}
}
}
}
else
{
CreateSelectedEventsViewRecursivelyFlatCoalesced( SourceEvent->Children );
}
}
}
}
void SEventsTree::OnFilterTextChanged( const FText& InFilterText )
{
FilterText = InFilterText.ToString();
CreateSelectedEventsView();
EventsListView->RequestTreeRefresh();
}
void SEventsTree::OnFilterTextCommitted( const FText& InFilterText, ETextCommit::Type /*CommitInfo*/ )
{
}
#undef LOCTEXT_NAMESPACE