// 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 Tab = TabManager->FindExistingLiveTab(TabId); if (Tab.IsValid()) { Tab->RequestCloseTab(); } } //////////////////////////////////////////////////////////////////////////////////////////////////// void SMajorTabWindow::AddOpenTab(const TSharedRef& DockTab) { UE_LOG(TraceInsights, Log, TEXT("[Tab] SHOW %s"), *DockTab->GetContent()->GetTypeAsString()); OpenTabs.Add(DockTab); } //////////////////////////////////////////////////////////////////////////////////////////////////// void SMajorTabWindow::RemoveOpenTab(const TSharedRef& DockTab) { UE_LOG(TraceInsights, Log, TEXT("[Tab] HIDE %s"), *DockTab->GetContent()->GetTypeAsString()); OpenTabs.Remove(DockTab); } //////////////////////////////////////////////////////////////////////////////////////////////////// void SMajorTabWindow::CloseAllOpenTabs() { WindowClosedEvent.Broadcast(); // Close all tabs. TArray> LocalOpenTabs; for (TSharedPtr& Tab : OpenTabs) { LocalOpenTabs.Add(Tab); } for (TSharedPtr& Tab : LocalOpenTabs) { Tab->RequestCloseTab(); } LocalOpenTabs.Reset(); check(OpenTabs.IsEmpty()); } //////////////////////////////////////////////////////////////////////////////////////////////////// TSharedRef 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& ConstructUnderMajorTab, const TSharedPtr& ConstructUnderWindow) { using namespace UE::Insights; ////////////////////////////////////////////////// // Create & initialize tab manager. TabManager = FGlobalTabmanager::Get()->NewTabManager(ConstructUnderMajorTab); const auto& PersistLayout = [](const TSharedRef& LayoutToSave) { FLayoutSaveRestore::SaveToConfig(FTraceInsightsModule::GetUnrealInsightsLayoutIni(), LayoutToSave); }; TabManager->SetOnPersistLayout(FTabManager::FOnPersistLayout::CreateLambda(PersistLayout)); ////////////////////////////////////////////////// TSharedRef MenuGroup = CreateWorkspaceMenuGroup(); WorkspaceMenuGroup = MenuGroup; Extension = MakeShared(TabManager, MenuGroup); RegisterTabSpawners(); ////////////////////////////////////////////////// FTraceInsightsModule& TraceInsightsModule = FModuleManager::GetModuleChecked("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 Layout = [&TabConfig, this]() -> TSharedRef { 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(), Extension->GetMenuExtender()); MenuBarBuilder.AddPullDownMenu( LOCTEXT("MenuLabel", "Menu"), FText::GetEmpty(), FNewMenuDelegate::CreateStatic(&SMajorTabWindow::FillMenu, TabManager), FName(TEXT("Menu")) ); #if !WITH_EDITOR TSharedRef 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 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 SMajorTabWindow::CreateToolbar(TSharedPtr Extender) { return SNew(SBox); } //////////////////////////////////////////////////////////////////////////////////////////////////// void SMajorTabWindow::FillMenu(FMenuBuilder& MenuBuilder, const TSharedPtr 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