Files
UnrealEngineUWP/Engine/Source/Editor/SceneOutliner/Private/ActorHierarchy.cpp
Richard Malo 223c790fef ActorFolder Improvements :
- Optimized FFolder resolving to UActorFolder : now uses and maintains an acceleration table
- Favor creation of FFolder using ActorFolderGuid when available
- FFolder creation now always passes a Root Object to facilitate Root Object ptr resolving (even when it's the main world)
- Fixed Duplicate Hierarchy when using Actor Folders and target level is different
- Fixed copy/paste actor from Persistent to LevelInstance not loosing actor folder
- Fixed mark for delete of an Actor Folder that could generate duplicates
- Modified fix of duplicate Actor Folders in a level : instead of renaming duplicates, mark for delete all duplicates except one and redirect children to the one we keep

#jira UE-150566
#rb patrick.enfedaque, jeanfrancois.dube
#preflight 6284101f486700b561a555ff

[CL 20346124 by Richard Malo in ue5-main branch]
2022-05-24 06:58:06 -04:00

833 lines
28 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "ActorHierarchy.h"
#include "Engine/Engine.h"
#include "Engine/World.h"
#include "EngineUtils.h"
#include "WorldTreeItem.h"
#include "LevelTreeItem.h"
#include "ActorTreeItem.h"
#include "ActorDescTreeItem.h"
#include "ComponentTreeItem.h"
#include "ActorFolderTreeItem.h"
#include "ISceneOutlinerMode.h"
#include "ActorEditorUtils.h"
#include "LevelUtils.h"
#include "GameFramework/WorldSettings.h"
#include "EditorActorFolders.h"
#include "EditorFolderUtils.h"
#include "LevelInstance/LevelInstanceInterface.h"
#include "LevelInstance/LevelInstanceSubsystem.h"
#include "Modules/ModuleManager.h"
#include "WorldPartition/WorldPartitionHelpers.h"
#include "WorldPartition/WorldPartitionActorDesc.h"
#include "WorldPartition/IWorldPartitionEditorModule.h"
#include "WorldPartition/WorldPartitionSubsystem.h"
#include "ActorFolder.h"
TUniquePtr<FActorHierarchy> FActorHierarchy::Create(ISceneOutlinerMode* Mode, const TWeakObjectPtr<UWorld>& World)
{
FActorHierarchy* Hierarchy = new FActorHierarchy(Mode, World);
GEngine->OnLevelActorAdded().AddRaw(Hierarchy, &FActorHierarchy::OnLevelActorAdded);
GEngine->OnLevelActorDeleted().AddRaw(Hierarchy, &FActorHierarchy::OnLevelActorDeleted);
GEngine->OnLevelActorDetached().AddRaw(Hierarchy, &FActorHierarchy::OnLevelActorDetached);
GEngine->OnLevelActorAttached().AddRaw(Hierarchy, &FActorHierarchy::OnLevelActorAttached);
GEngine->OnLevelActorFolderChanged().AddRaw(Hierarchy, &FActorHierarchy::OnLevelActorFolderChanged);
GEngine->OnLevelActorListChanged().AddRaw(Hierarchy, &FActorHierarchy::OnLevelActorListChanged);
GEngine->OnActorFolderAdded().AddRaw(Hierarchy, &FActorHierarchy::OnActorFolderAdded);
GEngine->OnActorFoldersUpdatedEvent().AddRaw(Hierarchy, &FActorHierarchy::OnActorFoldersUpdatedEvent);
IWorldPartitionEditorModule& WorldPartitionEditorModule = FModuleManager::LoadModuleChecked<IWorldPartitionEditorModule>("WorldPartitionEditor");
WorldPartitionEditorModule.OnWorldPartitionCreated().AddRaw(Hierarchy, &FActorHierarchy::OnWorldPartitionCreated);
if (World.IsValid())
{
if (World->PersistentLevel)
{
World->PersistentLevel->OnLoadedActorAddedToLevelEvent.AddRaw(Hierarchy, &FActorHierarchy::OnLoadedActorAdded);
World->PersistentLevel->OnLoadedActorRemovedFromLevelEvent.AddRaw(Hierarchy, &FActorHierarchy::OnLoadedActorRemoved);
}
World->OnWorldPartitionInitialized().AddRaw(Hierarchy, &FActorHierarchy::OnWorldPartitionInitialized);
World->OnWorldPartitionUninitialized().AddRaw(Hierarchy, &FActorHierarchy::OnWorldPartitionUninitialized);
if (UWorldPartition* WorldPartition = World->GetWorldPartition())
{
WorldPartition->OnActorDescAddedEvent.AddRaw(Hierarchy, &FActorHierarchy::OnActorDescAdded);
WorldPartition->OnActorDescRemovedEvent.AddRaw(Hierarchy, &FActorHierarchy::OnActorDescRemoved);
}
}
FWorldDelegates::LevelAddedToWorld.AddRaw(Hierarchy, &FActorHierarchy::OnLevelAdded);
FWorldDelegates::LevelRemovedFromWorld.AddRaw(Hierarchy, &FActorHierarchy::OnLevelRemoved);
auto& Folders = FActorFolders::Get();
Folders.OnFolderCreated.AddRaw(Hierarchy, &FActorHierarchy::OnBroadcastFolderCreate);
Folders.OnFolderMoved.AddRaw(Hierarchy, &FActorHierarchy::OnBroadcastFolderMove);
Folders.OnFolderDeleted.AddRaw(Hierarchy, &FActorHierarchy::OnBroadcastFolderDelete);
return TUniquePtr<FActorHierarchy>(Hierarchy);
}
FActorHierarchy::FActorHierarchy(ISceneOutlinerMode* Mode, const TWeakObjectPtr<UWorld>& World)
: ISceneOutlinerHierarchy(Mode)
, RepresentingWorld(World)
{
}
FActorHierarchy::~FActorHierarchy()
{
if (GEngine)
{
GEngine->OnLevelActorAdded().RemoveAll(this);
GEngine->OnLevelActorDeleted().RemoveAll(this);
GEngine->OnLevelActorDetached().RemoveAll(this);
GEngine->OnLevelActorAttached().RemoveAll(this);
GEngine->OnLevelActorFolderChanged().RemoveAll(this);
GEngine->OnLevelActorListChanged().RemoveAll(this);
GEngine->OnActorFolderAdded().RemoveAll(this);
GEngine->OnActorFoldersUpdatedEvent().RemoveAll(this);
}
IWorldPartitionEditorModule& WorldPartitionEditorModule = FModuleManager::LoadModuleChecked<IWorldPartitionEditorModule>("WorldPartitionEditor");
WorldPartitionEditorModule.OnWorldPartitionCreated().RemoveAll(this);
if (RepresentingWorld.IsValid())
{
if (RepresentingWorld->PersistentLevel)
{
RepresentingWorld->PersistentLevel->OnLoadedActorAddedToLevelEvent.RemoveAll(this);
RepresentingWorld->PersistentLevel->OnLoadedActorRemovedFromLevelEvent.RemoveAll(this);
}
RepresentingWorld->OnWorldPartitionInitialized().RemoveAll(this);
RepresentingWorld->OnWorldPartitionUninitialized().RemoveAll(this);
if (UWorldPartition* WorldPartition = RepresentingWorld->GetWorldPartition())
{
WorldPartition->OnActorDescAddedEvent.RemoveAll(this);
WorldPartition->OnActorDescRemovedEvent.RemoveAll(this);
}
}
FWorldDelegates::LevelAddedToWorld.RemoveAll(this);
FWorldDelegates::LevelRemovedFromWorld.RemoveAll(this);
if (FActorFolders::IsAvailable())
{
auto& Folders = FActorFolders::Get();
Folders.OnFolderCreated.RemoveAll(this);
Folders.OnFolderMoved.RemoveAll(this);
Folders.OnFolderDeleted.RemoveAll(this);
}
}
static FFolder GetFolderPathFromActorDesc(UWorld* InWorld, const FWorldPartitionActorDesc* InActorDesc)
{
if (UWorld* OuterWorld = InActorDesc->GetContainer() ? InActorDesc->GetContainer()->GetTypedOuter<UWorld>() : InWorld)
{
ULevel* OuterLevel = OuterWorld->PersistentLevel;
if (OuterLevel->IsUsingActorFolders())
{
if (UActorFolder* ActorFolder = OuterLevel->GetActorFolder(InActorDesc->GetFolderGuid()))
{
return ActorFolder->GetFolder();
}
return FFolder::GetWorldRootFolder(OuterWorld).GetRootObject();
}
return FFolder(FFolder::GetWorldRootFolder(OuterWorld).GetRootObject(), InActorDesc->GetFolderPath());
}
return FFolder::GetInvalidFolder();
}
FSceneOutlinerTreeItemPtr FActorHierarchy::FindOrCreateParentItem(const ISceneOutlinerTreeItem& Item, const TMap<FSceneOutlinerTreeItemID, FSceneOutlinerTreeItemPtr>& Items, bool bCreate)
{
if (Item.IsA<FWorldTreeItem>())
{
return nullptr;
}
else if (const FActorTreeItem* ActorTreeItem = Item.CastTo<FActorTreeItem>())
{
if (AActor* Actor = ActorTreeItem->Actor.Get())
{
// Parent Actor (Actor attachement / parenting)
if (AActor* ParentActor = Actor->GetSceneOutlinerParent())
{
if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(ParentActor))
{
return *ParentItem;
}
// If Parent can be listed in SceneOutliner return nullptr so it gets created
else if (ParentActor->IsListedInSceneOutliner())
{
return bCreate ? Mode->CreateItemFor<FActorTreeItem>(ParentActor, true) : nullptr;
}
}
// Parent Folder
FFolder Folder = Actor->GetFolder();
if (Mode->ShouldShowFolders() && !Folder.IsNone())
{
if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(Folder))
{
return *ParentItem;
}
else
{
return bCreate ? Mode->CreateItemFor<FActorFolderTreeItem>(FActorFolderTreeItem(Folder, ActorTreeItem->Actor->GetWorld()), true) : nullptr;
}
}
// Parent Level Instance
if (ILevelInstanceInterface* OwningLevelInstance = Cast<ILevelInstanceInterface>(Folder.GetRootObjectPtr()))
{
const ILevelInstanceInterface* LevelInstance = Cast<ILevelInstanceInterface>(Actor);
const bool bIsAnEditingLevelInstance = LevelInstance ? LevelInstance->IsEditing() : false;
// Parent this to a LevelInstance if the parent LevelInstance is being edited or if this is a sub LevelInstance which is being edited
if (bShowingLevelInstances || (OwningLevelInstance->IsEditing() || bIsAnEditingLevelInstance))
{
AActor* OwningActor = CastChecked<AActor>(OwningLevelInstance);
if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(OwningActor))
{
return *ParentItem;
}
else
{
return bCreate ? Mode->CreateItemFor<FActorTreeItem>(OwningActor, true) : nullptr;
}
}
}
// Parent Level Using Actor Folders
ULevel* OwningLevel = Cast<ULevel>(Folder.GetRootObjectPtr());
// For the persistent level, fallback on the world
if (OwningLevel && !OwningLevel->IsPersistentLevel())
{
if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(OwningLevel))
{
return *ParentItem;
}
else
{
return bCreate ? Mode->CreateItemFor<FLevelTreeItem>(OwningLevel, true) : nullptr;
}
}
}
}
else if (const FActorFolderTreeItem* FolderItem = Item.CastTo<FActorFolderTreeItem>())
{
// We should never call FindParents on a folder item if folders are not being shown
check(Mode->ShouldShowFolders());
const FFolder ParentPath = FolderItem->GetFolder().GetParent();
// Parent Folder
if (!ParentPath.IsNone())
{
if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(ParentPath))
{
return *ParentItem;
}
else
{
return bCreate ? Mode->CreateItemFor<FActorFolderTreeItem>(FActorFolderTreeItem(ParentPath, FolderItem->World), true) : nullptr;
}
}
// Parent Level Instance
else if (ILevelInstanceInterface* OwningLevelInstance = Cast<ILevelInstanceInterface>(ParentPath.GetRootObjectPtr()))
{
if (bShowingLevelInstances || OwningLevelInstance->IsEditing())
{
AActor* OwningActor = CastChecked<AActor>(OwningLevelInstance);
if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(OwningActor))
{
return *ParentItem;
}
else
{
return bCreate ? Mode->CreateItemFor<FActorTreeItem>(OwningActor, true) : nullptr;
}
}
}
// Parent Level Using Actor Folders
else if (ULevel* OwningLevel = Cast<ULevel>(ParentPath.GetRootObjectPtr()))
{
// For the persistent level, fallback on the world
if (!OwningLevel->IsPersistentLevel())
{
if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(OwningLevel))
{
return *ParentItem;
}
else
{
return bCreate ? Mode->CreateItemFor<FLevelTreeItem>(OwningLevel, true) : nullptr;
}
}
}
}
else if (const FComponentTreeItem* ComponentTreeItem = Item.CastTo<FComponentTreeItem>())
{
if (AActor* Owner = ComponentTreeItem->Component->GetOwner())
{
if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(Owner))
{
return *ParentItem;
}
else
{
return bCreate ? Mode->CreateItemFor<FActorTreeItem>(Owner, true) : nullptr;
}
}
// do not default to world on Component items
return nullptr;
}
else if (const FActorDescTreeItem* ActorDescItem = Item.CastTo<FActorDescTreeItem>())
{
if (const FWorldPartitionActorDesc* ActorDesc = ActorDescItem->ActorDescHandle.Get())
{
const FFolder ActorDescFolder = GetFolderPathFromActorDesc(RepresentingWorld.Get(), ActorDesc);
if (!ActorDescFolder.IsNone())
{
if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(ActorDescFolder))
{
return *ParentItem;
}
else
{
return bCreate ? Mode->CreateItemFor<FActorFolderTreeItem>(FActorFolderTreeItem(ActorDescFolder, RepresentingWorld), true) : nullptr;
}
}
// Parent Actor (Actor attachement / parenting)
const FGuid& ParentActorGuid = ActorDesc->GetSceneOutlinerParent();
if (ParentActorGuid.IsValid())
{
if (UActorDescContainer* ActorDescContainer = ActorDesc->GetContainer())
{
if (const FWorldPartitionActorDesc* ParentActorDesc = ActorDescContainer->GetActorDesc(ParentActorGuid))
{
// If parent actor is loaded
if (AActor* ParentActor = ParentActorDesc->GetActor())
{
// Find loaded parent actor node (from the object ptr)
if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(ParentActor))
{
return *ParentItem;
}
else
{
return bCreate ? Mode->CreateItemFor<FActorTreeItem>(ParentActor, true) : nullptr;
}
}
// Find unloaded parent actor node (from the guid)
if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(ParentActorGuid))
{
return *ParentItem;
}
else
{
return bCreate ? Mode->CreateItemFor<FActorDescTreeItem>(FActorDescTreeItem(ParentActorGuid, ActorDescContainer)) : nullptr;
}
}
}
}
else if (UActorDescContainer* ActorDescContainer = ActorDesc->GetContainer())
{
const ULevelInstanceSubsystem* LevelInstanceSubsystem = UWorld::GetSubsystem<ULevelInstanceSubsystem>(RepresentingWorld.Get());
UWorld* OuterWorld = ActorDescContainer->GetTypedOuter<UWorld>();
// If parent actor is loaded
if (AActor* ParentActor = OuterWorld ? Cast<AActor>(LevelInstanceSubsystem->GetOwningLevelInstance(OuterWorld->PersistentLevel)) : nullptr)
{
if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(ParentActor))
{
return *ParentItem;
}
else
{
return bCreate ? Mode->CreateItemFor<FActorTreeItem>(ParentActor, true) : nullptr;
}
}
}
}
}
// If we get here return world item
if (const FSceneOutlinerTreeItemPtr* ParentItem = Items.Find(RepresentingWorld.Get()))
{
return *ParentItem;
}
else
{
return bCreate ? Mode->CreateItemFor<FWorldTreeItem>(RepresentingWorld.Get(), true) : nullptr;
}
}
void FActorHierarchy::CreateComponentItems(const AActor* Actor, TArray<FSceneOutlinerTreeItemPtr>& OutItems) const
{
check(Actor);
// Add all this actors components if showing components and the owning actor was created
if (bShowingComponents)
{
for (UActorComponent* Component : Actor->GetComponents())
{
if (Component != nullptr)
{
if (FSceneOutlinerTreeItemPtr ComponentItem = Mode->CreateItemFor<FComponentTreeItem>(Component))
{
OutItems.Add(ComponentItem);
}
}
}
}
}
void FActorHierarchy::CreateWorldChildren(UWorld* World, TArray<FSceneOutlinerTreeItemPtr>& OutItems) const
{
check(World);
if (Mode->ShouldShowFolders())
{
// Add any folders which might match the current search terms
FActorFolders::Get().ForEachFolder(*World, [this, &World, &OutItems](const FFolder& Folder)
{
if (FSceneOutlinerTreeItemPtr FolderItem = Mode->CreateItemFor<FActorFolderTreeItem>(FActorFolderTreeItem(Folder, World)))
{
OutItems.Add(FolderItem);
}
return true;
});
}
const ULevelInstanceSubsystem* LevelInstanceSubsystem = World->GetSubsystem<ULevelInstanceSubsystem>();
// Create all actor items
for (FActorIterator ActorIt(World); ActorIt; ++ActorIt)
{
AActor* Actor = *ActorIt;
// If we are not showing LevelInstances, LevelInstance sub actor items should not be created unless they belong to a LevelInstance which is being edited
if (LevelInstanceSubsystem)
{
if (const ILevelInstanceInterface* ParentLevelInstance = LevelInstanceSubsystem->GetParentLevelInstance(Actor))
{
if (!bShowingLevelInstances && !ParentLevelInstance->IsEditing())
{
continue;
}
}
}
if (FSceneOutlinerTreeItemPtr ActorItem = Mode->CreateItemFor<FActorTreeItem>(Actor))
{
if (bShowingOnlyActorWithValidComponents)
{
int32 InsertLocation = OutItems.Num();
// Create all component items
CreateComponentItems(Actor, OutItems);
if (OutItems.Num() != InsertLocation)
{
// Add the actor before the components
OutItems.Insert(ActorItem, InsertLocation);
}
}
else
{
OutItems.Add(ActorItem);
// Create all component items
CreateComponentItems(Actor, OutItems);
}
}
}
if (bShowingUnloadedActors)
{
if (UWorldPartitionSubsystem* WorldPartitionSubsystem = UWorld::GetSubsystem<UWorldPartitionSubsystem>(World))
{
WorldPartitionSubsystem->ForEachWorldPartition([this, LevelInstanceSubsystem, &OutItems](UWorldPartition* WorldPartition)
{
// Skip unloaded actors if they are part of a non editing level instance and the outliner hides the content of level instances
if (!bShowingLevelInstances)
{
UWorld* OuterWorld = WorldPartition->GetTypedOuter<UWorld>();
ULevel* OuterLevel = OuterWorld ? OuterWorld->PersistentLevel : nullptr;
ILevelInstanceInterface* LevelInstance = LevelInstanceSubsystem ? LevelInstanceSubsystem->GetOwningLevelInstance(OuterLevel) : nullptr;
if (LevelInstance && !LevelInstance->IsEditing())
{
return true;
}
}
FWorldPartitionHelpers::ForEachActorDesc(WorldPartition, [this, &OutItems](const FWorldPartitionActorDesc* ActorDesc)
{
if (ActorDesc != nullptr && !ActorDesc->IsLoaded(true))
{
if (const FSceneOutlinerTreeItemPtr ActorDescItem = Mode->CreateItemFor<FActorDescTreeItem>(FActorDescTreeItem(ActorDesc->GetGuid(), ActorDesc->GetContainer())))
{
OutItems.Add(ActorDescItem);
}
}
return true;
});
return true;
});
}
}
}
void FActorHierarchy::CreateItems(TArray<FSceneOutlinerTreeItemPtr>& OutItems) const
{
if (RepresentingWorld.IsValid())
{
UWorld* RepresentingWorldPtr = RepresentingWorld.Get();
check(RepresentingWorldPtr);
if (FSceneOutlinerTreeItemPtr WorldItem = Mode->CreateItemFor<FWorldTreeItem>(RepresentingWorldPtr))
{
OutItems.Add(WorldItem);
}
// Create world children regardless of if a world item was created
CreateWorldChildren(RepresentingWorldPtr, OutItems);
}
}
void FActorHierarchy::CreateChildren(const FSceneOutlinerTreeItemPtr& Item, TArray<FSceneOutlinerTreeItemPtr>& OutChildren) const
{
auto CreateChildrenFolders = [this](UWorld* InWorld, const FFolder& InParentFolder, const FFolder::FRootObject& InFolderRootObject, TArray<FSceneOutlinerTreeItemPtr>& OutChildren)
{
FActorFolders::Get().ForEachFolderWithRootObject(*InWorld, InFolderRootObject, [this, InWorld, &InParentFolder, &OutChildren](const FFolder& Folder)
{
if (Folder.IsChildOf(InParentFolder))
{
if (FSceneOutlinerTreeItemPtr NewFolderItem = Mode->CreateItemFor<FActorFolderTreeItem>(FActorFolderTreeItem(Folder, InWorld)))
{
OutChildren.Add(NewFolderItem);
}
}
return true;
});
};
UWorld* World = RepresentingWorld.Get();
if (FWorldTreeItem* WorldItem = Item->CastTo<FWorldTreeItem>())
{
check(WorldItem->World == RepresentingWorld);
CreateWorldChildren(WorldItem->World.Get(), OutChildren);
}
else if (const FActorTreeItem* ParentActorItem = Item->CastTo<FActorTreeItem>())
{
AActor* ParentActor = ParentActorItem->Actor.Get();
check(ParentActor->GetWorld() == RepresentingWorld);
CreateComponentItems(ParentActor, OutChildren);
TArray<AActor*> ChildActors;
if (const ILevelInstanceInterface* LevelInstanceParent = Cast<ILevelInstanceInterface>(ParentActor))
{
const ULevelInstanceSubsystem* LevelInstanceSubsystem = RepresentingWorld->GetSubsystem<ULevelInstanceSubsystem>();
check(LevelInstanceSubsystem);
LevelInstanceSubsystem->ForEachActorInLevelInstance(LevelInstanceParent, [this, LevelInstanceParent, LevelInstanceSubsystem, &ChildActors](AActor* SubActor)
{
const ILevelInstanceInterface* LevelInstance = Cast<ILevelInstanceInterface>(SubActor);
const bool bIsAnEditingLevelInstance = LevelInstance ? LevelInstanceSubsystem->IsEditingLevelInstance(LevelInstance) : false;
if (bShowingLevelInstances || (LevelInstanceSubsystem->IsEditingLevelInstance(LevelInstanceParent) || bIsAnEditingLevelInstance))
{
ChildActors.Add(SubActor);
}
return true;
});
check(World == CastChecked<AActor>(LevelInstanceParent)->GetWorld());
FFolder ParentFolder = ParentActor->GetFolder();
CreateChildrenFolders(World, ParentFolder, ParentActor, OutChildren);
}
else
{
TFunction<bool(AActor*)> GetAttachedActors = [&ChildActors, &GetAttachedActors](AActor* Child)
{
ChildActors.Add(Child);
Child->ForEachAttachedActors(GetAttachedActors);
// Always continue
return true;
};
// Grab all direct/indirect children of an actor
ParentActor->ForEachAttachedActors(GetAttachedActors);
}
for (auto ChildActor : ChildActors)
{
if (FSceneOutlinerTreeItemPtr ChildActorItem = Mode->CreateItemFor<FActorTreeItem>(ChildActor))
{
OutChildren.Add(ChildActorItem);
CreateComponentItems(ChildActor, OutChildren);
}
}
}
else if (FActorFolderTreeItem* FolderItem = Item->CastTo<FActorFolderTreeItem>())
{
check(Mode->ShouldShowFolders());
check(World == FolderItem->World.Get());
FFolder ParentFolder = FolderItem->GetFolder();
check(!ParentFolder.IsNone());
CreateChildrenFolders(World, ParentFolder, ParentFolder.GetRootObject(), OutChildren);
}
}
void FActorHierarchy::FullRefreshEvent()
{
FSceneOutlinerHierarchyChangedData EventData;
EventData.Type = FSceneOutlinerHierarchyChangedData::FullRefresh;
HierarchyChangedEvent.Broadcast(EventData);
}
void FActorHierarchy::OnWorldPartitionCreated(UWorld* InWorld)
{
if (RepresentingWorld.Get() == InWorld)
{
FullRefreshEvent();
}
}
void FActorHierarchy::OnLevelActorAdded(AActor* InActor)
{
if (InActor != nullptr && RepresentingWorld.Get() == InActor->GetWorld())
{
FSceneOutlinerHierarchyChangedData EventData;
EventData.Type = FSceneOutlinerHierarchyChangedData::Added;
EventData.Items.Add(Mode->CreateItemFor<FActorTreeItem>(InActor));
HierarchyChangedEvent.Broadcast(EventData);
}
}
void FActorHierarchy::OnLevelActorDeleted(AActor* InActor)
{
if (InActor != nullptr && RepresentingWorld.Get() == InActor->GetWorld())
{
FSceneOutlinerHierarchyChangedData EventData;
EventData.Type = FSceneOutlinerHierarchyChangedData::Removed;
EventData.ItemIDs.Add(InActor);
HierarchyChangedEvent.Broadcast(EventData);
}
}
void FActorHierarchy::OnLevelActorAttached(AActor* InActor, const AActor* InParent)
{
if (InActor != nullptr && RepresentingWorld.Get() == InActor->GetWorld())
{
FSceneOutlinerHierarchyChangedData EventData;
EventData.Type = FSceneOutlinerHierarchyChangedData::Moved;
EventData.ItemIDs.Add(InActor);
HierarchyChangedEvent.Broadcast(EventData);
}
}
void FActorHierarchy::OnLevelActorDetached(AActor* InActor, const AActor* InParent)
{
if (InActor != nullptr && RepresentingWorld.Get() == InActor->GetWorld())
{
FSceneOutlinerHierarchyChangedData EventData;
EventData.Type = FSceneOutlinerHierarchyChangedData::Moved;
EventData.ItemIDs.Add(InActor);
HierarchyChangedEvent.Broadcast(EventData);
}
}
void FActorHierarchy::OnLoadedActorAdded(AActor& InActor)
{
OnLevelActorAdded(&InActor);
FSceneOutlinerHierarchyChangedData EventData;
EventData.Type = FSceneOutlinerHierarchyChangedData::Removed;
EventData.ItemIDs.Add(InActor.GetActorGuid());
HierarchyChangedEvent.Broadcast(EventData);
}
void FActorHierarchy::OnLoadedActorRemoved(AActor& InActor)
{
OnLevelActorDeleted(&InActor);
if (bShowingUnloadedActors)
{
if (UWorldPartition* WorldPartition = RepresentingWorld->GetWorldPartition())
{
const FGuid& ActorGuid = InActor.GetActorGuid();
if (WorldPartition->GetActorDesc(ActorGuid) != nullptr)
{
FSceneOutlinerHierarchyChangedData EventData;
EventData.Type = FSceneOutlinerHierarchyChangedData::Added;
EventData.Items.Add(Mode->CreateItemFor<FActorDescTreeItem>(FActorDescTreeItem(ActorGuid, WorldPartition)));
HierarchyChangedEvent.Broadcast(EventData);
}
}
}
}
void FActorHierarchy::OnActorDescAdded(FWorldPartitionActorDesc* ActorDesc)
{
if (bShowingUnloadedActors && ActorDesc && !ActorDesc->IsLoaded(true))
{
if (UWorldPartition* WorldPartition = RepresentingWorld->GetWorldPartition())
{
FSceneOutlinerHierarchyChangedData EventData;
EventData.Type = FSceneOutlinerHierarchyChangedData::Added;
EventData.Items.Add(Mode->CreateItemFor<FActorDescTreeItem>(FActorDescTreeItem(ActorDesc->GetGuid(), WorldPartition)));
HierarchyChangedEvent.Broadcast(EventData);
}
}
}
void FActorHierarchy::OnActorDescRemoved(FWorldPartitionActorDesc* ActorDesc)
{
if (bShowingUnloadedActors && ActorDesc)
{
FSceneOutlinerHierarchyChangedData EventData;
EventData.Type = FSceneOutlinerHierarchyChangedData::Removed;
EventData.ItemIDs.Add(ActorDesc->GetGuid());
HierarchyChangedEvent.Broadcast(EventData);
}
}
void FActorHierarchy::OnWorldPartitionInitialized(UWorldPartition* InWorldPartition)
{
FullRefreshEvent();
}
void FActorHierarchy::OnWorldPartitionUninitialized(UWorldPartition* InWorldPartition)
{
FullRefreshEvent();
}
void FActorHierarchy::OnComponentsUpdated()
{
FullRefreshEvent();
}
void FActorHierarchy::OnLevelActorListChanged()
{
FullRefreshEvent();
}
void FActorHierarchy::OnActorFoldersUpdatedEvent(ULevel* InLevel)
{
FullRefreshEvent();
}
void FActorHierarchy::OnActorFolderAdded(UActorFolder* InActorFolder)
{
check(InActorFolder);
ULevel* Level = InActorFolder->GetOuterULevel();
if (Level && Mode->ShouldShowFolders() && (RepresentingWorld.Get() == Level->GetWorld()))
{
FSceneOutlinerHierarchyChangedData EventData;
EventData.Type = FSceneOutlinerHierarchyChangedData::Added;
EventData.Items.Add(Mode->CreateItemFor<FActorFolderTreeItem>(FActorFolderTreeItem(InActorFolder->GetFolder(), RepresentingWorld)));
HierarchyChangedEvent.Broadcast(EventData);
}
}
void FActorHierarchy::OnLevelAdded(ULevel* InLevel, UWorld* InWorld)
{
if (InLevel != nullptr && RepresentingWorld.Get() == InWorld)
{
FSceneOutlinerHierarchyChangedData EventData;
EventData.Type = FSceneOutlinerHierarchyChangedData::Added;
EventData.Items.Reserve(InLevel->Actors.Num());
for (AActor* Actor : InLevel->Actors)
{
if (Actor != nullptr)
{
EventData.Items.Add(Mode->CreateItemFor<FActorTreeItem>(Actor));
}
}
HierarchyChangedEvent.Broadcast(EventData);
}
}
void FActorHierarchy::OnLevelRemoved(ULevel* InLevel, UWorld* InWorld)
{
if (InLevel != nullptr && RepresentingWorld.Get() == InWorld)
{
{
FSceneOutlinerHierarchyChangedData EventData;
EventData.Type = FSceneOutlinerHierarchyChangedData::Removed;
EventData.ItemIDs.Reserve(InLevel->Actors.Num());
for (AActor* Actor : InLevel->Actors)
{
if (Actor != nullptr)
{
EventData.ItemIDs.Add(Actor);
}
}
HierarchyChangedEvent.Broadcast(EventData);
}
// If either this level or the owning world are using actor folders, remove level's actor folders
if (InLevel->IsUsingActorFolders() || InWorld->PersistentLevel->IsUsingActorFolders())
{
FSceneOutlinerHierarchyChangedData EventData;
EventData.Type = FSceneOutlinerHierarchyChangedData::Removed;
EventData.ItemIDs.Add(InLevel);
HierarchyChangedEvent.Broadcast(EventData);
}
}
}
/** Called when a folder is to be created */
void FActorHierarchy::OnBroadcastFolderCreate(UWorld& InWorld, const FFolder& InNewFolder)
{
if (Mode->ShouldShowFolders() && RepresentingWorld.Get() == &InWorld)
{
FSceneOutlinerHierarchyChangedData EventData;
EventData.Type = FSceneOutlinerHierarchyChangedData::Added;
EventData.Items.Add(Mode->CreateItemFor<FActorFolderTreeItem>(FActorFolderTreeItem(InNewFolder, &InWorld)));
EventData.ItemActions = SceneOutliner::ENewItemAction::Select | SceneOutliner::ENewItemAction::Rename;
HierarchyChangedEvent.Broadcast(EventData);
}
}
/** Called when a folder is to be moved */
void FActorHierarchy::OnBroadcastFolderMove(UWorld& InWorld, const FFolder& InOldFolder, const FFolder& InNewFolder)
{
if (Mode->ShouldShowFolders() && RepresentingWorld.Get() == &InWorld)
{
FSceneOutlinerHierarchyChangedData EventData;
EventData.Type = FSceneOutlinerHierarchyChangedData::FolderMoved;
EventData.ItemIDs.Add(InOldFolder);
EventData.NewPaths.Add(InNewFolder);
HierarchyChangedEvent.Broadcast(EventData);
}
}
/** Called when a folder is to be deleted */
void FActorHierarchy::OnBroadcastFolderDelete(UWorld& InWorld, const FFolder& InFolder)
{
if (Mode->ShouldShowFolders() && RepresentingWorld.Get() == &InWorld)
{
FSceneOutlinerHierarchyChangedData EventData;
EventData.Type = FSceneOutlinerHierarchyChangedData::Removed;
EventData.ItemIDs.Add(InFolder);
HierarchyChangedEvent.Broadcast(EventData);
}
}
void FActorHierarchy::OnLevelActorFolderChanged(const AActor* InActor, FName OldPath)
{
if (Mode->ShouldShowFolders() && RepresentingWorld.Get() == InActor->GetWorld())
{
FSceneOutlinerHierarchyChangedData EventData;
EventData.Type = FSceneOutlinerHierarchyChangedData::Moved;
EventData.ItemIDs.Add(FSceneOutlinerTreeItemID(InActor));
HierarchyChangedEvent.Broadcast(EventData);
}
}