// Copyright Epic Games, Inc. All Rights Reserved. #include "SLevelViewportToolBar.h" #include "Framework/Commands/UIAction.h" #include "Framework/MultiBox/MultiBoxDefs.h" #include "Framework/MultiBox/MultiBoxExtender.h" #include "Framework/MultiBox/MultiBoxBuilder.h" #include "SceneView.h" #include "Subsystems/PanelExtensionSubsystem.h" #include "ToolMenus.h" #include "LevelEditorMenuContext.h" #include "ViewportToolBarContext.h" #include "Modules/ModuleManager.h" #include "Widgets/Layout/SBorder.h" #include "Widgets/Layout/SBox.h" #include "Widgets/Input/SSpinBox.h" #include "Styling/AppStyle.h" #include "Camera/CameraActor.h" #include "Misc/ConfigCacheIni.h" #include "GameFramework/ActorPrimitiveColorHandler.h" #include "GameFramework/WorldSettings.h" #include "EngineUtils.h" #include "LevelEditor.h" #include "STransformViewportToolbar.h" #include "EditorShowFlags.h" #include "LevelViewportActions.h" #include "LevelEditorViewport.h" #include "Layers/LayersSubsystem.h" #include "DeviceProfiles/DeviceProfile.h" #include "IDeviceProfileServicesModule.h" #include "EditorViewportCommands.h" #include "SEditorViewportToolBarMenu.h" #include "SEditorViewportToolBarButton.h" #include "SEditorViewportViewMenu.h" #include "Stats/StatsData.h" #include "BufferVisualizationData.h" #include "NaniteVisualizationData.h" #include "LumenVisualizationData.h" #include "SubstrateVisualizationData.h" #include "GroomVisualizationData.h" #include "VirtualShadowMapVisualizationData.h" #include "FoliageType.h" #include "ShowFlagMenuCommands.h" #include "Bookmarks/BookmarkUI.h" #include "Scalability.h" #include "SScalabilitySettings.h" #include "Editor/EditorPerformanceSettings.h" #include "SEditorViewportViewMenuContext.h" #include "Bookmarks/IBookmarkTypeTools.h" #include "ToolMenu.h" #include "WorldPartition/IWorldPartitionEditorModule.h" #include "WorldPartition/WorldPartitionSubsystem.h" #include "WorldPartition/WorldPartition.h" #include "SLevelViewport.h" #include "SortHelper.h" #include "Interfaces/IMainFrameModule.h" #include "SCommonEditorViewportToolbarBase.h" #include "Engine/SceneCapture.h" #include "ViewportToolbar/LevelEditorSubmenus.h" #include "ViewportToolbar/LevelViewportContext.h" #include "ViewportToolbar/UnrealEdViewportToolbar.h" #define LOCTEXT_NAMESPACE "LevelViewportToolBar" /** Override the view menu, just so we can specify the level viewport as active when the button is clicked */ class SLevelEditorViewportViewMenu : public SEditorViewportViewMenu { public: void Construct(const FArguments& InArgs, TSharedRef InViewport, TSharedRef InParentToolBar) { SEditorViewportViewMenu::Construct(InArgs, InViewport, InParentToolBar); MenuName = FName("LevelEditor.LevelViewportToolBar.View"); } virtual void RegisterMenus() const override { SEditorViewportViewMenu::RegisterMenus(); // Use a static bool to track whether or not this menu is registered. Bool instead of checking the registered // state with ToolMenus because we want the new viewport toolbar to be able to create this menu without breaking // this code. Static because this code can be called multiple times using different instances of this class. static bool bDidRegisterMenu = false; if (!bDidRegisterMenu) { bDidRegisterMenu = true; // Don't warn here to avoid warnings if the new viewport toolbar already has created an empty version // of this menu. UToolMenu* Menu = UToolMenus::Get()->RegisterMenu( "LevelEditor.LevelViewportToolBar.View", "UnrealEd.ViewportToolbar.View", EMultiBoxType::Menu, false ); Menu->AddDynamicSection("LevelSection", FNewToolMenuDelegate::CreateLambda([](UToolMenu* InMenu) { if (UEditorViewportViewMenuContext* Context = InMenu->FindContext()) { if (TSharedPtr Menu = Context->EditorViewportViewMenu.Pin()) { if (TSharedPtr LevelViewportToolBar = StaticCastSharedPtr(Menu->GetParentToolBar().Pin())) { LevelViewportToolBar->FillViewMenu(InMenu); } } } })); } } virtual TSharedRef GenerateViewMenuContent() const override { SLevelViewport* LevelViewport = static_cast(Viewport.Pin().Get()); LevelViewport->OnFloatingButtonClicked(); return SEditorViewportViewMenu::GenerateViewMenuContent(); } }; void SLevelViewportToolBar::Construct( const FArguments& InArgs ) { Viewport = InArgs._Viewport; TSharedRef ViewportRef = Viewport.Pin().ToSharedRef(); FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked(TEXT("LevelEditor")); UViewportToolBarContext* ExtensionContextObject = NewObject(); ExtensionContextObject->ViewportToolBar = SharedThis(this); ExtensionContextObject->Viewport = Viewport; const FMargin ToolbarSlotPadding(4.0f, 1.0f); // clang-format off ChildSlot [ SNew(SBorder) .BorderImage(FAppStyle::Get().GetBrush("EditorViewportToolBar.Background")) .Visibility(EVisibility::SelfHitTestInvisible) [ SNew( SVerticalBox ) + SVerticalBox::Slot() .AutoHeight() .Padding(ToolbarSlotPadding) [ SNew( SHorizontalBox ) + SHorizontalBox::Slot() .AutoWidth() .Padding(ToolbarSlotPadding) [ SNew( SEditorViewportToolbarMenu ) .ParentToolBar( SharedThis( this ) ) .Visibility(Viewport.Pin().Get(), &SLevelViewport::GetOptionsMenuVisibility) .Image("EditorViewportToolBar.OptionsDropdown") .AddMetaData(FTagMetaData(TEXT("EditorViewportToolBar.MenuDropdown"))) .OnGetMenuContent( this, &SLevelViewportToolBar::GenerateOptionsMenu ) ] + SHorizontalBox::Slot() [ SNew( SHorizontalBox ) .Visibility(Viewport.Pin().Get(), &SLevelViewport::GetFullToolbarVisibility) + SHorizontalBox::Slot() .AutoWidth() .Padding( ToolbarSlotPadding ) [ SNew( SEditorViewportToolbarMenu ) .ParentToolBar( SharedThis( this ) ) .Label( this, &SLevelViewportToolBar::GetCameraMenuLabel ) .LabelIcon( this, &SLevelViewportToolBar::GetCameraMenuLabelIcon ) .AddMetaData(FTagMetaData(TEXT("EditorViewportToolBar.CameraMenu"))) .OnGetMenuContent( this, &SLevelViewportToolBar::GenerateCameraMenu ) ] + SHorizontalBox::Slot() .AutoWidth() .Padding( ToolbarSlotPadding ) [ SNew( SLevelEditorViewportViewMenu, ViewportRef, SharedThis(this) ) .MenuExtenders(UE::LevelEditor::GetViewModesLegacyExtenders()) .AddMetaData(FTagMetaData(TEXT("ViewMenuButton"))) ] + SHorizontalBox::Slot() .AutoWidth() .Padding( ToolbarSlotPadding ) [ SNew( SEditorViewportToolbarMenu ) .Label( LOCTEXT("ShowMenuTitle", "Show") ) .ParentToolBar( SharedThis( this ) ) .AddMetaData(FTagMetaData(TEXT("EditorViewportToolBar.ShowMenu"))) .OnGetMenuContent( this, &SLevelViewportToolBar::GenerateShowMenu ) ] + SHorizontalBox::Slot() .AutoWidth() .Padding( ToolbarSlotPadding ) [ SNew( SEditorViewportToolbarMenu ) .Label( this, &SLevelViewportToolBar::GetViewModeOptionsMenuLabel ) .ParentToolBar( SharedThis( this ) ) .Visibility( this, &SLevelViewportToolBar::GetViewModeOptionsVisibility ) .AddMetaData(FTagMetaData(TEXT("EditorViewportToolBar.ViewModeOptions"))) .OnGetMenuContent( this, &SLevelViewportToolBar::GenerateViewModeOptionsMenu ) ] + SHorizontalBox::Slot() .AutoWidth() .Padding( ToolbarSlotPadding ) [ SNew( SEditorViewportToolbarMenu ) .ParentToolBar( SharedThis( this ) ) .Label( this, &SLevelViewportToolBar::GetDevicePreviewMenuLabel ) .LabelIcon( this, &SLevelViewportToolBar::GetDevicePreviewMenuLabelIcon ) .OnGetMenuContent( this, &SLevelViewportToolBar::GenerateDevicePreviewMenu ) //@todo rendering: mobile preview in view port is not functional yet - remove this once it is. .Visibility(EVisibility::Collapsed) ] + SHorizontalBox::Slot() .Padding(ToolbarSlotPadding) .AutoWidth() .HAlign(HAlign_Left) .VAlign(VAlign_Fill) [ SNew(SExtensionPanel) .ExtensionPanelID("LevelViewportToolBar.LeftExtension") .ExtensionContext(ExtensionContextObject) ] + SHorizontalBox::Slot() .AutoWidth() .Padding(ToolbarSlotPadding) [ // Button to show that realtime is off SNew(SEditorViewportToolBarButton) .ButtonType(EUserInterfaceActionType::Button) .ButtonStyle(&FAppStyle::Get().GetWidgetStyle("EditorViewportToolBar.WarningButton")) .OnClicked(this, &SLevelViewportToolBar::OnRealtimeWarningClicked) .Visibility(this, &SLevelViewportToolBar::GetRealtimeWarningVisibility) .ToolTipText(LOCTEXT("RealtimeOff_ToolTip", "This viewport is not updating in realtime. Click to turn on realtime mode.")) .Content() [ SNew(STextBlock) .TextStyle(&FAppStyle::Get().GetWidgetStyle("SmallText")) .Text(LOCTEXT("RealtimeOff", "Realtime Off")) ] ] + SHorizontalBox::Slot() .AutoWidth() .Padding(ToolbarSlotPadding) [ // Button to show scalability warnings SNew(SEditorViewportToolbarMenu) .ParentToolBar(SharedThis(this)) .Label_Static(&UE::UnrealEd::GetScalabilityWarningLabel) .MenuStyle(&FAppStyle::Get().GetWidgetStyle("EditorViewportToolBar.WarningButton")) .OnGetMenuContent(this, &SLevelViewportToolBar::GetScalabilityWarningMenuContent) .Visibility(this, &SLevelViewportToolBar::GetScalabilityWarningVisibility) .ToolTipText_Static(&UE::UnrealEd::GetScalabilityWarningTooltip) ] + SHorizontalBox::Slot() .Padding(ToolbarSlotPadding) .HAlign(HAlign_Center) .VAlign(VAlign_Fill) [ SNew(SExtensionPanel) .ExtensionPanelID("LevelViewportToolBar.MiddleExtension") .ExtensionContext(ExtensionContextObject) ] + SHorizontalBox::Slot() .Padding(ToolbarSlotPadding) .AutoWidth() .HAlign(HAlign_Right) .VAlign(VAlign_Fill) [ SNew(SExtensionPanel) .ExtensionPanelID("LevelViewportToolBar.RightExtension") .ExtensionContext(ExtensionContextObject) ] + SHorizontalBox::Slot() .AutoWidth() .MaxWidth(TAttribute::CreateSP(this, &SLevelViewportToolBar::GetTransformToolbarWidth)) .Padding(ToolbarSlotPadding) .HAlign(HAlign_Right) [ SAssignNew(TransformToolbar, STransformViewportToolBar) .Viewport(ViewportRef) .CommandList(ViewportRef->GetCommandList()) .Extenders(LevelEditorModule.GetToolBarExtensibilityManager()->GetAllExtenders()) .Visibility(ViewportRef, &SLevelViewport::GetTransformToolbarVisibility) ] + SHorizontalBox::Slot() .Padding(ToolbarSlotPadding) .AutoWidth() .HAlign(HAlign_Right) .VAlign(VAlign_Fill) [ SNew(SExtensionPanel) .ExtensionPanelID("LevelViewportToolBar.RightmostExtension") .ExtensionContext(ExtensionContextObject) ] + SHorizontalBox::Slot() .HAlign(HAlign_Right) .AutoWidth() .Padding(ToolbarSlotPadding) [ //The Maximize/Minimize button is only displayed when not in Immersive mode. SNew(SEditorViewportToolBarButton) .ButtonType(EUserInterfaceActionType::ToggleButton) .CheckBoxStyle(&FAppStyle::Get().GetWidgetStyle("EditorViewportToolBar.MaximizeRestoreButton")) .IsChecked(ViewportRef, &SLevelViewport::IsMaximized) .OnClicked(ViewportRef, &SLevelViewport::OnToggleMaximize) .Visibility(ViewportRef, &SLevelViewport::GetMaximizeToggleVisibility) .Image("EditorViewportToolBar.Maximize") .ToolTipText(LOCTEXT("Maximize_ToolTip", "Maximizes or restores this viewport")) ] + SHorizontalBox::Slot() .HAlign(HAlign_Right) .AutoWidth() .Padding(ToolbarSlotPadding) [ //The Restore from Immersive' button is only displayed when the editor is in Immersive mode. SNew(SEditorViewportToolBarButton) .ButtonType(EUserInterfaceActionType::Button) .OnClicked(ViewportRef, &SLevelViewport::OnToggleMaximize) .Visibility(ViewportRef, &SLevelViewport::GetCloseImmersiveButtonVisibility) .Image("EditorViewportToolBar.RestoreFromImmersive.Normal") .ToolTipText(LOCTEXT("RestoreFromImmersive_ToolTip", "Restore from Immersive")) ] ] ] ] ]; // clang-format on SViewportToolBar::Construct(SViewportToolBar::FArguments()); } bool SLevelViewportToolBar::IsViewModeSupported(EViewModeIndex ViewModeIndex) const { return true; } FLevelEditorViewportClient* SLevelViewportToolBar::GetLevelViewportClient() const { if (TSharedPtr PinnedViewport = Viewport.Pin()) { return &PinnedViewport->GetLevelViewportClient(); } return nullptr; } FText SLevelViewportToolBar::GetCameraMenuLabel() const { TSharedPtr< SLevelViewport > PinnedViewport( Viewport.Pin() ); if( PinnedViewport.IsValid() ) { return UE::UnrealEd::GetCameraSubmenuLabelFromViewportType(PinnedViewport->GetLevelViewportClient().ViewportType); } return LOCTEXT("CameraMenuTitle_Default", "Camera"); } const FSlateBrush* SLevelViewportToolBar::GetCameraMenuLabelIcon() const { TSharedPtr< SLevelViewport > PinnedViewport( Viewport.Pin() ); if( PinnedViewport.IsValid() ) { return GetCameraMenuLabelIconFromViewportType(PinnedViewport->GetLevelViewportClient().ViewportType ); } return FStyleDefaults::GetNoBrush(); } FText SLevelViewportToolBar::GetDevicePreviewMenuLabel() const { FText Label = LOCTEXT("DevicePreviewMenuTitle_Default", "Preview"); TSharedPtr< SLevelViewport > PinnedViewport( Viewport.Pin() ); if( PinnedViewport.IsValid() ) { if ( PinnedViewport->GetDeviceProfileString() != "Default" ) { Label = FText::FromString( PinnedViewport->GetDeviceProfileString() ); } } return Label; } const FSlateBrush* SLevelViewportToolBar::GetDevicePreviewMenuLabelIcon() const { TSharedRef ViewportRef = Viewport.Pin().ToSharedRef(); FString DeviceProfileName = ViewportRef->GetDeviceProfileString(); FName PlatformIcon = NAME_None; if( !DeviceProfileName.IsEmpty() && DeviceProfileName != "Default" ) { static FName DeviceProfileServices( "DeviceProfileServices" ); IDeviceProfileServicesModule& ScreenDeviceProfileUIServices = FModuleManager::LoadModuleChecked(TEXT( "DeviceProfileServices")); IDeviceProfileServicesUIManagerPtr UIManager = ScreenDeviceProfileUIServices.GetProfileServicesManager(); PlatformIcon = UIManager->GetDeviceIconName( DeviceProfileName ); return FAppStyle::GetOptionalBrush( PlatformIcon ); } return nullptr; } bool SLevelViewportToolBar::IsCurrentLevelViewport() const { TSharedPtr< SLevelViewport > PinnedViewport( Viewport.Pin() ); if( PinnedViewport.IsValid() ) { if( &( PinnedViewport->GetLevelViewportClient() ) == GCurrentLevelEditingViewportClient ) { return true; } } return false; } bool SLevelViewportToolBar::IsPerspectiveViewport() const { TSharedPtr< SLevelViewport > PinnedViewport( Viewport.Pin() ); if( PinnedViewport.IsValid() ) { if( PinnedViewport->GetLevelViewportClient().IsPerspective() ) { return true; } } return false; } TSharedRef SLevelViewportToolBar::GenerateOptionsMenu() { static const FName MenuName("LevelEditor.LevelViewportToolBar.Options"); if (!UToolMenus::Get()->IsMenuRegistered(MenuName)) { UToolMenu* Menu = UToolMenus::Get()->RegisterMenu(MenuName); Menu->AddDynamicSection("DynamicSection", FNewToolMenuDelegate::CreateLambda([](UToolMenu* InMenu) { if (ULevelViewportToolBarContext* Context = InMenu->FindContext()) { if (TSharedPtr ToolBar = Context->LevelViewportToolBarWidget.Pin()) { ToolBar->FillOptionsMenu(InMenu); } } })); } Viewport.Pin()->OnFloatingButtonClicked(); const FLevelViewportCommands& LevelViewportActions = FLevelViewportCommands::Get(); TSharedRef CommandList = Viewport.Pin()->GetCommandList().ToSharedRef(); // Get all menu extenders for this context menu from the level editor module FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked( TEXT("LevelEditor") ); TSharedPtr MenuExtender = LevelEditorModule.AssembleExtenders(CommandList, LevelEditorModule.GetAllLevelViewportOptionsMenuExtenders()); FToolMenuContext MenuContext(CommandList, MenuExtender); { { ULevelViewportToolBarContext* ToolbarContextObject = NewObject(); ToolbarContextObject->LevelViewportToolBarWidget = SharedThis(this); MenuContext.AddObject(ToolbarContextObject); } { ULevelViewportContext* const LevelContextObject = NewObject(); LevelContextObject->LevelViewport = Viewport; MenuContext.AddObject(LevelContextObject); } { UUnrealEdViewportToolbarContext* EdViewportToolbarContext = NewObject(); EdViewportToolbarContext->Viewport = Viewport; MenuContext.AddObject(EdViewportToolbarContext); } } return UToolMenus::Get()->GenerateWidget(MenuName, MenuContext); } void SLevelViewportToolBar::FillOptionsMenu(UToolMenu* Menu) { const FLevelViewportCommands& LevelViewportActions = FLevelViewportCommands::Get(); const bool bIsPerspective = Viewport.Pin()->GetLevelViewportClient().IsPerspective(); { { FToolMenuSection& Section = Menu->AddSection("LevelViewportViewportOptions", LOCTEXT("OptionsMenuHeader", "Viewport Options")); Section.AddMenuEntry(FEditorViewportCommands::Get().ToggleRealTime); // Add an option to disable the temporary override if there is one { Section.AddEntry(UE::UnrealEd::CreateRemoveRealtimeOverrideEntry(Viewport)); Section.AddSeparator("DisableRealtimeOverrideSeparator"); } Section.AddMenuEntry(FEditorViewportCommands::Get().ToggleFPS); #if STATS { Section.AddMenuEntry(FEditorViewportCommands::Get().ToggleStats); Section.AddEntry(UE::LevelEditor::CreateShowStatsSubmenu()); } #endif Section.AddMenuEntry(LevelViewportActions.ToggleAllowConstrainedAspectRatioInPreview); Section.AddMenuEntry(LevelViewportActions.ToggleViewportToolbar); if( bIsPerspective ) { Section.AddEntry(UE::LevelEditor::CreateFOVMenu(Viewport)); Section.AddEntry(UE::LevelEditor::CreateFarViewPlaneMenu(Viewport)); } Section.AddEntry(UE::UnrealEd::CreateScreenPercentageSubmenu()); } { FToolMenuSection& Section = Menu->AddSection("LevelViewportViewportOptions2"); if( bIsPerspective ) { // Cinematic preview only applies to perspective Section.AddMenuEntry( LevelViewportActions.ToggleCinematicPreview ); } Section.AddMenuEntry( LevelViewportActions.ToggleGameView ); Section.AddMenuEntry( LevelViewportActions.ToggleImmersive ); } { FToolMenuSection& Section = Menu->AddSection("LevelViewportBookmarks"); if( bIsPerspective ) { // Bookmarks only work in perspective viewports so only show the menu option if this toolbar is in one Section.AddSubMenu( "Bookmark", LOCTEXT("BookmarkSubMenu", "Bookmarks"), LOCTEXT("BookmarkSubMenu_ToolTip", "Viewport location bookmarking"), FNewToolMenuDelegate::CreateStatic(&UE::LevelEditor::CreateBookmarksMenu), false, FSlateIcon(FAppStyle::Get().GetStyleSetName(), "EditorViewport.SubMenu.Bookmarks") ); Section.AddSubMenu( "Camera", LOCTEXT("CameraSubMeun", "Create Camera Here"), LOCTEXT("CameraSubMenu_ToolTip", "Select a camera type to create at current viewport's location"), FNewToolMenuDelegate::CreateStatic(&UE::LevelEditor::CreateCameraSpawnMenu), false, FSlateIcon(FAppStyle::Get().GetStyleSetName(), "EditorViewport.SubMenu.CreateCamera") ); } Section.AddMenuEntry(LevelViewportActions.HighResScreenshot); } { FToolMenuSection& Section = Menu->AddSection("LevelViewportLayouts"); Section.AddSubMenu("Configs", LOCTEXT("ConfigsSubMenu", "Layouts"), FText::GetEmpty(), FNewToolMenuDelegate::CreateLambda( [](UToolMenu* InMenu) { UE::LevelEditor::GenerateViewportLayoutsMenu(InMenu); }), false, FSlateIcon(FAppStyle::Get().GetStyleSetName(), "Icons.Layout")); } { FToolMenuSection& Section = Menu->AddSection("LevelViewportSettings"); Section.AddMenuEntry(LevelViewportActions.AdvancedSettings); } } } TSharedRef SLevelViewportToolBar::GenerateDevicePreviewMenu() const { static const FName MenuName("LevelEditor.LevelViewportToolBar.DevicePreview"); if (!UToolMenus::Get()->IsMenuRegistered(MenuName)) { UToolMenu* Menu = UToolMenus::Get()->RegisterMenu(MenuName); Menu->AddDynamicSection("DynamicSection", FNewToolMenuDelegate::CreateLambda([](UToolMenu* InMenu) { if (ULevelViewportToolBarContext* Context = InMenu->FindContext()) { if (TSharedPtr ToolBar = Context->LevelViewportToolBarWidget.Pin()) { ToolBar->FillDevicePreviewMenu(InMenu); } } })); } ULevelViewportToolBarContext* ContextObject = NewObject(); ContextObject->LevelViewportToolBarWidget = ConstCastSharedRef(SharedThis(this)); FToolMenuContext MenuContext(Viewport.Pin()->GetCommandList(), TSharedPtr(), ContextObject); return UToolMenus::Get()->GenerateWidget(MenuName, MenuContext); } void SLevelViewportToolBar::FillDevicePreviewMenu(UToolMenu* Menu) const { IDeviceProfileServicesModule& ScreenDeviceProfileUIServices = FModuleManager::LoadModuleChecked(TEXT( "DeviceProfileServices")); IDeviceProfileServicesUIManagerPtr UIManager = ScreenDeviceProfileUIServices.GetProfileServicesManager(); TSharedRef ViewportRef = Viewport.Pin().ToSharedRef(); // Default menu - clear all settings { FToolMenuSection& Section = Menu->AddSection("DevicePreview", LOCTEXT("DevicePreviewMenuTitle", "Device Preview")); FUIAction Action( FExecuteAction::CreateSP( const_cast(this), &SLevelViewportToolBar::SetLevelProfile, FString( "Default" ) ), FCanExecuteAction(), FIsActionChecked::CreateSP( ViewportRef, &SLevelViewport::IsDeviceProfileStringSet, FString( "Default" ) ) ); Section.AddMenuEntry("DevicePreviewMenuClear", LOCTEXT("DevicePreviewMenuClear", "Off"), FText::GetEmpty(), FSlateIcon(), Action, EUserInterfaceActionType::Button); } // Recent Device Profiles { FToolMenuSection& Section = Menu->AddSection("Recent", LOCTEXT("RecentMenuHeading", "Recent")); const FString INISection = "SelectedProfile"; const FString INIKeyBase = "ProfileItem"; const int32 MaxItems = 4; // Move this into a config file FString CurItem; for( int32 ItemIdx = 0 ; ItemIdx < MaxItems; ++ItemIdx ) { // Build the menu from the contents of the game ini //@todo This should probably be using GConfig->GetText [10/21/2013 justin.sargent] if ( GConfig->GetString( *INISection, *FString::Printf( TEXT("%s%d"), *INIKeyBase, ItemIdx ), CurItem, GEditorPerProjectIni ) ) { const FName PlatformIcon = UIManager->GetDeviceIconName( CurItem ); FUIAction Action( FExecuteAction::CreateSP( const_cast(this), &SLevelViewportToolBar::SetLevelProfile, CurItem ), FCanExecuteAction(), FIsActionChecked::CreateSP( ViewportRef, &SLevelViewport::IsDeviceProfileStringSet, CurItem ) ); Section.AddMenuEntry(NAME_None, FText::FromString(CurItem), FText(), FSlateIcon(FAppStyle::GetAppStyleSetName(), PlatformIcon), Action, EUserInterfaceActionType::Button ); } } } // Device List { FToolMenuSection& Section = Menu->AddSection("Devices", LOCTEXT("DevicesMenuHeading", "Devices")); const TArray > PlatformList = UIManager->GetPlatformList(); for ( int32 Index = 0; Index < PlatformList.Num(); Index++ ) { TArray< UDeviceProfile* > DeviceProfiles; UIManager->GetProfilesByType( DeviceProfiles, *PlatformList[Index] ); if ( DeviceProfiles.Num() > 0 ) { const FString PlatformNameStr = DeviceProfiles[0]->DeviceType; const FName PlatformIcon = UIManager->GetPlatformIconName( PlatformNameStr ); Section.AddSubMenu( NAME_None, FText::FromString( PlatformNameStr ), FText::GetEmpty(), FNewToolMenuDelegate::CreateRaw( const_cast(this), &SLevelViewportToolBar::MakeDevicePreviewSubMenu, DeviceProfiles ), false, FSlateIcon(FAppStyle::GetAppStyleSetName(), PlatformIcon) ); } } } } void SLevelViewportToolBar::MakeDevicePreviewSubMenu(UToolMenu* Menu, TArray< class UDeviceProfile* > InProfiles) { TSharedRef ViewportRef = Viewport.Pin().ToSharedRef(); FToolMenuSection& Section = Menu->AddSection("Section"); for ( int32 Index = 0; Index < InProfiles.Num(); Index++ ) { FUIAction Action( FExecuteAction::CreateSP( this, &SLevelViewportToolBar::SetLevelProfile, InProfiles[ Index ]->GetName() ), FCanExecuteAction(), FIsActionChecked::CreateSP( ViewportRef, &SLevelViewport::IsDeviceProfileStringSet, InProfiles[ Index ]->GetName() ) ); Section.AddMenuEntry(NAME_None, FText::FromString( InProfiles[ Index ]->GetName() ), FText(), FSlateIcon(), Action, EUserInterfaceActionType::RadioButton ); } } void SLevelViewportToolBar::SetLevelProfile( FString DeviceProfileName ) { TSharedRef ViewportRef = Viewport.Pin().ToSharedRef(); ViewportRef->SetDeviceProfileString( DeviceProfileName ); IDeviceProfileServicesModule& ScreenDeviceProfileUIServices = FModuleManager::LoadModuleChecked(TEXT( "DeviceProfileServices")); IDeviceProfileServicesUIManagerPtr UIManager = ScreenDeviceProfileUIServices.GetProfileServicesManager(); UIManager->SetProfile( DeviceProfileName ); } void SLevelViewportToolBar::GeneratePlacedCameraMenuEntries(FToolMenuSection& Section, TArray LookThroughActors) const { FSlateIcon CameraIcon( FAppStyle::GetAppStyleSetName(), "ClassIcon.CameraComponent" ); // Sort the cameras to make the ordering predictable for users. LookThroughActors.StableSort([](const AActor& Left, const AActor& Right) { // Do "natural sorting" via SceneOutliner::FNumericStringWrapper to make more sense to humans (also matches the Scene Outliner). This sorts "Camera2" before "Camera10" which a normal lexicographical sort wouldn't. SceneOutliner::FNumericStringWrapper LeftWrapper(FString(Left.GetActorLabel())); SceneOutliner::FNumericStringWrapper RightWrapper(FString(Right.GetActorLabel())); return LeftWrapper < RightWrapper; }); for( AActor* LookThroughActor : LookThroughActors) { // Needed for the delegate hookup to work below AActor* GenericActor = LookThroughActor; FText ActorDisplayName = FText::FromString(LookThroughActor->GetActorLabel()); FUIAction LookThroughCameraAction( FExecuteAction::CreateSP(Viewport.Pin().ToSharedRef(), &SLevelViewport::OnActorLockToggleFromMenu, GenericActor), FCanExecuteAction(), FIsActionChecked::CreateSP(Viewport.Pin().ToSharedRef(), &SLevelViewport::IsActorLocked, MakeWeakObjectPtr(GenericActor)) ); Section.AddMenuEntry( NAME_None, ActorDisplayName, FText::Format(LOCTEXT("LookThroughCameraActor_ToolTip", "Look through and pilot {0}"), ActorDisplayName), CameraIcon, LookThroughCameraAction, EUserInterfaceActionType::RadioButton ); } } void SLevelViewportToolBar::GeneratePlacedCameraMenuEntries(UToolMenu* Menu, TArray LookThroughActors) const { FToolMenuSection& Section = Menu->AddSection("Section"); GeneratePlacedCameraMenuEntries(Section, LookThroughActors); } void SLevelViewportToolBar::GenerateViewportTypeMenu(FToolMenuSection& Section) const { FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked(TEXT("LevelEditor")); LevelEditorModule.IterateViewportTypes([&](FName ViewportTypeName, const FViewportTypeDefinition& InDefinition) { if (InDefinition.ActivationCommand.IsValid()) { Section.AddMenuEntry(*FString::Printf(TEXT("ViewportType_%s"), *ViewportTypeName.ToString()), InDefinition.ActivationCommand); } }); } void SLevelViewportToolBar::GenerateViewportTypeMenu(UToolMenu* Menu) const { FToolMenuSection& Section = Menu->AddSection("Section"); GenerateViewportTypeMenu(Section); } void SLevelViewportToolBar::GenerateCameraSpawnMenu(UToolMenu* Menu) const { FToolMenuSection& Section = Menu->AddSection("Section"); const FLevelViewportCommands& Actions = FLevelViewportCommands::Get(); for (TSharedPtr Camera : Actions.CreateCameras) { Section.AddMenuEntry(NAME_None, Camera); } } TSharedRef SLevelViewportToolBar::GenerateCameraMenu() const { static const FName MenuName("LevelEditor.LevelViewportToolBar.Camera"); // Use a static bool to track whether or not this menu is registered. Bool instead of checking the registered // state with ToolMenus because we want the new viewport toolbar to be able to create this menu without breaking // this code. Static because this code can be called multiple times using different instances of this class. static bool bDidRegisterMenu = false; if (!bDidRegisterMenu) { bDidRegisterMenu = true; constexpr bool bWarnIfAlreadyRegistered = false; UToolMenu* Menu = UToolMenus::Get()->RegisterMenu(MenuName, NAME_None, EMultiBoxType::Menu, bWarnIfAlreadyRegistered); Menu->AddDynamicSection( "DynamicSection", FNewToolMenuDelegate::CreateLambda( [](UToolMenu* InMenu) { if (ULevelViewportToolBarContext* Context = InMenu->FindContext()) { if (TSharedPtr ViewportToolbar = Context->LevelViewportToolBarWidget.Pin()) { ViewportToolbar->FillCameraMenu(InMenu); } } } ) ); } Viewport.Pin()->OnFloatingButtonClicked(); ULevelViewportToolBarContext* ContextObject = NewObject(); ContextObject->LevelViewportToolBarWidget = ConstCastSharedRef(SharedThis(this)); FToolMenuContext MenuContext(Viewport.Pin()->GetCommandList(), TSharedPtr(), ContextObject); return UToolMenus::Get()->GenerateWidget(MenuName, MenuContext); } void SLevelViewportToolBar::FillCameraMenu(UToolMenu* Menu) const { // Camera types { FToolMenuSection& Section = Menu->AddSection("CameraTypes"); Section.AddMenuEntry(FEditorViewportCommands::Get().Perspective); } { FToolMenuSection& Section = Menu->AddSection("LevelViewportCameraType_Ortho", LOCTEXT("CameraTypeHeader_Ortho", "Orthographic")); Section.AddMenuEntry(FEditorViewportCommands::Get().Top); Section.AddMenuEntry(FEditorViewportCommands::Get().Bottom); Section.AddMenuEntry(FEditorViewportCommands::Get().Left); Section.AddMenuEntry(FEditorViewportCommands::Get().Right); Section.AddMenuEntry(FEditorViewportCommands::Get().Front); Section.AddMenuEntry(FEditorViewportCommands::Get().Back); } TArray LookThroughActors; for( TActorIterator It(GetWorld().Get()); It; ++It ) { LookThroughActors.Add( Cast(*It) ); } for (TActorIterator It(GetWorld().Get()); It; ++It) { LookThroughActors.Add(Cast(*It)); } FText CameraActorsHeading = LOCTEXT("CameraActorsHeading", "Placed Cameras and Scene Capture Actors"); // Don't add too many cameras to the top level menu or else it becomes too large const uint32 MaxCamerasInTopLevelMenu = 10; if(LookThroughActors.Num() > MaxCamerasInTopLevelMenu ) { FToolMenuSection& Section = Menu->AddSection("CameraActors"); Section.AddSubMenu("CameraActors", CameraActorsHeading, LOCTEXT("LookThroughPlacedCameras_ToolTip", "Look through and pilot placed cameras"), FNewToolMenuDelegate::CreateSP(this, &SLevelViewportToolBar::GeneratePlacedCameraMenuEntries, LookThroughActors ) ); } else { FToolMenuSection& Section = Menu->AddSection("CameraActors", CameraActorsHeading); GeneratePlacedCameraMenuEntries(Section, LookThroughActors); } UE::UnrealEd::GenerateViewportTypeMenu(Menu); } TSharedRef SLevelViewportToolBar::GenerateShowMenu() const { static const FName MenuName("LevelEditor.LevelViewportToolbar.Show"); // Use a static bool to track whether or not this menu is registered. Bool instead of checking the registered state // with ToolMenus because we want the new viewport toolbar to be able to create this menu without breaking this // code. Static because this code can be called multiple times using different instances of this class. static bool bDidRegisterMenu = false; if (!bDidRegisterMenu) { bDidRegisterMenu = true; // Don't warn here to avoid warnings if the new viewport toolbar already has created an empty version // of this menu. UToolMenu* Menu = UToolMenus::Get()->RegisterMenu(MenuName, NAME_None, EMultiBoxType::Menu, false); Menu->AddDynamicSection( "LevelDynamicSection", FNewToolMenuDelegate::CreateLambda( [](UToolMenu* InMenu) { if (ULevelViewportToolBarContext* Context = InMenu->FindContext()) { if (TSharedPtr ToolBar = Context->LevelViewportToolBarWidget.Pin()) { ToolBar->FillShowMenu(InMenu); } } } ) ); } Viewport.Pin()->OnFloatingButtonClicked(); TSharedRef CommandList = Viewport.Pin()->GetCommandList().ToSharedRef(); // Get all menu extenders for this context menu from the level editor module FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked(TEXT("LevelEditor")); TSharedPtr MenuExtender = LevelEditorModule.AssembleExtenders(CommandList, LevelEditorModule.GetAllLevelViewportShowMenuExtenders()); FToolMenuContext MenuContext; { MenuContext.AppendCommandList(CommandList); MenuContext.AddExtender(MenuExtender); { ULevelViewportToolBarContext* ContextObject = NewObject(); ContextObject->LevelViewportToolBarWidget = ConstCastSharedRef(SharedThis(this)); MenuContext.AddObject(ContextObject); } { ULevelViewportContext* const ContextObject = NewObject(); ContextObject->LevelViewport = Viewport; MenuContext.AddObject(ContextObject); } } return UToolMenus::Get()->GenerateWidget(MenuName, MenuContext); } void SLevelViewportToolBar::FillShowMenu(UToolMenu* Menu) const { const FLevelViewportCommands& Actions = FLevelViewportCommands::Get(); { { FToolMenuSection& Section = Menu->AddSection("UseDefaultShowFlags"); Section.AddMenuEntry(Actions.UseDefaultShowFlags); } FShowFlagMenuCommands::Get().BuildShowFlagsMenu(Menu); FText ShowAllLabel = LOCTEXT("ShowAllLabel", "Show All"); FText HideAllLabel = LOCTEXT("HideAllLabel", "Hide All"); FLevelEditorViewportClient& ViewClient = Viewport.Pin()->GetLevelViewportClient(); const UWorld* World = ViewClient.GetWorld(); { FToolMenuSection& Section = Menu->AddSection("LevelViewportEditorShow", LOCTEXT("EditorShowHeader", "Editor")); Section.AddEntry(UE::LevelEditor::CreateShowVolumesSubmenu()); Section.AddEntry(UE::LevelEditor::CreateShowLayersSubmenu()); Section.AddEntry(UE::LevelEditor::CreateShowSpritesSubmenu()); Section.AddEntry(UE::LevelEditor::CreateShowFoliageSubmenu()); // Show 'HLODs' sub-menu is dynamically generated when the user enters 'show' menu if (World->IsPartitionedWorld()) { Section.AddEntry(UE::LevelEditor::CreateShowHLODsSubmenu()); } } } } EVisibility SLevelViewportToolBar::GetViewModeOptionsVisibility() const { const FLevelEditorViewportClient& ViewClient = Viewport.Pin()->GetLevelViewportClient(); if (ViewClient.GetViewMode() == VMI_MeshUVDensityAccuracy || ViewClient.GetViewMode() == VMI_MaterialTextureScaleAccuracy || ViewClient.GetViewMode() == VMI_RequiredTextureResolution) { return EVisibility::SelfHitTestInvisible; } else { return EVisibility::Collapsed; } } FText SLevelViewportToolBar::GetViewModeOptionsMenuLabel() const { Viewport.Pin()->OnFloatingButtonClicked(); const FLevelEditorViewportClient& ViewClient = Viewport.Pin()->GetLevelViewportClient(); return ::GetViewModeOptionsMenuLabel(ViewClient.GetViewMode()); } TSharedRef SLevelViewportToolBar::GenerateViewModeOptionsMenu() const { Viewport.Pin()->OnFloatingButtonClicked(); FLevelEditorViewportClient& ViewClient = Viewport.Pin()->GetLevelViewportClient(); const UWorld* World = ViewClient.GetWorld(); return BuildViewModeOptionsMenu(Viewport.Pin()->GetCommandList(), ViewClient.GetViewMode(), World ? World->GetFeatureLevel() : GMaxRHIFeatureLevel, ViewClient.GetViewModeParamNameMap()); } double SLevelViewportToolBar::OnGetHLODInEditorMaxDrawDistanceValue() const { IWorldPartitionEditorModule* WorldPartitionEditorModule = FModuleManager::GetModulePtr("WorldPartitionEditor"); return WorldPartitionEditorModule ? WorldPartitionEditorModule->GetHLODInEditorMaxDrawDistance() : 0; } void SLevelViewportToolBar::OnHLODInEditorMaxDrawDistanceValueChanged(double NewValue) const { IWorldPartitionEditorModule* WorldPartitionEditorModule = FModuleManager::GetModulePtr("WorldPartitionEditor"); if (WorldPartitionEditorModule) { WorldPartitionEditorModule->SetHLODInEditorMaxDrawDistance(NewValue); GEditor->RedrawLevelEditingViewports(true); } } TWeakObjectPtr SLevelViewportToolBar::GetWorld() const { if (Viewport.IsValid()) { return Viewport.Pin()->GetWorld(); } return NULL; } void SLevelViewportToolBar::FillViewMenu(UToolMenu* Menu) { if (!Menu) { return; } ULevelViewportContext* const ContextObject = NewObject(); ContextObject->LevelViewport = Viewport; Menu->Context.AddObject(ContextObject); UE::LevelEditor::PopulateViewModesMenu(Menu); } float SLevelViewportToolBar::GetTransformToolbarWidth() const { if (TransformToolbar) { const float TransformToolbarWidth = TransformToolbar->GetDesiredSize().X; if (TransformToolbar_CachedMaxWidth == 0.0f) { TransformToolbar_CachedMaxWidth = TransformToolbarWidth; } const float ToolbarWidthMinusPreviousTransformToolbar = GetDesiredSize().X - TransformToolbar_CachedMaxWidth; const float ToolbarWidthEstimate = ToolbarWidthMinusPreviousTransformToolbar + TransformToolbarWidth; const float ViewportToolBarWidth = static_cast(GetCachedGeometry().GetLocalSize().X); const float OverflowWidth = ToolbarWidthEstimate - ViewportToolBarWidth; if (OverflowWidth > 0.0f) { // There isn't enough space in the viewport to show the toolbar! // Try and shrink the transform toolbar (which has an overflow area) to make things fit TransformToolbar_CachedMaxWidth = FMath::Max(FMath::Min(4.0f, TransformToolbarWidth), TransformToolbarWidth - OverflowWidth); } else { TransformToolbar_CachedMaxWidth = TransformToolbarWidth; } return TransformToolbar_CachedMaxWidth; } return 0.0f; } FReply SLevelViewportToolBar::OnRealtimeWarningClicked() { FLevelEditorViewportClient& ViewportClient = Viewport.Pin()->GetLevelViewportClient(); ViewportClient.SetRealtime(true); return FReply::Handled(); } EVisibility SLevelViewportToolBar::GetRealtimeWarningVisibility() const { const FEditorViewportClient& ViewportClient = Viewport.Pin()->GetLevelViewportClient(); const bool bWarn = UE::UnrealEd::ShouldShowViewportRealtimeWarning(ViewportClient); return bWarn ? EVisibility::Visible : EVisibility::Collapsed; } FText SLevelViewportToolBar::GetScalabilityWarningLabel() const { const int32 QualityLevel = Scalability::GetQualityLevels().GetMinQualityLevel(); if (QualityLevel >= 0) { return FText::Format(LOCTEXT("ScalabilityWarning", "Scalability: {0}"), Scalability::GetScalabilityNameFromQualityLevel(QualityLevel)); } return FText::GetEmpty(); } EVisibility SLevelViewportToolBar::GetScalabilityWarningVisibility() const { return UE::UnrealEd::IsScalabilityWarningVisible() ? EVisibility::Visible : EVisibility::Collapsed; } TSharedRef SLevelViewportToolBar::GetScalabilityWarningMenuContent() const { return SNew(SBorder) .BorderImage(FAppStyle::GetBrush("Menu.Background")) [ SNew(SScalabilitySettings) ]; } FLevelEditorViewportClient* ULevelViewportToolBarContext::GetLevelViewportClient() const { if (TSharedPtr Toolbar = LevelViewportToolBarWidget.Pin()) { return Toolbar->GetLevelViewportClient(); } return nullptr; } #undef LOCTEXT_NAMESPACE