Files
UnrealEngine/Engine/Source/Editor/SceneOutliner/Private/SceneOutlinerHelpers.cpp
2025-05-18 13:04:45 +08:00

229 lines
6.4 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SceneOutlinerHelpers.h"
#include "ActorTreeItem.h"
#include "ActorDescTreeItem.h"
#include "ActorFolderTreeItem.h"
#include "ComponentTreeItem.h"
#include "EditorActorFolders.h"
#include "EditorClassUtils.h"
#include "Engine/Blueprint.h"
#include "Misc/PackageName.h"
#include "Modules/ModuleManager.h"
#include "WorldPartition/WorldPartitionActorDescInstance.h"
#include "UObject/Package.h"
#define LOCTEXT_NAMESPACE "SceneOutlinerHelpers"
namespace SceneOutliner
{
FString FSceneOutlinerHelpers::GetExternalPackageName(const ISceneOutlinerTreeItem& TreeItem)
{
if (const FActorTreeItem* ActorItem = TreeItem.CastTo<FActorTreeItem>())
{
if (AActor* Actor = ActorItem->Actor.Get())
{
if (Actor->IsPackageExternal())
{
return Actor->GetExternalPackage()->GetName();
}
}
}
else if (const FActorFolderTreeItem* ActorFolderItem = TreeItem.CastTo<FActorFolderTreeItem>())
{
if (const UActorFolder* ActorFolder = ActorFolderItem->GetActorFolder())
{
if (ActorFolder->IsPackageExternal())
{
return ActorFolder->GetExternalPackage()->GetName();
}
}
}
else if (const FActorDescTreeItem* ActorDescItem = TreeItem.CastTo<FActorDescTreeItem>())
{
if (const FWorldPartitionActorDescInstance* ActorDescInstance = *ActorDescItem->ActorDescHandle)
{
return ActorDescInstance->GetActorPackage().ToString();
}
}
return FString();
}
UPackage* FSceneOutlinerHelpers::GetExternalPackage(const ISceneOutlinerTreeItem& TreeItem)
{
if (const FActorTreeItem* ActorItem = TreeItem.CastTo<FActorTreeItem>())
{
if (AActor* Actor = ActorItem->Actor.Get())
{
if (Actor->IsPackageExternal())
{
return Actor->GetExternalPackage();
}
}
}
else if (const FActorFolderTreeItem* ActorFolderItem = TreeItem.CastTo<FActorFolderTreeItem>())
{
if (const UActorFolder* ActorFolder = ActorFolderItem->GetActorFolder())
{
if (ActorFolder->IsPackageExternal())
{
return ActorFolder->GetExternalPackage();
}
}
}
else if (const FActorDescTreeItem* ActorDescItem = TreeItem.CastTo<FActorDescTreeItem>())
{
if (const FWorldPartitionActorDescInstance* ActorDescInstance = *ActorDescItem->ActorDescHandle)
{
return FindPackage(nullptr, *ActorDescInstance->GetActorPackage().ToString());
}
}
return nullptr;
}
TSharedPtr<SWidget> FSceneOutlinerHelpers::GetClassHyperlink(UObject* InObject)
{
if (InObject)
{
if (UClass* Class = InObject->GetClass())
{
// Always show blueprints
const bool bIsBlueprintClass = UBlueprint::GetBlueprintFromClass(Class) != nullptr;
// Also show game or game plugin native classes (but not engine classes as that makes the scene outliner pretty noisy)
bool bIsGameClass = false;
if (!bIsBlueprintClass)
{
UPackage* Package = Class->GetOutermost();
const FString ModuleName = FPackageName::GetShortName(Package->GetFName());
FModuleStatus PackageModuleStatus;
if (FModuleManager::Get().QueryModule(*ModuleName, /*out*/ PackageModuleStatus))
{
bIsGameClass = PackageModuleStatus.bIsGameModule;
}
}
if (bIsBlueprintClass || bIsGameClass)
{
FEditorClassUtils::FSourceLinkParams SourceLinkParams;
SourceLinkParams.Object = InObject;
SourceLinkParams.bUseDefaultFormat = true;
return FEditorClassUtils::GetSourceLink(Class, SourceLinkParams);
}
}
}
return nullptr;
}
void FSceneOutlinerHelpers::PopulateExtraSearchStrings(const ISceneOutlinerTreeItem& TreeItem, TArray< FString >& OutSearchStrings)
{
// For components, we want them to be searchable by the actor name if they request so. This is so you can search by actors in component
// pickers without the actual components themselves being filtered out.
if (const FComponentTreeItem* ComponentTreeItem = TreeItem.CastTo<FComponentTreeItem>())
{
if (ComponentTreeItem->GetSearchComponentByActorName())
{
if (const UActorComponent* Component = ComponentTreeItem->Component.Get())
{
if (const AActor* Owner = Component->GetOwner())
{
constexpr bool bCreateIfNone = false;
OutSearchStrings.Add(Owner->GetActorLabel(bCreateIfNone));
}
}
}
}
}
void FSceneOutlinerHelpers::RenameFolder(const FFolder& InFolder, const FText& NewFolderName, UWorld* World)
{
if (!World)
{
return;
}
FName NewPath = InFolder.GetParent().GetPath();
if (NewPath.IsNone())
{
NewPath = FName(*NewFolderName.ToString());
}
else
{
NewPath = FName(*(NewPath.ToString() / NewFolderName.ToString()));
}
const FFolder TreeItemNewFolder(InFolder.GetRootObject(), NewPath);
FActorFolders::Get().RenameFolderInWorld(*World, InFolder, TreeItemNewFolder);
}
bool FSceneOutlinerHelpers::ValidateFolderName(const FFolder& InFolder, UWorld* World, const FText& InLabel, FText& OutErrorMessage)
{
const FText TrimmedLabel = FText::TrimPrecedingAndTrailing(InLabel);
if (TrimmedLabel.IsEmpty())
{
OutErrorMessage = LOCTEXT("RenameFailed_LeftBlank", "Names cannot be left blank");
return false;
}
if (TrimmedLabel.ToString().Len() >= NAME_SIZE)
{
FFormatNamedArguments Arguments;
Arguments.Add(TEXT("CharCount"), NAME_SIZE);
OutErrorMessage = FText::Format(LOCTEXT("RenameFailed_TooLong", "Names must be less than {CharCount} characters long."), Arguments);
return false;
}
const FString LabelString = TrimmedLabel.ToString();
if (InFolder.GetLeafName().ToString() == LabelString)
{
return true;
}
int32 Dummy = 0;
if (LabelString.FindChar('/', Dummy) || LabelString.FindChar('\\', Dummy))
{
OutErrorMessage = LOCTEXT("RenameFailed_InvalidChar", "Folder names cannot contain / or \\.");
return false;
}
// Validate that this folder doesn't exist already
FName NewPath = InFolder.GetParent().GetPath();
if (NewPath.IsNone())
{
NewPath = FName(*LabelString);
}
else
{
NewPath = FName(*(NewPath.ToString() / LabelString));
}
FFolder NewFolder(InFolder.GetRootObject(), NewPath);
if (World && FActorFolders::Get().ContainsFolder(*World, NewFolder))
{
OutErrorMessage = LOCTEXT("RenameFailed_AlreadyExists", "A folder with this name already exists at this level");
return false;
}
return true;
}
bool FSceneOutlinerHelpers::IsFolderCurrent(const FFolder& InFolder, UWorld* World)
{
if (World)
{
return FActorFolders::Get().GetActorEditorContextFolder(*World) == InFolder;
}
return false;
}
}
#undef LOCTEXT_NAMESPACE