// 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 // for CreateEvent #include // 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(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 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 STraceStoreWindow::ConstructFiltersToolbar() { FSlimHorizontalToolBarBuilder ToolbarBuilder(TSharedPtr(), 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(), 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 STraceStoreWindow::ConstructSessionsPanel() { TSharedRef Widget = SAssignNew(TraceListView, SListView>) .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 STraceStoreWindow::ConstructLoadPanel() { TSharedRef Widget = SNew(SHorizontalBox) + SHorizontalBox::Slot() .FillWidth(1.0f) [ SNew(SBox) .HAlign(HAlign_Left) [ ConstructAutoStartPanel() ] ] + SHorizontalBox::Slot() .AutoWidth() [ SNew(SButton) .ButtonStyle(&FAppStyle::Get().GetWidgetStyle("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("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 STraceStoreWindow::ConstructTraceStoreDirectoryPanel() { TSharedRef 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("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("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>) .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>) .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("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 STraceStoreWindow::ConstructAutoStartPanel() { TSharedRef 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 STraceStoreWindow::TraceList_OnGenerateRow(TSharedPtr InTrace, const TSharedRef& 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(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 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 SelectedTrace = GetSingleSelectedTrace(); return SelectedTrace.IsValid() && SelectedTrace->TraceId != FTraceViewModel::InvalidTraceId && !SelectedTrace->bIsLive; } //////////////////////////////////////////////////////////////////////////////////////////////////// void STraceStoreWindow::RenameSelectedTrace() { FSlateApplication::Get().CloseToolTip(); if (!CanRenameSelectedTrace()) { return; } TSharedPtr SelectedTrace = GetSingleSelectedTrace(); SelectedTrace->bIsRenaming = true; TSharedPtr 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> SelectedTraces = TraceListView->GetSelectedItems(); for (const TSharedPtr& SelectedTrace : SelectedTraces) { if ((SelectedTrace->TraceId != FTraceViewModel::InvalidTraceId) && !SelectedTrace->bIsLive) { return true; } } return false; } //////////////////////////////////////////////////////////////////////////////////////////////////// void STraceStoreWindow::DeleteSelectedTraces() { FSlateApplication::Get().CloseToolTip(); if (!CanDeleteSelectedTraces()) { return; } TArray> TracesToDelete = TraceListView->GetSelectedItems(); // Filter the traces that can actually be deleted : TracesToDelete.RemoveAll([](const TSharedPtr& 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& 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 TraceToSelect = (TraceIndexToSelect >= 0) ? FilteredTraceViewModels[TraceIndexToSelect] : nullptr; // Delete traces. int32 NumDeletedTraces = 0; for (int32 TraceIndex = 0; TraceIndex < TracesToDelete.Num(); ++TraceIndex) { const TSharedPtr& 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& 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 SelectedTrace = GetSingleSelectedTrace(); return SelectedTrace.IsValid() && SelectedTrace->TraceId != FTraceViewModel::InvalidTraceId; } //////////////////////////////////////////////////////////////////////////////////////////////////// void STraceStoreWindow::CopyTraceId() { if (CanCopyTraceId()) { TSharedPtr SelectedTrace = GetSingleSelectedTrace(); FString ClipboardText = FString::Printf(TEXT("0x%X"), SelectedTrace->TraceId); FPlatformApplicationMisc::ClipboardCopy(*ClipboardText); } } //////////////////////////////////////////////////////////////////////////////////////////////////// bool STraceStoreWindow::CanCopyFullPath() const { TSharedPtr SelectedTrace = GetSingleSelectedTrace(); return SelectedTrace.IsValid() && SelectedTrace->TraceId != FTraceViewModel::InvalidTraceId; } //////////////////////////////////////////////////////////////////////////////////////////////////// void STraceStoreWindow::CopyFullPath() { if (CanCopyFullPath()) { TSharedPtr SelectedTrace = GetSingleSelectedTrace(); FPlatformApplicationMisc::ClipboardCopy(*SelectedTrace->Uri.ToString()); } } //////////////////////////////////////////////////////////////////////////////////////////////////// bool STraceStoreWindow::CanOpenContainingFolder() const { if (!CanChangeStoreSettings()) { return false; } TSharedPtr SelectedTrace = GetSingleSelectedTrace(); return SelectedTrace.IsValid() && SelectedTrace->TraceId != FTraceViewModel::InvalidTraceId; } //////////////////////////////////////////////////////////////////////////////////////////////////// void STraceStoreWindow::OpenContainingFolder() { FSlateApplication::Get().CloseToolTip(); if (CanOpenContainingFolder()) { TSharedPtr 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* Dir = WatchDirectoriesModel.FindByPredicate([&](const TSharedPtr& 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( 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( 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>& InTraces = StoreBrowser->GetTraces(); const TMap>& InTraceMap = StoreBrowser->GetTraceMap(); // Check for removed traces. { int32 TraceViewModelCount = TraceViewModels.Num(); for (int32 TraceIndex = 0; TraceIndex < TraceViewModelCount; ++TraceIndex) { FTraceViewModel& Trace = *TraceViewModels[TraceIndex]; const TSharedPtr* 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& InTracePtr : InTraces) { const FStoreBrowserTraceInfo& SourceTrace = *InTracePtr; TSharedPtr* 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 TracePtr = MakeShared(); 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> NewSelectedTraces; if (bIsUserSelectedTrace) { // Identify the previously selected traces (if still available) to ensure selection remains unchanged. TArray> SelectedTraces = TraceListView->GetSelectedItems(); for (const TSharedPtr& SelectedTrace : SelectedTraces) { TSharedPtr* FoundNewTrace = TraceViewModelMap.Find(SelectedTrace->TraceId); if (!FoundNewTrace) { FoundNewTrace = TraceViewModels.FindByPredicate([SelectedTrace](const TSharedPtr& Trace) { return Trace->Uri.EqualTo(SelectedTrace->Uri); }); } if (!FoundNewTrace) { FoundNewTrace = TraceViewModels.FindByPredicate([SelectedTrace](const TSharedPtr& 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 STraceStoreWindow::GetSingleSelectedTrace() const { return (TraceListView->GetNumItemsSelected() == 1) ? TraceListView->GetSelectedItems()[0] : nullptr; } //////////////////////////////////////////////////////////////////////////////////////////////////// void STraceStoreWindow::TraceList_OnSelectionChanged(TSharedPtr TraceSession, ESelectInfo::Type SelectInfo) { if (SelectInfo != ESelectInfo::Direct) { bIsUserSelectedTrace = true; } } //////////////////////////////////////////////////////////////////////////////////////////////////// void STraceStoreWindow::TraceList_OnMouseButtonDoubleClick(TSharedPtr 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(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 DragDropOp = DragDropEvent.GetOperationAs(); if (DragDropOp.IsValid()) { if (DragDropOp->HasFiles()) { const TArray& 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 DragDropOp = DragDropEvent.GetOperationAs(); if (DragDropOp.IsValid()) { if (DragDropOp->HasFiles()) { // For now, only allow a single file. const TArray& 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 SelectedTrace = GetSingleSelectedTrace(); return SelectedTrace.IsValid() && SelectedTrace->TraceId != FTraceViewModel::InvalidTraceId; } //////////////////////////////////////////////////////////////////////////////////////////////////// FReply STraceStoreWindow::Open_OnClicked() { TSharedPtr SelectedTrace = GetSingleSelectedTrace(); OpenTraceSession(SelectedTrace); return FReply::Handled(); } //////////////////////////////////////////////////////////////////////////////////////////////////// bool STraceStoreWindow::ShowOpenTraceFileDialog(FString& OutTraceFile) const { if (OpenTraceFileDefaultDirectory.IsEmpty()) { OpenTraceFileDefaultDirectory = FPaths::ConvertRelativePathToFull(TraceStoreConnection->GetStoreDir()); } TArray 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 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* TraceSessionPtrPtr = TraceViewModelMap.Find(InTraceId); if (TraceSessionPtrPtr) { FTraceViewModel& TraceSession = **TraceSessionPtrPtr; SplashScreenOverlayTraceFile = FPaths::GetBaseFilename(TraceSession.Uri.ToString()); } ShowSplashScreenOverlay(); } //////////////////////////////////////////////////////////////////////////////////////////////////// TSharedRef 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> 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(), // 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 STraceStoreWindow::TraceDirs_OnGenerateRow(TSharedPtr Item, const TSharedRef& Owner) { return SNew(STableRow>, 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>& STraceStoreWindow::GetAllAvailableTraces() const { return TraceViewModels; } //////////////////////////////////////////////////////////////////////////////////////////////////// void STraceStoreWindow::CreateFilters() { Filters = MakeShared(); FilterByName = MakeShared(FTraceTextFilter::FItemToStringArray::CreateSP(this, &STraceStoreWindow::HandleItemToStringArray)); Filters->Add(FilterByName); FilterByPlatform = MakeShared(); Filters->Add(FilterByPlatform); FilterByAppName = MakeShared(); Filters->Add(FilterByAppName); FilterByBuildConfig = MakeShared(); Filters->Add(FilterByBuildConfig); FilterByBuildTarget = MakeShared(); Filters->Add(FilterByBuildTarget); FilterByBranch = MakeShared(); Filters->Add(FilterByBranch); FilterByVersion = MakeShared(); Filters->Add(FilterByVersion); FilterBySize = MakeShared(); Filters->Add(FilterBySize); FilterByStatus = MakeShared(); Filters->Add(FilterByStatus); } //////////////////////////////////////////////////////////////////////////////////////////////////// void STraceStoreWindow::HandleItemToStringArray(const FTraceViewModel& InTrace, TArray& 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& 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& 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& A, const TSharedPtr& B) { return A->Timestamp < B->Timestamp; }); } else { FilteredTraceViewModels.Sort([](const TSharedPtr& A, const TSharedPtr& B) { return A->Timestamp > B->Timestamp; }); } } else if (SortColumn == FTraceListColumns::Name) { if (SortMode == EColumnSortMode::Ascending) { FilteredTraceViewModels.Sort([](const TSharedPtr& A, const TSharedPtr& B) { return A->Name.CompareTo(B->Name) < 0; }); } else { FilteredTraceViewModels.Sort([](const TSharedPtr& A, const TSharedPtr& B) { return B->Name.CompareTo(A->Name) < 0; }); } } else if (SortColumn == FTraceListColumns::Uri) { if (SortMode == EColumnSortMode::Ascending) { FilteredTraceViewModels.Sort([](const TSharedPtr& A, const TSharedPtr& B) { return A->Uri.CompareTo(B->Uri) < 0; }); } else { FilteredTraceViewModels.Sort([](const TSharedPtr& A, const TSharedPtr& B) { return B->Uri.CompareTo(A->Uri) < 0; }); } } else if (SortColumn == FTraceListColumns::Platform) { if (SortMode == EColumnSortMode::Ascending) { FilteredTraceViewModels.Sort([](const TSharedPtr& A, const TSharedPtr& B) { int32 CompareResult = A->Platform.CompareTo(B->Platform); return CompareResult == 0 ? A->Timestamp < B->Timestamp : CompareResult < 0; }); } else { FilteredTraceViewModels.Sort([](const TSharedPtr& A, const TSharedPtr& 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& A, const TSharedPtr& B) { int32 CompareResult = A->AppName.CompareTo(B->AppName); return CompareResult == 0 ? A->Timestamp < B->Timestamp : CompareResult < 0; }); } else { FilteredTraceViewModels.Sort([](const TSharedPtr& A, const TSharedPtr& 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& A, const TSharedPtr& B) { return A->ConfigurationType == B->ConfigurationType ? A->Timestamp < B->Timestamp : A->ConfigurationType < B->ConfigurationType; }); } else { FilteredTraceViewModels.Sort([](const TSharedPtr& A, const TSharedPtr& 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& A, const TSharedPtr& B) { return A->TargetType == B->TargetType ? A->Timestamp < B->Timestamp : A->TargetType < B->TargetType; }); } else { FilteredTraceViewModels.Sort([](const TSharedPtr& A, const TSharedPtr& 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& A, const TSharedPtr& B) { return A->Branch.CompareTo(B->Branch) < 0; }); } else { FilteredTraceViewModels.Sort([](const TSharedPtr& A, const TSharedPtr& B) { return B->Branch.CompareTo(A->Branch) < 0; }); } } else if (SortColumn == FTraceListColumns::BuildVersion) { if (SortMode == EColumnSortMode::Ascending) { FilteredTraceViewModels.Sort([](const TSharedPtr& A, const TSharedPtr& B) { return A->BuildVersion.CompareTo(B->BuildVersion) < 0; }); } else { FilteredTraceViewModels.Sort([](const TSharedPtr& A, const TSharedPtr& B) { return B->BuildVersion.CompareTo(A->BuildVersion) < 0; }); } } else if (SortColumn == FTraceListColumns::Size) { if (SortMode == EColumnSortMode::Ascending) { FilteredTraceViewModels.Sort([](const TSharedPtr& A, const TSharedPtr& B) { return A->Size < B->Size; }); } else { FilteredTraceViewModels.Sort([](const TSharedPtr& A, const TSharedPtr& B) { return A->Size > B->Size; }); } } else if (SortColumn == FTraceListColumns::Status) { if (SortMode == EColumnSortMode::Ascending) { FilteredTraceViewModels.Sort([](const TSharedPtr& A, const TSharedPtr& B) { return A->bIsLive == B->bIsLive ? A->Timestamp < B->Timestamp : B->bIsLive; }); } else { FilteredTraceViewModels.Sort([](const TSharedPtr& A, const TSharedPtr& 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 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 NotificationItem = NotificationList->AddNotification(NotificationInfo); NotificationItem->SetCompletionState(SNotificationItem::CS_Fail); NotificationItem->ExpireAndFadeout(); } //////////////////////////////////////////////////////////////////////////////////////////////////// FInsightsFrontendSettings& STraceStoreWindow::GetSettings() { FTraceInsightsFrontendModule& Module = FModuleManager::Get().LoadModuleChecked("TraceInsightsFrontend"); return Module.GetSettings(); } //////////////////////////////////////////////////////////////////////////////////////////////////// const FInsightsFrontendSettings& STraceStoreWindow::GetSettings() const { FTraceInsightsFrontendModule& Module = FModuleManager::Get().LoadModuleChecked("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