You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
1633 lines
61 KiB
C++
1633 lines
61 KiB
C++
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "SLevelEditor.h"
|
|
#include "Framework/MultiBox/MultiBoxExtender.h"
|
|
#include "Framework/Docking/LayoutService.h"
|
|
#include "EditorModeRegistry.h"
|
|
#include "EdMode.h"
|
|
#include "Engine/Selection.h"
|
|
#include "Misc/MessageDialog.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "Styling/SlateStyleRegistry.h"
|
|
#include "Framework/Application/SlateApplication.h"
|
|
#include "Widgets/Layout/SBorder.h"
|
|
#include "Widgets/Layout/SBox.h"
|
|
#include "EditorStyleSet.h"
|
|
#include "Editor/EditorPerProjectUserSettings.h"
|
|
#include "Editor/UnrealEdEngine.h"
|
|
#include "Settings/EditorExperimentalSettings.h"
|
|
#include "LevelEditorViewport.h"
|
|
#include "EditorModes.h"
|
|
#include "UnrealEdGlobals.h"
|
|
#include "LevelEditor.h"
|
|
#include "LevelEditorMenu.h"
|
|
#include "IDetailsView.h"
|
|
#include "LevelEditorActions.h"
|
|
#include "LevelEditorModesActions.h"
|
|
#include "LevelEditorContextMenu.h"
|
|
#include "LevelEditorToolBar.h"
|
|
#include "SLevelEditorToolBox.h"
|
|
#include "SLevelEditorModeContent.h"
|
|
#include "SLevelEditorBuildAndSubmit.h"
|
|
#include "Kismet2/DebuggerCommands.h"
|
|
#include "SceneOutlinerPublicTypes.h"
|
|
#include "SceneOutlinerModule.h"
|
|
#include "Editor/Layers/Public/LayersModule.h"
|
|
#include "Editor/WorldBrowser/Public/WorldBrowserModule.h"
|
|
#include "Toolkits/ToolkitManager.h"
|
|
#include "PropertyEditorModule.h"
|
|
#include "Interfaces/IMainFrameModule.h"
|
|
#include "Editor/WorkspaceMenuStructure/Public/WorkspaceMenuStructure.h"
|
|
#include "Editor/WorkspaceMenuStructure/Public/WorkspaceMenuStructureModule.h"
|
|
#include "StatsViewerModule.h"
|
|
#include "Widgets/SToolTip.h"
|
|
#include "IDocumentation.h"
|
|
#include "TutorialMetaData.h"
|
|
#include "Widgets/Docking/SDockTab.h"
|
|
#include "SActorDetails.h"
|
|
#include "GameFramework/WorldSettings.h"
|
|
#include "Framework/Docking/LayoutExtender.h"
|
|
#include "HierarchicalLODOutlinerModule.h"
|
|
|
|
|
|
static const FName LevelEditorBuildAndSubmitTab("LevelEditorBuildAndSubmit");
|
|
static const FName LevelEditorStatsViewerTab("LevelEditorStatsViewer");
|
|
static const FName MainFrameModuleName("MainFrame");
|
|
static const FName LevelEditorModuleName("LevelEditor");
|
|
static const FName WorldBrowserHierarchyTab("WorldBrowserHierarchy");
|
|
static const FName WorldBrowserDetailsTab("WorldBrowserDetails");
|
|
static const FName WorldBrowserCompositionTab("WorldBrowserComposition");
|
|
|
|
|
|
namespace LevelEditorConstants
|
|
{
|
|
/** The size of the thumbnail pool */
|
|
const int32 ThumbnailPoolSize = 32;
|
|
}
|
|
|
|
SLevelEditor::SLevelEditor()
|
|
: World(NULL)
|
|
{
|
|
const bool bAreRealTimeThumbnailsAllowed = false;
|
|
ThumbnailPool = MakeShareable(new FAssetThumbnailPool(LevelEditorConstants::ThumbnailPoolSize, bAreRealTimeThumbnailsAllowed));
|
|
}
|
|
|
|
void SLevelEditor::BindCommands()
|
|
{
|
|
LevelEditorCommands = MakeShareable( new FUICommandList );
|
|
|
|
const FLevelEditorCommands& Actions = FLevelEditorCommands::Get();
|
|
|
|
// Map UI commands to delegates that are executed when the command is handled by a keybinding or menu
|
|
FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked<FLevelEditorModule>( LevelEditorModuleName );
|
|
|
|
// Append the list of the level editor commands for this instance with the global list of commands for all instances.
|
|
LevelEditorCommands->Append( LevelEditorModule.GetGlobalLevelEditorActions() );
|
|
|
|
// Append the list of global PlayWorld commands
|
|
LevelEditorCommands->Append( FPlayWorldCommands::GlobalPlayWorldActions.ToSharedRef() );
|
|
|
|
LevelEditorCommands->MapAction(
|
|
Actions.EditAssetNoConfirmMultiple,
|
|
FExecuteAction::CreateStatic( &FLevelEditorActionCallbacks::EditAsset_Clicked, EToolkitMode::Standalone, TWeakPtr< SLevelEditor >( SharedThis( this ) ), false ) );
|
|
|
|
LevelEditorCommands->MapAction(
|
|
Actions.EditAsset,
|
|
FExecuteAction::CreateStatic( &FLevelEditorActionCallbacks::EditAsset_Clicked, EToolkitMode::Standalone, TWeakPtr< SLevelEditor >( SharedThis( this ) ), true ) );
|
|
|
|
LevelEditorCommands->MapAction(
|
|
Actions.CheckOutProjectSettingsConfig,
|
|
FExecuteAction::CreateStatic( &FLevelEditorActionCallbacks::CheckOutProjectSettingsConfig ) );
|
|
|
|
LevelEditorCommands->MapAction(
|
|
Actions.OpenLevelBlueprint,
|
|
FExecuteAction::CreateStatic< TWeakPtr< SLevelEditor > >( &FLevelEditorActionCallbacks::OpenLevelBlueprint, SharedThis( this ) ) );
|
|
|
|
LevelEditorCommands->MapAction(
|
|
Actions.CreateBlankBlueprintClass,
|
|
FExecuteAction::CreateStatic( &FLevelEditorActionCallbacks::CreateBlankBlueprintClass ) );
|
|
|
|
LevelEditorCommands->MapAction(
|
|
Actions.ConvertSelectionToBlueprintViaHarvest,
|
|
FExecuteAction::CreateStatic( &FLevelEditorActionCallbacks::HarvestSelectedActorsIntoBlueprintClass ),
|
|
FCanExecuteAction::CreateStatic( &FLevelEditorActionCallbacks::CanHarvestSelectedActorsIntoBlueprintClass ) );
|
|
|
|
LevelEditorCommands->MapAction(
|
|
Actions.ConvertSelectionToBlueprintViaSubclass,
|
|
FExecuteAction::CreateStatic( &FLevelEditorActionCallbacks::SubclassSelectedActorIntoBlueprintClass ),
|
|
FCanExecuteAction::CreateStatic( &FLevelEditorActionCallbacks::CanSubclassSelectedActorIntoBlueprintClass ) );
|
|
|
|
LevelEditorCommands->MapAction(
|
|
Actions.OpenContentBrowser,
|
|
FExecuteAction::CreateStatic( &FLevelEditorActionCallbacks::OpenContentBrowser ) );
|
|
|
|
LevelEditorCommands->MapAction(
|
|
Actions.OpenMarketplace,
|
|
FExecuteAction::CreateStatic( &FLevelEditorActionCallbacks::OpenMarketplace ) );
|
|
|
|
LevelEditorCommands->MapAction(
|
|
Actions.ToggleVR,
|
|
FExecuteAction::CreateStatic( &FLevelEditorActionCallbacks::ToggleVR ),
|
|
FCanExecuteAction::CreateStatic( &FLevelEditorActionCallbacks::ToggleVR_CanExecute ),
|
|
FIsActionChecked::CreateStatic( &FLevelEditorActionCallbacks::ToggleVR_IsChecked ),
|
|
FIsActionButtonVisible::CreateStatic(&FLevelEditorActionCallbacks::ToggleVR_IsButtonActive));
|
|
|
|
LevelEditorCommands->MapAction(
|
|
Actions.WorldProperties,
|
|
FExecuteAction::CreateStatic< TWeakPtr< SLevelEditor > >( &FLevelEditorActionCallbacks::OnShowWorldProperties, SharedThis( this ) ) );
|
|
|
|
LevelEditorCommands->MapAction(
|
|
Actions.FocusAllViewportsToSelection,
|
|
FExecuteAction::CreateStatic( &FLevelEditorActionCallbacks::ExecuteExecCommand, FString( TEXT("CAMERA ALIGN") ) )
|
|
);
|
|
}
|
|
|
|
void SLevelEditor::Construct( const SLevelEditor::FArguments& InArgs)
|
|
{
|
|
// Important: We use raw bindings here because we are releasing our binding in our destructor (where a weak pointer would be invalid)
|
|
// It's imperative that our delegate is removed in the destructor for the level editor module to play nicely with reloading.
|
|
|
|
FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked< FLevelEditorModule >( LevelEditorModuleName );
|
|
LevelEditorModule.OnNotificationBarChanged().AddRaw( this, &SLevelEditor::ConstructNotificationBar );
|
|
|
|
GetMutableDefault<UEditorExperimentalSettings>()->OnSettingChanged().AddRaw(this, &SLevelEditor::HandleExperimentalSettingChanged);
|
|
|
|
BindCommands();
|
|
|
|
// We need to register when modes list changes so that we can refresh the auto generated commands.
|
|
FEditorModeRegistry::Get().OnRegisteredModesChanged().AddRaw(this, &SLevelEditor::EditorModeCommandsChanged);
|
|
|
|
// @todo This is a hack to get this working for now. This won't work with multiple worlds
|
|
GEditor->GetEditorWorldContext(true).AddRef(World);
|
|
|
|
// Set the initial preview feature level.
|
|
UEditorEngine* Editor = (UEditorEngine*)GEngine;
|
|
World->ChangeFeatureLevel(Editor->GetActiveFeatureLevelPreviewType());
|
|
|
|
// Patch into the OnPreviewFeatureLevelChanged() delegate to swap out the current feature level with a user selection.
|
|
PreviewFeatureLevelChangedHandle = Editor->OnPreviewFeatureLevelChanged().AddLambda([this](ERHIFeatureLevel::Type NewFeatureLevel)
|
|
{
|
|
World->ChangeFeatureLevel(NewFeatureLevel);
|
|
});
|
|
|
|
FEditorDelegates::MapChange.AddRaw(this, &SLevelEditor::HandleEditorMapChange);
|
|
HandleEditorMapChange(MapChangeEventFlags::NewMap);
|
|
}
|
|
|
|
void SLevelEditor::Initialize( const TSharedRef<SDockTab>& OwnerTab, const TSharedRef<SWindow>& OwnerWindow )
|
|
{
|
|
// Bind the level editor tab's label to the currently loaded level name string in the main frame
|
|
OwnerTab->SetLabel( TAttribute<FText>( this, &SLevelEditor::GetTabTitle) );
|
|
OwnerTab->SetTabLabelSuffix(TAttribute<FText>(this, &SLevelEditor::GetTabSuffix));
|
|
|
|
FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked< FLevelEditorModule >(LevelEditorModuleName);
|
|
|
|
LevelEditorModule.OnActorSelectionChanged().AddSP(this, &SLevelEditor::OnActorSelectionChanged);
|
|
|
|
TSharedRef<SWidget> Widget2 = RestoreContentArea( OwnerTab, OwnerWindow );
|
|
TSharedRef<SWidget> Widget1 = FLevelEditorMenu::MakeLevelEditorMenu( LevelEditorCommands, SharedThis(this) );
|
|
|
|
ChildSlot
|
|
[
|
|
SNew( SVerticalBox )
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
[
|
|
SNew( SOverlay )
|
|
|
|
+SOverlay::Slot()
|
|
[
|
|
SNew( SBox )
|
|
.AddMetaData<FTagMetaData>(FTagMetaData(TEXT("MainMenu")))
|
|
[
|
|
Widget1
|
|
]
|
|
]
|
|
|
|
// For platforms without a global menu bar we can put the perf. tools in the editor window's menu bar
|
|
#if !PLATFORM_MAC
|
|
+ SOverlay::Slot()
|
|
.HAlign( HAlign_Right )
|
|
.VAlign( VAlign_Center )
|
|
[
|
|
SAssignNew( NotificationBarBox, SHorizontalBox )
|
|
.AddMetaData<FTagMetaData>(FTagMetaData(TEXT("PerformanceTools")))
|
|
]
|
|
#endif
|
|
]
|
|
|
|
#if PLATFORM_MAC
|
|
// Without the in-window menu bar, we need some space between the tab bar and tab contents
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
[
|
|
SNew( SBox )
|
|
.HeightOverride( 1.0f )
|
|
]
|
|
#endif
|
|
|
|
+SVerticalBox::Slot()
|
|
.FillHeight( 1.0f )
|
|
[
|
|
Widget2
|
|
]
|
|
];
|
|
|
|
// For OS X we need to put it into the window's title bar since there's no per-window menu bar
|
|
#if PLATFORM_MAC
|
|
OwnerTab->SetRightContent(
|
|
SAssignNew( NotificationBarBox, SHorizontalBox )
|
|
.AddMetaData<FTagMetaData>(FTagMetaData(TEXT("PerformanceTools")))
|
|
);
|
|
#endif
|
|
|
|
ConstructNotificationBar();
|
|
|
|
OnLayoutHasChanged();
|
|
}
|
|
|
|
void SLevelEditor::ConstructNotificationBar()
|
|
{
|
|
NotificationBarBox->ClearChildren();
|
|
|
|
// level editor commands
|
|
NotificationBarBox->AddSlot()
|
|
.AutoWidth()
|
|
.Padding(5.0f, 0.0f, 0.0f, 0.0f)
|
|
[
|
|
FLevelEditorMenu::MakeNotificationBar( LevelEditorCommands, SharedThis(this ) )
|
|
];
|
|
|
|
// developer tools
|
|
const IMainFrameModule& MainFrameModule = FModuleManager::GetModuleChecked<IMainFrameModule>(MainFrameModuleName);
|
|
const FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked< FLevelEditorModule >(LevelEditorModuleName);
|
|
|
|
TArray<FMainFrameDeveloperTool> Tools;
|
|
for (const TTuple<FName, FLevelEditorModule::FStatusBarItem>& Item : LevelEditorModule.GetStatusBarItems())
|
|
{
|
|
Tools.Add({Item.Value.Visibility, Item.Value.Label, Item.Value.Value});
|
|
}
|
|
|
|
NotificationBarBox->AddSlot()
|
|
.AutoWidth()
|
|
.Padding(5.0f, 0.0f, 0.0f, 0.0f)
|
|
[
|
|
MainFrameModule.MakeDeveloperTools( Tools )
|
|
];
|
|
}
|
|
|
|
|
|
SLevelEditor::~SLevelEditor()
|
|
{
|
|
// We're going away now, so make sure all toolkits that are hosted within this level editor are shut down
|
|
FToolkitManager::Get().OnToolkitHostDestroyed( this );
|
|
HostedToolkits.Reset();
|
|
|
|
FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked< FLevelEditorModule >( LevelEditorModuleName );
|
|
LevelEditorModule.OnNotificationBarChanged().RemoveAll( this );
|
|
|
|
if(UObjectInitialized())
|
|
{
|
|
GetMutableDefault<UEditorExperimentalSettings>()->OnSettingChanged().RemoveAll(this);
|
|
GetMutableDefault<UEditorPerProjectUserSettings>()->OnUserSettingChanged().RemoveAll(this);
|
|
}
|
|
|
|
FEditorModeRegistry::Get().OnRegisteredModesChanged().RemoveAll( this );
|
|
|
|
FEditorDelegates::MapChange.RemoveAll(this);
|
|
|
|
if (GEngine)
|
|
{
|
|
CastChecked<UEditorEngine>(GEngine)->OnPreviewFeatureLevelChanged().Remove(PreviewFeatureLevelChangedHandle);
|
|
}
|
|
|
|
if (GEditor)
|
|
{
|
|
GEditor->GetEditorWorldContext(true).RemoveRef(World);
|
|
}
|
|
}
|
|
|
|
FText SLevelEditor::GetTabTitle() const
|
|
{
|
|
const IMainFrameModule& MainFrameModule = FModuleManager::GetModuleChecked< IMainFrameModule >( MainFrameModuleName );
|
|
|
|
return FText::FromString(MainFrameModule.GetLoadedLevelName());
|
|
}
|
|
|
|
FText SLevelEditor::GetTabSuffix() const
|
|
{
|
|
const bool bDirtyState = World && World->GetCurrentLevel()->GetOutermost()->IsDirty();
|
|
return bDirtyState ? FText::FromString(TEXT("*")) : FText::GetEmpty();
|
|
}
|
|
|
|
bool SLevelEditor::HasActivePlayInEditorViewport() const
|
|
{
|
|
// Search through all current viewport layouts
|
|
for( int32 TabIndex = 0; TabIndex < ViewportTabs.Num(); ++TabIndex )
|
|
{
|
|
TWeakPtr<FLevelViewportTabContent> ViewportTab = ViewportTabs[ TabIndex ];
|
|
|
|
if (ViewportTab.IsValid())
|
|
{
|
|
// Get all the viewports in the layout
|
|
const TMap< FName, TSharedPtr< IViewportLayoutEntity > >* LevelViewports = ViewportTab.Pin()->GetViewports();
|
|
|
|
if (LevelViewports != NULL)
|
|
{
|
|
// Search for a viewport with a pie session
|
|
for (auto& Pair : *LevelViewports)
|
|
{
|
|
if (Pair.Value->IsPlayInEditorViewportActive())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Also check standalone viewports
|
|
for( const TWeakPtr< SLevelViewport >& StandaloneViewportWeakPtr : StandaloneViewports )
|
|
{
|
|
const TSharedPtr< SLevelViewport > Viewport = StandaloneViewportWeakPtr.Pin();
|
|
if( Viewport.IsValid() )
|
|
{
|
|
if( Viewport->IsPlayInEditorViewportActive() )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
TSharedPtr<SLevelViewport> SLevelEditor::GetActiveViewport()
|
|
{
|
|
// The first visible viewport
|
|
TSharedPtr<SLevelViewport> FirstVisibleViewport;
|
|
|
|
// Search through all current viewport tabs
|
|
for( int32 TabIndex = 0; TabIndex < ViewportTabs.Num(); ++TabIndex )
|
|
{
|
|
TSharedPtr<FLevelViewportTabContent> ViewportTab = ViewportTabs[ TabIndex ].Pin();
|
|
|
|
if (ViewportTab.IsValid())
|
|
{
|
|
// Only check the viewports in the tab if its visible
|
|
if( ViewportTab->IsVisible() )
|
|
{
|
|
const TMap< FName, TSharedPtr< IViewportLayoutEntity > >* LevelViewports = ViewportTab->GetViewports();
|
|
|
|
if (LevelViewports != NULL)
|
|
{
|
|
for(auto& Pair : *LevelViewports)
|
|
{
|
|
TSharedPtr<SLevelViewport> Viewport = Pair.Value->AsLevelViewport();
|
|
if( Viewport.IsValid() && Viewport->IsInForegroundTab() )
|
|
{
|
|
if( &Viewport->GetLevelViewportClient() == GCurrentLevelEditingViewportClient )
|
|
{
|
|
// If the viewport is visible and is also the current level editing viewport client
|
|
// return it as the active viewport
|
|
return Viewport;
|
|
}
|
|
else if( !FirstVisibleViewport.IsValid() )
|
|
{
|
|
// If there is no current first visible viewport set it now
|
|
// We will return this viewport if the current level editing viewport client is not visible
|
|
FirstVisibleViewport = Viewport;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Also check standalone viewports
|
|
for( const TWeakPtr< SLevelViewport >& StandaloneViewportWeakPtr : StandaloneViewports )
|
|
{
|
|
const TSharedPtr< SLevelViewport > Viewport = StandaloneViewportWeakPtr.Pin();
|
|
if( Viewport.IsValid() )
|
|
{
|
|
if( &Viewport->GetLevelViewportClient() == GCurrentLevelEditingViewportClient )
|
|
{
|
|
// If the viewport is visible and is also the current level editing viewport client
|
|
// return it as the active viewport
|
|
return Viewport;
|
|
}
|
|
else if( !FirstVisibleViewport.IsValid() )
|
|
{
|
|
// If there is no current first visible viewport set it now
|
|
// We will return this viewport if the current level editing viewport client is not visible
|
|
FirstVisibleViewport = Viewport;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return the first visible viewport if we found one. This can be null if we didn't find any visible viewports
|
|
return FirstVisibleViewport;
|
|
}
|
|
|
|
|
|
TSharedRef< SWidget > SLevelEditor::GetParentWidget()
|
|
{
|
|
return AsShared();
|
|
}
|
|
|
|
void SLevelEditor::BringToFront()
|
|
{
|
|
FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked<FLevelEditorModule>( LevelEditorModuleName );
|
|
TSharedPtr<SDockTab> LevelEditorTab = LevelEditorModule.GetLevelEditorInstanceTab().Pin();
|
|
TSharedPtr<FTabManager> LevelEditorTabManager = LevelEditorModule.GetLevelEditorTabManager();
|
|
if (LevelEditorTabManager.IsValid() && LevelEditorTab.IsValid())
|
|
{
|
|
LevelEditorTabManager->DrawAttention( LevelEditorTab.ToSharedRef() );
|
|
}
|
|
}
|
|
|
|
TSharedRef< SDockTabStack > SLevelEditor::GetTabSpot( const EToolkitTabSpot::Type TabSpot )
|
|
{
|
|
ensureMsgf(false, TEXT("Unimplemented"));
|
|
return TSharedPtr<SDockTabStack>().ToSharedRef();
|
|
}
|
|
|
|
void SLevelEditor::OnToolkitHostingStarted( const TSharedRef< class IToolkit >& Toolkit )
|
|
{
|
|
// @todo toolkit minor: We should consider only allowing a single toolkit for a specific asset editor type hosted
|
|
// at once. OR, we allow multiple to be hosted, but we only show tabs for one at a time (fast switching.)
|
|
// Otherwise, it's going to be a huge cluster trying to distinguish tabs for different assets of the same type
|
|
// of editor
|
|
|
|
TSharedPtr<FTabManager> LevelEditorTabManager = GetTabManager();
|
|
|
|
HostedToolkits.Add( Toolkit );
|
|
|
|
Toolkit->RegisterTabSpawners( LevelEditorTabManager.ToSharedRef() );
|
|
|
|
// @todo toolkit minor: We should clean out old invalid array entries from time to time
|
|
|
|
// Tell all of the toolkit area widgets about the new toolkit
|
|
for( auto ToolBoxIt = ToolBoxTabs.CreateIterator(); ToolBoxIt; ++ToolBoxIt )
|
|
{
|
|
if( ToolBoxIt->IsValid() )
|
|
{
|
|
ToolBoxIt->Pin()->OnToolkitHostingStarted( Toolkit );
|
|
}
|
|
}
|
|
|
|
// Tell all of the toolkit area widgets about the new toolkit
|
|
for( auto ToolBoxIt = ModesTabs.CreateIterator(); ToolBoxIt; ++ToolBoxIt )
|
|
{
|
|
if( ToolBoxIt->IsValid() )
|
|
{
|
|
ToolBoxIt->Pin()->OnToolkitHostingStarted( Toolkit );
|
|
}
|
|
}
|
|
}
|
|
|
|
void SLevelEditor::OnToolkitHostingFinished( const TSharedRef< class IToolkit >& Toolkit )
|
|
{
|
|
TSharedPtr<FTabManager> LevelEditorTabManager = GetTabManager();
|
|
|
|
Toolkit->UnregisterTabSpawners(LevelEditorTabManager.ToSharedRef());
|
|
|
|
// Tell all of the toolkit area widgets that our toolkit was removed
|
|
for( auto ToolBoxIt = ToolBoxTabs.CreateIterator(); ToolBoxIt; ++ToolBoxIt )
|
|
{
|
|
if( ToolBoxIt->IsValid() )
|
|
{
|
|
ToolBoxIt->Pin()->OnToolkitHostingFinished( Toolkit );
|
|
}
|
|
}
|
|
|
|
// Tell all of the toolkit area widgets that our toolkit was removed
|
|
for( auto ToolBoxIt = ModesTabs.CreateIterator(); ToolBoxIt; ++ToolBoxIt )
|
|
{
|
|
if( ToolBoxIt->IsValid() )
|
|
{
|
|
ToolBoxIt->Pin()->OnToolkitHostingFinished( Toolkit );
|
|
}
|
|
}
|
|
|
|
HostedToolkits.Remove( Toolkit );
|
|
|
|
// @todo toolkit minor: If user clicks X on all opened world-centric toolkit tabs, should we exit that toolkit automatically?
|
|
// Feel 50/50 about this. It's totally valid to use the "Save" menu even after closing tabs, etc. Plus, you can spawn the tabs back up using the tab area down-down menu.
|
|
}
|
|
|
|
TSharedPtr<FTabManager> SLevelEditor::GetTabManager() const
|
|
{
|
|
FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked<FLevelEditorModule>( LevelEditorModuleName );
|
|
TSharedPtr<FTabManager> LevelEditorTabManager = LevelEditorModule.GetLevelEditorTabManager();
|
|
return LevelEditorTabManager;
|
|
}
|
|
|
|
void SLevelEditor::AttachSequencer( TSharedPtr<SWidget> SequencerWidget, TSharedPtr<IAssetEditorInstance> NewSequencerAssetEditor )
|
|
{
|
|
struct Local
|
|
{
|
|
static void OnSequencerClosed( TSharedRef<SDockTab> DockTab, TWeakPtr<IAssetEditorInstance> InSequencerAssetEditor )
|
|
{
|
|
TSharedPtr<IAssetEditorInstance> AssetEditorInstance = InSequencerAssetEditor.Pin();
|
|
|
|
if (AssetEditorInstance.IsValid())
|
|
{
|
|
InSequencerAssetEditor.Pin()->CloseWindow();
|
|
}
|
|
}
|
|
};
|
|
|
|
static bool bIsReentrant = false;
|
|
|
|
if( !bIsReentrant )
|
|
{
|
|
TSharedRef<SDockTab> Tab = SNew(SDockTab);
|
|
Tab = InvokeTab("Sequencer");
|
|
|
|
// Close the sequence editor after invoking a sequencer tab instead of before so that the existing asset editor doesn't refer to a stale sequencer.
|
|
if(SequencerAssetEditor.IsValid())
|
|
{
|
|
// Closing the window will invoke this method again but we are handling reopening with a new movie scene ourselves
|
|
TGuardValue<bool> ReentrantGuard(bIsReentrant, true);
|
|
// Shutdown cleanly
|
|
SequencerAssetEditor.Pin()->CloseWindow();
|
|
}
|
|
|
|
if(!FGlobalTabmanager::Get()->OnOverrideDockableAreaRestore_Handler.IsBound())
|
|
{
|
|
// Don't allow standard tab closing behavior when the override is active
|
|
Tab->SetOnTabClosed(SDockTab::FOnTabClosedCallback::CreateStatic(&Local::OnSequencerClosed, TWeakPtr<IAssetEditorInstance>(NewSequencerAssetEditor)));
|
|
}
|
|
if(SequencerWidget.IsValid() && NewSequencerAssetEditor.IsValid())
|
|
{
|
|
Tab->SetContent(SequencerWidget.ToSharedRef());
|
|
SequencerWidgetPtr = SequencerWidget;
|
|
SequencerAssetEditor = NewSequencerAssetEditor;
|
|
if (FGlobalTabmanager::Get()->OnOverrideDockableAreaRestore_Handler.IsBound())
|
|
{
|
|
// @todo vreditor: more general vr editor tab manager should handle windows instead
|
|
// Close the original tab so we just work with the override window
|
|
Tab->RequestCloseTab();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Tab->SetContent(SNullWidget::NullWidget);
|
|
SequencerAssetEditor.Reset();
|
|
}
|
|
}
|
|
}
|
|
|
|
TSharedRef<SDockTab> SLevelEditor::SummonDetailsPanel( FName TabIdentifier )
|
|
{
|
|
TSharedRef<SActorDetails> ActorDetails = StaticCastSharedRef<SActorDetails>( CreateActorDetails( TabIdentifier ) );
|
|
|
|
const FText Label = NSLOCTEXT( "LevelEditor", "DetailsTabTitle", "Details" );
|
|
|
|
TSharedRef<SDockTab> DocTab = SNew(SDockTab)
|
|
.Icon( FEditorStyle::GetBrush( "LevelEditor.Tabs.Details" ) )
|
|
.Label( Label )
|
|
.ToolTip( IDocumentation::Get()->CreateToolTip( Label, nullptr, "Shared/LevelEditor", "DetailsTab" ) )
|
|
[
|
|
SNew( SBox )
|
|
.AddMetaData<FTutorialMetaData>(FTutorialMetaData(TEXT("ActorDetails"), TEXT("LevelEditorSelectionDetails")))
|
|
[
|
|
ActorDetails
|
|
]
|
|
];
|
|
|
|
return DocTab;
|
|
}
|
|
/** Method to call when a tab needs to be spawned by the FLayoutService */
|
|
TSharedRef<SDockTab> SLevelEditor::SpawnLevelEditorTab( const FSpawnTabArgs& Args, FName TabIdentifier, FString InitializationPayload )
|
|
{
|
|
if( TabIdentifier == TEXT("LevelEditorViewport" ) )
|
|
{
|
|
return this->BuildViewportTab( NSLOCTEXT("LevelViewportTypes", "LevelEditorViewport", "Viewport 1"), TEXT("Viewport 1"), InitializationPayload );
|
|
}
|
|
else if( TabIdentifier == TEXT("LevelEditorViewport_Clone1" ) )
|
|
{
|
|
return this->BuildViewportTab( NSLOCTEXT("LevelViewportTypes", "LevelEditorViewport_Clone1", "Viewport 2"), TEXT("Viewport 2"), InitializationPayload );
|
|
}
|
|
else if( TabIdentifier == TEXT("LevelEditorViewport_Clone2" ) )
|
|
{
|
|
return this->BuildViewportTab( NSLOCTEXT("LevelViewportTypes", "LevelEditorViewport_Clone2", "Viewport 3"), TEXT("Viewport 3"), InitializationPayload );
|
|
}
|
|
else if( TabIdentifier == TEXT("LevelEditorViewport_Clone3" ) )
|
|
{
|
|
return this->BuildViewportTab( NSLOCTEXT("LevelViewportTypes", "LevelEditorViewport_Clone3", "Viewport 4"), TEXT("Viewport 4"), InitializationPayload );
|
|
}
|
|
else if( TabIdentifier == TEXT( "LevelEditorToolBar") )
|
|
{
|
|
return SNew( SDockTab )
|
|
.Label( NSLOCTEXT("LevelEditor", "ToolBarTabTitle", "Toolbar") )
|
|
.ShouldAutosize(true)
|
|
.Icon( FEditorStyle::GetBrush("ToolBar.Icon") )
|
|
[
|
|
SNew(SHorizontalBox)
|
|
.AddMetaData<FTagMetaData>(FTagMetaData(TEXT("LevelEditorToolbar")))
|
|
+SHorizontalBox::Slot()
|
|
.FillWidth(1)
|
|
.VAlign(VAlign_Bottom)
|
|
.HAlign(HAlign_Left)
|
|
[
|
|
FLevelEditorToolBar::MakeLevelEditorToolBar( LevelEditorCommands.ToSharedRef(), SharedThis(this) )
|
|
]
|
|
];
|
|
|
|
}
|
|
else if (TabIdentifier == FEditorModeTools::EditorModeToolbarTabName)
|
|
{
|
|
return GLevelEditorModeTools().MakeModeToolbarTab();
|
|
}
|
|
else if( TabIdentifier == TEXT("LevelEditorSelectionDetails") || TabIdentifier == TEXT("LevelEditorSelectionDetails2") || TabIdentifier == TEXT("LevelEditorSelectionDetails3") || TabIdentifier == TEXT("LevelEditorSelectionDetails4") )
|
|
{
|
|
TSharedRef<SDockTab> DetailsPanel = SummonDetailsPanel( TabIdentifier );
|
|
GUnrealEd->UpdateFloatingPropertyWindows();
|
|
return DetailsPanel;
|
|
}
|
|
else if( TabIdentifier == TEXT("LevelEditorToolBox") )
|
|
{
|
|
TSharedRef<SLevelEditorToolBox> NewToolBox = StaticCastSharedRef<SLevelEditorToolBox>( CreateToolBox() );
|
|
|
|
return SNew( SDockTab )
|
|
.Icon( FEditorStyle::GetBrush( "LevelEditor.Tabs.Modes" ) )
|
|
.Label( NSLOCTEXT( "LevelEditor", "ToolsTabTitle", "Modes" ) )
|
|
[
|
|
SNew( SBox )
|
|
.AddMetaData<FTutorialMetaData>(FTutorialMetaData(TEXT("ToolsPanel"), TEXT("LevelEditorToolBox")))
|
|
[
|
|
NewToolBox
|
|
]
|
|
];
|
|
}
|
|
else if( TabIdentifier == LevelEditorBuildAndSubmitTab )
|
|
{
|
|
TSharedRef<SLevelEditorBuildAndSubmit> NewBuildAndSubmit = SNew( SLevelEditorBuildAndSubmit, SharedThis( this ) );
|
|
|
|
TSharedRef<SDockTab> NewTab = SNew( SDockTab )
|
|
.Icon( FEditorStyle::GetBrush( "LevelEditor.Tabs.BuildAndSubmit" ) )
|
|
.Label( NSLOCTEXT("LevelEditor", "BuildAndSubmitTabTitle", "Build and Submit") )
|
|
[
|
|
NewBuildAndSubmit
|
|
];
|
|
|
|
NewBuildAndSubmit->SetDockableTab(NewTab);
|
|
|
|
return NewTab;
|
|
}
|
|
else if( TabIdentifier == TEXT("LevelEditorSceneOutliner") )
|
|
{
|
|
SceneOutliner::FInitializationOptions InitOptions;
|
|
InitOptions.bShowTransient = true;
|
|
InitOptions.Mode = ESceneOutlinerMode::ActorBrowsing;
|
|
{
|
|
TWeakPtr<SLevelEditor> WeakLevelEditor = SharedThis(this);
|
|
InitOptions.DefaultMenuExtender = MakeShareable(new FExtender);
|
|
InitOptions.DefaultMenuExtender->AddMenuExtension(
|
|
"MainSection", EExtensionHook::Before, GetLevelEditorActions(),
|
|
FMenuExtensionDelegate::CreateStatic([](FMenuBuilder& MenuBuilder, TWeakPtr<SLevelEditor> InWeakLevelEditor){
|
|
// Extend the menu even if no actors selected, as Edit menu should always exist for scene outliner
|
|
FLevelEditorContextMenu::FillMenu(MenuBuilder, InWeakLevelEditor, LevelEditorMenuContext::SceneOutliner, TSharedPtr<FExtender>());
|
|
}, WeakLevelEditor)
|
|
);
|
|
}
|
|
|
|
|
|
FText Label = NSLOCTEXT( "LevelEditor", "SceneOutlinerTabTitle", "World Outliner" );
|
|
|
|
FSceneOutlinerModule& SceneOutlinerModule = FModuleManager::Get().LoadModuleChecked<FSceneOutlinerModule>("SceneOutliner");
|
|
TSharedRef<ISceneOutliner> SceneOutlinerRef = SceneOutlinerModule.CreateSceneOutliner(
|
|
InitOptions,
|
|
FOnActorPicked() /* Not used for outliner when in browsing mode */);
|
|
SceneOutlinerPtr = SceneOutlinerRef;
|
|
|
|
return SNew( SDockTab )
|
|
.Icon( FEditorStyle::GetBrush( "LevelEditor.Tabs.Outliner" ) )
|
|
.Label( Label )
|
|
.ToolTip( IDocumentation::Get()->CreateToolTip( Label, nullptr, "Shared/LevelEditor", "SceneOutlinerTab" ) )
|
|
[
|
|
SNew(SBorder)
|
|
.Padding(4)
|
|
.BorderImage( FEditorStyle::GetBrush("ToolPanel.GroupBorder") )
|
|
.AddMetaData<FTutorialMetaData>(FTutorialMetaData(TEXT("SceneOutliner"), TEXT("LevelEditorSceneOutliner")))
|
|
[
|
|
SceneOutlinerRef
|
|
]
|
|
];
|
|
}
|
|
else if( TabIdentifier == TEXT("LevelEditorLayerBrowser") )
|
|
{
|
|
|
|
FLayersModule& LayersModule = FModuleManager::LoadModuleChecked<FLayersModule>( "Layers" );
|
|
return SNew( SDockTab )
|
|
.Icon( FEditorStyle::GetBrush( "LevelEditor.Tabs.Layers" ) )
|
|
.Label( NSLOCTEXT("LevelEditor", "LayersTabTitle", "Layers") )
|
|
[
|
|
SNew(SBorder)
|
|
.Padding( 0 )
|
|
.BorderImage( FEditorStyle::GetBrush("ToolPanel.GroupBorder") )
|
|
.AddMetaData<FTutorialMetaData>(FTutorialMetaData(TEXT("LayerBrowser"), TEXT("LevelEditorLayerBrowser")))
|
|
[
|
|
LayersModule.CreateLayerBrowser()
|
|
]
|
|
];
|
|
}
|
|
else if (TabIdentifier == TEXT("LevelEditorHierarchicalLODOutliner"))
|
|
{
|
|
FText Label = NSLOCTEXT("LevelEditor", "HLODOutlinerTabTitle", "Hierarchical LOD Outliner");
|
|
|
|
FHierarchicalLODOutlinerModule& HLODModule = FModuleManager::LoadModuleChecked<FHierarchicalLODOutlinerModule>("HierarchicalLODOutliner");
|
|
return SNew(SDockTab)
|
|
.Icon(FEditorStyle::GetBrush("LevelEditor.Tabs.HLOD"))
|
|
.Label(Label)
|
|
.ToolTip(IDocumentation::Get()->CreateToolTip(Label, nullptr, "Shared/Editor/HLOD", "main"))
|
|
[
|
|
HLODModule.CreateHLODOutlinerWidget()
|
|
];
|
|
}
|
|
else if( TabIdentifier == WorldBrowserHierarchyTab )
|
|
{
|
|
FWorldBrowserModule& WorldBrowserModule = FModuleManager::LoadModuleChecked<FWorldBrowserModule>( "WorldBrowser" );
|
|
return SNew( SDockTab )
|
|
.Icon( FEditorStyle::GetBrush( "LevelEditor.Tabs.WorldBrowser" ) )
|
|
.Label( NSLOCTEXT("LevelEditor", "WorldBrowserHierarchyTabTitle", "Levels") )
|
|
[
|
|
WorldBrowserModule.CreateWorldBrowserHierarchy()
|
|
];
|
|
}
|
|
else if( TabIdentifier == WorldBrowserDetailsTab )
|
|
{
|
|
FWorldBrowserModule& WorldBrowserModule = FModuleManager::LoadModuleChecked<FWorldBrowserModule>( "WorldBrowser" );
|
|
return SNew( SDockTab )
|
|
.Icon( FEditorStyle::GetBrush( "LevelEditor.Tabs.WorldBrowserDetails" ) )
|
|
.Label( NSLOCTEXT("LevelEditor", "WorldBrowserDetailsTabTitle", "Level Details") )
|
|
[
|
|
WorldBrowserModule.CreateWorldBrowserDetails()
|
|
];
|
|
}
|
|
else if( TabIdentifier == WorldBrowserCompositionTab )
|
|
{
|
|
FWorldBrowserModule& WorldBrowserModule = FModuleManager::LoadModuleChecked<FWorldBrowserModule>( "WorldBrowser" );
|
|
return SNew( SDockTab )
|
|
.Icon( FEditorStyle::GetBrush( "LevelEditor.Tabs.WorldBrowserComposition" ) )
|
|
.Label( NSLOCTEXT("LevelEditor", "WorldBrowserCompositionTabTitle", "World Composition") )
|
|
[
|
|
WorldBrowserModule.CreateWorldBrowserComposition()
|
|
];
|
|
}
|
|
else if( TabIdentifier == TEXT("Sequencer") )
|
|
{
|
|
if (FSlateStyleRegistry::FindSlateStyle("LevelSequenceEditorStyle"))
|
|
{
|
|
// @todo sequencer: remove when world-centric mode is added
|
|
return SNew(SDockTab)
|
|
.Icon(FSlateStyleRegistry::FindSlateStyle("LevelSequenceEditorStyle")->GetBrush("LevelSequenceEditor.Tabs.Sequencer"))
|
|
.Label(NSLOCTEXT("Sequencer", "SequencerMainTitle", "Sequencer"))
|
|
[
|
|
SNullWidget::NullWidget
|
|
];
|
|
}
|
|
}
|
|
else if( TabIdentifier == TEXT("SequencerGraphEditor") )
|
|
{
|
|
const FSlateIcon SequencerGraphIcon = FSlateIcon(FEditorStyle::GetStyleSetName(), "GenericCurveEditor.TabIcon");
|
|
// @todo sequencer: remove when world-centric mode is added
|
|
return SNew(SDockTab)
|
|
.Icon(SequencerGraphIcon.GetIcon())
|
|
.Label(NSLOCTEXT("Sequencer", "SequencerMainGraphEditorTitle", "Sequencer Curves"))
|
|
[
|
|
SNullWidget::NullWidget
|
|
];
|
|
}
|
|
else if( TabIdentifier == LevelEditorStatsViewerTab )
|
|
{
|
|
FStatsViewerModule& StatsViewerModule = FModuleManager::Get().LoadModuleChecked<FStatsViewerModule>( "StatsViewer" );
|
|
return SNew( SDockTab )
|
|
.Icon( FEditorStyle::GetBrush( "LevelEditor.Tabs.StatsViewer" ) )
|
|
.Label( NSLOCTEXT("LevelEditor", "StatsViewerTabTitle", "Statistics") )
|
|
[
|
|
StatsViewerModule.CreateStatsViewer()
|
|
];
|
|
}
|
|
else if ( TabIdentifier == "WorldSettingsTab" )
|
|
{
|
|
FPropertyEditorModule& PropPlugin = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
|
|
FDetailsViewArgs DetailsViewArgs( false, false, true, FDetailsViewArgs::HideNameArea, false, GUnrealEd );
|
|
DetailsViewArgs.bShowActorLabel = false;
|
|
|
|
WorldSettingsView = PropPlugin.CreateDetailView( DetailsViewArgs );
|
|
|
|
if (GetWorld() != NULL)
|
|
{
|
|
WorldSettingsView->SetObject(GetWorld()->GetWorldSettings());
|
|
}
|
|
|
|
return SNew( SDockTab )
|
|
.Icon( FEditorStyle::GetBrush( "LevelEditor.WorldProperties.Tab" ) )
|
|
.Label( NSLOCTEXT("LevelEditor", "WorldSettingsTabTitle", "World Settings") )
|
|
.AddMetaData<FTutorialMetaData>(FTutorialMetaData(TEXT("WorldSettings"), TEXT("WorldSettingsTab")))
|
|
[
|
|
WorldSettingsView.ToSharedRef()
|
|
];
|
|
}
|
|
|
|
return SNew(SDockTab);
|
|
}
|
|
|
|
bool SLevelEditor::CanSpawnEditorModeToolbarTab(const FSpawnTabArgs& Args) const
|
|
{
|
|
return GLevelEditorModeTools().ShouldShowModeToolbar();
|
|
}
|
|
|
|
TSharedRef<SDockTab> SLevelEditor::InvokeTab( FName TabID )
|
|
{
|
|
TSharedPtr<FTabManager> LevelEditorTabManager = GetTabManager();
|
|
return LevelEditorTabManager->InvokeTab(TabID);
|
|
}
|
|
|
|
void SLevelEditor::SyncDetailsToSelection()
|
|
{
|
|
static const FName DetailsTabIdentifiers[] = { "LevelEditorSelectionDetails", "LevelEditorSelectionDetails2", "LevelEditorSelectionDetails3", "LevelEditorSelectionDetails4" };
|
|
|
|
FPropertyEditorModule& PropPlugin = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
|
|
|
|
FName FirstClosedDetailsTabIdentifier;
|
|
|
|
// First see if there is an already open details view that can handle the request
|
|
// For instance, if "Details 3" is open, we don't want to open "Details 2" to handle this
|
|
for(const FName& DetailsTabIdentifier : DetailsTabIdentifiers)
|
|
{
|
|
TSharedPtr<IDetailsView> DetailsView = PropPlugin.FindDetailView(DetailsTabIdentifier);
|
|
|
|
if(!DetailsView.IsValid())
|
|
{
|
|
// Track the first closed details view in case no currently open ones can handle our request
|
|
if(FirstClosedDetailsTabIdentifier.IsNone())
|
|
{
|
|
FirstClosedDetailsTabIdentifier = DetailsTabIdentifier;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if(DetailsView->IsUpdatable() && !DetailsView->IsLocked())
|
|
{
|
|
InvokeTab(DetailsTabIdentifier);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// If we got this far then there were no open details views, so open the first available one
|
|
if(!FirstClosedDetailsTabIdentifier.IsNone())
|
|
{
|
|
InvokeTab(FirstClosedDetailsTabIdentifier);
|
|
}
|
|
}
|
|
|
|
/** Builds a viewport tab. */
|
|
TSharedRef<SDockTab> SLevelEditor::BuildViewportTab( const FText& Label, const FString LayoutId, const FString& InitializationPayload )
|
|
{
|
|
// The tab must be created before the viewport layout because the layout needs them
|
|
TSharedRef< SDockTab > DockableTab =
|
|
SNew(SDockTab)
|
|
.Label(Label)
|
|
.Icon(FEditorStyle::GetBrush("LevelEditor.Tabs.Viewports"))
|
|
.OnTabClosed(this, &SLevelEditor::OnViewportTabClosed);
|
|
|
|
// Create a new tab
|
|
TSharedRef<FLevelViewportTabContent> ViewportTabContent = MakeShareable(new FLevelViewportTabContent());
|
|
|
|
// Track the viewport
|
|
CleanupPointerArray(ViewportTabs);
|
|
ViewportTabs.Add(ViewportTabContent);
|
|
|
|
ViewportTabContent->Initialize(SharedThis(this), DockableTab, LayoutId);
|
|
|
|
// Restore transient camera position
|
|
RestoreViewportTabInfo(ViewportTabContent);
|
|
|
|
return DockableTab;
|
|
}
|
|
|
|
void SLevelEditor::OnViewportTabClosed(TSharedRef<SDockTab> ClosedTab)
|
|
{
|
|
TWeakPtr<FLevelViewportTabContent>* const ClosedTabContent = ViewportTabs.FindByPredicate([&ClosedTab](TWeakPtr<FLevelViewportTabContent>& InPotentialElement) -> bool
|
|
{
|
|
TSharedPtr<FLevelViewportTabContent> ViewportTabContent = InPotentialElement.Pin();
|
|
return ViewportTabContent.IsValid() && ViewportTabContent->BelongsToTab(ClosedTab);
|
|
});
|
|
|
|
if(ClosedTabContent)
|
|
{
|
|
TSharedPtr<FLevelViewportTabContent> ClosedTabContentPin = ClosedTabContent->Pin();
|
|
if(ClosedTabContentPin.IsValid())
|
|
{
|
|
SaveViewportTabInfo(ClosedTabContentPin.ToSharedRef());
|
|
|
|
// Untrack the viewport
|
|
ViewportTabs.Remove(ClosedTabContentPin);
|
|
CleanupPointerArray(ViewportTabs);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SLevelEditor::SaveViewportTabInfo(TSharedRef<const FLevelViewportTabContent> ViewportTabContent)
|
|
{
|
|
const TMap<FName, TSharedPtr<IViewportLayoutEntity>>* const Viewports = ViewportTabContent->GetViewports();
|
|
if(Viewports)
|
|
{
|
|
const FString& LayoutId = ViewportTabContent->GetLayoutString();
|
|
for (auto& Pair : *Viewports)
|
|
{
|
|
TSharedPtr<SLevelViewport> Viewport = Pair.Value->AsLevelViewport();
|
|
if( !Viewport.IsValid() )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//@todo there could potentially be more than one of the same viewport type. This effectively takes the last one of a specific type
|
|
const FLevelEditorViewportClient& LevelViewportClient = Viewport->GetLevelViewportClient();
|
|
const FString Key = FString::Printf(TEXT("%s[%d]"), *LayoutId, static_cast<int32>(LevelViewportClient.ViewportType));
|
|
TransientEditorViews.Add(
|
|
Key, FLevelViewportInfo(
|
|
LevelViewportClient.GetViewLocation(),
|
|
LevelViewportClient.GetViewRotation(),
|
|
LevelViewportClient.GetOrthoZoom()
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SLevelEditor::RestoreViewportTabInfo(TSharedRef<FLevelViewportTabContent> ViewportTabContent) const
|
|
{
|
|
const TMap<FName, TSharedPtr<IViewportLayoutEntity>>* const Viewports = ViewportTabContent->GetViewports();
|
|
if(Viewports)
|
|
{
|
|
const FString& LayoutId = ViewportTabContent->GetLayoutString();
|
|
for (auto& Pair : *Viewports)
|
|
{
|
|
TSharedPtr<SLevelViewport> Viewport = Pair.Value->AsLevelViewport();
|
|
if( !Viewport.IsValid() )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
FLevelEditorViewportClient& LevelViewportClient = Viewport->GetLevelViewportClient();
|
|
bool bInitializedOrthoViewport = false;
|
|
for (int32 ViewportType = 0; ViewportType < LVT_MAX; ViewportType++)
|
|
{
|
|
if (ViewportType == LVT_Perspective || !bInitializedOrthoViewport)
|
|
{
|
|
const FString Key = FString::Printf(TEXT("%s[%d]"), *LayoutId, ViewportType);
|
|
const FLevelViewportInfo* const TransientEditorView = TransientEditorViews.Find(Key);
|
|
if (TransientEditorView)
|
|
{
|
|
LevelViewportClient.SetInitialViewTransform(
|
|
static_cast<ELevelViewportType>(ViewportType),
|
|
TransientEditorView->CamPosition,
|
|
TransientEditorView->CamRotation,
|
|
TransientEditorView->CamOrthoZoom
|
|
);
|
|
|
|
if (ViewportType != LVT_Perspective)
|
|
{
|
|
bInitializedOrthoViewport = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SLevelEditor::ResetViewportTabInfo()
|
|
{
|
|
TransientEditorViews.Reset();
|
|
}
|
|
|
|
TSharedRef<SWidget> SLevelEditor::RestoreContentArea( const TSharedRef<SDockTab>& OwnerTab, const TSharedRef<SWindow>& OwnerWindow )
|
|
{
|
|
const IWorkspaceMenuStructure& MenuStructure = WorkspaceMenu::GetMenuStructure();
|
|
|
|
FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked<FLevelEditorModule>( LevelEditorModuleName );
|
|
LevelEditorModule.SetLevelEditorTabManager(OwnerTab);
|
|
|
|
TSharedPtr<FTabManager> LevelEditorTabManager = LevelEditorModule.GetLevelEditorTabManager();
|
|
|
|
// Register Level Editor tab spawners
|
|
{
|
|
{
|
|
const FText ViewportTooltip = NSLOCTEXT("LevelEditorTabs", "LevelEditorViewportTooltip", "Open a Viewport tab. Use this to view and edit the current level.");
|
|
const FSlateIcon ViewportIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Viewports");
|
|
|
|
LevelEditorTabManager->RegisterTabSpawner( "LevelEditorViewport", FOnSpawnTab::CreateSP(this, &SLevelEditor::SpawnLevelEditorTab, FName("LevelEditorViewport"), FString()) )
|
|
.SetDisplayName(NSLOCTEXT("LevelEditorTabs", "LevelEditorViewport", "Viewport 1"))
|
|
.SetTooltipText(ViewportTooltip)
|
|
.SetGroup( MenuStructure.GetLevelEditorViewportsCategory() )
|
|
.SetIcon(ViewportIcon);
|
|
|
|
LevelEditorTabManager->RegisterTabSpawner( "LevelEditorViewport_Clone1", FOnSpawnTab::CreateSP(this, &SLevelEditor::SpawnLevelEditorTab, FName("LevelEditorViewport_Clone1"), FString()) )
|
|
.SetDisplayName(NSLOCTEXT("LevelEditorTabs", "LevelEditorViewport_Clone1", "Viewport 2"))
|
|
.SetTooltipText(ViewportTooltip)
|
|
.SetGroup( MenuStructure.GetLevelEditorViewportsCategory() )
|
|
.SetIcon(ViewportIcon);
|
|
|
|
LevelEditorTabManager->RegisterTabSpawner( "LevelEditorViewport_Clone2", FOnSpawnTab::CreateSP(this, &SLevelEditor::SpawnLevelEditorTab, FName("LevelEditorViewport_Clone2"), FString()) )
|
|
.SetDisplayName(NSLOCTEXT("LevelEditorTabs", "LevelEditorViewport_Clone2", "Viewport 3"))
|
|
.SetTooltipText(ViewportTooltip)
|
|
.SetGroup( MenuStructure.GetLevelEditorViewportsCategory() )
|
|
.SetIcon(ViewportIcon);
|
|
|
|
LevelEditorTabManager->RegisterTabSpawner( "LevelEditorViewport_Clone3", FOnSpawnTab::CreateSP(this, &SLevelEditor::SpawnLevelEditorTab, FName("LevelEditorViewport_Clone3"), FString()) )
|
|
.SetDisplayName(NSLOCTEXT("LevelEditorTabs", "LevelEditorViewport_Clone3", "Viewport 4"))
|
|
.SetTooltipText(ViewportTooltip)
|
|
.SetGroup( MenuStructure.GetLevelEditorViewportsCategory() )
|
|
.SetIcon(ViewportIcon);
|
|
}
|
|
|
|
{
|
|
const FSlateIcon ToolbarIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Toolbar");
|
|
LevelEditorTabManager->RegisterTabSpawner( "LevelEditorToolBar", FOnSpawnTab::CreateSP<SLevelEditor, FName, FString>(this, &SLevelEditor::SpawnLevelEditorTab, FName("LevelEditorToolBar"), FString()))
|
|
.SetDisplayName(NSLOCTEXT("LevelEditorTabs", "LevelEditorToolBar", "Toolbar"))
|
|
.SetTooltipText(NSLOCTEXT("LevelEditorTabs", "LevelEditorToolBarTooltipText", "Open the Toolbar tab, which provides access to the most common / important actions."))
|
|
.SetGroup( MenuStructure.GetLevelEditorCategory() )
|
|
.SetIcon( ToolbarIcon );
|
|
}
|
|
|
|
{
|
|
const FText DetailsTooltip = NSLOCTEXT("LevelEditorTabs", "LevelEditorSelectionDetailsTooltip", "Open a Details tab. Use this to view and edit properties of the selected object(s).");
|
|
const FSlateIcon DetailsIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Details");
|
|
|
|
LevelEditorTabManager->RegisterTabSpawner( "LevelEditorSelectionDetails", FOnSpawnTab::CreateSP<SLevelEditor, FName, FString>(this, &SLevelEditor::SpawnLevelEditorTab, FName("LevelEditorSelectionDetails"), FString()) )
|
|
.SetDisplayName(NSLOCTEXT("LevelEditorTabs", "LevelEditorSelectionDetails", "Details 1"))
|
|
.SetTooltipText(DetailsTooltip)
|
|
.SetGroup( MenuStructure.GetLevelEditorDetailsCategory() )
|
|
.SetIcon( DetailsIcon );
|
|
|
|
LevelEditorTabManager->RegisterTabSpawner( "LevelEditorSelectionDetails2", FOnSpawnTab::CreateSP<SLevelEditor, FName, FString>(this, &SLevelEditor::SpawnLevelEditorTab, FName("LevelEditorSelectionDetails2"), FString()) )
|
|
.SetDisplayName(NSLOCTEXT("LevelEditorTabs", "LevelEditorSelectionDetails2", "Details 2"))
|
|
.SetTooltipText(DetailsTooltip)
|
|
.SetGroup( MenuStructure.GetLevelEditorDetailsCategory() )
|
|
.SetIcon( DetailsIcon );
|
|
|
|
LevelEditorTabManager->RegisterTabSpawner( "LevelEditorSelectionDetails3", FOnSpawnTab::CreateSP<SLevelEditor, FName, FString>(this, &SLevelEditor::SpawnLevelEditorTab, FName("LevelEditorSelectionDetails3"), FString()) )
|
|
.SetDisplayName(NSLOCTEXT("LevelEditorTabs", "LevelEditorSelectionDetails3", "Details 3"))
|
|
.SetTooltipText(DetailsTooltip)
|
|
.SetGroup( MenuStructure.GetLevelEditorDetailsCategory() )
|
|
.SetIcon( DetailsIcon );
|
|
|
|
LevelEditorTabManager->RegisterTabSpawner( "LevelEditorSelectionDetails4", FOnSpawnTab::CreateSP<SLevelEditor, FName, FString>(this, &SLevelEditor::SpawnLevelEditorTab, FName("LevelEditorSelectionDetails4"), FString()) )
|
|
.SetDisplayName(NSLOCTEXT("LevelEditorTabs", "LevelEditorSelectionDetails4", "Details 4"))
|
|
.SetTooltipText(DetailsTooltip)
|
|
.SetGroup( MenuStructure.GetLevelEditorDetailsCategory() )
|
|
.SetIcon( DetailsIcon );
|
|
}
|
|
|
|
{
|
|
|
|
const FSlateIcon ToolsIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Modes");
|
|
LevelEditorTabManager->RegisterTabSpawner("LevelEditorToolBox", FOnSpawnTab::CreateSP<SLevelEditor, FName, FString>(this, &SLevelEditor::SpawnLevelEditorTab, FName("LevelEditorToolBox"), FString()))
|
|
.SetDisplayName(NSLOCTEXT("LevelEditorTabs", "LevelEditorToolBox", "Modes Panel"))
|
|
.SetTooltipText(NSLOCTEXT("LevelEditorTabs", "LevelEditorToolBoxTooltipText", "Open the Modes tab, which specifies all the available editing modes."))
|
|
.SetGroup(MenuStructure.GetLevelEditorModesCategory())
|
|
.SetIcon(ToolsIcon);
|
|
|
|
FCanSpawnTab CanSpawnTabDelegate = FCanSpawnTab::CreateSP(this, &SLevelEditor::CanSpawnEditorModeToolbarTab);
|
|
LevelEditorTabManager->RegisterTabSpawner(FEditorModeTools::EditorModeToolbarTabName, FOnSpawnTab::CreateSP<SLevelEditor, FName, FString>(this, &SLevelEditor::SpawnLevelEditorTab, FEditorModeTools::EditorModeToolbarTabName, FString()), CanSpawnTabDelegate)
|
|
.SetDisplayName(NSLOCTEXT("LevelEditorTabs", "LevelEditorModesTab", "Active Mode Tools"))
|
|
.SetTooltipText(NSLOCTEXT("LevelEditorTabs", "LevelEditorModesTabTooltipText", "Open the modes toolbar which has the active mode's tools"))
|
|
.SetGroup(MenuStructure.GetLevelEditorModesCategory())
|
|
.SetIcon(ToolsIcon);
|
|
}
|
|
|
|
{
|
|
const FSlateIcon OutlinerIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Outliner");
|
|
LevelEditorTabManager->RegisterTabSpawner( "LevelEditorSceneOutliner", FOnSpawnTab::CreateSP<SLevelEditor, FName, FString>(this, &SLevelEditor::SpawnLevelEditorTab, FName("LevelEditorSceneOutliner"), FString()) )
|
|
.SetDisplayName(NSLOCTEXT("LevelEditorTabs", "LevelEditorSceneOutliner", "World Outliner"))
|
|
.SetTooltipText(NSLOCTEXT("LevelEditorTabs", "LevelEditorSceneOutlinerTooltipText", "Open the World Outliner tab, which provides a searchable and filterable list of all actors in the world."))
|
|
.SetGroup( MenuStructure.GetLevelEditorCategory() )
|
|
.SetIcon( OutlinerIcon );
|
|
}
|
|
|
|
{
|
|
const FSlateIcon LayersIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Layers");
|
|
LevelEditorTabManager->RegisterTabSpawner( "LevelEditorLayerBrowser", FOnSpawnTab::CreateSP<SLevelEditor, FName, FString>(this, &SLevelEditor::SpawnLevelEditorTab, FName("LevelEditorLayerBrowser"), FString()) )
|
|
.SetDisplayName(NSLOCTEXT("LevelEditorTabs", "LevelEditorLayerBrowser", "Layers"))
|
|
.SetTooltipText(NSLOCTEXT("LevelEditorTabs", "LevelEditorLayerBrowserTooltipText", "Open the Layers tab. Use this to manage which actors in the world belong to which layers."))
|
|
.SetGroup( MenuStructure.GetLevelEditorCategory() )
|
|
.SetIcon( LayersIcon );
|
|
}
|
|
|
|
{
|
|
const FSlateIcon LayersIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.HLOD");
|
|
LevelEditorTabManager->RegisterTabSpawner("LevelEditorHierarchicalLODOutliner", FOnSpawnTab::CreateSP<SLevelEditor, FName, FString>(this, &SLevelEditor::SpawnLevelEditorTab, FName("LevelEditorHierarchicalLODOutliner"), FString()))
|
|
.SetDisplayName(NSLOCTEXT("LevelEditorTabs", "LevelEditorHierarchicalLODOutliner", "Hierarchical LOD Outliner"))
|
|
.SetTooltipText(NSLOCTEXT("LevelEditorTabs", "LevelEditorHierarchicalLODOutlinerTooltipText", "Open the Hierarchical LOD Outliner."))
|
|
.SetGroup(MenuStructure.GetLevelEditorCategory())
|
|
.SetIcon(LayersIcon);
|
|
}
|
|
|
|
{
|
|
LevelEditorTabManager->RegisterTabSpawner( WorldBrowserHierarchyTab, FOnSpawnTab::CreateSP<SLevelEditor, FName, FString>(this, &SLevelEditor::SpawnLevelEditorTab, WorldBrowserHierarchyTab, FString()) )
|
|
.SetDisplayName(NSLOCTEXT("LevelEditorTabs", "WorldBrowserHierarchy", "Levels"))
|
|
.SetTooltipText(NSLOCTEXT("LevelEditorTabs", "WorldBrowserHierarchyTooltipText", "Open the Levels tab. Use this to manage the levels in the current project."))
|
|
.SetGroup( WorkspaceMenu::GetMenuStructure().GetLevelEditorCategory() )
|
|
.SetIcon( FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.WorldBrowser") );
|
|
|
|
LevelEditorTabManager->RegisterTabSpawner( WorldBrowserDetailsTab, FOnSpawnTab::CreateSP<SLevelEditor, FName, FString>(this, &SLevelEditor::SpawnLevelEditorTab, WorldBrowserDetailsTab, FString()) )
|
|
.SetMenuType( ETabSpawnerMenuType::Hidden )
|
|
.SetDisplayName(NSLOCTEXT("LevelEditorTabs", "WorldBrowserDetails", "Level Details"))
|
|
.SetGroup( WorkspaceMenu::GetMenuStructure().GetLevelEditorCategory() )
|
|
.SetIcon( FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.WorldBrowserDetails") );
|
|
|
|
LevelEditorTabManager->RegisterTabSpawner( WorldBrowserCompositionTab, FOnSpawnTab::CreateSP<SLevelEditor, FName, FString>(this, &SLevelEditor::SpawnLevelEditorTab, WorldBrowserCompositionTab, FString()) )
|
|
.SetMenuType( ETabSpawnerMenuType::Hidden )
|
|
.SetDisplayName(NSLOCTEXT("LevelEditorTabs", "WorldBrowserComposition", "World Composition"))
|
|
.SetGroup( WorkspaceMenu::GetMenuStructure().GetLevelEditorCategory() )
|
|
.SetIcon( FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.WorldBrowserComposition") );
|
|
}
|
|
|
|
{
|
|
const FSlateIcon StatsViewerIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.StatsViewer");
|
|
LevelEditorTabManager->RegisterTabSpawner(LevelEditorStatsViewerTab, FOnSpawnTab::CreateSP<SLevelEditor, FName, FString>(this, &SLevelEditor::SpawnLevelEditorTab, LevelEditorStatsViewerTab, FString()))
|
|
.SetDisplayName(NSLOCTEXT("LevelEditorTabs", "LevelEditorStatsViewer", "Statistics"))
|
|
.SetTooltipText(NSLOCTEXT("LevelEditorTabs", "LevelEditorStatsViewerTooltipText", "Open the Statistics tab, in order to see data pertaining to lighting, textures and primitives."))
|
|
.SetGroup(MenuStructure.GetLevelEditorCategory())
|
|
.SetIcon(StatsViewerIcon);
|
|
}
|
|
|
|
{
|
|
// @todo remove when world-centric mode is added
|
|
const FSlateIcon SequencerIcon("LevelSequenceEditorStyle", "LevelSequenceEditor.Tabs.Sequencer" );
|
|
LevelEditorTabManager->RegisterTabSpawner( "Sequencer", FOnSpawnTab::CreateSP<SLevelEditor, FName, FString>(this, &SLevelEditor::SpawnLevelEditorTab, FName("Sequencer"), FString()) )
|
|
.SetDisplayName(NSLOCTEXT("LevelEditorTabs", "Sequencer", "Sequencer"))
|
|
.SetGroup( MenuStructure.GetLevelEditorCategory() )
|
|
.SetIcon( SequencerIcon );
|
|
}
|
|
|
|
{
|
|
// @todo remove when world-centric mode is added
|
|
const FSlateIcon SequencerGraphIcon = FSlateIcon(FEditorStyle::GetStyleSetName(), "GenericCurveEditor.TabIcon");
|
|
LevelEditorTabManager->RegisterTabSpawner("SequencerGraphEditor", FOnSpawnTab::CreateSP<SLevelEditor, FName, FString>(this, &SLevelEditor::SpawnLevelEditorTab, FName("SequencerGraphEditor"), FString()))
|
|
.SetMenuType(ETabSpawnerMenuType::Type::Hidden);
|
|
}
|
|
|
|
{
|
|
const FSlateIcon WorldPropertiesIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.WorldProperties.Tab");
|
|
LevelEditorTabManager->RegisterTabSpawner( "WorldSettingsTab", FOnSpawnTab::CreateSP<SLevelEditor, FName, FString>(this, &SLevelEditor::SpawnLevelEditorTab, FName("WorldSettingsTab"), FString()) )
|
|
.SetDisplayName(NSLOCTEXT("LevelEditorTabs", "WorldSettings", "World Settings"))
|
|
.SetTooltipText(NSLOCTEXT("LevelEditorTabs", "WorldSettingsTooltipText", "Open the World Settings tab, in which global properties of the level can be viewed and edited."))
|
|
.SetGroup( MenuStructure.GetLevelEditorCategory() )
|
|
.SetIcon( WorldPropertiesIcon );
|
|
}
|
|
|
|
FTabSpawnerEntry& BuildAndSubmitEntry = LevelEditorTabManager->RegisterTabSpawner(LevelEditorBuildAndSubmitTab, FOnSpawnTab::CreateSP<SLevelEditor, FName, FString>(this, &SLevelEditor::SpawnLevelEditorTab, LevelEditorBuildAndSubmitTab, FString()));
|
|
BuildAndSubmitEntry.SetAutoGenerateMenuEntry(false);
|
|
|
|
LevelEditorModule.OnRegisterTabs().Broadcast(LevelEditorTabManager);
|
|
}
|
|
|
|
// Rebuild the editor mode commands and their tab spawners before we restore the layout,
|
|
// or there wont be any tab spawners for the modes.
|
|
RefreshEditorModeCommands();
|
|
|
|
const TSharedRef<FTabManager::FLayout> Layout = FLayoutSaveRestore::LoadFromConfig(GEditorLayoutIni,
|
|
FTabManager::NewLayout( "LevelEditor_Layout_v1.1" )
|
|
->AddArea
|
|
(
|
|
FTabManager::NewPrimaryArea()
|
|
->SetOrientation( Orient_Horizontal )
|
|
->SetExtensionId( "TopLevelArea" )
|
|
->Split
|
|
(
|
|
FTabManager::NewSplitter()
|
|
->SetOrientation( Orient_Vertical )
|
|
->SetSizeCoefficient( 1 )
|
|
->Split
|
|
(
|
|
FTabManager::NewSplitter()
|
|
->SetSizeCoefficient( .75f )
|
|
->SetOrientation(Orient_Horizontal)
|
|
->Split
|
|
(
|
|
FTabManager::NewStack()
|
|
->SetSizeCoefficient( 0.3f )
|
|
->AddTab( "LevelEditorToolBox", ETabState::OpenedTab )
|
|
)
|
|
->Split
|
|
(
|
|
FTabManager::NewSplitter()
|
|
->SetOrientation(Orient_Vertical)
|
|
->SetSizeCoefficient( 1.15f )
|
|
->Split
|
|
(
|
|
FTabManager::NewStack()
|
|
->SetHideTabWell(true)
|
|
->AddTab("LevelEditorToolBar", ETabState::OpenedTab)
|
|
)
|
|
->Split
|
|
(
|
|
FTabManager::NewStack()
|
|
->SetHideTabWell(true)
|
|
->AddTab(FEditorModeTools::EditorModeToolbarTabName, ETabState::ClosedTab)
|
|
)
|
|
->Split
|
|
(
|
|
FTabManager::NewStack()
|
|
->SetHideTabWell(true)
|
|
->SetSizeCoefficient( 1.0f )
|
|
->AddTab("LevelEditorViewport", ETabState::OpenedTab)
|
|
)
|
|
)
|
|
)
|
|
->Split
|
|
(
|
|
FTabManager::NewStack()
|
|
->SetSizeCoefficient(.4)
|
|
->AddTab("ContentBrowserTab1", ETabState::OpenedTab)
|
|
->AddTab("OutputLog", ETabState::ClosedTab)
|
|
)
|
|
)
|
|
->Split
|
|
(
|
|
FTabManager::NewSplitter()
|
|
->SetSizeCoefficient(0.25f)
|
|
->SetOrientation(Orient_Vertical)
|
|
->Split
|
|
(
|
|
FTabManager::NewStack()
|
|
->SetSizeCoefficient(0.4f)
|
|
->AddTab("LevelEditorSceneOutliner", ETabState::OpenedTab)
|
|
->AddTab("LevelEditorLayerBrowser", ETabState::ClosedTab)
|
|
|
|
)
|
|
->Split
|
|
(
|
|
FTabManager::NewStack()
|
|
->AddTab("LevelEditorSelectionDetails", ETabState::OpenedTab)
|
|
->AddTab("WorldSettingsTab", ETabState::ClosedTab)
|
|
->SetForegroundTab(FName("LevelEditorSelectionDetails"))
|
|
)
|
|
)
|
|
|
|
));
|
|
|
|
FLayoutExtender LayoutExtender;
|
|
|
|
// Make a layout extension for the mode toolbar. This avoids bumping level editor layout version to add a mandatory tab
|
|
LayoutExtender.ExtendLayout(FName("LevelEditorToolBar"), ELayoutExtensionPosition::Below, FTabManager::FTab(FEditorModeTools::EditorModeToolbarTabName, ETabState::ClosedTab));
|
|
|
|
LevelEditorModule.OnRegisterLayoutExtensions().Broadcast(LayoutExtender);
|
|
Layout->ProcessExtensions(LayoutExtender);
|
|
|
|
return LevelEditorTabManager->RestoreFrom( Layout, OwnerWindow ).ToSharedRef();
|
|
}
|
|
|
|
void SLevelEditor::HandleExperimentalSettingChanged(FName PropertyName)
|
|
{
|
|
FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked<FLevelEditorModule>("LevelEditor");
|
|
TSharedPtr<FTabManager> LevelEditorTabManager = LevelEditorModule.GetLevelEditorTabManager();
|
|
LevelEditorTabManager->UpdateMainMenu(true);
|
|
}
|
|
|
|
FName SLevelEditor::GetEditorModeTabId( FEditorModeID ModeID )
|
|
{
|
|
return FName(*(FString("EditorMode.Tab.") + ModeID.ToString()));
|
|
}
|
|
|
|
void SLevelEditor::ToggleEditorMode( FEditorModeID ModeID )
|
|
{
|
|
// Prompt the user if Matinee must be closed before activating new mode
|
|
if (ModeID != FBuiltinEditorModes::EM_InterpEdit)
|
|
{
|
|
FEdMode* MatineeMode = GLevelEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_InterpEdit);
|
|
if (MatineeMode && !MatineeMode->IsCompatibleWith(ModeID))
|
|
{
|
|
FEditorModeInfo MatineeModeInfo = FEditorModeRegistry::Get().GetModeInfo(ModeID);
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("ModeName"), MatineeModeInfo.Name);
|
|
FText Msg = FText::Format(NSLOCTEXT("LevelEditor", "ModeSwitchCloseMatineeQ", "Activating '{ModeName}' editor mode will close UnrealMatinee. Continue?"), Args);
|
|
|
|
if (EAppReturnType::Yes != FMessageDialog::Open(EAppMsgType::YesNo, Msg))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// *Important* - activate the mode first since FEditorModeTools::DeactivateMode will
|
|
// activate the default mode when the stack becomes empty, resulting in multiple active visible modes.
|
|
GLevelEditorModeTools().ActivateMode( ModeID );
|
|
|
|
// Find and disable any other 'visible' modes since we only ever allow one of those active at a time.
|
|
TArray<FEdMode*> ActiveModes;
|
|
GLevelEditorModeTools().GetActiveModes( ActiveModes );
|
|
for ( FEdMode* Mode : ActiveModes )
|
|
{
|
|
if ( Mode->GetID() != ModeID && Mode->GetModeInfo().bVisible )
|
|
{
|
|
GLevelEditorModeTools().DeactivateMode( Mode->GetID() );
|
|
}
|
|
}
|
|
|
|
FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked<FLevelEditorModule>( "LevelEditor" );
|
|
TSharedPtr<FTabManager> LevelEditorTabManager = LevelEditorModule.GetLevelEditorTabManager();
|
|
|
|
TSharedRef<SDockTab> ToolboxTab = LevelEditorTabManager->InvokeTab( FTabId("LevelEditorToolBox") );
|
|
|
|
//// If it's already active deactivate the mode
|
|
//if ( GLevelEditorModeTools().IsModeActive( ModeID ) )
|
|
//{
|
|
// //GLevelEditorModeTools().DeactivateAllModes();
|
|
// //GLevelEditorModeTools().DeactivateMode( ModeID );
|
|
//}
|
|
//else // Activate the mode and create the tab for it.
|
|
//{
|
|
// GLevelEditorModeTools().DeactivateAllModes();
|
|
// GLevelEditorModeTools().ActivateMode( ModeID );
|
|
|
|
// //FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked<FLevelEditorModule>( "LevelEditor" );
|
|
// //TSharedPtr<FTabManager> LevelEditorTabManager = LevelEditorModule.GetLevelEditorTabManager();
|
|
|
|
// //TSharedRef<SDockTab> ToolboxTab = LevelEditorTabManager->InvokeTab( GetEditorModeTabId( ModeID ) );
|
|
//}
|
|
}
|
|
|
|
bool SLevelEditor::IsModeActive( FEditorModeID ModeID )
|
|
{
|
|
// The level editor changes the default mode to placement
|
|
if ( ModeID == FBuiltinEditorModes::EM_Placement )
|
|
{
|
|
// Only return true if this is the *only* active mode
|
|
TArray<FEdMode*> ActiveModes;
|
|
GLevelEditorModeTools().GetActiveModes(ActiveModes);
|
|
for( FEdMode* Mode : ActiveModes )
|
|
{
|
|
if( Mode->GetModeInfo().bVisible && Mode->GetID() != FBuiltinEditorModes::EM_Placement )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return GLevelEditorModeTools().IsModeActive( ModeID );
|
|
}
|
|
|
|
void SLevelEditor::EditorModeCommandsChanged()
|
|
{
|
|
if (FLevelEditorModesCommands::IsRegistered())
|
|
{
|
|
FLevelEditorModesCommands::Unregister();
|
|
}
|
|
|
|
RefreshEditorModeCommands();
|
|
}
|
|
|
|
void SLevelEditor::RefreshEditorModeCommands()
|
|
{
|
|
FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked<FLevelEditorModule>( "LevelEditor" );
|
|
|
|
if(!FLevelEditorModesCommands::IsRegistered())
|
|
{
|
|
FLevelEditorModesCommands::Register();
|
|
}
|
|
const IWorkspaceMenuStructure& MenuStructure = WorkspaceMenu::GetMenuStructure();
|
|
TSharedPtr<FTabManager> LevelEditorTabManager = LevelEditorModule.GetLevelEditorTabManager();
|
|
|
|
// We need to remap all the actions to commands.
|
|
const FLevelEditorModesCommands& Commands = FLevelEditorModesCommands::Get();
|
|
|
|
int32 CommandIndex = 0;
|
|
for( const FEditorModeInfo& Mode : FEditorModeRegistry::Get().GetSortedModeInfo() )
|
|
{
|
|
// If the mode isn't visible don't create a menu option for it.
|
|
if( !Mode.bVisible )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
FName EditorModeTabName = GetEditorModeTabId( Mode.ID );
|
|
FName EditorModeCommandName = FName(*(FString("EditorMode.") + Mode.ID.ToString()));
|
|
|
|
TSharedPtr<FUICommandInfo> EditorModeCommand =
|
|
FInputBindingManager::Get().FindCommandInContext(Commands.GetContextName(), EditorModeCommandName);
|
|
|
|
// If a command isn't yet registered for this mode, we need to register one.
|
|
if ( EditorModeCommand.IsValid() && !LevelEditorCommands->IsActionMapped(Commands.EditorModeCommands[CommandIndex]) )
|
|
{
|
|
LevelEditorCommands->MapAction(
|
|
Commands.EditorModeCommands[CommandIndex],
|
|
FExecuteAction::CreateStatic( &SLevelEditor::ToggleEditorMode, Mode.ID ),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateStatic( &SLevelEditor::IsModeActive, Mode.ID ));
|
|
}
|
|
|
|
CommandIndex++;
|
|
}
|
|
|
|
for( const auto& ToolBoxTab : ToolBoxTabs )
|
|
{
|
|
auto Tab = ToolBoxTab.Pin();
|
|
if( Tab.IsValid() )
|
|
{
|
|
Tab->OnEditorModeCommandsChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
FReply SLevelEditor::OnKeyDown( const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent )
|
|
{
|
|
// Check to see if any of the actions for the level editor can be processed by the current event
|
|
// If we are in debug mode do not process commands
|
|
if (FSlateApplication::Get().IsNormalExecution())
|
|
{
|
|
for (const auto& ActiveToolkit : HostedToolkits)
|
|
{
|
|
// A toolkit is active, so direct all command processing to it
|
|
if (ActiveToolkit->ProcessCommandBindings(InKeyEvent))
|
|
{
|
|
return FReply::Handled();
|
|
}
|
|
}
|
|
|
|
// No toolkit processed the key, so let the level editor have a chance at the keystroke
|
|
if (LevelEditorCommands->ProcessCommandBindings(InKeyEvent))
|
|
{
|
|
return FReply::Handled();
|
|
}
|
|
}
|
|
|
|
return FReply::Unhandled();
|
|
}
|
|
|
|
FReply SLevelEditor::OnKeyDownInViewport( const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent )
|
|
{
|
|
// Check to see if any of the actions for the level editor can be processed by the current keyboard from a viewport
|
|
if( LevelEditorCommands->ProcessCommandBindings( InKeyEvent ) )
|
|
{
|
|
return FReply::Handled();
|
|
}
|
|
|
|
// NOTE: Currently, we don't bother allowing toolkits to get a chance at viewport keys
|
|
|
|
return FReply::Unhandled();
|
|
}
|
|
|
|
/** Callback for when the level editor layout has changed */
|
|
void SLevelEditor::OnLayoutHasChanged()
|
|
{
|
|
// ...
|
|
}
|
|
|
|
void SLevelEditor::SummonLevelViewportContextMenu()
|
|
{
|
|
FLevelEditorContextMenu::SummonMenu( SharedThis( this ), LevelEditorMenuContext::Viewport );
|
|
}
|
|
|
|
void SLevelEditor::SummonLevelViewportViewOptionMenu(const ELevelViewportType ViewOption)
|
|
{
|
|
FLevelEditorContextMenu::SummonViewOptionMenu( SharedThis( this ), ViewOption);
|
|
}
|
|
|
|
const TArray< TSharedPtr< IToolkit > >& SLevelEditor::GetHostedToolkits() const
|
|
{
|
|
return HostedToolkits;
|
|
}
|
|
|
|
TArray< TSharedPtr< ILevelViewport > > SLevelEditor::GetViewports() const
|
|
{
|
|
TArray< TSharedPtr<ILevelViewport> > OutViewports;
|
|
|
|
for( int32 TabIndex = 0; TabIndex < ViewportTabs.Num(); ++TabIndex )
|
|
{
|
|
TSharedPtr<FLevelViewportTabContent> ViewportTab = ViewportTabs[ TabIndex ].Pin();
|
|
|
|
if (ViewportTab.IsValid())
|
|
{
|
|
const TMap< FName, TSharedPtr< IViewportLayoutEntity > >* LevelViewports = ViewportTab->GetViewports();
|
|
|
|
if (LevelViewports != NULL)
|
|
{
|
|
for (auto& Pair : *LevelViewports)
|
|
{
|
|
TSharedPtr<SLevelViewport> Viewport = Pair.Value->AsLevelViewport();
|
|
if( Viewport.IsValid() )
|
|
{
|
|
OutViewports.Add(Viewport);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Also add any standalone viewports
|
|
{
|
|
for( const TWeakPtr< SLevelViewport >& StandaloneViewportWeakPtr : StandaloneViewports )
|
|
{
|
|
const TSharedPtr< SLevelViewport > Viewport = StandaloneViewportWeakPtr.Pin();
|
|
if( Viewport.IsValid() )
|
|
{
|
|
OutViewports.Add( Viewport );
|
|
}
|
|
}
|
|
}
|
|
|
|
return OutViewports;
|
|
}
|
|
|
|
TSharedPtr<ILevelViewport> SLevelEditor::GetActiveViewportInterface()
|
|
{
|
|
return GetActiveViewport();
|
|
}
|
|
|
|
TSharedPtr< class FAssetThumbnailPool > SLevelEditor::GetThumbnailPool() const
|
|
{
|
|
return ThumbnailPool;
|
|
}
|
|
|
|
void SLevelEditor::AppendCommands( const TSharedRef<FUICommandList>& InCommandsToAppend )
|
|
{
|
|
LevelEditorCommands->Append(InCommandsToAppend);
|
|
}
|
|
|
|
UWorld* SLevelEditor::GetWorld() const
|
|
{
|
|
return World;
|
|
}
|
|
|
|
void SLevelEditor::HandleEditorMapChange( uint32 MapChangeFlags )
|
|
{
|
|
ResetViewportTabInfo();
|
|
|
|
if (WorldSettingsView.IsValid())
|
|
{
|
|
WorldSettingsView->SetObject(GetWorld()->GetWorldSettings(), true);
|
|
}
|
|
}
|
|
|
|
void SLevelEditor::OnActorSelectionChanged(const TArray<UObject*>& NewSelection, bool bForceRefresh)
|
|
{
|
|
for( auto It = AllActorDetailPanels.CreateIterator(); It; ++It )
|
|
{
|
|
TSharedPtr<SActorDetails> ActorDetails = It->Pin();
|
|
if( ActorDetails.IsValid() )
|
|
{
|
|
ActorDetails->SetObjects(NewSelection, bForceRefresh);
|
|
}
|
|
else
|
|
{
|
|
// remove stray entries here
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void SLevelEditor::AddStandaloneLevelViewport( const TSharedRef<SLevelViewport>& LevelViewport )
|
|
{
|
|
CleanupPointerArray( StandaloneViewports );
|
|
StandaloneViewports.Add( LevelViewport );
|
|
}
|
|
|
|
|
|
TSharedRef<SWidget> SLevelEditor::CreateActorDetails( const FName TabIdentifier )
|
|
{
|
|
TSharedRef<SActorDetails> ActorDetails = SNew( SActorDetails, TabIdentifier, LevelEditorCommands, GetTabManager() );
|
|
|
|
// Immediately update it (otherwise it will appear empty)
|
|
{
|
|
TArray<UObject*> SelectedActors;
|
|
for( FSelectionIterator It( GEditor->GetSelectedActorIterator() ); It; ++It )
|
|
{
|
|
AActor* Actor = static_cast<AActor*>( *It );
|
|
checkSlow( Actor->IsA( AActor::StaticClass() ) );
|
|
|
|
if( !Actor->IsPendingKill() )
|
|
{
|
|
SelectedActors.Add( Actor );
|
|
}
|
|
}
|
|
|
|
const bool bForceRefresh = true;
|
|
ActorDetails->SetObjects( SelectedActors, bForceRefresh );
|
|
}
|
|
|
|
AllActorDetailPanels.Add( ActorDetails );
|
|
return ActorDetails;
|
|
}
|
|
|
|
|
|
TSharedRef<SWidget> SLevelEditor::CreateToolBox()
|
|
{
|
|
TSharedRef<SLevelEditorToolBox> NewToolBox =
|
|
SNew( SLevelEditorToolBox, SharedThis( this ) )
|
|
.IsEnabled( FSlateApplication::Get().GetNormalExecutionAttribute() );
|
|
|
|
ToolBoxTabs.Add( NewToolBox );
|
|
|
|
return NewToolBox;
|
|
}
|
|
|