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

307 lines
11 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"
#include "ISourceControlWindowExtenderModule.h"
#include "ISourceControlWindowsModule.h"
#include "SourceControlMenuContext.h"
#include "SourceControlHelpers.h"
#include "ToolMenus.h"
#include "AssetRegistry/AssetData.h"
#include "GameFramework/Actor.h"
#include "Editor.h"
#include "Selection.h"
#include "ScopedTransaction.h"
#include "WorldPartition/WorldPartition.h"
#include "WorldPartition/WorldPartitionActorDesc.h"
#include "WorldPartition/WorldPartitionActorDescUtils.h"
#define LOCTEXT_NAMESPACE "SourceControlWindowExtender"
/**
* SourceControlWindowExtender module
*/
class FSourceControlWindowExtenderModule : public ISourceControlWindowExtenderModule
{
public:
/**
* Called right after the module DLL has been loaded and the module object has been created
*/
virtual void StartupModule() override;
/**
* Called before the module is unloaded, right before the module object is destroyed.
*/
virtual void ShutdownModule() override;
private:
void ExtendMenu();
bool CanPinActors() const { return CurrentWorldUnloadedActors.Num() > 0; }
void PinActors();
void PinActors(const TArray<FAssetData>& ActorsToPin);
bool CanSelectActors() const { return CurrentWorldLoadedActors.Num() > 0; }
void SelectActors();
void SelectActors(const TArray<FAssetData>& ActorsToSelect);
void BrowseToAssets();
void BrowseToAssets(const TArray<FAssetData>& Assets);
void FocusActors(const TArray<FAssetData>& ActorToFocus, bool bSelect);
bool CanFocusActors() const { return CurrentWorldLoadedActors.Num() > 0 || CurrentWorldUnloadedActors.Num() > 0; }
void FocusActors();
void OnChangelistFileDoubleClicked(const FString& Filename);
void GetAssetsFromFilenames(const TArray<FString>& Filenames, TArray<FAssetData>& OutNonActorAssets, TArray<FAssetData>& OutCurrentWorldLoadedActors, TArray<FAssetData>& OutCurrentWorldUnloadedActors);
TArray<FAssetData> SelectedAssets;
TArray<FAssetData> CurrentWorldLoadedActors;
TArray<FAssetData> CurrentWorldUnloadedActors;
FDelegateHandle EventHandle;
};
IMPLEMENT_MODULE(FSourceControlWindowExtenderModule, SourceControlWindowExtender);
void FSourceControlWindowExtenderModule::StartupModule()
{
ExtendMenu();
EventHandle = ISourceControlWindowsModule::Get().OnChangelistFileDoubleClicked().AddRaw(this, &FSourceControlWindowExtenderModule::OnChangelistFileDoubleClicked);
}
void FSourceControlWindowExtenderModule::ShutdownModule()
{
if (ISourceControlWindowsModule* Module = ISourceControlWindowsModule::TryGet())
{
Module->OnChangelistFileDoubleClicked().Remove(EventHandle);
}
}
void FSourceControlWindowExtenderModule::GetAssetsFromFilenames(const TArray<FString>& Filenames, TArray<FAssetData>& OutNonActorAssets, TArray<FAssetData>& OutCurrentWorldLoadedActors, TArray<FAssetData>& OutCurrentWorldUnloadedActors)
{
UWorld* CurrentWorld = GEditor->GetEditorWorldContext().World();
for (const FString& Filename : Filenames)
{
TArray<FAssetData> OutAssets;
if (SourceControlHelpers::GetAssetData(Filename, OutAssets) && OutAssets.Num() == 1)
{
const FAssetData& AssetData = OutAssets[0];
if (TSubclassOf<AActor> ActorClass = AssetData.GetClass())
{
if (CurrentWorld && AssetData.GetObjectPathString().StartsWith(CurrentWorld->GetPathName()))
{
if (AssetData.IsAssetLoaded())
{
OutCurrentWorldLoadedActors.Add(AssetData);
}
else
{
OutCurrentWorldUnloadedActors.Add(AssetData);
}
}
else
{
TArray<FAssetData> OutWorldAsset;
FString AssetPathName = AssetData.ToSoftObjectPath().GetLongPackageName();
if (SourceControlHelpers::GetAssetDataFromPackage(AssetPathName, OutWorldAsset) && OutWorldAsset.Num() == 1)
{
OutNonActorAssets.Add(OutWorldAsset[0]);
}
}
}
else
{
OutNonActorAssets.Add(AssetData);
}
}
}
}
void FSourceControlWindowExtenderModule::ExtendMenu()
{
UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("SourceControl.ChangelistContextMenu");
FToolMenuSection& Section = Menu->AddDynamicSection("SourceControlWindowExtenderSection", FNewToolMenuDelegate::CreateLambda([this](UToolMenu* InMenu)
{
if (USourceControlMenuContext* MenuContext = InMenu->FindContext<USourceControlMenuContext>())
{
SelectedAssets.Empty();
CurrentWorldLoadedActors.Empty();
CurrentWorldUnloadedActors.Empty();
GetAssetsFromFilenames(MenuContext->SelectedFiles, SelectedAssets, CurrentWorldLoadedActors, CurrentWorldUnloadedActors);
auto FindOrAddExtendSection = [InMenu]() -> FToolMenuSection*
{
FToolMenuSection* ExtendSection = InMenu->FindSection("SourceControlWindowExtender");
if (!ExtendSection)
{
ExtendSection = &InMenu->AddSection("SourceControlWindowExtender", TAttribute<FText>(), FToolMenuInsert("Source Control", EToolMenuInsertType::After));
ExtendSection->AddSeparator(NAME_None);
}
return ExtendSection;
};
if (CurrentWorldUnloadedActors.Num() > 0 || CurrentWorldLoadedActors.Num() > 0)
{
FindOrAddExtendSection()->AddSubMenu(TEXT("Actors"),
LOCTEXT("ActorSubMenu", "Actors"),
FText(),
FNewToolMenuChoice(FNewMenuDelegate::CreateLambda([this](FMenuBuilder& MenuBuilder)
{
MenuBuilder.AddMenuEntry(LOCTEXT("PinActors", "Pin"), LOCTEXT("PinActors_Tooltip", "Load actors"),
FSlateIcon(), FUIAction(FExecuteAction::CreateRaw(this, &FSourceControlWindowExtenderModule::PinActors), FCanExecuteAction::CreateRaw(this, &FSourceControlWindowExtenderModule::CanPinActors)));
MenuBuilder.AddMenuEntry(LOCTEXT("SelectActors", "Select"), LOCTEXT("SelectActors_Tooltip", "Select actors"),
FSlateIcon(), FUIAction(FExecuteAction::CreateRaw(this, &FSourceControlWindowExtenderModule::SelectActors), FCanExecuteAction::CreateRaw(this, &FSourceControlWindowExtenderModule::CanSelectActors)));
MenuBuilder.AddMenuEntry(LOCTEXT("FocusActors", "Focus"), LOCTEXT("FocusActors_Tooltip", "Focus actors"),
FSlateIcon(), FUIAction(FExecuteAction::CreateRaw(this, &FSourceControlWindowExtenderModule::FocusActors), FCanExecuteAction::CreateRaw(this, &FSourceControlWindowExtenderModule::CanFocusActors)));
})));
}
if (SelectedAssets.Num() > 0)
{
FindOrAddExtendSection()->AddSubMenu(TEXT("Assets"),
LOCTEXT("AssetSubMenu", "Assets"),
FText(),
FNewToolMenuChoice(FNewMenuDelegate::CreateLambda([this](FMenuBuilder& MenuBuilder)
{
MenuBuilder.BeginSection(NAME_None);
MenuBuilder.AddMenuEntry(LOCTEXT("BrowseToAssets", "Browse to Asset"), LOCTEXT("BrowseToAssets_Tooltip", "Browse to Asset in Content Browser"), FSlateIcon(FAppStyle::GetAppStyleSetName(), "SystemWideCommands.FindInContentBrowser.Small"), FUIAction(FExecuteAction::CreateRaw(this, &FSourceControlWindowExtenderModule::BrowseToAssets)));
MenuBuilder.EndSection();
})));
}
}
}));
}
void FSourceControlWindowExtenderModule::PinActors()
{
PinActors(CurrentWorldUnloadedActors);
}
void FSourceControlWindowExtenderModule::PinActors(const TArray<FAssetData>& ActorsToPin)
{
UWorld* CurrentWorld = GEditor->GetEditorWorldContext().World();
check(CurrentWorld);
if (UWorldPartition* WorldPartition = CurrentWorld->GetWorldPartition())
{
TArray<FGuid> ActorGuids;
for (const FAssetData& ActorToPin : ActorsToPin)
{
if(TUniquePtr<FWorldPartitionActorDesc> ActorDesc = FWorldPartitionActorDescUtils::GetActorDescriptorFromAssetData(ActorToPin))
{
ActorGuids.Add(ActorDesc->GetGuid());
}
}
if (!ActorGuids.IsEmpty())
{
WorldPartition->PinActors(ActorGuids);
SelectActors(CurrentWorldUnloadedActors);
}
}
}
void FSourceControlWindowExtenderModule::SelectActors()
{
SelectActors(CurrentWorldLoadedActors);
}
void FSourceControlWindowExtenderModule::SelectActors(const TArray<FAssetData>& ActorsToSelect)
{
const FScopedTransaction Transaction(LOCTEXT("SelectActorsFromChangelist", "Select Actor(s)"));
UWorld* CurrentWorld = GEditor->GetEditorWorldContext().World();
check(CurrentWorld);
GEditor->GetSelectedActors()->BeginBatchSelectOperation();
bool bNotify = false;
const bool bDeselectBSPSurfs = true;
GEditor->SelectNone(bNotify, bDeselectBSPSurfs);
for (const FAssetData& ActorToSelect : ActorsToSelect)
{
if (AActor* Actor = Cast<AActor>(ActorToSelect.FastGetAsset()))
{
const bool bSelected = true;
GEditor->SelectActor(Actor, bSelected, bNotify);
}
}
bNotify = true;
GEditor->GetSelectedActors()->EndBatchSelectOperation(bNotify);
}
void FSourceControlWindowExtenderModule::BrowseToAssets()
{
BrowseToAssets(SelectedAssets);
}
void FSourceControlWindowExtenderModule::BrowseToAssets(const TArray<FAssetData>& Assets)
{
GEditor->SyncBrowserToObjects(const_cast<TArray<FAssetData>&>(Assets));
}
void FSourceControlWindowExtenderModule::FocusActors()
{
TArray<FAssetData> ActorsToFocus;
ActorsToFocus.Append(CurrentWorldLoadedActors);
ActorsToFocus.Append(CurrentWorldUnloadedActors);
const bool bSelectActors = false;
FocusActors(ActorsToFocus, bSelectActors);
}
void FSourceControlWindowExtenderModule::FocusActors(const TArray<FAssetData>& ActorsToFocus, bool bSelectActors)
{
FBox FocusBounds(EForceInit::ForceInit);
UWorld* CurrentWorld = GEditor->GetEditorWorldContext().World();
check(CurrentWorld);
for (const FAssetData& ActorToFocus : ActorsToFocus)
{
if (TUniquePtr<FWorldPartitionActorDesc> ActorDesc = FWorldPartitionActorDescUtils::GetActorDescriptorFromAssetData(ActorToFocus))
{
const FBox EditorBounds = ActorDesc->GetEditorBounds();
if (EditorBounds.IsValid)
{
FocusBounds += EditorBounds;
}
}
}
if (FocusBounds.IsValid)
{
const bool bActiveViewportOnly = true;
const float TimeInSeconds = 0.5f;
GEditor->MoveViewportCamerasToBox(FocusBounds, bActiveViewportOnly, TimeInSeconds);
}
if (bSelectActors)
{
SelectActors(ActorsToFocus);
}
}
void FSourceControlWindowExtenderModule::OnChangelistFileDoubleClicked(const FString& Filename)
{
TArray<FAssetData> OutNonActorAssets;
TArray<FAssetData> OutCurrentWorldLoadedActors;
TArray<FAssetData> OutCurrentWorldUnloadedActors;
GetAssetsFromFilenames({ Filename }, OutNonActorAssets, OutCurrentWorldLoadedActors, OutCurrentWorldUnloadedActors);
if (OutNonActorAssets.Num() > 0)
{
BrowseToAssets(OutNonActorAssets);
}
else if (OutCurrentWorldUnloadedActors.Num() > 0)
{
const bool bSelectActors = false;
FocusActors(OutCurrentWorldUnloadedActors, bSelectActors);
}
else if (OutCurrentWorldLoadedActors.Num() > 0)
{
const bool bSelectActors = true;
FocusActors(OutCurrentWorldLoadedActors, bSelectActors);
}
}
#undef LOCTEXT_NAMESPACE