2014-12-07 19:09:38 -05:00
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
# include "SceneOutlinerPrivatePCH.h"
# include "SSceneOutliner.h"
# include "ScopedTransaction.h"
# include "ClassIconFinder.h"
# include "SSocketChooser.h"
# include "SceneOutlinerFilters.h"
2014-11-10 10:09:21 -05:00
# include "SceneOutlinerDragDrop.h"
# include "SortHelper.h"
# include "ActorEditorUtils.h"
# include "LevelUtils.h"
2014-03-14 14:13:41 -04:00
2014-04-02 18:09:23 -04:00
# include "EditorActorFolders.h"
2014-10-14 22:50:06 -04:00
# include "SSearchBox.h"
# include "SInlineEditableTextBlock.h"
# include "SNotificationList.h"
# include "NotificationManager.h"
2014-11-12 04:43:54 -05:00
# include "Engine/Selection.h"
# include "EngineUtils.h"
2014-04-02 18:09:23 -04:00
2014-03-14 14:13:41 -04:00
DEFINE_LOG_CATEGORY_STATIC ( LogSceneOutliner , Log , All ) ;
# define LOCTEXT_NAMESPACE "SSceneOutliner"
2014-11-10 10:09:21 -05:00
// The amount of time that must pass before the Scene Outliner will attempt a sort when in PIE/SIE.
2014-03-14 14:13:41 -04:00
# define SCENE_OUTLINER_RESORT_TIMER 1.0f
namespace SceneOutliner
{
2014-11-10 10:09:21 -05:00
TSharedPtr < FOutlinerFilter > CreateSelectedActorFilter ( )
2014-04-23 17:50:18 -04:00
{
2014-11-13 09:58:00 -05:00
auto * Filter = new FOutlinerPredicateFilter ( FActorFilterPredicate : : CreateStatic ( [ ] ( const AActor * InActor ) { return InActor - > IsSelected ( ) ; } ) , EDefaultFilterBehaviour : : Fail ) ;
// If anything fails this filter, make it non interactive. We don't want to allow selection of implicitly included parents which might nuke the actor selection.
Filter - > FailedItemState = EFailedFilterState : : NonInteractive ;
return MakeShareable ( Filter ) ;
2014-11-10 10:09:21 -05:00
}
TSharedPtr < FOutlinerFilter > CreateHideTemporaryActorsFilter ( )
{
return MakeShareable ( new FOutlinerPredicateFilter ( FActorFilterPredicate : : CreateStatic ( [ ] ( const AActor * InActor ) {
return InActor - > GetWorld ( ) - > WorldType ! = EWorldType : : PIE | | GEditor - > ObjectsThatExistInEditorWorld . Get ( InActor ) ;
} ) , EDefaultFilterBehaviour : : Pass ) ) ;
}
TSharedPtr < FOutlinerFilter > CreateIsInCurrentLevelFilter ( )
{
2014-11-13 09:58:00 -05:00
struct FOnlyCurrentLevelFilter : FOutlinerFilter
{
FOnlyCurrentLevelFilter ( ) : FOutlinerFilter ( EDefaultFilterBehaviour : : Fail , EFailedFilterState : : Interactive ) { }
virtual bool PassesFilter ( const AActor * InActor ) const override
{
return InActor - > GetLevel ( ) = = InActor - > GetWorld ( ) - > GetCurrentLevel ( ) ;
}
} ;
return MakeShareable ( new FOnlyCurrentLevelFilter ( ) ) ;
2014-11-10 10:09:21 -05:00
}
struct FItemSelection : IMutableTreeItemVisitor
{
mutable TArray < FActorTreeItem * > Actors ;
mutable TArray < FWorldTreeItem * > Worlds ;
mutable TArray < FFolderTreeItem * > Folders ;
FItemSelection ( )
{ }
FItemSelection ( SOutlinerTreeView & Tree )
2014-04-23 17:50:18 -04:00
{
2014-11-10 10:09:21 -05:00
for ( auto & Item : Tree . GetSelectedItems ( ) )
2014-04-23 17:50:18 -04:00
{
2014-11-10 10:09:21 -05:00
Item - > Visit ( * this ) ;
2014-04-23 17:50:18 -04:00
}
}
2014-11-10 10:09:21 -05:00
TArray < TWeakObjectPtr < AActor > > GetWeakActors ( ) const
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
TArray < TWeakObjectPtr < AActor > > ActorArray ;
for ( const auto * ActorItem : Actors )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
if ( ActorItem - > Actor . IsValid ( ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
ActorArray . Add ( ActorItem - > Actor ) ;
2014-03-14 14:13:41 -04:00
}
}
2014-11-10 10:09:21 -05:00
return ActorArray ;
}
TArray < AActor * > GetActorPtrs ( ) const
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
TArray < AActor * > ActorArray ;
for ( const auto * ActorItem : Actors )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
if ( AActor * Actor = ActorItem - > Actor . Get ( ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
ActorArray . Add ( Actor ) ;
2014-03-14 14:13:41 -04:00
}
}
2014-11-10 10:09:21 -05:00
return ActorArray ;
}
2014-03-14 14:13:41 -04:00
2014-11-10 10:09:21 -05:00
private :
virtual void Visit ( FActorTreeItem & ActorItem ) const override
{
Actors . Add ( & ActorItem ) ;
}
virtual void Visit ( FWorldTreeItem & WorldItem ) const override
{
Worlds . Add ( & WorldItem ) ;
}
virtual void Visit ( FFolderTreeItem & FolderItem ) const override
{
Folders . Add ( & FolderItem ) ;
}
} ;
void SSceneOutliner : : Construct ( const FArguments & InArgs , const FInitializationOptions & InInitOptions )
2014-04-23 16:39:22 -04:00
{
2014-11-10 10:09:21 -05:00
// Copy over the shared data from the initialization options
static_cast < FSharedDataBase & > ( * SharedData ) = static_cast < const FSharedDataBase & > ( InInitOptions ) ;
2014-03-14 14:13:41 -04:00
2014-04-02 18:09:23 -04:00
OnItemPicked = InArgs . _OnItemPickedDelegate ;
2014-03-14 14:13:41 -04:00
2014-03-15 01:14:25 -04:00
if ( InInitOptions . OnSelectionChanged . IsBound ( ) )
2014-03-14 14:13:41 -04:00
{
2014-03-15 01:14:25 -04:00
SelectionChanged . Add ( InInitOptions . OnSelectionChanged ) ;
2014-03-14 14:13:41 -04:00
}
bFullRefresh = true ;
bNeedsRefresh = true ;
bIsReentrant = false ;
2014-11-10 10:09:21 -05:00
bSortDirty = true ;
2014-11-13 09:58:00 -05:00
bActorSelectionDirty = SharedData - > Mode = = ESceneOutlinerMode : : ActorBrowsing ;
2014-03-14 14:13:41 -04:00
FilteredActorCount = 0 ;
SortOutlinerTimer = 0.0f ;
2014-03-15 01:14:25 -04:00
bPendingFocusNextFrame = InInitOptions . bFocusSearchBoxWhenOpened ;
2014-03-14 14:13:41 -04:00
2014-11-10 10:09:21 -05:00
SortByColumn = FBuiltInColumnTypes : : Label ( ) ;
2014-03-14 14:13:41 -04:00
SortMode = EColumnSortMode : : Ascending ;
// @todo outliner: Should probably save this in layout!
// @todo outliner: Should save spacing for list view in layout
NoBorder = FEditorStyle : : GetBrush ( " LevelViewport.NoViewportBorder " ) ;
PlayInEditorBorder = FEditorStyle : : GetBrush ( " LevelViewport.StartingPlayInEditorBorder " ) ;
SimulateBorder = FEditorStyle : : GetBrush ( " LevelViewport.StartingSimulateBorder " ) ;
//Setup the SearchBox filter
{
auto Delegate = TreeItemTextFilter : : FItemToStringArray : : CreateSP ( this , & SSceneOutliner : : PopulateSearchStrings ) ;
SearchBoxFilter = MakeShareable ( new TreeItemTextFilter ( Delegate ) ) ;
}
TSharedRef < SVerticalBox > VerticalBox = SNew ( SVerticalBox ) ;
2014-11-10 10:09:21 -05:00
// We use the filter collection provided, otherwise we create our own
2014-04-29 05:59:53 -04:00
Filters = InInitOptions . Filters . IsValid ( ) ? InInitOptions . Filters : MakeShareable ( new FOutlinerFilters ) ;
2014-03-14 14:13:41 -04:00
2014-04-02 18:09:23 -04:00
SearchBoxFilter - > OnChanged ( ) . AddSP ( this , & SSceneOutliner : : FullRefresh ) ;
2014-04-29 05:59:53 -04:00
Filters - > OnChanged ( ) . AddSP ( this , & SSceneOutliner : : FullRefresh ) ;
2014-03-14 14:13:41 -04:00
//Apply custom filters based on global preferences
2014-11-10 10:09:21 -05:00
if ( SharedData - > Mode = = ESceneOutlinerMode : : ActorBrowsing )
2014-03-14 14:13:41 -04:00
{
ApplyShowOnlySelectedFilter ( IsShowingOnlySelected ( ) ) ;
ApplyHideTemporaryActorsFilter ( IsHidingTemporaryActors ( ) ) ;
2014-04-23 18:36:28 -04:00
ApplyShowOnlyCurrentLevelFilter ( IsShowingOnlyCurrentLevel ( ) ) ;
2014-03-14 14:13:41 -04:00
}
TSharedRef < SHeaderRow > HeaderRowWidget =
SNew ( SHeaderRow )
// Only show the list header if the user configured the outliner for that
2014-03-15 01:14:25 -04:00
. Visibility ( InInitOptions . bShowHeaderRow ? EVisibility : : Visible : EVisibility : : Collapsed ) ;
2014-03-14 14:13:41 -04:00
2014-11-10 10:09:21 -05:00
SetupColumns ( * HeaderRowWidget ) ;
2014-03-14 14:13:41 -04:00
ChildSlot
[
SNew ( SBorder )
. BorderImage ( this , & SSceneOutliner : : OnGetBorderBrush )
. BorderBackgroundColor ( this , & SSceneOutliner : : OnGetBorderColorAndOpacity )
. ShowEffectWhenDisabled ( false )
[
VerticalBox
]
] ;
auto Toolbar = SNew ( SHorizontalBox ) ;
Toolbar - > AddSlot ( )
. VAlign ( VAlign_Center )
[
SAssignNew ( FilterTextBoxWidget , SSearchBox )
2014-11-10 10:09:21 -05:00
. Visibility ( InInitOptions . bShowSearchBox ? EVisibility : : Visible : EVisibility : : Collapsed )
2014-04-02 18:09:23 -04:00
. HintText ( LOCTEXT ( " FilterSearch " , " Search... " ) )
2015-01-08 11:35:01 -05:00
. ToolTipText ( LOCTEXT ( " FilterSearchHint " , " Type here to search (pressing enter selects the results) " ) )
2014-03-14 14:13:41 -04:00
. OnTextChanged ( this , & SSceneOutliner : : OnFilterTextChanged )
. OnTextCommitted ( this , & SSceneOutliner : : OnFilterTextCommitted )
] ;
2014-11-19 03:23:56 -05:00
if ( SharedData - > Mode = = ESceneOutlinerMode : : ActorBrowsing & & InInitOptions . bShowCreateNewFolder )
2014-03-14 14:13:41 -04:00
{
Toolbar - > AddSlot ( )
. VAlign ( VAlign_Center )
. AutoWidth ( )
. Padding ( 4.f , 0.f , 0.f , 0.f )
[
SNew ( SButton )
. ButtonStyle ( FEditorStyle : : Get ( ) , " HoverHintOnly " )
2014-04-02 18:09:23 -04:00
. ToolTipText ( LOCTEXT ( " CreateFolderToolTip " , " Create a new folder containing the current actor selection " ) )
2014-03-14 14:13:41 -04:00
. OnClicked ( this , & SSceneOutliner : : OnCreateFolderClicked )
[
SNew ( SImage )
. Image ( FEditorStyle : : GetBrush ( " SceneOutliner.NewFolderIcon " ) )
]
] ;
}
VerticalBox - > AddSlot ( )
. AutoHeight ( )
. Padding ( 0.0f , 0.0f , 0.0f , 4.0f )
[
Toolbar
] ;
VerticalBox - > AddSlot ( )
. FillHeight ( 1.0f )
[
SNew ( SOverlay )
+ SOverlay : : Slot ( )
. HAlign ( HAlign_Center )
[
SNew ( STextBlock )
. Visibility ( this , & SSceneOutliner : : GetEmptyLabelVisibility )
2014-04-23 18:06:41 -04:00
. Text ( LOCTEXT ( " EmptyLabel " , " Empty " ) )
2014-03-14 14:13:41 -04:00
. ColorAndOpacity ( FLinearColor ( 0.4f , 1.0f , 0.4f ) )
]
+ SOverlay : : Slot ( )
[
SAssignNew ( OutlinerTreeView , SOutlinerTreeView , StaticCastSharedRef < SSceneOutliner > ( AsShared ( ) ) )
// multi-select if we're in browsing mode,
// single-select if we're in picking mode,
. SelectionMode ( this , & SSceneOutliner : : GetSelectionMode )
// Point the tree to our array of root-level items. Whenever this changes, we'll call RequestTreeRefresh()
. TreeItemsSource ( & RootTreeItems )
// Find out when the user selects something in the tree
. OnSelectionChanged ( this , & SSceneOutliner : : OnOutlinerTreeSelectionChanged )
// Called when the user double-clicks with LMB on an item in the list
. OnMouseButtonDoubleClick ( this , & SSceneOutliner : : OnOutlinerTreeDoubleClick )
// Called when an item is scrolled into view
. OnItemScrolledIntoView ( this , & SSceneOutliner : : OnOutlinerTreeItemScrolledIntoView )
2014-05-21 09:35:52 -04:00
// Called when an item is expanded or collapsed
. OnExpansionChanged ( this , & SSceneOutliner : : OnItemExpansionChanged )
2014-03-14 14:13:41 -04:00
// Called to child items for any given parent item
. OnGetChildren ( this , & SSceneOutliner : : OnGetChildrenForOutlinerTree )
// Generates the actual widget for a tree item
. OnGenerateRow ( this , & SSceneOutliner : : OnGenerateRowForOutlinerTree )
// Use the level viewport context menu as the right click menu for tree items
. OnContextMenuOpening ( this , & SSceneOutliner : : OnOpenContextMenu )
// Header for the tree
. HeaderRow ( HeaderRowWidget )
]
] ;
2014-11-10 10:09:21 -05:00
if ( SharedData - > Mode = = ESceneOutlinerMode : : ActorBrowsing )
2014-03-14 14:13:41 -04:00
{
// Separator
VerticalBox - > AddSlot ( )
. AutoHeight ( )
. Padding ( 0 , 0 , 0 , 1 )
[
SNew ( SSeparator )
] ;
// Bottom panel
VerticalBox - > AddSlot ( )
. AutoHeight ( )
[
SNew ( SHorizontalBox )
// Asset count
+ SHorizontalBox : : Slot ( )
. FillWidth ( 1.f )
. VAlign ( VAlign_Center )
. Padding ( 8 , 0 )
[
SNew ( STextBlock )
. Text ( this , & SSceneOutliner : : GetFilterStatusText )
. ColorAndOpacity ( this , & SSceneOutliner : : GetFilterStatusTextColor )
]
// View mode combo button
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
[
SAssignNew ( ViewOptionsComboButton , SComboButton )
. ContentPadding ( 0 )
. ForegroundColor ( this , & SSceneOutliner : : GetViewButtonForegroundColor )
. ButtonStyle ( FEditorStyle : : Get ( ) , " ToggleButton " ) // Use the tool bar item style for this button
. OnGetMenuContent ( this , & SSceneOutliner : : GetViewButtonContent )
. ButtonContent ( )
[
SNew ( SHorizontalBox )
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
. VAlign ( VAlign_Center )
[
SNew ( SImage ) . Image ( FEditorStyle : : GetBrush ( " GenericViewButton " ) )
]
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
. Padding ( 2 , 0 , 0 , 0 )
. VAlign ( VAlign_Center )
[
2014-04-23 18:06:41 -04:00
SNew ( STextBlock ) . Text ( LOCTEXT ( " ViewButton " , " View Options " ) )
2014-03-14 14:13:41 -04:00
]
]
]
] ;
2014-11-10 10:09:21 -05:00
} //end if (SharedData->Mode == ESceneOutlinerMode::ActorBrowsing)
2014-03-14 14:13:41 -04:00
// Don't allow tool-tips over the header
HeaderRowWidget - > EnableToolTipForceField ( true ) ;
// Populate our data set
Populate ( ) ;
// We only synchronize selection when in actor browsing mode
2014-11-10 10:09:21 -05:00
if ( SharedData - > Mode = = ESceneOutlinerMode : : ActorBrowsing )
2014-03-14 14:13:41 -04:00
{
// Populate and register to find out when the level's selection changes
OnLevelSelectionChanged ( NULL ) ;
USelection : : SelectionChangedEvent . AddRaw ( this , & SSceneOutliner : : OnLevelSelectionChanged ) ;
USelection : : SelectObjectEvent . AddRaw ( this , & SSceneOutliner : : OnLevelSelectionChanged ) ;
}
// Register to find out when actors are added or removed
// @todo outliner: Might not catch some cases (see: CALLBACK_ActorPropertiesChange, CALLBACK_LayerChange, CALLBACK_LevelDirtied, CALLBACK_OnActorMoved, CALLBACK_UpdateLevelsForAllActors)
FEditorDelegates : : MapChange . AddSP ( this , & SSceneOutliner : : OnMapChange ) ;
2014-08-11 04:43:59 -04:00
FEditorDelegates : : NewCurrentLevel . AddSP ( this , & SSceneOutliner : : OnNewCurrentLevel ) ;
2014-04-02 18:09:23 -04:00
GEngine - > OnLevelActorListChanged ( ) . AddSP ( this , & SSceneOutliner : : FullRefresh ) ;
2014-03-14 14:13:41 -04:00
FWorldDelegates : : LevelAddedToWorld . AddSP ( this , & SSceneOutliner : : OnLevelAdded ) ;
FWorldDelegates : : LevelRemovedFromWorld . AddSP ( this , & SSceneOutliner : : OnLevelRemoved ) ;
GEngine - > OnLevelActorAdded ( ) . AddSP ( this , & SSceneOutliner : : OnLevelActorsAdded ) ;
GEngine - > OnLevelActorDetached ( ) . AddSP ( this , & SSceneOutliner : : OnLevelActorsDetached ) ;
GEngine - > OnLevelActorFolderChanged ( ) . AddSP ( this , & SSceneOutliner : : OnLevelActorFolderChanged ) ;
GEngine - > OnLevelActorDeleted ( ) . AddSP ( this , & SSceneOutliner : : OnLevelActorsRemoved ) ;
GEngine - > OnLevelActorAttached ( ) . AddSP ( this , & SSceneOutliner : : OnLevelActorsAttached ) ;
GEngine - > OnLevelActorRequestRename ( ) . AddSP ( this , & SSceneOutliner : : OnLevelActorsRequestRename ) ;
// Register to update when an undo/redo operation has been called to update our list of actors
GEditor - > RegisterForUndo ( this ) ;
// Register to be notified when properties are edited
2014-05-09 14:02:33 -04:00
FCoreDelegates : : OnActorLabelChanged . AddRaw ( this , & SSceneOutliner : : OnActorLabelChanged ) ;
2014-03-14 14:13:41 -04:00
2014-04-02 18:09:23 -04:00
auto & Folders = FActorFolders : : Get ( ) ;
Folders . OnFolderCreate . AddSP ( this , & SSceneOutliner : : OnBroadcastFolderCreate ) ;
2014-11-10 10:09:21 -05:00
Folders . OnFolderMove . AddSP ( this , & SSceneOutliner : : OnBroadcastFolderMove ) ;
2014-04-02 18:09:23 -04:00
Folders . OnFolderDelete . AddSP ( this , & SSceneOutliner : : OnBroadcastFolderDelete ) ;
2014-03-14 14:13:41 -04:00
}
2014-11-10 10:09:21 -05:00
void SSceneOutliner : : SetupColumns ( SHeaderRow & HeaderRow )
{
FSceneOutlinerModule & SceneOutlinerModule = FModuleManager : : LoadModuleChecked < FSceneOutlinerModule > ( " SceneOutliner " ) ;
if ( SharedData - > ColumnMap . Num ( ) = = 0 )
{
SharedData - > UseDefaultColumns ( ) ;
}
Columns . Empty ( SharedData - > ColumnMap . Num ( ) ) ;
HeaderRow . ClearColumns ( ) ;
// Get a list of sorted columns IDs to create
TArray < FName > SortedIDs ;
SortedIDs . Reserve ( SharedData - > ColumnMap . Num ( ) ) ;
SharedData - > ColumnMap . GenerateKeyArray ( SortedIDs ) ;
SortedIDs . Sort ( [ & ] ( const FName & A , const FName & B ) {
return SharedData - > ColumnMap [ A ] . PriorityIndex < SharedData - > ColumnMap [ B ] . PriorityIndex ;
} ) ;
for ( const FName & ID : SortedIDs )
{
if ( SharedData - > ColumnMap [ ID ] . Visibility = = EColumnVisibility : : Invisible )
{
continue ;
}
TSharedPtr < ISceneOutlinerColumn > Column ;
if ( SharedData - > ColumnMap [ ID ] . Factory . IsBound ( ) )
{
Column = SharedData - > ColumnMap [ ID ] . Factory . Execute ( * this ) ;
}
else
{
Column = SceneOutlinerModule . FactoryColumn ( ID , * this ) ;
}
if ( ensure ( Column . IsValid ( ) ) )
{
check ( Column - > GetColumnID ( ) = = ID ) ;
Columns . Add ( Column - > GetColumnID ( ) , Column ) ;
auto ColumnArgs = Column - > ConstructHeaderRowColumn ( ) ;
if ( Column - > SupportsSorting ( ) )
{
ColumnArgs
. SortMode ( this , & SSceneOutliner : : GetColumnSortMode , Column - > GetColumnID ( ) )
. OnSort ( this , & SSceneOutliner : : OnColumnSortModeChanged ) ;
}
HeaderRow . AddColumn ( ColumnArgs ) ;
}
}
Columns . Shrink ( ) ;
}
2014-03-14 14:13:41 -04:00
SSceneOutliner : : ~ SSceneOutliner ( )
{
// We only synchronize selection when in actor browsing mode
2014-11-10 10:09:21 -05:00
if ( SharedData - > Mode = = ESceneOutlinerMode : : ActorBrowsing )
2014-03-14 14:13:41 -04:00
{
USelection : : SelectionChangedEvent . RemoveAll ( this ) ;
USelection : : SelectObjectEvent . RemoveAll ( this ) ;
}
FEditorDelegates : : MapChange . RemoveAll ( this ) ;
2014-08-11 04:43:59 -04:00
FEditorDelegates : : NewCurrentLevel . RemoveAll ( this ) ;
2014-03-14 14:13:41 -04:00
GEngine - > OnLevelActorListChanged ( ) . RemoveAll ( this ) ;
GEditor - > UnregisterForUndo ( this ) ;
SearchBoxFilter - > OnChanged ( ) . RemoveAll ( this ) ;
2014-04-29 05:59:53 -04:00
Filters - > OnChanged ( ) . RemoveAll ( this ) ;
2014-03-14 14:13:41 -04:00
FWorldDelegates : : LevelAddedToWorld . RemoveAll ( this ) ;
FWorldDelegates : : LevelRemovedFromWorld . RemoveAll ( this ) ;
2014-05-09 14:02:33 -04:00
FCoreDelegates : : OnActorLabelChanged . RemoveAll ( this ) ;
2014-03-14 14:13:41 -04:00
2014-05-30 07:57:54 -04:00
if ( FActorFolders : : IsAvailable ( ) )
{
auto & Folders = FActorFolders : : Get ( ) ;
Folders . OnFolderCreate . RemoveAll ( this ) ;
2014-11-10 10:09:21 -05:00
Folders . OnFolderMove . RemoveAll ( this ) ;
2014-05-30 07:57:54 -04:00
Folders . OnFolderDelete . RemoveAll ( this ) ;
}
2014-03-14 14:13:41 -04:00
}
2014-11-10 10:09:21 -05:00
void SSceneOutliner : : OnItemAdded ( const FTreeItemID & ItemID , uint8 ActionMask )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
NewItemActions . Add ( ItemID , ActionMask ) ;
2014-03-14 14:13:41 -04:00
}
FSlateColor SSceneOutliner : : GetViewButtonForegroundColor ( ) const
{
2014-11-18 09:57:20 -05:00
static const FName InvertedForegroundName ( " InvertedForeground " ) ;
static const FName DefaultForegroundName ( " DefaultForeground " ) ;
return ViewOptionsComboButton - > IsHovered ( ) ? FEditorStyle : : GetSlateColor ( InvertedForegroundName ) : FEditorStyle : : GetSlateColor ( DefaultForegroundName ) ;
2014-03-14 14:13:41 -04:00
}
TSharedRef < SWidget > SSceneOutliner : : GetViewButtonContent ( )
{
FMenuBuilder MenuBuilder ( true , NULL ) ;
MenuBuilder . BeginSection ( " AssetThumbnails " , LOCTEXT ( " ShowHeading " , " Show " ) ) ;
{
MenuBuilder . AddMenuEntry (
LOCTEXT ( " ToggleShowOnlySelected " , " Only Selected " ) ,
LOCTEXT ( " ToggleShowOnlySelectedToolTip " , " When enabled, only displays actors that are currently selected. " ) ,
FSlateIcon ( ) ,
FUIAction (
FExecuteAction : : CreateSP ( this , & SSceneOutliner : : ToggleShowOnlySelected ) ,
FCanExecuteAction ( ) ,
FIsActionChecked : : CreateSP ( this , & SSceneOutliner : : IsShowingOnlySelected )
) ,
NAME_None ,
EUserInterfaceActionType : : ToggleButton
) ;
MenuBuilder . AddMenuEntry (
LOCTEXT ( " ToggleHideTemporaryActors " , " Hide Temporary Actors " ) ,
LOCTEXT ( " ToggleHideTemporaryActorsToolTip " , " When enabled, hides temporary/run-time Actors. " ) ,
FSlateIcon ( ) ,
FUIAction (
FExecuteAction : : CreateSP ( this , & SSceneOutliner : : ToggleHideTemporaryActors ) ,
FCanExecuteAction ( ) ,
FIsActionChecked : : CreateSP ( this , & SSceneOutliner : : IsHidingTemporaryActors )
) ,
NAME_None ,
EUserInterfaceActionType : : ToggleButton
) ;
2014-04-23 18:36:28 -04:00
MenuBuilder . AddMenuEntry (
LOCTEXT ( " ToggleShowOnlyCurrentLevel " , " Only in Current Level " ) ,
LOCTEXT ( " ToggleShowOnlyCurrentLevelToolTip " , " When enabled, only shows Actors that are in the Current Level. " ) ,
FSlateIcon ( ) ,
FUIAction (
FExecuteAction : : CreateSP ( this , & SSceneOutliner : : ToggleShowOnlyCurrentLevel ) ,
FCanExecuteAction ( ) ,
FIsActionChecked : : CreateSP ( this , & SSceneOutliner : : IsShowingOnlyCurrentLevel )
) ,
NAME_None ,
EUserInterfaceActionType : : ToggleButton
) ;
2014-03-14 14:13:41 -04:00
}
MenuBuilder . EndSection ( ) ;
return MenuBuilder . MakeWidget ( ) ;
}
/** FILTERS */
2014-04-23 18:36:28 -04:00
// Show Only Selected
2014-03-14 14:13:41 -04:00
void SSceneOutliner : : ToggleShowOnlySelected ( )
{
const bool bEnableFlag = ! IsShowingOnlySelected ( ) ;
2014-05-08 21:34:05 -04:00
USceneOutlinerSettings * Settings = GetMutableDefault < USceneOutlinerSettings > ( ) ;
Settings - > bShowOnlySelectedActors = bEnableFlag ;
Settings - > PostEditChange ( ) ;
2014-03-14 14:13:41 -04:00
ApplyShowOnlySelectedFilter ( bEnableFlag ) ;
}
void SSceneOutliner : : ApplyShowOnlySelectedFilter ( bool bShowOnlySelected )
{
if ( ! SelectedActorFilter . IsValid ( ) )
{
2014-11-10 10:09:21 -05:00
SelectedActorFilter = CreateSelectedActorFilter ( ) ;
2014-03-14 14:13:41 -04:00
}
if ( bShowOnlySelected )
{
2014-04-29 05:59:53 -04:00
Filters - > Add ( SelectedActorFilter ) ;
2014-03-14 14:13:41 -04:00
}
else
{
2014-04-29 05:59:53 -04:00
Filters - > Remove ( SelectedActorFilter ) ;
2014-03-14 14:13:41 -04:00
}
}
bool SSceneOutliner : : IsShowingOnlySelected ( ) const
{
2014-05-08 21:34:05 -04:00
return GetDefault < USceneOutlinerSettings > ( ) - > bShowOnlySelectedActors ;
2014-03-14 14:13:41 -04:00
}
2014-04-23 18:36:28 -04:00
// Hide Temporary Actors
2014-03-14 14:13:41 -04:00
void SSceneOutliner : : ToggleHideTemporaryActors ( )
{
const bool bEnableFlag = ! IsHidingTemporaryActors ( ) ;
2014-05-08 21:34:05 -04:00
USceneOutlinerSettings * Settings = GetMutableDefault < USceneOutlinerSettings > ( ) ;
Settings - > bHideTemporaryActors = bEnableFlag ;
Settings - > PostEditChange ( ) ;
2014-03-14 14:13:41 -04:00
ApplyHideTemporaryActorsFilter ( bEnableFlag ) ;
}
2014-04-29 05:59:53 -04:00
2014-03-14 14:13:41 -04:00
void SSceneOutliner : : ApplyHideTemporaryActorsFilter ( bool bHideTemporaryActors )
{
if ( ! HideTemporaryActorsFilter . IsValid ( ) )
{
2014-11-10 10:09:21 -05:00
HideTemporaryActorsFilter = CreateHideTemporaryActorsFilter ( ) ;
2014-03-14 14:13:41 -04:00
}
if ( bHideTemporaryActors )
{
2014-04-29 05:59:53 -04:00
Filters - > Add ( HideTemporaryActorsFilter ) ;
2014-03-14 14:13:41 -04:00
}
else
{
2014-04-29 05:59:53 -04:00
Filters - > Remove ( HideTemporaryActorsFilter ) ;
2014-03-14 14:13:41 -04:00
}
}
bool SSceneOutliner : : IsHidingTemporaryActors ( ) const
{
2014-05-08 21:34:05 -04:00
return GetDefault < USceneOutlinerSettings > ( ) - > bHideTemporaryActors ;
2014-03-14 14:13:41 -04:00
}
2014-04-23 18:36:28 -04:00
// Show Only Actors In Current Level
void SSceneOutliner : : ToggleShowOnlyCurrentLevel ( )
{
const bool bEnableFlag = ! IsShowingOnlyCurrentLevel ( ) ;
2014-05-08 21:34:05 -04:00
USceneOutlinerSettings * Settings = GetMutableDefault < USceneOutlinerSettings > ( ) ;
Settings - > bShowOnlyActorsInCurrentLevel = bEnableFlag ;
Settings - > PostEditChange ( ) ;
2014-04-23 18:36:28 -04:00
ApplyShowOnlyCurrentLevelFilter ( bEnableFlag ) ;
}
void SSceneOutliner : : ApplyShowOnlyCurrentLevelFilter ( bool bShowOnlyActorsInCurrentLevel )
{
if ( ! ShowOnlyActorsInCurrentLevelFilter . IsValid ( ) )
{
2014-11-10 10:09:21 -05:00
ShowOnlyActorsInCurrentLevelFilter = CreateIsInCurrentLevelFilter ( ) ;
2014-04-23 18:36:28 -04:00
}
if ( bShowOnlyActorsInCurrentLevel )
{
2014-04-29 05:59:53 -04:00
Filters - > Add ( ShowOnlyActorsInCurrentLevelFilter ) ;
2014-04-23 18:36:28 -04:00
}
else
{
2014-04-29 05:59:53 -04:00
Filters - > Remove ( ShowOnlyActorsInCurrentLevelFilter ) ;
2014-04-23 18:36:28 -04:00
}
}
bool SSceneOutliner : : IsShowingOnlyCurrentLevel ( ) const
{
2014-05-08 21:34:05 -04:00
return GetDefault < USceneOutlinerSettings > ( ) - > bShowOnlyActorsInCurrentLevel ;
2014-04-23 18:36:28 -04:00
}
2014-03-14 14:13:41 -04:00
/** END FILTERS */
const FSlateBrush * SSceneOutliner : : OnGetBorderBrush ( ) const
{
2014-11-10 10:09:21 -05:00
if ( SharedData - > bRepresentingPlayWorld )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
return GEditor - > bIsSimulatingInEditor ? SimulateBorder : PlayInEditorBorder ;
2014-03-14 14:13:41 -04:00
}
else
{
return NoBorder ;
}
}
FSlateColor SSceneOutliner : : OnGetBorderColorAndOpacity ( ) const
{
2014-11-10 10:09:21 -05:00
return SharedData - > bRepresentingPlayWorld ? FLinearColor ( 1.0f , 1.0f , 1.0f , 0.6f )
: FLinearColor : : Transparent ;
2014-03-14 14:13:41 -04:00
}
ESelectionMode : : Type SSceneOutliner : : GetSelectionMode ( ) const
{
2014-11-10 10:09:21 -05:00
return ( SharedData - > Mode = = ESceneOutlinerMode : : ActorBrowsing ) ?
2014-03-14 14:13:41 -04:00
ESelectionMode : : Multi : ESelectionMode : : Single ;
}
void SSceneOutliner : : Refresh ( )
{
bNeedsRefresh = true ;
}
2014-04-02 18:09:23 -04:00
void SSceneOutliner : : FullRefresh ( )
2014-03-14 14:13:41 -04:00
{
bFullRefresh = true ;
Refresh ( ) ;
}
void SSceneOutliner : : Populate ( )
{
// Block events while we clear out the list. We don't want actors in the level to become deselected
// while we doing this
TGuardValue < bool > ReentrantGuard ( bIsReentrant , true ) ;
2014-06-27 15:53:24 -04:00
for ( const FWorldContext & Context : GEngine - > GetWorldContexts ( ) )
2014-03-14 14:13:41 -04:00
{
2014-06-27 15:53:24 -04:00
if ( Context . WorldType = = EWorldType : : PIE )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
SharedData - > RepresentingWorld = Context . World ( ) ;
2014-03-14 14:13:41 -04:00
break ;
}
2014-06-27 15:53:24 -04:00
else if ( Context . WorldType = = EWorldType : : Editor )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
SharedData - > RepresentingWorld = Context . World ( ) ;
2014-03-14 14:13:41 -04:00
}
}
2014-11-10 10:09:21 -05:00
if ( ! CheckWorld ( ) )
2014-03-14 14:13:41 -04:00
{
return ;
}
2014-11-10 10:09:21 -05:00
SharedData - > bRepresentingPlayWorld = SharedData - > RepresentingWorld - > WorldType = = EWorldType : : PIE ;
2014-03-14 14:13:41 -04:00
#ttp 330692 - LIVE: EDITOR: Scene Outliner opens all parent trees when undo button pressed.
#branch UE4
#proj Editor.SceneOutliner, Runtime.Slate
#summary Fixed issue where undo button caused scene outliner to expand all parent trees.
#add Added STreeView::RefreshItemExpansion. Its purpose is to force the given item, during linearization, to linearize its children too, even if that item is not itself expanded. The reason you may wish to do this is to force the tree to recognise an expanded subtree which is hidden underneath a collapsed item, for example during repopulation.
#add Made STreeView::IsItemExpanded const-correct.
#add Added some helper functions in the SSceneOutliner namespace for determining whether an actor or folder is a parent of another actor or folder, somewhere in its hierarchy.
#add Added two new TSets to SSceneOutliner for storing any parent items which have been collapsed, prior to repopulating the tree. Upon adding parent items, the expansion state is set according to whether the item was found in the corresponding TSet, hence preserving its state. Additionally, if the item has children which are also parents, and are in an expanded state, STreeView::RefreshItemExpansion is called, so that the expansion state of any children is preserved upon opening a collapsed parent.
#change Deferred setting the expansion state of folders and parent items until they have all been created, and until all parents have been determined.
#reviewedby Chris.Wood, Andrew.Rodham, Nick.Atamas
[CL 2043071 by Richard TalbotWatkin in Main branch]
2014-04-23 18:13:30 -04:00
// Get a collection of items and folders which were formerly collapsed
2014-11-10 10:09:21 -05:00
const FParentsExpansionState ExpansionStateInfo = GetParentsExpansionState ( ) ;
#ttp 330692 - LIVE: EDITOR: Scene Outliner opens all parent trees when undo button pressed.
#branch UE4
#proj Editor.SceneOutliner, Runtime.Slate
#summary Fixed issue where undo button caused scene outliner to expand all parent trees.
#add Added STreeView::RefreshItemExpansion. Its purpose is to force the given item, during linearization, to linearize its children too, even if that item is not itself expanded. The reason you may wish to do this is to force the tree to recognise an expanded subtree which is hidden underneath a collapsed item, for example during repopulation.
#add Made STreeView::IsItemExpanded const-correct.
#add Added some helper functions in the SSceneOutliner namespace for determining whether an actor or folder is a parent of another actor or folder, somewhere in its hierarchy.
#add Added two new TSets to SSceneOutliner for storing any parent items which have been collapsed, prior to repopulating the tree. Upon adding parent items, the expansion state is set according to whether the item was found in the corresponding TSet, hence preserving its state. Additionally, if the item has children which are also parents, and are in an expanded state, STreeView::RefreshItemExpansion is called, so that the expansion state of any children is preserved upon opening a collapsed parent.
#change Deferred setting the expansion state of folders and parent items until they have all been created, and until all parents have been determined.
#reviewedby Chris.Wood, Andrew.Rodham, Nick.Atamas
[CL 2043071 by Richard TalbotWatkin in Main branch]
2014-04-23 18:13:30 -04:00
2014-03-14 14:13:41 -04:00
bool bMadeAnySignificantChanges = false ;
if ( bFullRefresh )
{
2014-11-10 10:09:21 -05:00
// Clear the selection here - RepopulateEntireTree will reconstruct it.
2014-03-14 14:13:41 -04:00
OutlinerTreeView - > ClearSelection ( ) ;
2014-11-10 10:09:21 -05:00
RepopulateEntireTree ( ) ;
2014-03-14 14:13:41 -04:00
bMadeAnySignificantChanges = true ;
bFullRefresh = false ;
}
2014-11-10 10:09:21 -05:00
// Only deal with 500 at a time
const int32 End = FMath : : Min ( PendingOperations . Num ( ) , 500 ) ;
for ( int32 Index = 0 ; Index < End ; + + Index )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
auto & PendingOp = PendingOperations [ Index ] ;
switch ( PendingOp . Type )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
case FPendingTreeOperation : : Added :
bMadeAnySignificantChanges = AddItemToTree ( PendingOp . Item ) | | bMadeAnySignificantChanges ;
break ;
2014-03-14 14:13:41 -04:00
2014-11-10 10:09:21 -05:00
case FPendingTreeOperation : : Moved :
2014-03-14 14:13:41 -04:00
bMadeAnySignificantChanges = true ;
2014-11-10 10:09:21 -05:00
OnItemMoved ( PendingOp . Item ) ;
break ;
2014-03-14 14:13:41 -04:00
2014-11-10 10:09:21 -05:00
case FPendingTreeOperation : : Removed :
bMadeAnySignificantChanges = true ;
RemoveItemFromTree ( PendingOp . Item ) ;
break ;
2014-03-14 14:13:41 -04:00
2014-11-10 10:09:21 -05:00
default :
check ( false ) ;
break ;
2014-03-14 14:13:41 -04:00
}
}
2014-11-10 10:09:21 -05:00
PendingOperations . RemoveAt ( 0 , End ) ;
SetParentsExpansionState ( ExpansionStateInfo ) ;
2014-03-14 14:13:41 -04:00
if ( bMadeAnySignificantChanges )
{
RequestSort ( ) ;
}
2014-11-10 10:09:21 -05:00
if ( PendingOperations . Num ( ) = = 0 )
{
// We're fully refreshed now.
NewItemActions . Empty ( ) ;
bNeedsRefresh = false ;
}
2014-03-14 14:13:41 -04:00
}
2014-04-02 18:09:23 -04:00
bool SSceneOutliner : : ShouldShowFolders ( ) const
{
2014-11-10 10:09:21 -05:00
return SharedData - > Mode = = ESceneOutlinerMode : : ActorBrowsing | | SharedData - > bOnlyShowFolders ;
2014-04-02 18:09:23 -04:00
}
2014-03-14 14:13:41 -04:00
void SSceneOutliner : : EmptyTreeItems ( )
{
FilteredActorCount = 0 ;
2014-11-10 10:09:21 -05:00
ApplicableActors . Empty ( ) ;
PendingOperations . Empty ( ) ;
TreeItemMap . Reset ( ) ;
2014-03-14 14:13:41 -04:00
RootTreeItems . Empty ( ) ;
}
2014-11-10 10:09:21 -05:00
void SSceneOutliner : : RepopulateEntireTree ( )
2014-03-14 14:13:41 -04:00
{
EmptyTreeItems ( ) ;
2014-11-13 09:58:00 -05:00
ConstructItemFor < FWorldTreeItem > ( SharedData - > RepresentingWorld ) ;
2014-11-10 10:09:21 -05:00
if ( ! SharedData - > bOnlyShowFolders )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
// Iterate over every actor in memory. WARNING: This is potentially very expensive!
for ( FActorIterator ActorIt ( SharedData - > RepresentingWorld ) ; ActorIt ; + + ActorIt )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
AActor * Actor = * ActorIt ;
if ( Actor & & IsActorDisplayable ( Actor ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
if ( Filters - > PassesAllFilters ( FActorTreeItem ( Actor ) ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
ApplicableActors . Emplace ( Actor ) ;
2014-03-14 14:13:41 -04:00
}
2014-11-10 10:09:21 -05:00
ConstructItemFor < FActorTreeItem > ( Actor ) ;
2014-03-14 14:13:41 -04:00
}
}
}
2014-11-10 10:09:21 -05:00
if ( ! IsShowingOnlySelected ( ) & & ShouldShowFolders ( ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
// Add any folders which might match the current search terms
for ( const auto & Pair : FActorFolders : : Get ( ) . GetFolderPropertiesForWorld ( * SharedData - > RepresentingWorld ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
if ( ! TreeItemMap . Contains ( Pair . Key ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
ConstructItemFor < FFolderTreeItem > ( Pair . Key ) ;
2014-03-14 14:13:41 -04:00
}
}
}
}
2014-11-10 10:09:21 -05:00
void SSceneOutliner : : OnChildRemovedFromParent ( ITreeItem & Parent )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
if ( Parent . Flags . bIsFilteredOut & & ! Parent . GetChildren ( ) . Num ( ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
// The parent no longer has any children that match the current search terms. Remove it.
RemoveItemFromTree ( Parent . AsShared ( ) ) ;
2014-03-14 14:13:41 -04:00
}
}
2014-11-10 10:09:21 -05:00
void SSceneOutliner : : OnItemMoved ( const FTreeItemRef & Item )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
// Just remove the item if it no longer matches the filters
if ( ! Item - > Flags . bIsFilteredOut & & ! SearchBoxFilter - > PassesFilter ( * Item ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
// This will potentially remove any non-matching, empty parents as well
RemoveItemFromTree ( Item ) ;
2014-03-14 14:13:41 -04:00
}
2014-11-10 10:09:21 -05:00
else
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
// The item still matches the filters (or has children that do)
// When an item has been asked to move, it will still reside under its old parent
FTreeItemPtr Parent = Item - > GetParent ( ) ;
if ( Parent . IsValid ( ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
Parent - > RemoveChild ( Item ) ;
OnChildRemovedFromParent ( * Parent ) ;
2014-03-14 14:13:41 -04:00
}
else
{
2014-11-10 10:09:21 -05:00
RootTreeItems . Remove ( Item ) ;
2014-03-14 14:13:41 -04:00
}
2014-11-10 10:09:21 -05:00
Parent = EnsureParentForItem ( Item ) ;
if ( Parent . IsValid ( ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
Parent - > AddChild ( Item ) ;
OutlinerTreeView - > SetItemExpansion ( Parent , true ) ;
2014-03-14 14:13:41 -04:00
}
else
{
2014-11-10 10:09:21 -05:00
RootTreeItems . Add ( Item ) ;
2014-03-14 14:13:41 -04:00
}
}
}
2014-11-10 10:09:21 -05:00
void SSceneOutliner : : RemoveItemFromTree ( FTreeItemRef InItem )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
if ( TreeItemMap . Contains ( InItem - > GetID ( ) ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
auto Parent = InItem - > GetParent ( ) ;
2014-03-14 14:13:41 -04:00
2014-11-10 10:09:21 -05:00
if ( Parent . IsValid ( ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
Parent - > RemoveChild ( InItem ) ;
OnChildRemovedFromParent ( * Parent ) ;
2014-03-14 14:13:41 -04:00
}
else
{
2014-11-10 10:09:21 -05:00
RootTreeItems . Remove ( InItem ) ;
2014-03-14 14:13:41 -04:00
}
2014-11-10 10:09:21 -05:00
InItem - > Visit ( FFunctionalVisitor ( ) . Actor ( [ & ] ( const FActorTreeItem & ActorItem ) {
if ( ! ActorItem . Flags . bIsFilteredOut )
{
- - FilteredActorCount ;
}
} ) ) ;
TreeItemMap . Remove ( InItem - > GetID ( ) ) ;
2014-03-14 14:13:41 -04:00
}
}
2014-11-10 10:09:21 -05:00
FTreeItemPtr SSceneOutliner : : EnsureParentForItem ( FTreeItemRef Item )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
if ( SharedData - > bShowParentTree )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
FTreeItemPtr Parent = Item - > FindParent ( TreeItemMap ) ;
if ( Parent . IsValid ( ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
return Parent ;
2014-03-14 14:13:41 -04:00
}
else
{
2014-11-10 10:09:21 -05:00
auto NewParent = Item - > CreateParent ( ) ;
if ( NewParent . IsValid ( ) )
{
2014-11-13 09:58:00 -05:00
NewParent - > Flags . bIsFilteredOut = ! Filters - > TestAndSetInteractiveState ( * NewParent ) | | ! SearchBoxFilter - > PassesFilter ( * NewParent ) ;
2014-11-10 10:09:21 -05:00
AddUnfilteredItemToTree ( NewParent . ToSharedRef ( ) ) ;
return NewParent ;
}
2014-03-14 14:13:41 -04:00
}
2014-11-10 10:09:21 -05:00
}
2014-03-14 14:13:41 -04:00
2014-11-10 10:09:21 -05:00
return nullptr ;
}
bool SSceneOutliner : : AddItemToTree ( FTreeItemRef Item )
{
const auto ItemID = Item - > GetID ( ) ;
// If a tree item already exists that represents the same data, bail
if ( TreeItemMap . Find ( ItemID ) )
{
return false ;
}
// Set the filtered out flag
Item - > Flags . bIsFilteredOut = ! SearchBoxFilter - > PassesFilter ( * Item ) ;
if ( ! Item - > Flags . bIsFilteredOut )
{
AddUnfilteredItemToTree ( Item ) ;
// Check if we need to do anything with this new item
if ( uint8 * ActionMask = NewItemActions . Find ( ItemID ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
if ( * ActionMask & ENewItemAction : : Select )
{
OutlinerTreeView - > ClearSelection ( ) ;
OutlinerTreeView - > SetItemSelection ( Item , true ) ;
}
if ( * ActionMask & ENewItemAction : : Rename )
{
PendingRenameItem = Item ;
}
if ( * ActionMask & ( ENewItemAction : : ScrollIntoView | ENewItemAction : : Rename ) )
{
ScrollItemIntoView ( Item ) ;
}
2014-03-14 14:13:41 -04:00
}
}
return true ;
}
2014-11-10 10:09:21 -05:00
void SSceneOutliner : : AddUnfilteredItemToTree ( FTreeItemRef Item )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
Item - > SharedData = SharedData ;
auto Parent = EnsureParentForItem ( Item ) ;
const auto ItemID = Item - > GetID ( ) ;
check ( ! TreeItemMap . Contains ( ItemID ) ) ;
TreeItemMap . Add ( ItemID , Item ) ;
if ( Parent . IsValid ( ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
Parent - > AddChild ( Item ) ;
}
else
{
RootTreeItems . Add ( Item ) ;
2014-03-14 14:13:41 -04:00
}
2014-11-10 10:09:21 -05:00
Item - > Visit ( FOnItemAddedToTree ( * this ) ) ;
2014-03-14 14:13:41 -04:00
}
2014-11-10 10:09:21 -05:00
SSceneOutliner : : FParentsExpansionState SSceneOutliner : : GetParentsExpansionState ( ) const
2014-04-23 17:50:18 -04:00
{
2014-11-10 10:09:21 -05:00
FParentsExpansionState States ;
for ( const auto & Pair : TreeItemMap )
2014-04-23 17:50:18 -04:00
{
2014-11-10 10:09:21 -05:00
if ( Pair . Value - > GetChildren ( ) . Num ( ) )
2014-04-23 17:50:18 -04:00
{
2014-11-10 10:09:21 -05:00
States . Add ( Pair . Key , Pair . Value - > Flags . bIsExpanded ) ;
2014-04-23 17:50:18 -04:00
}
}
2014-11-10 10:09:21 -05:00
return States ;
2014-04-23 17:50:18 -04:00
}
2014-11-10 10:09:21 -05:00
void SSceneOutliner : : SetParentsExpansionState ( const FParentsExpansionState & ExpansionStateInfo ) const
{
for ( const auto & Pair : TreeItemMap )
{
auto & Item = Pair . Value ;
if ( Item - > GetChildren ( ) . Num ( ) )
{
const bool * bIsExpanded = ExpansionStateInfo . Find ( Pair . Key ) ;
if ( bIsExpanded )
{
OutlinerTreeView - > SetItemExpansion ( Item , * bIsExpanded ) ;
}
else
{
OutlinerTreeView - > SetItemExpansion ( Item , Item - > Flags . bIsExpanded ) ;
}
}
}
}
void SSceneOutliner : : PopulateSearchStrings ( const ITreeItem & Item , TArray < FString > & OutSearchStrings ) const
{
for ( const auto & Pair : Columns )
{
Pair . Value - > PopulateSearchStrings ( Item , OutSearchStrings ) ;
}
}
TArray < FFolderTreeItem * > SSceneOutliner : : GetSelectedFolders ( ) const
{
return FItemSelection ( * OutlinerTreeView ) . Folders ;
}
TSharedPtr < SWidget > SSceneOutliner : : OnOpenContextMenu ( )
2014-03-14 14:13:41 -04:00
{
TArray < AActor * > SelectedActors ;
GEditor - > GetSelectedActors ( ) - > GetSelectedObjects < AActor > ( SelectedActors ) ;
2014-11-10 10:09:21 -05:00
/** Legacy context menu override only supports actors */
if ( SelectedActors . Num ( ) & & SharedData - > ContextMenuOverride . IsBound ( ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
return SharedData - > ContextMenuOverride . Execute ( ) ;
2014-03-14 14:13:41 -04:00
}
2014-04-23 16:39:22 -04:00
2014-11-10 10:09:21 -05:00
if ( SharedData - > Mode = = ESceneOutlinerMode : : ActorBrowsing )
2014-03-14 14:13:41 -04:00
{
2015-02-20 13:46:19 -05:00
// 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 ) ;
}
2014-04-23 16:39:22 -04:00
return BuildDefaultContextMenu ( ) ;
2014-03-14 14:13:41 -04:00
}
return TSharedPtr < SWidget > ( ) ;
}
2014-11-10 10:09:21 -05:00
TSharedPtr < SWidget > SSceneOutliner : : BuildDefaultContextMenu ( )
2014-04-02 18:09:23 -04:00
{
2014-11-10 10:09:21 -05:00
if ( ! CheckWorld ( ) )
{
return nullptr ;
}
// Build up the menu for a selection
2014-04-02 18:09:23 -04:00
const bool bCloseAfterSelection = true ;
2014-11-10 10:09:21 -05:00
FMenuBuilder MenuBuilder ( bCloseAfterSelection , TSharedPtr < FUICommandList > ( ) , SharedData - > DefaultMenuExtender ) ;
2014-04-02 18:09:23 -04:00
2014-11-10 10:09:21 -05:00
const auto NumSelectedItems = OutlinerTreeView - > GetNumItemsSelected ( ) ;
if ( NumSelectedItems = = 1 )
2014-04-02 18:09:23 -04:00
{
2014-11-10 10:09:21 -05:00
OutlinerTreeView - > GetSelectedItems ( ) [ 0 ] - > GenerateContextMenu ( MenuBuilder , * this ) ;
2014-04-02 18:09:23 -04:00
}
2014-05-21 09:00:28 -04:00
// We always create a section here, even if there is no parent so that clients can still extend the menu
2014-11-10 10:09:21 -05:00
MenuBuilder . BeginSection ( " MainSection " ) ;
2014-04-02 18:09:23 -04:00
{
2014-11-10 10:09:21 -05:00
FItemSelection ItemSelection ( * OutlinerTreeView ) ;
2014-04-02 18:09:23 -04:00
2014-11-10 10:09:21 -05:00
// Don't add any of these menu items if we're not showing the parent tree
if ( SharedData - > bShowParentTree )
2014-04-23 16:39:22 -04:00
{
2014-11-10 10:09:21 -05:00
if ( NumSelectedItems = = 0 )
2014-04-23 17:50:18 -04:00
{
2014-11-10 10:09:21 -05:00
const FSlateIcon NewFolderIcon ( FEditorStyle : : GetStyleSetName ( ) , " SceneOutliner.NewFolderIcon " ) ;
MenuBuilder . AddMenuEntry ( LOCTEXT ( " CreateFolder " , " Create Folder " ) , FText ( ) , NewFolderIcon , FUIAction ( FExecuteAction : : CreateSP ( this , & SSceneOutliner : : CreateFolder ) ) ) ;
}
else
{
// Can't move worlds or level blueprints
2015-03-09 15:55:02 -04:00
const bool bCanMoveSelection = ItemSelection . Worlds . Num ( ) = = 0 ;
2014-11-10 10:09:21 -05:00
if ( bCanMoveSelection )
{
2014-11-13 09:58:00 -05:00
MenuBuilder . AddSubMenu (
LOCTEXT ( " MoveActorsTo " , " Move To " ) ,
LOCTEXT ( " MoveActorsTo_Tooltip " , " Move selection to another folder " ) ,
FNewMenuDelegate : : CreateSP ( this , & SSceneOutliner : : FillFoldersSubMenu ) ) ;
2014-11-10 10:09:21 -05:00
}
2014-05-21 09:00:28 -04:00
// If we've only got folders selected, show the selection sub menu
2014-11-10 10:09:21 -05:00
if ( ItemSelection . Folders . Num ( ) = = NumSelectedItems )
2014-05-21 09:00:28 -04:00
{
MenuBuilder . AddSubMenu (
LOCTEXT ( " SelectSubmenu " , " Select " ) ,
LOCTEXT ( " SelectSubmenu_Tooltip " , " Select the contents of the current selection " ) ,
FNewMenuDelegate : : CreateSP ( this , & SSceneOutliner : : FillSelectionSubMenu ) ) ;
}
2014-04-23 17:50:18 -04:00
}
2014-04-23 16:39:22 -04:00
}
}
2014-11-10 10:09:21 -05:00
MenuBuilder . EndSection ( ) ;
2014-04-02 18:09:23 -04:00
return MenuBuilder . MakeWidget ( ) ;
}
void SSceneOutliner : : FillFoldersSubMenu ( FMenuBuilder & MenuBuilder ) const
{
2014-11-10 10:09:21 -05:00
MenuBuilder . AddMenuEntry ( LOCTEXT ( " CreateNew " , " Create New Folder " ) , LOCTEXT ( " CreateNew_ToolTip " , " Move to a new folder " ) ,
2014-04-23 16:39:22 -04:00
FSlateIcon ( FEditorStyle : : GetStyleSetName ( ) , " SceneOutliner.NewFolderIcon " ) , FExecuteAction : : CreateSP ( this , & SSceneOutliner : : CreateFolder ) ) ;
2014-04-29 05:59:53 -04:00
AddMoveToFolderOutliner ( MenuBuilder ) ;
}
2014-11-10 10:09:21 -05:00
TSharedRef < TSet < FName > > SSceneOutliner : : GatherInvalidMoveToDestinations ( ) const
{
// We use a pointer here to save copying the whole array for every invocation of the filter delegate
TSharedRef < TSet < FName > > ExcludedParents ( new TSet < FName > ( ) ) ;
struct FFindInvalidFolders : ITreeItemVisitor
{
TSet < FName > & ExcludedParents ;
const TMap < FTreeItemID , FTreeItemPtr > & TreeItemMap ;
FFindInvalidFolders ( TSet < FName > & InExcludedParents , const TMap < FTreeItemID , FTreeItemPtr > & InTreeItemMap )
: ExcludedParents ( InExcludedParents ) , TreeItemMap ( InTreeItemMap )
{ }
static bool ItemHasSubFolders ( const TWeakPtr < ITreeItem > & WeakItem )
{
bool bHasSubFolder = false ;
WeakItem . Pin ( ) - > Visit ( FFunctionalVisitor ( ) . Folder ( [ & ] ( const FFolderTreeItem & ) {
bHasSubFolder = true ;
} ) ) ;
return bHasSubFolder ;
}
virtual void Visit ( const FActorTreeItem & ActorItem ) const override
{
if ( AActor * Actor = ActorItem . Actor . Get ( ) )
{
// We exclude actor parent folders if they don't have any sub folders
const FName & Folder = Actor - > GetFolderPath ( ) ;
if ( ! Folder . IsNone ( ) & & ! ExcludedParents . Contains ( Folder ) )
{
auto FolderItem = TreeItemMap . FindRef ( Folder ) ;
if ( FolderItem . IsValid ( ) & & ! FolderItem - > GetChildren ( ) . ContainsByPredicate ( & ItemHasSubFolders ) )
{
ExcludedParents . Add ( Folder ) ;
}
}
}
}
virtual void Visit ( const FFolderTreeItem & Folder ) const override
{
// Cannot move into its parent
const FName ParentPath = GetParentPath ( Folder . Path ) ;
if ( ! ParentPath . IsNone ( ) )
{
ExcludedParents . Add ( ParentPath ) ;
}
else
{
// Failing that, cannot move into itself, or any child
ExcludedParents . Add ( Folder . Path ) ;
}
}
} ;
auto Visitor = FFindInvalidFolders ( * ExcludedParents , TreeItemMap ) ;
for ( const auto & Item : OutlinerTreeView - > GetSelectedItems ( ) )
{
Item - > Visit ( Visitor ) ;
}
return ExcludedParents ;
}
2014-04-29 05:59:53 -04:00
void SSceneOutliner : : AddMoveToFolderOutliner ( FMenuBuilder & MenuBuilder ) const
{
2014-11-10 10:09:21 -05:00
// We don't show this if there aren't any folders in the world
if ( ! FActorFolders : : Get ( ) . GetFolderPropertiesForWorld ( * SharedData - > RepresentingWorld ) . Num ( ) )
2014-04-29 05:24:48 -04:00
{
2014-04-29 05:59:53 -04:00
return ;
}
// Add a mini scene outliner for choosing an existing folder
2014-11-10 10:09:21 -05:00
FInitializationOptions MiniSceneOutlinerInitOptions ;
2014-06-05 12:16:00 -04:00
MiniSceneOutlinerInitOptions . bShowHeaderRow = false ;
MiniSceneOutlinerInitOptions . bFocusSearchBoxWhenOpened = true ;
MiniSceneOutlinerInitOptions . bOnlyShowFolders = true ;
2014-11-10 10:09:21 -05:00
// Don't show any folders that are a child of any of the selected folders
auto ExcludedParents = GatherInvalidMoveToDestinations ( ) ;
if ( ExcludedParents - > Num ( ) )
2014-04-29 05:59:53 -04:00
{
2014-11-10 10:09:21 -05:00
// Add a filter if necessary
auto FilterOutChildFolders = [ ] ( FName Path , TSharedRef < TSet < FName > > InExcludedParents ) {
for ( const auto & Parent : * InExcludedParents )
2014-04-29 05:59:53 -04:00
{
2014-11-10 10:09:21 -05:00
if ( Path = = Parent | | FActorFolders : : PathIsChildOf ( Path . ToString ( ) , Parent . ToString ( ) ) )
2014-04-29 05:59:53 -04:00
{
2014-11-10 10:09:21 -05:00
return false ;
2014-04-29 05:59:53 -04:00
}
}
2014-11-10 10:09:21 -05:00
return true ;
} ;
2014-04-29 05:59:53 -04:00
2014-11-10 10:09:21 -05:00
MiniSceneOutlinerInitOptions . Filters - > AddFilterPredicate ( FFolderFilterPredicate : : CreateStatic ( FilterOutChildFolders , ExcludedParents ) , EDefaultFilterBehaviour : : Pass ) ;
2014-04-29 05:24:48 -04:00
}
2014-11-10 10:09:21 -05:00
{
// Filter in/out the world according to whether it is valid to move to/from the root
FDragDropPayload DraggedObjects ( OutlinerTreeView - > GetSelectedItems ( ) ) ;
const bool bMoveToRootValid = FFolderDropTarget ( FName ( ) ) . ValidateDrop ( DraggedObjects , * SharedData - > RepresentingWorld ) . IsValid ( ) ;
2015-03-03 12:30:55 -05:00
MiniSceneOutlinerInitOptions . Filters - > AddFilterPredicate ( FWorldFilterPredicate : : CreateStatic ( [ ] ( const UWorld * , bool bInMoveToRootValid ) {
return bInMoveToRootValid ;
2014-11-10 10:09:21 -05:00
} , bMoveToRootValid ) , EDefaultFilterBehaviour : : Pass ) ;
}
// Don't show the actor info column
MiniSceneOutlinerInitOptions . UseDefaultColumns ( ) ;
MiniSceneOutlinerInitOptions . ColumnMap . Remove ( FBuiltInColumnTypes : : ActorInfo ( ) ) ;
// Actor selector to allow the user to choose a folder
FSceneOutlinerModule & SceneOutlinerModule = FModuleManager : : LoadModuleChecked < FSceneOutlinerModule > ( " SceneOutliner " ) ;
TSharedRef < SWidget > MiniSceneOutliner =
SNew ( SVerticalBox )
+ SVerticalBox : : Slot ( )
. MaxHeight ( 400.0f )
[
SNew ( SSceneOutliner , MiniSceneOutlinerInitOptions )
. IsEnabled ( FSlateApplication : : Get ( ) . GetNormalExecutionAttribute ( ) )
. OnItemPickedDelegate ( FOnSceneOutlinerItemPicked : : CreateSP ( this , & SSceneOutliner : : MoveSelectionTo ) )
] ;
MenuBuilder . BeginSection ( FName ( ) , LOCTEXT ( " ExistingFolders " , " Existing: " ) ) ;
MenuBuilder . AddWidget ( MiniSceneOutliner , FText : : GetEmpty ( ) , false ) ;
MenuBuilder . EndSection ( ) ;
2014-04-02 18:09:23 -04:00
}
2014-04-23 17:50:18 -04:00
void SSceneOutliner : : FillSelectionSubMenu ( FMenuBuilder & MenuBuilder ) const
{
MenuBuilder . AddMenuEntry (
LOCTEXT ( " AddChildrenToSelection " , " Immediate Children " ) ,
LOCTEXT ( " AddChildrenToSelection_ToolTip " , " Select all immediate children of the selected folders " ) ,
FSlateIcon ( ) ,
FExecuteAction : : CreateSP ( this , & SSceneOutliner : : SelectFoldersImmediateChildren ) ) ;
MenuBuilder . AddMenuEntry (
LOCTEXT ( " AddDescendantsToSelection " , " All Descendants " ) ,
LOCTEXT ( " AddDescendantsToSelection_ToolTip " , " Select all descendants of the selected folders " ) ,
FSlateIcon ( ) ,
FExecuteAction : : CreateSP ( this , & SSceneOutliner : : SelectFoldersDescendants ) ) ;
}
2014-11-10 10:09:21 -05:00
struct FSelectActors : ITreeItemVisitor
{
virtual void Visit ( const FActorTreeItem & ActorItem ) const override
{
if ( AActor * Actor = ActorItem . Actor . Get ( ) )
{
GEditor - > SelectActor ( Actor , true , /*bNotify=*/ false ) ;
}
}
2015-03-19 10:23:29 -04:00
} ;
2014-11-10 10:09:21 -05:00
2015-03-19 10:23:29 -04:00
struct FSelectActorsRecursive : FSelectActors
{
2015-03-19 12:07:57 -04:00
using FSelectActors : : Visit ;
2014-11-10 10:09:21 -05:00
virtual void Visit ( const FFolderTreeItem & FolderItem ) const override
{
for ( auto & Child : FolderItem . GetChildren ( ) )
{
2015-03-19 10:23:29 -04:00
Child . Pin ( ) - > Visit ( FSelectActorsRecursive ( ) ) ;
2014-11-10 10:09:21 -05:00
}
}
} ;
2014-04-23 17:50:18 -04:00
void SSceneOutliner : : SelectFoldersImmediateChildren ( ) const
{
auto SelectedFolders = GetSelectedFolders ( ) ;
if ( SelectedFolders . Num ( ) )
{
// We'll batch selection changes instead by using BeginBatchSelectOperation()
GEditor - > GetSelectedActors ( ) - > BeginBatchSelectOperation ( ) ;
2014-11-10 10:09:21 -05:00
OutlinerTreeView - > ClearSelection ( ) ;
2014-04-23 17:50:18 -04:00
for ( const auto & Folder : SelectedFolders )
{
2014-11-10 10:09:21 -05:00
for ( auto & Child : Folder - > GetChildren ( ) )
2014-04-23 17:50:18 -04:00
{
2014-11-10 10:09:21 -05:00
Child . Pin ( ) - > Visit ( FSelectActors ( ) ) ;
2014-04-23 17:50:18 -04:00
}
}
GEditor - > GetSelectedActors ( ) - > EndBatchSelectOperation ( ) ;
GEditor - > NoteSelectionChange ( ) ;
}
}
void SSceneOutliner : : SelectFoldersDescendants ( ) const
{
auto SelectedFolders = GetSelectedFolders ( ) ;
if ( SelectedFolders . Num ( ) )
{
// We'll batch selection changes instead by using BeginBatchSelectOperation()
GEditor - > GetSelectedActors ( ) - > BeginBatchSelectOperation ( ) ;
2014-11-10 10:09:21 -05:00
OutlinerTreeView - > ClearSelection ( ) ;
2014-04-23 17:50:18 -04:00
for ( const auto & Folder : SelectedFolders )
{
2015-03-19 10:23:29 -04:00
Folder - > Visit ( FSelectActorsRecursive ( ) ) ;
2014-04-23 17:50:18 -04:00
}
GEditor - > GetSelectedActors ( ) - > EndBatchSelectOperation ( ) ;
GEditor - > NoteSelectionChange ( ) ;
}
}
2014-11-10 10:09:21 -05:00
void SSceneOutliner : : MoveSelectionTo ( FTreeItemRef NewParent )
2014-04-23 16:39:22 -04:00
{
2014-11-10 10:09:21 -05:00
struct FMoveToFolder : ITreeItemVisitor
2014-04-23 16:39:22 -04:00
{
2014-11-10 10:09:21 -05:00
SSceneOutliner & Outliner ;
FMoveToFolder ( SSceneOutliner & InOutliner ) : Outliner ( InOutliner ) { }
2014-04-23 16:39:22 -04:00
2014-11-10 10:09:21 -05:00
virtual void Visit ( const FFolderTreeItem & Folder ) const override
{
Outliner . MoveSelectionTo ( Folder . Path ) ;
}
virtual void Visit ( const FWorldTreeItem & ) const override
{
Outliner . MoveSelectionTo ( FName ( ) ) ;
}
} ;
2014-04-23 16:39:22 -04:00
2014-11-10 10:09:21 -05:00
NewParent - > Visit ( FMoveToFolder ( * this ) ) ;
2014-04-23 16:39:22 -04:00
}
void SSceneOutliner : : MoveSelectionTo ( FName NewParent )
{
2014-11-10 10:09:21 -05:00
if ( ! CheckWorld ( ) )
2014-04-23 16:39:22 -04:00
{
2014-11-10 10:09:21 -05:00
return ;
}
FSlateApplication : : Get ( ) . DismissAllMenus ( ) ;
FFolderDropTarget DropTarget ( NewParent ) ;
FDragDropPayload DraggedObjects ( OutlinerTreeView - > GetSelectedItems ( ) ) ;
FDragValidationInfo Validation = DropTarget . ValidateDrop ( DraggedObjects , * SharedData - > RepresentingWorld ) ;
if ( ! Validation . IsValid ( ) )
{
FNotificationInfo Info ( Validation . ValidationText ) ;
2014-04-29 05:59:53 -04:00
Info . ExpireDuration = 3.0f ;
Info . bUseLargeFont = false ;
Info . bFireAndForget = true ;
Info . bUseSuccessFailIcons = true ;
FSlateNotificationManager : : Get ( ) . AddNotification ( Info ) - > SetCompletionState ( SNotificationItem : : CS_Fail ) ;
2014-04-23 16:39:22 -04:00
return ;
}
2015-02-06 09:03:00 -05:00
const FScopedTransaction Transaction ( LOCTEXT ( " MoveOutlinerItems " , " Move World Outliner Items " ) ) ;
2014-11-10 10:09:21 -05:00
DropTarget . OnDrop ( DraggedObjects , * SharedData - > RepresentingWorld , Validation , SNullWidget : : NullWidget ) ;
2014-03-14 14:13:41 -04:00
}
FReply SSceneOutliner : : OnCreateFolderClicked ( )
{
2014-04-23 16:39:22 -04:00
CreateFolder ( ) ;
2014-03-14 14:13:41 -04:00
return FReply : : Handled ( ) ;
}
2014-04-23 16:39:22 -04:00
void SSceneOutliner : : CreateFolder ( )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
if ( ! CheckWorld ( ) )
2014-03-14 14:13:41 -04:00
{
2014-04-02 18:09:23 -04:00
return ;
2014-03-14 14:13:41 -04:00
}
2014-11-10 10:09:21 -05:00
UWorld & World = * SharedData - > RepresentingWorld ;
2014-04-23 16:39:22 -04:00
const FScopedTransaction Transaction ( LOCTEXT ( " UndoAction_CreateFolder " , " Create Folder " ) ) ;
2014-11-10 10:09:21 -05:00
const FName NewFolderName = FActorFolders : : Get ( ) . GetDefaultFolderNameForSelection ( World ) ;
FActorFolders : : Get ( ) . CreateFolderContainingSelection ( World , NewFolderName ) ;
2014-04-23 16:39:22 -04:00
2014-11-10 10:09:21 -05:00
auto PreviouslySelectedItems = OutlinerTreeView - > GetSelectedItems ( ) ;
auto Visit = [ & ] ( const FFolderTreeItem & Folder )
{
MoveFolderTo ( Folder . Path , NewFolderName , World ) ;
} ;
auto Visitor = FFunctionalVisitor ( ) . Folder ( Visit ) ;
2014-04-23 16:39:22 -04:00
// Move any selected folders into the new folder name
2014-11-10 10:09:21 -05:00
for ( const auto & Item : PreviouslySelectedItems )
2014-04-23 16:39:22 -04:00
{
2014-11-10 10:09:21 -05:00
Item - > Visit ( Visitor ) ;
2014-04-23 16:39:22 -04:00
}
// At this point the new folder will be in our newly added list, so select it and open a rename when it gets refreshed
2014-11-10 10:09:21 -05:00
NewItemActions . Add ( NewFolderName , ENewItemAction : : Select | ENewItemAction : : Rename ) ;
2014-03-14 14:13:41 -04:00
}
2014-05-21 09:35:52 -04:00
void SSceneOutliner : : OnBroadcastFolderCreate ( UWorld & InWorld , FName NewPath )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
if ( ! ShouldShowFolders ( ) | | & InWorld ! = SharedData - > RepresentingWorld )
2014-03-14 14:13:41 -04:00
{
2014-04-02 18:09:23 -04:00
return ;
}
2014-11-10 10:09:21 -05:00
if ( ! TreeItemMap . Contains ( NewPath ) )
2014-04-02 18:09:23 -04:00
{
2014-11-10 10:09:21 -05:00
ConstructItemFor < FFolderTreeItem > ( NewPath ) ;
2014-03-14 14:13:41 -04:00
}
}
2014-11-10 10:09:21 -05:00
void SSceneOutliner : : OnBroadcastFolderMove ( UWorld & InWorld , FName OldPath , FName NewPath )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
if ( ! ShouldShowFolders ( ) | | & InWorld ! = SharedData - > RepresentingWorld )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
return ;
2014-03-14 14:13:41 -04:00
}
2014-11-10 10:09:21 -05:00
auto Item = TreeItemMap . FindRef ( OldPath ) ;
if ( Item . IsValid ( ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
// Remove it from the map under the old ID (which is derived from the folder path)
TreeItemMap . Remove ( Item - > GetID ( ) ) ;
// Now change the path and put it back in the map with its new ID
auto Folder = StaticCastSharedPtr < FFolderTreeItem > ( Item ) ;
Folder - > Path = NewPath ;
Folder - > LeafName = GetFolderLeafName ( NewPath ) ;
TreeItemMap . Add ( Item - > GetID ( ) , Item ) ;
// Add an operation to move the item in the hierarchy
PendingOperations . Emplace ( FPendingTreeOperation : : Moved , Item . ToSharedRef ( ) ) ;
Refresh ( ) ;
2014-03-14 14:13:41 -04:00
}
}
2014-05-21 09:35:52 -04:00
void SSceneOutliner : : OnBroadcastFolderDelete ( UWorld & InWorld , FName Path )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
if ( & InWorld ! = SharedData - > RepresentingWorld )
2014-04-02 18:09:23 -04:00
{
return ;
}
2014-11-10 10:09:21 -05:00
auto * Folder = TreeItemMap . Find ( Path ) ;
2014-03-14 14:13:41 -04:00
if ( Folder )
{
2014-11-10 10:09:21 -05:00
PendingOperations . Emplace ( FPendingTreeOperation : : Removed , Folder - > ToSharedRef ( ) ) ;
2014-03-14 14:13:41 -04:00
Refresh ( ) ;
}
}
2014-11-10 10:09:21 -05:00
void SSceneOutliner : : ScrollItemIntoView ( FTreeItemPtr Item )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
auto Parent = Item - > GetParent ( ) ;
while ( Parent . IsValid ( ) )
{
OutlinerTreeView - > SetItemExpansion ( Parent - > AsShared ( ) , true ) ;
Parent = Parent - > GetParent ( ) ;
}
OutlinerTreeView - > RequestScrollIntoView ( Item ) ;
2014-03-14 14:13:41 -04:00
}
2014-11-10 10:09:21 -05:00
void SSceneOutliner : : InitiateRename ( TSharedRef < ITreeItem > Item )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
if ( Item - > CanInteract ( ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
PendingRenameItem = Item ;
ScrollItemIntoView ( Item ) ;
2014-03-14 14:13:41 -04:00
}
}
2014-11-10 10:09:21 -05:00
TSharedRef < ITableRow > SSceneOutliner : : OnGenerateRowForOutlinerTree ( FTreeItemPtr Item , const TSharedRef < STableViewBase > & OwnerTable )
{
return SNew ( SSceneOutlinerTreeRow , OutlinerTreeView . ToSharedRef ( ) , SharedThis ( this ) ) . Item ( Item ) ;
}
void SSceneOutliner : : OnGetChildrenForOutlinerTree ( FTreeItemPtr InParent , TArray < FTreeItemPtr > & OutChildren )
{
if ( SharedData - > bShowParentTree )
{
for ( auto & WeakChild : InParent - > GetChildren ( ) )
{
auto Child = WeakChild . Pin ( ) ;
// Should never have bogus entries in this list
check ( Child . IsValid ( ) ) ;
OutChildren . Add ( Child ) ;
}
// If the item needs it's children sorting, do that now
if ( OutChildren . Num ( ) & & InParent - > Flags . bChildrenRequireSort )
{
// Sort the children we returned
SortItems ( OutChildren ) ;
// Empty out the children and repopulate them in the correct order
InParent - > Children . Empty ( ) ;
for ( auto & Child : OutChildren )
{
InParent - > Children . Emplace ( Child ) ;
}
// They no longer need sorting
InParent - > Flags . bChildrenRequireSort = false ;
}
}
}
2014-03-14 14:13:41 -04:00
bool SSceneOutliner : : IsActorDisplayable ( const AActor * Actor ) const
{
2014-11-10 10:09:21 -05:00
return ! SharedData - > bOnlyShowFolders & & // Don't show actors if we're only showing folders
Actor - > IsEditable ( ) & & // Only show actors that are allowed to be selected and drawn in editor
Actor - > IsListedInSceneOutliner ( ) & &
2014-12-19 08:57:26 -05:00
( SharedData - > bRepresentingPlayWorld | | ! Actor - > HasAnyFlags ( RF_Transient ) ) & & // Don't show transient actors in non-play worlds
2014-11-10 10:09:21 -05:00
! Actor - > IsTemplate ( ) & & // Should never happen, but we never want CDOs displayed
! FActorEditorUtils : : IsABuilderBrush ( Actor ) & & // Don't show the builder brush
! Actor - > IsA ( AWorldSettings : : StaticClass ( ) ) & & // Don't show the WorldSettings actor, even though it is technically editable
! Actor - > IsPendingKill ( ) & & // We don't want to show actors that are about to go away
FLevelUtils : : IsLevelVisible ( Actor - > GetLevel ( ) ) ; // Only show Actors whose level is visible
2014-03-14 14:13:41 -04:00
}
2014-11-10 10:09:21 -05:00
void SSceneOutliner : : OnOutlinerTreeSelectionChanged ( FTreeItemPtr TreeItem , ESelectInfo : : Type SelectInfo )
2014-03-14 14:13:41 -04:00
{
2014-05-01 09:43:17 -04:00
if ( SelectInfo = = ESelectInfo : : Direct )
{
return ;
}
2014-11-10 10:09:21 -05:00
if ( SharedData - > Mode = = ESceneOutlinerMode : : ActorPicker )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
// In actor picking mode, we fire off the notification to whoever is listening.
// This may often cause the widget itself to be enqueued for destruction
if ( OutlinerTreeView - > GetNumItemsSelected ( ) > 0 )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
auto FirstItem = OutlinerTreeView - > GetSelectedItems ( ) [ 0 ] ;
if ( FirstItem - > CanInteract ( ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
OnItemPicked . ExecuteIfBound ( FirstItem - > AsShared ( ) ) ;
2014-04-02 18:09:23 -04:00
}
2014-03-14 14:13:41 -04:00
}
}
// We only synchronize selection when in actor browsing mode
2014-11-10 10:09:21 -05:00
else if ( SharedData - > Mode = = ESceneOutlinerMode : : ActorBrowsing )
2014-03-14 14:13:41 -04:00
{
if ( ! bIsReentrant )
{
TGuardValue < bool > ReentrantGuard ( bIsReentrant , true ) ;
// @todo outliner: Can be called from non-interactive selection
// The tree let us know that selection has changed, but wasn't able to tell us
// what changed. So we'll perform a full difference check and update the editor's
// selected actors to match the control's selection set.
// Make a list of all the actors that should now be selected in the world.
2014-11-10 10:09:21 -05:00
FItemSelection Selection ( * OutlinerTreeView ) ;
auto SelectedActors = TSet < AActor * > ( Selection . GetActorPtrs ( ) ) ;
2014-03-14 14:13:41 -04:00
bool bChanged = false ;
2015-06-30 14:05:38 -04:00
bool bAnyInPIE = false ;
2014-11-10 10:09:21 -05:00
for ( auto * Actor : SelectedActors )
2014-03-14 14:13:41 -04:00
{
2015-06-30 14:05:38 -04:00
if ( ! bAnyInPIE & & Actor & & ( Actor - > GetOutermost ( ) - > PackageFlags & PKG_PlayInEditor ) ! = 0 )
{
bAnyInPIE = true ;
}
2014-11-10 10:09:21 -05:00
if ( ! GEditor - > GetSelectedActors ( ) - > IsSelected ( Actor ) )
2014-03-14 14:13:41 -04:00
{
bChanged = true ;
2014-11-10 10:09:21 -05:00
break ;
2014-03-14 14:13:41 -04:00
}
}
2014-11-10 10:09:21 -05:00
for ( FSelectionIterator SelectionIt ( * GEditor - > GetSelectedActors ( ) ) ; SelectionIt & & ! bChanged ; + + SelectionIt )
2014-03-14 14:13:41 -04:00
{
AActor * Actor = CastChecked < AActor > ( * SelectionIt ) ;
2015-06-30 14:05:38 -04:00
if ( ! bAnyInPIE & & ( Actor - > GetOutermost ( ) - > PackageFlags & PKG_PlayInEditor ) ! = 0 )
{
bAnyInPIE = true ;
}
2014-11-10 10:09:21 -05:00
if ( ! SelectedActors . Contains ( Actor ) )
2014-03-14 14:13:41 -04:00
{
// Actor has been deselected
bChanged = true ;
// If actor was a group actor, remove its members from the ActorsToSelect list
AGroupActor * DeselectedGroupActor = Cast < AGroupActor > ( Actor ) ;
if ( DeselectedGroupActor )
{
TArray < AActor * > GroupActors ;
DeselectedGroupActor - > GetGroupActors ( GroupActors ) ;
2014-11-10 10:09:21 -05:00
for ( auto * GroupActor : GroupActors )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
SelectedActors . Remove ( GroupActor ) ;
2014-03-14 14:13:41 -04:00
}
}
}
}
// If there's a discrepancy, update the selected actors to reflect this list.
if ( bChanged )
{
2015-06-30 14:05:38 -04:00
const FScopedTransaction Transaction ( NSLOCTEXT ( " UnrealEd " , " ClickingOnActors " , " Clicking on Actors " ) , ! bAnyInPIE ) ;
2014-03-14 14:13:41 -04:00
GEditor - > GetSelectedActors ( ) - > Modify ( ) ;
// Clear the selection.
GEditor - > SelectNone ( false , true , true ) ;
// We'll batch selection changes instead by using BeginBatchSelectOperation()
GEditor - > GetSelectedActors ( ) - > BeginBatchSelectOperation ( ) ;
const bool bShouldSelect = true ;
const bool bNotifyAfterSelect = false ;
const bool bSelectEvenIfHidden = true ; // @todo outliner: Is this actually OK?
2014-11-10 10:09:21 -05:00
for ( auto * Actor : SelectedActors )
2014-03-14 14:13:41 -04:00
{
2015-02-06 09:03:00 -05:00
UE_LOG ( LogSceneOutliner , Verbose , TEXT ( " Clicking on Actor (world outliner): %s (%s) " ) , * Actor - > GetClass ( ) - > GetName ( ) , * Actor - > GetActorLabel ( ) ) ;
2014-03-14 14:13:41 -04:00
GEditor - > SelectActor ( Actor , bShouldSelect , bNotifyAfterSelect , bSelectEvenIfHidden ) ;
}
// Commit selection changes
GEditor - > GetSelectedActors ( ) - > EndBatchSelectOperation ( ) ;
// Fire selection changed event
GEditor - > NoteSelectionChange ( ) ;
}
2014-11-10 10:09:21 -05:00
bActorSelectionDirty = true ;
2014-03-14 14:13:41 -04:00
}
}
}
void SSceneOutliner : : OnLevelSelectionChanged ( UObject * Obj )
{
// We only synchronize selection when in actor browsing mode
2014-11-10 10:09:21 -05:00
if ( SharedData - > Mode = = ESceneOutlinerMode : : ActorBrowsing )
2014-03-14 14:13:41 -04:00
{
// @todo outliner: Because we are not notified of which items are being added/removed from selection, we have
// no immediate means to incrementally update the tree when selection changes.
// Ideally, we can improve the filtering paradigm to better support incremental updates in cases such as these
if ( IsShowingOnlySelected ( ) )
{
FullRefresh ( ) ;
}
2014-11-10 10:09:21 -05:00
else if ( ! bIsReentrant )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
OutlinerTreeView - > ClearSelection ( ) ;
bActorSelectionDirty = true ;
2014-03-14 14:13:41 -04:00
2014-11-10 10:09:21 -05:00
// Scroll last item into view - this means if we are multi-selecting, we show newest selection. @TODO Not perfect though
if ( AActor * LastSelectedActor = GEditor - > GetSelectedActors ( ) - > GetBottom < AActor > ( ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
auto TreeItem = TreeItemMap . FindRef ( LastSelectedActor ) ;
if ( TreeItem . IsValid ( ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
if ( ! OutlinerTreeView - > IsItemVisible ( TreeItem ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
ScrollItemIntoView ( TreeItem ) ;
2014-03-14 14:13:41 -04:00
}
}
2014-11-10 10:09:21 -05:00
else
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
OnItemAdded ( LastSelectedActor , ENewItemAction : : ScrollIntoView ) ;
2014-03-14 14:13:41 -04:00
}
}
}
}
}
2014-11-10 10:09:21 -05:00
void SSceneOutliner : : OnOutlinerTreeDoubleClick ( FTreeItemPtr TreeItem )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
// We only deal with double clicks when in actor browsing mode
if ( SharedData - > Mode = = ESceneOutlinerMode : : ActorBrowsing )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
auto ExpandCollapseFolder = [ & ] ( const FFolderTreeItem & Folder ) {
auto Shared = const_cast < FFolderTreeItem & > ( Folder ) . AsShared ( ) ;
OutlinerTreeView - > SetItemExpansion ( Shared , ! OutlinerTreeView - > IsItemExpanded ( Shared ) ) ;
} ;
2014-03-14 14:13:41 -04:00
2014-11-10 10:09:21 -05:00
if ( TreeItem - > CanInteract ( ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
TreeItem - > Visit ( FFunctionalVisitor ( )
2014-03-14 14:13:41 -04:00
2014-11-10 10:09:21 -05:00
. Actor ( [ & ] ( const FActorTreeItem & ) {
// Move all actors into view
FItemSelection Selection ( * OutlinerTreeView ) ;
if ( Selection . Actors . Num ( ) > 0 )
{
const bool bActiveViewportOnly = false ;
GEditor - > MoveViewportCamerasToActor ( Selection . GetActorPtrs ( ) , bActiveViewportOnly ) ;
}
} )
. Folder ( ExpandCollapseFolder )
. World ( [ ] ( const FWorldTreeItem & WorldItem ) {
WorldItem . OpenWorldSettings ( ) ;
} )
) ;
}
else
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
TreeItem - > Visit ( FFunctionalVisitor ( )
. Folder ( ExpandCollapseFolder )
. Actor ( [ & ] ( const FActorTreeItem & Item ) {
// Move just this actor into view
if ( AActor * Actor = Item . Actor . Get ( ) )
{
const bool bActiveViewportOnly = false ;
GEditor - > MoveViewportCamerasToActor ( * Actor , bActiveViewportOnly ) ;
}
} )
) ;
2014-03-14 14:13:41 -04:00
}
}
}
2014-11-10 10:09:21 -05:00
void SSceneOutliner : : OnOutlinerTreeItemScrolledIntoView ( FTreeItemPtr TreeItem , const TSharedPtr < ITableRow > & Widget )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
if ( TreeItem = = PendingRenameItem . Pin ( ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
PendingRenameItem = nullptr ;
TreeItem - > RenameRequestEvent . ExecuteIfBound ( ) ;
2014-03-14 14:13:41 -04:00
}
}
2014-05-21 09:35:52 -04:00
2014-11-10 10:09:21 -05:00
void SSceneOutliner : : OnItemExpansionChanged ( FTreeItemPtr TreeItem , bool bIsExpanded ) const
2014-05-21 09:35:52 -04:00
{
2014-11-10 10:09:21 -05:00
TreeItem - > Flags . bIsExpanded = bIsExpanded ;
TreeItem - > OnExpansionChanged ( ) ;
// Expand any children that are also expanded
for ( auto WeakChild : TreeItem - > GetChildren ( ) )
2014-05-21 09:35:52 -04:00
{
2014-11-10 10:09:21 -05:00
auto Child = WeakChild . Pin ( ) ;
if ( Child - > Flags . bIsExpanded )
2014-05-21 09:35:52 -04:00
{
2014-11-10 10:09:21 -05:00
OutlinerTreeView - > SetItemExpansion ( Child , true ) ;
2014-05-21 09:35:52 -04:00
}
}
}
2014-03-14 14:13:41 -04:00
void SSceneOutliner : : OnLevelAdded ( ULevel * InLevel , UWorld * InWorld )
{
FullRefresh ( ) ;
}
void SSceneOutliner : : OnLevelRemoved ( ULevel * InLevel , UWorld * InWorld )
{
FullRefresh ( ) ;
}
void SSceneOutliner : : OnLevelActorsAdded ( AActor * InActor )
{
if ( ! bIsReentrant )
{
2014-11-10 10:09:21 -05:00
if ( InActor & & SharedData - > RepresentingWorld = = InActor - > GetWorld ( ) & & IsActorDisplayable ( InActor ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
if ( ! TreeItemMap . Find ( InActor ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
// Update the total actor count that match the filters
if ( Filters - > PassesAllFilters ( FActorTreeItem ( InActor ) ) )
{
ApplicableActors . Emplace ( InActor ) ;
}
2014-03-14 14:13:41 -04:00
2014-11-10 10:09:21 -05:00
ConstructItemFor < FActorTreeItem > ( InActor ) ;
}
2014-03-14 14:13:41 -04:00
}
}
}
void SSceneOutliner : : OnLevelActorsRemoved ( AActor * InActor )
{
if ( ! bIsReentrant )
{
2014-11-10 10:09:21 -05:00
if ( InActor & & SharedData - > RepresentingWorld = = InActor - > GetWorld ( ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
ApplicableActors . Remove ( InActor ) ;
2014-04-02 18:09:23 -04:00
2014-11-10 10:09:21 -05:00
if ( auto * ItemPtr = TreeItemMap . Find ( InActor ) )
{
PendingOperations . Emplace ( FPendingTreeOperation : : Removed , ItemPtr - > ToSharedRef ( ) ) ;
2014-03-14 14:13:41 -04:00
Refresh ( ) ;
}
}
}
}
void SSceneOutliner : : OnLevelActorsAttached ( AActor * InActor , const AActor * InParent )
{
// InActor can be equal to InParent in cases of components being attached internally. The Scene Outliner does not need to do anything in this case.
if ( ! bIsReentrant & & InActor ! = InParent )
{
2014-11-10 10:09:21 -05:00
if ( InActor & & SharedData - > RepresentingWorld = = InActor - > GetWorld ( ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
if ( auto * ItemPtr = TreeItemMap . Find ( InActor ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
PendingOperations . Emplace ( FPendingTreeOperation : : Moved , ItemPtr - > ToSharedRef ( ) ) ;
2014-03-14 14:13:41 -04:00
Refresh ( ) ;
}
}
}
}
void SSceneOutliner : : OnLevelActorsDetached ( AActor * InActor , const AActor * InParent )
{
// InActor can be equal to InParent in cases of components being attached internally. The Scene Outliner does not need to do anything in this case.
if ( ! bIsReentrant & & InActor ! = InParent )
{
2014-11-10 10:09:21 -05:00
if ( InActor & & SharedData - > RepresentingWorld = = InActor - > GetWorld ( ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
if ( auto * ItemPtr = TreeItemMap . Find ( InActor ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
PendingOperations . Emplace ( FPendingTreeOperation : : Moved , ItemPtr - > ToSharedRef ( ) ) ;
2014-03-14 14:13:41 -04:00
Refresh ( ) ;
}
else
{
// We should find the item, but if we don't, do an add.
OnLevelActorsAdded ( InActor ) ;
}
}
}
}
/** Called by the engine when an actor's folder is changed */
void SSceneOutliner : : OnLevelActorFolderChanged ( const AActor * InActor , FName OldPath )
{
2014-11-10 10:09:21 -05:00
auto * ActorTreeItem = TreeItemMap . Find ( InActor ) ;
2014-04-02 18:09:23 -04:00
if ( ! ShouldShowFolders ( ) | | ! InActor | | ! ActorTreeItem )
2014-03-14 14:13:41 -04:00
{
return ;
}
2014-11-10 10:09:21 -05:00
PendingOperations . Emplace ( FPendingTreeOperation : : Moved , ActorTreeItem - > ToSharedRef ( ) ) ;
2014-03-14 14:13:41 -04:00
Refresh ( ) ;
}
void SSceneOutliner : : OnLevelActorsRequestRename ( const AActor * InActor )
{
2014-11-10 10:09:21 -05:00
auto SelectedItems = OutlinerTreeView - > GetSelectedItems ( ) ;
2014-03-14 14:13:41 -04:00
if ( SelectedItems . Num ( ) > 0 )
{
// Ensure that the item we want to rename is visible in the tree
2014-11-10 10:09:21 -05:00
FTreeItemPtr ItemToRename = SelectedItems [ SelectedItems . Num ( ) - 1 ] ;
if ( ItemToRename - > CanInteract ( ) )
{
PendingRenameItem = ItemToRename - > AsShared ( ) ;
ScrollItemIntoView ( ItemToRename ) ;
}
2014-03-14 14:13:41 -04:00
}
}
void SSceneOutliner : : OnMapChange ( uint32 MapFlags )
{
2014-04-02 18:09:23 -04:00
FullRefresh ( ) ;
2014-03-14 14:13:41 -04:00
}
2014-08-11 04:43:59 -04:00
void SSceneOutliner : : OnNewCurrentLevel ( )
{
if ( IsShowingOnlyCurrentLevel ( ) )
{
FullRefresh ( ) ;
}
}
2014-03-14 14:13:41 -04:00
void SSceneOutliner : : PostUndo ( bool bSuccess )
{
// Refresh our tree in case any changes have been made to the scene that might effect our actor list
if ( ! bIsReentrant )
{
FullRefresh ( ) ;
}
}
void SSceneOutliner : : OnActorLabelChanged ( AActor * ChangedActor )
{
if ( ! ensure ( ChangedActor ) )
{
return ;
}
2014-11-10 10:09:21 -05:00
auto TreeItem = TreeItemMap . FindRef ( ChangedActor ) ;
if ( TreeItem . IsValid ( ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
if ( SearchBoxFilter - > PassesFilter ( * TreeItem ) )
2014-04-23 17:50:40 -04:00
{
2014-11-10 10:09:21 -05:00
OutlinerTreeView - > FlashHighlightOnItem ( TreeItem ) ;
RequestSort ( ) ;
}
else
{
// Do longer matches the filters, remove it
PendingOperations . Emplace ( FPendingTreeOperation : : Removed , TreeItem . ToSharedRef ( ) ) ;
Refresh ( ) ;
2014-04-23 17:50:40 -04:00
}
}
2014-11-10 10:09:21 -05:00
else if ( IsActorDisplayable ( ChangedActor ) )
2014-04-23 17:50:40 -04:00
{
2014-11-10 10:09:21 -05:00
// Attempt to add the item if we didn't find it - perhaps it now matches the filter?
ConstructItemFor < FActorTreeItem > ( ChangedActor ) ;
2014-04-23 17:50:40 -04:00
}
}
2014-03-14 14:13:41 -04:00
void SSceneOutliner : : OnFilterTextChanged ( const FText & InFilterText )
{
SearchBoxFilter - > SetRawFilterText ( InFilterText ) ;
2015-06-10 13:22:27 -04:00
FilterTextBoxWidget - > SetError ( SearchBoxFilter - > GetFilterErrorText ( ) ) ;
2014-03-14 14:13:41 -04:00
}
void SSceneOutliner : : OnFilterTextCommitted ( const FText & InFilterText , ETextCommit : : Type CommitInfo )
{
const FString CurrentFilterText = InFilterText . ToString ( ) ;
// We'll only select actors if the user actually pressed the enter key. We don't want to change
// selection just because focus was lost from the search text field.
if ( CommitInfo = = ETextCommit : : OnEnter )
{
// Any text in the filter? If not, we won't bother doing anything
if ( ! CurrentFilterText . IsEmpty ( ) )
{
2014-11-10 10:09:21 -05:00
FItemSelection Selection ;
2014-03-14 14:13:41 -04:00
// Gather all of the actors that match the filter text
2014-11-10 10:09:21 -05:00
for ( auto & Pair : TreeItemMap )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
if ( ! Pair . Value - > Flags . bIsFilteredOut )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
Pair . Value - > Visit ( Selection ) ;
2014-03-14 14:13:41 -04:00
}
}
// We only select level actors when in actor browsing mode
2014-11-10 10:09:21 -05:00
if ( SharedData - > Mode = = ESceneOutlinerMode : : ActorBrowsing )
2014-03-14 14:13:41 -04:00
{
// 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 ) ;
2014-11-10 10:09:21 -05:00
for ( auto * Actor : Selection . GetActorPtrs ( ) )
2014-03-14 14:13:41 -04:00
{
const bool bShouldSelect = true ;
const bool bSelectEvenIfHidden = false ;
GEditor - > SelectActor ( Actor , bShouldSelect , bNoteSelectionChange , bSelectEvenIfHidden ) ;
}
// Commit selection changes
GEditor - > GetSelectedActors ( ) - > EndBatchSelectOperation ( ) ;
// 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.)
2014-11-10 10:09:21 -05:00
SetKeyboardFocus ( ) ;
2014-03-14 14:13:41 -04:00
}
// In 'actor picking' mode, we allow the user to commit their selection by pressing enter
// in the search window when a single actor is available
2014-11-10 10:09:21 -05:00
else if ( SharedData - > Mode = = ESceneOutlinerMode : : ActorPicker )
2014-03-14 14:13:41 -04:00
{
// In actor picking mode, we check to see if we have a selected actor, and if so, fire
// off the notification to whoever is listening. This may often cause the widget itself
// to be enqueued for destruction
2014-11-10 10:09:21 -05:00
if ( Selection . Actors . Num ( ) = = 1 )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
// Signal that an actor was selected. We assume it is valid as it won't have been added to ActorsToSelect if not.
OnItemPicked . ExecuteIfBound ( Selection . Actors [ 0 ] - > AsShared ( ) ) ;
2014-03-14 14:13:41 -04:00
}
}
}
}
}
EVisibility SSceneOutliner : : GetFilterStatusVisibility ( ) const
{
return IsFilterActive ( ) ? EVisibility : : Visible : EVisibility : : Collapsed ;
}
EVisibility SSceneOutliner : : GetEmptyLabelVisibility ( ) const
{
return ( IsFilterActive ( ) | | RootTreeItems . Num ( ) > 0 ) ? EVisibility : : Collapsed : EVisibility : : Visible ;
}
2015-01-07 09:52:40 -05:00
FText SSceneOutliner : : GetFilterStatusText ( ) const
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
const int32 TotalActorCount = ApplicableActors . Num ( ) ;
int32 SelectedActorCount = 0 ;
auto Count = [ & ] ( const FActorTreeItem & ) { + + SelectedActorCount ; } ;
for ( const auto & Item : OutlinerTreeView - > GetSelectedItems ( ) )
{
Item - > Visit ( FFunctionalVisitor ( ) . Actor ( Count ) ) ;
}
2014-03-14 14:13:41 -04:00
if ( ! IsFilterActive ( ) )
{
if ( SelectedActorCount = = 0 )
{
2015-01-07 09:52:40 -05:00
return FText : : Format ( LOCTEXT ( " ShowingAllActorsFmt " , " {0} actors " ) , FText : : AsNumber ( TotalActorCount ) ) ;
2014-03-14 14:13:41 -04:00
}
else
{
2015-01-07 09:52:40 -05:00
return FText : : Format ( LOCTEXT ( " ShowingAllActorsSelectedFmt " , " {0} actors ({1} selected) " ) , FText : : AsNumber ( TotalActorCount ) , FText : : AsNumber ( SelectedActorCount ) ) ;
2014-03-14 14:13:41 -04:00
}
}
else if ( IsFilterActive ( ) & & FilteredActorCount = = 0 )
{
2015-01-07 09:52:40 -05:00
return FText : : Format ( LOCTEXT ( " ShowingNoActorsFmt " , " No matching actors ({0} total) " ) , FText : : AsNumber ( TotalActorCount ) ) ;
2014-03-14 14:13:41 -04:00
}
else if ( SelectedActorCount ! = 0 )
{
2015-01-07 09:52:40 -05:00
return FText : : Format ( LOCTEXT ( " ShowingOnlySomeActorsSelectedFmt " , " Showing {0} of {1} actors ({2} selected) " ) , FText : : AsNumber ( FilteredActorCount ) , FText : : AsNumber ( TotalActorCount ) , FText : : AsNumber ( SelectedActorCount ) ) ;
2014-03-14 14:13:41 -04:00
}
else
{
2015-01-07 09:52:40 -05:00
return FText : : Format ( LOCTEXT ( " ShowingOnlySomeActorsFmt " , " Showing {0} of {1} actors " ) , FText : : AsNumber ( FilteredActorCount ) , FText : : AsNumber ( TotalActorCount ) ) ;
2014-03-14 14:13:41 -04:00
}
}
FSlateColor SSceneOutliner : : GetFilterStatusTextColor ( ) const
{
if ( ! IsFilterActive ( ) )
{
// White = no text filter
return FLinearColor ( 1.0f , 1.0f , 1.0f ) ;
}
else if ( FilteredActorCount = = 0 )
{
// Red = no matching actors
return FLinearColor ( 1.0f , 0.4f , 0.4f ) ;
}
else
{
// Green = found at least one match!
return FLinearColor ( 0.4f , 1.0f , 0.4f ) ;
}
}
bool SSceneOutliner : : IsFilterActive ( ) const
{
2014-11-10 10:09:21 -05:00
return FilterTextBoxWidget - > GetText ( ) . ToString ( ) . Len ( ) > 0 & & ApplicableActors . Num ( ) ! = FilteredActorCount ;
2014-03-14 14:13:41 -04:00
}
const FSlateBrush * SSceneOutliner : : GetFilterButtonGlyph ( ) const
{
if ( IsFilterActive ( ) )
{
return FEditorStyle : : GetBrush ( TEXT ( " SceneOutliner.FilterCancel " ) ) ;
}
else
{
return FEditorStyle : : GetBrush ( TEXT ( " SceneOutliner.FilterSearch " ) ) ;
}
}
FString SSceneOutliner : : GetFilterButtonToolTip ( ) const
{
return IsFilterActive ( ) ? LOCTEXT ( " ClearSearchFilter " , " Clear search filter " ) . ToString ( ) : LOCTEXT ( " StartSearching " , " Search " ) . ToString ( ) ;
}
2014-11-10 10:09:21 -05:00
TAttribute < FText > SSceneOutliner : : GetFilterHighlightText ( ) const
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
return TAttribute < FText > : : Create ( TAttribute < FText > : : FGetter : : CreateStatic ( [ ] ( TWeakPtr < TreeItemTextFilter > Filter ) {
auto FilterPtr = Filter . Pin ( ) ;
return FilterPtr . IsValid ( ) ? FilterPtr - > GetRawFilterText ( ) : FText ( ) ;
} , TWeakPtr < TreeItemTextFilter > ( SearchBoxFilter ) ) ) ;
2014-03-14 14:13:41 -04:00
}
2014-11-10 10:09:21 -05:00
void SSceneOutliner : : SetKeyboardFocus ( )
{
if ( SupportsKeyboardFocus ( ) )
{
FWidgetPath OutlinerTreeViewWidgetPath ;
// NOTE: Careful, GeneratePathToWidget can be reentrant in that it can call visibility delegates and such
FSlateApplication : : Get ( ) . GeneratePathToWidgetUnchecked ( OutlinerTreeView . ToSharedRef ( ) , OutlinerTreeViewWidgetPath ) ;
FSlateApplication : : Get ( ) . SetKeyboardFocus ( OutlinerTreeViewWidgetPath , EFocusCause : : SetDirectly ) ;
}
}
2014-03-14 14:13:41 -04:00
bool SSceneOutliner : : SupportsKeyboardFocus ( ) const
{
// We only need to support keyboard focus if we're in actor browsing mode
2014-11-10 10:09:21 -05:00
if ( SharedData - > Mode = = ESceneOutlinerMode : : ActorBrowsing )
2014-03-14 14:13:41 -04:00
{
// Scene outliner needs keyboard focus so the user can press keys to activate commands, such as the Delete
// key to delete selected actors
return true ;
}
return false ;
}
2014-10-30 12:29:36 -04:00
FReply SSceneOutliner : : OnKeyDown ( const FGeometry & MyGeometry , const FKeyEvent & InKeyEvent )
2014-03-14 14:13:41 -04:00
{
// @todo outliner: Use command system for these for discoverability? (allow bindings?)
// We only allow these operations in actor browsing mode
2014-11-10 10:09:21 -05:00
if ( SharedData - > Mode = = ESceneOutlinerMode : : ActorBrowsing )
2014-03-14 14:13:41 -04:00
{
2014-12-02 17:06:51 -05:00
// Rename key: Rename selected actors (not rebindable, because it doesn't make much sense to bind.)
2014-10-30 12:29:36 -04:00
if ( InKeyEvent . GetKey ( ) = = EKeys : : F2 )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
if ( OutlinerTreeView - > GetNumItemsSelected ( ) = = 1 )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
FTreeItemPtr ItemToRename = OutlinerTreeView - > GetSelectedItems ( ) [ 0 ] ;
if ( ItemToRename - > CanInteract ( ) )
{
PendingRenameItem = ItemToRename - > AsShared ( ) ;
ScrollItemIntoView ( ItemToRename ) ;
}
2014-03-14 14:13:41 -04:00
return FReply : : Handled ( ) ;
}
}
2014-11-10 10:09:21 -05:00
// F5 forces a full refresh
else if ( InKeyEvent . GetKey ( ) = = EKeys : : F5 )
{
FullRefresh ( ) ;
return FReply : : Handled ( ) ;
}
// Delete key: Delete selected actors (not rebindable, because it doesn't make much sense to bind.)
2014-10-30 12:29:36 -04:00
else if ( InKeyEvent . GetKey ( ) = = EKeys : : Platform_Delete )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
const FItemSelection Selection ( * OutlinerTreeView ) ;
if ( SharedData - > CustomDelete . IsBound ( ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
SharedData - > CustomDelete . Execute ( Selection . GetWeakActors ( ) ) ;
2014-03-14 14:13:41 -04:00
}
2014-11-10 10:09:21 -05:00
else if ( CheckWorld ( ) )
2014-03-14 14:13:41 -04:00
{
const FScopedTransaction Transaction ( LOCTEXT ( " UndoAction_DeleteSelection " , " Delete selection " ) ) ;
// Delete selected folders too
auto SelectedItems = OutlinerTreeView - > GetSelectedItems ( ) ;
GEditor - > SelectNone ( true , true ) ;
2014-11-10 10:09:21 -05:00
for ( auto * Folder : Selection . Folders )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
Folder - > Delete ( ) ;
}
for ( auto * Actor : Selection . GetActorPtrs ( ) )
{
GEditor - > SelectActor ( Actor , true , false ) ;
2014-03-14 14:13:41 -04:00
}
// Code from FLevelEditorActionCallbacks::Delete_CanExecute()
// Should this be just return FReply::Unhandled()?
TArray < FEdMode * > ActiveModes ;
2014-06-18 10:16:16 -04:00
GLevelEditorModeTools ( ) . GetActiveModes ( ActiveModes ) ;
2014-03-14 14:13:41 -04:00
for ( int32 ModeIndex = 0 ; ModeIndex < ActiveModes . Num ( ) ; + + ModeIndex )
{
const EEditAction : : Type CanProcess = ActiveModes [ ModeIndex ] - > GetActionEditDelete ( ) ;
if ( CanProcess = = EEditAction : : Process )
{
return FReply : : Handled ( ) ;
}
else if ( CanProcess = = EEditAction : : Halt )
{
return FReply : : Unhandled ( ) ;
}
}
2014-11-10 10:09:21 -05:00
if ( GUnrealEd - > CanDeleteSelectedActors ( SharedData - > RepresentingWorld , true , false ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
GEditor - > edactDeleteSelected ( SharedData - > RepresentingWorld ) ;
2014-03-14 14:13:41 -04:00
}
}
return FReply : : Handled ( ) ;
}
}
return FReply : : Unhandled ( ) ;
}
2014-11-10 10:09:21 -05:00
void SSceneOutliner : : SynchronizeActorSelection ( )
{
TGuardValue < bool > ReentrantGuard ( bIsReentrant , true ) ;
auto * SelectedActors = GEditor - > GetSelectedActors ( ) ;
// Deselect actors in the tree that are no longer selected in the world
FItemSelection Selection ( * OutlinerTreeView ) ;
for ( auto * ActorItem : Selection . Actors )
{
if ( ! SelectedActors - > IsSelected ( ActorItem - > Actor . Get ( ) ) )
{
OutlinerTreeView - > SetItemSelection ( ActorItem - > AsShared ( ) , false ) ;
}
}
// Ensure that all selected actors in the world are selected in the tree
for ( FSelectionIterator SelectionIt ( * SelectedActors ) ; SelectionIt ; + + SelectionIt )
{
AActor * Actor = CastChecked < AActor > ( * SelectionIt ) ;
if ( auto * ActorItem = TreeItemMap . Find ( Actor ) )
{
OutlinerTreeView - > SetItemSelection ( * ActorItem , true ) ;
}
}
// Broadcast selection changed delegate
SelectionChanged . Broadcast ( ) ;
}
2014-03-14 14:13:41 -04:00
void SSceneOutliner : : Tick ( const FGeometry & AllottedGeometry , const double InCurrentTime , const float InDeltaTime )
---- Merging with SlateDev branch ----
Introduces the concept of "Active Ticking" to allow Slate to go to sleep when there is no need to update the UI.
While asleep, Slate will skip the Tick & Paint pass for that frame entirely.
- There are TWO ways to "wake" Slate and cause a Tick/Paint pass:
1. Provide some sort of input (mouse movement, clicks, and key presses). Slate will always tick when the user is active.
- Therefore, if the logic in a given widget's Tick is only relevant in response to user action, there is no need to register an active tick.
2. Register an Active Tick. Currently this is an all-or-nothing situation, so if a single active tick needs to execute, all of Slate will be ticked.
- The purpose of an Active Tick is to allow a widget to "drive" Slate and guarantee a Tick/Paint pass in the absence of any user action.
- Examples include animation, async operations that update periodically, progress updates, loading bars, etc.
- An empty active tick is registered for viewports when they are real-time, so game project widgets are unaffected by this change and should continue to work as before.
- An Active Tick is registered by creating an FWidgetActiveTickDelegate and passing it to SWidget::RegisterActiveTick()
- There are THREE ways to unregister an active tick:
1. Return EActiveTickReturnType::StopTicking from the active tick function
2. Pass the FActiveTickHandle returned by RegisterActiveTick() to SWidget::UnregisterActiveTick()
3. Destroy the widget responsible for the active tick
- Sleeping is currently disabled, can be enabled with Slate.AllowSlateToSleep cvar
- There is currently a little buffer time during which Slate continues to tick following any input. Long-term, this is planned to be removed.
- The duration of the buffer can be adjusted using Slate.SleepBufferPostInput cvar (defaults to 1.0f)
- The FCurveSequence API has been updated to work with the active tick system
- Playing a curve sequence now requires that you pass the widget being animated by the sequence
- The active tick will automatically be registered on behalf of the widget and unregister when the sequence is complete
- GetLerpLooping() has been removed. Instead, pass true as the second param to Play() to indicate that the animation will loop. This causes the active tick to be registered indefinitely until paused or jumped to the start/end.
[CL 2391669 by Dan Hertzka in Main branch]
2014-12-17 16:07:57 -05:00
{
2014-11-10 10:09:21 -05:00
for ( auto & Pair : Columns )
{
Pair . Value - > Tick ( InCurrentTime , InDeltaTime ) ;
}
2014-03-14 14:13:41 -04:00
if ( bPendingFocusNextFrame & & FilterTextBoxWidget - > GetVisibility ( ) = = EVisibility : : Visible )
{
FWidgetPath WidgetToFocusPath ;
FSlateApplication : : Get ( ) . GeneratePathToWidgetUnchecked ( FilterTextBoxWidget . ToSharedRef ( ) , WidgetToFocusPath ) ;
2014-10-30 12:29:36 -04:00
FSlateApplication : : Get ( ) . SetKeyboardFocus ( WidgetToFocusPath , EFocusCause : : SetDirectly ) ;
2014-03-14 14:13:41 -04:00
bPendingFocusNextFrame = false ;
}
if ( bNeedsRefresh )
{
if ( ! bIsReentrant )
{
Populate ( ) ;
}
}
SortOutlinerTimer - = InDeltaTime ;
2014-11-10 10:09:21 -05:00
if ( bSortDirty & & ( ! SharedData - > bRepresentingPlayWorld | | SortOutlinerTimer < = 0 ) )
{
SortItems ( RootTreeItems ) ;
for ( const auto & Pair : TreeItemMap )
{
Pair . Value - > Flags . bChildrenRequireSort = true ;
}
OutlinerTreeView - > RequestTreeRefresh ( ) ;
bSortDirty = false ;
}
if ( SortOutlinerTimer < = 0 )
{
SortOutlinerTimer = SCENE_OUTLINER_RESORT_TIMER ;
}
if ( bActorSelectionDirty )
{
SynchronizeActorSelection ( ) ;
bActorSelectionDirty = false ;
}
2014-03-14 14:13:41 -04:00
}
EColumnSortMode : : Type SSceneOutliner : : GetColumnSortMode ( const FName ColumnId ) const
{
2014-11-10 10:09:21 -05:00
if ( SortByColumn = = ColumnId )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
auto Column = Columns . FindRef ( ColumnId ) ;
if ( Column . IsValid ( ) & & Column - > SupportsSorting ( ) )
{
return SortMode ;
}
2014-03-14 14:13:41 -04:00
}
2014-11-10 10:09:21 -05:00
return EColumnSortMode : : None ;
2014-03-14 14:13:41 -04:00
}
Asset View now supports secondary sorting
#UDN: Feature suggestion; Content browser can sort by a second column
#branch UE4
#change
Added two new images for secondary sort ascending and descending.
(Updated styles where needed).
Added new enum EColumnSortPriority: currently only lists Primary and Secondary, but can easily accommodate more*.
(FOnSortModeChanged was modified to also have the priority as a param, fixedup existing usage).
SHeaderRow now also has a SortPriority attribute to specify the SortMode order
(Defaults to Primary if unused).
Modified TUniquePtr so that assigning one from another worked correctly.
(Reviewed by Steve Robb).
SAssetView is the only table that has been modified, so far, to take advantage of the secondary sort. SetMajorityAssetType has been updated to correctly filter out all those sorts which are no longer relevant and bump the priority of the remaining sorts to fill in the ægapsÆ made by non-longer-relevant ones.
FAssetViewSortManager has been overhauled to take SortPriority into consideration when sortingÃ
Firstly, duplicate comparison structs were removed in favour of single structs which have æascendingÆ as a paramà any remaining duplicate code was removed in favour of an inherited usage. The base struct has an array of æfurther methodsÆ to employ in the result of a tie. Should a tie occur the ænextÆ comparison is done, and so on until itÆs not a tie or we run out of comparisons to perform.*
The manager defaults to having no secondary sort, so it relies on the interface thatÆs using it to provide it.
Whenever a column is assign the code makes sure that itÆs not already assigned to another column and corrects the order to take this into account.
Fixed a bug in FCompareFAssetItemByTagNumericalAscending comparing A with A (instead of B).
Various optimizations to the sort to descrease times
*The only places æSecondaryÆ is referred to in code is in GetSortingBrush (so it can display the correct icon for the sort), and OnTitleClicked (so it can set to correct sort based on input). The sorting code itself has no concept as to a secondary sort, it can support any number of sorting levels so if in future a tertiary (or more) is added, it is only these two function which should need updating as the sort will automatically accommodate it.
reviewed by Thomas.Sarkanen, Bob.Tellez
[CL 2119201 by Andrew Brown in Main branch]
2014-06-27 04:30:08 -04:00
void SSceneOutliner : : OnColumnSortModeChanged ( const EColumnSortPriority : : Type SortPriority , const FName & ColumnId , const EColumnSortMode : : Type InSortMode )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
auto Column = Columns . FindRef ( ColumnId ) ;
if ( ! Column . IsValid ( ) | | ! Column - > SupportsSorting ( ) )
2014-03-14 14:13:41 -04:00
{
return ;
}
SortByColumn = ColumnId ;
SortMode = InSortMode ;
RequestSort ( ) ;
}
void SSceneOutliner : : RequestSort ( )
{
2014-11-10 10:09:21 -05:00
bSortDirty = true ;
2014-03-14 14:13:41 -04:00
}
2014-11-10 10:09:21 -05:00
void SSceneOutliner : : SortItems ( TArray < FTreeItemPtr > & Items ) const
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
auto Column = Columns . FindRef ( SortByColumn ) ;
if ( Column . IsValid ( ) )
2014-03-14 14:13:41 -04:00
{
2014-11-10 10:09:21 -05:00
Column - > SortItems ( Items , SortMode ) ;
2014-04-23 16:39:22 -04:00
}
}
2014-11-10 10:09:21 -05:00
void SSceneOutliner : : FOnItemAddedToTree : : Visit ( FActorTreeItem & ActorItem ) const
2014-04-23 16:39:22 -04:00
{
2014-11-10 10:09:21 -05:00
Outliner . FilteredActorCount + = ActorItem . Flags . bIsFilteredOut ? 0 : 1 ;
2014-04-23 16:39:22 -04:00
2014-11-10 10:09:21 -05:00
// Synchronize selection
if ( Outliner . SharedData - > Mode = = ESceneOutlinerMode : : ActorBrowsing & & GEditor - > GetSelectedActors ( ) - > IsSelected ( ActorItem . Actor . Get ( ) ) )
{
// Have to const cast here as the tree view is templated on non-const ptrs
Outliner . OutlinerTreeView - > SetItemSelection ( ActorItem . AsShared ( ) , true ) ;
2014-04-23 16:39:22 -04:00
}
}
2014-11-10 10:09:21 -05:00
void SSceneOutliner : : FOnItemAddedToTree : : Visit ( FFolderTreeItem & Folder ) const
2014-04-23 16:39:22 -04:00
{
2014-11-10 10:09:21 -05:00
if ( ! Outliner . SharedData - > RepresentingWorld )
2014-04-23 16:39:22 -04:00
{
2014-11-10 10:09:21 -05:00
return ;
}
if ( FActorFolderProps * Props = FActorFolders : : Get ( ) . GetFolderProperties ( * Outliner . SharedData - > RepresentingWorld , Folder . Path ) )
{
Folder . Flags . bIsExpanded = Props - > bIsExpanded ;
2014-04-23 16:39:22 -04:00
}
}
2014-03-14 14:13:41 -04:00
} // namespace SceneOutliner
# undef LOCTEXT_NAMESPACE