2020-07-28 10:35:27 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
2020-07-22 09:35:57 -04:00
# include "ActorFolderTreeItem.h"
# include "ActorTreeItem.h"
# include "SSceneOutliner.h"
# include "ActorEditorUtils.h"
# include "EditorActorFolders.h"
2020-08-26 12:46:21 -04:00
# include "EditorFolderUtils.h"
2020-07-22 09:35:57 -04:00
# include "Widgets/Text/SInlineEditableTextBlock.h"
# include "ISceneOutliner.h"
# include "ISceneOutlinerMode.h"
# include "ScopedTransaction.h"
2021-03-31 11:34:53 -04:00
# include "ToolMenus.h"
# include "ActorDescTreeItem.h"
2021-12-16 19:26:29 -05:00
# include "Engine/World.h"
2022-01-07 10:01:19 -05:00
# include "WorldPersistentFolders.h"
# include "Engine/Level.h"
# include "LevelUtils.h"
2022-03-29 09:19:22 -04:00
# include "LevelInstance/LevelInstanceInterface.h"
2022-01-07 10:01:19 -05:00
# include "LevelInstance/LevelInstanceSubsystem.h"
2020-07-22 09:35:57 -04:00
# define LOCTEXT_NAMESPACE "SceneOutliner_ActorFolderTreeItem"
2020-07-28 09:25:56 -04:00
const FSceneOutlinerTreeItemType FActorFolderTreeItem : : Type ( & FFolderTreeItem : : Type ) ;
struct SActorFolderTreeLabel : FSceneOutlinerCommonLabelData , public SCompoundWidget
2020-07-22 09:35:57 -04:00
{
2020-07-28 09:25:56 -04:00
SLATE_BEGIN_ARGS ( SActorFolderTreeLabel ) { }
SLATE_END_ARGS ( )
2020-07-22 09:35:57 -04:00
2020-07-28 09:25:56 -04:00
void Construct ( const FArguments & InArgs , FActorFolderTreeItem & FolderItem , ISceneOutliner & SceneOutliner , const STableRow < FSceneOutlinerTreeItemPtr > & InRow )
2020-07-22 09:35:57 -04:00
{
2020-07-28 09:25:56 -04:00
TreeItemPtr = StaticCastSharedRef < FActorFolderTreeItem > ( FolderItem . AsShared ( ) ) ;
WeakSceneOutliner = StaticCastSharedRef < ISceneOutliner > ( SceneOutliner . AsShared ( ) ) ;
2020-07-22 09:35:57 -04:00
2020-07-28 09:25:56 -04:00
TSharedPtr < SInlineEditableTextBlock > InlineTextBlock = SNew ( SInlineEditableTextBlock )
. Text ( this , & SActorFolderTreeLabel : : GetDisplayText )
2022-05-02 11:00:12 -04:00
. ToolTipText ( this , & SActorFolderTreeLabel : : GetTooltipText )
2020-07-28 09:25:56 -04:00
. HighlightText ( SceneOutliner . GetFilterHighlightText ( ) )
. ColorAndOpacity ( this , & SActorFolderTreeLabel : : GetForegroundColor )
. OnTextCommitted ( this , & SActorFolderTreeLabel : : OnLabelCommitted )
. OnVerifyTextChanged ( this , & SActorFolderTreeLabel : : OnVerifyItemLabelChanged )
2022-05-02 11:00:12 -04:00
. OnEnterEditingMode ( this , & SActorFolderTreeLabel : : OnEnterEditingMode )
. OnExitEditingMode ( this , & SActorFolderTreeLabel : : OnExitEditingMode )
2020-07-28 09:25:56 -04:00
. IsSelected ( FIsSelected : : CreateSP ( & InRow , & STableRow < FSceneOutlinerTreeItemPtr > : : IsSelectedExclusively ) )
. IsReadOnly_Lambda ( [ Item = FolderItem . AsShared ( ) , this ] ( )
2020-07-22 09:35:57 -04:00
{
2020-07-28 09:25:56 -04:00
return ! CanExecuteRenameRequest ( Item . Get ( ) ) ;
} ) ;
2020-07-22 09:35:57 -04:00
2020-07-28 09:25:56 -04:00
if ( WeakSceneOutliner . Pin ( ) - > GetMode ( ) - > IsInteractive ( ) )
{
FolderItem . RenameRequestEvent . BindSP ( InlineTextBlock . Get ( ) , & SInlineEditableTextBlock : : EnterEditingMode ) ;
2020-07-22 09:35:57 -04:00
}
2020-07-28 09:25:56 -04:00
ChildSlot
[
SNew ( SHorizontalBox )
+ SHorizontalBox : : Slot ( )
2022-03-08 09:40:27 -05:00
. AutoWidth ( )
. VAlign ( VAlign_Center )
. Padding ( FSceneOutlinerDefaultTreeItemMetrics : : IconPadding ( ) )
[
SNew ( SBox )
. WidthOverride ( FSceneOutlinerDefaultTreeItemMetrics : : IconSize ( ) )
. HeightOverride ( FSceneOutlinerDefaultTreeItemMetrics : : IconSize ( ) )
[
SNew ( SImage )
. Image ( this , & SActorFolderTreeLabel : : GetIcon )
. ColorAndOpacity ( FSlateColor : : UseForeground ( ) )
]
]
+ SHorizontalBox : : Slot ( )
. FillWidth ( 1.0f )
. VAlign ( VAlign_Center )
. Padding ( 0.0f , 2.0f )
[
InlineTextBlock . ToSharedRef ( )
]
2020-07-28 09:25:56 -04:00
] ;
}
private :
TWeakPtr < FActorFolderTreeItem > TreeItemPtr ;
2022-03-08 09:40:27 -05:00
bool IsInActorEditorContext ( ) const
{
auto Folder = TreeItemPtr . Pin ( ) ;
const bool bIsCurrentFolder = ( Folder . IsValid ( ) & & Folder - > World . IsValid ( ) ) ? ( FActorFolders : : Get ( ) . GetActorEditorContextFolder ( * Folder - > World ) = = Folder - > GetFolder ( ) ) : false ;
return bIsCurrentFolder ;
}
2020-07-28 09:25:56 -04:00
FText GetDisplayText ( ) const
{
auto Folder = TreeItemPtr . Pin ( ) ;
2022-05-02 11:00:12 -04:00
if ( Folder . IsValid ( ) )
{
if ( ! bInEditingMode )
{
FText IsCurrentSuffixText = IsInActorEditorContext ( ) ? FText ( LOCTEXT ( " IsCurrentSuffix " , " (Current) " ) ) : FText : : GetEmpty ( ) ;
return FText : : Format ( LOCTEXT ( " LevelInstanceDisplay " , " {0}{1} " ) , FText : : FromString ( Folder - > GetDisplayString ( ) ) , IsCurrentSuffixText ) ;
}
return FText : : FromString ( Folder - > GetDisplayString ( ) ) ;
}
return FText ( ) ;
}
FText GetTooltipText ( ) const
{
if ( const FSceneOutlinerTreeItemPtr TreeItem = TreeItemPtr . Pin ( ) )
{
FText Description = IsInActorEditorContext ( ) ? LOCTEXT ( " ActorFolderIsCurrentDescription " , " This Folder is set as Current Folder. New actors will be added to this Folder. " ) : FText : : GetEmpty ( ) ;
return FText : : Format ( LOCTEXT ( " DataLayerTooltipText " , " {0} \n {1} " ) , FText : : FromString ( TreeItem - > GetDisplayString ( ) ) , Description ) ;
}
return FText ( ) ;
2020-07-28 09:25:56 -04:00
}
const FSlateBrush * GetIcon ( ) const
{
auto TreeItem = TreeItemPtr . Pin ( ) ;
if ( ! TreeItem . IsValid ( ) )
2020-07-22 09:35:57 -04:00
{
2022-05-09 13:12:28 -04:00
return FAppStyle : : Get ( ) . GetBrush ( TEXT ( " SceneOutliner.FolderClosed " ) ) ;
2020-07-22 09:35:57 -04:00
}
2020-07-28 09:25:56 -04:00
if ( TreeItem - > Flags . bIsExpanded & & TreeItem - > GetChildren ( ) . Num ( ) )
2020-07-22 09:35:57 -04:00
{
2022-05-09 13:12:28 -04:00
return FAppStyle : : Get ( ) . GetBrush ( TEXT ( " SceneOutliner.FolderOpen " ) ) ;
2020-07-28 09:25:56 -04:00
}
else
{
2022-05-09 13:12:28 -04:00
return FAppStyle : : Get ( ) . GetBrush ( TEXT ( " SceneOutliner.FolderClosed " ) ) ;
2020-07-28 09:25:56 -04:00
}
}
2020-07-22 09:35:57 -04:00
2020-07-28 09:25:56 -04:00
FSlateColor GetForegroundColor ( ) const
{
if ( auto BaseColor = FSceneOutlinerCommonLabelData : : GetForegroundColor ( * TreeItemPtr . Pin ( ) ) )
{
return BaseColor . GetValue ( ) ;
2020-07-22 09:35:57 -04:00
}
2022-05-02 11:00:12 -04:00
if ( IsInActorEditorContext ( ) )
{
return FAppStyle : : Get ( ) . GetSlateColor ( " Colors.AccentGreen " ) ;
}
2020-07-28 09:25:56 -04:00
return FSlateColor : : UseForeground ( ) ;
}
2020-07-22 09:35:57 -04:00
2020-07-28 09:25:56 -04:00
bool OnVerifyItemLabelChanged ( const FText & InLabel , FText & OutErrorMessage )
{
auto TreeItem = TreeItemPtr . Pin ( ) ;
if ( ! TreeItem . IsValid ( ) )
{
OutErrorMessage = LOCTEXT ( " RenameFailed_TreeItemDeleted " , " Tree item no longer exists " ) ;
return false ;
2020-07-22 09:35:57 -04:00
}
2020-07-28 09:25:56 -04:00
const FText TrimmedLabel = FText : : TrimPrecedingAndTrailing ( InLabel ) ;
if ( TrimmedLabel . IsEmpty ( ) )
2020-07-22 09:35:57 -04:00
{
2020-07-28 09:25:56 -04:00
OutErrorMessage = LOCTEXT ( " RenameFailed_LeftBlank " , " Names cannot be left blank " ) ;
return false ;
}
2020-07-22 09:35:57 -04:00
2020-07-28 09:25:56 -04:00
if ( TrimmedLabel . ToString ( ) . Len ( ) > = NAME_SIZE )
{
FFormatNamedArguments Arguments ;
Arguments . Add ( TEXT ( " CharCount " ) , NAME_SIZE ) ;
OutErrorMessage = FText : : Format ( LOCTEXT ( " RenameFailed_TooLong " , " Names must be less than {CharCount} characters long. " ) , Arguments ) ;
return false ;
}
2020-07-22 09:35:57 -04:00
2020-07-28 09:25:56 -04:00
const FString LabelString = TrimmedLabel . ToString ( ) ;
2021-11-23 14:43:39 -05:00
if ( TreeItem - > GetLeafName ( ) . ToString ( ) = = LabelString )
2020-07-28 09:25:56 -04:00
{
2020-07-22 09:35:57 -04:00
return true ;
}
2020-07-28 09:25:56 -04:00
int32 Dummy = 0 ;
if ( LabelString . FindChar ( ' / ' , Dummy ) | | LabelString . FindChar ( ' \\ ' , Dummy ) )
2020-07-22 09:35:57 -04:00
{
2020-07-28 09:25:56 -04:00
OutErrorMessage = LOCTEXT ( " RenameFailed_InvalidChar " , " Folder names cannot contain / or \\ . " ) ;
return false ;
}
// Validate that this folder doesn't exist already
2021-11-23 14:43:39 -05:00
FFolder Folder = TreeItem - > GetFolder ( ) ;
FName NewPath = Folder . GetParent ( ) . GetPath ( ) ;
2020-07-28 09:25:56 -04:00
if ( NewPath . IsNone ( ) )
{
NewPath = FName ( * LabelString ) ;
}
else
{
NewPath = FName ( * ( NewPath . ToString ( ) / LabelString ) ) ;
}
2022-05-24 06:58:06 -04:00
FFolder NewFolder ( Folder . GetRootObject ( ) , NewPath ) ;
2020-07-28 09:25:56 -04:00
2022-05-24 06:58:06 -04:00
if ( FActorFolders : : Get ( ) . ContainsFolder ( * TreeItem - > World . Get ( ) , NewFolder ) )
2020-07-28 09:25:56 -04:00
{
OutErrorMessage = LOCTEXT ( " RenameFailed_AlreadyExists " , " A folder with this name already exists at this level " ) ;
return false ;
}
return true ;
}
void OnLabelCommitted ( const FText & InLabel , ETextCommit : : Type InCommitInfo )
{
auto TreeItem = TreeItemPtr . Pin ( ) ;
2021-11-23 14:43:39 -05:00
if ( TreeItem . IsValid ( ) & & ! InLabel . ToString ( ) . Equals ( TreeItem - > GetLeafName ( ) . ToString ( ) , ESearchCase : : CaseSensitive ) )
2020-07-28 09:25:56 -04:00
{
// Rename the item
2021-11-23 14:43:39 -05:00
FFolder Folder = TreeItem - > GetFolder ( ) ;
FName NewPath = Folder . GetParent ( ) . GetPath ( ) ;
2020-07-28 09:25:56 -04:00
if ( NewPath . IsNone ( ) )
2020-07-22 09:35:57 -04:00
{
2020-07-28 09:25:56 -04:00
NewPath = FName ( * InLabel . ToString ( ) ) ;
}
else
{
NewPath = FName ( * ( NewPath . ToString ( ) / InLabel . ToString ( ) ) ) ;
}
2022-05-24 06:58:06 -04:00
FFolder TreeItemNewFolder ( Folder . GetRootObject ( ) , NewPath ) ;
2020-07-22 09:35:57 -04:00
2021-11-23 14:43:39 -05:00
FActorFolders : : Get ( ) . RenameFolderInWorld ( * TreeItem - > World . Get ( ) , Folder , TreeItemNewFolder ) ;
2020-07-22 09:35:57 -04:00
2020-07-28 09:25:56 -04:00
auto Outliner = WeakSceneOutliner . Pin ( ) ;
if ( Outliner . IsValid ( ) )
{
Outliner - > SetKeyboardFocus ( ) ;
2020-07-22 09:35:57 -04:00
}
}
2020-07-28 09:25:56 -04:00
}
2022-05-02 11:00:12 -04:00
void OnEnterEditingMode ( )
{
bInEditingMode = true ;
}
void OnExitEditingMode ( )
{
bInEditingMode = false ;
}
bool bInEditingMode = false ;
2020-07-28 09:25:56 -04:00
} ;
2020-07-22 09:35:57 -04:00
2021-11-23 14:43:39 -05:00
FActorFolderTreeItem : : FActorFolderTreeItem ( const FFolder & InFolder , const TWeakObjectPtr < UWorld > & InWorld )
: FFolderTreeItem ( InFolder , Type )
2020-07-28 09:25:56 -04:00
, World ( InWorld )
{
2022-05-24 06:58:06 -04:00
FFolderTreeItem : : SetPath ( InFolder . GetPath ( ) ) ;
// Initialize ActorFolder
ActorFolder = InFolder . GetActorFolder ( ) ;
2020-07-28 09:25:56 -04:00
}
void FActorFolderTreeItem : : OnExpansionChanged ( )
{
if ( ! World . IsValid ( ) )
2020-07-22 09:35:57 -04:00
{
2020-07-28 09:25:56 -04:00
return ;
2020-07-22 09:35:57 -04:00
}
2020-07-28 09:25:56 -04:00
// Update the central store of folder properties with this folder's new expansion state
2021-11-23 14:43:39 -05:00
FActorFolders : : Get ( ) . SetIsFolderExpanded ( * World , GetFolder ( ) , Flags . bIsExpanded ) ;
2020-07-22 09:35:57 -04:00
}
2021-11-23 14:43:39 -05:00
void FActorFolderTreeItem : : Delete ( const FFolder & InNewParentFolder )
2020-07-28 09:25:56 -04:00
{
if ( ! World . IsValid ( ) )
{
return ;
}
const FScopedTransaction Transaction ( LOCTEXT ( " DeleteFolderTransaction " , " Delete Folder " ) ) ;
2021-11-23 14:43:39 -05:00
const FFolder : : FRootObject & NewParentRootObject = InNewParentFolder . GetRootObject ( ) ;
2020-07-28 09:25:56 -04:00
for ( const TWeakPtr < ISceneOutlinerTreeItem > & ChildPtr : GetChildren ( ) )
{
FSceneOutlinerTreeItemPtr Child = ChildPtr . Pin ( ) ;
if ( const FActorTreeItem * ActorItem = Child - > CastTo < FActorTreeItem > ( ) )
{
if ( AActor * Actor = ActorItem - > Actor . Get ( ) )
{
2021-11-23 14:43:39 -05:00
check ( Actor - > GetFolderRootObject ( ) = = NewParentRootObject ) ;
2022-01-07 10:01:19 -05:00
// When using actor folders, no need to update actors since the folder path resolving is dynamic
if ( ! Actor - > GetLevel ( ) - > IsUsingActorFolders ( ) )
{
Actor - > SetFolderPath_Recursively ( InNewParentFolder . GetPath ( ) ) ;
}
2020-07-28 09:25:56 -04:00
}
}
2022-01-07 10:01:19 -05:00
else if ( FActorFolderTreeItem * FolderItem = Child - > CastTo < FActorFolderTreeItem > ( ) )
2020-07-28 09:25:56 -04:00
{
2022-01-07 10:01:19 -05:00
// When using actor folders, no need to update child folders since the folder path resolving is dynamic
if ( ! FolderItem - > GetActorFolder ( ) )
{
FolderItem - > MoveTo ( InNewParentFolder ) ;
}
2020-07-28 09:25:56 -04:00
}
}
2021-11-23 14:43:39 -05:00
FActorFolders : : Get ( ) . DeleteFolder ( * World , GetFolder ( ) ) ;
2020-07-28 09:25:56 -04:00
}
2021-11-23 14:43:39 -05:00
void FActorFolderTreeItem : : MoveTo ( const FFolder & InNewParentFolder )
2020-07-28 09:25:56 -04:00
{
if ( World . IsValid ( ) )
{
2021-11-23 14:43:39 -05:00
check ( InNewParentFolder . GetRootObject ( ) = = GetRootObject ( ) ) ;
2020-07-28 09:25:56 -04:00
// Get unique name
2021-11-23 14:43:39 -05:00
const FFolder NewPath = FActorFolders : : Get ( ) . GetFolderName ( * World , InNewParentFolder , GetLeafName ( ) ) ;
FActorFolders : : Get ( ) . RenameFolderInWorld ( * World , GetFolder ( ) , NewPath ) ;
2020-07-28 09:25:56 -04:00
}
}
2022-01-07 10:01:19 -05:00
void FActorFolderTreeItem : : SetPath ( const FName & InNewPath )
{
FFolderTreeItem : : SetPath ( InNewPath ) ;
2022-05-24 06:58:06 -04:00
// Validate that if the item's ActorFolder object is set, that changing the path keeps the same ActorFolder but with a different path
check ( ! ActorFolder . IsValid ( ) | | ( ActorFolder - > GetFolder ( ) . GetPath ( ) = = Path ) ) ;
2022-01-07 10:01:19 -05:00
}
2020-07-28 09:25:56 -04:00
void FActorFolderTreeItem : : CreateSubFolder ( TWeakPtr < SSceneOutliner > WeakOutliner )
{
auto Outliner = WeakOutliner . Pin ( ) ;
if ( Outliner . IsValid ( ) & & World . IsValid ( ) )
{
const FScopedTransaction Transaction ( LOCTEXT ( " UndoAction_CreateFolder " , " Create Folder " ) ) ;
2021-11-23 14:43:39 -05:00
const FFolder NewFolderName = FActorFolders : : Get ( ) . GetDefaultFolderName ( * World , GetFolder ( ) ) ;
2020-07-28 09:25:56 -04:00
FActorFolders : : Get ( ) . CreateFolder ( * World , NewFolderName ) ;
// At this point the new folder will be in our newly added list, so select it and open a rename when it gets refreshed
Outliner - > OnItemAdded ( NewFolderName , SceneOutliner : : ENewItemAction : : Select | SceneOutliner : : ENewItemAction : : Rename ) ;
}
}
TSharedRef < SWidget > FActorFolderTreeItem : : GenerateLabelWidget ( ISceneOutliner & Outliner , const STableRow < FSceneOutlinerTreeItemPtr > & InRow )
{
return SNew ( SActorFolderTreeLabel , * this , Outliner , InRow ) ;
}
2021-12-16 19:26:29 -05:00
bool FActorFolderTreeItem : : ShouldShowPinnedState ( ) const
{
2022-05-24 06:58:06 -04:00
if ( World . IsValid ( ) & & ! World - > IsGameWorld ( ) & & World - > IsPartitionedWorld ( ) )
{
return FFolder : : IsRootObjectPersistentLevel ( GetRootObject ( ) ) ;
}
return false ;
}
FFolder FActorFolderTreeItem : : GetFolder ( ) const
{
// Use Folder resolved by ActorFolder when valid (a null owning world would will fail resolving, see UActorFolder::GetFolder())
FFolder Folder = ActorFolder . IsValid ( ) ? ActorFolder - > GetFolder ( ) : FFolder : : GetInvalidFolder ( ) ;
return Folder . IsValid ( ) ? Folder : FFolderTreeItem : : GetFolder ( ) ;
2021-12-16 19:26:29 -05:00
}
2022-01-07 10:01:19 -05:00
bool FActorFolderTreeItem : : CanInteract ( ) const
{
if ( ! FFolderTreeItem : : CanInteract ( ) )
{
return false ;
}
if ( ULevelInstanceSubsystem * LevelInstanceSubsystem = World . IsValid ( ) ? World - > GetSubsystem < ULevelInstanceSubsystem > ( ) : nullptr )
{
2022-03-29 09:19:22 -04:00
if ( ILevelInstanceInterface * EditingLevelInstance = LevelInstanceSubsystem - > GetEditingLevelInstance ( ) )
2022-01-07 10:01:19 -05:00
{
2022-03-29 09:19:22 -04:00
if ( GetRootObject ( ) ! = FFolder : : FRootObject ( CastChecked < AActor > ( EditingLevelInstance ) ) )
2022-01-07 10:01:19 -05:00
{
return false ;
}
}
}
ULevel * Level = ActorFolder . IsValid ( ) ? ActorFolder - > GetOuterULevel ( ) : nullptr ;
return Level ? ! FLevelUtils : : IsLevelLocked ( Level ) : true ;
}
2021-03-31 11:34:53 -04:00
# undef LOCTEXT_NAMESPACE