Files
UnrealEngine/Engine/Source/Developer/TraceInsightsFrontend/Private/InsightsFrontend/Widgets/STraceStoreWindow.cpp
2025-05-18 13:04:45 +08:00

3241 lines
105 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "STraceStoreWindow.h"
#include "DesktopPlatformModule.h"
#include "Framework/Application/SlateApplication.h"
#include "Framework/MetaData/DriverMetaData.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "HAL/FileManager.h"
#include "HAL/PlatformApplicationMisc.h"
#include "HAL/PlatformProcess.h"
#include "Internationalization/Text.h"
#include "Logging/MessageLog.h"
#include "Misc/MessageDialog.h"
#include "Misc/PathViews.h"
#include "SlateOptMacros.h"
#include "Styling/AppStyle.h"
#include "Styling/StyleColors.h"
#include "Widgets/Images/SImage.h"
#include "Widgets/Input/SButton.h"
#include "Widgets/Input/SCheckBox.h"
#include "Widgets/Input/SComboButton.h"
#include "Widgets/Input/SEditableTextBox.h"
#include "Widgets/Input/SSearchBox.h"
#include "Widgets/Layout/SBorder.h"
#include "Widgets/Layout/SBox.h"
#include "Widgets/Layout/SScrollBox.h"
#include "Widgets/Layout/SSeparator.h"
#include "Widgets/Notifications/SNotificationList.h"
#include "Widgets/SToolTip.h"
#include "Widgets/Testing/SStarshipSuite.h" // for RestoreStarshipSuite()
#include "Widgets/Text/STextBlock.h"
#if WITH_EDITOR
#include "AnalyticsEventAttribute.h"
#include "EngineAnalytics.h"
#include "Interfaces/IAnalyticsProvider.h"
#endif // WITH_EDITOR
// TraceAnalysis
#include "Trace/StoreClient.h"
#include "Trace/StoreConnection.h"
// TraceInsightsCore
#include "InsightsCore/Common/InsightsCoreStyle.h"
#include "InsightsCore/Common/MiscUtils.h"
#include "InsightsCore/Common/Stopwatch.h"
#include "InsightsCore/Table/ViewModels/TableImporter.h"
#include "InsightsCore/Version.h"
// TraceInsightsFrontend
#include "InsightsFrontend/Common/InsightsFrontendStyle.h"
#include "InsightsFrontend/Common/Log.h"
#include "InsightsFrontend/InsightsFrontendSettings.h"
#include "InsightsFrontend/ITraceInsightsFrontendModule.h"
#include "InsightsFrontend/StoreService/StoreBrowser.h"
#include "InsightsFrontend/TraceInsightsFrontendModule.h"
#include "InsightsFrontend/ViewModels/TraceSetFilter.h"
#include "InsightsFrontend/ViewModels/TraceViewModel.h"
#include "InsightsFrontend/Widgets/STraceDirectoryItem.h"
#include "InsightsFrontend/Widgets/STraceListRow.h"
#if PLATFORM_WINDOWS
#include "Windows/AllowWindowsPlatformTypes.h"
#include <handleapi.h> // for CreateEvent
#include <synchapi.h> // for CloseHandle
#include "Windows/HideWindowsPlatformTypes.h"
#endif
#define LOCTEXT_NAMESPACE "UE::Insights::STraceStoreWindow"
namespace UE::Insights
{
FName STraceStoreWindow::LogListingName(TEXT("InsightsFrontend"));
////////////////////////////////////////////////////////////////////////////////////////////////////
// STraceStoreWindow
////////////////////////////////////////////////////////////////////////////////////////////////////
STraceStoreWindow::STraceStoreWindow()
: TableImporter(MakeShared<FTableImporter>(LogListingName))
{
SortColumn = FTraceListColumns::Date;
SortMode = EColumnSortMode::Ascending;
// Add controls for the local server
ServerControls.Emplace(TEXT("127.0.0.1"), 0, FAppStyle::Get().GetStyleSetName());
}
////////////////////////////////////////////////////////////////////////////////////////////////////
STraceStoreWindow::~STraceStoreWindow()
{
if (OnTickHandle.IsValid())
{
FTSTicker::GetCoreTicker().RemoveTicker(OnTickHandle);
}
#if WITH_EDITOR
if (DurationActive > 0.0f && FEngineAnalytics::IsAvailable())
{
FEngineAnalytics::GetProvider().RecordEvent(TEXT("Insights.Usage.SessionBrowser"), FAnalyticsEventAttribute(TEXT("Duration"), DurationActive));
}
#endif // WITH_EDITOR
DisableAutoConnect();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void STraceStoreWindow::Construct(const FArguments& InArgs, TSharedRef<UE::Trace::FStoreConnection> InTraceStoreConnection)
{
TraceStoreConnection = InTraceStoreConnection;
StoreBrowser.Reset(new FStoreBrowser(InTraceStoreConnection));
ChildSlot
[
SNew(SOverlay)
// Version
+ SOverlay::Slot()
.HAlign(HAlign_Right)
.VAlign(VAlign_Top)
.Padding(0.0f, -16.0f, 4.0f, 0.0f)
[
SNew(STextBlock)
.Clipping(EWidgetClipping::ClipToBoundsWithoutIntersecting)
.Text(FText::FromString(TEXT(UNREAL_INSIGHTS_VERSION_STRING_EX)))
.ColorAndOpacity(FLinearColor(0.15f, 0.15f, 0.15f, 1.0f))
]
+ SOverlay::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
.Padding(0.0f, 0.0f, 0.0f, 0.0f)
[
SNew(SBox)
[
SNew(SBorder)
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
.Padding(0.0f)
.BorderImage(FAppStyle::Get().GetBrush("WhiteBrush"))
.BorderBackgroundColor(FSlateColor(EStyleColor::Panel))
]
]
// Overlay slot for the main window area
+ SOverlay::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
[
SAssignNew(MainContentPanel, SVerticalBox)
+ SVerticalBox::Slot()
.HAlign(HAlign_Fill)
.AutoHeight()
.Padding(6.0f, 8.0f, 12.0f, 0.0f)
[
ConstructTraceStoreDirectoryPanel()
]
+ SVerticalBox::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
.AutoHeight()
.Padding(0.0f, 0.0f, 0.0f, 0.0f)
[
ConstructFiltersToolbar()
]
+ SVerticalBox::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
.FillHeight(1.0f)
.Padding(3.0f, 0.0f, 3.0f, 4.0f)
[
ConstructSessionsPanel()
]
+ SVerticalBox::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
.AutoHeight()
.Padding(12.0f, 4.0f, 12.0f, 8.0f)
[
ConstructLoadPanel()
]
]
// Overlay for fake splash-screen.
+ SOverlay::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
.Padding(0.0f)
[
SNew(SBox)
.Visibility(this, &STraceStoreWindow::SplashScreenOverlay_Visibility)
[
SNew(SBorder)
.BorderImage(FAppStyle::Get().GetBrush("PopupText.Background"))
.BorderBackgroundColor(this, &STraceStoreWindow::SplashScreenOverlay_ColorAndOpacity)
.Padding(0.0f)
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
[
SNew(SBox)
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(this, &STraceStoreWindow::GetSplashScreenOverlayText)
.Font(FAppStyle::Get().GetFontStyle("NormalFontBold"))
.ColorAndOpacity(this, &STraceStoreWindow::SplashScreenOverlay_TextColorAndOpacity)
]
]
]
]
// Notification area overlay
+ SOverlay::Slot()
.HAlign(HAlign_Right)
.VAlign(VAlign_Bottom)
.Padding(16.0f)
[
SAssignNew(NotificationList, SNotificationList)
]
// Settings dialog overlay
+ SOverlay::Slot()
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
.Expose(OverlaySettingsSlot)
];
// Register tick functions.
OnTick = FTickerDelegate::CreateSP(this, &STraceStoreWindow::CoreTick);
OnTickHandle = FTSTicker::GetCoreTicker().AddTicker(OnTick, 0.0f);
CreateFilters();
if (StoreHostTextBox)
{
StoreHostTextBox->SetText(FText::FromString(TraceStoreConnection->GetLastStoreHost()));
}
RefreshTraceList();
if (AutoConnect_IsChecked() == ECheckBoxState::Checked)
{
EnableAutoConnect();
}
bSetKeyboardFocusOnNextTick = true;
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
////////////////////////////////////////////////////////////////////////////////////////////////////
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
TSharedRef<SWidget> STraceStoreWindow::ConstructFiltersToolbar()
{
FSlimHorizontalToolBarBuilder ToolbarBuilder(TSharedPtr<const FUICommandList>(), FMultiBoxCustomization::None);
ToolbarBuilder.SetStyle(&FInsightsCoreStyle::Get(), "SecondaryToolbar");
ToolbarBuilder.BeginSection("Filters");
{
// Toggle between filtering the list of trace sessions by name or by command line
ToolbarBuilder.AddWidget(
SNew(SCheckBox)
.Style(FAppStyle::Get(), "ToggleButtonCheckbox")
.HAlign(HAlign_Center)
.Padding(3.0f)
.OnCheckStateChanged_Lambda([this](ECheckBoxState NewState) { bSearchByCommandLine = (NewState == ECheckBoxState::Checked); OnFilterChanged(); })
.IsChecked_Lambda([this]() { return bSearchByCommandLine ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; })
.ToolTipText(LOCTEXT("ToggleNameFilter_Tooltip", "Toggle between filtering the list of trace sessions by name or by command line."))
[
SNew(SBox)
.Padding(1.0f)
[
SNew(SImage)
.Image(FInsightsFrontendStyle::Get().GetBrush("Icons.Console"))
]
]);
// Text Filter (Search Box)
ToolbarBuilder.AddWidget(
SNew(SBox)
.MaxDesiredWidth(400.0f)
[
SAssignNew(FilterByNameSearchBox, SSearchBox)
.MinDesiredWidth(150.0f)
.HintText_Lambda([this]()
{
return bSearchByCommandLine ?
LOCTEXT("CmdLineFilter_Hint", "Command Line") :
LOCTEXT("NameFilter_Hint", "Name");
})
.ToolTipText_Lambda([this]()
{
return bSearchByCommandLine ?
LOCTEXT("CmdLineFilter_Tooltip", "Type here to filter the list of trace sessions by command line.") :
LOCTEXT("NameFilter_Tooltip", "Type here to filter the list of trace sessions by name.");
})
.IsEnabled_Lambda([this]() { return TraceViewModels.Num() > 0; })
.OnTextChanged(this, &STraceStoreWindow::FilterByNameSearchBox_OnTextChanged)
.DelayChangeNotificationsWhileTyping(true)
]);
// Filter by Platform
ToolbarBuilder.AddComboButton(
FUIAction(),
FOnGetContent::CreateSP(this, &STraceStoreWindow::MakePlatformFilterMenu),
LOCTEXT("FilterByPlatformText", "Platform"),
LOCTEXT("FilterByPlatformToolTip", "Filters the list of trace sessions by platform."),
FSlateIcon(FInsightsFrontendStyle::GetStyleSetName(), "Icons.Filter.ToolBar"),
false);
// Filter by AppName
ToolbarBuilder.AddComboButton(
FUIAction(),
FOnGetContent::CreateSP(this, &STraceStoreWindow::MakeAppNameFilterMenu),
LOCTEXT("FilterByAppNameText", "App Name"),
LOCTEXT("FilterByAppNameToolTip", "Filters the list of trace sessions by application name."),
FSlateIcon(FInsightsFrontendStyle::GetStyleSetName(), "Icons.Filter.ToolBar"),
false);
// Filter by Build Config
ToolbarBuilder.AddComboButton(
FUIAction(),
FOnGetContent::CreateSP(this, &STraceStoreWindow::MakeBuildConfigFilterMenu),
LOCTEXT("FilterByBuildConfigText", "Config"),
LOCTEXT("FilterByBuildConfigToolTip", "Filters the list of trace sessions by build configuration."),
FSlateIcon(FInsightsFrontendStyle::GetStyleSetName(), "Icons.Filter.ToolBar"),
false);
// Filter by Build Target
ToolbarBuilder.AddComboButton(
FUIAction(),
FOnGetContent::CreateSP(this, &STraceStoreWindow::MakeBuildTargetFilterMenu),
LOCTEXT("FilterByBuildTargetText", "Target"),
LOCTEXT("FilterByBuildTargetToolTip", "Filters the list of trace sessions by build target."),
FSlateIcon(FInsightsFrontendStyle::GetStyleSetName(), "Icons.Filter.ToolBar"),
false);
// Filter by Branch
ToolbarBuilder.AddComboButton(
FUIAction(),
FOnGetContent::CreateSP(this, &STraceStoreWindow::MakeBranchFilterMenu),
LOCTEXT("FilterByBranchText", "Branch"),
LOCTEXT("FilterByBranchToolTip", "Filters the list of trace sessions by branch."),
FSlateIcon(FInsightsFrontendStyle::GetStyleSetName(), "Icons.Filter.ToolBar"),
false);
// Filter by Version
ToolbarBuilder.AddComboButton(
FUIAction(),
FOnGetContent::CreateSP(this, &STraceStoreWindow::MakeVersionFilterMenu),
LOCTEXT("FilterByVersionText", "Version"),
LOCTEXT("FilterByVersionToolTip", "Filters the list of trace sessions by Version."),
FSlateIcon(FInsightsFrontendStyle::GetStyleSetName(), "Icons.Filter.ToolBar"),
false);
// Filter by Size
ToolbarBuilder.AddComboButton(
FUIAction(),
FOnGetContent::CreateSP(this, &STraceStoreWindow::MakeSizeFilterMenu),
LOCTEXT("FilterBySizeText", "Size"),
LOCTEXT("FilterBySizeToolTip", "Filters the list of trace sessions by size."),
FSlateIcon(FInsightsFrontendStyle::GetStyleSetName(), "Icons.Filter.ToolBar"),
false);
// Filter by Status
ToolbarBuilder.AddComboButton(
FUIAction(),
FOnGetContent::CreateSP(this, &STraceStoreWindow::MakeStatusFilterMenu),
LOCTEXT("FilterByStatusText", "Status"),
LOCTEXT("FilterByStatusToolTip", "Filters the list of trace sessions by status.."),
FSlateIcon(FInsightsFrontendStyle::GetStyleSetName(), "Icons.Filter.ToolBar"),
false);
}
ToolbarBuilder.EndSection();
FSlimHorizontalToolBarBuilder RightSideToolbarBuilder(TSharedPtr<const FUICommandList>(), FMultiBoxCustomization::None);
RightSideToolbarBuilder.SetStyle(&FInsightsCoreStyle::Get(), "PrimaryToolbar");
RightSideToolbarBuilder.BeginSection("FilterStats");
{
// Filter Stats Text (number and size of filtered trace sessions)
RightSideToolbarBuilder.AddWidget(
SNew(SBox)
.VAlign(VAlign_Bottom)
.Padding(FMargin(4.0f, 0.0f, 4.0f, 0.0f))
[
SNew(STextBlock)
.Text(this, &STraceStoreWindow::GetFilterStatsText)
]);
}
RightSideToolbarBuilder.EndSection();
return SNew(SHorizontalBox)
.Visibility(this, &STraceStoreWindow::VisibleIfConnected)
+ SHorizontalBox::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Center)
.FillWidth(1.0f)
.Padding(0.0f)
[
ToolbarBuilder.MakeWidget()
]
+ SHorizontalBox::Slot()
.HAlign(HAlign_Right)
.VAlign(VAlign_Center)
.AutoWidth()
.Padding(0.0f)
[
RightSideToolbarBuilder.MakeWidget()
];
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
////////////////////////////////////////////////////////////////////////////////////////////////////
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
TSharedRef<SWidget> STraceStoreWindow::ConstructSessionsPanel()
{
TSharedRef<SWidget> Widget = SAssignNew(TraceListView, SListView<TSharedPtr<FTraceViewModel>>)
.Visibility(this, &STraceStoreWindow::HiddenIfNotConnected)
.IsFocusable(true)
.SelectionMode(ESelectionMode::Multi)
.OnSelectionChanged(this, &STraceStoreWindow::TraceList_OnSelectionChanged)
.OnMouseButtonDoubleClick(this, &STraceStoreWindow::TraceList_OnMouseButtonDoubleClick)
.ListItemsSource(&FilteredTraceViewModels)
.OnGenerateRow(this, &STraceStoreWindow::TraceList_OnGenerateRow)
.ConsumeMouseWheel(EConsumeMouseWheel::Always)
.OnContextMenuOpening(FOnContextMenuOpening::CreateSP(this, &STraceStoreWindow::TraceList_GetMenuContent))
.HeaderRow
(
SNew(SHeaderRow)
+ SHeaderRow::Column(FTraceListColumns::Name)
.FillWidth(0.25f)
.InitialSortMode(EColumnSortMode::Ascending)
.SortMode(this, &STraceStoreWindow::GetSortModeForColumn, FTraceListColumns::Name)
.OnSort(this, &STraceStoreWindow::OnSortModeChanged)
[
SNew(SBox)
.VAlign(VAlign_Center)
.MinDesiredHeight(24.0f)
[
SNew(STextBlock)
.Text(LOCTEXT("NameColumn", "Name"))
.ColorAndOpacity_Lambda([this] { return FilterByName->GetRawFilterText().IsEmpty() ? FLinearColor(0.5f, 0.5f, 0.5f, 1.0f) : FLinearColor(0.3f, 0.75f, 1.0f, 1.0f); })
]
]
+ SHeaderRow::Column(FTraceListColumns::Platform)
.FillWidth(0.1f)
.InitialSortMode(EColumnSortMode::Ascending)
.SortMode(this, &STraceStoreWindow::GetSortModeForColumn, FTraceListColumns::Platform)
.OnSort(this, &STraceStoreWindow::OnSortModeChanged)
.OnGetMenuContent(this, &STraceStoreWindow::MakePlatformColumnHeaderMenu)
[
SNew(SBox)
.VAlign(VAlign_Center)
.MinDesiredHeight(24.0f)
[
SNew(STextBlock)
.Text(LOCTEXT("PlatformColumn", "Platform"))
.ColorAndOpacity_Lambda([this] { return FilterByPlatform->IsEmpty() ? FLinearColor(0.5f, 0.5f, 0.5f, 1.0f) : FLinearColor(0.3f, 0.75f, 1.0f, 1.0f); })
]
]
+ SHeaderRow::Column(FTraceListColumns::AppName)
.FillWidth(0.1f)
.InitialSortMode(EColumnSortMode::Ascending)
.SortMode(this, &STraceStoreWindow::GetSortModeForColumn, FTraceListColumns::AppName)
.OnSort(this, &STraceStoreWindow::OnSortModeChanged)
.OnGetMenuContent(this, &STraceStoreWindow::MakeAppNameColumnHeaderMenu)
[
SNew(SBox)
.VAlign(VAlign_Center)
.MinDesiredHeight(24.0f)
[
SNew(STextBlock)
.Text(LOCTEXT("AppNameColumn", "App Name"))
.ColorAndOpacity_Lambda([this] { return FilterByAppName->IsEmpty() ? FLinearColor(0.5f, 0.5f, 0.5f, 1.0f) : FLinearColor(0.3f, 0.75f, 1.0f, 1.0f); })
]
]
+ SHeaderRow::Column(FTraceListColumns::BuildConfig)
.FillWidth(0.1f)
.InitialSortMode(EColumnSortMode::Ascending)
.SortMode(this, &STraceStoreWindow::GetSortModeForColumn, FTraceListColumns::BuildConfig)
.OnSort(this, &STraceStoreWindow::OnSortModeChanged)
.OnGetMenuContent(this, &STraceStoreWindow::MakeBuildConfigColumnHeaderMenu)
[
SNew(SBox)
.VAlign(VAlign_Center)
.MinDesiredHeight(24.0f)
[
SNew(STextBlock)
.Text(LOCTEXT("BuildConfigColumn", "Build Config"))
.ColorAndOpacity_Lambda([this] { return FilterByBuildConfig->IsEmpty() ? FLinearColor(0.5f, 0.5f, 0.5f, 1.0f) : FLinearColor(0.3f, 0.75f, 1.0f, 1.0f); })
]
]
+ SHeaderRow::Column(FTraceListColumns::BuildTarget)
.FillWidth(0.1f)
.InitialSortMode(EColumnSortMode::Ascending)
.SortMode(this, &STraceStoreWindow::GetSortModeForColumn, FTraceListColumns::BuildTarget)
.OnSort(this, &STraceStoreWindow::OnSortModeChanged)
.OnGetMenuContent(this, &STraceStoreWindow::MakeBuildTargetColumnHeaderMenu)
[
SNew(SBox)
.VAlign(VAlign_Center)
.MinDesiredHeight(24.0f)
[
SNew(STextBlock)
.Text(LOCTEXT("BuildTargetColumn", "Build Target"))
.ColorAndOpacity_Lambda([this] { return FilterByBuildTarget->IsEmpty() ? FLinearColor(0.5f, 0.5f, 0.5f, 1.0f) : FLinearColor(0.3f, 0.75f, 1.0f, 1.0f); })
]
]
+ SHeaderRow::Column(FTraceListColumns::BuildBranch)
.FillWidth(0.2f)
.InitialSortMode(EColumnSortMode::Ascending)
.SortMode(this, &STraceStoreWindow::GetSortModeForColumn, FTraceListColumns::BuildBranch)
.OnSort(this, &STraceStoreWindow::OnSortModeChanged)
.OnGetMenuContent(this, &STraceStoreWindow::MakeBranchColumnHeaderMenu)
[
SNew(SBox)
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(LOCTEXT("BranchColumn", "Build Branch"))
.ColorAndOpacity_Lambda([this] { return FilterByBranch->IsEmpty() ? FLinearColor(0.5f, 0.5f, 0.5f, 1.0f) : FLinearColor(0.3f, 0.75f, 1.0f, 1.0f); })
]
]
+ SHeaderRow::Column(FTraceListColumns::BuildVersion)
.FillWidth(0.25f)
.InitialSortMode(EColumnSortMode::Ascending)
.SortMode(this, &STraceStoreWindow::GetSortModeForColumn, FTraceListColumns::BuildVersion)
.OnSort(this, &STraceStoreWindow::OnSortModeChanged)
.OnGetMenuContent(this, &STraceStoreWindow::MakeVersionColumnHeaderMenu)
[
SNew(SBox)
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(LOCTEXT("BuildVersionColumn", "Build Version"))
.ColorAndOpacity_Lambda([this] { return FilterByVersion->IsEmpty() ? FLinearColor(0.5f, 0.5f, 0.5f, 1.0f) : FLinearColor(0.3f, 0.75f, 1.0f, 1.0f); })
]
]
+ SHeaderRow::Column(FTraceListColumns::Size)
.FixedWidth(100.0f)
.HAlignHeader(HAlign_Right)
.HAlignCell(HAlign_Right)
.InitialSortMode(EColumnSortMode::Descending)
.SortMode(this, &STraceStoreWindow::GetSortModeForColumn, FTraceListColumns::Size)
.OnSort(this, &STraceStoreWindow::OnSortModeChanged)
.OnGetMenuContent(this, &STraceStoreWindow::MakeSizeColumnHeaderMenu)
[
SNew(SBox)
.VAlign(VAlign_Center)
.MinDesiredHeight(24.0f)
[
SNew(STextBlock)
.Text(LOCTEXT("SizeColumn", "File Size"))
.ColorAndOpacity_Lambda([this] { return FilterBySize->IsEmpty() ? FLinearColor(0.5f, 0.5f, 0.5f, 1.0f) : FLinearColor(0.3f, 0.75f, 1.0f, 1.0f); })
]
]
+ SHeaderRow::Column(FTraceListColumns::Status)
.FixedWidth(60.0f)
.HAlignHeader(HAlign_Right)
.HAlignCell(HAlign_Right)
.InitialSortMode(EColumnSortMode::Ascending)
.SortMode(this, &STraceStoreWindow::GetSortModeForColumn, FTraceListColumns::Status)
.OnSort(this, &STraceStoreWindow::OnSortModeChanged)
.OnGetMenuContent(this, &STraceStoreWindow::MakeStatusColumnHeaderMenu)
[
SNew(SBox)
.VAlign(VAlign_Center)
.MinDesiredHeight(24.0f)
[
SNew(STextBlock)
.Text(LOCTEXT("StatusColumn", "Status"))
.ColorAndOpacity_Lambda([this] { return FilterByStatus->IsEmpty() ? FLinearColor(0.5f, 0.5f, 0.5f, 1.0f) : FLinearColor(0.3f, 0.75f, 1.0f, 1.0f); })
]
]
);
return Widget;
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
////////////////////////////////////////////////////////////////////////////////////////////////////
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
TSharedRef<SWidget> STraceStoreWindow::ConstructLoadPanel()
{
TSharedRef<SWidget> Widget = SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
[
SNew(SBox)
.HAlign(HAlign_Left)
[
ConstructAutoStartPanel()
]
]
+ SHorizontalBox::Slot()
.AutoWidth()
[
SNew(SButton)
.ButtonStyle(&FAppStyle::Get().GetWidgetStyle<FButtonStyle>("PrimaryButton"))
.IsEnabled(this, &STraceStoreWindow::Open_IsEnabled)
.OnClicked(this, &STraceStoreWindow::Open_OnClicked)
.ToolTipText(LOCTEXT("OpenButtonTooltip", "Start analysis for selected trace session."))
.AddMetaData(FDriverMetaData::Id("OpenTraceButton"))
.ContentPadding(FMargin(0.0f, 0.0f, 0.0f, 0.0f))
.Content()
[
SNew(SBox)
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.TextStyle(&FAppStyle::Get().GetWidgetStyle<FTextBlockStyle>("DialogButtonText"))
.Justification(ETextJustify::Center)
.Text(LOCTEXT("OpenButtonText", "Open Trace"))
]
]
]
+ SHorizontalBox::Slot()
.Padding(FMargin(6.0f, 0.0f, 0.0f, 0.0f))
.AutoWidth()
[
SNew(SComboButton)
.ToolTipText(LOCTEXT("MRU_Tooltip", "Open a trace file or choose a trace session."))
.OnGetMenuContent(this, &STraceStoreWindow::MakeTraceListMenu)
.HasDownArrow(true)
];
return Widget;
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
////////////////////////////////////////////////////////////////////////////////////////////////////
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
TSharedRef<SWidget> STraceStoreWindow::ConstructTraceStoreDirectoryPanel()
{
TSharedRef<SWidget> Widget =
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 8.0f)
.HAlign(HAlign_Fill)
[
SNew(SHorizontalBox)
.Visibility(this, &STraceStoreWindow::VisibleIfNotConnected)
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(0.0f, 0.0f, 0.0f, 0.0f)
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text_Raw(this, &STraceStoreWindow::GetConnectionStatusTooltip)
.ColorAndOpacity(EStyleColor::Error)
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 0.0f)
.HAlign(HAlign_Fill)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(0.0f, 0.0f, 0.0f, 0.0f)
.VAlign(VAlign_Center)
[
SNew(SButton)
.ButtonStyle(&FAppStyle::Get().GetWidgetStyle<FButtonStyle>("SimpleButton"))
.ToolTipText(LOCTEXT("ManageStoreSettingsTooltip", "Manage store settings."))
.OnClicked(this, &STraceStoreWindow::StoreSettingsArea_Toggle)
[
SNew(SImage)
.Image_Raw(this, &STraceStoreWindow::StoreSettingsToggle_Icon)
.ColorAndOpacity(FSlateColor::UseForeground())
]
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(4.0f, 0.0f, 4.0f, 0.0f)
.VAlign(VAlign_Center)
[
SNew(SImage)
.Image_Raw(this, &STraceStoreWindow::GetConnectionStatusIcon)
.ToolTip(SNew(SToolTip)
.Text_Raw(this, &STraceStoreWindow::GetConnectionStatusTooltip))
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(4.0f, 0.0f, 0.0f, 0.0f)
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(LOCTEXT("StoreHostText", "Store Host:"))
]
+ SHorizontalBox::Slot()
.Padding(4.0f, 0.0f, 0.0f, 0.0f)
.AutoWidth()
.VAlign(VAlign_Center)
[
SAssignNew(StoreHostTextBox, SEditableTextBox)
.IsReadOnly(true)
.BackgroundColor(FSlateColor(EStyleColor::Background))
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(8.0f, 0.0f, 0.0f, 0.0f)
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Visibility(this, &STraceStoreWindow::VisibleIfConnected)
.Text(LOCTEXT("TraceStoreDirText", "Directory:"))
]
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
.Padding(4.0f, 0.0f, 0.0f, 0.0f)
.VAlign(VAlign_Center)
[
SAssignNew(StoreDirTextBox, SEditableTextBox)
.Visibility(this, &STraceStoreWindow::VisibleIfConnected)
.IsReadOnly(true)
.BackgroundColor(FSlateColor(EStyleColor::Background))
.Text(this, &STraceStoreWindow::GetTraceStoreDirectory)
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(4.0f, 0.0f, 0.0f, 0.0f)
.VAlign(VAlign_Center)
[
SNew(SButton)
.Visibility(this, &STraceStoreWindow::VisibleIfConnected)
.ButtonStyle(&FAppStyle::Get().GetWidgetStyle<FButtonStyle>("SimpleButton"))
.ToolTipText(LOCTEXT("ExploreTraceStoreDirButtonToolTip", "Explores the Trace Store Directory."))
.OnClicked(this, &STraceStoreWindow::ExploreTraceStoreDirectory_OnClicked)
.AddMetaData(FDriverMetaData::Id("ExploreTraceStoreDirButton"))
.IsEnabled(this, &STraceStoreWindow::CanChangeStoreSettings)
[
SNew(SImage)
.Image(FInsightsCoreStyle::Get().GetBrush("Icons.FolderExplore"))
.ColorAndOpacity(FSlateColor::UseForeground())
]
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.MaxHeight(400)
.Padding(0.0f, 8.0f)
.HAlign(HAlign_Left)
[
SAssignNew(StoreSettingsArea, SScrollBox)
.Orientation(Orient_Vertical)
.Visibility(EVisibility::Collapsed)
+ SScrollBox::Slot()
[
SNew(SVerticalBox)
.Visibility(this, &STraceStoreWindow::VisibleIfNotConnected)
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 4.0f)
[
SNew(STextBlock)
.Text(LOCTEXT("NotConnected", "Not connected to a Trace Server!"))
.ColorAndOpacity(EStyleColor::Warning)
]
]
+ SScrollBox::Slot()
[
SNew(SVerticalBox)
.Visibility(this, &STraceStoreWindow::VisibleIfConnected)
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 4.0f)
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(LOCTEXT("StoreDirLabel", "Trace Store Directory (new traces will be stored here):"))
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 4.0f)
[
SAssignNew(StoreDirListView, SListView<TSharedPtr<FTraceDirectoryModel>>)
.ListItemsSource(&StoreDirectoryModel)
.OnGenerateRow(this, &STraceStoreWindow::TraceDirs_OnGenerateRow)
.SelectionMode(ESelectionMode::None)
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 4.0f)
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(LOCTEXT("WatchDirsLabel", "Additional directories to monitor for traces:"))
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 4.0f)
[
SAssignNew(WatchDirsListView, SListView<TSharedPtr<FTraceDirectoryModel>>)
.ListItemsSource(&WatchDirectoriesModel)
.OnGenerateRow(this, &STraceStoreWindow::TraceDirs_OnGenerateRow)
.SelectionMode(ESelectionMode::None)
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 4.0f)
.HAlign(HAlign_Left)
[
SNew(SButton)
.ButtonStyle(&FAppStyle::Get().GetWidgetStyle<FButtonStyle>("Button"))
.ToolTipText(LOCTEXT("WatchDirsAddTooltip", "Adds an additional directory to monitor for traces."))
.IsEnabled(this, &STraceStoreWindow::CanChangeStoreSettings)
.OnClicked(this, &STraceStoreWindow::AddWatchDir_Clicked)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
[
SNew(SImage)
.Image(FInsightsFrontendStyle::Get().GetBrush("Icons.AddWatchDir"))
.ColorAndOpacity(FSlateColor::UseForeground())
]
+ SHorizontalBox::Slot()
.Padding(4.0f, 0.0f, 0.0f, 0.0f)
.AutoWidth()
[
SNew(STextBlock)
.Text(FText::FromStringView(TEXTVIEW("Add Directory...")))
]
]
]
]
]
;
return Widget;
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
////////////////////////////////////////////////////////////////////////////////////////////////////
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
TSharedRef<SWidget> STraceStoreWindow::ConstructAutoStartPanel()
{
TSharedRef<SHorizontalBox> Box = SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(0.0f, 0.0f, 0.0f, 0.0f)
.HAlign(HAlign_Left)
.VAlign(VAlign_Center)
[
SNew(SCheckBox)
.ToolTipText(LOCTEXT("AutoStart_Tooltip", "Enable auto-start analysis for LIVE trace sessions."))
.IsChecked(this, &STraceStoreWindow::AutoStart_IsChecked)
.OnCheckStateChanged(this, &STraceStoreWindow::AutoStart_OnCheckStateChanged)
[
SNew(STextBlock)
.Text(LOCTEXT("AutoStart_Text", "Auto-start (LIVE)"))
]
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(6.0f, 0.0f, 0.0f, 0.0f)
.HAlign(HAlign_Left)
.VAlign(VAlign_Center)
[
SNew(SBox)
.MaxDesiredWidth(200.0f)
[
SAssignNew(AutoStartPlatformFilter, SSearchBox)
.InitialText(FText::FromString(GetSettings().GetAutoStartAnalysisPlatform()))
.OnTextCommitted(this, &STraceStoreWindow::AutoStartPlatformFilterBox_OnValueCommitted)
.HintText(LOCTEXT("AutoStartPlatformFilter_Hint", "Platform"))
.ToolTipText(LOCTEXT("AutoStartPlatformFilter_Tooltip", "Type here to specify the Platform filter.\nAuto-start analysis will be enabled only for live trace sessions with this specified Platform."))
]
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(6.0f, 0.0f, 0.0f, 0.0f)
.HAlign(HAlign_Left)
.VAlign(VAlign_Center)
[
SNew(SBox)
.MaxDesiredWidth(200.0f)
[
SAssignNew(AutoStartAppNameFilter, SSearchBox)
.InitialText(FText::FromString(GetSettings().GetAutoStartAnalysisAppName()))
.OnTextCommitted(this, &STraceStoreWindow::AutoStartAppNameFilterBox_OnValueCommitted)
.HintText(LOCTEXT("AutoStartAppNameFilter_Hint", "AppName"))
.ToolTipText(LOCTEXT("AutoStartAppNameFilter_Tooltip", "Type here to specify the AppName filter.\nAuto-start analysis will be enabled only for live trace sessions with this specified AppName."))
]
];
Box->AddSlot()
.AutoWidth()
.Padding(6.0f, 0.0f, 0.0f, 0.0f)
.HAlign(HAlign_Left)
.VAlign(VAlign_Center)
[
SNew(SSeparator)
.Orientation(Orient_Vertical)
];
Box->AddSlot()
.AutoWidth()
.Padding(6.0f, 0.0f, 0.0f, 0.0f)
.HAlign(HAlign_Left)
.VAlign(VAlign_Center)
[
SNew(SCheckBox)
.ToolTipText(LOCTEXT("AutoConnect_Tooltip", "Signal to UE applications to auto-connect with local trace server and start tracing if Insights is running."))
.IsChecked(this, &STraceStoreWindow::AutoConnect_IsChecked)
.OnCheckStateChanged(this, &STraceStoreWindow::AutoConnect_OnCheckStateChanged)
[
SNew(STextBlock)
.Text(LOCTEXT("AutoConnect_Text", "Auto-connect"))
]
];
return Box;
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
////////////////////////////////////////////////////////////////////////////////////////////////////
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
TSharedRef<ITableRow> STraceStoreWindow::TraceList_OnGenerateRow(TSharedPtr<FTraceViewModel> InTrace, const TSharedRef<STableViewBase>& OwnerTable)
{
return SNew(STraceListRow, InTrace, SharedThis(this), OwnerTable);
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
////////////////////////////////////////////////////////////////////////////////////////////////////
FText STraceStoreWindow::GetConnectionStatusTooltip() const
{
static FText Connected = LOCTEXT("Connected", "Connected to the trace server.\nServer version: {0}\nRecorder port: {1}, Store port: {2}");
static FText NotConnected = LOCTEXT("NoConnection", "Unable to connect to trace server.");
static FText Connecting = LOCTEXT("Connecting", "Trying to connect to trace server.");
static FText Disconnected = LOCTEXT("Disconnected", "Connection to trace server has been lost. Attempting to reconnect in {0} seconds.");
const FStoreBrowser::EConnectionStatus Status = StoreBrowser->GetConnectionStatus();
switch (Status)
{
case FStoreBrowser::EConnectionStatus::Connected:
{
StoreBrowser->LockSettings();
FText Version = FText::FromString(StoreBrowser->GetVersion());
const uint32 RecorderPort = StoreBrowser->GetRecorderPort();
const uint32 StorePort = StoreBrowser->GetStorePort();
StoreBrowser->UnlockSettings();
return FText::Format(Connected,
Version,
FText::AsNumber(RecorderPort, &FNumberFormattingOptions::DefaultNoGrouping()),
FText::AsNumber(StorePort, &FNumberFormattingOptions::DefaultNoGrouping())
);
}
case FStoreBrowser::EConnectionStatus::NoConnection:
return NotConnected;
case FStoreBrowser::EConnectionStatus::Connecting:
return Connecting;
default:
return FText::Format(Disconnected, FText::AsNumber(static_cast<uint32>(Status)));
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
const FSlateBrush* STraceStoreWindow::GetConnectionStatusIcon() const
{
const FStoreBrowser::EConnectionStatus Status = StoreBrowser->GetConnectionStatus();
return (Status == FStoreBrowser::EConnectionStatus::Connected) ?
FInsightsFrontendStyle::Get().GetBrush("Icons.Online") :
FInsightsFrontendStyle::Get().GetBrush("Icons.Offline");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
EVisibility STraceStoreWindow::VisibleIfNotConnected() const
{
const FStoreBrowser::EConnectionStatus Status = StoreBrowser->GetConnectionStatus();
return (Status == FStoreBrowser::EConnectionStatus::Connected) ?
EVisibility::Collapsed :
EVisibility::Visible;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
EVisibility STraceStoreWindow::VisibleIfConnected() const
{
const FStoreBrowser::EConnectionStatus Status = StoreBrowser->GetConnectionStatus();
return (Status == FStoreBrowser::EConnectionStatus::Connected) ?
EVisibility::Visible :
EVisibility::Collapsed;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
EVisibility STraceStoreWindow::HiddenIfNotConnected() const
{
const FStoreBrowser::EConnectionStatus Status = StoreBrowser->GetConnectionStatus();
return (Status == FStoreBrowser::EConnectionStatus::Connected) ?
EVisibility::Visible :
EVisibility::Hidden;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
TSharedPtr<SWidget> STraceStoreWindow::TraceList_GetMenuContent()
{
const bool bShouldCloseWindowAfterMenuSelection = true;
FMenuBuilder MenuBuilder(bShouldCloseWindowAfterMenuSelection, NULL);
MenuBuilder.BeginSection("Misc");
{
{
FMenuEntryParams MenuEntry;
MenuEntry.LabelOverride = LOCTEXT("ContextMenu_Rename", "Rename...");
MenuEntry.InputBindingOverride = LOCTEXT("ContextMenu_Rename_InputBinding", "F2");
MenuEntry.ToolTipOverride = LOCTEXT("ContextMenu_Rename_ToolTip", "Starts renaming of the selected trace file.");
MenuEntry.IconOverride = FSlateIcon(FInsightsCoreStyle::GetStyleSetName(), "Icons.Rename");
MenuEntry.DirectActions = FUIAction(
FExecuteAction::CreateSP(this, &STraceStoreWindow::RenameSelectedTrace),
FCanExecuteAction::CreateSP(this, &STraceStoreWindow::CanRenameSelectedTrace));
MenuEntry.UserInterfaceActionType = EUserInterfaceActionType::Button;
MenuBuilder.AddMenuEntry(MenuEntry);
}
{
FMenuEntryParams MenuEntry;
MenuEntry.LabelOverride = LOCTEXT("ContextMenu_Delete", "Delete");
MenuEntry.InputBindingOverride = LOCTEXT("ContextMenu_Delete_InputBinding", "Del");
MenuEntry.ToolTipOverride = LOCTEXT("ContextMenu_Delete_ToolTip", "Deletes the selected trace files.");
MenuEntry.IconOverride = FSlateIcon(FAppStyle::Get().GetStyleSetName(), "Icons.Delete");
MenuEntry.DirectActions = FUIAction(
FExecuteAction::CreateSP(this, &STraceStoreWindow::DeleteSelectedTraces),
FCanExecuteAction::CreateSP(this, &STraceStoreWindow::CanDeleteSelectedTraces));
MenuEntry.UserInterfaceActionType = EUserInterfaceActionType::Button;
MenuBuilder.AddMenuEntry(MenuEntry);
}
MenuBuilder.AddSeparator();
MenuBuilder.AddMenuEntry(
LOCTEXT("ContextMenu_CopyTraceId", "Copy Trace Id"),
LOCTEXT("ContextMenu_CopyTraceId_ToolTip", "Copies the unique id of the selected trace session."),
FSlateIcon(FAppStyle::Get().GetStyleSetName(), "GenericCommands.Copy"),
FUIAction(
FExecuteAction::CreateSP(this, &STraceStoreWindow::CopyTraceId),
FCanExecuteAction::CreateSP(this, &STraceStoreWindow::CanCopyTraceId)),
NAME_None,
EUserInterfaceActionType::Button);
MenuBuilder.AddMenuEntry(
LOCTEXT("ContextMenu_CopyUri", "Copy Full Path"),
LOCTEXT("ContextMenu_CopyUri_ToolTip", "Copies the full path of the selected trace file."),
FSlateIcon(FAppStyle::Get().GetStyleSetName(), "GenericCommands.Copy"),
FUIAction(
FExecuteAction::CreateSP(this, &STraceStoreWindow::CopyFullPath),
FCanExecuteAction::CreateSP(this, &STraceStoreWindow::CanCopyFullPath)),
NAME_None,
EUserInterfaceActionType::Button);
MenuBuilder.AddMenuEntry(
LOCTEXT("ContextMenu_OpenContainingFolder", "Open Containing Folder"),
LOCTEXT("ContextMenu_OpenContainingFolder_ToolTip", "Opens the containing folder of the selected trace file."),
FSlateIcon(FAppStyle::Get().GetStyleSetName(), "Icons.FolderOpen"),
FUIAction(
FExecuteAction::CreateSP(this, &STraceStoreWindow::OpenContainingFolder),
FCanExecuteAction::CreateSP(this, &STraceStoreWindow::CanOpenContainingFolder)),
NAME_None,
EUserInterfaceActionType::Button);
}
return MenuBuilder.MakeWidget();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
bool STraceStoreWindow::CanRenameSelectedTrace() const
{
if (!CanChangeStoreSettings())
{
return false;
}
TSharedPtr<FTraceViewModel> SelectedTrace = GetSingleSelectedTrace();
return SelectedTrace.IsValid()
&& SelectedTrace->TraceId != FTraceViewModel::InvalidTraceId
&& !SelectedTrace->bIsLive;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::RenameSelectedTrace()
{
FSlateApplication::Get().CloseToolTip();
if (!CanRenameSelectedTrace())
{
return;
}
TSharedPtr<FTraceViewModel> SelectedTrace = GetSingleSelectedTrace();
SelectedTrace->bIsRenaming = true;
TSharedPtr<SEditableTextBox> RenameTextBox = SelectedTrace->RenameTextBox.Pin();
if (RenameTextBox.IsValid())
{
FSlateApplication::Get().SetKeyboardFocus(RenameTextBox.ToSharedRef(), EFocusCause::SetDirectly);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
bool STraceStoreWindow::CanDeleteSelectedTraces() const
{
if (!CanChangeStoreSettings() ||
TraceListView->GetNumItemsSelected() == 0)
{
return false;
}
TArray<TSharedPtr<FTraceViewModel>> SelectedTraces = TraceListView->GetSelectedItems();
for (const TSharedPtr<FTraceViewModel>& SelectedTrace : SelectedTraces)
{
if ((SelectedTrace->TraceId != FTraceViewModel::InvalidTraceId) &&
!SelectedTrace->bIsLive)
{
return true;
}
}
return false;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::DeleteSelectedTraces()
{
FSlateApplication::Get().CloseToolTip();
if (!CanDeleteSelectedTraces())
{
return;
}
TArray<TSharedPtr<FTraceViewModel>> TracesToDelete = TraceListView->GetSelectedItems();
// Filter the traces that can actually be deleted :
TracesToDelete.RemoveAll([](const TSharedPtr<FTraceViewModel>& InTrace) { return (InTrace->TraceId == FTraceViewModel::InvalidTraceId) || InTrace->bIsLive; });
if (TracesToDelete.Num() == 0)
{
return;
}
if (bIsDeleteTraceConfirmWindowVisible)
{
//TODO: Make a custom OkCancel modal dialog. See FSlateApplication::Get().AddModalWindow(..).
FText Title = LOCTEXT("ConfirmToDeleteTraceFile_Title", "Unreal Insights");
TStringBuilder<2048> TraceFilesToDelete;
for (int32 TraceIndex = 0; TraceIndex < TracesToDelete.Num() && TraceIndex < 3; ++TraceIndex)
{
TraceFilesToDelete.Append(TracesToDelete[TraceIndex]->Uri.ToString());
TraceFilesToDelete.Append(TEXT("\n"));
}
if (TracesToDelete.Num() > 3)
{
TraceFilesToDelete.Append(TEXT("...\n"));
}
FText ConfirmMessage = FText::Format(LOCTEXT("ConfirmToDeleteTraceFile", "You are about to delete {0} trace {0}|plural(one=file,other=files):\n\n{1}\nPress OK to continue."),
TracesToDelete.Num(), FText::FromStringView(TraceFilesToDelete.ToView()));
EAppReturnType::Type OkToDelete = FMessageDialog::Open(EAppMsgType::OkCancel, ConfirmMessage, Title);
if (OkToDelete == EAppReturnType::Cancel)
{
return;
}
}
// Find an unselected item (close to last selected one).
int32 TraceIndexToSelect = -1;
for (int32 TraceIndex = 0; TraceIndex < TracesToDelete.Num(); ++TraceIndex)
{
FTraceViewModel* TraceVM = TracesToDelete[TraceIndex].Get();
int32 FilteredTraceIndex = FilteredTraceViewModels.IndexOfByPredicate([TraceVM](const TSharedPtr<FTraceViewModel>& VM) { return VM.Get() == TraceVM; });
if (FilteredTraceIndex + 1 >= 0 &&
FilteredTraceIndex + 1 < FilteredTraceViewModels.Num() &&
!TraceListView->IsItemSelected(FilteredTraceViewModels[FilteredTraceIndex + 1]))
{
if (FilteredTraceIndex + 1> TraceIndexToSelect)
{
TraceIndexToSelect = FilteredTraceIndex + 1;
}
}
else
if (FilteredTraceIndex - 1 >= 0 &&
FilteredTraceIndex - 1 < FilteredTraceViewModels.Num() &&
!TraceListView->IsItemSelected(FilteredTraceViewModels[FilteredTraceIndex - 1]))
{
if (FilteredTraceIndex - 1 > TraceIndexToSelect)
{
TraceIndexToSelect = FilteredTraceIndex - 1;
}
}
}
TSharedPtr<FTraceViewModel> TraceToSelect = (TraceIndexToSelect >= 0) ? FilteredTraceViewModels[TraceIndexToSelect] : nullptr;
// Delete traces.
int32 NumDeletedTraces = 0;
for (int32 TraceIndex = 0; TraceIndex < TracesToDelete.Num(); ++TraceIndex)
{
const TSharedPtr<FTraceViewModel>& TraceToDelete = TracesToDelete[TraceIndex];
if (DeleteTrace(TraceToDelete))
{
++NumDeletedTraces;
TraceListView->SetItemSelection(TraceToDelete, false);
FilteredTraceViewModels.Remove(TraceToDelete);
TraceViewModels.Remove(TraceToDelete);
TraceViewModelMap.Remove(TraceToDelete->TraceId);
}
}
if (NumDeletedTraces == TracesToDelete.Num())
{
FText Message = FText::Format(LOCTEXT("DeleteSuccessFmt", "Successfully deleted {0} trace {0}|plural(one=file,other=files)."), NumDeletedTraces);
ShowSuccessMessage(Message);
// Set new selection.
if (TraceToSelect.IsValid())
{
TraceListView->SetItemSelection(TraceToSelect, true);
bIsUserSelectedTrace = true;
}
}
else
{
FText Message = FText::Format(LOCTEXT("FailedToDeleteAllTracesFmt", "Deleted {0} trace {0}|plural(one=file,other=files). Failed to delete {1} trace {1}|plural(one=file,other=files)!"),
NumDeletedTraces, TracesToDelete.Num() - NumDeletedTraces);
ShowFailMessage(Message);
}
OnTraceListChanged();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
bool STraceStoreWindow::DeleteTrace(const TSharedPtr<FTraceViewModel>& TraceToDelete)
{
FString TraceName = TraceToDelete->Name.ToString();
UE_LOG(LogInsightsFrontend, Log, TEXT("[TraceStore] Deleting \"%s\"..."), *TraceName);
if (TraceToDelete->bIsLive)
{
FText Message = FText::Format(LOCTEXT("CannotDeleteLiveTraceFmt", "Cannot delete a live trace (\"{0}\")!"), FText::FromString(TraceName));
ShowFailMessage(Message);
return false;
}
FString TraceFile = TraceToDelete->Uri.ToString();
if (!FPaths::FileExists(TraceFile) || !IFileManager::Get().Delete(*TraceFile))
{
UE_LOG(LogInsightsFrontend, Warning, TEXT("[TraceStore] Failed to delete trace file (\"%s\")!"), *TraceFile);
FText Message = FText::Format(LOCTEXT("DeleteFailFmt", "Failed to delete \"{0}\"!"), FText::FromString(TraceName));
ShowFailMessage(Message);
return false;
}
UE_LOG(LogInsightsFrontend, Verbose, TEXT("[TraceStore] Deleted utrace file (\"%s\")."), *TraceFile);
FString CacheFile = FPaths::ChangeExtension(TraceFile, TEXT("ucache"));
if (FPaths::FileExists(CacheFile))
{
if (IFileManager::Get().Delete(*CacheFile))
{
UE_LOG(LogInsightsFrontend, Verbose, TEXT("[TraceStore] Deleted ucache file (\"%s\")."), *CacheFile);
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
bool STraceStoreWindow::CanCopyTraceId() const
{
TSharedPtr<FTraceViewModel> SelectedTrace = GetSingleSelectedTrace();
return SelectedTrace.IsValid()
&& SelectedTrace->TraceId != FTraceViewModel::InvalidTraceId;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::CopyTraceId()
{
if (CanCopyTraceId())
{
TSharedPtr<FTraceViewModel> SelectedTrace = GetSingleSelectedTrace();
FString ClipboardText = FString::Printf(TEXT("0x%X"), SelectedTrace->TraceId);
FPlatformApplicationMisc::ClipboardCopy(*ClipboardText);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
bool STraceStoreWindow::CanCopyFullPath() const
{
TSharedPtr<FTraceViewModel> SelectedTrace = GetSingleSelectedTrace();
return SelectedTrace.IsValid()
&& SelectedTrace->TraceId != FTraceViewModel::InvalidTraceId;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::CopyFullPath()
{
if (CanCopyFullPath())
{
TSharedPtr<FTraceViewModel> SelectedTrace = GetSingleSelectedTrace();
FPlatformApplicationMisc::ClipboardCopy(*SelectedTrace->Uri.ToString());
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
bool STraceStoreWindow::CanOpenContainingFolder() const
{
if (!CanChangeStoreSettings())
{
return false;
}
TSharedPtr<FTraceViewModel> SelectedTrace = GetSingleSelectedTrace();
return SelectedTrace.IsValid()
&& SelectedTrace->TraceId != FTraceViewModel::InvalidTraceId;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::OpenContainingFolder()
{
FSlateApplication::Get().CloseToolTip();
if (CanOpenContainingFolder())
{
TSharedPtr<FTraceViewModel> SelectedTrace = GetSingleSelectedTrace();
FPlatformProcess::ExploreFolder(*SelectedTrace->Uri.ToString());
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
bool STraceStoreWindow::HasAnyLiveTrace() const
{
return TraceViewModels.FindByPredicate(&FTraceViewModel::bIsLive) != nullptr;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::ShowSplashScreenOverlay()
{
SplashScreenOverlayFadeTime = 3.5f;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::TickSplashScreenOverlay(const float InDeltaTime)
{
if (SplashScreenOverlayFadeTime > 0.0f)
{
SplashScreenOverlayFadeTime = FMath::Max(0.0f, SplashScreenOverlayFadeTime - InDeltaTime);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
float STraceStoreWindow::SplashScreenOverlayOpacity() const
{
constexpr float FadeInStartTime = 3.5f;
constexpr float FadeInEndTime = 3.0f;
constexpr float FadeOutStartTime = 1.0f;
constexpr float FadeOutEndTime = 0.0f;
const float Opacity =
SplashScreenOverlayFadeTime > FadeInStartTime ? 0.0f :
SplashScreenOverlayFadeTime > FadeInEndTime ? 1.0f - (SplashScreenOverlayFadeTime - FadeInEndTime) / (FadeInStartTime - FadeInEndTime) :
SplashScreenOverlayFadeTime > FadeOutStartTime ? 1.0f :
SplashScreenOverlayFadeTime > FadeOutEndTime ? (SplashScreenOverlayFadeTime - FadeOutEndTime) / (FadeOutStartTime - FadeOutEndTime) : 0.0f;
return Opacity;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
EVisibility STraceStoreWindow::SplashScreenOverlay_Visibility() const
{
return SplashScreenOverlayFadeTime > 0.0f ? EVisibility::Visible : EVisibility::Collapsed;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
FSlateColor STraceStoreWindow::SplashScreenOverlay_ColorAndOpacity() const
{
return FSlateColor(FLinearColor(0.7f, 0.7f, 0.7f, SplashScreenOverlayOpacity()));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
FSlateColor STraceStoreWindow::SplashScreenOverlay_TextColorAndOpacity() const
{
return FSlateColor(FLinearColor(0.8f, 0.8f, 0.8f, SplashScreenOverlayOpacity()));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
FText STraceStoreWindow::GetSplashScreenOverlayText() const
{
return FText::Format(LOCTEXT("StartAnalysis", "Starting analysis...\n{0}"), FText::FromString(SplashScreenOverlayTraceFile));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
FReply STraceStoreWindow::RefreshTraces_OnClicked()
{
RefreshTraceList();
return FReply::Handled();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
FSlateColor STraceStoreWindow::GetColorByPath(const FString& Uri)
{
const FStringView UriBase = FPathViews::GetPath(Uri);
const TSharedPtr<FTraceDirectoryModel>* Dir = WatchDirectoriesModel.FindByPredicate([&](const TSharedPtr<FTraceDirectoryModel>& Dir)
{
return FPathViews::Equals(UriBase, Dir->Path);
});
if (Dir)
{
return FAppStyle::Get().GetSlateColor((*Dir)->Color);
}
// If this is default trace store directory, use foreground
return FSlateColor(FSlateColor::UseForeground());
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::RefreshTraceList()
{
FStopwatch StopwatchTotal;
StopwatchTotal.Start();
int32 AddedTraces = 0;
int32 RemovedTraces = 0;
int32 UpdatedTraces = 0;
bool bSettingsChanged = false;
{
StoreBrowser->LockSettings();
const uint32 NewSettingsChangeSerial = StoreBrowser->GetSettingsChangeSerial();
if (NewSettingsChangeSerial != SettingsChangeSerial)
{
SettingsChangeSerial = NewSettingsChangeSerial;
// Add remote server controls. It's not possible to change server
// address on the fly so we can expect that there cannot be more than
// two entries (the local and possibly a currently connected remote server)
if (!StoreBrowser->GetHost().Equals(TEXT("127.0.0.1")) && ServerControls.Num() == 1)
{
ServerControls.Emplace(*StoreBrowser->GetHost(), StoreBrowser->GetStorePort(), FAppStyle::Get().GetStyleSetName());
}
// Update the host text
if (StoreHostTextBox)
{
StoreHostTextBox->SetText(FText::FromString(StoreBrowser->GetHost()));
}
// Update the store text box
if (StoreDirTextBox)
{
StoreDirTextBox->SetText(FText::FromString(StoreBrowser->GetStoreDirectory()));
}
// Update store directory model
StoreDirectoryModel.Empty(1);
StoreDirectoryModel.Push(MakeShared<FTraceDirectoryModel>(
FString(StoreBrowser->GetStoreDirectory()),
NAME_None,
ETraceDirOperations::ModifyStore|ETraceDirOperations::Explore
));
if (StoreDirListView)
{
StoreDirListView->RequestListRefresh();
}
// Update additional monitored directories model
static const FName DirColor[] =
{
FName("Colors.AccentBlue"),
FName("Colors.AccentGreen"),
FName("Colors.AccentYellow"),
FName("Colors.AccentOrange"),
FName("Colors.AccentPurple"),
FName("Colors.AccentPink")
};
int32 ColorIdx = 0;
WatchDirectoriesModel.Empty();
for (const auto& Dir : StoreBrowser->GetWatchDirectories())
{
WatchDirectoriesModel.Emplace(MakeShared<FTraceDirectoryModel>(
FString(Dir),
DirColor[ColorIdx],
ETraceDirOperations::Delete|ETraceDirOperations::Explore
));
ColorIdx = FMath::WrapExclusive(++ColorIdx, (int32)0, int32(UE_ARRAY_COUNT(DirColor)));
}
if (WatchDirsListView)
{
WatchDirsListView->RequestListRefresh();
}
bSettingsChanged = true;
}
StoreBrowser->UnlockSettings();
StoreBrowser->LockTraces();
const uint32 NewTracesChangeSerial = StoreBrowser->GetTracesChangeSerial();
if (NewTracesChangeSerial != TracesChangeSerial || bSettingsChanged)
{
TracesChangeSerial = NewTracesChangeSerial;
//UE_LOG(LogInsightsFrontend, Log, TEXT("[TraceStore] Syncing the trace list with StoreBrowser..."));
const TArray<TSharedPtr<FStoreBrowserTraceInfo>>& InTraces = StoreBrowser->GetTraces();
const TMap<uint32, TSharedPtr<FStoreBrowserTraceInfo>>& InTraceMap = StoreBrowser->GetTraceMap();
// Check for removed traces.
{
int32 TraceViewModelCount = TraceViewModels.Num();
for (int32 TraceIndex = 0; TraceIndex < TraceViewModelCount; ++TraceIndex)
{
FTraceViewModel& Trace = *TraceViewModels[TraceIndex];
const TSharedPtr<FStoreBrowserTraceInfo>* InTracePtrPtr = InTraceMap.Find(Trace.TraceId);
if (!InTracePtrPtr)
{
// This trace was removed.
RemovedTraces++;
TraceViewModels.RemoveAtSwap(TraceIndex);
TraceViewModelMap.Remove(Trace.TraceId);
TraceIndex--;
TraceViewModelCount--;
}
}
}
// Check for added traces and for updated traces.
for (const TSharedPtr<FStoreBrowserTraceInfo>& InTracePtr : InTraces)
{
const FStoreBrowserTraceInfo& SourceTrace = *InTracePtr;
TSharedPtr<FTraceViewModel>* TracePtrPtr = TraceViewModelMap.Find(SourceTrace.TraceId);
if (TracePtrPtr)
{
FTraceViewModel& Trace = **TracePtrPtr;
if (Trace.ChangeSerial != SourceTrace.ChangeSerial || bSettingsChanged)
{
// This trace was updated or settings updated
UpdatedTraces++;
UpdateTrace(Trace, SourceTrace);
}
}
else
{
// This trace was added.
AddedTraces++;
TSharedPtr<FTraceViewModel> TracePtr = MakeShared<FTraceViewModel>();
TracePtr->TraceId = SourceTrace.TraceId;
UpdateTrace(*TracePtr, SourceTrace);
TraceViewModels.Add(TracePtr);
TraceViewModelMap.Add(TracePtr->TraceId, TracePtr);
}
}
}
StoreBrowser->UnlockTraces();
}
if (AddedTraces > 0 || RemovedTraces > 0)
{
// If we have new or removed traces we need to rebuild the list view.
OnTraceListChanged();
}
StopwatchTotal.Stop();
const double Duration = StopwatchTotal.GetAccumulatedTime();
if ((Duration > 0.0001) && (UpdatedTraces > 0 || AddedTraces > 0 || RemovedTraces > 0))
{
UE_LOG(LogInsightsFrontend, Log, TEXT("[TraceStore] The trace list refreshed in %.0f ms (%d traces : %d updated, %d added, %d removed)."),
Duration * 1000.0, TraceViewModels.Num(), UpdatedTraces, AddedTraces, RemovedTraces);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
bool STraceStoreWindow::IsConnected() const
{
return StoreBrowser->GetConnectionStatus() == FStoreBrowser::EConnectionStatus::Connected;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::UpdateTrace(FTraceViewModel& InOutTrace, const FStoreBrowserTraceInfo& InSourceTrace)
{
check(InOutTrace.TraceId == InSourceTrace.TraceId);
InOutTrace.ChangeSerial = InSourceTrace.ChangeSerial;
InOutTrace.Name = FText::FromString(InSourceTrace.Name);
InOutTrace.Uri = FText::FromString(InSourceTrace.Uri);
InOutTrace.DirectoryColor = GetColorByPath(InSourceTrace.Uri);
InOutTrace.Timestamp = InSourceTrace.Timestamp;
InOutTrace.Size = InSourceTrace.Size;
InOutTrace.bIsLive = InSourceTrace.bIsLive;
InOutTrace.IpAddress = InSourceTrace.IpAddress;
// Is metadata updated?
if (!InOutTrace.bIsMetadataUpdated && InSourceTrace.MetadataUpdateCount == 0)
{
InOutTrace.bIsMetadataUpdated = true;
InOutTrace.Platform = FText::FromString(InSourceTrace.Platform);
if (!InSourceTrace.ProjectName.IsEmpty())
{
InOutTrace.AppName = FText::FromString(InSourceTrace.ProjectName);
}
else
{
InOutTrace.AppName = FText::FromString(InSourceTrace.AppName);
}
InOutTrace.CommandLine = FText::FromString(InSourceTrace.CommandLine);
InOutTrace.Branch = FText::FromString(InSourceTrace.Branch);
InOutTrace.BuildVersion = FText::FromString(InSourceTrace.BuildVersion);
InOutTrace.Changelist = InSourceTrace.Changelist;
InOutTrace.ConfigurationType = InSourceTrace.ConfigurationType;
InOutTrace.TargetType = InSourceTrace.TargetType;
}
const FInsightsFrontendSettings& Settings = GetSettings();
// Auto start analysis for a live trace session.
if (InOutTrace.bIsLive &&
InOutTrace.bIsMetadataUpdated &&
Settings.IsAutoStartAnalysisEnabled() && // is auto start enabled?
!AutoStartedSessions.Contains(InOutTrace.TraceId)) // is not already auto-started?
{
const FString& AutoStartPlatformFilterStr = Settings.GetAutoStartAnalysisPlatform();
const FString& AutoStartAppNameFilterStr = Settings.GetAutoStartAnalysisAppName();
// matches filter?
if ((AutoStartPlatformFilterStr.IsEmpty() || FCString::Strcmp(*AutoStartPlatformFilterStr, *InOutTrace.Platform.ToString()) == 0) &&
(AutoStartAppNameFilterStr.IsEmpty() || FCString::Strcmp(*AutoStartAppNameFilterStr, *InOutTrace.AppName.ToString()) == 0) &&
(AutoStartConfigurationTypeFilter == EBuildConfiguration::Unknown || AutoStartConfigurationTypeFilter == InOutTrace.ConfigurationType) &&
(AutoStartTargetTypeFilter == EBuildTargetType::Unknown || AutoStartTargetTypeFilter == InOutTrace.TargetType))
{
UE_LOG(LogInsightsFrontend, Log, TEXT("[TraceStore] Auto starting analysis for trace with id 0x%08X..."), InOutTrace.TraceId);
AutoStartedSessions.Add(InOutTrace.TraceId);
OpenTraceSession(InOutTrace.TraceId);
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::OnTraceListChanged()
{
UpdateFiltering();
UpdateSorting();
UpdateTraceListView();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::UpdateTraceListView()
{
if (!TraceListView)
{
return;
}
TArray<TSharedPtr<FTraceViewModel>> NewSelectedTraces;
if (bIsUserSelectedTrace)
{
// Identify the previously selected traces (if still available) to ensure selection remains unchanged.
TArray<TSharedPtr<FTraceViewModel>> SelectedTraces = TraceListView->GetSelectedItems();
for (const TSharedPtr<FTraceViewModel>& SelectedTrace : SelectedTraces)
{
TSharedPtr<FTraceViewModel>* FoundNewTrace = TraceViewModelMap.Find(SelectedTrace->TraceId);
if (!FoundNewTrace)
{
FoundNewTrace = TraceViewModels.FindByPredicate([SelectedTrace](const TSharedPtr<FTraceViewModel>& Trace) { return Trace->Uri.EqualTo(SelectedTrace->Uri); });
}
if (!FoundNewTrace)
{
FoundNewTrace = TraceViewModels.FindByPredicate([SelectedTrace](const TSharedPtr<FTraceViewModel>& Trace) { return Trace->Name.EqualTo(SelectedTrace->Name); });
}
if (FoundNewTrace)
{
NewSelectedTraces.Add(*FoundNewTrace);
}
}
}
double DistanceFromTop = TraceListView->GetScrollDistance().Y;
double DistanceFromBottom = TraceListView->GetScrollDistanceRemaining().Y;
TraceListView->RebuildList();
// If no selection...
if (NewSelectedTraces.Num() == 0 && FilteredTraceViewModels.Num() > 0)
{
if ((SortColumn == FTraceListColumns::Date && SortMode == EColumnSortMode::Ascending) ||
(SortColumn == FTraceListColumns::Status && SortMode == EColumnSortMode::Ascending))
{
// Auto select the last (newest) trace.
NewSelectedTraces.Add(FilteredTraceViewModels.Last());
DistanceFromTop = 1.0;
DistanceFromBottom = 0.0; // scroll to bottom
}
else
{
// Auto select the first trace.
NewSelectedTraces.Add(FilteredTraceViewModels[0]);
DistanceFromTop = 0.0; // scroll to top
DistanceFromBottom = 1.0;
}
}
if (FMath::IsNearlyZero(DistanceFromBottom, 1.0E-8))
{
TraceListView->ScrollToBottom();
}
else if (FMath::IsNearlyZero(DistanceFromTop, 1.0E-8))
{
TraceListView->ScrollToTop();
}
// Restore selection.
if (NewSelectedTraces.Num() > 0)
{
TraceListView->ClearSelection();
TraceListView->SetItemSelection(NewSelectedTraces, true);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
TSharedPtr<FTraceViewModel> STraceStoreWindow::GetSingleSelectedTrace() const
{
return (TraceListView->GetNumItemsSelected() == 1) ? TraceListView->GetSelectedItems()[0] : nullptr;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::TraceList_OnSelectionChanged(TSharedPtr<FTraceViewModel> TraceSession, ESelectInfo::Type SelectInfo)
{
if (SelectInfo != ESelectInfo::Direct)
{
bIsUserSelectedTrace = true;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::TraceList_OnMouseButtonDoubleClick(TSharedPtr<FTraceViewModel> TraceSession)
{
OpenTraceSession(TraceSession);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
ECheckBoxState STraceStoreWindow::AutoStart_IsChecked() const
{
return GetSettings().IsAutoStartAnalysisEnabled() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::AutoStart_OnCheckStateChanged(ECheckBoxState NewState)
{
if (AutoStart_IsChecked() == NewState)
{
return;
}
GetSettings().SetAndSaveAutoStartAnalysis(!GetSettings().IsAutoStartAnalysisEnabled());
}
////////////////////////////////////////////////////////////////////////////////////////////////////
ECheckBoxState STraceStoreWindow::AutoConnect_IsChecked() const
{
return GetSettings().IsAutoConnectEnabled() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::AutoConnect_OnCheckStateChanged(ECheckBoxState NewState)
{
if (AutoConnect_IsChecked() == NewState)
{
return;
}
GetSettings().SetAndSaveAutoConnect(!GetSettings().IsAutoConnectEnabled());
if (GetSettings().IsAutoConnectEnabled())
{
EnableAutoConnect();
}
else
{
DisableAutoConnect();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::EnableAutoConnect()
{
#if PLATFORM_WINDOWS
ensure(AutoConnectEvent == nullptr);
// The event is used by runtime to choose when to try to auto-connect.
// See FTraceAuxiliary::TryAutoConnect() in \Runtime\Core\Private\ProfilingDebugging\TraceAuxiliary.cpp
AutoConnectEvent = CreateEvent(NULL, true, false, TEXT("Local\\UnrealInsightsAutoConnect"));
if (AutoConnectEvent == nullptr || GetLastError() != ERROR_SUCCESS)
{
UE_LOG(LogInsightsFrontend, Warning, TEXT("[TraceStore] Failed to create AutoConnect event."));
}
#elif PLATFORM_MAC || PLATFORM_LINUX
ensure(AutoConnectEvent == SEM_FAILED);
sem_unlink("/UnrealInsightsAutoConnect");
AutoConnectEvent = sem_open("/UnrealInsightsAutoConnect", O_CREAT | O_WRONLY | O_EXCL, 0644, 1);
if (AutoConnectEvent == SEM_FAILED)
{
UE_LOG(LogInsightsFrontend, Warning, TEXT("[TraceStore] Failed to create AutoConnect semaphore: %d"), errno);
}
#endif
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::DisableAutoConnect()
{
#if PLATFORM_WINDOWS
if (AutoConnectEvent != nullptr)
{
CloseHandle(AutoConnectEvent);
AutoConnectEvent = nullptr;
}
#elif PLATFORM_MAC || PLATFORM_LINUX
if (AutoConnectEvent != SEM_FAILED)
{
sem_close(AutoConnectEvent);
AutoConnectEvent = SEM_FAILED;
if (sem_unlink("/UnrealInsightsAutoConnect"))
{
UE_LOG(LogInsightsFrontend, Warning, TEXT("[TraceStore] Failed to remove AutoConnect semaphore: %d"), errno);
}
}
#endif
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
{
}
////////////////////////////////////////////////////////////////////////////////////////////////////
bool STraceStoreWindow::CoreTick(float DeltaTime)
{
// We need to update the trace list, but not too often.
static uint64 NextTimestamp = 0;
uint64 Time = FPlatformTime::Cycles64();
if (Time > NextTimestamp)
{
const uint64 WaitTime = static_cast<uint64>(0.5 / FPlatformTime::GetSecondsPerCycle64()); // 500ms
NextTimestamp = Time + WaitTime;
RefreshTraceList();
if (bFilterStatsTextIsDirty)
{
UpdateFilterStatsText();
}
}
if (bSetKeyboardFocusOnNextTick)
{
bSetKeyboardFocusOnNextTick = false;
FSlateApplication::Get().ClearKeyboardFocus();
FSlateApplication::Get().SetKeyboardFocus(TraceListView);
}
TickSplashScreenOverlay(DeltaTime);
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
EActiveTimerReturnType STraceStoreWindow::UpdateActiveDuration(double InCurrentTime, float InDeltaTime)
{
DurationActive += InDeltaTime;
// The window will explicitly unregister this active timer when the mouse leaves.
return EActiveTimerReturnType::Continue;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
SCompoundWidget::OnMouseEnter(MyGeometry, MouseEvent);
if (!ActiveTimerHandle.IsValid())
{
ActiveTimerHandle = RegisterActiveTimer(0.f, FWidgetActiveTimerDelegate::CreateSP(this, &STraceStoreWindow::UpdateActiveDuration));
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::OnMouseLeave(const FPointerEvent& MouseEvent)
{
SCompoundWidget::OnMouseLeave(MouseEvent);
auto PinnedActiveTimerHandle = ActiveTimerHandle.Pin();
if (PinnedActiveTimerHandle.IsValid())
{
UnRegisterActiveTimer(PinnedActiveTimerHandle.ToSharedRef());
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
FReply STraceStoreWindow::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent)
{
//TODO: Make commands for Rename and Delete.
//return GetCommandList()->ProcessCommandBindings(InKeyEvent) ? FReply::Handled() : FReply::Unhandled();
if (InKeyEvent.GetKey() == EKeys::F5) // refresh metadata for all trace sessions
{
StoreBrowser->Refresh();
SettingsChangeSerial = 0;
TracesChangeSerial = 0;
TraceViewModels.Reset();
TraceViewModelMap.Reset();
OnTraceListChanged();
return FReply::Handled();
}
else if (InKeyEvent.GetKey() == EKeys::F2)
{
RenameSelectedTrace();
return FReply::Handled();
}
else if (InKeyEvent.GetKey() == EKeys::Delete)
{
DeleteSelectedTraces();
return FReply::Handled();
}
return SCompoundWidget::OnKeyDown(MyGeometry, InKeyEvent);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
FReply STraceStoreWindow::OnDragOver(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent)
{
TSharedPtr<FExternalDragOperation> DragDropOp = DragDropEvent.GetOperationAs<FExternalDragOperation>();
if (DragDropOp.IsValid())
{
if (DragDropOp->HasFiles())
{
const TArray<FString>& Files = DragDropOp->GetFiles();
if (Files.Num() == 1)
{
const FString DraggedFileExtension = FPaths::GetExtension(Files[0], true);
if (DraggedFileExtension == TEXT(".utrace"))
{
return FReply::Handled();
}
if (DraggedFileExtension == TEXT(".csv") || DraggedFileExtension == TEXT(".tsv"))
{
return FReply::Handled();
}
}
}
}
return SCompoundWidget::OnDragOver(MyGeometry, DragDropEvent);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
FReply STraceStoreWindow::OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent)
{
TSharedPtr<FExternalDragOperation> DragDropOp = DragDropEvent.GetOperationAs<FExternalDragOperation>();
if (DragDropOp.IsValid())
{
if (DragDropOp->HasFiles())
{
// For now, only allow a single file.
const TArray<FString>& Files = DragDropOp->GetFiles();
if (Files.Num() == 1)
{
const FString DraggedFileExtension = FPaths::GetExtension(Files[0], true);
if (DraggedFileExtension == TEXT(".utrace"))
{
OpenTraceFile(Files[0]);
return FReply::Handled();
}
if (DraggedFileExtension == TEXT(".csv") || DraggedFileExtension == TEXT(".tsv"))
{
TableImporter->ImportFile(Files[0]);
return FReply::Handled();
}
}
}
}
return SCompoundWidget::OnDrop(MyGeometry, DragDropEvent);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
bool STraceStoreWindow::Open_IsEnabled() const
{
TSharedPtr<FTraceViewModel> SelectedTrace = GetSingleSelectedTrace();
return SelectedTrace.IsValid()
&& SelectedTrace->TraceId != FTraceViewModel::InvalidTraceId;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
FReply STraceStoreWindow::Open_OnClicked()
{
TSharedPtr<FTraceViewModel> SelectedTrace = GetSingleSelectedTrace();
OpenTraceSession(SelectedTrace);
return FReply::Handled();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
bool STraceStoreWindow::ShowOpenTraceFileDialog(FString& OutTraceFile) const
{
if (OpenTraceFileDefaultDirectory.IsEmpty())
{
OpenTraceFileDefaultDirectory = FPaths::ConvertRelativePathToFull(TraceStoreConnection->GetStoreDir());
}
TArray<FString> OutFiles;
bool bOpened = false;
IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
if (DesktopPlatform != nullptr)
{
FSlateApplication::Get().CloseToolTip();
bOpened = DesktopPlatform->OpenFileDialog
(
FSlateApplication::Get().FindBestParentWindowHandleForDialogs(nullptr),
LOCTEXT("LoadTrace_FileDesc", "Open trace file...").ToString(),
OpenTraceFileDefaultDirectory,
TEXT(""),
LOCTEXT("LoadTrace_FileFilter", "Trace files (*.utrace)|*.utrace|All files (*.*)|*.*").ToString(),
EFileDialogFlags::None,
OutFiles
);
}
if (bOpened == true && OutFiles.Num() == 1)
{
OutTraceFile = OutFiles[0];
OpenTraceFileDefaultDirectory = FPaths::GetPath(OutTraceFile);
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::OpenTraceFile()
{
FString TraceFile;
if (ShowOpenTraceFileDialog(TraceFile))
{
OpenTraceFile(TraceFile);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::OpenTraceFile(const FString& InTraceFile)
{
UE_LOG(LogInsightsFrontend, Log, TEXT("[TraceStore] Start analysis (in separate process) for trace file: \"%s\""), *InTraceFile);
FString CmdLine = TEXT("-OpenTraceFile=\"") + InTraceFile + TEXT("\"");
FString ExtraCmdParams;
GetExtraCommandLineParams(ExtraCmdParams);
CmdLine += ExtraCmdParams;
FMiscUtils::OpenUnrealInsights(*CmdLine);
SplashScreenOverlayTraceFile = FPaths::GetBaseFilename(InTraceFile);
ShowSplashScreenOverlay();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::OpenTraceSession(TSharedPtr<FTraceViewModel> InTraceSession)
{
if (InTraceSession.IsValid() &&
InTraceSession->TraceId != FTraceViewModel::InvalidTraceId)
{
OpenTraceSession(InTraceSession->TraceId);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::OpenTraceSession(uint32 InTraceId)
{
uint32 StoreAddress = 0;
uint32 StorePort = 0;
if (!TraceStoreConnection->GetStoreAddressAndPort(StoreAddress, StorePort))
{
return;
}
UE_LOG(LogInsightsFrontend, Log, TEXT("[TraceStore] Start analysis (in separate process) for trace id: 0x%08X"), InTraceId);
FString CmdLine = FString::Printf(TEXT("-OpenTraceId=0x%X -Store=%u.%u.%u.%u:%u"),
InTraceId,
(StoreAddress >> 24) & 0xFF,
(StoreAddress >> 16) & 0xFF,
(StoreAddress >> 8) & 0xFF,
(StoreAddress ) & 0xFF,
StorePort);
FString ExtraCmdParams;
GetExtraCommandLineParams(ExtraCmdParams);
CmdLine += ExtraCmdParams;
FMiscUtils::OpenUnrealInsights(*CmdLine);
TSharedPtr<FTraceViewModel>* TraceSessionPtrPtr = TraceViewModelMap.Find(InTraceId);
if (TraceSessionPtrPtr)
{
FTraceViewModel& TraceSession = **TraceSessionPtrPtr;
SplashScreenOverlayTraceFile = FPaths::GetBaseFilename(TraceSession.Uri.ToString());
}
ShowSplashScreenOverlay();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
TSharedRef<SWidget> STraceStoreWindow::MakeTraceListMenu()
{
FSlateApplication::Get().CloseToolTip();
RefreshTraceList();
FMenuBuilder MenuBuilder(/*bInShouldCloseWindowAfterMenuSelection=*/true, nullptr);
MenuBuilder.BeginSection("Misc", LOCTEXT("TraceListMenu_Section_Misc", "Misc"));
{
MenuBuilder.AddMenuEntry(
LOCTEXT("OpenFileButtonLabel", "Open Trace File..."),
LOCTEXT("OpenFileButtonTooltip", "Starts analysis for a specified trace file."),
FSlateIcon(FAppStyle::Get().GetStyleSetName(), "Icons.FolderOpen"),
FUIAction(FExecuteAction::CreateSP(this, &STraceStoreWindow::OpenTraceFile)),
NAME_None,
EUserInterfaceActionType::Button);
MenuBuilder.AddMenuEntry(
LOCTEXT("ImportTableButtonLabel", "Import Table..."),
LOCTEXT("ImportTableButtonTooltip", "Opens .csv or .tsv file."),
FSlateIcon(FInsightsCoreStyle::GetStyleSetName(), "Icons.ImportTable"),
FUIAction(FExecuteAction::CreateLambda([this]{ TableImporter->StartImportProcess(); })),
NAME_None,
EUserInterfaceActionType::Button);
MenuBuilder.AddMenuEntry(
LOCTEXT("DiffTablesButtonLabel", "Diff Tables..."),
LOCTEXT("DiffTablesButtonTooltip", "Opens two table files in diff mode."),
FSlateIcon(FAppStyle::Get().GetStyleSetName(), "Icons.FolderOpen"),
FUIAction(FExecuteAction::CreateLambda([this]{ TableImporter->StartDiffProcess(); })),
NAME_None,
EUserInterfaceActionType::Button);
}
MenuBuilder.EndSection();
MenuBuilder.BeginSection("AvailableTraces", LOCTEXT("TraceListMenu_Section_AvailableTraces", "Top Most Recently Created Traces"));
{
// Make a copy of the trace list (to allow list view to be sorted by other criteria).
TArray<TSharedPtr<FTraceViewModel>> SortedTraces(TraceViewModels);
Algo::SortBy(SortedTraces, &FTraceViewModel::Timestamp);
int32 TraceCountLimit = 10; // top 10
// Iterate in reverse order as we want most recently created traces first.
for (int32 TraceIndex = SortedTraces.Num() - 1; TraceIndex >= 0 && TraceCountLimit > 0; --TraceIndex, --TraceCountLimit)
{
const FTraceViewModel& Trace = *SortedTraces[TraceIndex];
FText Label = Trace.Name;
if (Trace.bIsLive)
{
Label = FText::Format(LOCTEXT("LiveTraceTextFmt", "{0} (LIVE!)"), Label);
}
MenuBuilder.AddMenuEntry(
Label,
TAttribute<FText>(), // no tooltip
FSlateIcon(),
FUIAction(FExecuteAction::CreateSP(this, &STraceStoreWindow::OpenTraceSession, Trace.TraceId)),
NAME_None,
EUserInterfaceActionType::Button);
}
}
MenuBuilder.EndSection();
MenuBuilder.BeginSection("UnrealTraceServer", LOCTEXT("TraceListMenu_Section_Server", "Server"));
{
MenuBuilder.AddSubMenu(
LOCTEXT("ServerControlLabel", "Unreal Trace Server"),
LOCTEXT("ServerControlTooltip", "Info and controls for the Unreal Trace Server instances"),
FNewMenuDelegate::CreateLambda([this](FMenuBuilder& MenuBuilder)
{
for (auto& ServerControl : ServerControls)
{
ServerControl.MakeMenu(MenuBuilder);
}
}),
false,
FSlateIcon(FAppStyle::Get().GetStyleSetName(), "Icons.Server"));
}
MenuBuilder.EndSection();
MenuBuilder.BeginSection("DebugOptions", LOCTEXT("TraceListMenu_Section_DebugOptions", "Debug Options"));
// Enable Automation Tests Option.
{
FUIAction ToogleAutomationTestsAction;
ToogleAutomationTestsAction.ExecuteAction = FExecuteAction::CreateLambda([this]()
{
this->SetEnableAutomaticTesting(!this->GetEnableAutomaticTesting());
});
ToogleAutomationTestsAction.GetActionCheckState = FGetActionCheckState::CreateLambda([this]()
{
return this->GetEnableAutomaticTesting() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
});
MenuBuilder.AddMenuEntry(
LOCTEXT("EnableAutomatedTesting", "Enable Session Automation Testing"),
LOCTEXT("EnableAutomatedTestingDesc", "Activates the automatic test system for new sessions opened from this window."),
FSlateIcon(FInsightsCoreStyle::GetStyleSetName(), "Icons.TestAutomation"),
ToogleAutomationTestsAction,
NAME_None,
EUserInterfaceActionType::ToggleButton);
}
// Enable Debug Tools Option.
{
FUIAction ToogleDebugToolsAction;
ToogleDebugToolsAction.ExecuteAction = FExecuteAction::CreateLambda([this]()
{
this->SetEnableDebugTools(!this->GetEnableDebugTools());
});
ToogleDebugToolsAction.GetActionCheckState = FGetActionCheckState::CreateLambda([this]()
{
return this->GetEnableDebugTools() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
});
MenuBuilder.AddMenuEntry(
LOCTEXT("EnableDebugTools", "Enable Debug Tools"),
LOCTEXT("EnableDebugToolsDesc", "Enables debug tools for new sessions opened from this window."),
FSlateIcon(FInsightsCoreStyle::GetStyleSetName(), "Icons.Debug"),
ToogleDebugToolsAction,
NAME_None,
EUserInterfaceActionType::ToggleButton);
}
#if !UE_BUILD_SHIPPING
// Open Starship Test Suite
{
FUIAction OpenStarshipSuiteAction;
OpenStarshipSuiteAction.ExecuteAction = FExecuteAction::CreateLambda([this]()
{
::RestoreStarshipSuite();
});
MenuBuilder.AddMenuEntry(
LOCTEXT("OpenStarshipSuite", "Starship Test Suite"),
LOCTEXT("OpenStarshipSuiteDesc", "Opens the Starship UX test suite."),
FSlateIcon(FInsightsCoreStyle::GetStyleSetName(), "Icons.Test"),
OpenStarshipSuiteAction,
NAME_None,
EUserInterfaceActionType::Button);
}
#endif // !UE_BUILD_SHIPPING
MenuBuilder.EndSection();
return MenuBuilder.MakeWidget();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
TSharedRef<SWidget> STraceStoreWindow::MakePlatformColumnHeaderMenu()
{
FSlateApplication::Get().CloseToolTip();
FMenuBuilder MenuBuilder(/*bInShouldCloseWindowAfterMenuSelection=*/true, nullptr);
MenuBuilder.BeginSection("Filter", LOCTEXT("ColumnHeaderMenuSection_PlatformFilter", "Platform Filter"));
BuildPlatformFilterSubMenu(MenuBuilder);
MenuBuilder.EndSection();
return MenuBuilder.MakeWidget();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
TSharedRef<SWidget> STraceStoreWindow::MakePlatformFilterMenu()
{
FSlateApplication::Get().CloseToolTip();
FMenuBuilder MenuBuilder(/*bInShouldCloseWindowAfterMenuSelection=*/true, nullptr);
MenuBuilder.BeginSection("Filter", LOCTEXT("MenuSection_PlatformFilter", "Platform Filter"));
BuildPlatformFilterSubMenu(MenuBuilder);
MenuBuilder.EndSection();
return MenuBuilder.MakeWidget();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::BuildPlatformFilterSubMenu(FMenuBuilder& InMenuBuilder)
{
FilterByPlatform->BuildMenu(InMenuBuilder, *this);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
TSharedRef<SWidget> STraceStoreWindow::MakeAppNameColumnHeaderMenu()
{
FSlateApplication::Get().CloseToolTip();
FMenuBuilder MenuBuilder(/*bInShouldCloseWindowAfterMenuSelection=*/true, nullptr);
MenuBuilder.BeginSection("Filter", LOCTEXT("ColumnHeaderMenuSection_AppNameFilter", "App Name Filter"));
BuildAppNameFilterSubMenu(MenuBuilder);
MenuBuilder.EndSection();
return MenuBuilder.MakeWidget();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
TSharedRef<SWidget> STraceStoreWindow::MakeAppNameFilterMenu()
{
FSlateApplication::Get().CloseToolTip();
FMenuBuilder MenuBuilder(/*bInShouldCloseWindowAfterMenuSelection=*/true, nullptr);
MenuBuilder.BeginSection("Filter", LOCTEXT("MenuSection_AppNameFilter", "App Name Filter"));
BuildAppNameFilterSubMenu(MenuBuilder);
MenuBuilder.EndSection();
return MenuBuilder.MakeWidget();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::BuildAppNameFilterSubMenu(FMenuBuilder& InMenuBuilder)
{
FilterByAppName->BuildMenu(InMenuBuilder, *this);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
TSharedRef<SWidget> STraceStoreWindow::MakeBuildConfigColumnHeaderMenu()
{
FSlateApplication::Get().CloseToolTip();
FMenuBuilder MenuBuilder(/*bInShouldCloseWindowAfterMenuSelection=*/true, nullptr);
MenuBuilder.BeginSection("Filter", LOCTEXT("ColumnHeaderMenuSection_BuildConfigFilter", "Build Config Filter"));
BuildBuildConfigFilterSubMenu(MenuBuilder);
MenuBuilder.EndSection();
return MenuBuilder.MakeWidget();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
TSharedRef<SWidget> STraceStoreWindow::MakeBuildConfigFilterMenu()
{
FSlateApplication::Get().CloseToolTip();
FMenuBuilder MenuBuilder(/*bInShouldCloseWindowAfterMenuSelection=*/true, nullptr);
MenuBuilder.BeginSection("Filter", LOCTEXT("MenuSection_BuildConfigFilter", "Build Config Filter"));
BuildBuildConfigFilterSubMenu(MenuBuilder);
MenuBuilder.EndSection();
return MenuBuilder.MakeWidget();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::BuildBuildConfigFilterSubMenu(FMenuBuilder& InMenuBuilder)
{
FilterByBuildConfig->BuildMenu(InMenuBuilder, *this);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
TSharedRef<SWidget> STraceStoreWindow::MakeBuildTargetColumnHeaderMenu()
{
FSlateApplication::Get().CloseToolTip();
FMenuBuilder MenuBuilder(/*bInShouldCloseWindowAfterMenuSelection=*/true, nullptr);
MenuBuilder.BeginSection("Filter", LOCTEXT("ColumnHeaderMenuSection_BuildTargetFilter", "Build Target Filter"));
BuildBuildTargetFilterSubMenu(MenuBuilder);
MenuBuilder.EndSection();
return MenuBuilder.MakeWidget();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
TSharedRef<SWidget> STraceStoreWindow::MakeBuildTargetFilterMenu()
{
FSlateApplication::Get().CloseToolTip();
FMenuBuilder MenuBuilder(/*bInShouldCloseWindowAfterMenuSelection=*/true, nullptr);
MenuBuilder.BeginSection("Filter", LOCTEXT("MenuSection_BuildTargetFilter", "Build Target Filter"));
BuildBuildTargetFilterSubMenu(MenuBuilder);
MenuBuilder.EndSection();
return MenuBuilder.MakeWidget();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::BuildBuildTargetFilterSubMenu(FMenuBuilder& InMenuBuilder)
{
FilterByBuildTarget->BuildMenu(InMenuBuilder, *this);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
TSharedRef<SWidget> STraceStoreWindow::MakeBranchColumnHeaderMenu()
{
FSlateApplication::Get().CloseToolTip();
FMenuBuilder MenuBuilder(/*bInShouldCloseWindowAfterMenuSelection=*/true, nullptr);
MenuBuilder.BeginSection("Filter", LOCTEXT("ColumnHeaderMenuSection_BranchFilter", "Branch Filter"));
BuildBranchFilterSubMenu(MenuBuilder);
MenuBuilder.EndSection();
return MenuBuilder.MakeWidget();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
TSharedRef<SWidget> STraceStoreWindow::MakeBranchFilterMenu()
{
FSlateApplication::Get().CloseToolTip();
FMenuBuilder MenuBuilder(/*bInShouldCloseWindowAfterMenuSelection=*/true, nullptr);
MenuBuilder.BeginSection("Filter", LOCTEXT("MenuSection_BranchFilter", "Branch Filter"));
BuildBranchFilterSubMenu(MenuBuilder);
MenuBuilder.EndSection();
return MenuBuilder.MakeWidget();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::BuildBranchFilterSubMenu(FMenuBuilder& InMenuBuilder)
{
FilterByBranch->BuildMenu(InMenuBuilder, *this);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
TSharedRef<SWidget> STraceStoreWindow::MakeVersionColumnHeaderMenu()
{
FSlateApplication::Get().CloseToolTip();
FMenuBuilder MenuBuilder(/*bInShouldCloseWindowAfterMenuSelection=*/true, nullptr);
MenuBuilder.BeginSection("Filter", LOCTEXT("ColumnHeaderMenuSection_VersionFilter", "Version Filter"));
BuildVersionFilterSubMenu(MenuBuilder);
MenuBuilder.EndSection();
return MenuBuilder.MakeWidget();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
TSharedRef<SWidget> STraceStoreWindow::MakeVersionFilterMenu()
{
FSlateApplication::Get().CloseToolTip();
FMenuBuilder MenuBuilder(/*bInShouldCloseWindowAfterMenuSelection=*/true, nullptr);
MenuBuilder.BeginSection("Filter", LOCTEXT("MenuSection_VersionFilter", "Version Filter"));
BuildVersionFilterSubMenu(MenuBuilder);
MenuBuilder.EndSection();
return MenuBuilder.MakeWidget();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::BuildVersionFilterSubMenu(FMenuBuilder& InMenuBuilder)
{
FilterByVersion->BuildMenu(InMenuBuilder, *this);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
TSharedRef<SWidget> STraceStoreWindow::MakeSizeColumnHeaderMenu()
{
FSlateApplication::Get().CloseToolTip();
FMenuBuilder MenuBuilder(/*bInShouldCloseWindowAfterMenuSelection=*/true, nullptr);
MenuBuilder.BeginSection("Filter", LOCTEXT("ColumnHeaderMenuSection_SizeFilter", "Size Filter"));
BuildSizeFilterSubMenu(MenuBuilder);
MenuBuilder.EndSection();
return MenuBuilder.MakeWidget();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
TSharedRef<SWidget> STraceStoreWindow::MakeSizeFilterMenu()
{
FSlateApplication::Get().CloseToolTip();
FMenuBuilder MenuBuilder(/*bInShouldCloseWindowAfterMenuSelection=*/true, nullptr);
MenuBuilder.BeginSection("Filter", LOCTEXT("MenuSection_SizeFilter", "Size Filter"));
BuildSizeFilterSubMenu(MenuBuilder);
MenuBuilder.EndSection();
return MenuBuilder.MakeWidget();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::BuildSizeFilterSubMenu(FMenuBuilder& InMenuBuilder)
{
FilterBySize->BuildMenu(InMenuBuilder, *this);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
TSharedRef<SWidget> STraceStoreWindow::MakeStatusColumnHeaderMenu()
{
FSlateApplication::Get().CloseToolTip();
FMenuBuilder MenuBuilder(/*bInShouldCloseWindowAfterMenuSelection=*/true, nullptr);
MenuBuilder.BeginSection("Filter", LOCTEXT("ColumnHeaderMenuSection_StatusFilter", "Status Filter"));
BuildStatusFilterSubMenu(MenuBuilder);
MenuBuilder.EndSection();
return MenuBuilder.MakeWidget();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
TSharedRef<SWidget> STraceStoreWindow::MakeStatusFilterMenu()
{
FSlateApplication::Get().CloseToolTip();
FMenuBuilder MenuBuilder(/*bInShouldCloseWindowAfterMenuSelection=*/true, nullptr);
MenuBuilder.BeginSection("Filter", LOCTEXT("MenuSection_StatusFilter", "Status Filter"));
BuildStatusFilterSubMenu(MenuBuilder);
MenuBuilder.EndSection();
return MenuBuilder.MakeWidget();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::BuildStatusFilterSubMenu(FMenuBuilder& InMenuBuilder)
{
FilterByStatus->BuildMenu(InMenuBuilder, *this);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
FText STraceStoreWindow::GetTraceStoreDirectory() const
{
return FText::FromString(FPaths::ConvertRelativePathToFull(GetStoreDirectory()));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
FReply STraceStoreWindow::ExploreTraceStoreDirectory_OnClicked()
{
FString FullPath(FPaths::ConvertRelativePathToFull(GetStoreDirectory()));
FPlatformProcess::ExploreFolder(*FullPath);
return FReply::Handled();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
bool STraceStoreWindow::CanChangeStoreSettings() const
{
return TraceStoreConnection->CanChangeStoreSettings();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
TSharedRef<ITableRow> STraceStoreWindow::TraceDirs_OnGenerateRow(TSharedPtr<FTraceDirectoryModel> Item, const TSharedRef<STableViewBase>& Owner)
{
return SNew(STableRow<TSharedPtr<FTraceDirectoryModel>>, Owner)
.Content()
[
SNew(STraceDirectoryItem, Item, this)
];
}
////////////////////////////////////////////////////////////////////////////////////////////////////
FReply STraceStoreWindow::StoreSettingsArea_Toggle() const
{
if (StoreSettingsArea)
{
if (StoreSettingsArea->GetVisibility() == EVisibility::Visible)
{
StoreSettingsArea->SetVisibility(EVisibility::Collapsed);
}
else
{
StoreSettingsArea->SetVisibility(EVisibility::Visible);
}
}
return FReply::Handled();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
const FSlateBrush* STraceStoreWindow::StoreSettingsToggle_Icon() const
{
return (StoreSettingsArea && StoreSettingsArea->GetVisibility() == EVisibility::Visible) ?
FInsightsFrontendStyle::Get().GetBrush("Icons.Expanded") :
FInsightsFrontendStyle::Get().GetBrush("Icons.Expand");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
FReply STraceStoreWindow::AddWatchDir_Clicked()
{
FSlateApplication::Get().CloseToolTip();
if (IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get())
{
const FString Title = LOCTEXT("AddWatchDirectory_DialogTitle", "Add Monitored Directory").ToString();
const FString& CurrentStoreDirectory = StoreDirectoryModel.IsEmpty() ? FString() : StoreDirectoryModel.Last()->Path;
FString SelectedDirectory;
const bool bHasSelected = DesktopPlatform->OpenDirectoryDialog(
FSlateApplication::Get().FindBestParentWindowHandleForDialogs(AsShared()),
Title,
CurrentStoreDirectory,
SelectedDirectory);
if (bHasSelected && !FPathViews::Equals(SelectedDirectory, CurrentStoreDirectory))
{
FPaths::MakePlatformFilename(SelectedDirectory);
UE_LOG(LogInsightsFrontend, Log, TEXT("[TraceStore] Adding monitored directory: \"%s\"..."), *SelectedDirectory);
UE::Trace::FStoreClient* StoreClient = TraceStoreConnection->GetStoreClient();
if (!StoreClient ||
!StoreClient->SetStoreDirectories(nullptr, { (*SelectedDirectory) }, {}))
{
FMessageLog(LogListingName).Error(LOCTEXT("StoreCommunicationFail", "Failed to change settings on the store service."));
}
}
}
return FReply::Handled();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
FString STraceStoreWindow::GetStoreDirectory() const
{
return StoreDirectoryModel.IsEmpty() ? FString() : StoreDirectoryModel.Last()->Path;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::OpenSettings()
{
#if 0
MainContentPanel->SetEnabled(false);
(*OverlaySettingsSlot)
[
SNew(SBorder)
.BorderImage(FAppStyle::Get().GetBrush("PopupText.Background"))
.Padding(8.0f)
[
SNew(SInsightsSettings)
.OnClose(this, &STraceStoreWindow::CloseSettings)
.SettingPtr(&STraceStoreWindow::GetSettings())
]
];
#endif
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::CloseSettings()
{
// Close the profiler settings by simply replacing widget with a null one.
(*OverlaySettingsSlot)
[
SNullWidget::NullWidget
];
MainContentPanel->SetEnabled(true);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::GetExtraCommandLineParams(FString& OutParams) const
{
if (bEnableAutomaticTesting)
{
OutParams.Append(TEXT(" -InsightsTest"));
}
if (bEnableDebugTools)
{
OutParams.Append(TEXT(" -DebugTools"));
}
if (bStartProcessWithStompMalloc)
{
OutParams.Append(TEXT(" -stompmalloc"));
}
if (bDisableFramerateThrottle)
{
OutParams.Append(TEXT(" -DisableFramerateThrottle"));
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::FilterByNameSearchBox_OnTextChanged(const FText& InFilterText)
{
FilterByName->SetRawFilterText(InFilterText);
FilterByNameSearchBox->SetError(FilterByName->GetFilterErrorText());
OnFilterChanged();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::OnFilterChanged()
{
UpdateFiltering();
UpdateSorting();
UpdateTraceListView();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
const TArray<TSharedPtr<FTraceViewModel>>& STraceStoreWindow::GetAllAvailableTraces() const
{
return TraceViewModels;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::CreateFilters()
{
Filters = MakeShared<FTraceViewModelFilterCollection>();
FilterByName = MakeShared<FTraceTextFilter>(FTraceTextFilter::FItemToStringArray::CreateSP(this, &STraceStoreWindow::HandleItemToStringArray));
Filters->Add(FilterByName);
FilterByPlatform = MakeShared<FTraceFilterByPlatform>();
Filters->Add(FilterByPlatform);
FilterByAppName = MakeShared<FTraceFilterByAppName>();
Filters->Add(FilterByAppName);
FilterByBuildConfig = MakeShared<FTraceFilterByBuildConfig>();
Filters->Add(FilterByBuildConfig);
FilterByBuildTarget = MakeShared<FTraceFilterByBuildTarget>();
Filters->Add(FilterByBuildTarget);
FilterByBranch = MakeShared<FTraceFilterByBranch>();
Filters->Add(FilterByBranch);
FilterByVersion = MakeShared<FTraceFilterByVersion>();
Filters->Add(FilterByVersion);
FilterBySize = MakeShared<FTraceFilterBySize>();
Filters->Add(FilterBySize);
FilterByStatus = MakeShared<FTraceFilterByStatus>();
Filters->Add(FilterByStatus);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::HandleItemToStringArray(const FTraceViewModel& InTrace, TArray<FString>& OutSearchStrings) const
{
if (bSearchByCommandLine)
{
OutSearchStrings.Add(InTrace.CommandLine.ToString());
}
else
{
OutSearchStrings.Add(InTrace.Name.ToString());
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::UpdateFiltering()
{
FilteredTraceViewModels.Reset();
if (FilterByName->GetRawFilterText().IsEmpty() &&
FilterByPlatform->IsEmpty() &&
FilterByAppName->IsEmpty() &&
FilterByBuildConfig->IsEmpty() &&
FilterByBuildTarget->IsEmpty() &&
FilterByBranch->IsEmpty() &&
FilterByVersion->IsEmpty() &&
FilterBySize->IsEmpty() &&
FilterByStatus->IsEmpty())
{
// No filtering.
FilteredTraceViewModels = TraceViewModels;
}
else
{
for (const TSharedPtr<FTraceViewModel>& Trace : TraceViewModels)
{
const bool bIsTraceVisible = Filters->PassesAllFilters(*Trace.Get());
if (bIsTraceVisible)
{
FilteredTraceViewModels.Add(Trace);
}
}
}
UpdateFilterStatsText();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::UpdateFilterStatsText()
{
bFilterStatsTextIsDirty = false;
uint64 FilterdTotalSize = 0;
for (const TSharedPtr<FTraceViewModel>& Trace : FilteredTraceViewModels)
{
FilterdTotalSize += Trace->Size;
if (Trace->bIsLive)
{
bFilterStatsTextIsDirty = true;
}
}
// When having live sessions, but too many traces, do not further update the stats text on Tick().
if (FilteredTraceViewModels.Num() > 1000)
{
bFilterStatsTextIsDirty = false;
}
FNumberFormattingOptions FormattingOptionsSize;
FormattingOptionsSize.MaximumFractionalDigits = 1;
if (FilteredTraceViewModels.Num() == TraceViewModels.Num())
{
FilterStatsText = FText::Format(LOCTEXT("FilterStatsText_Fmt1", "{0} trace sessions ({1})"),
FText::AsNumber(TraceViewModels.Num()),
FText::AsMemory(FilterdTotalSize, &FormattingOptionsSize));
}
else
{
FilterStatsText = FText::Format(LOCTEXT("FilterStatsText_Fmt2", "{0} / {1} trace sessions ({2})"),
FText::AsNumber(FilteredTraceViewModels.Num()),
FText::AsNumber(TraceViewModels.Num()),
FText::AsMemory(FilterdTotalSize, &FormattingOptionsSize));
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
EColumnSortMode::Type STraceStoreWindow::GetSortModeForColumn(const FName ColumnId) const
{
return ColumnId == SortColumn ? SortMode : EColumnSortMode::None;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::OnSortModeChanged(const EColumnSortPriority::Type SortPriority, const FName& ColumnId, const EColumnSortMode::Type InSortMode)
{
SortColumn = ColumnId;
SortMode = InSortMode;
UpdateSorting();
UpdateTraceListView();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::UpdateSorting()
{
if (SortColumn == FTraceListColumns::Date)
{
if (SortMode == EColumnSortMode::Ascending)
{
FilteredTraceViewModels.Sort([](const TSharedPtr<FTraceViewModel>& A, const TSharedPtr<FTraceViewModel>& B)
{ return A->Timestamp < B->Timestamp; });
}
else
{
FilteredTraceViewModels.Sort([](const TSharedPtr<FTraceViewModel>& A, const TSharedPtr<FTraceViewModel>& B)
{ return A->Timestamp > B->Timestamp; });
}
}
else if (SortColumn == FTraceListColumns::Name)
{
if (SortMode == EColumnSortMode::Ascending)
{
FilteredTraceViewModels.Sort([](const TSharedPtr<FTraceViewModel>& A, const TSharedPtr<FTraceViewModel>& B)
{ return A->Name.CompareTo(B->Name) < 0; });
}
else
{
FilteredTraceViewModels.Sort([](const TSharedPtr<FTraceViewModel>& A, const TSharedPtr<FTraceViewModel>& B)
{ return B->Name.CompareTo(A->Name) < 0; });
}
}
else if (SortColumn == FTraceListColumns::Uri)
{
if (SortMode == EColumnSortMode::Ascending)
{
FilteredTraceViewModels.Sort([](const TSharedPtr<FTraceViewModel>& A, const TSharedPtr<FTraceViewModel>& B)
{ return A->Uri.CompareTo(B->Uri) < 0; });
}
else
{
FilteredTraceViewModels.Sort([](const TSharedPtr<FTraceViewModel>& A, const TSharedPtr<FTraceViewModel>& B)
{ return B->Uri.CompareTo(A->Uri) < 0; });
}
}
else if (SortColumn == FTraceListColumns::Platform)
{
if (SortMode == EColumnSortMode::Ascending)
{
FilteredTraceViewModels.Sort([](const TSharedPtr<FTraceViewModel>& A, const TSharedPtr<FTraceViewModel>& B)
{
int32 CompareResult = A->Platform.CompareTo(B->Platform);
return CompareResult == 0 ? A->Timestamp < B->Timestamp : CompareResult < 0;
});
}
else
{
FilteredTraceViewModels.Sort([](const TSharedPtr<FTraceViewModel>& A, const TSharedPtr<FTraceViewModel>& B)
{
int32 CompareResult = B->Platform.CompareTo(A->Platform);
return CompareResult == 0 ? A->Timestamp < B->Timestamp : CompareResult < 0;
});
}
}
else if (SortColumn == FTraceListColumns::AppName)
{
if (SortMode == EColumnSortMode::Ascending)
{
FilteredTraceViewModels.Sort([](const TSharedPtr<FTraceViewModel>& A, const TSharedPtr<FTraceViewModel>& B)
{
int32 CompareResult = A->AppName.CompareTo(B->AppName);
return CompareResult == 0 ? A->Timestamp < B->Timestamp : CompareResult < 0;
});
}
else
{
FilteredTraceViewModels.Sort([](const TSharedPtr<FTraceViewModel>& A, const TSharedPtr<FTraceViewModel>& B)
{
int32 CompareResult = B->AppName.CompareTo(A->AppName);
return CompareResult == 0 ? A->Timestamp < B->Timestamp : CompareResult < 0;
});
}
}
else if (SortColumn == FTraceListColumns::BuildConfig)
{
if (SortMode == EColumnSortMode::Ascending)
{
FilteredTraceViewModels.Sort([](const TSharedPtr<FTraceViewModel>& A, const TSharedPtr<FTraceViewModel>& B)
{
return A->ConfigurationType == B->ConfigurationType ?
A->Timestamp < B->Timestamp :
A->ConfigurationType < B->ConfigurationType;
});
}
else
{
FilteredTraceViewModels.Sort([](const TSharedPtr<FTraceViewModel>& A, const TSharedPtr<FTraceViewModel>& B)
{
return A->ConfigurationType == B->ConfigurationType ?
A->Timestamp < B->Timestamp :
A->ConfigurationType > B->ConfigurationType;
});
}
}
else if (SortColumn == FTraceListColumns::BuildTarget)
{
if (SortMode == EColumnSortMode::Ascending)
{
FilteredTraceViewModels.Sort([](const TSharedPtr<FTraceViewModel>& A, const TSharedPtr<FTraceViewModel>& B)
{
return A->TargetType == B->TargetType ?
A->Timestamp < B->Timestamp :
A->TargetType < B->TargetType;
});
}
else
{
FilteredTraceViewModels.Sort([](const TSharedPtr<FTraceViewModel>& A, const TSharedPtr<FTraceViewModel>& B)
{
return A->TargetType == B->TargetType ?
A->Timestamp < B->Timestamp :
A->TargetType > B->TargetType;
});
}
}
else if (SortColumn == FTraceListColumns::BuildBranch)
{
if (SortMode == EColumnSortMode::Ascending)
{
FilteredTraceViewModels.Sort([](const TSharedPtr<FTraceViewModel>& A, const TSharedPtr<FTraceViewModel>& B)
{ return A->Branch.CompareTo(B->Branch) < 0; });
}
else
{
FilteredTraceViewModels.Sort([](const TSharedPtr<FTraceViewModel>& A, const TSharedPtr<FTraceViewModel>& B)
{ return B->Branch.CompareTo(A->Branch) < 0; });
}
}
else if (SortColumn == FTraceListColumns::BuildVersion)
{
if (SortMode == EColumnSortMode::Ascending)
{
FilteredTraceViewModels.Sort([](const TSharedPtr<FTraceViewModel>& A, const TSharedPtr<FTraceViewModel>& B)
{ return A->BuildVersion.CompareTo(B->BuildVersion) < 0; });
}
else
{
FilteredTraceViewModels.Sort([](const TSharedPtr<FTraceViewModel>& A, const TSharedPtr<FTraceViewModel>& B)
{ return B->BuildVersion.CompareTo(A->BuildVersion) < 0; });
}
}
else if (SortColumn == FTraceListColumns::Size)
{
if (SortMode == EColumnSortMode::Ascending)
{
FilteredTraceViewModels.Sort([](const TSharedPtr<FTraceViewModel>& A, const TSharedPtr<FTraceViewModel>& B)
{ return A->Size < B->Size; });
}
else
{
FilteredTraceViewModels.Sort([](const TSharedPtr<FTraceViewModel>& A, const TSharedPtr<FTraceViewModel>& B)
{ return A->Size > B->Size; });
}
}
else if (SortColumn == FTraceListColumns::Status)
{
if (SortMode == EColumnSortMode::Ascending)
{
FilteredTraceViewModels.Sort([](const TSharedPtr<FTraceViewModel>& A, const TSharedPtr<FTraceViewModel>& B)
{
return A->bIsLive == B->bIsLive ?
A->Timestamp < B->Timestamp :
B->bIsLive;
});
}
else
{
FilteredTraceViewModels.Sort([](const TSharedPtr<FTraceViewModel>& A, const TSharedPtr<FTraceViewModel>& B)
{
return A->bIsLive == B->bIsLive ?
A->Timestamp < B->Timestamp :
A->bIsLive;
});
}
}
else
{
Algo::SortBy(FilteredTraceViewModels, &FTraceViewModel::Timestamp);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::ShowSuccessMessage(FText& InMessage)
{
FNotificationInfo NotificationInfo(InMessage);
NotificationInfo.bFireAndForget = false;
NotificationInfo.bUseLargeFont = false;
NotificationInfo.bUseSuccessFailIcons = true;
NotificationInfo.ExpireDuration = 3.0f;
TSharedRef<SNotificationItem> NotificationItem = NotificationList->AddNotification(NotificationInfo);
NotificationItem->SetCompletionState(SNotificationItem::CS_Success);
NotificationItem->ExpireAndFadeout();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::ShowFailMessage(FText& InMessage)
{
FNotificationInfo NotificationInfo(InMessage);
NotificationInfo.bFireAndForget = false;
NotificationInfo.bUseLargeFont = false;
NotificationInfo.bUseSuccessFailIcons = true;
NotificationInfo.ExpireDuration = 3.0f;
TSharedRef<SNotificationItem> NotificationItem = NotificationList->AddNotification(NotificationInfo);
NotificationItem->SetCompletionState(SNotificationItem::CS_Fail);
NotificationItem->ExpireAndFadeout();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
FInsightsFrontendSettings& STraceStoreWindow::GetSettings()
{
FTraceInsightsFrontendModule& Module = FModuleManager::Get().LoadModuleChecked<FTraceInsightsFrontendModule>("TraceInsightsFrontend");
return Module.GetSettings();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
const FInsightsFrontendSettings& STraceStoreWindow::GetSettings() const
{
FTraceInsightsFrontendModule& Module = FModuleManager::Get().LoadModuleChecked<FTraceInsightsFrontendModule>("TraceInsightsFrontend");
return Module.GetSettings();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::AutoStartPlatformFilterBox_OnValueCommitted(const FText& InText, ETextCommit::Type InCommitType)
{
GetSettings().SetAndSaveAutoStartAnalysisPlatform(InText.ToString());
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void STraceStoreWindow::AutoStartAppNameFilterBox_OnValueCommitted(const FText& InText, ETextCommit::Type InCommitType)
{
GetSettings().SetAndSaveAutoStartAnalysisAppName(InText.ToString());
}
////////////////////////////////////////////////////////////////////////////////////////////////////
} // namespace UE::Insights
#undef LOCTEXT_NAMESPACE