Files
UnrealEngineUWP/Engine/Source/Editor/UnrealEd/Private/EditorClassUtils.cpp
dave belanger 4b72e06fd5 Disable code navigation when C++ is not allowed in the editor
#rb Rex.Hill
#preflight 6202c1f2e85c7a08bbf3987b

#ROBOMERGE-OWNER: dave.belanger
#ROBOMERGE-AUTHOR: dave.belanger
#ROBOMERGE-SOURCE: CL 18918552 via CL 18918618 via CL 18918624 via CL 18918631 via CL 18922663 via CL 18923570
#ROBOMERGE-BOT: UE5 (Release-Engine-Test -> Main) (v916-18915374)

[CL 18923679 by dave belanger in ue5-main branch]
2022-02-09 15:28:22 -05:00

342 lines
10 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "EditorClassUtils.h"
#include "HAL/FileManager.h"
#include "Widgets/Layout/SSpacer.h"
#include "EditorStyleSet.h"
#include "Engine/Blueprint.h"
#include "Editor.h"
#include "AssetRegistryModule.h"
#include "IDocumentationPage.h"
#include "IDocumentation.h"
#include "SourceCodeNavigation.h"
#include "Widgets/Input/SHyperlink.h"
#include "BlueprintEditorSettings.h"
FString FEditorClassUtils::GetDocumentationPage(const UClass* Class)
{
return (Class ? FString::Printf( TEXT("Shared/Types/%s%s"), Class->GetPrefixCPP(), *Class->GetName() ) : FString());
}
FString FEditorClassUtils::GetDocumentationExcerpt(const UClass* Class)
{
return (Class ? FString::Printf( TEXT("%s%s"), Class->GetPrefixCPP(), *Class->GetName() ) : FString());
}
TSharedRef<SToolTip> FEditorClassUtils::GetTooltip(const UClass* Class)
{
return (Class ? GetTooltip(Class, Class->GetToolTipText(GetDefault<UBlueprintEditorSettings>()->bShowShortTooltips)) : SNew(SToolTip));
}
TSharedRef<SToolTip> FEditorClassUtils::GetTooltip(const UClass* Class, const TAttribute<FText>& OverrideText)
{
return (Class ? IDocumentation::Get()->CreateToolTip(OverrideText, nullptr, GetDocumentationPage(Class), GetDocumentationExcerpt(Class)) : SNew(SToolTip));
}
FString FEditorClassUtils::GetDocumentationLinkFromExcerpt(const FString& DocLink, const FString DocExcerpt)
{
FString DocumentationLink;
TSharedRef<IDocumentation> Documentation = IDocumentation::Get();
if (Documentation->PageExists(DocLink))
{
TSharedRef<IDocumentationPage> ClassDocs = Documentation->GetPage(DocLink, NULL);
FExcerpt Excerpt;
if (ClassDocs->GetExcerpt(DocExcerpt, Excerpt))
{
FString* FullDocumentationLink = Excerpt.Variables.Find(TEXT("ToolTipFullLink"));
if (FullDocumentationLink)
{
DocumentationLink = *FullDocumentationLink;
}
}
}
return DocumentationLink;
}
FString FEditorClassUtils::GetDocumentationLink(const UClass* Class, const FString& OverrideExcerpt)
{
const FString ClassDocsPage = GetDocumentationPage(Class);
const FString ExcerptSection = (OverrideExcerpt.IsEmpty() ? GetDocumentationExcerpt(Class) : OverrideExcerpt);
return GetDocumentationLinkFromExcerpt(ClassDocsPage, ExcerptSection);
}
TSharedRef<SWidget> FEditorClassUtils::GetDocumentationLinkWidget(const UClass* Class)
{
TSharedRef<SWidget> DocLinkWidget = SNullWidget::NullWidget;
const FString DocumentationLink = GetDocumentationLink(Class);
if (!DocumentationLink.IsEmpty())
{
DocLinkWidget = IDocumentation::Get()->CreateAnchor(DocumentationLink);
}
return DocLinkWidget;
}
TSharedRef<SWidget> FEditorClassUtils::GetDynamicDocumentationLinkWidget(const TAttribute<const UClass*>& ClassAttribute)
{
auto GetLink = [ClassAttribute]()
{
return GetDocumentationLink(ClassAttribute.Get(nullptr));
};
return IDocumentation::Get()->CreateAnchor(TAttribute<FString>::CreateLambda(GetLink));
}
TSharedRef<SWidget> FEditorClassUtils::GetSourceLink(const UClass* Class, const FSourceLinkParams& Params)
{
TSharedRef<SWidget> Link = SNew(SSpacer);
UBlueprint* Blueprint = (Class ? Cast<UBlueprint>(Class->ClassGeneratedBy) : nullptr);
if (Blueprint)
{
struct Local
{
static void OnEditBlueprintClicked(TWeakObjectPtr<UBlueprint> InBlueprint, TWeakObjectPtr<UObject> InAsset)
{
if (UBlueprint* BlueprintToEdit = InBlueprint.Get())
{
// Set the object being debugged if given an actor reference (if we don't do this before we edit the object the editor wont know we are debugging something)
if (UObject* Asset = InAsset.Get())
{
check(Asset->GetClass()->ClassGeneratedBy == BlueprintToEdit);
BlueprintToEdit->SetObjectBeingDebugged(Asset);
}
// Open the blueprint
GEditor->EditObject(BlueprintToEdit);
}
}
};
TWeakObjectPtr<UBlueprint> BlueprintPtr = Blueprint;
FText Text = FText::FromName(Blueprint->GetFName());
if (Params.BlueprintFormat)
{
Text = FText::Format(*Params.BlueprintFormat, Text);
}
else if (Params.bUseDefaultFormat)
{
const FText DefaultBlueprintFormat = NSLOCTEXT("SourceHyperlink", "EditBlueprint", "Edit {0}");
Text = FText::Format(DefaultBlueprintFormat, Text);
}
Link = SNew(SHyperlink)
.Style(FEditorStyle::Get(), "Common.GotoBlueprintHyperlink")
.OnNavigate_Static(&Local::OnEditBlueprintClicked, BlueprintPtr, Params.Object)
.Text(Text)
.ToolTipText(NSLOCTEXT("SourceHyperlink", "EditBlueprint_ToolTip", "Click to edit the blueprint"));
}
else if (Class)
{
TWeakObjectPtr<const UClass> WeakClassPtr(Class);
auto OnNavigateToClassCode = [WeakClassPtr]()
{
if (const UClass* Class = WeakClassPtr.Get())
{
FSourceCodeNavigation::NavigateToClass(Class);
}
};
auto CanNavigateToClassCode = [WeakClassPtr]()
{
if (const UClass* Class = WeakClassPtr.Get())
{
return FSourceCodeNavigation::CanNavigateToClass(Class);
}
return false;
};
FText ClassNameText = FText::FromName(Class->GetFName());
FText FormattedText;
if (Params.CodeFormat)
{
FormattedText = FText::Format(*Params.CodeFormat, ClassNameText);
}
else if (Params.bUseDefaultFormat)
{
const FText DefaultCodeFormat = NSLOCTEXT("SourceHyperlink", "GoToCode", "Open {0}");
FormattedText = FText::Format(DefaultCodeFormat, ClassNameText);
}
else
{
FormattedText = ClassNameText;
}
TSharedRef<SWidget> NoLinkWidget = SNullWidget::NullWidget;
if (Params.bEmptyIfNoLink)
{
NoLinkWidget = SNew(SSpacer)
.Visibility_Lambda([CanNavigateToClassCode]() { return CanNavigateToClassCode() ? EVisibility::Collapsed : EVisibility::Visible; });
}
else
{
NoLinkWidget = SNew(STextBlock)
.Text(Params.bUseFormatIfNoLink ? FormattedText : ClassNameText)
.ColorAndOpacity(FSlateColor::UseSubduedForeground())
.Visibility_Lambda([CanNavigateToClassCode]() { return CanNavigateToClassCode() ? EVisibility::Collapsed : EVisibility::Visible; });
}
Link = SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
[
SNew(SHyperlink)
.Style(FEditorStyle::Get(), "Common.GotoNativeCodeHyperlink")
.OnNavigate_Lambda(OnNavigateToClassCode)
.Text(FormattedText)
.ToolTipText(FText::Format(NSLOCTEXT("SourceHyperlink", "GoToCode_ToolTip", "Click to open this source file in {0}"), FSourceCodeNavigation::GetSelectedSourceCodeIDE()))
.Visibility_Lambda([CanNavigateToClassCode]() { return CanNavigateToClassCode() ? EVisibility::Visible : EVisibility::Collapsed; })
]
+ SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
[
NoLinkWidget
];
}
return Link;
}
TSharedRef<SWidget> FEditorClassUtils::GetSourceLink(const UClass* Class, const TWeakObjectPtr<UObject> ObjectWeakPtr)
{
FSourceLinkParams Params;
Params.Object = ObjectWeakPtr;
Params.bUseDefaultFormat = true;
Params.bEmptyIfNoLink = true;
return GetSourceLink(Class, Params);
}
TSharedRef<SWidget> FEditorClassUtils::GetSourceLinkFormatted(const UClass* Class, const TWeakObjectPtr<UObject> ObjectWeakPtr, const FText& BlueprintFormat, const FText& CodeFormat)
{
FSourceLinkParams Params;
Params.Object = ObjectWeakPtr;
Params.BlueprintFormat = &BlueprintFormat;
Params.CodeFormat = &CodeFormat;
Params.bEmptyIfNoLink = true;
return GetSourceLink(Class, Params);
}
UClass* FEditorClassUtils::GetClassFromString(const FString& ClassName)
{
if(ClassName.IsEmpty() || ClassName == "None")
{
return nullptr;
}
UClass* Class = FindObject<UClass>(ANY_PACKAGE, *ClassName);
if(!Class)
{
Class = LoadObject<UClass>(nullptr, *ClassName);
}
return Class;
}
bool FEditorClassUtils::IsBlueprintAsset(const FAssetData& InAssetData, bool* bOutIsBPGC /*= nullptr*/)
{
bool bIsBP = (InAssetData.AssetClass == UBlueprint::StaticClass()->GetFName());
bool bIsBPGC = (InAssetData.AssetClass == UBlueprintGeneratedClass::StaticClass()->GetFName());
if (!bIsBP && !bIsBPGC)
{
IAssetRegistry& AssetRegistry = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(AssetRegistryConstants::ModuleName).Get();
TArray<FName> AncestorClassNames;
AssetRegistry.GetAncestorClassNames(InAssetData.AssetClass, AncestorClassNames);
if (AncestorClassNames.Contains(UBlueprint::StaticClass()->GetFName()))
{
bIsBP = true;
}
else if (AncestorClassNames.Contains(UBlueprintGeneratedClass::StaticClass()->GetFName()))
{
bIsBPGC = true;
}
}
if (bOutIsBPGC)
{
*bOutIsBPGC = bIsBPGC;
}
return bIsBP || bIsBPGC;
}
FName FEditorClassUtils::GetClassPathFromAssetTag(const FAssetData& InAssetData)
{
const FString GeneratedClassPath = InAssetData.GetTagValueRef<FString>(FBlueprintTags::GeneratedClassPath);
return FName(FPackageName::ExportTextPathToObjectPath(FStringView(GeneratedClassPath)));
}
FName FEditorClassUtils::GetClassPathFromAsset(const FAssetData& InAssetData, bool bGenerateClassPathIfMissing /*= false*/)
{
bool bIsBPGC = false;
const bool bIsBP = IsBlueprintAsset(InAssetData, &bIsBPGC);
if (bIsBPGC)
{
return InAssetData.ObjectPath;
}
else if (bIsBP)
{
FName ClassPath = GetClassPathFromAssetTag(InAssetData);
if (bGenerateClassPathIfMissing && ClassPath.IsNone())
{
FNameBuilder ClassPathBuilder(InAssetData.ObjectPath);
ClassPathBuilder << "_C";
ClassPath = FName(ClassPathBuilder.ToString());
}
return ClassPath;
}
return NAME_None;
}
void FEditorClassUtils::GetImplementedInterfaceClassPathsFromAsset(const struct FAssetData& InAssetData, TArray<FString>& OutClassPaths)
{
if (!InAssetData.IsValid())
{
return;
}
const FString ImplementedInterfaces = InAssetData.GetTagValueRef<FString>(FBlueprintTags::ImplementedInterfaces);
if (!ImplementedInterfaces.IsEmpty())
{
// Parse string like "((Interface=Class'"/Script/VPBookmark.VPBookmarkProvider"'),(Interface=Class'"/Script/VPUtilities.VPContextMenuProvider"'))"
// We don't want to actually resolve the hard ref so do some manual parsing
FString FullInterface;
FString CurrentString = *ImplementedInterfaces;
while (CurrentString.Split(TEXT("Interface="), nullptr, &FullInterface))
{
// Cutoff at next )
int32 RightParen = INDEX_NONE;
if (FullInterface.FindChar(TCHAR(')'), RightParen))
{
// Keep parsing
CurrentString = FullInterface.Mid(RightParen);
// Strip class name
FullInterface = *FPackageName::ExportTextPathToObjectPath(FullInterface.Left(RightParen));
// Handle quotes
FString InterfacePath;
const TCHAR* NewBuffer = FPropertyHelpers::ReadToken(*FullInterface, InterfacePath, true);
if (NewBuffer)
{
OutClassPaths.Add(InterfacePath);
}
}
}
}
}