// Copyright Epic Games, Inc. All Rights Reserved. #include "LevelEditorToolBar.h" #include "Misc/MessageDialog.h" #include "HAL/FileManager.h" #include "Modules/ModuleManager.h" #include "Framework/Application/SlateApplication.h" #include "Widgets/Input/SComboButton.h" #include "Widgets/Layout/SBorder.h" #include "Widgets/Text/STextBlock.h" #include "Widgets/Layout/SBox.h" #include "Framework/MultiBox/MultiBoxDefs.h" #include "Framework/MultiBox/MultiBoxExtender.h" #include "Framework/MultiBox/MultiBoxBuilder.h" #include "Widgets/Input/SCheckBox.h" #include "Widgets/Input/SSpinBox.h" #include "Widgets/Input/SSlider.h" #include "Widgets/Layout/SSpacer.h" #include "Styling/AppStyle.h" #include "Settings/EditorExperimentalSettings.h" #include "GameMapsSettings.h" #include "GameFramework/PlayerController.h" #include "GameFramework/GameModeBase.h" #include "GameFramework/HUD.h" #include "GameFramework/GameStateBase.h" #include "Engine/TextureStreamingTypes.h" #include "LevelEditor.h" #include "LevelEditorActions.h" #include "SourceCodeNavigation.h" #include "Kismet2/DebuggerCommands.h" #include "SceneOutlinerPublicTypes.h" #include "SceneOutlinerModule.h" #include "ActorTreeItem.h" #include "SScalabilitySettings.h" #include "IContentBrowserSingleton.h" #include "ContentBrowserModule.h" #include "LevelSequenceActor.h" #include "LevelSequence.h" #include "Engine/LevelScriptBlueprint.h" #include "ISettingsCategory.h" #include "ISettingsContainer.h" #include "ISettingsModule.h" #include "ISettingsSection.h" #include "ClassViewerModule.h" #include "ClassViewerFilter.h" #include "Kismet2/KismetEditorUtilities.h" #include "Kismet2/BlueprintEditorUtils.h" #include "Widgets/Input/SVolumeControl.h" #include "Features/IModularFeatures.h" #include "EngineUtils.h" #include "ScopedTransaction.h" #include "Features/EditorFeatures.h" #include "Misc/ConfigCacheIni.h" #include "ILauncherPlatform.h" #include "LauncherPlatformModule.h" #include "Misc/ScopedSlowTask.h" #include "MaterialShaderQualitySettings.h" #include "LevelEditorMenuContext.h" #include "ToolMenus.h" #include "Subsystems/AssetEditorSubsystem.h" #include "LevelEditorModesActions.h" #include "ISourceControlModule.h" #include "Styling/ToolBarStyle.h" #include "PlatformInfo.h" #include "DataDrivenShaderPlatformInfo.h" #include "ViewportToolbar/LevelEditorSubmenus.h" namespace UE::Editor::Private { int32 CVarLevelEditorToolbarSettingsValue = 0; } static FAutoConsoleVariableRef CVarToolMenusViewportToolbars( TEXT("LevelEditorToolbarSettings"), UE::Editor::Private::CVarLevelEditorToolbarSettingsValue, TEXT("Control whether the level editor Settings menu is visible in the toolbar. Set to 1 (default) to show it and " "0 to hide it."), ECVF_Default ); FName FLevelEditorToolBar::SecondaryModeToolbarName("LevelEditor.SecondaryToolbar"); namespace PreviewModeFunctionality { FText GetPreviewModeText() { const FPreviewPlatformMenuItem* Item = FDataDrivenPlatformInfoRegistry::GetAllPreviewPlatformMenuItems().FindByPredicate([](const FPreviewPlatformMenuItem& TestItem) { return GEditor->PreviewPlatform.PreviewPlatformName == TestItem.PlatformName && GEditor->PreviewPlatform.PreviewShaderFormatName == TestItem.ShaderFormat && GEditor->PreviewPlatform.PreviewShaderPlatformName == TestItem.PreviewShaderPlatformName; }); return Item ? Item->IconText : FText(); } FText GetPreviewModeTooltip() { #define LOCTEXT_NAMESPACE "LevelEditorToolBar" EShaderPlatform PreviewShaderPlatform = GEditor->PreviewPlatform.GetShaderPlatform(); EShaderPlatform MaxRHIFeatureLevelPlatform = GetFeatureLevelShaderPlatform(GMaxRHIFeatureLevel); { const FText& RenderingAsPlatformName = GEditor->PreviewPlatform.bPreviewFeatureLevelActive ? GEditor->PreviewPlatform.GetFriendlyName() : FDataDrivenShaderPlatformInfo::GetFriendlyName(MaxRHIFeatureLevelPlatform); const FText& SwitchToPlatformName = GEditor->PreviewPlatform.bPreviewFeatureLevelActive ? FDataDrivenShaderPlatformInfo::GetFriendlyName(MaxRHIFeatureLevelPlatform) :GEditor->PreviewPlatform.GetFriendlyName(); if (PreviewShaderPlatform == MaxRHIFeatureLevelPlatform) { return FText::Format(LOCTEXT("PreviewModeViewingAs", "Viewing {0}."), RenderingAsPlatformName); } else if (GWorld->GetFeatureLevel() == GMaxRHIFeatureLevel) { return FText::Format(LOCTEXT("PreviewModeViewingAsSwitchTo", "Viewing {0}. Click to preview {1}."), RenderingAsPlatformName, SwitchToPlatformName); } else { return FText::Format(LOCTEXT("PreviewModePreviewingAsSwitchTo", "Previewing {0}. Click to view {1}."), RenderingAsPlatformName, SwitchToPlatformName); } } #undef LOCTEXT_NAMESPACE } FSlateIcon GetPreviewModeIcon() { const FPreviewPlatformMenuItem* Item = FDataDrivenPlatformInfoRegistry::GetAllPreviewPlatformMenuItems().FindByPredicate([](const FPreviewPlatformMenuItem& TestItem) { return GEditor->PreviewPlatform.PreviewPlatformName == TestItem.PlatformName; }); if (Item) { return FSlateIcon(FAppStyle::GetAppStyleSetName(), GEditor->IsFeatureLevelPreviewActive() ? Item->ActiveIconName : Item->InactiveIconName); } EShaderPlatform ShaderPlatform = FDataDrivenShaderPlatformInfo::GetShaderPlatformFromName(GEditor->PreviewPlatform.PreviewShaderPlatformName); if (ShaderPlatform == SP_NumPlatforms) { ShaderPlatform = GetFeatureLevelShaderPlatform(GEditor->PreviewPlatform.PreviewFeatureLevel); } switch (GEditor->PreviewPlatform.PreviewFeatureLevel) { case ERHIFeatureLevel::ES3_1: { return FSlateIcon(FAppStyle::GetAppStyleSetName(), GEditor->IsFeatureLevelPreviewActive() ? "LevelEditor.PreviewMode.Enabled" : "LevelEditor.PreviewMode.Disabled"); } default: { return FSlateIcon(FAppStyle::GetAppStyleSetName(), GEditor->IsFeatureLevelPreviewActive() ? "LevelEditor.PreviewMode.Enabled" : "LevelEditor.PreviewMode.Disabled"); } } } LEVELEDITOR_API void AddPreviewToggleButton(FToolMenuSection& Section) { Section.AddEntry(FToolMenuEntry::InitToolBarButton( FLevelEditorCommands::Get().ToggleFeatureLevelPreview, TAttribute::Create(&GetPreviewModeText), TAttribute::Create(&GetPreviewModeTooltip), TAttribute::Create(&GetPreviewModeIcon) )); } } namespace LevelEditorActionHelpers { /** Filters out any classes for the Class Picker when creating or selecting classes in the Blueprints dropdown */ class FBlueprintParentFilter_MapModeSettings : public IClassViewerFilter { public: /** Classes to not allow any children of into the Class Viewer/Picker. */ TSet< const UClass* > AllowedChildrenOfClasses; virtual bool IsClassAllowed(const FClassViewerInitializationOptions& InInitOptions, const UClass* InClass, TSharedRef< FClassViewerFilterFuncs > InFilterFuncs ) override { return InFilterFuncs->IfInChildOfClassesSet(AllowedChildrenOfClasses, InClass) == EFilterReturn::Passed; } virtual bool IsUnloadedClassAllowed(const FClassViewerInitializationOptions& InInitOptions, const TSharedRef< const IUnloadedBlueprintData > InUnloadedClassData, TSharedRef< FClassViewerFilterFuncs > InFilterFuncs) override { return InFilterFuncs->IfInChildOfClassesSet(AllowedChildrenOfClasses, InUnloadedClassData) == EFilterReturn::Passed; } }; /** * Retrieves the GameMode class * * @param InLevelEditor The editor to extract the world from * @param bInIsProjectSettings TRUE if retrieving the game mode from the project settings * @return The GameMode class in the Project Settings or World Settings */ static UClass* GetGameModeClass(TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings); /** Callback for the label to display for the GameMode menu selection */ static FText GetOpenGameModeBlueprintLabel(TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings); /** Callback when selecting a GameMode class, assigns it to the world */ static void OnSelectGameModeClassPicked(UClass* InChosenClass, TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings); /** Callback when creating a new GameMode class, creates the Blueprint and assigns it to the world */ static void OnCreateGameModeClassPicked(UClass* InChosenClass, TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings); /** * Retrieves the active GameState class from * * @param InLevelEditor The editor to extract the world from * @param bInIsProjectSettings TRUE if retrieving the game mode from the project settings * @return The active GameState class in the World Settings */ static UClass* GetGameStateClass(TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings); /** Callback for the label to display for the GameState menu selection */ static FText GetOpenGameStateBlueprintLabel(TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings); /** Callback when selecting a GameState class, assigns it to the world */ static void OnSelectGameStateClassPicked(UClass* InChosenClass, TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings); /** Callback when creating a new GameState class, creates the Blueprint and assigns it to the world */ static void OnCreateGameStateClassPicked(UClass* InChosenClass, TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings); /** * Retrieves the active Pawn class from * * @param InLevelEditor The editor to extract the world from * @return The active Pawn class in the World Settings */ static UClass* GetPawnClass(TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings); /** Callback for the label to display for the Pawn menu selection */ static FText GetOpenPawnBlueprintLabel(TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings); /** Callback for the tooltip to display for the Pawn menu selection */ static FText GetOpenPawnBlueprintTooltip(TWeakPtr< SLevelEditor > InLevelEditor); /** Callback when selecting a Pawn class, assigns it to the world */ static void OnSelectPawnClassPicked(UClass* InChosenClass, TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings); /** Callback when creating a new Pawn class, creates the Blueprint and assigns it to the world */ static void OnCreatePawnClassPicked(UClass* InChosenClass, TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings); /** * Retrieves the active HUD class from * * @param InLevelEditor The editor to extract the world from * @param bInIsProjectSettings TRUE if retrieving the game mode from the project settings * @return The active HUD class in the World Settings */ static UClass* GetHUDClass(TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings); /** Callback for the label to display for the HUD menu selection */ static FText GetOpenHUDBlueprintLabel(TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings); /** Callback when selecting a HUD class, assigns it to the world */ static void OnSelectHUDClassPicked(UClass* InChosenClass, TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings); /** Callback when creating a new HUD class, creates the Blueprint and assigns it to the world */ static void OnCreateHUDClassPicked(UClass* InChosenClass, TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings); /** * Retrieves the active PlayerController class from * * @param InLevelEditor The editor to extract the world from * @param bInIsProjectSettings TRUE if retrieving the game mode from the project settings * @return The active PlayerController class in the World Settings */ static UClass* GetPlayerControllerClass(TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings); /** Callback for the label to display for the PlayerController menu selection */ static FText GetOpenPlayerControllerBlueprintLabel(TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings); /** Callback when selecting a PlayerController class, assigns it to the world */ static void OnSelectPlayerControllerClassPicked(UClass* InChosenClass, TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings); /** Callback when creating a new PlayerController class, creates the Blueprint and assigns it to the world */ static void OnCreatePlayerControllerClassPicked(UClass* InChosenClass, TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings); /** Opens a native class's header file if the compiler is available. */ static void OpenNativeClass(UClass* InClass) { if(InClass->HasAllClassFlags(CLASS_Native) && FSourceCodeNavigation::IsCompilerAvailable()) { FString NativeParentClassHeaderPath; const bool bFileFound = FSourceCodeNavigation::FindClassHeaderPath(InClass, NativeParentClassHeaderPath) && (IFileManager::Get().FileSize(*NativeParentClassHeaderPath) != INDEX_NONE); if (bFileFound) { const FString AbsoluteHeaderPath = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*NativeParentClassHeaderPath); FSourceCodeNavigation::OpenSourceFile( AbsoluteHeaderPath ); } } } /** Open the game mode blueprint, in the project settings or world settings */ static void OpenGameModeBlueprint( TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings ) { if(UClass* GameModeClass = GetGameModeClass(InLevelEditor, bInIsProjectSettings)) { if(UBlueprint* BlueprintClass = Cast(GameModeClass->ClassGeneratedBy)) { // @todo Re-enable once world centric works const bool bOpenWorldCentric = false; GEditor->GetEditorSubsystem()->OpenEditorForAsset( BlueprintClass, bOpenWorldCentric ? EToolkitMode::WorldCentric : EToolkitMode::Standalone, InLevelEditor.Pin() ); } else { OpenNativeClass(GameModeClass); } } } /** Open the game state blueprint, in the project settings or world settings */ static void OpenGameStateBlueprint( TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings ) { if(UClass* GameStateClass = GetGameStateClass(InLevelEditor, bInIsProjectSettings)) { if(UBlueprint* BlueprintClass = Cast(GameStateClass->ClassGeneratedBy)) { // @todo Re-enable once world centric works const bool bOpenWorldCentric = false; GEditor->GetEditorSubsystem()->OpenEditorForAsset( BlueprintClass, bOpenWorldCentric ? EToolkitMode::WorldCentric : EToolkitMode::Standalone, InLevelEditor.Pin() ); } else { OpenNativeClass(GameStateClass); } } } /** Open the default pawn blueprint, in the project settings or world settings */ static void OpenDefaultPawnBlueprint( TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings ) { if(UClass* DefaultPawnClass = GetPawnClass(InLevelEditor, bInIsProjectSettings)) { if(UBlueprint* BlueprintClass = Cast(DefaultPawnClass->ClassGeneratedBy)) { // @todo Re-enable once world centric works const bool bOpenWorldCentric = false; GEditor->GetEditorSubsystem()->OpenEditorForAsset( BlueprintClass, bOpenWorldCentric ? EToolkitMode::WorldCentric : EToolkitMode::Standalone, InLevelEditor.Pin() ); } else { OpenNativeClass(DefaultPawnClass); } } } /** Open the HUD blueprint, in the project settings or world settings */ static void OpenHUDBlueprint( TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings ) { if(UClass* DefaultHUDClass = GetHUDClass(InLevelEditor, bInIsProjectSettings)) { if(UBlueprint* BlueprintClass = Cast(DefaultHUDClass->ClassGeneratedBy)) { // @todo Re-enable once world centric works const bool bOpenWorldCentric = false; GEditor->GetEditorSubsystem()->OpenEditorForAsset( BlueprintClass, bOpenWorldCentric ? EToolkitMode::WorldCentric : EToolkitMode::Standalone, InLevelEditor.Pin() ); } else { OpenNativeClass(DefaultHUDClass); } } } /** Open the player controller blueprint, in the project settings or world settings */ static void OpenPlayerControllerBlueprint( TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings ) { if(UClass* PlayerControllerClass = GetPlayerControllerClass(InLevelEditor, bInIsProjectSettings)) { if(UBlueprint* BlueprintClass = Cast(PlayerControllerClass->ClassGeneratedBy)) { // @todo Re-enable once world centric works const bool bOpenWorldCentric = false; GEditor->GetEditorSubsystem()->OpenEditorForAsset( BlueprintClass, bOpenWorldCentric ? EToolkitMode::WorldCentric : EToolkitMode::Standalone, InLevelEditor.Pin() ); } else { OpenNativeClass(PlayerControllerClass); } } } /** * Builds a sub-menu for selecting a class * * @param InMenu Object to append menu items/widgets to * @param InRootClass The root class to filter the Class Viewer by to only show children of * @param InOnClassPicked Callback delegate to fire when a class is picked */ void GetSelectSettingsClassSubMenu(UToolMenu* InMenu, UClass* InRootClass, FOnClassPicked InOnClassPicked) { // The contents of this menu are added as a custom widget with its own search field so we // disable searching in this parent menu to avoid displaying two search fields to the user. InMenu->bSearchable = false; FClassViewerInitializationOptions Options; Options.Mode = EClassViewerMode::ClassPicker; Options.DisplayMode = EClassViewerDisplayMode::ListView; Options.bShowObjectRootClass = true; Options.bShowNoneOption = true; // Only want blueprint actor base classes. Options.bIsBlueprintBaseOnly = true; // This will allow unloaded blueprints to be shown. Options.bShowUnloadedBlueprints = true; TSharedPtr< FBlueprintParentFilter_MapModeSettings > Filter = MakeShareable(new FBlueprintParentFilter_MapModeSettings); Filter->AllowedChildrenOfClasses.Add(InRootClass); Options.ClassFilters.Add(Filter.ToSharedRef()); FText RootClassName = FText::FromString(InRootClass->GetName()); TSharedRef ClassViewer = FModuleManager::LoadModuleChecked("ClassViewer").CreateClassViewer(Options, InOnClassPicked); FFormatNamedArguments FormatArgs; FormatArgs.Add(TEXT("RootClass"), RootClassName); FToolMenuSection& Section = InMenu->AddSection("SelectSettingsClass", FText::Format(NSLOCTEXT("LevelToolBarViewMenu", "SelectGameModeLabel", "Select {RootClass} class"), FormatArgs)); Section.AddEntry(FToolMenuEntry::InitWidget("ClassViewer", ClassViewer, FText::GetEmpty(), true)); } /** * Builds a sub-menu for creating a class * * @param InMenu Object to append menu items/widgets to * @param InRootClass The root class to filter the Class Viewer by to only show children of * @param InOnClassPicked Callback delegate to fire when a class is picked */ void GetCreateSettingsClassSubMenu(UToolMenu* InMenu, UClass* InRootClass, FOnClassPicked InOnClassPicked) { // The contents of this menu are added as a custom widget with its own search field so we // disable searching in this parent menu to avoid displaying two search fields to the user. InMenu->bSearchable = false; FClassViewerInitializationOptions Options; Options.Mode = EClassViewerMode::ClassPicker; Options.DisplayMode = EClassViewerDisplayMode::ListView; Options.bShowObjectRootClass = true; // Only want blueprint actor base classes. Options.bIsBlueprintBaseOnly = true; // This will allow unloaded blueprints to be shown. Options.bShowUnloadedBlueprints = true; TSharedPtr< FBlueprintParentFilter_MapModeSettings > Filter = MakeShareable(new FBlueprintParentFilter_MapModeSettings); Filter->AllowedChildrenOfClasses.Add(InRootClass); Options.ClassFilters.Add(Filter.ToSharedRef()); FText RootClassName = FText::FromString(InRootClass->GetName()); TSharedRef ClassViewer = FModuleManager::LoadModuleChecked("ClassViewer").CreateClassViewer(Options, InOnClassPicked); FFormatNamedArguments FormatArgs; FormatArgs.Add(TEXT("RootClass"), RootClassName); FToolMenuSection& Section = InMenu->AddSection("CreateSettingsClass", FText::Format(NSLOCTEXT("LevelToolBarViewMenu", "CreateGameModeLabel", "Select {RootClass} parent class"), FormatArgs)); Section.AddEntry(FToolMenuEntry::InitWidget("ClassViewer", ClassViewer, FText::GetEmpty(), true)); } /** Helper struct for passing all required data to the GetBlueprintSettingsSubMenu function */ struct FBlueprintMenuSettings { /** The UI command for editing the Blueprint class associated with the menu */ FUIAction EditCommand; /** Current class associated with the menu */ UClass* CurrentClass; /** Root class that defines what class children can be set through the menu */ UClass* RootClass; /** Callback when a class is picked, to assign the new class */ FOnClassPicked OnSelectClassPicked; /** Callback when a class is picked, to create a new child class of and assign */ FOnClassPicked OnCreateClassPicked; /** Level Editor these menu settings are for */ TWeakPtr< SLevelEditor > LevelEditor; /** TRUE if these represent Project Settings, FALSE if they represent World Settings */ bool bIsProjectSettings; }; /** Returns the label of the "Check Out" option based on if source control is present or not */ FText GetCheckOutLabel() { #define LOCTEXT_NAMESPACE "LevelToolBarViewMenu" if(ISourceControlModule::Get().IsEnabled()) { return LOCTEXT("CheckoutMenuLabel", "Check Out"); } else { return LOCTEXT("MakeWritableLabel", "Make Writable"); } #undef LOCTEXT_NAMESPACE } /** Returns the tooltip of the "Check Out" option based on if source control is present or not */ FText GetCheckOutTooltip() { #define LOCTEXT_NAMESPACE "LevelToolBarViewMenu" if(ISourceControlModule::Get().IsEnabled()) { return LOCTEXT("CheckoutMenuTooltip", "Checks out the project settings config file so the game mode can be set."); } else { return LOCTEXT("MakeWritableTooltip", "Forces the project settings config file to be writable so the game mode can be set."); } #undef LOCTEXT_NAMESPACE } /** * A sub-menu for the Blueprints dropdown, facilitates all the sub-menu actions such as creating, editing, and selecting classes for the world settings game mode. * * @param InMenu Object to append menu items/widgets to * @param InCommandList Commandlist for menu items * @param InSettingsData All the data needed to create the menu actions */ void GetBlueprintSettingsSubMenu(UToolMenu* InMenu, FBlueprintMenuSettings InSettingsData); /** Returns TRUE if the class can be edited, always TRUE for Blueprints and for native classes a compiler must be present */ bool CanEditClass(UClass* InClass) { // For native classes, we can only edit them if a compiler is available if(InClass && InClass->HasAllClassFlags(CLASS_Native)) { return FSourceCodeNavigation::IsCompilerAvailable(); } return true; } /** Returns TRUE if the GameMode's sub-class can be created or selected */ bool CanCreateSelectSubClass(UClass* InGameModeClass, bool bInIsProjectSettings) { // Can never create or select project settings sub-classes if the config file is not checked out if(bInIsProjectSettings && !FLevelEditorActionCallbacks::CanSelectGameModeBlueprint()) { return false; } // If the game mode class is native, we cannot set the sub class if(!InGameModeClass || InGameModeClass->HasAllClassFlags(CLASS_Native)) { return false; } return true; } /** Creates a tooltip for a submenu */ FText GetSubMenuTooltip(UClass* InClass, UClass* InRootClass, bool bInIsProjectSettings) { #define LOCTEXT_NAMESPACE "LevelToolBarViewMenu" FFormatNamedArguments Args; Args.Add(TEXT("Class"), FText::FromString(InRootClass->GetName())); Args.Add(TEXT("TargetLocation"), bInIsProjectSettings? LOCTEXT("Project", "project") : LOCTEXT("World", "world")); return FText::Format(LOCTEXT("ClassSubmenu_Tooltip", "Select, edit, or create a new {Class} blueprint for the {TargetLocation}"), Args); #undef LOCTEXT_NAMESPACE } /** Creates a tooltip for the create class submenu */ FText GetCreateMenuTooltip(UClass* InGameModeClass, UClass* InRootClass, bool bInIsProjectSettings) { #define LOCTEXT_NAMESPACE "LevelToolBarViewMenu" FText ResultText; // Game modes can always be created and selected (providing the config is checked out, handled separately) if(InRootClass != AGameModeBase::StaticClass() && InGameModeClass->HasAllClassFlags(CLASS_Native)) { ResultText = LOCTEXT("CannotCreateClasses", "Cannot create classes when the game mode is a native class!"); } else if(bInIsProjectSettings && !FLevelEditorActionCallbacks::CanSelectGameModeBlueprint()) { ResultText = LOCTEXT("CannotCreateClasses_NeedsCheckOut", "Cannot create classes when the config file is not writable!"); } else { FFormatNamedArguments Args; Args.Add(TEXT("RootClass"), FText::FromString(InRootClass->GetName())); Args.Add(TEXT("TargetLocation"), bInIsProjectSettings? LOCTEXT("Project", "project") : LOCTEXT("World", "world")); ResultText = FText::Format( LOCTEXT("CreateClass_Tooltip", "Create a new {RootClass} based on a selected class and auto-assign it to the {TargetLocation}"), Args ); } return ResultText; #undef LOCTEXT_NAMESPACE } /** Creates a tooltip for the select class submenu */ FText GetSelectMenuTooltip(UClass* InGameModeClass, UClass* InRootClass, bool bInIsProjectSettings) { #define LOCTEXT_NAMESPACE "LevelToolBarViewMenu" FText ResultText; // Game modes can always be created and selected (providing the config is checked out, handled separately) if(InRootClass != AGameModeBase::StaticClass() && InGameModeClass->HasAllClassFlags(CLASS_Native)) { ResultText = LOCTEXT("CannotSelectClasses", "Cannot select classes when the game mode is a native class!"); } else if(bInIsProjectSettings && !FLevelEditorActionCallbacks::CanSelectGameModeBlueprint()) { ResultText = LOCTEXT("CannotSelectClasses_NeedsCheckOut", "Cannot select classes when the config file is not writable!"); } else { FFormatNamedArguments Args; Args.Add(TEXT("RootClass"), FText::FromString(InRootClass->GetName())); Args.Add(TEXT("TargetLocation"), bInIsProjectSettings? LOCTEXT("Project", "project") : LOCTEXT("World", "world")); ResultText = FText::Format( LOCTEXT("SelectClass_Tooltip", "Select a new {RootClass} based on a selected class and auto-assign it to the {TargetLocation}"), Args ); } return ResultText; #undef LOCTEXT_NAMESPACE } void CreateGameModeSubMenu(FToolMenuSection& Section, const FName InName, bool bInProjectSettings) { #define LOCTEXT_NAMESPACE "LevelToolBarViewMenu" Section.AddDynamicEntry(InName, FNewToolMenuSectionDelegate::CreateLambda([=](FToolMenuSection& InSection) { if (ULevelEditorMenuContext* const Context = InSection.FindContext(); Context && Context->LevelEditor.IsValid()) { LevelEditorActionHelpers::FBlueprintMenuSettings GameModeMenuSettings; GameModeMenuSettings.EditCommand = FUIAction( FExecuteAction::CreateStatic(&OpenGameModeBlueprint, Context->LevelEditor, bInProjectSettings) ); GameModeMenuSettings.OnCreateClassPicked = FOnClassPicked::CreateStatic(&LevelEditorActionHelpers::OnCreateGameModeClassPicked, Context->LevelEditor, bInProjectSettings); GameModeMenuSettings.OnSelectClassPicked = FOnClassPicked::CreateStatic(&LevelEditorActionHelpers::OnSelectGameModeClassPicked, Context->LevelEditor, bInProjectSettings); GameModeMenuSettings.CurrentClass = LevelEditorActionHelpers::GetGameModeClass(Context->LevelEditor, bInProjectSettings); GameModeMenuSettings.RootClass = AGameModeBase::StaticClass(); GameModeMenuSettings.LevelEditor = Context->LevelEditor; GameModeMenuSettings.bIsProjectSettings = bInProjectSettings; auto IsGameModeActive = [](TWeakPtr< SLevelEditor > InLevelEditorPtr, bool bInProjSettings)->bool { UClass* WorldSettingsGameMode = LevelEditorActionHelpers::GetGameModeClass(InLevelEditorPtr, false); if ((WorldSettingsGameMode == nullptr) ^ bInProjSettings) //(WorldSettingsGameMode && !bInProjectSettings) || (!WorldSettingsGameMode && bInProjectSettings) ) { return false; } return true; }; InSection.AddSubMenu(InName, LevelEditorActionHelpers::GetOpenGameModeBlueprintLabel(Context->LevelEditor, bInProjectSettings), GetSubMenuTooltip(GameModeMenuSettings.CurrentClass, GameModeMenuSettings.RootClass, bInProjectSettings), FNewToolMenuDelegate::CreateStatic(&LevelEditorActionHelpers::GetBlueprintSettingsSubMenu, GameModeMenuSettings), FUIAction(FExecuteAction(), FCanExecuteAction(), FIsActionChecked::CreateStatic(IsGameModeActive, Context->LevelEditor, bInProjectSettings)), EUserInterfaceActionType::RadioButton); } })); #undef LOCTEXT_NAMESPACE } /** * Builds the game mode's sub menu objects * * @param InSection Object to append menu items/widgets to * @param InCommandList Commandlist for menu items * @param InSettingsData All the data needed to create the menu actions */ void GetGameModeSubMenu(FToolMenuSection& InSection, const FBlueprintMenuSettings& InSettingsData) { #define LOCTEXT_NAMESPACE "LevelToolBarViewMenu" // Game State LevelEditorActionHelpers::FBlueprintMenuSettings GameStateMenuSettings; GameStateMenuSettings.EditCommand = FUIAction( FExecuteAction::CreateStatic( &OpenGameStateBlueprint, InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings ) ); GameStateMenuSettings.OnCreateClassPicked = FOnClassPicked::CreateStatic( &LevelEditorActionHelpers::OnCreateGameStateClassPicked, InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings ); GameStateMenuSettings.OnSelectClassPicked = FOnClassPicked::CreateStatic( &LevelEditorActionHelpers::OnSelectGameStateClassPicked, InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings ); GameStateMenuSettings.CurrentClass = LevelEditorActionHelpers::GetGameStateClass(InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings); GameStateMenuSettings.RootClass = AGameStateBase::StaticClass(); GameStateMenuSettings.LevelEditor = InSettingsData.LevelEditor; GameStateMenuSettings.bIsProjectSettings = InSettingsData.bIsProjectSettings; InSection.AddSubMenu("OpenGameStateBlueprint", LevelEditorActionHelpers::GetOpenGameStateBlueprintLabel(InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings), GetSubMenuTooltip(GameStateMenuSettings.CurrentClass, GameStateMenuSettings.RootClass, InSettingsData.bIsProjectSettings), FNewToolMenuDelegate::CreateStatic( &LevelEditorActionHelpers::GetBlueprintSettingsSubMenu, GameStateMenuSettings ) ); // Pawn LevelEditorActionHelpers::FBlueprintMenuSettings PawnMenuSettings; PawnMenuSettings.EditCommand = FUIAction( FExecuteAction::CreateStatic( &OpenDefaultPawnBlueprint, InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings ) ); PawnMenuSettings.OnCreateClassPicked = FOnClassPicked::CreateStatic( &LevelEditorActionHelpers::OnCreatePawnClassPicked, InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings ); PawnMenuSettings.OnSelectClassPicked = FOnClassPicked::CreateStatic( &LevelEditorActionHelpers::OnSelectPawnClassPicked, InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings ); PawnMenuSettings.CurrentClass = LevelEditorActionHelpers::GetPawnClass(InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings); PawnMenuSettings.RootClass = APawn::StaticClass(); PawnMenuSettings.LevelEditor = InSettingsData.LevelEditor; PawnMenuSettings.bIsProjectSettings = InSettingsData.bIsProjectSettings; InSection.AddSubMenu("OpenPawnBlueprint", LevelEditorActionHelpers::GetOpenPawnBlueprintLabel(InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings), GetSubMenuTooltip(PawnMenuSettings.CurrentClass, PawnMenuSettings.RootClass, InSettingsData.bIsProjectSettings), FNewToolMenuDelegate::CreateStatic( &LevelEditorActionHelpers::GetBlueprintSettingsSubMenu, PawnMenuSettings ) ); // HUD LevelEditorActionHelpers::FBlueprintMenuSettings HUDMenuSettings; HUDMenuSettings.EditCommand = FUIAction( FExecuteAction::CreateStatic( &OpenHUDBlueprint, InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings ) ); HUDMenuSettings.OnCreateClassPicked = FOnClassPicked::CreateStatic( &LevelEditorActionHelpers::OnCreateHUDClassPicked, InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings ); HUDMenuSettings.OnSelectClassPicked = FOnClassPicked::CreateStatic( &LevelEditorActionHelpers::OnSelectHUDClassPicked, InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings ); HUDMenuSettings.CurrentClass = LevelEditorActionHelpers::GetHUDClass(InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings); HUDMenuSettings.RootClass = AHUD::StaticClass(); HUDMenuSettings.LevelEditor = InSettingsData.LevelEditor; HUDMenuSettings.bIsProjectSettings = InSettingsData.bIsProjectSettings; InSection.AddSubMenu("OpenHUDBlueprint", LevelEditorActionHelpers::GetOpenHUDBlueprintLabel(InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings), GetSubMenuTooltip(HUDMenuSettings.CurrentClass, HUDMenuSettings.RootClass, InSettingsData.bIsProjectSettings), FNewToolMenuDelegate::CreateStatic( &LevelEditorActionHelpers::GetBlueprintSettingsSubMenu, HUDMenuSettings ) ); // Player Controller LevelEditorActionHelpers::FBlueprintMenuSettings PlayerControllerMenuSettings; PlayerControllerMenuSettings.EditCommand = FUIAction( FExecuteAction::CreateStatic( &OpenPlayerControllerBlueprint, InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings ) ); PlayerControllerMenuSettings.OnCreateClassPicked = FOnClassPicked::CreateStatic( &LevelEditorActionHelpers::OnCreatePlayerControllerClassPicked, InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings ); PlayerControllerMenuSettings.OnSelectClassPicked = FOnClassPicked::CreateStatic( &LevelEditorActionHelpers::OnSelectPlayerControllerClassPicked, InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings ); PlayerControllerMenuSettings.CurrentClass = LevelEditorActionHelpers::GetPlayerControllerClass(InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings); PlayerControllerMenuSettings.RootClass = APlayerController::StaticClass(); PlayerControllerMenuSettings.LevelEditor = InSettingsData.LevelEditor; PlayerControllerMenuSettings.bIsProjectSettings = InSettingsData.bIsProjectSettings; InSection.AddSubMenu("OpenPlayerControllerBlueprint", LevelEditorActionHelpers::GetOpenPlayerControllerBlueprintLabel(InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings), GetSubMenuTooltip(PlayerControllerMenuSettings.CurrentClass, PlayerControllerMenuSettings.RootClass, InSettingsData.bIsProjectSettings), FNewToolMenuDelegate::CreateStatic( &LevelEditorActionHelpers::GetBlueprintSettingsSubMenu, PlayerControllerMenuSettings ) ); #undef LOCTEXT_NAMESPACE } struct FLevelSortByName { bool operator ()(const ULevel* LHS, const ULevel* RHS) const { if (LHS != NULL && LHS->GetOutermost() != NULL && RHS != NULL && RHS->GetOutermost() != NULL) { return FPaths::GetCleanFilename(LHS->GetOutermost()->GetName()) < FPaths::GetCleanFilename(RHS->GetOutermost()->GetName()); } else { return false; } } }; } void LevelEditorActionHelpers::GetBlueprintSettingsSubMenu(UToolMenu* Menu, FBlueprintMenuSettings InSettingsData) { #define LOCTEXT_NAMESPACE "LevelToolBarViewMenu" FSlateIcon EditBPIcon(FAppStyle::Get().GetStyleSetName(), TEXT("Icons.Edit")); FSlateIcon NewBPIcon(FAppStyle::Get().GetStyleSetName(), TEXT("Icons.PlusCircle")); FText RootClassName = FText::FromString(InSettingsData.RootClass->GetName()); // If there is currently a valid GameMode Blueprint, offer to edit the Blueprint if(InSettingsData.CurrentClass) { FFormatNamedArguments Args; Args.Add(TEXT("RootClass"), RootClassName); Args.Add(TEXT("TargetLocation"), InSettingsData.bIsProjectSettings? LOCTEXT("Project", "project") : LOCTEXT("World", "world")); FToolMenuSection& Section = Menu->AddSection("EditBlueprintOrClass"); if(InSettingsData.CurrentClass->ClassGeneratedBy) { FText BlueprintName = FText::FromString(InSettingsData.CurrentClass->ClassGeneratedBy->GetName()); Args.Add(TEXT("Blueprint"), BlueprintName); Section.AddMenuEntry("EditBlueprint", FText::Format( LOCTEXT("EditBlueprint", "Edit {Blueprint}"), Args), FText::Format( LOCTEXT("EditBlueprint_Tooltip", "Open the {TargetLocation}'s assigned {RootClass} blueprint"), Args), EditBPIcon, InSettingsData.EditCommand ); } else { FText ClassName = FText::FromString(InSettingsData.CurrentClass->GetName()); Args.Add(TEXT("Class"), ClassName); FText MenuDescription = FText::Format( LOCTEXT("EditNativeClass", "Edit {Class}.h"), Args); if(FSourceCodeNavigation::IsCompilerAvailable()) { Section.AddMenuEntry("EditNativeClass", MenuDescription, FText::Format( LOCTEXT("EditNativeClass_Tooltip", "Open the {TargetLocation}'s assigned {RootClass} header"), Args), EditBPIcon, InSettingsData.EditCommand ); } else { auto CannotEditClass = []() -> bool { return false; }; // There is no compiler present, this is always disabled with a tooltip to explain why Section.AddMenuEntry("EditNativeClass", MenuDescription, FText::Format( LOCTEXT("CannotEditNativeClass_Tooltip", "Cannot edit the {TargetLocation}'s assigned {RootClass} header because no compiler is present!"), Args), EditBPIcon, FUIAction(FExecuteAction(), FCanExecuteAction::CreateStatic(CannotEditClass)) ); } } } if(InSettingsData.bIsProjectSettings && InSettingsData.CurrentClass && InSettingsData.CurrentClass->IsChildOf(AGameModeBase::StaticClass()) && !FLevelEditorActionCallbacks::CanSelectGameModeBlueprint()) { FToolMenuSection& Section = Menu->AddSection("CheckoutSection", LOCTEXT("CheckoutSection", "Check Out Project Settings") ); TAttribute CheckOutLabel; CheckOutLabel.BindStatic(&GetCheckOutLabel); TAttribute CheckOutTooltip; CheckOutTooltip.BindStatic(&GetCheckOutTooltip); Section.AddMenuEntry(FLevelEditorCommands::Get().CheckOutProjectSettingsConfig, CheckOutLabel, CheckOutTooltip, FSlateIcon(FAppStyle::Get().GetStyleSetName(), TEXT("Icons.Error"))); } auto CannotCreateSelectNativeProjectGameMode = [](bool bInIsProjectSettings) -> bool { // For the project settings, we can only create/select the game mode class if the config is writable if(bInIsProjectSettings) { return FLevelEditorActionCallbacks::CanSelectGameModeBlueprint(); } return true; }; FToolMenuSection& Section = Menu->AddSection("CreateBlueprint"); // Create a new GameMode, this is always available so the user can easily create a new one Section.AddSubMenu("CreateBlueprint", LOCTEXT("CreateBlueprint", "Create..."), GetCreateMenuTooltip(GetGameModeClass(InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings), InSettingsData.RootClass, InSettingsData.bIsProjectSettings), FNewToolMenuDelegate::CreateStatic( &LevelEditorActionHelpers::GetCreateSettingsClassSubMenu, InSettingsData.RootClass, InSettingsData.OnCreateClassPicked ), FUIAction( FExecuteAction(), InSettingsData.RootClass == AGameModeBase::StaticClass()? FCanExecuteAction::CreateStatic(CannotCreateSelectNativeProjectGameMode, InSettingsData.bIsProjectSettings) : FCanExecuteAction::CreateStatic( &CanCreateSelectSubClass, GetGameModeClass(InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings), InSettingsData.bIsProjectSettings ) ), EUserInterfaceActionType::Button, false, NewBPIcon ); // Select a game mode, this is always available so the user can switch their selection FFormatNamedArguments Args; Args.Add(TEXT("RootClass"), RootClassName); Section.AddSubMenu("SelectGameModeClass", FText::Format(LOCTEXT("SelectGameModeClass", "Select {RootClass} Class"), Args), GetSelectMenuTooltip(GetGameModeClass(InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings), InSettingsData.RootClass, InSettingsData.bIsProjectSettings), FNewToolMenuDelegate::CreateStatic( &LevelEditorActionHelpers::GetSelectSettingsClassSubMenu, InSettingsData.RootClass, InSettingsData.OnSelectClassPicked ), FUIAction( FExecuteAction(), InSettingsData.RootClass == AGameModeBase::StaticClass()? FCanExecuteAction::CreateStatic(CannotCreateSelectNativeProjectGameMode, InSettingsData.bIsProjectSettings) : FCanExecuteAction::CreateStatic( &CanCreateSelectSubClass, GetGameModeClass(InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings), InSettingsData.bIsProjectSettings ) ), EUserInterfaceActionType::Button ); // For GameMode classes only, there are some sub-classes we need to add to the menu if(InSettingsData.RootClass == AGameModeBase::StaticClass()) { FToolMenuSection& GameModeClassesSection = Menu->AddSection("GameModeClasses", LOCTEXT("GameModeClasses", "Game Mode Classes")); if(InSettingsData.CurrentClass) { GetGameModeSubMenu(GameModeClassesSection, InSettingsData); } } #undef LOCTEXT_NAMESPACE } UClass* LevelEditorActionHelpers::GetGameModeClass(TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings) { UClass* GameModeClass = nullptr; if(bInIsProjectSettings) { UObject* GameModeObject = LoadObject(nullptr, *UGameMapsSettings::GetGlobalDefaultGameMode()); if(UBlueprint* GameModeAsBlueprint = Cast(GameModeObject)) { GameModeClass = GameModeAsBlueprint->GeneratedClass; } else { GameModeClass = FindObject(nullptr, *UGameMapsSettings::GetGlobalDefaultGameMode()); } } else { AWorldSettings* WorldSettings = InLevelEditor.Pin()->GetWorld()->GetWorldSettings(); if(WorldSettings->DefaultGameMode) { GameModeClass = WorldSettings->DefaultGameMode; } } return GameModeClass; } FText LevelEditorActionHelpers::GetOpenGameModeBlueprintLabel(TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings) { #define LOCTEXT_NAMESPACE "LevelToolBarViewMenu" if(UClass* GameModeClass = GetGameModeClass(InLevelEditor, bInIsProjectSettings)) { if(GameModeClass->ClassGeneratedBy) { return FText::Format( LOCTEXT("GameModeEditBlueprint", "GameMode: Edit {0}"), FText::FromString(GameModeClass->ClassGeneratedBy->GetName())); } return FText::Format( LOCTEXT("GameModeBlueprint", "GameMode: {0}"), FText::FromString(GameModeClass->GetName())); } if(bInIsProjectSettings) { return LOCTEXT("GameModeCreateBlueprint", "GameMode: New..."); } // For World Settings, we want to inform the user that they are not overridding the Project Settings return LOCTEXT("GameModeNotOverridden", "GameMode: Not overridden!"); #undef LOCTEXT_NAMESPACE } void LevelEditorActionHelpers::OnCreateGameModeClassPicked(UClass* InChosenClass, TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings) { if(InChosenClass) { const FString NewBPName(TEXT("NewGameMode")); UBlueprint* Blueprint = FKismetEditorUtilities::CreateBlueprintFromClass(NSLOCTEXT("LevelEditorCommands", "CreateGameModeBlueprint_Title", "Create GameMode Blueprint"), InChosenClass, NewBPName); if(Blueprint) { // @todo Re-enable once world centric works const bool bOpenWorldCentric = false; GEditor->GetEditorSubsystem()->OpenEditorForAsset( Blueprint, bOpenWorldCentric ? EToolkitMode::WorldCentric : EToolkitMode::Standalone, InLevelEditor.Pin() ); OnSelectGameModeClassPicked(Blueprint->GeneratedClass, InLevelEditor, bInIsProjectSettings); } } FSlateApplication::Get().DismissAllMenus(); } void LevelEditorActionHelpers::OnSelectGameModeClassPicked(UClass* InChosenClass, TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings) { if(bInIsProjectSettings) { UGameMapsSettings::SetGlobalDefaultGameMode(InChosenClass? InChosenClass->GetPathName() : FString()); ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings"); if (SettingsModule != nullptr) { ISettingsContainerPtr SettingsContainer = SettingsModule->GetContainer("Project"); if (SettingsContainer.IsValid()) { ISettingsCategoryPtr SettingsCategory = SettingsContainer->GetCategory("Project"); if(SettingsCategory.IsValid()) { SettingsCategory->GetSection("Maps")->Save(); } } } } else { const FScopedTransaction Transaction( NSLOCTEXT("LevelEditorCommands", "SelectGameModeClassAction", "Set Override Game Mode Class") ); AWorldSettings* WorldSettings = InLevelEditor.Pin()->GetWorld()->GetWorldSettings(); WorldSettings->Modify(); WorldSettings->DefaultGameMode = InChosenClass; } FSlateApplication::Get().DismissAllMenus(); } UClass* LevelEditorActionHelpers::GetGameStateClass(TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings) { if(UClass* GameModeClass = GetGameModeClass(InLevelEditor, bInIsProjectSettings)) { AGameModeBase* ActiveGameMode = Cast(GameModeClass->GetDefaultObject()); if(ActiveGameMode) { return ActiveGameMode->GameStateClass; } } return NULL; } FText LevelEditorActionHelpers::GetOpenGameStateBlueprintLabel(TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings) { #define LOCTEXT_NAMESPACE "LevelToolBarViewMenu" if(UClass* GameStateClass = GetGameStateClass(InLevelEditor, bInIsProjectSettings)) { FFormatNamedArguments FormatArgs; if(GameStateClass->ClassGeneratedBy) { FormatArgs.Add(TEXT("GameStateName"), FText::FromString(GameStateClass->ClassGeneratedBy->GetName())); return FText::Format(LOCTEXT("GameStateEditBlueprint", "GameState: Edit {GameStateName}"), FormatArgs); } FormatArgs.Add(TEXT("GameStateName"), FText::FromString(GameStateClass->GetName())); return FText::Format(LOCTEXT("GameStateBlueprint", "GameState: {GameStateName}"), FormatArgs); } return LOCTEXT("GameStateCreateBlueprint", "GameState: New..."); #undef LOCTEXT_NAMESPACE } void LevelEditorActionHelpers::OnCreateGameStateClassPicked(UClass* InChosenClass, TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings) { if(InChosenClass) { const FString NewBPName(TEXT("NewGameState")); UBlueprint* Blueprint = FKismetEditorUtilities::CreateBlueprintFromClass(NSLOCTEXT("LevelEditorCommands", "CreateGameStateBlueprint_Title", "Create GameState Blueprint"), InChosenClass, NewBPName); if(Blueprint) { // @todo Re-enable once world centric works const bool bOpenWorldCentric = false; GEditor->GetEditorSubsystem()->OpenEditorForAsset( Blueprint, bOpenWorldCentric ? EToolkitMode::WorldCentric : EToolkitMode::Standalone, InLevelEditor.Pin() ); OnSelectGameStateClassPicked(Blueprint->GeneratedClass, InLevelEditor, bInIsProjectSettings); } } FSlateApplication::Get().DismissAllMenus(); } void LevelEditorActionHelpers::OnSelectGameStateClassPicked(UClass* InChosenClass, TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings) { if(UClass* GameModeClass = GetGameModeClass(InLevelEditor, bInIsProjectSettings)) { const FScopedTransaction Transaction( NSLOCTEXT("LevelEditorCommands", "SelectGameStateClassAction", "Set Game State Class") ); AGameModeBase* ActiveGameMode = Cast(GameModeClass->GetDefaultObject()); ActiveGameMode->GameStateClass = InChosenClass; UBlueprint* Blueprint = Cast(GameModeClass->ClassGeneratedBy); if (ensure(Blueprint)) { FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint); } } FSlateApplication::Get().DismissAllMenus(); } UClass* LevelEditorActionHelpers::GetPawnClass(TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings) { if(UClass* GameModeClass = GetGameModeClass(InLevelEditor, bInIsProjectSettings)) { AGameModeBase* ActiveGameMode = Cast(GameModeClass->GetDefaultObject()); if(ActiveGameMode) { return ActiveGameMode->DefaultPawnClass; } } return NULL; } FText LevelEditorActionHelpers::GetOpenPawnBlueprintLabel(TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings) { #define LOCTEXT_NAMESPACE "LevelToolBarViewMenu" if(UClass* PawnClass = GetPawnClass(InLevelEditor, bInIsProjectSettings)) { FFormatNamedArguments FormatArgs; if(PawnClass->ClassGeneratedBy) { FormatArgs.Add(TEXT("PawnName"), FText::FromString(PawnClass->ClassGeneratedBy->GetName())); return FText::Format(LOCTEXT("PawnEditBlueprint", "Pawn: Edit {PawnName}"), FormatArgs); } FormatArgs.Add(TEXT("PawnName"), FText::FromString(PawnClass->GetName())); return FText::Format(LOCTEXT("PawnBlueprint", "Pawn: {PawnName}"), FormatArgs); } return LOCTEXT("PawnCreateBlueprint", "Pawn: New..."); #undef LOCTEXT_NAMESPACE } void LevelEditorActionHelpers::OnCreatePawnClassPicked(UClass* InChosenClass, TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings) { if(InChosenClass) { const FString NewBPName(TEXT("NewPawn")); UBlueprint* Blueprint = FKismetEditorUtilities::CreateBlueprintFromClass(NSLOCTEXT("LevelEditorCommands", "CreatePawnBlueprint_Title", "Create Pawn Blueprint"), InChosenClass, NewBPName); if(Blueprint) { // @todo Re-enable once world centric works const bool bOpenWorldCentric = false; GEditor->GetEditorSubsystem()->OpenEditorForAsset( Blueprint, bOpenWorldCentric ? EToolkitMode::WorldCentric : EToolkitMode::Standalone, InLevelEditor.Pin() ); OnSelectPawnClassPicked(Blueprint->GeneratedClass, InLevelEditor, bInIsProjectSettings); } } FSlateApplication::Get().DismissAllMenus(); } void LevelEditorActionHelpers::OnSelectPawnClassPicked(UClass* InChosenClass, TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings) { if(UClass* GameModeClass = GetGameModeClass(InLevelEditor, bInIsProjectSettings)) { const FScopedTransaction Transaction( NSLOCTEXT("LevelEditorCommands", "SelectPawnClassAction", "Set Pawn Class") ); AGameModeBase* ActiveGameMode = Cast(GameModeClass->GetDefaultObject()); ActiveGameMode->DefaultPawnClass = InChosenClass; UBlueprint* Blueprint = Cast(GameModeClass->ClassGeneratedBy); if (ensure(Blueprint)) { FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint); } } FSlateApplication::Get().DismissAllMenus(); } UClass* LevelEditorActionHelpers::GetHUDClass(TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings) { if(UClass* GameModeClass = GetGameModeClass(InLevelEditor, bInIsProjectSettings)) { AGameModeBase* ActiveGameMode = Cast(GameModeClass->GetDefaultObject()); if(ActiveGameMode) { return ActiveGameMode->HUDClass; } } return NULL; } FText LevelEditorActionHelpers::GetOpenHUDBlueprintLabel(TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings) { #define LOCTEXT_NAMESPACE "LevelToolBarViewMenu" if(UClass* HUDClass = GetHUDClass(InLevelEditor, bInIsProjectSettings)) { FFormatNamedArguments FormatArgs; if (HUDClass->ClassGeneratedBy) { FormatArgs.Add(TEXT("HUDName"), FText::FromString(HUDClass->ClassGeneratedBy->GetName())); return FText::Format(LOCTEXT("HUDEditBlueprint", "HUD: Edit {HUDName}"), FormatArgs); } FormatArgs.Add(TEXT("HUDName"), FText::FromString(HUDClass->GetName())); return FText::Format(LOCTEXT("HUDBlueprint", "HUD: {HUDName}"), FormatArgs); } return LOCTEXT("HUDCreateBlueprint", "HUD: New..."); #undef LOCTEXT_NAMESPACE } void LevelEditorActionHelpers::OnCreateHUDClassPicked(UClass* InChosenClass, TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings) { if(InChosenClass) { const FString NewBPName(TEXT("NewHUD")); UBlueprint* Blueprint = FKismetEditorUtilities::CreateBlueprintFromClass(NSLOCTEXT("LevelEditorCommands", "CreateHUDBlueprint_Title", "Create HUD Blueprint"), InChosenClass, NewBPName); if(Blueprint) { // @todo Re-enable once world centric works const bool bOpenWorldCentric = false; GEditor->GetEditorSubsystem()->OpenEditorForAsset( Blueprint, bOpenWorldCentric ? EToolkitMode::WorldCentric : EToolkitMode::Standalone, InLevelEditor.Pin() ); OnSelectHUDClassPicked(Blueprint->GeneratedClass, InLevelEditor, bInIsProjectSettings); } } FSlateApplication::Get().DismissAllMenus(); } void LevelEditorActionHelpers::OnSelectHUDClassPicked(UClass* InChosenClass, TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings) { if(UClass* GameModeClass = GetGameModeClass(InLevelEditor, bInIsProjectSettings)) { const FScopedTransaction Transaction( NSLOCTEXT("LevelEditorCommands", "SelectHUDClassAction", "Set HUD Class") ); AGameModeBase* ActiveGameMode = Cast(GameModeClass->GetDefaultObject()); ActiveGameMode->HUDClass = InChosenClass; UBlueprint* Blueprint = Cast(GameModeClass->ClassGeneratedBy); if (ensure(Blueprint)) { FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint); } } FSlateApplication::Get().DismissAllMenus(); } UClass* LevelEditorActionHelpers::GetPlayerControllerClass(TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings) { if(UClass* GameModeClass = GetGameModeClass(InLevelEditor, bInIsProjectSettings)) { AGameModeBase* ActiveGameMode = Cast(GameModeClass->GetDefaultObject()); if(ActiveGameMode) { return ActiveGameMode->PlayerControllerClass; } } return NULL; } FText LevelEditorActionHelpers::GetOpenPlayerControllerBlueprintLabel(TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings) { #define LOCTEXT_NAMESPACE "LevelToolBarViewMenu" if(UClass* PlayerControllerClass = GetPlayerControllerClass(InLevelEditor, bInIsProjectSettings)) { FFormatNamedArguments FormatArgs; if (PlayerControllerClass->ClassGeneratedBy) { FormatArgs.Add(TEXT("PlayerControllerName"), FText::FromString(PlayerControllerClass->ClassGeneratedBy->GetName())); return FText::Format(LOCTEXT("PlayerControllerEditBlueprint", "PlayerController: Edit {PlayerControllerName}"), FormatArgs); } FormatArgs.Add(TEXT("PlayerControllerName"), FText::FromString(PlayerControllerClass->GetName())); return FText::Format(LOCTEXT("PlayerControllerBlueprint", "PlayerController: {PlayerControllerName}"), FormatArgs); } return LOCTEXT("PlayerControllerCreateBlueprint", "PlayerController: New..."); #undef LOCTEXT_NAMESPACE } void LevelEditorActionHelpers::OnCreatePlayerControllerClassPicked(UClass* InChosenClass, TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings) { if(InChosenClass) { const FString NewBPName(TEXT("NewPlayerController")); UBlueprint* Blueprint = FKismetEditorUtilities::CreateBlueprintFromClass(NSLOCTEXT("LevelEditorCommands", "CreatePlayerControllerBlueprint_Title", "Create PlayerController Blueprint"), InChosenClass, NewBPName); if(Blueprint) { // @todo Re-enable once world centric works const bool bOpenWorldCentric = false; GEditor->GetEditorSubsystem()->OpenEditorForAsset( Blueprint, bOpenWorldCentric ? EToolkitMode::WorldCentric : EToolkitMode::Standalone, InLevelEditor.Pin() ); OnSelectPlayerControllerClassPicked(Blueprint->GeneratedClass, InLevelEditor, bInIsProjectSettings); } } FSlateApplication::Get().DismissAllMenus(); } void LevelEditorActionHelpers::OnSelectPlayerControllerClassPicked(UClass* InChosenClass, TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings) { if(UClass* GameModeClass = GetGameModeClass(InLevelEditor, bInIsProjectSettings)) { const FScopedTransaction Transaction( NSLOCTEXT("LevelEditorCommands", "SelectPlayerControllerClassAction", "Set Player Controller Class") ); AGameModeBase* ActiveGameMode = Cast(GameModeClass->GetDefaultObject()); ActiveGameMode->PlayerControllerClass = InChosenClass; UBlueprint* Blueprint = Cast(GameModeClass->ClassGeneratedBy); if (ensure(Blueprint)) { FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint); } } FSlateApplication::Get().DismissAllMenus(); } FText FLevelEditorToolBar::GetActiveModeName(TWeakPtr LevelEditorPtr) { #define LOCTEXT_NAMESPACE "LevelEditorToolBar" for (const FEditorModeInfo& Mode : GEditor->GetEditorSubsystem()->GetEditorModeInfoOrderedByPriority()) { TSharedPtr LevelEditorPin = LevelEditorPtr.Pin(); if (LevelEditorPin.IsValid() && LevelEditorPin->GetEditorModeManager().IsModeActive(Mode.ID) && Mode.IsVisible()) { return FText::Format(LOCTEXT("ActiveMode", "{0} Mode"), Mode.Name); } } return LOCTEXT("NoActiveMode", "No Active Mode"); #undef LOCTEXT_NAMESPACE } const FSlateBrush* FLevelEditorToolBar::GetActiveModeIcon(TWeakPtr LevelEditorPtr) { for (const FEditorModeInfo& Mode : GEditor->GetEditorSubsystem()->GetEditorModeInfoOrderedByPriority()) { TSharedPtr LevelEditorPin = LevelEditorPtr.Pin(); if (LevelEditorPin.IsValid() && LevelEditorPin->GetEditorModeManager().IsModeActive(Mode.ID) && Mode.IsVisible()) { return Mode.IconBrush.GetIcon(); } } return nullptr; } void FLevelEditorToolBar::RegisterLevelEditorToolBar( const TSharedRef& InCommandList, const TSharedRef InLevelEditor) { static bool bHasRegistered = false; if (!bHasRegistered) { bHasRegistered = true; RegisterSourceControlMenu(); RegisterCinematicsMenu(); RegisterQuickSettingsMenu(); RegisterOpenBlueprintMenu(); RegisterAddMenu(); } #define LOCTEXT_NAMESPACE "LevelEditorToolBar" UToolMenu* ModesToolbar = UToolMenus::Get()->RegisterMenu("LevelEditor.LevelEditorToolBar.ModesToolBar", NAME_None, EMultiBoxType::SlimHorizontalToolBar, /*warn*/false); ModesToolbar->StyleName = "AssetEditorToolbar"; { { FToolMenuSection& Section = ModesToolbar->AddSection("File"); // Save All Levels Section.AddEntry(FToolMenuEntry::InitToolBarButton( FLevelEditorCommands::Get().Save, TAttribute(), TAttribute(), FSlateIcon(FAppStyle::GetAppStyleSetName(), "AssetEditor.SaveAsset"), NAME_None, FName("SaveAllLevels") )); // Browse Level Section.AddEntry(FToolMenuEntry::InitToolBarButton( FLevelEditorCommands::Get().BrowseLevel, TAttribute(), TAttribute(), FSlateIcon(FAppStyle::GetAppStyleSetName(), "SystemWideCommands.FindInContentBrowser") )); } TWeakPtr LevelEditorPtr = InLevelEditor; ModesToolbar->AddDynamicSection("EditorModes", FNewToolMenuDelegate::CreateLambda([LevelEditorPtr](UToolMenu* ToolMenu) { FToolMenuSection& Section = ToolMenu->AddSection("EditorModes"); // Combo Button to swap editor modes TSharedRef EditorModesComboButton = SNew(SComboButton) .OnGetMenuContent_Lambda([LevelEditorPtr]() { const FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked("LevelEditor"); const FLevelEditorModesCommands& Commands = LevelEditorModule.GetLevelEditorModesCommands(); TArray DefaultModes; TArray NonDefaultModes; TArray> CommandInfos; for (const FEditorModeInfo& Mode : GEditor->GetEditorSubsystem()->GetEditorModeInfoOrderedByPriority()) { TSharedPtr LevelEditorPin = LevelEditorPtr.Pin(); if (LevelEditorPin.IsValid() && LevelEditorPin->GetEditorModeManager().IsDefaultMode(Mode.ID)) { DefaultModes.Add(Mode); } else { NonDefaultModes.Add(Mode); } } auto GetCommandForModes = [&CommandInfos, &Commands](TArrayView Modes) { for (const FEditorModeInfo& Mode : Modes) { FName EditorModeCommandName = FName(*(FString("EditorMode.") + Mode.ID.ToString())); TSharedPtr EditorModeCommand = FInputBindingManager::Get().FindCommandInContext(Commands.GetContextName(), EditorModeCommandName); if (Mode.IsVisible()) { CommandInfos.Add(EditorModeCommand); } } }; // Default Modes first GetCommandForModes(DefaultModes); GetCommandForModes(NonDefaultModes); FMenuBuilder MenuBuilder(true, LevelEditorModule.GetGlobalLevelEditorActions()); TSharedPtr LevelEditorPin = LevelEditorPtr.Pin(); if (LevelEditorPin.IsValid()) { MenuBuilder.PushCommandList(LevelEditorPin->GetLevelEditorActions().ToSharedRef()); } MenuBuilder.BeginSection("EditorModes"); for (TSharedPtr Command : CommandInfos) { MenuBuilder.AddMenuEntry(Command); } MenuBuilder.EndSection(); return MenuBuilder.MakeWidget(); }) .ButtonContent() [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .AutoWidth() .Padding(FMargin(0.f, 0.f, 6.f, 0.f)) [ SNew(SBox) .WidthOverride(16.f) .HeightOverride(16.f) [ SNew(SImage) .Image_Static(&FLevelEditorToolBar::GetActiveModeIcon, LevelEditorPtr) ] ] + SHorizontalBox::Slot() [ SNew(STextBlock) .Text_Static(&FLevelEditorToolBar::GetActiveModeName, LevelEditorPtr) ] ]; // Horizontal Box to add some spacing beside the modes combo button TSharedRef EditorModesWidget = SNew(SHorizontalBox) + SHorizontalBox::Slot() .FillWidth(1.0) [ SNew(SSpacer) .Size(FVector2D(10.f, 1.0f)) ] + SHorizontalBox::Slot() .AutoWidth() [ EditorModesComboButton ] + SHorizontalBox::Slot() .FillWidth(1.0) [ SNew(SSpacer) .Size(FVector2D(10.f, 1.0f)) ]; Section.AddEntry(FToolMenuEntry::InitWidget("Editor Modes", EditorModesWidget, LOCTEXT("EditorModesLabel", "Editor Modes"))); Section.AddSeparator(NAME_None); })); } UToolMenu* AssetsToolBar = UToolMenus::Get()->RegisterMenu("LevelEditor.LevelEditorToolBar.AssetsToolBar", NAME_None, EMultiBoxType::SlimHorizontalToolBar, false); AssetsToolBar->StyleName = "AssetEditorToolbar"; { { FToolMenuSection& Section = AssetsToolBar->AddSection("Content"); FToolMenuEntry AddContentEntry = FToolMenuEntry::InitComboButton( "AddContent", FUIAction(), FOnGetContent::CreateStatic(&FLevelEditorToolBar::GenerateAddMenuWidget, InCommandList, TWeakPtr(InLevelEditor)), LOCTEXT("AddContent_Label", "Add"), LOCTEXT("AddContent_Tooltip", "Quickly add to the project."), FSlateIcon(FAppStyle::Get().GetStyleSetName(), "LevelEditor.OpenAddContent.Background", NAME_None, "LevelEditor.OpenAddContent.Overlay") ); AddContentEntry.StyleNameOverride = "AssetEditorToolbar"; Section.AddEntry(AddContentEntry); FToolMenuEntry BlueprintEntry = FToolMenuEntry::InitComboButton( "OpenBlueprint", FUIAction(), FOnGetContent::CreateStatic(&FLevelEditorToolBar::GenerateOpenBlueprintMenuContent, InCommandList, TWeakPtr(InLevelEditor)), LOCTEXT("OpenBlueprint_Label", "Blueprints"), LOCTEXT("OpenBlueprint_ToolTip", "List of world Blueprints available to the user for editing or creation."), FSlateIcon(FAppStyle::Get().GetStyleSetName(), "LevelEditor.CreateBlankBlueprintClass") ); BlueprintEntry.StyleNameOverride = "AssetEditorToolbar"; Section.AddEntry(BlueprintEntry); FToolMenuEntry CinematicsEntry = FToolMenuEntry::InitComboButton( "EditCinematics", FUIAction(), FOnGetContent::CreateStatic(&FLevelEditorToolBar::GenerateCinematicsMenuContent, InCommandList, TWeakPtr(InLevelEditor)), LOCTEXT("EditCinematics_Label", "Cinematics"), LOCTEXT("EditCinematics_Tooltip", "Displays a list of Level Sequence objects to open in their respective editors"), FSlateIcon(FAppStyle::Get().GetStyleSetName(), "LevelEditor.OpenCinematic") ); CinematicsEntry.StyleNameOverride = "AssetEditorToolbar"; Section.AddEntry(CinematicsEntry); } } UToolMenu* PlayToolBar = UToolMenus::Get()->RegisterMenu("LevelEditor.LevelEditorToolBar.PlayToolBar", NAME_None, EMultiBoxType::SlimHorizontalToolBar, false); PlayToolBar->StyleName = "AssetEditorToolbar"; { FToolMenuSection& PlaySection = PlayToolBar->AddSection("Play"); PlaySection.AddSeparator(NAME_None); PreviewModeFunctionality::AddPreviewToggleButton(PlaySection); // Add the shared play-world commands that will be shown on the Kismet toolbar as well FPlayWorldCommands::BuildToolbar(PlaySection, true); } UToolMenu* UserToolbar = UToolMenus::Get()->RegisterMenu("LevelEditor.LevelEditorToolBar.User", NAME_None, EMultiBoxType::SlimHorizontalToolBar, false); UserToolbar->StyleName = "AssetEditorToolbar"; UToolMenu* SettingsToolbar = UToolMenus::Get()->RegisterMenu("LevelEditor.LevelEditorToolBar.SettingsToolbar", NAME_None, EMultiBoxType::SlimHorizontalToolBar, false); SettingsToolbar->StyleName = "AssetEditorToolbar"; { FToolMenuSection& SettingsSection = SettingsToolbar->AddSection("ProjectSettings"); FUIAction Action; Action.IsActionVisibleDelegate = FIsActionButtonVisible::CreateLambda( []() -> bool { return UE::Editor::Private::CVarLevelEditorToolbarSettingsValue != 0; } ); FToolMenuEntry SettingsEntry = FToolMenuEntry::InitComboButton( "LevelToolbarQuickSettings", Action, FOnGetContent::CreateStatic( &FLevelEditorToolBar::GenerateQuickSettingsMenu, InCommandList, TWeakPtr(InLevelEditor) ), LOCTEXT("QuickSettingsCombo", "Settings"), LOCTEXT("QuickSettingsCombo_ToolTip", "Project and Editor settings"), FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.GameSettings"), false, "LevelToolbarQuickSettings" ); SettingsEntry.StyleNameOverride = "CalloutToolbar"; SettingsSection.AddEntry(SettingsEntry); } #undef LOCTEXT_NAMESPACE } FName FLevelEditorToolBar::GetSecondaryModeToolbarName() { return SecondaryModeToolbarName; } TSharedRef< SWidget > FLevelEditorToolBar::MakeLevelEditorSecondaryModeToolbar( TSharedRef InCommandList, TMap>& ModeUILayers ) { FToolMenuContext MenuContext(InCommandList); for(const TPair>& ModeUILayer : ModeUILayers) { MenuContext.AppendCommandList(ModeUILayer.Value->GetModeCommands()); } return SNew(SBorder) .Padding(0.f) .BorderImage(FAppStyle::Get().GetBrush("NoBorder")) .IsEnabled(FSlateApplication::Get().GetNormalExecutionAttribute()) [ UToolMenus::Get()->GenerateWidget(SecondaryModeToolbarName, MenuContext) ]; } void FLevelEditorToolBar::RegisterLevelEditorSecondaryModeToolbar() { UToolMenu* ModesToolbar = UToolMenus::Get()->RegisterMenu(SecondaryModeToolbarName, NAME_None, EMultiBoxType::SlimHorizontalToolBar, false); ModesToolbar->StyleName = "SecondaryToolbar"; } /** * Static: Creates a widget for the level editor tool bar * * @return New widget */ TSharedRef< SWidget > FLevelEditorToolBar::MakeLevelEditorToolBar( const TSharedRef& InCommandList, const TSharedRef InLevelEditor ) { FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked("LevelEditor"); FToolMenuContext MenuContext(InCommandList, LevelEditorModule.GetToolBarExtensibilityManager()->GetAllExtenders()); ULevelEditorMenuContext* LevelEditorMenuContext = NewObject(); LevelEditorMenuContext->LevelEditor = InLevelEditor; MenuContext.AddObject(LevelEditorMenuContext); TWeakPtr LevelEditorWeakPtr(InLevelEditor); // Create the tool bar! return SNew(SOverlay) +SOverlay::Slot() [ SNew(SImage) .Image(&FAppStyle::Get().GetWidgetStyle("SlimToolBar").BackgroundBrush) ] + SOverlay::Slot() [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .AutoWidth() [ SNew(SBorder) .Padding(0.f) .BorderImage(FAppStyle::Get().GetBrush("NoBorder")) .IsEnabled(FSlateApplication::Get().GetNormalExecutionAttribute()) [ UToolMenus::Get()->GenerateWidget("LevelEditor.LevelEditorToolBar.ModesToolBar", MenuContext) ] ] + SHorizontalBox::Slot() .AutoWidth() [ SNew(SBorder) .Padding(0.f) .BorderImage(FAppStyle::Get().GetBrush("NoBorder")) .IsEnabled(FSlateApplication::Get().GetNormalExecutionAttribute()) [ UToolMenus::Get()->GenerateWidget("LevelEditor.LevelEditorToolBar.AssetsToolBar", MenuContext) ] ] + SHorizontalBox::Slot() .HAlign(HAlign_Left) .AutoWidth() [ SNew(SBorder) .Padding(0.f) .BorderImage(FAppStyle::Get().GetBrush("NoBorder")) [ UToolMenus::Get()->GenerateWidget("LevelEditor.LevelEditorToolBar.PlayToolBar", MenuContext) // Always enabled ] ] + SHorizontalBox::Slot() .HAlign(HAlign_Fill) .FillWidth(1.0f) [ SNew(SBorder) .Padding(0.f) .BorderImage(FAppStyle::Get().GetBrush("NoBorder")) .IsEnabled(FSlateApplication::Get().GetNormalExecutionAttribute()) [ UToolMenus::Get()->GenerateWidget("LevelEditor.LevelEditorToolBar.User", MenuContext) ] ] + SHorizontalBox::Slot() .HAlign(HAlign_Right) .AutoWidth() .Padding(0.0f, 0.0f, 7.0f, 0.0f) [ SNew(SBorder) .Visibility_Lambda([] { return UE::Editor::Private::CVarLevelEditorToolbarSettingsValue != 0 ? EVisibility::Visible : EVisibility::Collapsed; }) .Padding(0.f) .BorderImage(FAppStyle::Get().GetBrush("NoBorder")) .IsEnabled(FSlateApplication::Get().GetNormalExecutionAttribute()) [ UToolMenus::Get()->GenerateWidget("LevelEditor.LevelEditorToolBar.SettingsToolBar", MenuContext) ] ] ]; } static void MakeScalabilityMenu( UToolMenu* InMenu ) { { FToolMenuSection& Section = InMenu->AddSection("Section"); Section.AddEntry(FToolMenuEntry::InitWidget("ScalabilitySettings", SNew(SScalabilitySettings), FText(), true)); } } static void MakePreviewSettingsMenu( UToolMenu* InMenu ) { #define LOCTEXT_NAMESPACE "LevelToolBarViewMenu" { FToolMenuSection& Section = InMenu->AddSection("LevelEditorPreview", LOCTEXT("PreviewHeading", "Previewing")); Section.AddMenuEntry(FLevelEditorCommands::Get().DrawBrushMarkerPolys); Section.AddMenuEntry(FLevelEditorCommands::Get().OnlyLoadVisibleInPIE); Section.AddMenuEntry(FLevelEditorCommands::Get().ToggleParticleSystemLOD); Section.AddMenuEntry(FLevelEditorCommands::Get().ToggleParticleSystemHelpers); Section.AddMenuEntry(FLevelEditorCommands::Get().ToggleFreezeParticleSimulation); Section.AddMenuEntry(FLevelEditorCommands::Get().ToggleLODViewLocking); Section.AddMenuEntry(FLevelEditorCommands::Get().LevelStreamingVolumePrevis); } #undef LOCTEXT_NAMESPACE } TSharedRef< SWidget > FLevelEditorToolBar::GenerateQuickSettingsMenu(TSharedRef InCommandList, TWeakPtr InLevelEditor) { // Get all menu extenders for this context menu from the level editor module FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked( TEXT("LevelEditor") ); TSharedPtr MenuExtender = LevelEditorModule.AssembleExtenders(InCommandList, LevelEditorModule.GetAllLevelEditorToolbarViewMenuExtenders()); FToolMenuContext MenuContext(InCommandList, MenuExtender); ULevelEditorMenuContext* LevelEditorMenuContext = NewObject(); LevelEditorMenuContext->LevelEditor = InLevelEditor; MenuContext.AddObject(LevelEditorMenuContext); return UToolMenus::Get()->GenerateWidget("LevelEditor.LevelEditorToolBar.LevelToolbarQuickSettings", MenuContext); } void FLevelEditorToolBar::RegisterQuickSettingsMenu() { #define LOCTEXT_NAMESPACE "LevelToolBarViewMenu" if (UToolMenus::Get()->IsMenuRegistered("LevelEditor.LevelEditorToolBar.LevelToolbarQuickSettings")) { return; } UToolMenu* Menu = UToolMenus::Get()->RegisterMenu("LevelEditor.LevelEditorToolBar.LevelToolbarQuickSettings"); struct Local { static void OpenSettings(FName ContainerName, FName CategoryName, FName SectionName) { FModuleManager::LoadModuleChecked("Settings").ShowViewer(ContainerName, CategoryName, SectionName); } }; { FToolMenuSection& Section = Menu->AddSection("ProjectSettingsSection", LOCTEXT("ProjectSettings", "Game Specific Settings")); Section.AddMenuEntry(FLevelEditorCommands::Get().WorldProperties); Section.AddMenuEntry( "ProjectSettings", LOCTEXT("ProjectSettingsMenuLabel", "Project Settings..."), LOCTEXT("ProjectSettingsMenuToolTip", "Change the settings of the currently loaded project"), FSlateIcon(FAppStyle::GetAppStyleSetName(), "ProjectSettings.TabIcon"), FUIAction(FExecuteAction::CreateStatic(&Local::OpenSettings, FName("Project"), FName("Project"), FName("General"))) ); Section.AddDynamicEntry("PluginsEditor", FNewToolMenuDelegateLegacy::CreateLambda([](FMenuBuilder& InMenuBuilder, UToolMenu* InMenu) { if (IModularFeatures::Get().IsModularFeatureAvailable(EditorFeatures::PluginsEditor)) { FGlobalTabmanager::Get()->PopulateTabSpawnerMenu(InMenuBuilder, "PluginsEditor"); } })); } { FToolMenuSection& Section = Menu->AddSection("LevelEditorSelection", LOCTEXT("SelectionHeading","Selection") ); Section.AddMenuEntry( FLevelEditorCommands::Get().AllowTranslucentSelection ); Section.AddMenuEntry( FLevelEditorCommands::Get().AllowGroupSelection ); Section.AddMenuEntry( FLevelEditorCommands::Get().StrictBoxSelect ); Section.AddMenuEntry( FLevelEditorCommands::Get().TransparentBoxSelect ); Section.AddMenuEntry( FLevelEditorCommands::Get().ShowTransformWidget ); Section.AddMenuEntry( FLevelEditorCommands::Get().ShowSelectionSubcomponents ); } { FToolMenuSection& Section = Menu->AddSection("LevelEditorScalability", LOCTEXT("ScalabilityHeading", "Scalability") ); Section.AddSubMenu( "Scalability", LOCTEXT( "ScalabilitySubMenu", "Engine Scalability Settings" ), LOCTEXT( "ScalabilitySubMenu_ToolTip", "Open the engine scalability settings" ), FNewToolMenuDelegate::CreateStatic( &MakeScalabilityMenu ) ); Section.AddEntry(UE::LevelEditor::CreateMaterialQualityLevelSubmenu()); Section.AddEntry(UE::LevelEditor::CreateFeatureLevelPreviewSubmenu()); } { FToolMenuSection& Section = Menu->AddSection( "Snapping", LOCTEXT("SnappingHeading","Snapping") ); Section.AddMenuEntry( FLevelEditorCommands::Get().EnableActorSnap ); TSharedRef SnapItem = SNew(SHorizontalBox) +SHorizontalBox::Slot() .FillWidth(0.9f) [ SNew(SSlider) .ToolTipText_Static(&FLevelEditorActionCallbacks::GetActorSnapTooltip) .Value_Static(&FLevelEditorActionCallbacks::GetActorSnapSetting) .OnValueChanged_Static(&FLevelEditorActionCallbacks::SetActorSnapSetting) ] +SHorizontalBox::Slot() .FillWidth(0.1f); Section.AddEntry(FToolMenuEntry::InitWidget("Snap", SnapItem, LOCTEXT("ActorSnapLabel", "Distance"))); Section.AddMenuEntry( FLevelEditorCommands::Get().ToggleSocketSnapping ); Section.AddMenuEntry( FLevelEditorCommands::Get().EnableVertexSnap ); } { FToolMenuSection& Section = Menu->AddSection("LevelEditorViewport", LOCTEXT("ViewportHeading", "Viewport") ); Section.AddMenuEntry( FLevelEditorCommands::Get().ToggleHideViewportUI ); Section.AddSubMenu( "Preview", LOCTEXT("PreviewMenu", "Previewing"), LOCTEXT("PreviewMenuTooltip","Game Preview Settings"), FNewToolMenuDelegate::CreateStatic( &MakePreviewSettingsMenu ) ); } #undef LOCTEXT_NAMESPACE } TSharedRef< SWidget > FLevelEditorToolBar::GenerateSourceControlMenu(TSharedRef InCommandList, TWeakPtr< SLevelEditor > InLevelEditor) { // Get all menu extenders for this context menu from the level editor module FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked(TEXT("LevelEditor")); TSharedPtr MenuExtender = LevelEditorModule.AssembleExtenders(InCommandList, LevelEditorModule.GetAllLevelEditorToolbarSourceControlMenuExtenders()); FToolMenuContext MenuContext(InCommandList, MenuExtender); ULevelEditorMenuContext* LevelEditorMenuContext = NewObject(); LevelEditorMenuContext->LevelEditor = InLevelEditor; MenuContext.AddObject(LevelEditorMenuContext); return UToolMenus::Get()->GenerateWidget("LevelEditor.LevelEditorToolBar.SourceControl", MenuContext); } void FLevelEditorToolBar::RegisterSourceControlMenu() { #define LOCTEXT_NAMESPACE "LevelToolBarSourceControlMenu" #undef LOCTEXT_NAMESPACE } TSharedRef< SWidget > FLevelEditorToolBar::GenerateOpenBlueprintMenuContent( TSharedRef InCommandList, TWeakPtr< SLevelEditor > InLevelEditor ) { #define LOCTEXT_NAMESPACE "LevelToolBarViewMenu" FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked("LevelEditor"); TSharedPtr MenuExtender = FExtender::Combine(LevelEditorModule.GetAllLevelEditorToolbarBlueprintsMenuExtenders()); FToolMenuContext MenuContext(InCommandList, MenuExtender); ULevelEditorMenuContext* LevelEditorMenuContext = NewObject(); LevelEditorMenuContext->LevelEditor = InLevelEditor; MenuContext.AddObject(LevelEditorMenuContext); return UToolMenus::Get()->GenerateWidget("LevelEditor.LevelEditorToolBar.OpenBlueprint", MenuContext); #undef LOCTEXT_NAMESPACE } void FLevelEditorToolBar::RegisterOpenBlueprintMenu() { #define LOCTEXT_NAMESPACE "LevelToolBarViewMenu" if (UToolMenus::Get()->IsMenuRegistered("LevelEditor.LevelEditorToolBar.OpenBlueprint")) { return; } UToolMenu* Menu = UToolMenus::Get()->RegisterMenu("LevelEditor.LevelEditorToolBar.OpenBlueprint"); struct FBlueprintMenus { /** Generates a sub-level Blueprints sub-menu */ static void MakeSubLevelsMenu(UToolMenu* InMenu) { ULevelEditorMenuContext* Context = InMenu->FindContext(); if (Context && Context->LevelEditor.IsValid()) { FSlateIcon EditBP(FAppStyle::Get().GetStyleSetName(), TEXT("LevelEditor.OpenLevelBlueprint")); { FToolMenuSection& Section = InMenu->AddSection("SubLevels", LOCTEXT("SubLevelsHeading", "Sub-Level Blueprints")); UWorld* World = Context->LevelEditor.Pin()->GetWorld(); // Sort the levels alphabetically TArray SortedLevels = World->GetLevels(); Algo::Sort(SortedLevels, LevelEditorActionHelpers::FLevelSortByName()); for (ULevel* const Level : SortedLevels) { if (Level != NULL && Level->GetOutermost() != NULL && !Level->IsPersistentLevel() && !Level->IsInstancedLevel()) { FUIAction UIAction ( FExecuteAction::CreateStatic(&FLevelEditorToolBar::OnOpenSubLevelBlueprint, Level) ); FText DisplayName = FText::Format(LOCTEXT("SubLevelBlueprintItem", "Edit {0}"), FText::FromString(FPaths::GetCleanFilename(Level->GetOutermost()->GetName()))); Section.AddMenuEntry(NAME_None, DisplayName, FText::GetEmpty(), EditBP, UIAction); } } } } } /** Handle BP being selected from popup picker */ static void OnBPSelected(const struct FAssetData& AssetData) { UBlueprint* SelectedBP = Cast(AssetData.GetAsset()); if(SelectedBP) { GEditor->GetEditorSubsystem()->OpenEditorForAsset(SelectedBP); } } /** Generates 'open blueprint' sub-menu */ static void MakeOpenBPClassMenu(UToolMenu* InMenu) { // The contents of this menu are added as a custom widget with its own search field so we // disable searching in this parent menu to avoid displaying two search fields to the user. InMenu->bSearchable = false; FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked(TEXT("ContentBrowser")); // Configure filter for asset picker FAssetPickerConfig Config; Config.Filter.ClassPaths.Add(UBlueprint::StaticClass()->GetClassPathName()); Config.InitialAssetViewType = EAssetViewType::List; Config.OnAssetSelected = FOnAssetSelected::CreateStatic(&FBlueprintMenus::OnBPSelected); Config.bAllowDragging = false; // Allow saving user defined filters via View Options Config.SaveSettingsName = FString(TEXT("ToolbarOpenBPClass")); TSharedRef Widget = SNew(SBox) .WidthOverride(300.f) .HeightOverride(300.f) [ ContentBrowserModule.Get().CreateAssetPicker(Config) ]; { FToolMenuSection& Section = InMenu->AddSection("Browse", LOCTEXT("BrowseHeader", "Browse")); Section.AddEntry(FToolMenuEntry::InitWidget("PickClassWidget", Widget, FText::GetEmpty())); } } }; { FToolMenuSection& Section = Menu->AddSection("BlueprintClass", LOCTEXT("BlueprintClass", "Blueprint Class")); // Create a blank BP Section.AddMenuEntry(FLevelEditorCommands::Get().CreateBlankBlueprintClass); // Convert selection to BP Section.AddMenuEntry(FLevelEditorCommands::Get().ConvertSelectionToBlueprint); // Open an existing Blueprint Class... FSlateIcon OpenBPIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.OpenClassBlueprint"); Section.AddSubMenu( "OpenBlueprintClass", LOCTEXT("OpenBlueprintClassSubMenu", "Open Blueprint Class..."), LOCTEXT("OpenBlueprintClassSubMenu_ToolTip", "Open an existing Blueprint Class in this project"), FNewToolMenuDelegate::CreateStatic(&FBlueprintMenus::MakeOpenBPClassMenu), false, OpenBPIcon); } { FToolMenuSection& Section = Menu->AddSection("LevelScriptBlueprints", LOCTEXT("LevelScriptBlueprints", "Level Blueprints")); Section.AddMenuEntry( FLevelEditorCommands::Get().OpenLevelBlueprint ); Section.AddDynamicEntry("SubLevels", FNewToolMenuSectionDelegate::CreateLambda([](FToolMenuSection& InSection) { ULevelEditorMenuContext* Context = InSection.FindContext(); if (Context && Context->LevelEditor.IsValid()) { // If there are any sub-levels, display the sub-menu. A single level means there is only the persistent level UWorld* World = Context->LevelEditor.Pin()->GetWorld(); if (World->GetNumLevels() > 1) { InSection.AddSubMenu( "SubLevels", LOCTEXT("SubLevelsSubMenu", "Sub-Levels"), LOCTEXT("SubLevelsSubMenu_ToolTip", "Shows available sub-level Blueprints that can be edited."), FNewToolMenuDelegate::CreateStatic(&FBlueprintMenus::MakeSubLevelsMenu), FUIAction(), EUserInterfaceActionType::Button, false, FSlateIcon(FAppStyle::Get().GetStyleSetName(), TEXT("LevelEditor.OpenLevelBlueprint"))); } } })); } { FToolMenuSection& Section = Menu->AddSection("ProjectSettingsClasses", LOCTEXT("ProjectSettingsClasses", "Project Settings")); LevelEditorActionHelpers::CreateGameModeSubMenu(Section, "ProjectSettingsClasses", true); } { FToolMenuSection& Section = Menu->AddSection("WorldSettingsClasses", LOCTEXT("WorldSettingsClasses", "World Override")); LevelEditorActionHelpers::CreateGameModeSubMenu(Section, "WorldSettingsClasses", false); } // If source control is enabled, queue up a query to the status of the config file so it is (hopefully) ready before we get to the sub-menu if(ISourceControlModule::Get().IsEnabled()) { FString ConfigFilePath = FPaths::ConvertRelativePathToFull(FString::Printf(TEXT("%sDefaultEngine.ini"), *FPaths::SourceConfigDir())); // note: calling QueueStatusUpdate often does not spam status updates as an internal timer prevents this ISourceControlModule::Get().QueueStatusUpdate(ConfigFilePath); } #undef LOCTEXT_NAMESPACE } void FLevelEditorToolBar::RegisterAddMenu() { #define LOCTEXT_NAMESPACE "LevelToolBarViewMenu" if (UToolMenus::Get()->IsMenuRegistered("LevelEditor.LevelEditorToolBar.AddQuickMenu")) { return; } UToolMenu* AddMenu = UToolMenus::Get()->RegisterMenu("LevelEditor.LevelEditorToolBar.AddQuickMenu"); { FToolMenuSection& Section = AddMenu->FindOrAddSection("Content"); Section.InitSection("Content", LOCTEXT("Content_Label", "Get Content"), FToolMenuInsert(NAME_None, EToolMenuInsertType::First)); Section.AddMenuEntry(FLevelEditorCommands::Get().ImportContent).InsertPosition.Position = EToolMenuInsertType::First; } #undef LOCTEXT_NAMESPACE } void FLevelEditorToolBar::OnOpenSubLevelBlueprint( ULevel* InLevel ) { ULevelScriptBlueprint* LevelScriptBlueprint = InLevel->GetLevelScriptBlueprint(); if( LevelScriptBlueprint ) { GEditor->GetEditorSubsystem()->OpenEditorForAsset(LevelScriptBlueprint); } else { FMessageDialog::Open( EAppMsgType::Ok, NSLOCTEXT("UnrealEd", "UnableToCreateLevelScript", "Unable to find or create a level blueprint for this level.") ); } } TSharedRef< SWidget > FLevelEditorToolBar::GenerateCinematicsMenuContent(TSharedRef InCommandList, TWeakPtr InLevelEditor) { FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked("LevelEditor"); FToolMenuContext MenuContext(InCommandList, FExtender::Combine(LevelEditorModule.GetAllLevelEditorToolbarCinematicsMenuExtenders())); ULevelEditorMenuContext* LevelEditorMenuContext = NewObject(); LevelEditorMenuContext->LevelEditor = InLevelEditor; MenuContext.AddObject(LevelEditorMenuContext); return UToolMenus::Get()->GenerateWidget("LevelEditor.LevelEditorToolBar.Cinematics", MenuContext); } TSharedRef< SWidget > FLevelEditorToolBar::GenerateAddMenuWidget(TSharedRef InCommandList, TWeakPtr InLevelEditor) { FToolMenuContext MenuContext(InCommandList); ULevelEditorMenuContext* LevelEditorMenuContext = NewObject(); LevelEditorMenuContext->LevelEditor = InLevelEditor; MenuContext.AddObject(LevelEditorMenuContext); return UToolMenus::Get()->GenerateWidget("LevelEditor.LevelEditorToolBar.AddQuickMenu", MenuContext); } void FLevelEditorToolBar::RegisterCinematicsMenu() { #define LOCTEXT_NAMESPACE "LevelToolBarCinematicsMenu" if (UToolMenus::Get()->IsMenuRegistered("LevelEditor.LevelEditorToolBar.Cinematics")) { return; } UToolMenu* Menu = UToolMenus::Get()->RegisterMenu("LevelEditor.LevelEditorToolBar.Cinematics"); Menu->bShouldCloseWindowAfterMenuSelection = true; Menu->AddSection("LevelEditorNewCinematics", LOCTEXT("CinematicsMenuCombo_NewHeading", "New")); //Add a heading to separate the existing cinematics from the 'Add New Cinematic Actor' button FToolMenuSection& ExistingCinematicSection = Menu->AddSection("LevelEditorExistingCinematic", LOCTEXT("CinematicMenuCombo_ExistingHeading", "Edit Existing Cinematic")); ExistingCinematicSection.AddDynamicEntry("LevelEditorExistingCinematic", FNewToolMenuSectionDelegate::CreateLambda([](FToolMenuSection& InSection) { ULevelEditorMenuContext* FoundContext = InSection.Context.FindContext(); if (!FoundContext) { return; } UWorld* World = FoundContext->LevelEditor.IsValid() ? FoundContext->LevelEditor.Pin()->GetWorld() : nullptr; const bool bHasAnyCinematicsActors = !!TActorIterator(World); if (!bHasAnyCinematicsActors) { return; } // We can't build a list of LevelSequenceActors while the current World is a PIE world. FSceneOutlinerInitializationOptions InitOptions; { // We hide the header row to keep the UI compact. // @todo: Might be useful to have this sometimes, actually. Ideally the user could summon it. InitOptions.bShowHeaderRow = false; InitOptions.bShowSearchBox = true; InitOptions.bShowCreateNewFolder = false; InitOptions.ColumnMap.Add(FSceneOutlinerBuiltInColumnTypes::Label(), FSceneOutlinerColumnInfo(ESceneOutlinerColumnVisibility::Visible, 0, FCreateSceneOutlinerColumn(), false, 0.6f)); InitOptions.ColumnMap.Add(FSceneOutlinerBuiltInColumnTypes::ActorInfo(), FSceneOutlinerColumnInfo(ESceneOutlinerColumnVisibility::Visible, 10, FCreateSceneOutlinerColumn(), false, 0.4f)); // Only display MovieScene actors auto ActorFilter = [&](const AActor* Actor) { return Actor && Actor->IsA(ALevelSequenceActor::StaticClass()); }; InitOptions.Filters->AddFilterPredicate(FActorTreeItem::FFilterPredicate::CreateLambda(ActorFilter)); } // actor selector to allow the user to choose an actor FSceneOutlinerModule& SceneOutlinerModule = FModuleManager::LoadModuleChecked("SceneOutliner"); TSharedRef< SWidget > MiniSceneOutliner = SNew(SVerticalBox) + SVerticalBox::Slot() .AutoHeight() .MaxHeight(400.0f) [ SNew(SBox) .WidthOverride(360.f) [ SceneOutlinerModule.CreateActorPicker( InitOptions, FOnActorPicked::CreateStatic(&FLevelEditorToolBar::OnCinematicsActorPicked)) ] ]; InSection.AddEntry(FToolMenuEntry::InitWidget("LevelEditorExistingCinematic", MiniSceneOutliner, FText::GetEmpty(), true)); })); #undef LOCTEXT_NAMESPACE } void FLevelEditorToolBar::OnCinematicsActorPicked( AActor* Actor ) { //The sequencer editor will not tick unless the editor viewport is in realtime mode. //the scene outliner eats input, so we must close any popups manually. FSlateApplication::Get().DismissAllMenus(); // Make sure we dismiss the menus before we open this if (ALevelSequenceActor* LevelSequenceActor = Cast(Actor)) { if (LevelSequenceActor->GetSequence()) { FScopedSlowTask SlowTask(1.f, NSLOCTEXT("LevelToolBarCinematicsMenu", "LoadSequenceSlowTask", "Loading Level Sequence...")); SlowTask.MakeDialog(); SlowTask.EnterProgressFrame(); UObject* Asset = LevelSequenceActor->GetSequence(); if (Asset != nullptr) { GEditor->GetEditorSubsystem()->OpenEditorForAsset(Asset); } } else { FMessageDialog::Open(EAppMsgType::Type::Ok, NSLOCTEXT("LevelToolBarCinematicsMenu", "LoadSequenceNotValid", "This Level Sequence actor does not have a Sequence asset assigned in the Details panel and cannot be opened.")); } } }