456 lines
10 KiB
C++
456 lines
10 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "LayerViewModel.h"
|
|
#include "Editor/EditorEngine.h"
|
|
#include "ScopedTransaction.h"
|
|
#include "Misc/DelegateFilter.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "Layer"
|
|
|
|
DEFINE_LOG_CATEGORY_STATIC(LogLayerViewModel, Fatal, All);
|
|
|
|
FLayerViewModel::FLayerViewModel( const TWeakObjectPtr< ULayer >& InLayer, const TWeakObjectPtr< UEditorEngine >& InEditor )
|
|
: Editor( InEditor )
|
|
, WorldLayers(Editor.IsValid() ? Editor->GetEditorSubsystem<ULayersSubsystem>() : nullptr)
|
|
, Layer( InLayer )
|
|
{
|
|
// Sanity checks
|
|
if (!Editor.IsValid())
|
|
{
|
|
UE_LOG(LogLayerViewModel, Fatal, TEXT("This function requires Editor.IsValid() == true."));
|
|
}
|
|
else if (WorldLayers == nullptr)
|
|
{
|
|
UE_LOG(LogLayerViewModel, Fatal, TEXT("This function requires Editor->GetEditorSubsystem<ULayersSubsystem>() to be already loaded rather than being a nullptr."));
|
|
}
|
|
}
|
|
|
|
|
|
void FLayerViewModel::Initialize()
|
|
{
|
|
WorldLayers->OnLayersChanged().AddSP( this, &FLayerViewModel::OnLayersChanged );
|
|
|
|
if ( Editor.IsValid() )
|
|
{
|
|
Editor->RegisterForUndo(this);
|
|
}
|
|
|
|
RefreshActorStats();
|
|
}
|
|
|
|
|
|
FLayerViewModel::~FLayerViewModel()
|
|
{
|
|
WorldLayers->OnLayersChanged().RemoveAll( this );
|
|
|
|
if ( Editor.IsValid() )
|
|
{
|
|
Editor->UnregisterForUndo(this);
|
|
}
|
|
}
|
|
|
|
|
|
FName FLayerViewModel::GetFName() const
|
|
{
|
|
if( !Layer.IsValid() )
|
|
{
|
|
return NAME_None;
|
|
}
|
|
|
|
return Layer->GetLayerName();
|
|
}
|
|
|
|
|
|
FString FLayerViewModel::GetName() const
|
|
{
|
|
FString name;
|
|
if( Layer.IsValid() )
|
|
{
|
|
name = Layer->GetLayerName().ToString();
|
|
}
|
|
return name;
|
|
}
|
|
|
|
FText FLayerViewModel::GetNameAsText() const
|
|
{
|
|
if( !Layer.IsValid() )
|
|
{
|
|
return FText::GetEmpty();
|
|
}
|
|
|
|
return FText::FromName(Layer->GetLayerName());
|
|
}
|
|
|
|
|
|
bool FLayerViewModel::IsVisible() const
|
|
{
|
|
if( !Layer.IsValid() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return Layer->IsVisible();
|
|
}
|
|
|
|
|
|
void FLayerViewModel::ToggleVisibility()
|
|
{
|
|
if( !Layer.IsValid() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
VisibilityToggledEvent.Broadcast( AsShared() );
|
|
}
|
|
|
|
|
|
bool FLayerViewModel::CanRenameTo( const FName& NewLayerName, FString& OutMessage ) const
|
|
{
|
|
if (NewLayerName.IsNone())
|
|
{
|
|
OutMessage = LOCTEXT("EmptyLayerName", "Layer must be given a name").ToString();
|
|
return false;
|
|
}
|
|
|
|
ULayer* FoundLayer;
|
|
if ( WorldLayers->TryGetLayer( NewLayerName, FoundLayer ) && FoundLayer != Layer.Get() )
|
|
{
|
|
OutMessage = LOCTEXT("RenameFailed_AlreadyExists", "This layer already exists").ToString();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void FLayerViewModel::RenameTo( const FName& NewLayerName )
|
|
{
|
|
if( !Layer.IsValid() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( Layer->GetLayerName() == NewLayerName)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int32 LayerIndex = 0;
|
|
FName UniqueNewLayerName = NewLayerName;
|
|
while( WorldLayers->IsLayer( UniqueNewLayerName ) )
|
|
{
|
|
UniqueNewLayerName = FName( *FString::Printf( TEXT( "%s_%d" ), *NewLayerName.ToString(), ++LayerIndex ) );
|
|
}
|
|
|
|
const FScopedTransaction Transaction( LOCTEXT("RenameTo", "Rename Layer") );
|
|
|
|
WorldLayers->RenameLayer( Layer->GetLayerName(), UniqueNewLayerName );
|
|
}
|
|
|
|
|
|
bool FLayerViewModel::CanAssignActors( const TArray< TWeakObjectPtr<AActor> > Actors, FText& OutMessage ) const
|
|
{
|
|
if( !Layer.IsValid() )
|
|
{
|
|
OutMessage = LOCTEXT("InvalidLayer", "Invalid Layer");
|
|
return false;
|
|
}
|
|
|
|
FFormatNamedArguments LayerArgs;
|
|
LayerArgs.Add(TEXT("LayerName"), FText::FromName(Layer->GetLayerName()));
|
|
|
|
bool bHasValidActorToAssign = false;
|
|
int32 bAlreadyAssignedActors = 0;
|
|
for( auto ActorIt = Actors.CreateConstIterator(); ActorIt; ++ActorIt )
|
|
{
|
|
TWeakObjectPtr< AActor > Actor = *ActorIt;
|
|
if( !Actor.IsValid() )
|
|
{
|
|
OutMessage = FText::Format(LOCTEXT("InvalidActors", "Cannot add invalid Actors to {LayerName}"), LayerArgs);
|
|
return false;
|
|
}
|
|
|
|
FFormatNamedArguments ActorArgs;
|
|
ActorArgs.Add(TEXT("ActorName"), FText::FromName(Actor->GetFName()));
|
|
|
|
if( !WorldLayers->IsActorValidForLayer( Actor.Get() ) )
|
|
{
|
|
OutMessage = FText::Format(LOCTEXT("InvalidLayers", "Actor '{ActorName}' cannot be associated with Layers"), ActorArgs);
|
|
return false;
|
|
}
|
|
|
|
if( Actor->Layers.Contains( Layer->GetLayerName() ) )
|
|
{
|
|
bAlreadyAssignedActors++;
|
|
}
|
|
else
|
|
{
|
|
bHasValidActorToAssign = true;
|
|
}
|
|
}
|
|
|
|
if( bAlreadyAssignedActors == Actors.Num() )
|
|
{
|
|
OutMessage = FText::Format(LOCTEXT("AlreadyAssignedActors", "All Actors already assigned to {LayerName}"), LayerArgs);
|
|
return false;
|
|
}
|
|
|
|
if( bHasValidActorToAssign )
|
|
{
|
|
OutMessage = FText::Format(LOCTEXT("AssignActors", "Assign Actors to {LayerName}"), LayerArgs);
|
|
return true;
|
|
}
|
|
|
|
OutMessage = FText::GetEmpty();
|
|
return false;
|
|
}
|
|
|
|
|
|
bool FLayerViewModel::CanAssignActor( const TWeakObjectPtr<AActor> Actor, FText& OutMessage ) const
|
|
{
|
|
if( !Layer.IsValid() )
|
|
{
|
|
OutMessage = LOCTEXT("InvalidLayer", "Invalid Layer");
|
|
return false;
|
|
}
|
|
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("LayerName"), FText::FromName(Layer->GetLayerName()));
|
|
|
|
if( !Actor.IsValid() )
|
|
{
|
|
OutMessage = FText::Format(LOCTEXT("InvalidActor", "Cannot add invalid Actors to {LayerName}"), Args);
|
|
return false;
|
|
}
|
|
|
|
Args.Add(TEXT("ActorName"), FText::FromName(Actor->GetFName()));
|
|
|
|
if( !WorldLayers->IsActorValidForLayer( Actor.Get()) )
|
|
{
|
|
OutMessage = FText::Format(LOCTEXT("InvalidLayers", "Actor '{ActorName}' cannot be associated with Layers"), Args);
|
|
return false;
|
|
}
|
|
|
|
if( Actor->Layers.Contains( Layer->GetLayerName() ) )
|
|
{
|
|
OutMessage = FText::Format(LOCTEXT("AlreadyAssignedActor", "Already assigned to {LayerName}"), Args);
|
|
return false;
|
|
}
|
|
|
|
OutMessage = FText::Format(LOCTEXT("AssignActor", "Assign to {LayerName}"), Args);
|
|
return true;
|
|
}
|
|
|
|
|
|
void FLayerViewModel::AppendActors( TArray< TWeakObjectPtr< AActor > >& InActors ) const
|
|
{
|
|
if( !Layer.IsValid() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
WorldLayers->AppendActorsFromLayer( Layer->GetLayerName(), InActors );
|
|
}
|
|
|
|
|
|
void FLayerViewModel::AppendActorsOfSpecificType( TArray< TWeakObjectPtr< AActor > >& InActors, const TWeakObjectPtr< UClass >& Class )
|
|
{
|
|
if( !Layer.IsValid() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
struct Local
|
|
{
|
|
static bool ActorIsOfClass( const TWeakObjectPtr< AActor >& Actor, const TWeakObjectPtr< UClass > InClass )
|
|
{
|
|
return Actor->GetClass() == InClass.Get();
|
|
}
|
|
};
|
|
|
|
TSharedRef< TDelegateFilter< const TWeakObjectPtr< AActor >& > > Filter = MakeShareable( new TDelegateFilter< const TWeakObjectPtr< AActor >& >( TDelegateFilter< const TWeakObjectPtr< AActor >& >::FPredicate::CreateStatic( &Local::ActorIsOfClass, Class ) ) );
|
|
WorldLayers->AppendActorsFromLayer( Layer->GetLayerName(), InActors, Filter );
|
|
}
|
|
|
|
|
|
void FLayerViewModel::AddActor( const TWeakObjectPtr< AActor >& Actor )
|
|
{
|
|
if( !Layer.IsValid() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FScopedTransaction Transaction( LOCTEXT("AddActor", "Add Actor to Layer") );
|
|
WorldLayers->AddActorToLayer( Actor.Get(), Layer->GetLayerName() );
|
|
}
|
|
|
|
|
|
void FLayerViewModel::AddActors( const TArray< TWeakObjectPtr< AActor > >& Actors )
|
|
{
|
|
if( !Layer.IsValid() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FScopedTransaction Transaction( LOCTEXT("AddActors", "Add Actors to Layer") );
|
|
WorldLayers->AddActorsToLayer( Actors, Layer->GetLayerName() );
|
|
}
|
|
|
|
|
|
void FLayerViewModel::RemoveActors( const TArray< TWeakObjectPtr< AActor > >& Actors )
|
|
{
|
|
if( !Layer.IsValid() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FScopedTransaction Transaction( LOCTEXT("RemoveActors", "Remove Actors from Layer") );
|
|
WorldLayers->RemoveActorsFromLayer( Actors, Layer->GetLayerName() );
|
|
}
|
|
|
|
|
|
void FLayerViewModel::RemoveActor( const TWeakObjectPtr< AActor >& Actor )
|
|
{
|
|
if( !Layer.IsValid() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FScopedTransaction Transaction( LOCTEXT("RemoveActor", "Remove Actor from Layer") );
|
|
WorldLayers->RemoveActorFromLayer( Actor.Get(), Layer->GetLayerName() );
|
|
}
|
|
|
|
|
|
void FLayerViewModel::SelectActors( bool bSelect, bool bNotify, bool bSelectEvenIfHidden, const TSharedPtr< IFilter< const TWeakObjectPtr< AActor >& > >& Filter )
|
|
{
|
|
if( !Layer.IsValid() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FScopedTransaction Transaction( LOCTEXT("SelectActors", "Select Actors in Layer") );
|
|
|
|
const bool bNotifySelectNone = false;
|
|
const bool bDeselectBSPSurfs = true;
|
|
Editor->SelectNone( bNotifySelectNone, bDeselectBSPSurfs );
|
|
|
|
WorldLayers->SelectActorsInLayer( Layer->GetLayerName(), bSelect, bNotify, bSelectEvenIfHidden, Filter );
|
|
}
|
|
|
|
|
|
FText FLayerViewModel::GetActorStatTotal( int32 StatsIndex ) const
|
|
{
|
|
if( !Layer.IsValid() )
|
|
{
|
|
return FText::AsNumber(0);
|
|
}
|
|
|
|
if( ActorStats.Num() <= StatsIndex )
|
|
{
|
|
return LOCTEXT("InvalidActorStatTotal", "Invalid");
|
|
}
|
|
|
|
return FText::AsNumber(ActorStats[ StatsIndex ].Total);
|
|
}
|
|
|
|
|
|
void FLayerViewModel::SelectActorsOfSpecificType( const TWeakObjectPtr< UClass >& Class )
|
|
{
|
|
if( !Layer.IsValid() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
struct Local
|
|
{
|
|
static bool ActorIsOfClass( const TWeakObjectPtr< AActor >& Actor, const TWeakObjectPtr< UClass > InClass )
|
|
{
|
|
return Actor->GetClass() == InClass.Get();
|
|
}
|
|
};
|
|
|
|
const bool bSelect = true;
|
|
const bool bNotify = true;
|
|
const bool bSelectEvenIfHidden = true;
|
|
TSharedRef< TDelegateFilter< const TWeakObjectPtr< AActor >& > > Filter = MakeShareable( new TDelegateFilter< const TWeakObjectPtr< AActor >& >( TDelegateFilter< const TWeakObjectPtr< AActor >& >::FPredicate::CreateStatic( &Local::ActorIsOfClass, Class ) ) );
|
|
SelectActors( bSelect, bNotify, bSelectEvenIfHidden, Filter );
|
|
}
|
|
|
|
|
|
const TArray< FLayerActorStats >& FLayerViewModel::GetActorStats() const
|
|
{
|
|
return ActorStats;
|
|
}
|
|
|
|
|
|
void FLayerViewModel::SetDataSource( const TWeakObjectPtr< ULayer >& InLayer )
|
|
{
|
|
if( Layer == InLayer )
|
|
{
|
|
return;
|
|
}
|
|
|
|
Layer = InLayer;
|
|
Refresh();
|
|
}
|
|
|
|
|
|
const TWeakObjectPtr< ULayer > FLayerViewModel::GetDataSource()
|
|
{
|
|
return Layer;
|
|
}
|
|
|
|
|
|
void FLayerViewModel::OnLayersChanged( const ELayersAction::Type Action, const TWeakObjectPtr< ULayer >& ChangedLayer, const FName& ChangedProperty )
|
|
{
|
|
if( Action != ELayersAction::Modify && Action != ELayersAction::Reset )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( ChangedLayer.IsValid() && ChangedLayer != Layer )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( Action == ELayersAction::Reset || ChangedProperty == TEXT( "ActorStats" ) )
|
|
{
|
|
RefreshActorStats();
|
|
}
|
|
|
|
ChangedEvent.Broadcast();
|
|
}
|
|
|
|
|
|
void FLayerViewModel::RefreshActorStats()
|
|
{
|
|
ActorStats.Empty();
|
|
|
|
if( !Layer.IsValid() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
ActorStats.Append( Layer->GetActorStats() );
|
|
|
|
struct FCompareLayerActorStats
|
|
{
|
|
FORCEINLINE bool operator()( const FLayerActorStats& Lhs, const FLayerActorStats& Rhs ) const
|
|
{
|
|
int32 Result = Lhs.Type->GetFName().Compare( Rhs.Type->GetFName() );
|
|
return Result > 0;
|
|
}
|
|
};
|
|
|
|
ActorStats.Sort( FCompareLayerActorStats() );
|
|
}
|
|
|
|
|
|
void FLayerViewModel::Refresh()
|
|
{
|
|
OnLayersChanged( ELayersAction::Reset, NULL, NAME_None );
|
|
}
|
|
|
|
|
|
#undef LOCTEXT_NAMESPACE
|