2022-05-27 07:18:07 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "ActorBrowsingMode.h"
# include "SceneOutlinerFilters.h"
# include "SceneOutlinerModule.h"
# include "SceneOutlinerMenuContext.h"
# include "ActorHierarchy.h"
# include "SceneOutlinerDelegates.h"
# include "Editor.h"
# include "Editor/GroupActor.h"
# include "UnrealEdGlobals.h"
# include "ToolMenus.h"
# include "Engine/Selection.h"
# include "Editor/UnrealEdEngine.h"
# include "HAL/PlatformApplicationMisc.h"
# include "SceneOutlinerDragDrop.h"
# include "EditorActorFolders.h"
# include "EditorFolderUtils.h"
# include "ActorEditorUtils.h"
# include "DragAndDrop/ActorDragDropOp.h"
# include "DragAndDrop/ActorDragDropGraphEdOp.h"
# include "DragAndDrop/FolderDragDropOp.h"
# include "Logging/MessageLog.h"
# include "SSocketChooser.h"
# include "ActorFolderPickingMode.h"
# include "ActorTreeItem.h"
# include "LevelTreeItem.h"
# include "ActorFolderTreeItem.h"
# include "WorldTreeItem.h"
# include "ComponentTreeItem.h"
# include "ActorBrowsingModeSettings.h"
# include "ActorDescTreeItem.h"
# include "ScopedTransaction.h"
# include "LevelInstance/LevelInstanceInterface.h"
# include "LevelInstance/LevelInstanceSubsystem.h"
# include "LevelInstance/LevelInstanceEditorInstanceActor.h"
# include "EditorLevelUtils.h"
DEFINE_LOG_CATEGORY_STATIC ( LogActorBrowser , Log , All ) ;
# define LOCTEXT_NAMESPACE "SceneOutliner_ActorBrowsingMode"
using FActorFilter = TSceneOutlinerPredicateFilter < FActorTreeItem > ;
using FActorDescFilter = TSceneOutlinerPredicateFilter < FActorDescTreeItem > ;
FActorBrowsingMode : : FActorBrowsingMode ( SSceneOutliner * InSceneOutliner , TWeakObjectPtr < UWorld > InSpecifiedWorldToDisplay )
: FActorModeInteractive ( FActorModeParams ( InSceneOutliner , InSpecifiedWorldToDisplay , /* bHideComponents */ true , /* bHideLevelInstanceHierarchy */ false , /* bHideUnloadedActors */ false ) )
, FilteredActorCount ( 0 )
{
// Capture selection changes of bones from mesh selection in fracture tools
FSceneOutlinerDelegates : : Get ( ) . OnComponentsUpdated . AddRaw ( this , & FActorBrowsingMode : : OnComponentsUpdated ) ;
GEngine - > OnLevelActorDeleted ( ) . AddRaw ( this , & FActorBrowsingMode : : OnLevelActorDeleted ) ;
GEditor - > OnSelectUnloadedActorsEvent ( ) . AddRaw ( this , & FActorBrowsingMode : : OnSelectUnloadedActors ) ;
FEditorDelegates : : OnEditCutActorsBegin . AddRaw ( this , & FActorBrowsingMode : : OnEditCutActorsBegin ) ;
FEditorDelegates : : OnEditCutActorsEnd . AddRaw ( this , & FActorBrowsingMode : : OnEditCutActorsEnd ) ;
FEditorDelegates : : OnEditCopyActorsBegin . AddRaw ( this , & FActorBrowsingMode : : OnEditCopyActorsBegin ) ;
FEditorDelegates : : OnEditCopyActorsEnd . AddRaw ( this , & FActorBrowsingMode : : OnEditCopyActorsEnd ) ;
FEditorDelegates : : OnEditPasteActorsBegin . AddRaw ( this , & FActorBrowsingMode : : OnEditPasteActorsBegin ) ;
FEditorDelegates : : OnEditPasteActorsEnd . AddRaw ( this , & FActorBrowsingMode : : OnEditPasteActorsEnd ) ;
FEditorDelegates : : OnDuplicateActorsBegin . AddRaw ( this , & FActorBrowsingMode : : OnDuplicateActorsBegin ) ;
FEditorDelegates : : OnDuplicateActorsEnd . AddRaw ( this , & FActorBrowsingMode : : OnDuplicateActorsEnd ) ;
FEditorDelegates : : OnDeleteActorsBegin . AddRaw ( this , & FActorBrowsingMode : : OnDeleteActorsBegin ) ;
FEditorDelegates : : OnDeleteActorsEnd . AddRaw ( this , & FActorBrowsingMode : : OnDeleteActorsEnd ) ;
UActorBrowserConfig : : Initialize ( ) ;
UActorBrowserConfig : : Get ( ) - > LoadEditorConfig ( ) ;
// Get a MutableConfig here to force create a config for the current outliner if it doesn't exist
const FActorBrowsingModeConfig * SavedSettings = GetMutableConfig ( ) ;
// Create a local struct to use the default values if this outliner doesn't want to save configs
FActorBrowsingModeConfig LocalSettings ;
// If this outliner doesn't want to save config (OutlinerIdentifier is empty, use the defaults)
if ( SavedSettings )
{
LocalSettings = * SavedSettings ;
}
// Get the OutlinerModule to register FilterInfos with the FilterInfoMap
FSceneOutlinerFilterInfo ShowOnlySelectedActorsInfo ( LOCTEXT ( " ToggleShowOnlySelected " , " Only Selected " ) , LOCTEXT ( " ToggleShowOnlySelectedToolTip " , " When enabled, only displays actors that are currently selected. " ) , LocalSettings . bShowOnlySelectedActors , FCreateSceneOutlinerFilter : : CreateStatic ( & FActorBrowsingMode : : CreateShowOnlySelectedActorsFilter ) ) ;
ShowOnlySelectedActorsInfo . OnToggle ( ) . AddLambda ( [ this ] ( bool bIsActive )
{
FActorBrowsingModeConfig * Settings = GetMutableConfig ( ) ;
if ( Settings )
{
Settings - > bShowOnlySelectedActors = bIsActive ;
SaveConfig ( ) ;
}
} ) ;
FilterInfoMap . Add ( TEXT ( " ShowOnlySelectedActors " ) , ShowOnlySelectedActorsInfo ) ;
FSceneOutlinerFilterInfo HideTemporaryActorsInfo ( LOCTEXT ( " ToggleHideTemporaryActors " , " Hide Temporary Actors " ) , LOCTEXT ( " ToggleHideTemporaryActorsToolTip " , " When enabled, hides temporary/run-time Actors. " ) , LocalSettings . bHideTemporaryActors , FCreateSceneOutlinerFilter : : CreateStatic ( & FActorBrowsingMode : : CreateHideTemporaryActorsFilter ) ) ;
HideTemporaryActorsInfo . OnToggle ( ) . AddLambda ( [ this ] ( bool bIsActive )
{
FActorBrowsingModeConfig * Settings = GetMutableConfig ( ) ;
if ( Settings )
{
Settings - > bHideTemporaryActors = bIsActive ;
SaveConfig ( ) ;
}
} ) ;
FilterInfoMap . Add ( TEXT ( " HideTemporaryActors " ) , HideTemporaryActorsInfo ) ;
FSceneOutlinerFilterInfo OnlyCurrentLevelInfo ( LOCTEXT ( " ToggleShowOnlyCurrentLevel " , " Only in Current Level " ) , LOCTEXT ( " ToggleShowOnlyCurrentLevelToolTip " , " When enabled, only shows Actors that are in the Current Level. " ) , LocalSettings . bShowOnlyActorsInCurrentLevel , FCreateSceneOutlinerFilter : : CreateStatic ( & FActorBrowsingMode : : CreateIsInCurrentLevelFilter ) ) ;
OnlyCurrentLevelInfo . OnToggle ( ) . AddLambda ( [ this ] ( bool bIsActive )
{
FActorBrowsingModeConfig * Settings = GetMutableConfig ( ) ;
if ( Settings )
{
Settings - > bShowOnlyActorsInCurrentLevel = bIsActive ;
SaveConfig ( ) ;
}
} ) ;
FilterInfoMap . Add ( TEXT ( " ShowOnlyCurrentLevel " ) , OnlyCurrentLevelInfo ) ;
bHideComponents = LocalSettings . bHideActorComponents ;
FSceneOutlinerFilterInfo HideComponentsInfo ( LOCTEXT ( " ToggleHideActorComponents " , " Hide Actor Components " ) , LOCTEXT ( " ToggleHideActorComponentsToolTip " , " When enabled, hides components belonging to actors. " ) , LocalSettings . bHideActorComponents , FCreateSceneOutlinerFilter : : CreateStatic ( & FActorBrowsingMode : : CreateHideComponentsFilter ) ) ;
HideComponentsInfo . OnToggle ( ) . AddLambda ( [ this ] ( bool bIsActive )
{
FActorBrowsingModeConfig * Settings = GetMutableConfig ( ) ;
if ( Settings )
{
Settings - > bHideActorComponents = bHideComponents = bIsActive ;
SaveConfig ( ) ;
}
if ( auto ActorHierarchy = StaticCast < FActorHierarchy * > ( Hierarchy . Get ( ) ) )
{
ActorHierarchy - > SetShowingComponents ( ! bIsActive ) ;
}
} ) ;
FilterInfoMap . Add ( TEXT ( " HideComponentsFilter " ) , HideComponentsInfo ) ;
FSceneOutlinerFilterInfo HideLevelInstancesInfo ( LOCTEXT ( " ToggleHideLevelInstanceContent " , " Hide Level Instance Content " ) , LOCTEXT ( " ToggleHideLevelInstancesToolTip " , " When enabled, hides all level instance content. " ) , LocalSettings . bHideLevelInstanceHierarchy , FCreateSceneOutlinerFilter : : CreateStatic ( & FActorBrowsingMode : : CreateHideLevelInstancesFilter ) ) ;
HideLevelInstancesInfo . OnToggle ( ) . AddLambda ( [ this ] ( bool bIsActive )
{
FActorBrowsingModeConfig * Settings = GetMutableConfig ( ) ;
if ( Settings )
{
Settings - > bHideLevelInstanceHierarchy = bHideLevelInstanceHierarchy = bIsActive ;
SaveConfig ( ) ;
}
if ( auto ActorHierarchy = StaticCast < FActorHierarchy * > ( Hierarchy . Get ( ) ) )
{
ActorHierarchy - > SetShowingLevelInstances ( ! bIsActive ) ;
}
} ) ;
FilterInfoMap . Add ( TEXT ( " HideLevelInstancesFilter " ) , HideLevelInstancesInfo ) ;
FSceneOutlinerFilterInfo HideUnloadedActorsInfo ( LOCTEXT ( " ToggleHideUnloadedActors " , " Hide Unloaded Actors " ) , LOCTEXT ( " ToggleHideUnloadedActorsToolTip " , " When enabled, hides all unloaded world partition actors. " ) , LocalSettings . bHideUnloadedActors , FCreateSceneOutlinerFilter : : CreateStatic ( & FActorBrowsingMode : : CreateHideUnloadedActorsFilter ) ) ;
HideUnloadedActorsInfo . OnToggle ( ) . AddLambda ( [ this ] ( bool bIsActive )
{
FActorBrowsingModeConfig * Settings = GetMutableConfig ( ) ;
if ( Settings )
{
Settings - > bHideUnloadedActors = bHideUnloadedActors = bIsActive ;
SaveConfig ( ) ;
}
if ( auto ActorHierarchy = StaticCast < FActorHierarchy * > ( Hierarchy . Get ( ) ) )
{
ActorHierarchy - > SetShowingUnloadedActors ( ! bIsActive ) ;
}
} ) ;
FilterInfoMap . Add ( TEXT ( " HideUnloadedActorsFilter " ) , HideUnloadedActorsInfo ) ;
// Add a filter which sets the interactive mode of LevelInstance items and their children
SceneOutliner - > AddFilter ( MakeShared < FActorFilter > ( FActorTreeItem : : FFilterPredicate : : CreateStatic ( [ ] ( const AActor * Actor ) { return true ; } ) , FSceneOutlinerFilter : : EDefaultBehaviour : : Pass , FActorTreeItem : : FFilterPredicate : : CreateLambda ( [ this ] ( const AActor * Actor )
{
if ( ! bHideLevelInstanceHierarchy )
{
if ( const ULevelInstanceSubsystem * LevelInstanceSubsystem = RepresentingWorld - > GetSubsystem < ULevelInstanceSubsystem > ( ) )
{
// if actor has a valid parent and the parent is not being edited,
// then the actor should not be selectable.
if ( const ILevelInstanceInterface * ParentLevelInstance = LevelInstanceSubsystem - > GetParentLevelInstance ( Actor ) )
{
if ( ! LevelInstanceSubsystem - > IsEditingLevelInstance ( ParentLevelInstance ) )
{
return false ;
}
}
}
}
return true ;
} ) ) ) ;
bAlwaysFrameSelection = LocalSettings . bAlwaysFrameSelection ;
Rebuild ( ) ;
}
FActorBrowsingMode : : ~ FActorBrowsingMode ( )
{
if ( RepresentingWorld . IsValid ( ) )
{
if ( UWorldPartition * const WorldPartition = RepresentingWorld - > GetWorldPartition ( ) )
{
WorldPartition - > OnActorDescRemovedEvent . RemoveAll ( this ) ;
}
}
FSceneOutlinerDelegates : : Get ( ) . OnComponentsUpdated . RemoveAll ( this ) ;
GEngine - > OnLevelActorDeleted ( ) . RemoveAll ( this ) ;
GEditor - > OnSelectUnloadedActorsEvent ( ) . RemoveAll ( this ) ;
FEditorDelegates : : OnEditCutActorsBegin . RemoveAll ( this ) ;
FEditorDelegates : : OnEditCutActorsEnd . RemoveAll ( this ) ;
FEditorDelegates : : OnEditCopyActorsBegin . RemoveAll ( this ) ;
FEditorDelegates : : OnEditCopyActorsEnd . RemoveAll ( this ) ;
FEditorDelegates : : OnEditPasteActorsBegin . RemoveAll ( this ) ;
FEditorDelegates : : OnEditPasteActorsEnd . RemoveAll ( this ) ;
FEditorDelegates : : OnDuplicateActorsBegin . RemoveAll ( this ) ;
FEditorDelegates : : OnDuplicateActorsEnd . RemoveAll ( this ) ;
FEditorDelegates : : OnDeleteActorsBegin . RemoveAll ( this ) ;
FEditorDelegates : : OnDeleteActorsEnd . RemoveAll ( this ) ;
}
void FActorBrowsingMode : : Rebuild ( )
{
// If we used to be representing a wp world, unbind delegates before rebuilding begins
if ( RepresentingWorld . IsValid ( ) )
{
if ( UWorldPartition * const WorldPartition = RepresentingWorld - > GetWorldPartition ( ) )
{
WorldPartition - > OnActorDescRemovedEvent . RemoveAll ( this ) ;
}
}
FActorMode : : Rebuild ( ) ;
FilteredActorCount = 0 ;
FilteredUnloadedActorCount = 0 ;
ApplicableUnloadedActors . Empty ( ) ;
ApplicableActors . Empty ( ) ;
bRepresentingWorldGameWorld = RepresentingWorld . IsValid ( ) & & RepresentingWorld - > IsGameWorld ( ) ;
bRepresentingWorldPartitionedWorld = RepresentingWorld . IsValid ( ) & & RepresentingWorld - > IsPartitionedWorld ( ) ;
if ( bRepresentingWorldPartitionedWorld )
{
UWorldPartition * const WorldPartition = RepresentingWorld - > GetWorldPartition ( ) ;
WorldPartition - > OnActorDescRemovedEvent . AddRaw ( this , & FActorBrowsingMode : : OnActorDescRemoved ) ;
}
}
FText FActorBrowsingMode : : GetStatusText ( ) const
{
if ( ! RepresentingWorld . IsValid ( ) )
{
return FText ( ) ;
}
// The number of actors in the outliner before applying the text filter
const int32 TotalActorCount = ApplicableActors . Num ( ) + ApplicableUnloadedActors . Num ( ) ;
const int32 SelectedActorCount = SceneOutliner - > GetSelection ( ) . Num < FActorTreeItem , FActorDescTreeItem > ( ) ;
if ( ! SceneOutliner - > IsTextFilterActive ( ) )
{
if ( SelectedActorCount = = 0 ) //-V547
{
if ( bRepresentingWorldPartitionedWorld )
{
return FText : : Format ( LOCTEXT ( " ShowingAllLoadedActorsFmt " , " {0} actors ({1} loaded) " ) , FText : : AsNumber ( FilteredActorCount ) , FText : : AsNumber ( FilteredActorCount - FilteredUnloadedActorCount ) ) ;
}
else
{
return FText : : Format ( LOCTEXT ( " ShowingAllActorsFmt " , " {0} actors " ) , FText : : AsNumber ( FilteredActorCount ) ) ;
}
}
else
{
return FText : : Format ( LOCTEXT ( " ShowingAllActorsSelectedFmt " , " {0} actors ({1} selected) " ) , FText : : AsNumber ( FilteredActorCount ) , FText : : AsNumber ( SelectedActorCount ) ) ;
}
}
else if ( SceneOutliner - > IsTextFilterActive ( ) & & FilteredActorCount = = 0 )
{
return FText : : Format ( LOCTEXT ( " ShowingNoActorsFmt " , " No matching actors ({0} total) " ) , FText : : AsNumber ( TotalActorCount ) ) ;
}
else if ( SelectedActorCount ! = 0 ) //-V547
{
return FText : : Format ( LOCTEXT ( " ShowingOnlySomeActorsSelectedFmt " , " Showing {0} of {1} actors ({2} selected) " ) , FText : : AsNumber ( FilteredActorCount ) , FText : : AsNumber ( TotalActorCount ) , FText : : AsNumber ( SelectedActorCount ) ) ;
}
else
{
return FText : : Format ( LOCTEXT ( " ShowingOnlySomeActorsFmt " , " Showing {0} of {1} actors " ) , FText : : AsNumber ( FilteredActorCount ) , FText : : AsNumber ( TotalActorCount ) ) ;
}
}
FSlateColor FActorBrowsingMode : : GetStatusTextColor ( ) const
{
if ( ! SceneOutliner - > IsTextFilterActive ( ) )
{
return FSlateColor : : UseForeground ( ) ;
}
else if ( FilteredActorCount = = 0 )
{
return FAppStyle : : Get ( ) . GetSlateColor ( " Colors.AccentRed " ) ;
}
else
{
return FAppStyle : : Get ( ) . GetSlateColor ( " Colors.AccentGreen " ) ;
}
}
void FActorBrowsingMode : : OnToggleAlwaysFrameSelection ( )
{
bAlwaysFrameSelection = ! bAlwaysFrameSelection ;
FActorBrowsingModeConfig * Settings = GetMutableConfig ( ) ;
if ( Settings )
{
Settings - > bAlwaysFrameSelection = bAlwaysFrameSelection ;
SaveConfig ( ) ;
}
}
bool FActorBrowsingMode : : ShouldAlwaysFrameSelection ( )
{
return bAlwaysFrameSelection ;
}
void FActorBrowsingMode : : CreateViewContent ( FMenuBuilder & MenuBuilder )
{
MenuBuilder . BeginSection ( " OutlinerSelectionOptions " , LOCTEXT ( " OptionsHeading " , " Options " ) ) ;
MenuBuilder . AddMenuEntry (
LOCTEXT ( " AlwaysFrameSelection " , " Always Frame Selection " ) ,
LOCTEXT ( " AlwaysFrameSelectionTooltip " , " Select an item in the outliner when it is selected through the level editor viewport. " ) ,
FSlateIcon ( ) ,
FUIAction (
FExecuteAction : : CreateRaw ( this , & FActorBrowsingMode : : OnToggleAlwaysFrameSelection ) ,
FCanExecuteAction ( ) ,
FIsActionChecked : : CreateRaw ( this , & FActorBrowsingMode : : ShouldAlwaysFrameSelection )
) ,
NAME_None ,
EUserInterfaceActionType : : ToggleButton
) ;
MenuBuilder . EndSection ( ) ;
MenuBuilder . BeginSection ( " AssetThumbnails " , LOCTEXT ( " ShowWorldHeading " , " World " ) ) ;
{
MenuBuilder . AddSubMenu (
LOCTEXT ( " ChooseWorldSubMenu " , " Choose World " ) ,
LOCTEXT ( " ChooseWorldSubMenuToolTip " , " Choose the world to display in the outliner. " ) ,
FNewMenuDelegate : : CreateRaw ( this , & FActorMode : : BuildWorldPickerMenu )
) ;
}
MenuBuilder . EndSection ( ) ;
}
TSharedRef < FSceneOutlinerFilter > FActorBrowsingMode : : CreateShowOnlySelectedActorsFilter ( )
{
auto IsActorSelected = [ ] ( const AActor * InActor )
{
return InActor & & InActor - > IsSelected ( ) ;
} ;
return MakeShareable ( new FActorFilter ( FActorTreeItem : : FFilterPredicate : : CreateStatic ( IsActorSelected ) , FSceneOutlinerFilter : : EDefaultBehaviour : : Fail , FActorTreeItem : : FFilterPredicate : : CreateStatic ( IsActorSelected ) ) ) ;
}
TSharedRef < FSceneOutlinerFilter > FActorBrowsingMode : : CreateHideTemporaryActorsFilter ( )
{
return MakeShareable ( new FActorFilter ( FActorTreeItem : : FFilterPredicate : : CreateStatic ( [ ] ( const AActor * InActor )
{
return ( ( InActor - > GetWorld ( ) & & InActor - > GetWorld ( ) - > WorldType ! = EWorldType : : PIE ) | | GEditor - > ObjectsThatExistInEditorWorld . Get ( InActor ) ) & & ! InActor - > HasAnyFlags ( EObjectFlags : : RF_Transient ) ;
} ) , FSceneOutlinerFilter : : EDefaultBehaviour : : Pass ) ) ;
}
TSharedRef < FSceneOutlinerFilter > FActorBrowsingMode : : CreateIsInCurrentLevelFilter ( )
{
return MakeShareable ( new FActorFilter ( FActorTreeItem : : FFilterPredicate : : CreateStatic ( [ ] ( const AActor * InActor )
{
if ( InActor - > GetWorld ( ) )
{
return InActor - > GetLevel ( ) = = InActor - > GetWorld ( ) - > GetCurrentLevel ( ) ;
}
return false ;
} ) , FSceneOutlinerFilter : : EDefaultBehaviour : : Pass ) ) ;
}
TSharedRef < FSceneOutlinerFilter > FActorBrowsingMode : : CreateHideComponentsFilter ( )
{
return MakeShared < TSceneOutlinerPredicateFilter < FComponentTreeItem > > ( TSceneOutlinerPredicateFilter < FComponentTreeItem > (
FComponentTreeItem : : FFilterPredicate : : CreateStatic ( [ ] ( const UActorComponent * ) { return false ; } ) ,
FSceneOutlinerFilter : : EDefaultBehaviour : : Pass ) ) ;
}
TSharedRef < FSceneOutlinerFilter > FActorBrowsingMode : : CreateHideLevelInstancesFilter ( )
{
return MakeShareable ( new FActorFilter ( FActorTreeItem : : FFilterPredicate : : CreateStatic ( [ ] ( const AActor * Actor )
{
// Check if actor belongs to a LevelInstance
if ( const ULevelInstanceSubsystem * LevelInstanceSubsystem = Actor - > GetWorld ( ) - > GetSubsystem < ULevelInstanceSubsystem > ( ) )
{
if ( const ILevelInstanceInterface * ParentLevelInstance = LevelInstanceSubsystem - > GetParentLevelInstance ( Actor ) )
{
if ( ! LevelInstanceSubsystem - > IsEditingLevelInstance ( ParentLevelInstance ) )
{
return false ;
}
}
}
// Or if the actor itself is a LevelInstance editor instance
return Cast < ALevelInstanceEditorInstanceActor > ( Actor ) = = nullptr ;
} ) , FSceneOutlinerFilter : : EDefaultBehaviour : : Pass ) ) ;
}
TSharedRef < FSceneOutlinerFilter > FActorBrowsingMode : : CreateHideUnloadedActorsFilter ( )
{
return MakeShareable ( new FActorDescFilter ( FActorDescTreeItem : : FFilterPredicate : : CreateStatic (
[ ] ( const FWorldPartitionActorDesc * ActorDesc ) { return false ; } ) , FSceneOutlinerFilter : : EDefaultBehaviour : : Pass ) ) ;
}
static const FName DefaultContextBaseMenuName ( " SceneOutliner.DefaultContextMenuBase " ) ;
static const FName DefaultContextMenuName ( " SceneOutliner.DefaultContextMenu " ) ;
void FActorBrowsingMode : : RegisterContextMenu ( )
{
UToolMenus * ToolMenus = UToolMenus : : Get ( ) ;
if ( ! ToolMenus - > IsMenuRegistered ( DefaultContextBaseMenuName ) )
{
UToolMenu * Menu = ToolMenus - > RegisterMenu ( DefaultContextBaseMenuName ) ;
Menu - > AddDynamicSection ( " DynamicHierarchySection " , FNewToolMenuDelegate : : CreateLambda ( [ ] ( UToolMenu * InMenu )
{
USceneOutlinerMenuContext * Context = InMenu - > FindContext < USceneOutlinerMenuContext > ( ) ;
if ( ! Context | | ! Context - > SceneOutliner . IsValid ( ) )
{
return ;
}
// NOTE: the name "Section" is used in many other places
FToolMenuSection & Section = InMenu - > FindOrAddSection ( " Section " ) ;
Section . Label = LOCTEXT ( " HierarchySectionName " , " Hierarchy " ) ;
SSceneOutliner * SceneOutliner = Context - > SceneOutliner . Pin ( ) . Get ( ) ;
if ( Context - > bShowParentTree )
{
if ( Context - > NumSelectedItems = = 0 )
{
Section . AddMenuEntry (
" CreateFolder " ,
LOCTEXT ( " CreateFolder " , " Create Folder " ) ,
FText ( ) ,
FSlateIcon ( FAppStyle : : GetAppStyleSetName ( ) , " SceneOutliner.NewFolderIcon " ) ,
FUIAction ( FExecuteAction : : CreateSP ( SceneOutliner , & SSceneOutliner : : CreateFolder ) ) ) ;
}
else
{
if ( Context - > NumSelectedItems = = 1 )
{
SceneOutliner - > GetTree ( ) . GetSelectedItems ( ) [ 0 ] - > GenerateContextMenu ( InMenu , * SceneOutliner ) ;
}
// If we've only got folders selected, show the selection and edit sub menus
if ( Context - > NumSelectedItems > 0 & & Context - > NumSelectedFolders = = Context - > NumSelectedItems )
{
Section . AddSubMenu (
" SelectSubMenu " ,
LOCTEXT ( " SelectSubmenu " , " Select " ) ,
LOCTEXT ( " SelectSubmenu_Tooltip " , " Select the contents of the current selection " ) ,
FNewToolMenuDelegate : : CreateSP ( SceneOutliner , & SSceneOutliner : : FillSelectionSubMenu ) ) ;
}
}
}
} ) ) ;
Menu - > AddDynamicSection ( " DynamicMainSection " , FNewToolMenuDelegate : : CreateLambda ( [ ] ( UToolMenu * InMenu )
{
// We always create a section here, even if there is no parent so that clients can still extend the menu
FToolMenuSection & Section = InMenu - > AddSection ( " MainSection " , LOCTEXT ( " OutlinerSectionName " , " Outliner " ) ) ;
if ( USceneOutlinerMenuContext * Context = InMenu - > FindContext < USceneOutlinerMenuContext > ( ) )
{
// Don't add any of these menu items if we're not showing the parent tree
// Can't move worlds or level blueprints
if ( Context - > bShowParentTree & & Context - > NumSelectedItems > 0 & & Context - > NumWorldsSelected = = 0 & & Context - > SceneOutliner . IsValid ( ) )
{
Section . AddSubMenu (
" MoveActorsTo " ,
LOCTEXT ( " MoveActorsTo " , " Move To " ) ,
LOCTEXT ( " MoveActorsTo_Tooltip " , " Move selection to another folder " ) ,
FNewToolMenuDelegate : : CreateSP ( Context - > SceneOutliner . Pin ( ) . Get ( ) , & SSceneOutliner : : FillFoldersSubMenu ) ) ;
}
if ( Context - > bShowParentTree & & Context - > NumSelectedItems > 0 & & Context - > SceneOutliner . IsValid ( ) )
{
// Only add the menu option to wp levels
if ( ! Context - > bRepresentingGameWorld & & Context - > bRepresentingPartitionedWorld )
{
// If selection contains some unpinned items, show the pin option
// If the selection contains folders, always show the pin option
if ( Context - > NumPinnedItems ! = Context - > NumSelectedItems | | Context - > NumSelectedFolders > 0 )
{
Section . AddMenuEntry (
" PinItems " ,
LOCTEXT ( " Pin " , " Pin " ) ,
FText ( ) ,
FSlateIcon ( ) ,
FUIAction ( FExecuteAction : : CreateSP ( Context - > SceneOutliner . Pin ( ) . Get ( ) , & SSceneOutliner : : PinSelectedItems ) ) ) ;
}
// If the selection contains some pinned items, show the unpin option
// If the selection contains folders, always show the unpin option
if ( Context - > NumPinnedItems ! = 0 | | Context - > NumSelectedFolders > 0 )
{
Section . AddMenuEntry (
" UnpinItems " ,
LOCTEXT ( " Unpin " , " Unpin " ) ,
FText ( ) ,
FSlateIcon ( ) ,
FUIAction ( FExecuteAction : : CreateSP ( Context - > SceneOutliner . Pin ( ) . Get ( ) , & SSceneOutliner : : UnpinSelectedItems ) ) ) ;
}
}
}
}
} ) ) ;
Menu - > AddDynamicSection ( " DynamicActorEditorContext " , FNewToolMenuDelegate : : CreateLambda ( [ this ] ( UToolMenu * InMenu )
{
USceneOutlinerMenuContext * Context = InMenu - > FindContext < USceneOutlinerMenuContext > ( ) ;
if ( Context & & Context - > SceneOutliner . IsValid ( ) & & Context - > bShowParentTree )
{
FToolMenuSection & Section = InMenu - > AddSection ( " ActorEditorContextSection " , LOCTEXT ( " ActorEditorContextSectionName " , " Actor Editor Context " ) ) ;
SSceneOutliner * SceneOutliner = Context - > SceneOutliner . Pin ( ) . Get ( ) ;
if ( ( Context - > NumSelectedItems = = 1 ) & & ( Context - > NumSelectedFolders = = 1 ) )
{
SceneOutliner - > GetTree ( ) . GetSelectedItems ( ) [ 0 ] - > GenerateContextMenu ( InMenu , * SceneOutliner ) ;
Section . AddMenuEntry (
" MakeCurrentFolder " ,
LOCTEXT ( " MakeCurrentFolder " , " Make Current Folder " ) ,
FText ( ) ,
FSlateIcon ( ) ,
FUIAction (
FExecuteAction : : CreateLambda ( [ SceneOutliner ] ( )
{
const FSceneOutlinerItemSelection & Selection = SceneOutliner - > GetSelection ( ) ;
if ( Selection . SelectedItems . Num ( ) = = 1 )
{
FSceneOutlinerTreeItemPtr Item = Selection . SelectedItems [ 0 ] . Pin ( ) ;
if ( FActorFolderTreeItem * FolderItem = Item - > CastTo < FActorFolderTreeItem > ( ) )
{
if ( FolderItem - > World . IsValid ( ) )
{
const FScopedTransaction Transaction ( NSLOCTEXT ( " UnrealEd " , " MakeCurrentActorFolder " , " Make Current Actor Folder " ) ) ;
FActorFolders : : Get ( ) . SetActorEditorContextFolder ( * FolderItem - > World , FolderItem - > GetFolder ( ) ) ;
}
}
}
} ) ,
FCanExecuteAction : : CreateLambda ( [ SceneOutliner ]
{
const FSceneOutlinerItemSelection & Selection = SceneOutliner - > GetSelection ( ) ;
if ( Selection . SelectedItems . Num ( ) = = 1 )
{
FSceneOutlinerTreeItemPtr Item = Selection . SelectedItems [ 0 ] . Pin ( ) ;
if ( FActorFolderTreeItem * FolderItem = Item - > CastTo < FActorFolderTreeItem > ( ) )
{
return FolderItem - > World . IsValid ( ) & & ( FolderItem - > World - > GetCurrentLevel ( ) = = FolderItem - > GetFolder ( ) . GetRootObjectAssociatedLevel ( ) ) ;
}
}
return false ;
} )
)
) ;
}
Section . AddMenuEntry (
" ClearCurrentFolder " ,
LOCTEXT ( " ClearCurrentFolder " , " Clear Current Folder " ) ,
FText ( ) ,
FSlateIcon ( ) ,
FUIAction (
FExecuteAction : : CreateLambda ( [ this , SceneOutliner ] ( )
{
if ( RepresentingWorld . IsValid ( ) )
{
const FScopedTransaction Transaction ( NSLOCTEXT ( " UnrealEd " , " ClearCurrentActorFolder " , " Clear Current Actor Folder " ) ) ;
FActorFolders : : Get ( ) . SetActorEditorContextFolder ( * RepresentingWorld . Get ( ) , FFolder : : GetWorldRootFolder ( RepresentingWorld . Get ( ) ) ) ;
}
} ) ,
FCanExecuteAction : : CreateLambda ( [ this ]
{
return RepresentingWorld . IsValid ( ) & & ! FActorFolders : : Get ( ) . GetActorEditorContextFolder ( * RepresentingWorld . Get ( ) ) . IsNone ( ) ;
} )
)
) ;
}
} ) ) ;
}
if ( ! ToolMenus - > IsMenuRegistered ( DefaultContextMenuName ) )
{
ToolMenus - > RegisterMenu ( DefaultContextMenuName , DefaultContextBaseMenuName ) ;
}
}
TSharedPtr < SWidget > FActorBrowsingMode : : BuildContextMenu ( )
{
RegisterContextMenu ( ) ;
const FSceneOutlinerItemSelection ItemSelection ( SceneOutliner - > GetSelection ( ) ) ;
USceneOutlinerMenuContext * ContextObject = NewObject < USceneOutlinerMenuContext > ( ) ;
ContextObject - > SceneOutliner = StaticCastSharedRef < SSceneOutliner > ( SceneOutliner - > AsShared ( ) ) ;
ContextObject - > bShowParentTree = SceneOutliner - > GetSharedData ( ) . bShowParentTree ;
ContextObject - > NumSelectedItems = ItemSelection . Num ( ) ;
ContextObject - > NumSelectedFolders = ItemSelection . Num < FFolderTreeItem > ( ) ;
ContextObject - > NumWorldsSelected = ItemSelection . Num < FWorldTreeItem > ( ) ;
ContextObject - > bRepresentingGameWorld = bRepresentingWorldGameWorld ;
ContextObject - > bRepresentingPartitionedWorld = bRepresentingWorldPartitionedWorld ;
int32 NumPinnedItems = 0 ;
if ( const UWorldPartition * const WorldPartition = RepresentingWorld - > GetWorldPartition ( ) )
{
ItemSelection . ForEachItem < IActorBaseTreeItem > ( [ WorldPartition , & NumPinnedItems ] ( const IActorBaseTreeItem & ActorItem )
{
if ( WorldPartition - > IsActorPinned ( ActorItem . GetGuid ( ) ) )
{
+ + NumPinnedItems ;
}
return true ;
} ) ;
}
ContextObject - > NumPinnedItems = NumPinnedItems ;
FToolMenuContext Context ( ContextObject ) ;
FName MenuName = DefaultContextMenuName ;
SceneOutliner - > GetSharedData ( ) . ModifyContextMenu . ExecuteIfBound ( MenuName , Context ) ;
// Build up the menu for a selection
UToolMenus * ToolMenus = UToolMenus : : Get ( ) ;
UToolMenu * Menu = ToolMenus - > GenerateMenu ( MenuName , Context ) ;
for ( const FToolMenuSection & Section : Menu - > Sections )
{
if ( Section . Blocks . Num ( ) > 0 )
{
return ToolMenus - > GenerateWidget ( Menu ) ;
}
}
return nullptr ;
}
TSharedPtr < SWidget > FActorBrowsingMode : : CreateContextMenu ( )
{
TArray < AActor * > SelectedActors ;
GEditor - > GetSelectedActors ( ) - > GetSelectedObjects < AActor > ( SelectedActors ) ;
// Make sure that no components are selected
if ( GEditor - > GetSelectedComponentCount ( ) > 0 )
{
// We want to be able to undo to regain the previous component selection
const FScopedTransaction Transaction ( NSLOCTEXT ( " UnrealEd " , " ClickingOnActorsContextMenu " , " Clicking on Actors (context menu) " )) ;
USelection * ComponentSelection = GEditor - > GetSelectedComponents ( ) ;
ComponentSelection - > Modify ( false ) ;
ComponentSelection - > DeselectAll ( ) ;
GUnrealEd - > UpdatePivotLocationForSelection ( ) ;
GEditor - > RedrawLevelEditingViewports ( false ) ;
}
return BuildContextMenu ( ) ;
}
void FActorBrowsingMode : : OnItemAdded ( FSceneOutlinerTreeItemPtr Item )
{
if ( const FActorTreeItem * ActorItem = Item - > CastTo < FActorTreeItem > ( ) )
{
if ( ! Item - > Flags . bIsFilteredOut )
{
+ + FilteredActorCount ;
// Synchronize selection
if ( GEditor - > GetSelectedActors ( ) - > IsSelected ( ActorItem - > Actor . Get ( ) ) )
{
SceneOutliner - > SetItemSelection ( Item , true ) ;
}
}
}
else if ( FActorFolderTreeItem * FolderItem = Item - > CastTo < FActorFolderTreeItem > ( ) )
{
if ( FolderItem - > World . IsValid ( ) )
{
FolderItem - > Flags . bIsExpanded = FActorFolders : : Get ( ) . IsFolderExpanded ( * FolderItem - > World , FolderItem - > GetFolder ( ) ) ;
}
}
else if ( Item - > IsA < FActorDescTreeItem > ( ) )
{
if ( ! Item - > Flags . bIsFilteredOut )
{
+ + FilteredActorCount ;
+ + FilteredUnloadedActorCount ;
}
}
}
void FActorBrowsingMode : : OnItemRemoved ( FSceneOutlinerTreeItemPtr Item )
{
if ( Item - > IsA < FActorTreeItem > ( ) )
{
if ( ! Item - > Flags . bIsFilteredOut )
{
- - FilteredActorCount ;
}
}
else if ( Item - > IsA < FActorDescTreeItem > ( ) )
{
if ( ! Item - > Flags . bIsFilteredOut )
{
- - FilteredActorCount ;
- - FilteredUnloadedActorCount ;
}
}
}
void FActorBrowsingMode : : OnComponentsUpdated ( )
{
SceneOutliner - > FullRefresh ( ) ;
}
void FActorBrowsingMode : : OnLevelActorDeleted ( AActor * Actor )
{
ApplicableActors . Remove ( Actor ) ;
}
void FActorBrowsingMode : : OnSelectUnloadedActors ( const TArray < FGuid > & ActorGuids )
{
TArray < FSceneOutlinerTreeItemPtr > ItemsToSelect ;
ItemsToSelect . Reserve ( ActorGuids . Num ( ) ) ;
for ( const FGuid & ActorGuid : ActorGuids )
{
if ( FSceneOutlinerTreeItemPtr ItemPtr = SceneOutliner - > GetTreeItem ( ActorGuid ) )
{
ItemsToSelect . Add ( ItemPtr ) ;
}
}
if ( ItemsToSelect . Num ( ) )
{
SceneOutliner - > SetItemSelection ( ItemsToSelect , true ) ;
}
}
void FActorBrowsingMode : : OnActorDescRemoved ( FWorldPartitionActorDesc * InActorDesc )
{
ApplicableUnloadedActors . Remove ( InActorDesc ) ;
}
void FActorBrowsingMode : : OnItemSelectionChanged ( FSceneOutlinerTreeItemPtr TreeItem , ESelectInfo : : Type SelectionType , const FSceneOutlinerItemSelection & Selection )
{
TArray < AActor * > SelectedActors = Selection . GetData < AActor * > ( SceneOutliner : : FActorSelector ( ) ) ;
bool bChanged = false ;
bool bAnyInPIE = false ;
for ( auto * Actor : SelectedActors )
{
if ( ! bAnyInPIE & & Actor & & Actor - > GetOutermost ( ) - > HasAnyPackageFlags ( PKG_PlayInEditor ) )
{
bAnyInPIE = true ;
}
if ( ! GEditor - > GetSelectedActors ( ) - > IsSelected ( Actor ) )
{
bChanged = true ;
break ;
}
}
for ( FSelectionIterator SelectionIt ( * GEditor - > GetSelectedActors ( ) ) ; SelectionIt & & ! bChanged ; + + SelectionIt )
{
const AActor * Actor = CastChecked < AActor > ( * SelectionIt ) ;
if ( ! bAnyInPIE & & Actor - > GetOutermost ( ) - > HasAnyPackageFlags ( PKG_PlayInEditor ) )
{
bAnyInPIE = true ;
}
if ( ! SelectedActors . Contains ( Actor ) )
{
// Actor has been deselected
bChanged = true ;
// If actor was a group actor, remove its members from the ActorsToSelect list
const AGroupActor * DeselectedGroupActor = Cast < AGroupActor > ( Actor ) ;
if ( DeselectedGroupActor )
{
TArray < AActor * > GroupActors ;
DeselectedGroupActor - > GetGroupActors ( GroupActors ) ;
for ( auto * GroupActor : GroupActors )
{
SelectedActors . Remove ( GroupActor ) ;
}
}
}
}
// If there's a discrepancy, update the selected actors to reflect this list.
if ( bChanged )
{
const FScopedTransaction Transaction ( NSLOCTEXT ( " UnrealEd " , " ClickingOnActors " , " Clicking on Actors " ) , ! bAnyInPIE ) ;
GEditor - > GetSelectedActors ( ) - > Modify ( ) ;
// We'll batch selection changes instead by using BeginBatchSelectOperation()
GEditor - > GetSelectedActors ( ) - > BeginBatchSelectOperation ( ) ;
// Clear the selection.
GEditor - > SelectNone ( false , true , true ) ;
const bool bShouldSelect = true ;
const bool bNotifyAfterSelect = false ;
const bool bSelectEvenIfHidden = true ; // @todo outliner: Is this actually OK?
for ( auto * Actor : SelectedActors )
{
UE_LOG ( LogActorBrowser , Verbose , TEXT ( " Clicking on Actor (world outliner): %s (%s) " ) , * Actor - > GetClass ( ) - > GetName ( ) , * Actor - > GetActorLabel ( ) ) ;
GEditor - > SelectActor ( Actor , bShouldSelect , bNotifyAfterSelect , bSelectEvenIfHidden ) ;
}
// Commit selection changes
GEditor - > GetSelectedActors ( ) - > EndBatchSelectOperation ( /*bNotify*/ false ) ;
// Fire selection changed event
GEditor - > NoteSelectionChange ( ) ;
// Set this outliner as the most recently interacted with
SetAsMostRecentOutliner ( ) ;
}
SceneOutliner - > RefreshSelection ( ) ;
}
void FActorBrowsingMode : : OnItemDoubleClick ( FSceneOutlinerTreeItemPtr Item )
{
if ( const FActorTreeItem * ActorItem = Item - > CastTo < FActorTreeItem > ( ) )
{
AActor * Actor = ActorItem - > Actor . Get ( ) ;
check ( Actor ) ;
ILevelInstanceInterface * LevelInstance = Cast < ILevelInstanceInterface > ( Actor ) ;
if ( LevelInstance & & FSlateApplication : : Get ( ) . GetModifierKeys ( ) . IsAltDown ( ) )
{
if ( LevelInstance - > CanEnterEdit ( ) )
{
LevelInstance - > EnterEdit ( ) ;
}
else if ( LevelInstance - > CanExitEdit ( ) )
{
LevelInstance - > ExitEdit ( ) ;
}
}
else if ( Item - > CanInteract ( ) )
{
FSceneOutlinerItemSelection Selection ( SceneOutliner - > GetSelection ( ) ) ;
if ( Selection . Has < FActorTreeItem > ( ) )
{
const bool bActiveViewportOnly = false ;
GEditor - > MoveViewportCamerasToActor ( Selection . GetData < AActor * > ( SceneOutliner : : FActorSelector ( ) ) , bActiveViewportOnly ) ;
}
}
else
{
const bool bActiveViewportOnly = false ;
GEditor - > MoveViewportCamerasToActor ( * Actor , bActiveViewportOnly ) ;
}
}
}
void FActorBrowsingMode : : OnFilterTextCommited ( FSceneOutlinerItemSelection & Selection , ETextCommit : : Type CommitType )
{
// Start batching selection changes
GEditor - > GetSelectedActors ( ) - > BeginBatchSelectOperation ( ) ;
// Select actors (and only the actors) that match the filter text
const bool bNoteSelectionChange = false ;
const bool bDeselectBSPSurfs = false ;
const bool WarnAboutManyActors = true ;
GEditor - > SelectNone ( bNoteSelectionChange , bDeselectBSPSurfs , WarnAboutManyActors ) ;
for ( AActor * Actor : Selection . GetData < AActor * > ( SceneOutliner : : FActorSelector ( ) ) )
{
const bool bShouldSelect = true ;
const bool bSelectEvenIfHidden = false ;
GEditor - > SelectActor ( Actor , bShouldSelect , bNoteSelectionChange , bSelectEvenIfHidden ) ;
}
// Commit selection changes
GEditor - > GetSelectedActors ( ) - > EndBatchSelectOperation ( /*bNotify*/ false ) ;
// Fire selection changed event
GEditor - > NoteSelectionChange ( ) ;
// Set keyboard focus to the SceneOutliner, so the user can perform keyboard commands that interact
// with selected actors (such as Delete, to delete selected actors.)
SceneOutliner - > SetKeyboardFocus ( ) ;
SetAsMostRecentOutliner ( ) ;
}
void FActorBrowsingMode : : OnItemPassesFilters ( const ISceneOutlinerTreeItem & Item )
{
if ( const FActorTreeItem * const ActorItem = Item . CastTo < FActorTreeItem > ( ) )
{
ApplicableActors . Add ( ActorItem - > Actor ) ;
}
else if ( const FActorDescTreeItem * const ActorDescItem = Item . CastTo < FActorDescTreeItem > ( ) ; ActorDescItem & & ActorDescItem - > IsValid ( ) )
{
ApplicableUnloadedActors . Add ( ActorDescItem - > ActorDescHandle . Get ( ) ) ;
}
}
FReply FActorBrowsingMode : : OnKeyDown ( const FKeyEvent & InKeyEvent )
{
const FSceneOutlinerItemSelection & Selection = SceneOutliner - > GetSelection ( ) ;
// Rename key: Rename selected actors (not rebindable, because it doesn't make much sense to bind.)
if ( InKeyEvent . GetKey ( ) = = EKeys : : F2 )
{
if ( Selection . Num ( ) = = 1 )
{
FSceneOutlinerTreeItemPtr ItemToRename = Selection . SelectedItems [ 0 ] . Pin ( ) ;
if ( ItemToRename . IsValid ( ) & & CanRenameItem ( * ItemToRename ) & & ItemToRename - > CanInteract ( ) )
{
SceneOutliner - > SetPendingRenameItem ( ItemToRename ) ;
SceneOutliner - > ScrollItemIntoView ( ItemToRename ) ;
}
return FReply : : Handled ( ) ;
}
}
// F5 forces a full refresh
else if ( InKeyEvent . GetKey ( ) = = EKeys : : F5 )
{
SceneOutliner - > FullRefresh ( ) ;
return FReply : : Handled ( ) ;
}
// Delete key: Delete selected actors (not rebindable, because it doesn't make much sense to bind.)
// Use Delete and Backspace instead of Platform_Delete because the LevelEditor default Edit Delete is bound to both
else if ( InKeyEvent . GetKey ( ) = = EKeys : : Delete | | InKeyEvent . GetKey ( ) = = EKeys : : BackSpace )
{
if ( SceneOutliner - > GetSharedData ( ) . CustomDelete . IsBound ( ) )
{
SceneOutliner - > GetSharedData ( ) . CustomDelete . Execute ( Selection . SelectedItems ) ;
}
else
{
if ( RepresentingWorld . IsValid ( ) )
{
GUnrealEd - > Exec ( RepresentingWorld . Get ( ) , TEXT ( " DELETE " ) ) ;
}
}
return FReply : : Handled ( ) ;
}
return FReply : : Unhandled ( ) ;
}
bool FActorBrowsingMode : : CanDelete ( ) const
{
const FSceneOutlinerItemSelection ItemSelection = SceneOutliner - > GetSelection ( ) ;
const uint32 NumberOfFolders = ItemSelection . Num < FFolderTreeItem > ( ) ;
return ( NumberOfFolders > 0 & & NumberOfFolders = = ItemSelection . Num ( ) ) ;
}
bool FActorBrowsingMode : : CanRename ( ) const
{
const FSceneOutlinerItemSelection ItemSelection = SceneOutliner - > GetSelection ( ) ;
const uint32 NumberOfFolders = ItemSelection . Num < FFolderTreeItem > ( ) ;
return ( NumberOfFolders = = 1 & & NumberOfFolders = = ItemSelection . Num ( ) ) ;
}
bool FActorBrowsingMode : : CanRenameItem ( const ISceneOutlinerTreeItem & Item ) const
{
// Can only rename actor and folder items when in actor browsing mode
return ( Item . IsValid ( ) & & ( Item . IsA < FActorTreeItem > ( ) | | Item . IsA < FFolderTreeItem > ( ) ) ) ;
}
bool FActorBrowsingMode : : CanCut ( ) const
{
const FSceneOutlinerItemSelection ItemSelection = SceneOutliner - > GetSelection ( ) ;
const uint32 NumberOfFolders = ItemSelection . Num < FFolderTreeItem > ( ) ;
return ( NumberOfFolders > 0 & & NumberOfFolders = = ItemSelection . Num ( ) ) ;
}
bool FActorBrowsingMode : : CanCopy ( ) const
{
const FSceneOutlinerItemSelection ItemSelection = SceneOutliner - > GetSelection ( ) ;
const uint32 NumberOfFolders = ItemSelection . Num < FFolderTreeItem > ( ) ;
return ( NumberOfFolders > 0 & & NumberOfFolders = = ItemSelection . Num ( ) ) ;
}
bool FActorBrowsingMode : : CanPaste ( ) const
{
return CanPasteFoldersOnlyFromClipboard ( ) ;
}
bool FActorBrowsingMode : : CanPasteFoldersOnlyFromClipboard ( ) const
{
// Intentionally not checking if the level is locked/hidden here, as it's better feedback for the user if they attempt to paste
// and get the message explaining why it's failed, than just not having the option available to them.
FString PasteString ;
FPlatformApplicationMisc : : ClipboardPaste ( PasteString ) ;
return PasteString . StartsWith ( " BEGIN FOLDERLIST " ) ;
}
bool FActorBrowsingMode : : GetFolderNamesFromPayload ( const FSceneOutlinerDragDropPayload & InPayload , TArray < FName > & OutFolders , FFolder : : FRootObject & OutCommonRootObject ) const
{
return FFolder : : GetFolderPathsAndCommonRootObject ( InPayload . GetData < FFolder > ( SceneOutliner : : FFolderPathSelector ( ) ) , OutFolders , OutCommonRootObject ) ;
}
TSharedPtr < FDragDropOperation > FActorBrowsingMode : : CreateDragDropOperation ( const FPointerEvent & MouseEvent , const TArray < FSceneOutlinerTreeItemPtr > & InTreeItems ) const
{
FSceneOutlinerDragDropPayload DraggedObjects ( InTreeItems ) ;
// If the drag contains only actors, we shortcut and create a simple FActorDragDropGraphEdOp rather than an FSceneOutlinerDragDrop composite op.
if ( DraggedObjects . Has < FActorTreeItem > ( ) & & ! DraggedObjects . Has < FFolderTreeItem > ( ) )
{
return FActorDragDropGraphEdOp : : New ( DraggedObjects . GetData < TWeakObjectPtr < AActor > > ( SceneOutliner : : FWeakActorSelector ( ) ) ) ;
}
TSharedPtr < FSceneOutlinerDragDropOp > OutlinerOp = MakeShareable ( new FSceneOutlinerDragDropOp ( ) ) ;
if ( DraggedObjects . Has < FActorTreeItem > ( ) )
{
TSharedPtr < FActorDragDropOp > ActorOperation = MakeShareable ( new FActorDragDropGraphEdOp ) ;
ActorOperation - > Init ( DraggedObjects . GetData < TWeakObjectPtr < AActor > > ( SceneOutliner : : FWeakActorSelector ( ) ) ) ;
OutlinerOp - > AddSubOp ( ActorOperation ) ;
}
if ( DraggedObjects . Has < FFolderTreeItem > ( ) )
{
FFolder : : FRootObject CommonRootObject ;
TArray < FName > DraggedFolders ;
if ( GetFolderNamesFromPayload ( DraggedObjects , DraggedFolders , CommonRootObject ) )
{
TSharedPtr < FFolderDragDropOp > FolderOperation = MakeShareable ( new FFolderDragDropOp ) ;
FolderOperation - > Init ( DraggedFolders , RepresentingWorld . Get ( ) , CommonRootObject ) ;
OutlinerOp - > AddSubOp ( FolderOperation ) ;
}
}
OutlinerOp - > Construct ( ) ;
return OutlinerOp ;
}
bool FActorBrowsingMode : : ParseDragDrop ( FSceneOutlinerDragDropPayload & OutPayload , const FDragDropOperation & Operation ) const
{
if ( Operation . IsOfType < FSceneOutlinerDragDropOp > ( ) )
{
const auto & OutlinerOp = static_cast < const FSceneOutlinerDragDropOp & > ( Operation ) ;
if ( const auto & FolderOp = OutlinerOp . GetSubOp < FFolderDragDropOp > ( ) )
{
for ( const auto & Folder : FolderOp - > Folders )
{
OutPayload . DraggedItems . Add ( SceneOutliner - > GetTreeItem ( FFolder ( FolderOp - > RootObject , Folder ) ) ) ;
}
}
if ( const auto & ActorOp = OutlinerOp . GetSubOp < FActorDragDropOp > ( ) )
{
for ( const auto & Actor : ActorOp - > Actors )
{
OutPayload . DraggedItems . Add ( SceneOutliner - > GetTreeItem ( Actor . Get ( ) ) ) ;
}
}
return true ;
}
else if ( Operation . IsOfType < FActorDragDropOp > ( ) )
{
for ( const TWeakObjectPtr < AActor > & Actor : static_cast < const FActorDragDropOp & > ( Operation ) . Actors )
{
OutPayload . DraggedItems . Add ( SceneOutliner - > GetTreeItem ( Actor . Get ( ) ) ) ;
}
return true ;
}
return false ;
}
FFolder FActorBrowsingMode : : GetWorldDefaultRootFolder ( ) const
{
return FFolder : : GetWorldRootFolder ( RepresentingWorld . Get ( ) ) ;
}
FSceneOutlinerDragValidationInfo FActorBrowsingMode : : ValidateDrop ( const ISceneOutlinerTreeItem & DropTarget , const FSceneOutlinerDragDropPayload & Payload ) const
{
if ( Payload . Has < FFolderTreeItem > ( ) )
{
FFolder : : FRootObject TargetRootObject = DropTarget . GetRootObject ( ) ;
FFolder : : FRootObject CommonPayloadFoldersRootObject ;
TArray < FName > PayloadFolders ;
const bool bHasCommonRootObject = GetFolderNamesFromPayload ( Payload , PayloadFolders , CommonPayloadFoldersRootObject ) ;
if ( ! bHasCommonRootObject )
{
return FSceneOutlinerDragValidationInfo ( ESceneOutlinerDropCompatibility : : IncompatibleGeneric , LOCTEXT ( " CantMoveFoldersWithMultipleRoots " , " Cannot move folders with multiple roots " ) ) ;
}
else if ( CommonPayloadFoldersRootObject ! = TargetRootObject )
{
return FSceneOutlinerDragValidationInfo ( ESceneOutlinerDropCompatibility : : IncompatibleGeneric , LOCTEXT ( " CantChangeFoldersRoot " , " Cannot change folders root " ) ) ;
}
}
if ( const FActorTreeItem * ActorItem = DropTarget . CastTo < FActorTreeItem > ( ) )
{
const AActor * ActorTarget = ActorItem - > Actor . Get ( ) ;
if ( ! ActorTarget )
{
return FSceneOutlinerDragValidationInfo ( ESceneOutlinerDropCompatibility : : IncompatibleGeneric , FText ( ) ) ;
}
const ILevelInstanceInterface * LevelInstanceTarget = Cast < ILevelInstanceInterface > ( ActorTarget ) ;
const ULevelInstanceSubsystem * LevelInstanceSubsystem = RepresentingWorld - > GetSubsystem < ULevelInstanceSubsystem > ( ) ;
if ( LevelInstanceTarget )
{
check ( LevelInstanceSubsystem ) ;
if ( ! LevelInstanceTarget - > IsEditing ( ) )
{
return FSceneOutlinerDragValidationInfo ( ESceneOutlinerDropCompatibility : : IncompatibleGeneric , LOCTEXT ( " Error_AttachToClosedLevelInstance " , " Cannot attach to LevelInstance which is not being edited " ) ) ;
}
}
else
{
if ( Payload . Has < FFolderTreeItem > ( ) )
{
return FSceneOutlinerDragValidationInfo ( ESceneOutlinerDropCompatibility : : IncompatibleGeneric , LOCTEXT ( " FoldersOnActorError " , " Cannot attach folders to actors " ) ) ;
}
if ( ! Payload . Has < FActorTreeItem > ( ) )
{
return FSceneOutlinerDragValidationInfo ( ESceneOutlinerDropCompatibility : : IncompatibleGeneric , FText ( ) ) ;
}
}
FText AttachErrorMsg ;
bool bCanAttach = true ;
bool bDraggedOntoAttachmentParent = true ;
const auto & DragActors = Payload . GetData < TWeakObjectPtr < AActor > > ( SceneOutliner : : FWeakActorSelector ( ) ) ;
for ( const auto & DragActorPtr : DragActors )
{
AActor * DragActor = DragActorPtr . Get ( ) ;
if ( DragActor )
{
if ( bCanAttach )
{
if ( LevelInstanceSubsystem )
{
// Either all actors must be in a LevelInstance or none of them
if ( const ILevelInstanceInterface * ParentLevelInstance = LevelInstanceSubsystem - > GetParentLevelInstance ( DragActor ) )
{
if ( ! ParentLevelInstance - > IsEditing ( ) )
{
return FSceneOutlinerDragValidationInfo ( ESceneOutlinerDropCompatibility : : IncompatibleGeneric , LOCTEXT ( " Error_RemoveEditingLevelInstance " , " Cannot detach from a LevelInstance which is not being edited " ) ) ;
}
}
if ( ! LevelInstanceSubsystem - > CanMoveActorToLevel ( DragActor , & AttachErrorMsg ) )
{
bCanAttach = bDraggedOntoAttachmentParent = false ;
break ;
}
}
if ( DragActor - > IsChildActor ( ) )
{
AttachErrorMsg = FText : : Format ( LOCTEXT ( " Error_AttachChildActor " , " Cannot move {0} as it is a child actor. " ) , FText : : FromString ( DragActor - > GetActorLabel ( ) ) ) ;
bCanAttach = bDraggedOntoAttachmentParent = false ;
break ;
}
if ( ! LevelInstanceTarget & & ! GEditor - > CanParentActors ( ActorTarget , DragActor , & AttachErrorMsg ) )
{
bCanAttach = false ;
}
}
if ( DragActor - > GetSceneOutlinerParent ( ) ! = ActorTarget )
{
bDraggedOntoAttachmentParent = false ;
}
}
}
const FText ActorLabel = FText : : FromString ( ActorTarget - > GetActorLabel ( ) ) ;
if ( bDraggedOntoAttachmentParent )
{
if ( DragActors . Num ( ) = = 1 )
{
return FSceneOutlinerDragValidationInfo ( ESceneOutlinerDropCompatibility : : CompatibleDetach , ActorLabel ) ;
}
else
{
return FSceneOutlinerDragValidationInfo ( ESceneOutlinerDropCompatibility : : CompatibleMultipleDetach , ActorLabel ) ;
}
}
else if ( bCanAttach )
{
if ( DragActors . Num ( ) = = 1 )
{
return FSceneOutlinerDragValidationInfo ( ESceneOutlinerDropCompatibility : : CompatibleAttach , ActorLabel ) ;
}
else
{
return FSceneOutlinerDragValidationInfo ( ESceneOutlinerDropCompatibility : : CompatibleMultipleAttach , ActorLabel ) ;
}
}
else
{
if ( DragActors . Num ( ) = = 1 )
{
return FSceneOutlinerDragValidationInfo ( ESceneOutlinerDropCompatibility : : IncompatibleGeneric , AttachErrorMsg ) ;
}
else
{
const FText ReasonText = FText : : Format ( LOCTEXT ( " DropOntoText " , " {0}. {1} " ) , ActorLabel , AttachErrorMsg ) ;
return FSceneOutlinerDragValidationInfo ( ESceneOutlinerDropCompatibility : : IncompatibleMultipleAttach , ReasonText ) ;
}
}
}
else if ( DropTarget . IsA < FFolderTreeItem > ( ) | | DropTarget . IsA < FWorldTreeItem > ( ) | | DropTarget . IsA < FLevelTreeItem > ( ) )
{
const FFolderTreeItem * FolderItem = DropTarget . CastTo < FFolderTreeItem > ( ) ;
const FWorldTreeItem * WorldItem = DropTarget . CastTo < FWorldTreeItem > ( ) ;
const FLevelTreeItem * LevelItem = DropTarget . CastTo < FLevelTreeItem > ( ) ;
// WorldTreeItem and LevelTreeItem are treated as root folders (path = none), with the difference that LevelTreeItem has a RootObject.
const FFolder DestinationPath = FolderItem ? FolderItem - > GetFolder ( ) : ( LevelItem ? FFolder ( FFolder : : GetOptionalFolderRootObject ( LevelItem - > Level . Get ( ) ) . Get ( FFolder : : GetInvalidRootObject ( ) ) ) : GetWorldDefaultRootFolder ( ) ) ;
const FFolder : : FRootObject & DestinationRootObject = DestinationPath . GetRootObject ( ) ;
ILevelInstanceInterface * LevelInstanceTarget = Cast < ILevelInstanceInterface > ( DestinationPath . GetRootObjectPtr ( ) ) ;
if ( LevelInstanceTarget & & ! LevelInstanceTarget - > IsEditing ( ) )
{
return FSceneOutlinerDragValidationInfo ( ESceneOutlinerDropCompatibility : : IncompatibleGeneric , LOCTEXT ( " Error_DragInNonEditingLevelInstance " , " Cannot drag into a LevelInstance which is not being edited " ) ) ;
}
if ( Payload . Has < FFolderTreeItem > ( ) )
{
FFolder : : FRootObject CommonFolderRootObject ;
TArray < FName > DraggedFolders ;
if ( GetFolderNamesFromPayload ( Payload , DraggedFolders , CommonFolderRootObject ) )
{
// Iterate over all the folders that have been dragged
for ( const FName & DraggedFolder : DraggedFolders )
{
const FName Leaf = FEditorFolderUtils : : GetLeafName ( DraggedFolder ) ;
const FName Parent = FEditorFolderUtils : : GetParentPath ( DraggedFolder ) ;
if ( ( CommonFolderRootObject ! = DestinationRootObject ) & & FFolder : : IsRootObjectValid ( CommonFolderRootObject ) & & FFolder : : IsRootObjectValid ( DestinationRootObject ) )
{
FFormatNamedArguments Args ;
Args . Add ( TEXT ( " SourceName " ) , FText : : FromName ( Leaf ) ) ;
FText Text = FText : : Format ( LOCTEXT ( " CantChangeFolderRoot " , " Cannot change {SourceName} folder root " ) , Args ) ;
return FSceneOutlinerDragValidationInfo ( ESceneOutlinerDropCompatibility : : IncompatibleGeneric , Text ) ;
}
if ( Parent = = DestinationPath . GetPath ( ) )
{
FFormatNamedArguments Args ;
Args . Add ( TEXT ( " SourceName " ) , FText : : FromName ( Leaf ) ) ;
FText Text ;
if ( DestinationPath . IsNone ( ) )
{
Text = FText : : Format ( LOCTEXT ( " FolderAlreadyAssignedRoot " , " {SourceName} is already assigned to root " ) , Args ) ;
}
else
{
Args . Add ( TEXT ( " DestPath " ) , FText : : FromName ( DestinationPath . GetPath ( ) ) ) ;
Text = FText : : Format ( LOCTEXT ( " FolderAlreadyAssigned " , " {SourceName} is already assigned to {DestPath} " ) , Args ) ;
}
return FSceneOutlinerDragValidationInfo ( ESceneOutlinerDropCompatibility : : IncompatibleGeneric , Text ) ;
}
const FString DragFolderPath = DraggedFolder . ToString ( ) ;
const FString LeafName = Leaf . ToString ( ) ;
const FString DstFolderPath = DestinationPath . IsNone ( ) ? FString ( ) : DestinationPath . ToString ( ) ;
const FString NewPath = DstFolderPath / LeafName ;
if ( FActorFolders : : Get ( ) . ContainsFolder ( * RepresentingWorld , FFolder ( DestinationRootObject , FName ( * NewPath ) ) ) )
{
// The folder already exists
FFormatNamedArguments Args ;
Args . Add ( TEXT ( " DragName " ) , FText : : FromString ( LeafName ) ) ;
return FSceneOutlinerDragValidationInfo ( ESceneOutlinerDropCompatibility : : IncompatibleGeneric ,
FText : : Format ( LOCTEXT ( " FolderAlreadyExistsRoot " , " A folder called \" {DragName} \" already exists at this level " ) , Args ) ) ;
}
else if ( DragFolderPath = = DstFolderPath | | DstFolderPath . StartsWith ( DragFolderPath + " / " ) )
{
// Cannot drag as a child of itself
FFormatNamedArguments Args ;
Args . Add ( TEXT ( " FolderPath " ) , FText : : FromName ( DraggedFolder ) ) ;
return FSceneOutlinerDragValidationInfo (
ESceneOutlinerDropCompatibility : : IncompatibleGeneric ,
FText : : Format ( LOCTEXT ( " ChildOfItself " , " Cannot move \" {FolderPath} \" to be a child of itself " ) , Args ) ) ;
}
}
}
}
if ( Payload . Has < FActorTreeItem > ( ) )
{
const ULevelInstanceSubsystem * LevelInstanceSubsystem = RepresentingWorld - > GetSubsystem < ULevelInstanceSubsystem > ( ) ;
// Iterate over all the actors that have been dragged
for ( const TWeakObjectPtr < AActor > & WeakActor : Payload . GetData < TWeakObjectPtr < AActor > > ( SceneOutliner : : FWeakActorSelector ( ) ) )
{
const AActor * Actor = WeakActor . Get ( ) ;
bool bActorContainedInLevelInstance = false ;
if ( LevelInstanceSubsystem )
{
if ( const ILevelInstanceInterface * ParentLevelInstance = LevelInstanceSubsystem - > GetParentLevelInstance ( Actor ) )
{
if ( ! ParentLevelInstance - > IsEditing ( ) )
{
return FSceneOutlinerDragValidationInfo ( ESceneOutlinerDropCompatibility : : IncompatibleGeneric , LOCTEXT ( " Error_RemoveEditingLevelInstance " , " Cannot detach from a LevelInstance which is not being edited " ) ) ;
}
bActorContainedInLevelInstance = true ;
}
if ( const ILevelInstanceInterface * LevelInstance = Cast < ILevelInstanceInterface > ( Actor ) )
{
FText Reason ;
if ( ! LevelInstanceSubsystem - > CanMoveActorToLevel ( Actor , & Reason ) )
{
return FSceneOutlinerDragValidationInfo ( ESceneOutlinerDropCompatibility : : IncompatibleGeneric , Reason ) ;
}
}
}
if ( Actor - > IsChildActor ( ) )
{
return FSceneOutlinerDragValidationInfo ( ESceneOutlinerDropCompatibility : : IncompatibleGeneric , FText : : Format ( LOCTEXT ( " Error_AttachChildActor " , " Cannot move {0} as it is a child actor. " ) , FText : : FromString ( Actor - > GetActorLabel ( ) ) ) ) ;
}
else if ( ( Actor - > GetFolderRootObject ( ) ! = DestinationRootObject ) & & FFolder : : IsRootObjectValid ( Actor - > GetFolderRootObject ( ) ) & & FFolder : : IsRootObjectValid ( DestinationRootObject ) )
{
FFormatNamedArguments Args ;
Args . Add ( TEXT ( " SourceName " ) , FText : : FromString ( Actor - > GetActorLabel ( ) ) ) ;
FText Text = FText : : Format ( LOCTEXT ( " CantChangeActorRoot " , " Cannot change {SourceName} folder root " ) , Args ) ;
return FSceneOutlinerDragValidationInfo ( ESceneOutlinerDropCompatibility : : IncompatibleGeneric , Text ) ;
}
else if ( Actor - > GetFolder ( ) = = DestinationPath & & ! Actor - > GetSceneOutlinerParent ( ) & & ! bActorContainedInLevelInstance )
{
FFormatNamedArguments Args ;
Args . Add ( TEXT ( " SourceName " ) , FText : : FromString ( Actor - > GetActorLabel ( ) ) ) ;
FText Text ;
if ( DestinationPath . IsNone ( ) )
{
Text = FText : : Format ( LOCTEXT ( " FolderAlreadyAssignedRoot " , " {SourceName} is already assigned to root " ) , Args ) ;
}
else
{
Args . Add ( TEXT ( " DestPath " ) , FText : : FromName ( DestinationPath . GetPath ( ) ) ) ;
Text = FText : : Format ( LOCTEXT ( " FolderAlreadyAssigned " , " {SourceName} is already assigned to {DestPath} " ) , Args ) ;
}
return FSceneOutlinerDragValidationInfo ( ESceneOutlinerDropCompatibility : : IncompatibleGeneric , Text ) ;
}
}
}
// Everything else is a valid operation
if ( DestinationPath . IsNone ( ) )
{
return FSceneOutlinerDragValidationInfo ( ESceneOutlinerDropCompatibility : : CompatibleGeneric , LOCTEXT ( " MoveToRoot " , " Move to root " ) ) ;
}
else
{
FFormatNamedArguments Args ;
Args . Add ( TEXT ( " DestPath " ) , FText : : FromName ( DestinationPath . GetPath ( ) ) ) ;
return FSceneOutlinerDragValidationInfo ( ESceneOutlinerDropCompatibility : : CompatibleGeneric , FText : : Format ( LOCTEXT ( " MoveInto " , " Move into \" {DestPath} \" " ) , Args ) ) ;
}
}
else if ( DropTarget . IsA < FComponentTreeItem > ( ) )
{
// we don't allow drag and drop on components for now
return FSceneOutlinerDragValidationInfo ( ESceneOutlinerDropCompatibility : : IncompatibleGeneric , FText ( ) ) ;
}
return FSceneOutlinerDragValidationInfo : : Invalid ( ) ;
}
void FActorBrowsingMode : : OnDrop ( ISceneOutlinerTreeItem & DropTarget , const FSceneOutlinerDragDropPayload & Payload , const FSceneOutlinerDragValidationInfo & ValidationInfo ) const
{
if ( const FActorTreeItem * ActorItem = DropTarget . CastTo < FActorTreeItem > ( ) )
{
AActor * DropActor = ActorItem - > Actor . Get ( ) ;
if ( ! DropActor )
{
return ;
}
FMessageLog EditorErrors ( " EditorErrors " ) ;
EditorErrors . NewPage ( LOCTEXT ( " ActorAttachmentsPageLabel " , " Actor attachment " ) ) ;
if ( ValidationInfo . CompatibilityType = = ESceneOutlinerDropCompatibility : : CompatibleMultipleDetach | | ValidationInfo . CompatibilityType = = ESceneOutlinerDropCompatibility : : CompatibleDetach )
{
const FScopedTransaction Transaction ( LOCTEXT ( " UndoAction_DetachActors " , " Detach actors " ) ) ;
TArray < TWeakObjectPtr < AActor > > DraggedActors = Payload . GetData < TWeakObjectPtr < AActor > > ( SceneOutliner : : FWeakActorSelector ( ) ) ;
for ( const auto & WeakActor : DraggedActors )
{
if ( auto * DragActor = WeakActor . Get ( ) )
{
// Detach from parent
USceneComponent * RootComp = DragActor - > GetRootComponent ( ) ;
if ( RootComp & & RootComp - > GetAttachParent ( ) )
{
AActor * OldParent = RootComp - > GetAttachParent ( ) - > GetOwner ( ) ;
// Attachment is persisted on the child so modify both actors for Undo/Redo but do not mark the Parent package dirty
OldParent - > Modify ( /*bAlwaysMarkDirty=*/ false ) ;
RootComp - > DetachFromComponent ( FDetachmentTransformRules : : KeepWorldTransform ) ;
DragActor - > SetFolderPath_Recursively ( OldParent - > GetFolderPath ( ) ) ;
}
}
}
}
else if ( ValidationInfo . CompatibilityType = = ESceneOutlinerDropCompatibility : : CompatibleMultipleAttach | | ValidationInfo . CompatibilityType = = ESceneOutlinerDropCompatibility : : CompatibleAttach )
{
// Show socket chooser if we have sockets to select
if ( ILevelInstanceInterface * TargetLevelInstance = Cast < ILevelInstanceInterface > ( DropActor ) )
{
check ( TargetLevelInstance - > IsEditing ( ) ) ;
const FScopedTransaction Transaction ( LOCTEXT ( " UndoAction_MoveActorsToLevelInstance " , " Move actors to LevelInstance " ) ) ;
const FFolder DestinationPath = FFolder ( FFolder : : FRootObject ( DropActor ) ) ;
auto MoveToDestination = [ & DestinationPath ] ( FFolderTreeItem & Item )
{
Item . MoveTo ( DestinationPath ) ;
} ;
Payload . ForEachItem < FFolderTreeItem > ( MoveToDestination ) ;
// Since target root is directly the Level Instance, clear folder path
TArray < AActor * > DraggedActors = Payload . GetData < AActor * > ( SceneOutliner : : FActorSelector ( ) ) ;
for ( auto & Actor : DraggedActors )
{
Actor - > SetFolderPath_Recursively ( FName ( ) ) ;
}
ULevelInstanceSubsystem * LevelInstanceSubsystem = RepresentingWorld - > GetSubsystem < ULevelInstanceSubsystem > ( ) ;
check ( LevelInstanceSubsystem ) ;
LevelInstanceSubsystem - > MoveActorsTo ( TargetLevelInstance , DraggedActors ) ;
}
else
{
auto PerformAttachment = [ ] ( FName SocketName , TWeakObjectPtr < AActor > Parent , const TArray < TWeakObjectPtr < AActor > > NewAttachments )
{
AActor * ParentActor = Parent . Get ( ) ;
if ( ParentActor )
{
// modify parent and child
const FScopedTransaction Transaction ( LOCTEXT ( " UndoAction_PerformAttachment " , " Attach actors " ) ) ;
// Attach each child
bool bAttached = false ;
for ( auto & Child : NewAttachments )
{
AActor * ChildActor = Child . Get ( ) ;
if ( GEditor - > CanParentActors ( ParentActor , ChildActor ) )
{
GEditor - > ParentActors ( ParentActor , ChildActor , SocketName ) ;
ChildActor - > SetFolderPath_Recursively ( ParentActor - > GetFolderPath ( ) ) ;
}
}
}
} ;
TArray < TWeakObjectPtr < AActor > > DraggedActors = Payload . GetData < TWeakObjectPtr < AActor > > ( SceneOutliner : : FWeakActorSelector ( ) ) ;
//@TODO: Should create a menu for each component that contains sockets, or have some form of disambiguation within the menu (like a fully qualified path)
// Instead, we currently only display the sockets on the root component
USceneComponent * Component = DropActor - > GetRootComponent ( ) ;
if ( ( Component ! = NULL ) & & ( Component - > HasAnySockets ( ) ) )
{
// Create the popup
FSlateApplication : : Get ( ) . PushMenu (
SceneOutliner - > AsShared ( ) ,
FWidgetPath ( ) ,
SNew ( SSocketChooserPopup )
. SceneComponent ( Component )
. OnSocketChosen_Lambda ( PerformAttachment , DropActor , MoveTemp ( DraggedActors ) ) ,
FSlateApplication : : Get ( ) . GetCursorPos ( ) ,
FPopupTransitionEffect ( FPopupTransitionEffect : : TypeInPopup )
) ;
}
else
{
PerformAttachment ( NAME_None , DropActor , MoveTemp ( DraggedActors ) ) ;
}
}
}
// Report errors
EditorErrors . Notify ( NSLOCTEXT ( " ActorAttachmentError " , " AttachmentsFailed " , " Attachments Failed! " ) ) ;
}
else if ( DropTarget . IsA < FFolderTreeItem > ( ) | | DropTarget . IsA < FWorldTreeItem > ( ) | | DropTarget . IsA < FLevelTreeItem > ( ) )
{
const FFolderTreeItem * FolderItem = DropTarget . CastTo < FFolderTreeItem > ( ) ;
const FWorldTreeItem * WorldItem = DropTarget . CastTo < FWorldTreeItem > ( ) ;
const FLevelTreeItem * LevelItem = DropTarget . CastTo < FLevelTreeItem > ( ) ;
// WorldTreeItem and LevelTreeItem are treated as root folders (path = none), with the difference that LevelTreeItem has a RootObject.
const FFolder DestinationPath = FolderItem ? FolderItem - > GetFolder ( ) : ( LevelItem ? FFolder ( FFolder : : GetOptionalFolderRootObject ( LevelItem - > Level . Get ( ) ) . Get ( FFolder : : GetInvalidRootObject ( ) ) ) : GetWorldDefaultRootFolder ( ) ) ;
const FScopedTransaction Transaction ( LOCTEXT ( " MoveOutlinerItems " , " Move World Outliner Items " ) ) ;
auto MoveToDestination = [ & DestinationPath ] ( FFolderTreeItem & Item )
{
Item . MoveTo ( DestinationPath ) ;
} ;
Payload . ForEachItem < FFolderTreeItem > ( MoveToDestination ) ;
// Set the folder path on all the dragged actors, and detach any that need to be moved
if ( Payload . Has < FActorTreeItem > ( ) )
{
TSet < const AActor * > ParentActors ;
TSet < const AActor * > ChildActors ;
TArray < AActor * > MovingActorsToValidRootObject ;
Payload . ForEachItem < FActorTreeItem > ( [ & DestinationPath , & ParentActors , & ChildActors , & MovingActorsToValidRootObject ] ( const FActorTreeItem & ActorItem )
{
AActor * Actor = ActorItem . Actor . Get ( ) ;
if ( Actor )
{
// First mark this object as a parent, then set its children's path
ParentActors . Add ( Actor ) ;
const FFolder SrcFolder = Actor - > GetFolder ( ) ;
// If the folder root object changes, 1st pass will put actors at root. 2nd pass will set the destination path.
FName NewPath = ( SrcFolder . GetRootObject ( ) = = DestinationPath . GetRootObject ( ) ) ? DestinationPath . GetPath ( ) : NAME_None ;
Actor - > SetFolderPath ( NewPath ) ;
FActorEditorUtils : : TraverseActorTree_ParentFirst ( Actor , [ & ] ( AActor * InActor ) {
ChildActors . Add ( InActor ) ;
InActor - > SetFolderPath ( NewPath ) ;
return true ;
} , false ) ;
if ( ( Actor - > GetFolderRootObject ( ) ! = DestinationPath . GetRootObject ( ) ) & & SrcFolder . IsRootObjectPersistentLevel ( ) & & ( DestinationPath . IsRootObjectValid ( ) & & ! DestinationPath . IsRootObjectPersistentLevel ( ) ) )
{
MovingActorsToValidRootObject . Add ( Actor ) ;
}
}
} ) ;
// Detach parent actors
for ( const AActor * Parent : ParentActors )
{
auto * RootComp = Parent - > GetRootComponent ( ) ;
// We don't detach if it's a child of another that's been dragged
if ( RootComp & & RootComp - > GetAttachParent ( ) & & ! ChildActors . Contains ( Parent ) )
{
if ( AActor * OldParentActor = RootComp - > GetAttachParent ( ) - > GetOwner ( ) )
{
// Attachment is persisted on the child so modify both actors for Undo/Redo but do not mark the Parent package dirty
OldParentActor - > Modify ( /*bAlwaysMarkDirty=*/ false ) ;
}
RootComp - > DetachFromComponent ( FDetachmentTransformRules : : KeepWorldTransform ) ;
}
}
auto MoveActorsToLevel = [ ] ( const TArray < AActor * > & InActorsToMove , ULevel * InDestLevel , const FName & InDestinationPath )
{
// We are moving actors to another level
const bool bWarnAboutReferences = true ;
const bool bWarnAboutRenaming = true ;
const bool bMoveAllOrFail = true ;
TArray < AActor * > MovedActors ;
if ( ! EditorLevelUtils : : MoveActorsToLevel ( InActorsToMove , InDestLevel , bWarnAboutReferences , bWarnAboutRenaming , bMoveAllOrFail , & MovedActors ) )
{
UE_LOG ( LogActorBrowser , Warning , TEXT ( " Failed to move actors because not all actors could be moved " ) ) ;
}
// Once moved, update actors folder path
for ( AActor * Actor : MovedActors )
{
Actor - > SetFolderPath_Recursively ( InDestinationPath ) ;
}
} ;
if ( DestinationPath . IsRootObjectPersistentLevel ( ) )
{
const ULevelInstanceSubsystem * LevelInstanceSubsystem = RepresentingWorld - > GetSubsystem < ULevelInstanceSubsystem > ( ) ;
check ( LevelInstanceSubsystem ) ;
ULevel * DestinationLevel = RepresentingWorld - > PersistentLevel ;
check ( DestinationLevel ) ;
TArray < AActor * > LevelInstanceActorsToMove ;
TArray < AActor * > ActorsToMoveToPersistentLevel ;
Payload . ForEachItem < FActorTreeItem > ( [ LevelInstanceSubsystem , & LevelInstanceActorsToMove , & ActorsToMoveToPersistentLevel ] ( const FActorTreeItem & ActorItem )
{
AActor * Actor = ActorItem . Actor . Get ( ) ;
if ( const ILevelInstanceInterface * ParentLevelInstance = LevelInstanceSubsystem - > GetParentLevelInstance ( Actor ) )
{
check ( ParentLevelInstance - > IsEditing ( ) ) ;
LevelInstanceActorsToMove . Add ( Actor ) ;
}
else
{
const FFolder ActorSrcFolder = Actor - > GetFolder ( ) ;
if ( ActorSrcFolder . IsRootObjectValid ( ) & & ! ActorSrcFolder . IsRootObjectPersistentLevel ( ) )
{
ActorsToMoveToPersistentLevel . Add ( Actor ) ;
}
}
} ) ;
// We are moving actors outside of an editing level instance to a folder (or root) into the persistent level.
if ( LevelInstanceActorsToMove . Num ( ) > 0 )
{
TArray < AActor * > MovedActors ;
LevelInstanceSubsystem - > MoveActorsToLevel ( LevelInstanceActorsToMove , DestinationLevel , & MovedActors ) ;
// Once moved, update actors folder path
for ( AActor * Actor : MovedActors )
{
Actor - > SetFolderPath_Recursively ( DestinationPath . GetPath ( ) ) ;
}
}
if ( ActorsToMoveToPersistentLevel . Num ( ) > 0 )
{
MoveActorsToLevel ( ActorsToMoveToPersistentLevel , DestinationLevel , DestinationPath . GetPath ( ) ) ;
}
}
else if ( MovingActorsToValidRootObject . Num ( ) )
{
if ( ILevelInstanceInterface * TargetLevelInstance = Cast < ILevelInstanceInterface > ( DestinationPath . GetRootObjectPtr ( ) ) )
{
// We are moving actors inside an editing level instance
check ( TargetLevelInstance - > IsEditing ( ) ) ;
ULevelInstanceSubsystem * LevelInstanceSubsystem = RepresentingWorld - > GetSubsystem < ULevelInstanceSubsystem > ( ) ;
check ( LevelInstanceSubsystem ) ;
TArray < AActor * > MovedActors ;
LevelInstanceSubsystem - > MoveActorsTo ( TargetLevelInstance , MovingActorsToValidRootObject , & MovedActors ) ;
// Once moved, update actors folder path
for ( AActor * Actor : MovedActors )
{
Actor - > SetFolderPath_Recursively ( DestinationPath . GetPath ( ) ) ;
}
}
else if ( ULevel * DestinationLevel = Cast < ULevel > ( DestinationPath . GetRootObjectPtr ( ) ) )
{
MoveActorsToLevel ( MovingActorsToValidRootObject , DestinationLevel , DestinationPath . GetPath ( ) ) ;
}
}
}
}
}
FFolder FActorBrowsingMode : : CreateNewFolder ( )
{
const FScopedTransaction Transaction ( LOCTEXT ( " UndoAction_CreateFolder " , " Create Folder " ) ) ;
TArray < FFolder > SelectedFolders = SceneOutliner - > GetSelection ( ) . GetData < FFolder > ( SceneOutliner : : FFolderPathSelector ( ) ) ;
const FFolder NewFolderName = FActorFolders : : Get ( ) . GetDefaultFolderForSelection ( * RepresentingWorld , & SelectedFolders ) ;
FActorFolders : : Get ( ) . CreateFolderContainingSelection ( * RepresentingWorld , NewFolderName ) ;
return NewFolderName ;
}
FFolder FActorBrowsingMode : : GetFolder ( const FFolder & ParentPath , const FName & LeafName )
{
// Return a unique folder under the provided parent path & root object and using the provided leaf name
return FActorFolders : : Get ( ) . GetFolderName ( * RepresentingWorld , ParentPath , LeafName ) ;
}
bool FActorBrowsingMode : : CreateFolder ( const FFolder & NewPath )
{
return FActorFolders : : Get ( ) . CreateFolder ( * RepresentingWorld , NewPath ) ;
}
bool FActorBrowsingMode : : ReparentItemToFolder ( const FFolder & FolderPath , const FSceneOutlinerTreeItemPtr & Item )
{
if ( FActorTreeItem * ActorItem = Item - > CastTo < FActorTreeItem > ( ) )
{
// Make sure actor has the same root object before updating path
if ( ActorItem - > Actor - > GetFolderRootObject ( ) = = FolderPath . GetRootObject ( ) )
{
ActorItem - > Actor - > SetFolderPath_Recursively ( FolderPath . GetPath ( ) ) ;
return true ;
}
}
return false ;
}
namespace ActorBrowsingModeUtils
{
static void RecursiveFolderExpandChildren ( SSceneOutliner * SceneOutliner , const FSceneOutlinerTreeItemPtr & Item )
{
if ( Item . IsValid ( ) )
{
for ( const TWeakPtr < ISceneOutlinerTreeItem > & Child : Item - > GetChildren ( ) )
{
FSceneOutlinerTreeItemPtr ChildPtr = Child . Pin ( ) ;
SceneOutliner - > SetItemExpansion ( ChildPtr , true ) ;
RecursiveFolderExpandChildren ( SceneOutliner , ChildPtr ) ;
}
}
}
static void RecursiveActorSelect ( SSceneOutliner * SceneOutliner , const FSceneOutlinerTreeItemPtr & Item , bool bSelectImmediateChildrenOnly )
{
if ( Item . IsValid ( ) )
{
// If the current item is an actor, ensure to select it as well
if ( FActorTreeItem * ActorItem = Item - > CastTo < FActorTreeItem > ( ) )
{
if ( AActor * Actor = ActorItem - > Actor . Get ( ) )
{
GEditor - > SelectActor ( Actor , true , false ) ;
}
}
// Select all children
for ( const TWeakPtr < ISceneOutlinerTreeItem > & Child : Item - > GetChildren ( ) )
{
FSceneOutlinerTreeItemPtr ChildPtr = Child . Pin ( ) ;
if ( ChildPtr . IsValid ( ) )
{
if ( FActorTreeItem * ActorItem = ChildPtr - > CastTo < FActorTreeItem > ( ) )
{
if ( AActor * Actor = ActorItem - > Actor . Get ( ) )
{
GEditor - > SelectActor ( Actor , true , false ) ;
}
}
else if ( FFolderTreeItem * FolderItem = ChildPtr - > CastTo < FFolderTreeItem > ( ) )
{
SceneOutliner - > SetItemSelection ( FolderItem - > AsShared ( ) , true ) ;
}
if ( ! bSelectImmediateChildrenOnly )
{
for ( const TWeakPtr < ISceneOutlinerTreeItem > & Grandchild : ChildPtr - > GetChildren ( ) )
{
RecursiveActorSelect ( SceneOutliner , Grandchild . Pin ( ) , bSelectImmediateChildrenOnly ) ;
}
}
}
}
}
}
static void RecursiveAddItemsToActorGuidList ( const TArray < FSceneOutlinerTreeItemPtr > & Items , TArray < FGuid > & List )
{
for ( const FSceneOutlinerTreeItemPtr & Item : Items )
{
if ( const FActorDescTreeItem * const ActorDescTreeItem = Item - > CastTo < FActorDescTreeItem > ( ) )
{
List . Add ( ActorDescTreeItem - > GetGuid ( ) ) ;
}
else if ( const FActorTreeItem * const ActorTreeItem = Item - > CastTo < FActorTreeItem > ( ) )
{
if ( ActorTreeItem - > Actor . IsValid ( ) )
{
List . Add ( ActorTreeItem - > Actor - > GetActorGuid ( ) ) ;
}
}
TArray < FSceneOutlinerTreeItemPtr > ChildrenItems ;
for ( const auto & Child : Item - > GetChildren ( ) )
{
if ( Child . IsValid ( ) )
{
ChildrenItems . Add ( Child . Pin ( ) ) ;
}
}
if ( ChildrenItems . Num ( ) )
{
RecursiveAddItemsToActorGuidList ( ChildrenItems , List ) ;
}
}
} ;
}
void FActorBrowsingMode : : SelectFoldersDescendants ( const TArray < FFolderTreeItem * > & FolderItems , bool bSelectImmediateChildrenOnly )
{
// Expand everything before beginning selection
for ( FFolderTreeItem * Folder : FolderItems )
{
FSceneOutlinerTreeItemPtr FolderPtr = Folder - > AsShared ( ) ;
SceneOutliner - > SetItemExpansion ( FolderPtr , true ) ;
if ( ! bSelectImmediateChildrenOnly )
{
ActorBrowsingModeUtils : : RecursiveFolderExpandChildren ( SceneOutliner , FolderPtr ) ;
}
}
// batch selection
GEditor - > GetSelectedActors ( ) - > BeginBatchSelectOperation ( ) ;
for ( FFolderTreeItem * Folder : FolderItems )
{
ActorBrowsingModeUtils : : RecursiveActorSelect ( SceneOutliner , Folder - > AsShared ( ) , bSelectImmediateChildrenOnly ) ;
}
GEditor - > GetSelectedActors ( ) - > EndBatchSelectOperation ( /*bNotify*/ false ) ;
GEditor - > NoteSelectionChange ( ) ;
}
void FActorBrowsingMode : : PinItems ( const TArray < FSceneOutlinerTreeItemPtr > & InItems )
{
UWorldPartition * const WorldPartition = RepresentingWorld - > GetWorldPartition ( ) ;
if ( ! WorldPartition )
{
return ;
}
TArray < FGuid > ActorsToPin ;
ActorBrowsingModeUtils : : RecursiveAddItemsToActorGuidList ( InItems , ActorsToPin ) ;
if ( ActorsToPin . Num ( ) )
{
GEditor - > GetSelectedActors ( ) - > BeginBatchSelectOperation ( ) ;
GEditor - > SelectNone ( /*bNoteSelectionChange=*/ false , /*bDeselectBSPSurfs=*/ true ) ;
WorldPartition - > PinActors ( ActorsToPin ) ;
AActor * LastPinnedActor = nullptr ;
for ( const FGuid & ActorGuid : ActorsToPin )
{
if ( FWorldPartitionHandle ActorHandle ( WorldPartition , ActorGuid ) ; ActorHandle . IsValid ( ) )
{
if ( AActor * PinnedActor = ActorHandle - > GetActor ( ) )
{
GEditor - > SelectActor ( PinnedActor , /*bInSelected=*/ true , /*bNotify=*/ false ) ;
LastPinnedActor = PinnedActor ;
}
}
}
GEditor - > GetSelectedActors ( ) - > EndBatchSelectOperation ( /*bNotify=*/ true ) ;
if ( LastPinnedActor )
{
SceneOutliner - > OnItemAdded ( LastPinnedActor , SceneOutliner : : ENewItemAction : : ScrollIntoView ) ;
}
}
}
void FActorBrowsingMode : : UnpinItems ( const TArray < FSceneOutlinerTreeItemPtr > & InItems )
{
UWorldPartition * const WorldPartition = RepresentingWorld - > GetWorldPartition ( ) ;
if ( ! WorldPartition )
{
return ;
}
TArray < FGuid > ActorsToUnpin ;
ActorBrowsingModeUtils : : RecursiveAddItemsToActorGuidList ( InItems , ActorsToUnpin ) ;
if ( ActorsToUnpin . Num ( ) )
{
GEditor - > GetSelectedActors ( ) - > BeginBatchSelectOperation ( ) ;
AActor * LastPinnedActor = nullptr ;
for ( const FGuid & ActorGuid : ActorsToUnpin )
{
if ( FWorldPartitionHandle ActorHandle ( WorldPartition , ActorGuid ) ; ActorHandle . IsValid ( ) )
{
if ( AActor * PinnedActor = ActorHandle - > GetActor ( ) )
{
GEditor - > SelectActor ( PinnedActor , /*bInSelected=*/ false , /*bNotify=*/ false ) ;
}
}
}
WorldPartition - > UnpinActors ( ActorsToUnpin ) ;
GEditor - > GetSelectedActors ( ) - > EndBatchSelectOperation ( /*bNotify=*/ true ) ;
}
}
void FActorBrowsingMode : : PinSelectedItems ( )
{
const FSceneOutlinerItemSelection Selection = SceneOutliner - > GetSelection ( ) ;
if ( Selection . Num ( ) )
{
TArray < FSceneOutlinerTreeItemPtr > ItemsToPin ;
Selection . ForEachItem ( [ this , & ItemsToPin ] ( const FSceneOutlinerTreeItemPtr & TreeItem )
{
ItemsToPin . Add ( TreeItem ) ;
return true ;
} ) ;
PinItems ( ItemsToPin ) ;
}
}
void FActorBrowsingMode : : UnpinSelectedItems ( )
{
const FSceneOutlinerItemSelection Selection = SceneOutliner - > GetSelection ( ) ;
if ( Selection . Num ( ) )
{
TArray < FSceneOutlinerTreeItemPtr > ItemsToUnpin ;
Selection . ForEachItem ( [ this , & ItemsToUnpin ] ( const FSceneOutlinerTreeItemPtr & TreeItem )
{
ItemsToUnpin . Add ( TreeItem ) ;
return true ;
} ) ;
UnpinItems ( ItemsToUnpin ) ;
}
}
FCreateSceneOutlinerMode FActorBrowsingMode : : CreateFolderPickerMode ( const FFolder : : FRootObject & InRootObject ) const
{
auto MoveSelectionTo = [ this , InRootObject ] ( const FSceneOutlinerTreeItemRef & NewParent )
{
if ( FWorldTreeItem * WorldItem = NewParent - > CastTo < FWorldTreeItem > ( ) )
{
SceneOutliner - > MoveSelectionTo ( GetWorldDefaultRootFolder ( ) ) ;
}
else if ( FFolderTreeItem * FolderItem = NewParent - > CastTo < FFolderTreeItem > ( ) )
{
SceneOutliner - > MoveSelectionTo ( FolderItem - > GetFolder ( ) ) ;
}
else if ( FActorTreeItem * ActorItem = NewParent - > CastTo < FActorTreeItem > ( ) )
{
if ( FFolder : : IsRootObjectValid ( InRootObject ) )
{
SceneOutliner - > MoveSelectionTo ( FFolder ( InRootObject ) ) ;
}
}
else if ( FLevelTreeItem * LevelItem = NewParent - > CastTo < FLevelTreeItem > ( ) )
{
if ( FFolder : : IsRootObjectValid ( InRootObject ) )
{
SceneOutliner - > MoveSelectionTo ( FFolder ( InRootObject ) ) ;
}
}
} ;
return FCreateSceneOutlinerMode : : CreateLambda ( [ this , MoveSelectionTo , InRootObject ] ( SSceneOutliner * Outliner )
{
return new FActorFolderPickingMode ( Outliner , FOnSceneOutlinerItemPicked : : CreateLambda ( MoveSelectionTo ) , nullptr , InRootObject ) ;
} ) ;
}
void FActorBrowsingMode : : OnDuplicateSelected ( )
{
GUnrealEd - > Exec ( RepresentingWorld . Get ( ) , TEXT ( " DUPLICATE " ) ) ;
}
void FActorBrowsingMode : : OnEditCutActorsBegin ( )
{
// Only a callback in actor browsing mode
SceneOutliner - > CopyFoldersBegin ( ) ;
SceneOutliner - > DeleteFoldersBegin ( ) ;
}
void FActorBrowsingMode : : OnEditCutActorsEnd ( )
{
// Only a callback in actor browsing mode
SceneOutliner - > CopyFoldersEnd ( ) ;
SceneOutliner - > DeleteFoldersEnd ( ) ;
}
void FActorBrowsingMode : : OnEditCopyActorsBegin ( )
{
// Only a callback in actor browsing mode
SceneOutliner - > CopyFoldersBegin ( ) ;
}
void FActorBrowsingMode : : OnEditCopyActorsEnd ( )
{
// Only a callback in actor browsing mode
SceneOutliner - > CopyFoldersEnd ( ) ;
}
void FActorBrowsingMode : : OnEditPasteActorsBegin ( )
{
// Only a callback in actor browsing mode
const TArray < FName > FolderPaths = SceneOutliner - > GetClipboardPasteFolders ( ) ;
SceneOutliner - > PasteFoldersBegin ( FolderPaths ) ;
}
void FActorBrowsingMode : : OnEditPasteActorsEnd ( )
{
// Only a callback in actor browsing mode
SceneOutliner - > PasteFoldersEnd ( ) ;
}
void FActorBrowsingMode : : OnDuplicateActorsBegin ( )
{
// Only a callback in actor browsing mode
FFolder : : FRootObject CommonRootObject ;
TArray < FName > SelectedFolderPaths ;
FFolder : : GetFolderPathsAndCommonRootObject ( SceneOutliner - > GetSelection ( ) . GetData < FFolder > ( SceneOutliner : : FFolderPathSelector ( ) ) , SelectedFolderPaths , CommonRootObject ) ;
SceneOutliner - > PasteFoldersBegin ( SelectedFolderPaths ) ;
}
void FActorBrowsingMode : : OnDuplicateActorsEnd ( )
{
// Only a callback in actor browsing mode
SceneOutliner - > PasteFoldersEnd ( ) ;
}
void FActorBrowsingMode : : OnDeleteActorsBegin ( )
{
SceneOutliner - > DeleteFoldersBegin ( ) ;
}
void FActorBrowsingMode : : OnDeleteActorsEnd ( )
{
SceneOutliner - > DeleteFoldersEnd ( ) ;
}
struct FActorBrowsingModeConfig * FActorBrowsingMode : : GetMutableConfig ( )
{
FName OutlinerIdentifier = SceneOutliner - > GetOutlinerIdentifier ( ) ;
if ( OutlinerIdentifier . IsNone ( ) )
{
return nullptr ;
}
return & UActorBrowserConfig : : Get ( ) - > ActorBrowsers . FindOrAdd ( OutlinerIdentifier ) ;
}
const FActorBrowsingModeConfig * FActorBrowsingMode : : GetConstConfig ( ) const
{
FName OutlinerIdentifier = SceneOutliner - > GetOutlinerIdentifier ( ) ;
if ( OutlinerIdentifier . IsNone ( ) )
{
return nullptr ;
}
return UActorBrowserConfig : : Get ( ) - > ActorBrowsers . Find ( OutlinerIdentifier ) ;
}
void FActorBrowsingMode : : SaveConfig ( )
{
UActorBrowserConfig : : Get ( ) - > SaveEditorConfig ( ) ;
}
# undef LOCTEXT_NAMESPACE