Files
UnrealEngineUWP/Engine/Source/Editor/LevelEditor/Private/LevelEditorToolBar.cpp
Matt Kuhlenschmidt 82b0277075 Fix the android mobile preview icon
#jira UE-69825
#lockdown nick.penwarden
#rb none

[CL 5494169 by Matt Kuhlenschmidt in 4.22 branch]
2019-03-21 13:46:25 -04:00

2417 lines
99 KiB
C++

// Copyright 1998-2019 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/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 "EditorStyleSet.h"
#include "ISourceControlOperation.h"
#include "SourceControlOperations.h"
#include "ISourceControlProvider.h"
#include "ISourceControlModule.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 "Toolkits/AssetEditorManager.h"
#include "LevelEditor.h"
#include "LevelEditorActions.h"
#include "SourceCodeNavigation.h"
#include "Kismet2/DebuggerCommands.h"
#include "SceneOutlinerPublicTypes.h"
#include "SceneOutlinerModule.h"
#include "SScalabilitySettings.h"
#include "IContentBrowserSingleton.h"
#include "ContentBrowserModule.h"
#include "Matinee/MatineeActor.h"
#include "LevelSequenceActor.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 "RHIShaderPlatformDefinitions.inl"
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<UBlueprint>(GameModeClass->ClassGeneratedBy))
{
// @todo Re-enable once world centric works
const bool bOpenWorldCentric = false;
FAssetEditorManager::Get().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<UBlueprint>(GameStateClass->ClassGeneratedBy))
{
// @todo Re-enable once world centric works
const bool bOpenWorldCentric = false;
FAssetEditorManager::Get().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<UBlueprint>(DefaultPawnClass->ClassGeneratedBy))
{
// @todo Re-enable once world centric works
const bool bOpenWorldCentric = false;
FAssetEditorManager::Get().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<UBlueprint>(DefaultHUDClass->ClassGeneratedBy))
{
// @todo Re-enable once world centric works
const bool bOpenWorldCentric = false;
FAssetEditorManager::Get().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<UBlueprint>(PlayerControllerClass->ClassGeneratedBy))
{
// @todo Re-enable once world centric works
const bool bOpenWorldCentric = false;
FAssetEditorManager::Get().OpenEditorForAsset(
BlueprintClass,
bOpenWorldCentric ? EToolkitMode::WorldCentric : EToolkitMode::Standalone,
InLevelEditor.Pin() );
}
else
{
OpenNativeClass(PlayerControllerClass);
}
}
}
/**
* Builds a sub-menu for selecting a class
*
* @param InMenuBuilder 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(FMenuBuilder& InMenuBuilder, UClass* InRootClass, FOnClassPicked InOnClassPicked)
{
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.ClassFilter = Filter;
FText RootClassName = FText::FromString(InRootClass->GetName());
TSharedRef<SWidget> ClassViewer = FModuleManager::LoadModuleChecked<FClassViewerModule>("ClassViewer").CreateClassViewer(Options, InOnClassPicked);
FFormatNamedArguments FormatArgs;
FormatArgs.Add(TEXT("RootClass"), RootClassName);
InMenuBuilder.BeginSection(NAME_None, FText::Format(NSLOCTEXT("LevelToolBarViewMenu", "SelectGameModeLabel", "Select {RootClass} class"), FormatArgs));
InMenuBuilder.AddWidget(ClassViewer, FText::GetEmpty(), true);
InMenuBuilder.EndSection();
}
/**
* Builds a sub-menu for creating a class
*
* @param InMenuBuilder 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(FMenuBuilder& InMenuBuilder, UClass* InRootClass, FOnClassPicked InOnClassPicked)
{
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.ClassFilter = Filter;
FText RootClassName = FText::FromString(InRootClass->GetName());
TSharedRef<SWidget> ClassViewer = FModuleManager::LoadModuleChecked<FClassViewerModule>("ClassViewer").CreateClassViewer(Options, InOnClassPicked);
FFormatNamedArguments FormatArgs;
FormatArgs.Add(TEXT("RootClass"), RootClassName);
InMenuBuilder.BeginSection(NAME_None, FText::Format(NSLOCTEXT("LevelToolBarViewMenu", "CreateGameModeLabel", "Select {RootClass} parent class"), FormatArgs));
InMenuBuilder.AddWidget(ClassViewer, FText::GetEmpty(), true);
InMenuBuilder.EndSection();
}
/** 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 InMenuBuilder 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(FMenuBuilder& InMenuBuilder, const TSharedRef<FUICommandList> InCommandList, 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(FMenuBuilder& InMenuBuilder, TSharedRef<FUICommandList> InCommandList, TWeakPtr< SLevelEditor > InLevelEditor, bool bInProjectSettings)
{
#define LOCTEXT_NAMESPACE "LevelToolBarViewMenu"
LevelEditorActionHelpers::FBlueprintMenuSettings GameModeMenuSettings;
GameModeMenuSettings.EditCommand =
FUIAction(
FExecuteAction::CreateStatic< TWeakPtr< SLevelEditor > >( &OpenGameModeBlueprint, InLevelEditor, bInProjectSettings )
);
GameModeMenuSettings.OnCreateClassPicked = FOnClassPicked::CreateStatic( &LevelEditorActionHelpers::OnCreateGameModeClassPicked, InLevelEditor, bInProjectSettings );
GameModeMenuSettings.OnSelectClassPicked = FOnClassPicked::CreateStatic( &LevelEditorActionHelpers::OnSelectGameModeClassPicked, InLevelEditor, bInProjectSettings );
GameModeMenuSettings.CurrentClass = LevelEditorActionHelpers::GetGameModeClass(InLevelEditor, bInProjectSettings);
GameModeMenuSettings.RootClass = AGameModeBase::StaticClass();
GameModeMenuSettings.LevelEditor = InLevelEditor;
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;
};
InMenuBuilder.AddSubMenu( LevelEditorActionHelpers::GetOpenGameModeBlueprintLabel(InLevelEditor, bInProjectSettings),
GetSubMenuTooltip(GameModeMenuSettings.CurrentClass, GameModeMenuSettings.RootClass, bInProjectSettings),
FNewMenuDelegate::CreateStatic( &LevelEditorActionHelpers::GetBlueprintSettingsSubMenu, InCommandList, GameModeMenuSettings),
FUIAction(FExecuteAction(), FCanExecuteAction(), FIsActionChecked::CreateStatic(IsGameModeActive, InLevelEditor, bInProjectSettings)),
NAME_None, EUserInterfaceActionType::RadioButton );
#undef LOCTEXT_NAMESPACE
}
/**
* Builds the game mode's sub menu objects
*
* @param InMenuBuilder 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(FMenuBuilder& InMenuBuilder, const TSharedRef<FUICommandList> InCommandList, const FBlueprintMenuSettings& InSettingsData)
{
#define LOCTEXT_NAMESPACE "LevelToolBarViewMenu"
// Game State
LevelEditorActionHelpers::FBlueprintMenuSettings GameStateMenuSettings;
GameStateMenuSettings.EditCommand =
FUIAction(
FExecuteAction::CreateStatic< TWeakPtr< SLevelEditor > >( &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;
InMenuBuilder.AddSubMenu( LevelEditorActionHelpers::GetOpenGameStateBlueprintLabel(InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings),
GetSubMenuTooltip(GameStateMenuSettings.CurrentClass, GameStateMenuSettings.RootClass, InSettingsData.bIsProjectSettings),
FNewMenuDelegate::CreateStatic( &LevelEditorActionHelpers::GetBlueprintSettingsSubMenu, InCommandList, GameStateMenuSettings )
);
// Pawn
LevelEditorActionHelpers::FBlueprintMenuSettings PawnMenuSettings;
PawnMenuSettings.EditCommand =
FUIAction(
FExecuteAction::CreateStatic< TWeakPtr< SLevelEditor > >( &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;
InMenuBuilder.AddSubMenu( LevelEditorActionHelpers::GetOpenPawnBlueprintLabel(InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings),
GetSubMenuTooltip(PawnMenuSettings.CurrentClass, PawnMenuSettings.RootClass, InSettingsData.bIsProjectSettings),
FNewMenuDelegate::CreateStatic( &LevelEditorActionHelpers::GetBlueprintSettingsSubMenu, InCommandList, PawnMenuSettings )
);
// HUD
LevelEditorActionHelpers::FBlueprintMenuSettings HUDMenuSettings;
HUDMenuSettings.EditCommand =
FUIAction(
FExecuteAction::CreateStatic< TWeakPtr< SLevelEditor > >( &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;
InMenuBuilder.AddSubMenu( LevelEditorActionHelpers::GetOpenHUDBlueprintLabel(InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings),
GetSubMenuTooltip(HUDMenuSettings.CurrentClass, HUDMenuSettings.RootClass, InSettingsData.bIsProjectSettings),
FNewMenuDelegate::CreateStatic( &LevelEditorActionHelpers::GetBlueprintSettingsSubMenu, InCommandList, HUDMenuSettings )
);
// Player Controller
LevelEditorActionHelpers::FBlueprintMenuSettings PlayerControllerMenuSettings;
PlayerControllerMenuSettings.EditCommand =
FUIAction(
FExecuteAction::CreateStatic< TWeakPtr< SLevelEditor > >( &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;
InMenuBuilder.AddSubMenu( LevelEditorActionHelpers::GetOpenPlayerControllerBlueprintLabel(InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings),
GetSubMenuTooltip(PlayerControllerMenuSettings.CurrentClass, PlayerControllerMenuSettings.RootClass, InSettingsData.bIsProjectSettings),
FNewMenuDelegate::CreateStatic( &LevelEditorActionHelpers::GetBlueprintSettingsSubMenu, InCommandList, PlayerControllerMenuSettings )
);
#undef LOCTEXT_NAMESPACE
}
}
void LevelEditorActionHelpers::GetBlueprintSettingsSubMenu(FMenuBuilder& InMenuBuilder, const TSharedRef<FUICommandList> InCommandList, FBlueprintMenuSettings InSettingsData)
{
#define LOCTEXT_NAMESPACE "LevelToolBarViewMenu"
InMenuBuilder.PushCommandList(InCommandList);
FSlateIcon EditBPIcon(FEditorStyle::Get().GetStyleSetName(), TEXT("PropertyWindow.Button_Edit"));
FSlateIcon NewBPIcon(FEditorStyle::Get().GetStyleSetName(), TEXT("PropertyWindow.Button_AddToArray"));
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"));
if(InSettingsData.CurrentClass->ClassGeneratedBy)
{
FText BlueprintName = FText::FromString(InSettingsData.CurrentClass->ClassGeneratedBy->GetName());
Args.Add(TEXT("Blueprint"), BlueprintName);
InMenuBuilder.AddMenuEntry( 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())
{
InMenuBuilder.AddMenuEntry( 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
InMenuBuilder.AddMenuEntry( 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())
{
InMenuBuilder.BeginSection("CheckoutSection", LOCTEXT("CheckoutSection","Check Out Project Settings") );
TAttribute<FText> CheckOutLabel;
CheckOutLabel.BindStatic(&GetCheckOutLabel);
TAttribute<FText> CheckOutTooltip;
CheckOutTooltip.BindStatic(&GetCheckOutTooltip);
InMenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().CheckOutProjectSettingsConfig, NAME_None, CheckOutLabel, CheckOutTooltip, FSlateIcon(FEditorStyle::Get().GetStyleSetName(), TEXT("Icons.Error")));
InMenuBuilder.EndSection();
}
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;
};
// Create a new GameMode, this is always available so the user can easily create a new one
InMenuBuilder.AddSubMenu( LOCTEXT("CreateBlueprint", "Create..."),
GetCreateMenuTooltip(GetGameModeClass(InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings), InSettingsData.RootClass, InSettingsData.bIsProjectSettings),
FNewMenuDelegate::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 )
),
NAME_None, EUserInterfaceActionType::Button, false, NewBPIcon
);
// Select a game mode, this is always available so the user can switch his selection
FFormatNamedArguments Args;
Args.Add(TEXT("RootClass"), RootClassName);
InMenuBuilder.AddSubMenu(FText::Format(LOCTEXT("SelectGameModeClass", "Select {RootClass} Class"), Args),
GetSelectMenuTooltip(GetGameModeClass(InSettingsData.LevelEditor, InSettingsData.bIsProjectSettings), InSettingsData.RootClass, InSettingsData.bIsProjectSettings),
FNewMenuDelegate::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 )
),
NAME_None, EUserInterfaceActionType::Button
);
// For GameMode classes only, there are some sub-classes we need to add to the menu
if(InSettingsData.RootClass == AGameModeBase::StaticClass())
{
InMenuBuilder.BeginSection(NAME_None, LOCTEXT("GameModeClasses", "Game Mode Classes"));
if(InSettingsData.CurrentClass)
{
GetGameModeSubMenu(InMenuBuilder, InCommandList, InSettingsData);
}
InMenuBuilder.EndSection();
}
InMenuBuilder.PopCommandList();
#undef LOCTEXT_NAMESPACE
}
UClass* LevelEditorActionHelpers::GetGameModeClass(TWeakPtr< SLevelEditor > InLevelEditor, bool bInIsProjectSettings)
{
UClass* GameModeClass = nullptr;
if(bInIsProjectSettings)
{
UObject* GameModeObject = LoadObject<UObject>(nullptr, *UGameMapsSettings::GetGlobalDefaultGameMode());
if(UBlueprint* GameModeAsBlueprint = Cast<UBlueprint>(GameModeObject))
{
GameModeClass = GameModeAsBlueprint->GeneratedClass;
}
else
{
GameModeClass = FindObject<UClass>(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;
FAssetEditorManager::Get().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<ISettingsModule>("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<AGameModeBase>(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;
FAssetEditorManager::Get().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<AGameModeBase>(GameModeClass->GetDefaultObject());
ActiveGameMode->GameStateClass = InChosenClass;
UBlueprint* Blueprint = Cast<UBlueprint>(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<AGameModeBase>(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;
FAssetEditorManager::Get().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<AGameModeBase>(GameModeClass->GetDefaultObject());
ActiveGameMode->DefaultPawnClass = InChosenClass;
UBlueprint* Blueprint = Cast<UBlueprint>(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<AGameModeBase>(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;
FAssetEditorManager::Get().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<AGameModeBase>(GameModeClass->GetDefaultObject());
ActiveGameMode->HUDClass = InChosenClass;
UBlueprint* Blueprint = Cast<UBlueprint>(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<AGameModeBase>(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;
FAssetEditorManager::Get().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<AGameModeBase>(GameModeClass->GetDefaultObject());
ActiveGameMode->PlayerControllerClass = InChosenClass;
UBlueprint* Blueprint = Cast<UBlueprint>(GameModeClass->ClassGeneratedBy);
if (ensure(Blueprint))
{
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
}
}
FSlateApplication::Get().DismissAllMenus();
}
/**
* Static: Creates a widget for the level editor tool bar
*
* @return New widget
*/
TSharedRef< SWidget > FLevelEditorToolBar::MakeLevelEditorToolBar( const TSharedRef<FUICommandList>& InCommandList, const TSharedRef<SLevelEditor> InLevelEditor )
{
#define LOCTEXT_NAMESPACE "LevelEditorToolBar"
FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");
TSharedPtr<FExtender> Extenders = LevelEditorModule.GetToolBarExtensibilityManager()->GetAllExtenders();
static const FName LevelEditorToolBarName = "LevelEditorToolBar";
FToolBarBuilder ToolbarBuilder( InCommandList, FMultiBoxCustomization::AllowCustomization( LevelEditorToolBarName ), Extenders );
ToolbarBuilder.BeginSection("File");
{
// Save All Levels
ToolbarBuilder.AddToolBarButton( FLevelEditorCommands::Get().Save, NAME_None, TAttribute<FText>(), TAttribute<FText>(), FSlateIcon(FEditorStyle::GetStyleSetName(), "AssetEditor.SaveAsset") );
// Source control buttons
{
enum EQueryState
{
NotQueried,
Querying,
Queried,
};
static EQueryState QueryState = EQueryState::NotQueried;
struct FSourceControlStatus
{
static void CheckSourceControlStatus()
{
ISourceControlModule& SourceControlModule = ISourceControlModule::Get();
if (SourceControlModule.IsEnabled())
{
SourceControlModule.GetProvider().Execute(ISourceControlOperation::Create<FConnect>(),
EConcurrency::Asynchronous,
FSourceControlOperationComplete::CreateStatic(&FSourceControlStatus::OnSourceControlOperationComplete));
QueryState = EQueryState::Querying;
}
}
static void OnSourceControlOperationComplete(const FSourceControlOperationRef& InOperation, ECommandResult::Type InResult)
{
QueryState = EQueryState::Queried;
}
static FText GetSourceControlTooltip()
{
if (QueryState == EQueryState::Querying)
{
return LOCTEXT("SourceControlUnknown", "Source control status is unknown");
}
else
{
return ISourceControlModule::Get().GetProvider().GetStatusText();
}
}
static FSlateIcon GetSourceControlIcon()
{
if (QueryState == EQueryState::Querying)
{
return FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.SourceControl.Unknown");
}
else
{
ISourceControlModule& SourceControlModule = ISourceControlModule::Get();
if (SourceControlModule.IsEnabled())
{
if (!SourceControlModule.GetProvider().IsAvailable())
{
return FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.SourceControl.Error");
}
else
{
return FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.SourceControl.On");
}
}
else
{
return FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.SourceControl.Off");
}
}
}
};
FSourceControlStatus::CheckSourceControlStatus();
ToolbarBuilder.AddComboButton(
FUIAction(),
FOnGetContent::CreateStatic(&FLevelEditorToolBar::GenerateSourceControlMenu, InCommandList),
LOCTEXT("SourceControl_Label", "Source Control"),
TAttribute<FText>::Create(&FSourceControlStatus::GetSourceControlTooltip),
TAttribute<FSlateIcon>::Create(&FSourceControlStatus::GetSourceControlIcon),
false
);
}
}
ToolbarBuilder.EndSection();
ToolbarBuilder.BeginSection("Content");
{
ToolbarBuilder.AddToolBarButton( FLevelEditorCommands::Get().OpenContentBrowser, NAME_None, LOCTEXT( "ContentBrowser_Override", "Content" ), TAttribute<FText>(), TAttribute<FSlateIcon>(), "LevelToolbarContent" );
if (FLauncherPlatformModule::Get()->CanOpenLauncher(true))
{
ToolbarBuilder.AddToolBarButton(FLevelEditorCommands::Get().OpenMarketplace, NAME_None, LOCTEXT("Marketplace_Override", "Marketplace"), TAttribute<FText>(), TAttribute<FSlateIcon>(), "LevelToolbarMarketplace");
}
}
ToolbarBuilder.EndSection();
ToolbarBuilder.BeginSection("Settings");
{
ToolbarBuilder.AddComboButton(
FUIAction(),
FOnGetContent::CreateStatic(&FLevelEditorToolBar::GenerateQuickSettingsMenu, InCommandList),
LOCTEXT("QuickSettingsCombo", "Settings"),
LOCTEXT("QuickSettingsCombo_ToolTip", "Project and Editor settings"),
FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.GameSettings"),
false,
"LevelToolbarQuickSettings"
);
}
{
struct FPreviewModeFunctionality
{
static FText GetPreviewModeText()
{
UMaterialShaderQualitySettings* MaterialShaderQualitySettings = UMaterialShaderQualitySettings::Get();
const FName& PreviewPlatform = MaterialShaderQualitySettings->GetPreviewPlatform();
EShaderPlatform ShaderPlatform = ShaderFormatToLegacyShaderPlatform(PreviewPlatform);
if (ShaderPlatform == SP_NumPlatforms)
{
ShaderPlatform = GetFeatureLevelShaderPlatform(GEditor->PreviewFeatureLevel);
}
switch (ShaderPlatform)
{
case SP_VULKAN_ES3_1_ANDROID:
{
return LOCTEXT("PreviewModeES31_Vulkan_Text", "Vulkan Preview");
}
}
switch (GEditor->PreviewFeatureLevel)
{
case ERHIFeatureLevel::SM4:
{
return LOCTEXT("PreviewModeSM4_Text", "SM4 Preview");
}
case ERHIFeatureLevel::ES2:
{
return LOCTEXT("PreviewModeES2_Text", "ES2 Preview");
}
case ERHIFeatureLevel::ES3_1:
{
return LOCTEXT("PreviewModeES3_1_Text", "ES3.1 Preview");
}
default:
{
return LOCTEXT("PreviewModeGeneric", "Preview Mode");
}
}
}
static FText GetPreviewModeTooltip()
{
UMaterialShaderQualitySettings* MaterialShaderQualitySettings = UMaterialShaderQualitySettings::Get();
const FName& PreviewPlatform = MaterialShaderQualitySettings->GetPreviewPlatform();
EShaderPlatform PreviewShaderPlatform = ShaderFormatToLegacyShaderPlatform(PreviewPlatform);
if (PreviewShaderPlatform == SP_NumPlatforms)
{
PreviewShaderPlatform = GetFeatureLevelShaderPlatform(GEditor->PreviewFeatureLevel);
}
EShaderPlatform MaxRHIFeatureLevelPlatform = GetFeatureLevelShaderPlatform(GMaxRHIFeatureLevel);
{
const FText& RenderingAsPlatformName = GetFriendlyShaderPlatformName(GWorld->FeatureLevel == GMaxRHIFeatureLevel ? MaxRHIFeatureLevelPlatform : PreviewShaderPlatform);
const FText& SwitchToPlatformName = GetFriendlyShaderPlatformName(GWorld->FeatureLevel == GMaxRHIFeatureLevel ? PreviewShaderPlatform : MaxRHIFeatureLevelPlatform);
if (GWorld->FeatureLevel == 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);
}
}
}
static FSlateIcon GetPreviewModeIcon()
{
UMaterialShaderQualitySettings* MaterialShaderQualitySettings = UMaterialShaderQualitySettings::Get();
const FName& PreviewPlatform = MaterialShaderQualitySettings->GetPreviewPlatform();
EShaderPlatform ShaderPlatform = ShaderFormatToLegacyShaderPlatform(PreviewPlatform);
if (ShaderPlatform == SP_NumPlatforms)
{
ShaderPlatform = GetFeatureLevelShaderPlatform(GEditor->PreviewFeatureLevel);
}
switch (ShaderPlatform)
{
case SP_OPENGL_ES3_1_ANDROID:
{
return FSlateIcon(FEditorStyle::GetStyleSetName(), GEditor->IsFeatureLevelPreviewActive() ? "LevelEditor.PreviewMode.AndroidES31.Enabled" : "LevelEditor.PreviewMode.AndroidES31.Disabled");
}
case SP_VULKAN_ES3_1_ANDROID:
{
return FSlateIcon(FEditorStyle::GetStyleSetName(), GEditor->IsFeatureLevelPreviewActive() ? "LevelEditor.PreviewMode.AndroidVulkan.Enabled" : "LevelEditor.PreviewMode.AndroidVulkan.Disabled");
}
case SP_METAL:
{
return FSlateIcon(FEditorStyle::GetStyleSetName(), GEditor->IsFeatureLevelPreviewActive() ? "LevelEditor.PreviewMode.iOS.Enabled" : "LevelEditor.PreviewMode.iOS.Disabled");
}
case SP_VULKAN_PCES3_1:
case SP_OPENGL_PCES2:
case SP_PCD3D_ES2:
case SP_METAL_MACES2:
case SP_OPENGL_ES2_WEBGL:
{
return FSlateIcon(FEditorStyle::GetStyleSetName(), GEditor->IsFeatureLevelPreviewActive() ? "LevelEditor.PreviewMode.HTML5.Enabled" : "LevelEditor.PreviewMode.HTML5.Disabled");
}
}
switch (GEditor->PreviewFeatureLevel)
{
case ERHIFeatureLevel::SM4:
{
return FSlateIcon(FEditorStyle::GetStyleSetName(), GEditor->IsFeatureLevelPreviewActive() ? "LevelEditor.PreviewMode.SM4.Enabled" : "LevelEditor.PreviewMode.SM4.Disabled");
}
case ERHIFeatureLevel::ES2:
{
return FSlateIcon(FEditorStyle::GetStyleSetName(), GEditor->IsFeatureLevelPreviewActive() ? "LevelEditor.PreviewMode.AndroidES2.Enabled" : "LevelEditor.PreviewMode.AndroidES2.Disabled");
}
case ERHIFeatureLevel::ES3_1:
{
return FSlateIcon(FEditorStyle::GetStyleSetName(), GEditor->IsFeatureLevelPreviewActive() ? "LevelEditor.PreviewMode.Enabled" : "LevelEditor.PreviewMode.Disabled");
}
default:
{
return FSlateIcon(FEditorStyle::GetStyleSetName(), GEditor->IsFeatureLevelPreviewActive() ? "LevelEditor.PreviewMode.Enabled" : "LevelEditor.PreviewMode.Disabled");
}
}
}
};
ToolbarBuilder.AddToolBarButton(
FLevelEditorCommands::Get().ToggleFeatureLevelPreview,
NAME_None,
TAttribute<FText>::Create(&FPreviewModeFunctionality::GetPreviewModeText),
TAttribute<FText>::Create(&FPreviewModeFunctionality::GetPreviewModeTooltip),
TAttribute<FSlateIcon>::Create(&FPreviewModeFunctionality::GetPreviewModeIcon)
);
}
ToolbarBuilder.EndSection();
ToolbarBuilder.BeginSection( NAME_None );
{
ToolbarBuilder.AddComboButton(
FUIAction(),
FOnGetContent::CreateStatic( &FLevelEditorToolBar::GenerateOpenBlueprintMenuContent, InCommandList, TWeakPtr<SLevelEditor>( InLevelEditor ) ),
LOCTEXT( "OpenBlueprint_Label", "Blueprints" ),
LOCTEXT( "OpenBlueprint_ToolTip", "List of world Blueprints available to the user for editing or creation." ),
FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.OpenLevelBlueprint")
);
ToolbarBuilder.AddComboButton(
FUIAction(),
FOnGetContent::CreateStatic( &FLevelEditorToolBar::GenerateCinematicsMenuContent, InCommandList, TWeakPtr<SLevelEditor>( InLevelEditor ) ),
LOCTEXT( "EditCinematics_Label", "Cinematics" ),
LOCTEXT( "EditCinematics_Tooltip", "Displays a list of Matinee and Level Sequence objects to open in their respective editors"),
FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.EditMatinee")
);
ToolbarBuilder.AddToolBarButton( FLevelEditorCommands::Get().ToggleVR, NAME_None, LOCTEXT("ToggleVR", "VR Mode") );
}
ToolbarBuilder.EndSection();
ToolbarBuilder.BeginSection("Compile");
{
// Build
ToolbarBuilder.AddToolBarButton( FLevelEditorCommands::Get().Build, NAME_None, LOCTEXT("BuildAll", "Build") );
// Build menu drop down
ToolbarBuilder.AddComboButton(
FUIAction(),
FOnGetContent::CreateStatic( &FLevelEditorToolBar::GenerateBuildMenuContent, InCommandList ),
LOCTEXT( "BuildCombo_Label", "Build Options" ),
LOCTEXT( "BuildComboToolTip", "Build options menu" ),
FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Build"),
true);
// Only show the compile options on machines with the solution (assuming they can build it)
if ( FSourceCodeNavigation::IsCompilerAvailable() )
{
// Since we can always add new code to the project, only hide these buttons if we haven't done so yet
ToolbarBuilder.AddToolBarButton(
FUIAction(
FExecuteAction::CreateStatic(&FLevelEditorActionCallbacks::RecompileGameCode_Clicked),
FCanExecuteAction::CreateStatic(&FLevelEditorActionCallbacks::Recompile_CanExecute),
FIsActionChecked(),
FIsActionButtonVisible::CreateStatic(FLevelEditorActionCallbacks::CanShowSourceCodeActions)),
NAME_None,
LOCTEXT( "CompileMenuButton", "Compile" ),
FLevelEditorCommands::Get().RecompileGameCode->GetDescription(),
FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Recompile")
);
#if WITH_LIVE_CODING
ToolbarBuilder.AddComboButton(
FUIAction(
FExecuteAction(),
FCanExecuteAction(),
FIsActionChecked(),
FIsActionButtonVisible::CreateStatic(FLevelEditorActionCallbacks::CanShowSourceCodeActions)),
FOnGetContent::CreateStatic( &FLevelEditorToolBar::GenerateCompileMenuContent, InCommandList ),
LOCTEXT( "CompileCombo_Label", "Compile Options" ),
LOCTEXT( "CompileComboToolTip", "Compile options menu" ),
FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Recompile"),
true
);
#endif
}
}
ToolbarBuilder.EndSection();
ToolbarBuilder.BeginSection("Game");
{
// Add the shared play-world commands that will be shown on the Kismet toolbar as well
FPlayWorldCommands::BuildToolbar(ToolbarBuilder, true);
}
ToolbarBuilder.EndSection();
// Create the tool bar!
return
SNew( SBorder )
.Padding(0)
.BorderImage( FEditorStyle::GetBrush("NoBorder") )
.IsEnabled( FSlateApplication::Get().GetNormalExecutionAttribute() )
[
ToolbarBuilder.MakeWidget()
];
#undef LOCTEXT_NAMESPACE
}
TSharedRef< SWidget > FLevelEditorToolBar::GenerateBuildMenuContent( TSharedRef<FUICommandList> InCommandList )
{
#define LOCTEXT_NAMESPACE "LevelToolBarBuildMenu"
// Get all menu extenders for this context menu from the level editor module
FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked<FLevelEditorModule>( TEXT("LevelEditor") );
TArray<FLevelEditorModule::FLevelEditorMenuExtender> MenuExtenderDelegates = LevelEditorModule.GetAllLevelEditorToolbarBuildMenuExtenders();
TArray<TSharedPtr<FExtender>> Extenders;
for (int32 i = 0; i < MenuExtenderDelegates.Num(); ++i)
{
if (MenuExtenderDelegates[i].IsBound())
{
Extenders.Add(MenuExtenderDelegates[i].Execute(InCommandList));
}
}
TSharedPtr<FExtender> MenuExtender = FExtender::Combine(Extenders);
const bool bShouldCloseWindowAfterMenuSelection = true;
FMenuBuilder MenuBuilder( bShouldCloseWindowAfterMenuSelection, InCommandList, MenuExtender );
struct FLightingMenus
{
/** Generates a lighting quality sub-menu */
static void MakeLightingQualityMenu( FMenuBuilder& InMenuBuilder )
{
InMenuBuilder.BeginSection("LevelEditorBuildLightingQuality", LOCTEXT( "LightingQualityHeading", "Quality Level" ) );
{
InMenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingQuality_Production );
InMenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingQuality_High );
InMenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingQuality_Medium );
InMenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingQuality_Preview );
}
InMenuBuilder.EndSection();
}
/** Generates a lighting density sub-menu */
static void MakeLightingDensityMenu( FMenuBuilder& InMenuBuilder )
{
InMenuBuilder.BeginSection("LevelEditorBuildLightingDensity", LOCTEXT( "LightingDensityHeading", "Density Rendering" ) );
{
TSharedRef<SWidget> Ideal = SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.Padding( FMargin( 27.0f, 0.0f, 0.0f, 0.0f ) )
.FillWidth(1.0f)
[
SNew(SSpinBox<float>)
.MinValue(0.f)
.MaxValue(100.f)
.Value(FLevelEditorActionCallbacks::GetLightingDensityIdeal())
.OnValueChanged_Static(&FLevelEditorActionCallbacks::SetLightingDensityIdeal)
];
InMenuBuilder.AddWidget(Ideal, LOCTEXT("LightingDensity_Ideal","Ideal Density"));
TSharedRef<SWidget> Maximum = SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.FillWidth(1.0f)
[
SNew(SSpinBox<float>)
.MinValue(0.01f)
.MaxValue(100.01f)
.Value(FLevelEditorActionCallbacks::GetLightingDensityMaximum())
.OnValueChanged_Static(&FLevelEditorActionCallbacks::SetLightingDensityMaximum)
];
InMenuBuilder.AddWidget(Maximum, LOCTEXT("LightingDensity_Maximum","Maximum Density"));
TSharedRef<SWidget> ClrScale = SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.Padding( FMargin( 35.0f, 0.0f, 0.0f, 0.0f ) )
.FillWidth(1.0f)
[
SNew(SSpinBox<float>)
.MinValue(0.f)
.MaxValue(10.f)
.Value(FLevelEditorActionCallbacks::GetLightingDensityColorScale())
.OnValueChanged_Static(&FLevelEditorActionCallbacks::SetLightingDensityColorScale)
];
InMenuBuilder.AddWidget(ClrScale, LOCTEXT("LightingDensity_ColorScale","Color Scale"));
TSharedRef<SWidget> GrayScale = SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.Padding( FMargin( 11.0f, 0.0f, 0.0f, 0.0f ) )
.FillWidth(1.0f)
[
SNew(SSpinBox<float>)
.MinValue(0.f)
.MaxValue(10.f)
.Value(FLevelEditorActionCallbacks::GetLightingDensityGrayscaleScale())
.OnValueChanged_Static(&FLevelEditorActionCallbacks::SetLightingDensityGrayscaleScale)
];
InMenuBuilder.AddWidget(GrayScale, LOCTEXT("LightingDensity_GrayscaleScale","Grayscale Scale"));
InMenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingDensity_RenderGrayscale );
}
InMenuBuilder.EndSection();
}
/** Generates a lighting resolution sub-menu */
static void MakeLightingResolutionMenu( FMenuBuilder& InMenuBuilder )
{
InMenuBuilder.BeginSection("LevelEditorBuildLightingResolution1", LOCTEXT( "LightingResolutionHeading1", "Primitive Types" ) );
{
TSharedRef<SWidget> Meshes = SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.AutoWidth()
[
SNew( SCheckBox )
.Style( FEditorStyle::Get(), "Menu.CheckBox" )
.ToolTipText(LOCTEXT( "StaticMeshesToolTip", "Static Meshes will be adjusted if checked." ))
.IsChecked_Static(&FLevelEditorActionCallbacks::IsLightingResolutionStaticMeshesChecked)
.OnCheckStateChanged_Static(&FLevelEditorActionCallbacks::SetLightingResolutionStaticMeshes)
.Content()
[
SNew( STextBlock )
.Text( LOCTEXT("StaticMeshes", "Static Meshes") )
]
]
+SHorizontalBox::Slot()
.AutoWidth()
.Padding( FMargin( 4.0f, 0.0f, 11.0f, 0.0f ) )
[
SNew(SSpinBox<float>)
.MinValue(4.f)
.MaxValue(4096.f)
.ToolTipText(LOCTEXT( "LightingResolutionStaticMeshesMinToolTip", "The minimum lightmap resolution for static mesh adjustments. Anything outside of Min/Max range will not be touched when adjusting." ))
.Value(FLevelEditorActionCallbacks::GetLightingResolutionMinSMs())
.OnValueChanged_Static(&FLevelEditorActionCallbacks::SetLightingResolutionMinSMs)
]
+SHorizontalBox::Slot()
.AutoWidth()
[
SNew(SSpinBox<float>)
.MinValue(4.f)
.MaxValue(4096.f)
.ToolTipText(LOCTEXT( "LightingResolutionStaticMeshesMaxToolTip", "The maximum lightmap resolution for static mesh adjustments. Anything outside of Min/Max range will not be touched when adjusting." ))
.Value(FLevelEditorActionCallbacks::GetLightingResolutionMaxSMs())
.OnValueChanged_Static(&FLevelEditorActionCallbacks::SetLightingResolutionMaxSMs)
];
InMenuBuilder.AddWidget(Meshes, FText::GetEmpty(), true);
TSharedRef<SWidget> BSPs = SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.AutoWidth()
[
SNew( SCheckBox )
.Style(FEditorStyle::Get(), "Menu.CheckBox")
.ToolTipText(LOCTEXT( "BSPSurfacesToolTip", "BSP Surfaces will be adjusted if checked." ))
.IsChecked_Static(&FLevelEditorActionCallbacks::IsLightingResolutionBSPSurfacesChecked)
.OnCheckStateChanged_Static(&FLevelEditorActionCallbacks::SetLightingResolutionBSPSurfaces)
.Content()
[
SNew( STextBlock )
.Text( LOCTEXT("BSPSurfaces", "BSP Surfaces") )
]
]
+SHorizontalBox::Slot()
.AutoWidth()
.Padding( FMargin( 6.0f, 0.0f, 4.0f, 0.0f ) )
[
SNew(SSpinBox<float>)
.MinValue(1.f)
.MaxValue(63556.f)
.ToolTipText(LOCTEXT( "LightingResolutionBSPsMinToolTip", "The minimum lightmap resolution of a BSP surface to adjust. When outside of the Min/Max range, the BSP surface will no be altered." ))
.Value(FLevelEditorActionCallbacks::GetLightingResolutionMinBSPs())
.OnValueChanged_Static(&FLevelEditorActionCallbacks::SetLightingResolutionMinBSPs)
]
+SHorizontalBox::Slot()
.AutoWidth()
[
SNew(SSpinBox<float>)
.MinValue(1.f)
.MaxValue(63556.f)
.ToolTipText(LOCTEXT( "LightingResolutionBSPsMaxToolTip", "The maximum lightmap resolution of a BSP surface to adjust. When outside of the Min/Max range, the BSP surface will no be altered." ))
.Value(FLevelEditorActionCallbacks::GetLightingResolutionMaxBSPs())
.OnValueChanged_Static(&FLevelEditorActionCallbacks::SetLightingResolutionMaxBSPs)
];
InMenuBuilder.AddWidget(BSPs, FText::GetEmpty(), true);
}
InMenuBuilder.EndSection(); //LevelEditorBuildLightingResolution1
InMenuBuilder.BeginSection("LevelEditorBuildLightingResolution2", LOCTEXT( "LightingResolutionHeading2", "Select Options" ) );
{
InMenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingResolution_CurrentLevel );
InMenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingResolution_SelectedLevels );
InMenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingResolution_AllLoadedLevels );
InMenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingResolution_SelectedObjectsOnly );
}
InMenuBuilder.EndSection();
InMenuBuilder.BeginSection("LevelEditorBuildLightingResolution3", LOCTEXT( "LightingResolutionHeading3", "Ratio" ) );
{
TSharedRef<SWidget> Ratio = SNew(SSpinBox<int32>)
.MinValue(0)
.MaxValue(400)
.ToolTipText(LOCTEXT( "LightingResolutionRatioToolTip", "Ratio to apply (New Resolution = Ratio / 100.0f * CurrentResolution)." ))
.Value(FLevelEditorActionCallbacks::GetLightingResolutionRatio())
.OnEndSliderMovement_Static(&FLevelEditorActionCallbacks::SetLightingResolutionRatio)
.OnValueCommitted_Static(&FLevelEditorActionCallbacks::SetLightingResolutionRatioCommit);
InMenuBuilder.AddWidget(Ratio, LOCTEXT( "LightingResolutionRatio", "Ratio" ));
}
InMenuBuilder.EndSection();
}
/** Generates a lighting info dialogs sub-menu */
static void MakeLightingInfoMenu( FMenuBuilder& InMenuBuilder )
{
InMenuBuilder.BeginSection("LevelEditorBuildLightingInfo", LOCTEXT( "LightingInfoHeading", "Lighting Info Dialogs" ) );
{
InMenuBuilder.AddSubMenu(
LOCTEXT( "LightingDensityRenderingSubMenu", "LightMap Density Rendering Options" ),
LOCTEXT( "LightingDensityRenderingSubMenu_ToolTip", "Shows the LightMap Density Rendering viewmode options." ),
FNewMenuDelegate::CreateStatic( &FLightingMenus::MakeLightingDensityMenu ) );
InMenuBuilder.AddSubMenu(
LOCTEXT( "LightingResolutionAdjustmentSubMenu", "LightMap Resolution Adjustment" ),
LOCTEXT( "LightingResolutionAdjustmentSubMenu_ToolTip", "Shows the LightMap Resolution Adjustment options." ),
FNewMenuDelegate::CreateStatic( &FLightingMenus::MakeLightingResolutionMenu ) );
InMenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingStaticMeshInfo, NAME_None, LOCTEXT( "BuildLightingInfo_LightingStaticMeshInfo", "Lighting StaticMesh Info..." ) );
}
InMenuBuilder.EndSection();
}
};
MenuBuilder.BeginSection("LevelEditorLighting", LOCTEXT( "LightingHeading", "Lighting" ) );
{
MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().BuildLightingOnly, NAME_None, LOCTEXT( "BuildLightingOnlyHeading", "Build Lighting Only" ) );
MenuBuilder.AddSubMenu(
LOCTEXT( "LightingQualitySubMenu", "Lighting Quality" ),
LOCTEXT( "LightingQualitySubMenu_ToolTip", "Allows you to select the quality level for precomputed lighting" ),
FNewMenuDelegate::CreateStatic( &FLightingMenus::MakeLightingQualityMenu ) );
MenuBuilder.AddSubMenu(
LOCTEXT( "BuildLightingInfoSubMenu", "Lighting Info" ),
LOCTEXT( "BuildLightingInfoSubMenu_ToolTip", "Access the lighting info dialogs" ),
FNewMenuDelegate::CreateStatic( &FLightingMenus::MakeLightingInfoMenu ) );
MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingBuildOptions_UseErrorColoring );
MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingBuildOptions_ShowLightingStats );
}
MenuBuilder.EndSection();
MenuBuilder.BeginSection("LevelEditorReflections", LOCTEXT( "ReflectionHeading", "Reflections" ) );
{
MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().BuildReflectionCapturesOnly );
}
MenuBuilder.EndSection();
MenuBuilder.BeginSection("LevelEditorVisibility", LOCTEXT( "VisibilityHeading", "Visibility" ) );
{
MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().BuildLightingOnly_VisibilityOnly );
}
MenuBuilder.EndSection();
MenuBuilder.BeginSection("LevelEditorGeometry", LOCTEXT( "GeometryHeading", "Geometry" ) );
{
MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().BuildGeometryOnly );
MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().BuildGeometryOnly_OnlyCurrentLevel );
}
MenuBuilder.EndSection();
MenuBuilder.BeginSection("LevelEditorNavigation", LOCTEXT( "NavigationHeading", "Navigation" ) );
{
MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().BuildPathsOnly );
}
MenuBuilder.EndSection();
MenuBuilder.BeginSection("LevelEditorLOD", LOCTEXT("LODHeading", "Hierarchical LOD"));
{
MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().BuildLODsOnly);
}
MenuBuilder.EndSection();
MenuBuilder.BeginSection("LevelEditorTextureStreaming", LOCTEXT("TextureStreamingHeading", "Texture Streaming"));
if (CVarStreamingUseNewMetrics.GetValueOnAnyThread() != 0) // There is not point of in building texture streaming data with the old system.
{
MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().BuildTextureStreamingOnly);
}
MenuBuilder.EndSection();
MenuBuilder.BeginSection("LevelEditorAutomation", LOCTEXT( "AutomationHeading", "Automation" ) );
{
MenuBuilder.AddMenuEntry(
FLevelEditorCommands::Get().BuildAndSubmitToSourceControl,
NAME_None,
TAttribute<FText>(),
TAttribute<FText>(),
FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.BuildAndSubmit")
);
}
MenuBuilder.EndSection();
// Map Check
MenuBuilder.BeginSection("LevelEditorVerification", LOCTEXT( "VerificationHeading", "Verification" ) );
{
MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().MapCheck, NAME_None, LOCTEXT("OpenMapCheck", "Map Check") );
}
MenuBuilder.EndSection();
#undef LOCTEXT_NAMESPACE
return MenuBuilder.MakeWidget();
}
static void MakeMaterialQualityLevelMenu( FMenuBuilder& MenuBuilder )
{
MenuBuilder.BeginSection("LevelEditorMaterialQualityLevel", NSLOCTEXT( "LevelToolBarViewMenu", "MaterialQualityLevelHeading", "Material Quality Level" ) );
{
MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().MaterialQualityLevel_Low);
MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().MaterialQualityLevel_Medium);
MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().MaterialQualityLevel_High);
}
MenuBuilder.EndSection();
}
static void MakeShaderModelPreviewMenu( FMenuBuilder& MenuBuilder )
{
#define LOCTEXT_NAMESPACE "LevelToolBarViewMenu"
MenuBuilder.BeginSection("EditorPreviewMode", LOCTEXT("EditorPreviewModeDevices", "Preview Devices"));
for (int32 i = GMaxRHIFeatureLevel; i >= 0; --i)
{
switch (i)
{
case ERHIFeatureLevel::ES2:
MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().PreviewPlatformOverride_AndroidGLES2);
MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().PreviewPlatformOverride_DefaultES2);
break;
case ERHIFeatureLevel::ES3_1:
{
//MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().PreviewPlatformOverride_DefaultES31);
bool bAndroidBuildForES31 = false;
GConfig->GetBool(TEXT("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings"), TEXT("bBuildForES31"), bAndroidBuildForES31, GEngineIni);
if(bAndroidBuildForES31)
{
MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().PreviewPlatformOverride_AndroidGLES31);
}
bool bAndroidSupportsVulkan = false;
GConfig->GetBool(TEXT("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings"), TEXT("bSupportsVulkan"), bAndroidSupportsVulkan, GEngineIni);
if(bAndroidSupportsVulkan)
{
MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().PreviewPlatformOverride_AndroidVulkanES31);
}
bool bIOSSupportsMetal = false;
GConfig->GetBool(TEXT("/Script/IOSRuntimeSettings.IOSRuntimeSettings"), TEXT("bSupportsMetal"), bIOSSupportsMetal, GEngineIni);
if(bIOSSupportsMetal)
{
MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().PreviewPlatformOverride_IOSMetalES31);
}
break;
}
default:
MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().FeatureLevelPreview[i]);
}
}
MenuBuilder.EndSection();
#undef LOCTEXT_NAMESPACE
}
static void MakeScalabilityMenu( FMenuBuilder& MenuBuilder )
{
MenuBuilder.AddWidget(SNew(SScalabilitySettings), FText(), true);
}
static void MakePreviewSettingsMenu( FMenuBuilder& MenuBuilder )
{
#define LOCTEXT_NAMESPACE "LevelToolBarViewMenu"
MenuBuilder.BeginSection("LevelEditorPreview", LOCTEXT("PreviewHeading", "Previewing"));
{
MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().DrawBrushMarkerPolys);
MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().OnlyLoadVisibleInPIE);
MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().ToggleParticleSystemLOD);
MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().ToggleParticleSystemHelpers);
MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().ToggleFreezeParticleSimulation);
MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().ToggleLODViewLocking);
MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().LevelStreamingVolumePrevis);
}
MenuBuilder.EndSection();
#undef LOCTEXT_NAMESPACE
}
#if WITH_LIVE_CODING
TSharedRef< SWidget > FLevelEditorToolBar::GenerateCompileMenuContent( TSharedRef<FUICommandList> InCommandList )
{
#define LOCTEXT_NAMESPACE "LevelToolBarCompileMenu"
const bool bShouldCloseWindowAfterMenuSelection = true;
FMenuBuilder MenuBuilder( bShouldCloseWindowAfterMenuSelection, InCommandList );
MenuBuilder.BeginSection("LiveCodingMode", LOCTEXT( "LiveCodingMode", "General" ) );
{
MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LiveCoding_Enable );
}
MenuBuilder.EndSection();
MenuBuilder.BeginSection("LiveCodingActions", LOCTEXT( "LiveCodingActions", "Actions" ) );
{
MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LiveCoding_StartSession );
MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LiveCoding_ShowConsole );
MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LiveCoding_Settings );
}
MenuBuilder.EndSection();
return MenuBuilder.MakeWidget();
#undef LOCTEXT_NAMESPACE
}
#endif
TSharedRef< SWidget > FLevelEditorToolBar::GenerateQuickSettingsMenu( TSharedRef<FUICommandList> InCommandList )
{
#define LOCTEXT_NAMESPACE "LevelToolBarViewMenu"
// Get all menu extenders for this context menu from the level editor module
FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked<FLevelEditorModule>( TEXT("LevelEditor") );
TArray<FLevelEditorModule::FLevelEditorMenuExtender> MenuExtenderDelegates = LevelEditorModule.GetAllLevelEditorToolbarViewMenuExtenders();
TArray<TSharedPtr<FExtender>> Extenders;
for (int32 i = 0; i < MenuExtenderDelegates.Num(); ++i)
{
if (MenuExtenderDelegates[i].IsBound())
{
Extenders.Add(MenuExtenderDelegates[i].Execute(InCommandList));
}
}
TSharedPtr<FExtender> MenuExtender = FExtender::Combine(Extenders);
const bool bShouldCloseWindowAfterMenuSelection = true;
FMenuBuilder MenuBuilder( bShouldCloseWindowAfterMenuSelection, InCommandList, MenuExtender );
struct Local
{
static void OpenSettings(FName ContainerName, FName CategoryName, FName SectionName)
{
FModuleManager::LoadModuleChecked<ISettingsModule>("Settings").ShowViewer(ContainerName, CategoryName, SectionName);
}
};
MenuBuilder.BeginSection("ProjectSettingsSection", LOCTEXT("ProjectSettings","Game Specific Settings") );
{
MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().WorldProperties);
MenuBuilder.AddMenuEntry(
LOCTEXT("ProjectSettingsMenuLabel", "Project Settings..."),
LOCTEXT("ProjectSettingsMenuToolTip", "Change the settings of the currently loaded project"),
FSlateIcon(FEditorStyle::GetStyleSetName(), "ProjectSettings.TabIcon"),
FUIAction(FExecuteAction::CreateStatic(&Local::OpenSettings, FName("Project"), FName("Project"), FName("General")))
);
if (IModularFeatures::Get().IsModularFeatureAvailable(EditorFeatures::PluginsEditor))
{
FGlobalTabmanager::Get()->PopulateTabSpawnerMenu(MenuBuilder, "PluginsEditor");
}
}
MenuBuilder.EndSection();
MenuBuilder.BeginSection("LevelEditorSelection", LOCTEXT("SelectionHeading","Selection") );
{
MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().AllowTranslucentSelection );
MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().AllowGroupSelection );
MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().StrictBoxSelect );
MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().TransparentBoxSelect );
MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().ShowTransformWidget );
}
MenuBuilder.EndSection();
MenuBuilder.BeginSection("LevelEditorScalability", LOCTEXT("ScalabilityHeading", "Scalability") );
{
MenuBuilder.AddSubMenu(
LOCTEXT( "ScalabilitySubMenu", "Engine Scalability Settings" ),
LOCTEXT( "ScalabilitySubMenu_ToolTip", "Open the engine scalability settings" ),
FNewMenuDelegate::CreateStatic( &MakeScalabilityMenu ) );
MenuBuilder.AddSubMenu(
LOCTEXT( "MaterialQualityLevelSubMenu", "Material Quality Level" ),
LOCTEXT( "MaterialQualityLevelSubMenu_ToolTip", "Sets the value of the CVar \"r.MaterialQualityLevel\" (low=0, high=1, medium=2). This affects materials via the QualitySwitch material expression." ),
FNewMenuDelegate::CreateStatic( &MakeMaterialQualityLevelMenu ) );
MenuBuilder.AddSubMenu(
LOCTEXT("FeatureLevelPreviewSubMenu", "Preview Rendering Level"),
LOCTEXT("FeatureLevelPreviewSubMenu_ToolTip", "Sets the rendering level used by the main editor"),
FNewMenuDelegate::CreateStatic(&MakeShaderModelPreviewMenu));
}
MenuBuilder.EndSection();
MenuBuilder.BeginSection("LevelEditorAudio", LOCTEXT("AudioHeading", "Real Time Audio") );
{
TSharedRef<SWidget> VolumeItem = SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.FillWidth(0.9f)
.Padding( FMargin(2.0f, 0.0f, 0.0f, 0.0f) )
[
SNew(SVolumeControl)
.ToolTipText_Static(&FLevelEditorActionCallbacks::GetAudioVolumeToolTip)
.Volume_Static(&FLevelEditorActionCallbacks::GetAudioVolume)
.OnVolumeChanged_Static(&FLevelEditorActionCallbacks::OnAudioVolumeChanged)
.Muted_Static(&FLevelEditorActionCallbacks::GetAudioMuted)
.OnMuteChanged_Static(&FLevelEditorActionCallbacks::OnAudioMutedChanged)
]
+SHorizontalBox::Slot()
.FillWidth(0.1f);
MenuBuilder.AddWidget(VolumeItem, LOCTEXT("VolumeControlLabel","Volume"));
}
MenuBuilder.EndSection();
MenuBuilder.BeginSection( "Snapping", LOCTEXT("SnappingHeading","Snapping") );
{
MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().EnableActorSnap );
TSharedRef<SWidget> 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);
MenuBuilder.AddWidget(SnapItem, LOCTEXT("ActorSnapLabel","Distance"));
MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().ToggleSocketSnapping );
MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().EnableVertexSnap );
}
MenuBuilder.EndSection();
MenuBuilder.BeginSection("LevelEditorViewport", LOCTEXT("ViewportHeading", "Viewport") );
{
MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().ToggleHideViewportUI );
MenuBuilder.AddSubMenu( LOCTEXT("PreviewMenu", "Previewing"), LOCTEXT("PreviewMenuTooltip","Game Preview Settings"), FNewMenuDelegate::CreateStatic( &MakePreviewSettingsMenu ) );
}
MenuBuilder.EndSection();
#undef LOCTEXT_NAMESPACE
return MenuBuilder.MakeWidget();
}
TSharedRef< SWidget > FLevelEditorToolBar::GenerateSourceControlMenu(TSharedRef<FUICommandList> InCommandList)
{
#define LOCTEXT_NAMESPACE "LevelToolBarSourceControlMenu"
// Get all menu extenders for this context menu from the level editor module
FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked<FLevelEditorModule>(TEXT("LevelEditor"));
TArray<FLevelEditorModule::FLevelEditorMenuExtender> MenuExtenderDelegates = LevelEditorModule.GetAllLevelEditorToolbarSourceControlMenuExtenders();
TArray<TSharedPtr<FExtender>> Extenders;
for (int32 i = 0; i < MenuExtenderDelegates.Num(); ++i)
{
if (MenuExtenderDelegates[i].IsBound())
{
Extenders.Add(MenuExtenderDelegates[i].Execute(InCommandList));
}
}
TSharedPtr<FExtender> MenuExtender = FExtender::Combine(Extenders);
const bool bShouldCloseWindowAfterMenuSelection = true;
FMenuBuilder MenuBuilder(bShouldCloseWindowAfterMenuSelection, InCommandList, MenuExtender);
MenuBuilder.BeginSection("SourceControlActions", LOCTEXT("SourceControlMenuHeadingActions", "Actions"));
ISourceControlModule& SourceControlModule = ISourceControlModule::Get();
if (ISourceControlModule::Get().IsEnabled() && ISourceControlModule::Get().GetProvider().IsAvailable())
{
MenuBuilder.AddMenuEntry(
FLevelEditorCommands::Get().ChangeSourceControlSettings,
NAME_None,
TAttribute<FText>(),
TAttribute<FText>(),
FSlateIcon(FEditorStyle::GetStyleSetName(), "SourceControl.Actions.ChangeSettings")
);
}
else
{
MenuBuilder.AddMenuEntry(
FLevelEditorCommands::Get().ConnectToSourceControl,
NAME_None,
TAttribute<FText>(),
TAttribute<FText>(),
FSlateIcon(FEditorStyle::GetStyleSetName(), "SourceControl.Actions.Connect")
);
}
MenuBuilder.AddMenuSeparator("SourceControlConnectionSeparator");
MenuBuilder.AddMenuEntry(
FLevelEditorCommands::Get().CheckOutModifiedFiles,
NAME_None,
TAttribute<FText>(),
TAttribute<FText>(),
FSlateIcon(FEditorStyle::GetStyleSetName(), "SourceControl.Actions.CheckOut")
);
MenuBuilder.AddMenuEntry(
FLevelEditorCommands::Get().SubmitToSourceControl,
NAME_None,
TAttribute<FText>(),
TAttribute<FText>(),
FSlateIcon(FEditorStyle::GetStyleSetName(), "SourceControl.Actions.Submit")
);
MenuBuilder.EndSection();
#undef LOCTEXT_NAMESPACE
return MenuBuilder.MakeWidget();
}
TSharedRef< SWidget > FLevelEditorToolBar::GenerateOpenBlueprintMenuContent( TSharedRef<FUICommandList> InCommandList, TWeakPtr< SLevelEditor > InLevelEditor )
{
#define LOCTEXT_NAMESPACE "LevelToolBarViewMenu"
struct FBlueprintMenus
{
/** Generates a sub-level Blueprints sub-menu */
static void MakeSubLevelsMenu(FMenuBuilder& InMenuBuilder, TWeakPtr< SLevelEditor > InLvlEditor)
{
FSlateIcon EditBP(FEditorStyle::Get().GetStyleSetName(), TEXT("LevelEditor.OpenLevelBlueprint"));
InMenuBuilder.BeginSection(NAME_None, LOCTEXT("SubLevelsHeading", "Sub-Level Blueprints"));
{
UWorld* World = InLvlEditor.Pin()->GetWorld();
for (int32 iLevel = 0; iLevel < World->GetNumLevels(); iLevel++)
{
ULevel* Level = World->GetLevel(iLevel);
if (Level != NULL && Level->GetOutermost() != NULL)
{
if (!Level->IsPersistentLevel())
{
FUIAction UIAction
(
FExecuteAction::CreateStatic(&FLevelEditorToolBar::OnOpenSubLevelBlueprint, Level)
);
FText DisplayName = FText::Format(LOCTEXT("SubLevelBlueprintItem", "Edit {0}"), FText::FromString(FPaths::GetCleanFilename(Level->GetOutermost()->GetName())));
InMenuBuilder.AddMenuEntry(DisplayName, FText::GetEmpty(), EditBP, UIAction);
}
}
}
}
InMenuBuilder.EndSection();
}
/** Handle BP being selected from popup picker */
static void OnBPSelected(const struct FAssetData& AssetData)
{
UBlueprint* SelectedBP = Cast<UBlueprint>(AssetData.GetAsset());
if(SelectedBP)
{
FAssetEditorManager::Get().OpenEditorForAsset(SelectedBP);
}
}
/** Generates 'open blueprint' sub-menu */
static void MakeOpenBPClassMenu(FMenuBuilder& InMenuBuilder)
{
FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked<FContentBrowserModule>(TEXT("ContentBrowser"));
// Configure filter for asset picker
FAssetPickerConfig Config;
Config.Filter.ClassNames.Add(UBlueprint::StaticClass()->GetFName());
Config.InitialAssetViewType = EAssetViewType::List;
Config.OnAssetSelected = FOnAssetSelected::CreateStatic(&FBlueprintMenus::OnBPSelected);
Config.bAllowDragging = false;
// Don't show stuff in Engine
Config.Filter.PackagePaths.Add("/Game");
Config.Filter.bRecursivePaths = true;
TSharedRef<SWidget> Widget =
SNew(SBox)
.WidthOverride(300.f)
.HeightOverride(300.f)
[
ContentBrowserModule.Get().CreateAssetPicker(Config)
];
InMenuBuilder.BeginSection(NAME_None, LOCTEXT("BrowseHeader", "Browse"));
{
InMenuBuilder.AddWidget(Widget, FText::GetEmpty());
}
InMenuBuilder.EndSection();
}
};
const bool bShouldCloseWindowAfterMenuSelection = true;
FMenuBuilder MenuBuilder( bShouldCloseWindowAfterMenuSelection, InCommandList );
MenuBuilder.BeginSection(NAME_None, LOCTEXT("BlueprintClass", "Blueprint Class"));
{
// Create a blank BP
MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().CreateBlankBlueprintClass);
// Convert selection to BP
MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().ConvertSelectionToBlueprintViaHarvest);
MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().ConvertSelectionToBlueprintViaSubclass);
// Open an existing Blueprint Class...
FSlateIcon OpenBPIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.OpenClassBlueprint");
MenuBuilder.AddSubMenu(
LOCTEXT("OpenBlueprintClassSubMenu", "Open Blueprint Class..."),
LOCTEXT("OpenBlueprintClassSubMenu_ToolTip", "Open an existing Blueprint Class in this project"),
FNewMenuDelegate::CreateStatic(&FBlueprintMenus::MakeOpenBPClassMenu),
false,
OpenBPIcon);
}
MenuBuilder.EndSection();
MenuBuilder.BeginSection(NAME_None, LOCTEXT("LevelScriptBlueprints", "Level Blueprints"));
{
MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().OpenLevelBlueprint );
// If there are any sub-levels, display the sub-menu. A single level means there is only the persistent level
UWorld* World = InLevelEditor.Pin()->GetWorld();
if(World->GetNumLevels() > 1)
{
MenuBuilder.AddSubMenu(
LOCTEXT( "SubLevelsSubMenu", "Sub-Levels" ),
LOCTEXT( "SubLevelsSubMenu_ToolTip", "Shows available sub-level Blueprints that can be edited." ),
FNewMenuDelegate::CreateStatic( &FBlueprintMenus::MakeSubLevelsMenu, InLevelEditor ),
FUIAction(), NAME_None, EUserInterfaceActionType::Button, false, FSlateIcon(FEditorStyle::Get().GetStyleSetName(), TEXT("LevelEditor.OpenLevelBlueprint")) );
}
}
MenuBuilder.EndSection();
MenuBuilder.BeginSection(NAME_None, LOCTEXT("ProjectSettingsClasses", "Project Settings"));
{
// 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);
}
LevelEditorActionHelpers::CreateGameModeSubMenu(MenuBuilder, InCommandList, InLevelEditor, true);
}
MenuBuilder.EndSection();
MenuBuilder.BeginSection(NAME_None, LOCTEXT("WorldSettingsClasses", "World Override"));
{
LevelEditorActionHelpers::CreateGameModeSubMenu(MenuBuilder, InCommandList, InLevelEditor, false);
}
MenuBuilder.EndSection();
#undef LOCTEXT_NAMESPACE
return MenuBuilder.MakeWidget();
}
void FLevelEditorToolBar::OnOpenSubLevelBlueprint( ULevel* InLevel )
{
ULevelScriptBlueprint* LevelScriptBlueprint = InLevel->GetLevelScriptBlueprint();
if( LevelScriptBlueprint )
{
FAssetEditorManager::Get().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<FUICommandList> InCommandList, TWeakPtr<SLevelEditor> LevelEditorWeakPtr )
{
#define LOCTEXT_NAMESPACE "LevelToolBarCinematicsMenu"
FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");
TSharedPtr<FExtender> Extender = FExtender::Combine(LevelEditorModule.GetAllLevelEditorToolbarCinematicsMenuExtenders());
const bool bShouldCloseWindowAfterMenuSelection = true;
FMenuBuilder MenuBuilder( bShouldCloseWindowAfterMenuSelection, InCommandList, Extender );
using namespace SceneOutliner;
// We can't build a list of Matinees and LevelSequenceActors while the current World is a PIE world.
FInitializationOptions InitOptions;
{
InitOptions.Mode = ESceneOutlinerMode::ActorPicker;
// 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 = false;
InitOptions.bShowCreateNewFolder = false;
InitOptions.ColumnMap.Add(FBuiltInColumnTypes::Label(), FColumnInfo(EColumnVisibility::Visible, 0));
InitOptions.ColumnMap.Add(FBuiltInColumnTypes::ActorInfo(), FColumnInfo(EColumnVisibility::Visible, 10));
// Only display Matinee and MovieScene actors
auto ActorFilter = [](const AActor* Actor){
return Actor->IsA( AMatineeActor::StaticClass() ) || Actor->IsA( ALevelSequenceActor::StaticClass() );
};
InitOptions.Filters->AddFilterPredicate( FActorFilterPredicate::CreateLambda( ActorFilter ) );
}
// actor selector to allow the user to choose an actor
FSceneOutlinerModule& SceneOutlinerModule = FModuleManager::LoadModuleChecked<FSceneOutlinerModule>( "SceneOutliner" );
TSharedRef< SWidget > MiniSceneOutliner =
SNew( SVerticalBox )
+SVerticalBox::Slot()
.AutoHeight()
.MaxHeight(400.0f)
[
SceneOutlinerModule.CreateSceneOutliner(
InitOptions,
FOnActorPicked::CreateStatic( &FLevelEditorToolBar::OnCinematicsActorPicked ) )
];
static const FName DefaultForegroundName("DefaultForeground");
// Give the scene outliner a border and background
const FSlateBrush* BackgroundBrush = FEditorStyle::GetBrush( "Menu.Background" );
TSharedRef< SBorder > RootBorder =
SNew( SBorder )
.Padding(3)
.BorderImage( BackgroundBrush )
.ForegroundColor( FEditorStyle::GetSlateColor(DefaultForegroundName) )
// Assign the box panel as the child
[
SNew( SVerticalBox )
+SVerticalBox::Slot()
.AutoHeight()
.Padding( 5 )
.HAlign( HAlign_Center )
[
SNew( STextBlock )
.Text( LOCTEXT( "SelectCinematicsActorToEdit", "Select an actor" ) )
]
+SVerticalBox::Slot()
.AutoHeight()
.Padding( 2 )
[
MiniSceneOutliner
]
]
;
MenuBuilder.BeginSection("LevelEditorNewMatinee", LOCTEXT("MatineeMenuCombo_NewHeading", "New"));
{
MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().AddMatinee, NAME_None, TAttribute<FText>(), TAttribute<FText>(), FSlateIcon(FEditorStyle::GetStyleSetName(), "ClassIcon.MatineeActor") );
}
MenuBuilder.EndSection();
UWorld* World = LevelEditorWeakPtr.Pin()->GetWorld();
const bool bHasAnyCinematicsActors = !!TActorIterator<AMatineeActor>(World) || !!TActorIterator<ALevelSequenceActor>(World);
//Add a heading to separate the existing cinematics from the 'Add New Cinematic Actor' button
MenuBuilder.BeginSection("LevelEditorExistingCinematic", LOCTEXT( "CinematicMenuCombo_ExistingHeading", "Edit Existing Cinematic" ) );
{
if( bHasAnyCinematicsActors )
{
MenuBuilder.AddWidget(MiniSceneOutliner, FText::GetEmpty(), true);
}
}
MenuBuilder.EndSection();
#undef LOCTEXT_NAMESPACE
return MenuBuilder.MakeWidget();
}
void FLevelEditorToolBar::OnCinematicsActorPicked( AActor* Actor )
{
//The matinee 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 (AMatineeActor* MatineeActor = Cast<AMatineeActor>(Actor))
{
// Open Matinee for editing!
GEditor->OpenMatinee( MatineeActor );
}
else if (ALevelSequenceActor* LevelSequenceActor = Cast<ALevelSequenceActor>(Actor))
{
FScopedSlowTask SlowTask(1.f, NSLOCTEXT("LevelToolBarCinematicsMenu", "LoadSequenceSlowTask", "Loading Level Sequence..."));
SlowTask.MakeDialog();
SlowTask.EnterProgressFrame();
UObject* Asset = LevelSequenceActor->LevelSequence.TryLoad();
if (Asset != nullptr)
{
FAssetEditorManager::Get().OpenEditorForAsset(Asset);
}
}
}