Files
2025-05-18 13:04:45 +08:00

440 lines
13 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Insights/Widgets/SMajorTabWindow.h"
#include "Framework/Commands/UICommandList.h"
#include "Framework/Docking/LayoutExtender.h"
#include "Framework/Docking/LayoutService.h"
#include "Framework/Docking/WorkspaceItem.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "Modules/ModuleManager.h"
#include "SlateOptMacros.h"
#include "Styling/AppStyle.h"
#include "Widgets/Docking/SDockTab.h"
#include "Widgets/Layout/SBorder.h"
#include "Widgets/Layout/SBox.h"
#include "Widgets/SBoxPanel.h"
#include "Widgets/Text/STextBlock.h"
#if WITH_EDITOR
#include "EngineAnalytics.h"
#include "AnalyticsEventAttribute.h"
#include "Interfaces/IAnalyticsProvider.h"
#endif // WITH_EDITOR
// TraceInsightsCore
#include "InsightsCore/Version.h"
// TraceInsights
#include "Insights/Common/InsightsMenuBuilder.h"
#include "Insights/InsightsManager.h"
#include "Insights/InsightsStyle.h"
#include "Insights/Log.h"
#include "Insights/TraceInsightsModule.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
#define LOCTEXT_NAMESPACE "SMajorTabWindow"
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Insights
{
////////////////////////////////////////////////////////////////////////////////////////////////////
// SMajorTabWindow
////////////////////////////////////////////////////////////////////////////////////////////////////
SMajorTabWindow::SMajorTabWindow(const FName& InMajorTabId)
: MajorTabId(InMajorTabId)
, DurationActive(0.0f)
{
}
////////////////////////////////////////////////////////////////////////////////////////////////////
SMajorTabWindow::~SMajorTabWindow()
{
CloseAllOpenTabs();
#if WITH_EDITOR
if (DurationActive > 0.0f && FEngineAnalytics::IsAvailable())
{
FString EventName = GetAnalyticsEventName();
FEngineAnalytics::GetProvider().RecordEvent(EventName, FAnalyticsEventAttribute(TEXT("Duration"), DurationActive));
}
#endif // WITH_EDITOR
}
////////////////////////////////////////////////////////////////////////////////////////////////////
const TCHAR* SMajorTabWindow::GetAnalyticsEventName() const
{
return TEXT("Insights.Usage.MajorTabWindow");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void SMajorTabWindow::Reset()
{
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void SMajorTabWindow::ShowTab(const FName& TabId)
{
if (TabManager->HasTabSpawner(TabId))
{
TabManager->TryInvokeTab(TabId);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void SMajorTabWindow::HideTab(const FName& TabId)
{
TSharedPtr<SDockTab> Tab = TabManager->FindExistingLiveTab(TabId);
if (Tab.IsValid())
{
Tab->RequestCloseTab();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void SMajorTabWindow::AddOpenTab(const TSharedRef<SDockTab>& DockTab)
{
UE_LOG(TraceInsights, Log, TEXT("[Tab] SHOW %s"), *DockTab->GetContent()->GetTypeAsString());
OpenTabs.Add(DockTab);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void SMajorTabWindow::RemoveOpenTab(const TSharedRef<SDockTab>& DockTab)
{
UE_LOG(TraceInsights, Log, TEXT("[Tab] HIDE %s"), *DockTab->GetContent()->GetTypeAsString());
OpenTabs.Remove(DockTab);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void SMajorTabWindow::CloseAllOpenTabs()
{
WindowClosedEvent.Broadcast();
// Close all tabs.
TArray<TSharedPtr<SDockTab>> LocalOpenTabs;
for (TSharedPtr<SDockTab>& Tab : OpenTabs)
{
LocalOpenTabs.Add(Tab);
}
for (TSharedPtr<SDockTab>& Tab : LocalOpenTabs)
{
Tab->RequestCloseTab();
}
LocalOpenTabs.Reset();
check(OpenTabs.IsEmpty());
}
////////////////////////////////////////////////////////////////////////////////////////////////////
TSharedRef<FWorkspaceItem> SMajorTabWindow::CreateWorkspaceMenuGroup()
{
check(TabManager.IsValid());
return TabManager->AddLocalWorkspaceMenuCategory(LOCTEXT("MajorTabMenuGroupName", "Insights"));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void SMajorTabWindow::RegisterTabSpawners()
{
}
////////////////////////////////////////////////////////////////////////////////////////////////////
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SMajorTabWindow::Construct(const FArguments& InArgs, const TSharedRef<SDockTab>& ConstructUnderMajorTab, const TSharedPtr<SWindow>& ConstructUnderWindow)
{
using namespace UE::Insights;
//////////////////////////////////////////////////
// Create & initialize tab manager.
TabManager = FGlobalTabmanager::Get()->NewTabManager(ConstructUnderMajorTab);
const auto& PersistLayout = [](const TSharedRef<FTabManager::FLayout>& LayoutToSave)
{
FLayoutSaveRestore::SaveToConfig(FTraceInsightsModule::GetUnrealInsightsLayoutIni(), LayoutToSave);
};
TabManager->SetOnPersistLayout(FTabManager::FOnPersistLayout::CreateLambda(PersistLayout));
//////////////////////////////////////////////////
TSharedRef<FWorkspaceItem> MenuGroup = CreateWorkspaceMenuGroup();
WorkspaceMenuGroup = MenuGroup;
Extension = MakeShared<FInsightsMajorTabExtender>(TabManager, MenuGroup);
RegisterTabSpawners();
//////////////////////////////////////////////////
FTraceInsightsModule& TraceInsightsModule = FModuleManager::GetModuleChecked<FTraceInsightsModule>("TraceInsights");
const FOnRegisterMajorTabExtensions* ExtensionDelegate = TraceInsightsModule.FindMajorTabLayoutExtension(GetMajorTabId());
if (ExtensionDelegate)
{
ExtensionDelegate->Broadcast(*Extension);
}
// Register any new minor tabs.
for (const FMinorTabConfig& MinorTabConfig : Extension->GetMinorTabs())
{
FTabSpawnerEntry& TabSpawnerEntry = TabManager->RegisterTabSpawner(MinorTabConfig.TabId, MinorTabConfig.OnSpawnTab, MinorTabConfig.CanSpawnTab);
TabSpawnerEntry
.SetDisplayName(MinorTabConfig.TabLabel)
.SetTooltipText(MinorTabConfig.TabTooltip)
.SetIcon(MinorTabConfig.TabIcon)
.SetReuseTabMethod(MinorTabConfig.OnFindTabToReuse);
if (MinorTabConfig.WorkspaceGroup.IsValid())
{
TabSpawnerEntry.SetGroup(MinorTabConfig.WorkspaceGroup.ToSharedRef());
}
}
// Check for layout overrides.
FInsightsMajorTabConfig TabConfig = TraceInsightsModule.FindMajorTabConfig(GetMajorTabId());
// Create tab layout.
TSharedRef<FTabManager::FLayout> Layout = [&TabConfig, this]() -> TSharedRef<FTabManager::FLayout>
{
if (TabConfig.Layout.IsValid())
{
return TabConfig.Layout.ToSharedRef();
}
else
{
return CreateDefaultTabLayout();
}
}();
Layout->ProcessExtensions(Extension->GetLayoutExtender());
Layout = FLayoutSaveRestore::LoadFromConfig(FTraceInsightsModule::GetUnrealInsightsLayoutIni(), Layout);
// Create & initialize main menu.
FMenuBarBuilder MenuBarBuilder = FMenuBarBuilder(TSharedPtr<FUICommandList>(), Extension->GetMenuExtender());
MenuBarBuilder.AddPullDownMenu(
LOCTEXT("MenuLabel", "Menu"),
FText::GetEmpty(),
FNewMenuDelegate::CreateStatic(&SMajorTabWindow::FillMenu, TabManager),
FName(TEXT("Menu"))
);
#if !WITH_EDITOR
TSharedRef<SWidget> MenuWidget = MenuBarBuilder.MakeWidget();
MenuWidget->SetClipping(EWidgetClipping::ClipToBoundsWithoutIntersecting);
#endif
ChildSlot
[
SNew(SOverlay)
#if !WITH_EDITOR
// Menu
+ SOverlay::Slot()
.HAlign(HAlign_Left)
.VAlign(VAlign_Top)
.Padding(34.0f, -60.0f, 0.0f, 0.0f)
[
MenuWidget
]
#endif
// Version
+ SOverlay::Slot()
.HAlign(HAlign_Right)
.VAlign(VAlign_Top)
.Padding(0.0f, -16.0f, 4.0f, 0.0f)
[
SNew(STextBlock)
.Clipping(EWidgetClipping::ClipToBoundsWithoutIntersecting)
.Text(LOCTEXT("UnrealInsightsVersion", UNREAL_INSIGHTS_VERSION_STRING_EX))
.ColorAndOpacity(FLinearColor(0.15f, 0.15f, 0.15f, 1.0f))
]
// Overlay slot for the main window area
+ SOverlay::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(FMargin(0.0f, 0.0f, 0.0f, 5.0f))
[
CreateToolbar(Extension->GetMenuExtender())
]
+ SVerticalBox::Slot()
.FillHeight(1.0f)
[
TabManager->RestoreFrom(Layout, ConstructUnderWindow, false, EOutputCanBeNullptr::IfNoTabValid).ToSharedRef()
]
]
// Session hint overlay
+ SOverlay::Slot()
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
[
SNew(SBorder)
.Visibility_Lambda([this]() { return IsValidSession() ? EVisibility::Collapsed : EVisibility::Visible; })
.BorderImage(FAppStyle::Get().GetBrush("PopupText.Background"))
.Padding(8.0f)
[
SNew(STextBlock)
.Text(LOCTEXT("SelectTraceOverlayText", "Please select a trace."))
]
]
];
#if !WITH_EDITOR
// Tell tab-manager about the global menu bar.
TabManager->SetMenuMultiBox(MenuBarBuilder.GetMultiBox(), MenuWidget);
#endif
// Tell clients about creation.
TraceInsightsModule.OnMajorTabCreated().Broadcast(GetMajorTabId(), TabManager.ToSharedRef());
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
////////////////////////////////////////////////////////////////////////////////////////////////////
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
TSharedRef<FTabManager::FLayout> SMajorTabWindow::CreateDefaultTabLayout() const
{
return FTabManager::NewLayout("InsightsMajorTabLayout_v1.0")
->AddArea
(
FTabManager::NewPrimaryArea()
->SetOrientation(Orient_Horizontal)
->Split
(
FTabManager::NewStack()
->SetSizeCoefficient(1.0f)
->SetHideTabWell(true)
)
);
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
////////////////////////////////////////////////////////////////////////////////////////////////////
TSharedRef<SWidget> SMajorTabWindow::CreateToolbar(TSharedPtr<FExtender> Extender)
{
return SNew(SBox);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void SMajorTabWindow::FillMenu(FMenuBuilder& MenuBuilder, const TSharedPtr<FTabManager> TabManager)
{
if (!TabManager.IsValid())
{
return;
}
UE::Insights::FInsightsManager::Get()->GetInsightsMenuBuilder()->PopulateMenu(MenuBuilder);
TabManager->PopulateLocalTabSpawnerMenu(MenuBuilder);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
bool SMajorTabWindow::IsValidSession() const
{
return UE::Insights::FInsightsManager::Get()->GetSession().IsValid();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
EActiveTimerReturnType SMajorTabWindow::UpdateActiveDuration(double InCurrentTime, float InDeltaTime)
{
DurationActive += InDeltaTime;
// The window will explicitly unregister this active timer when the mouse leaves.
return EActiveTimerReturnType::Continue;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void SMajorTabWindow::OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
SCompoundWidget::OnMouseEnter(MyGeometry, MouseEvent);
if (!ActiveTimerHandle.IsValid())
{
ActiveTimerHandle = RegisterActiveTimer(0.f, FWidgetActiveTimerDelegate::CreateSP(this, &SMajorTabWindow::UpdateActiveDuration));
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void SMajorTabWindow::OnMouseLeave(const FPointerEvent& MouseEvent)
{
SCompoundWidget::OnMouseLeave(MouseEvent);
auto PinnedActiveTimerHandle = ActiveTimerHandle.Pin();
if (PinnedActiveTimerHandle.IsValid())
{
UnRegisterActiveTimer(PinnedActiveTimerHandle.ToSharedRef());
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
FReply SMajorTabWindow::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent)
{
if (CommandList.IsValid())
{
return CommandList->ProcessCommandBindings(InKeyEvent) ? FReply::Handled() : FReply::Unhandled();
}
else
{
return SCompoundWidget::OnKeyDown(MyGeometry, InKeyEvent);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
FReply SMajorTabWindow::OnDragOver(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent)
{
if (UE::Insights::FInsightsManager::Get()->OnDragOver(DragDropEvent))
{
return FReply::Handled();
}
return SCompoundWidget::OnDragOver(MyGeometry, DragDropEvent);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
FReply SMajorTabWindow::OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent)
{
if (UE::Insights::FInsightsManager::Get()->OnDrop(DragDropEvent))
{
return FReply::Handled();
}
return SCompoundWidget::OnDrop(MyGeometry, DragDropEvent);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
} // namespace Insights
#undef LOCTEXT_NAMESPACE