Skip to content

Commit 411e68a

Browse files
authored
NewDynamicClass (crazytuzi#486)
1 parent 9b36a15 commit 411e68a

37 files changed

+2738
-60
lines changed

Source/CrossVersion/Public/UEVersion.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@
5353

5454
#define UE_ASSET_DATA_GET_SOFT_OBJECT_PATH UE_VERSION_START(5, 1, 0)
5555

56+
#define UE_ASSET_DATA_GET_ASSET_NAME UE_VERSION_START(5, 1, 0)
57+
5658
#define UE_NOTIFY_REGISTRATION_EVENT UE_VERSION_START(5, 1, 0)
5759

5860
#define UE_VECTOR2_COMPONENT_WISE_ALL_LESS_THAN UE_VERSION_START(5, 1, 0)

Source/UnrealCSharpCore/Private/Common/FUnrealCSharpFunctionLibrary.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,21 @@ FString FUnrealCSharpFunctionLibrary::GetGameProjectPropsPath()
756756
#endif
757757

758758
#if WITH_EDITOR
759+
TArray<FString> FUnrealCSharpFunctionLibrary::GetCustomProjectsName()
760+
{
761+
TArray<FString> CustomProjectsName;
762+
763+
if (const auto UnrealCSharpSetting = GetMutableDefaultSafe<UUnrealCSharpSetting>())
764+
{
765+
for (const auto& [Name] : UnrealCSharpSetting->GetCustomProjects())
766+
{
767+
CustomProjectsName.Add(Name);
768+
}
769+
}
770+
771+
return CustomProjectsName;
772+
}
773+
759774
TArray<FString> FUnrealCSharpFunctionLibrary::GetCustomProjectsDirectory()
760775
{
761776
TArray<FString> CustomProjectsDirectory;
@@ -869,6 +884,37 @@ FString FUnrealCSharpFunctionLibrary::GetPluginTemplateDirectory()
869884
{
870885
return GetPluginDirectory() / PLUGIN_TEMPLATE_PATH;
871886
}
887+
888+
FString FUnrealCSharpFunctionLibrary::GetPluginTemplateOverrideDirectory()
889+
{
890+
return GetPluginTemplateDirectory() / PLUGIN_TEMPLATE_OVERRIDE;
891+
}
892+
893+
FString FUnrealCSharpFunctionLibrary::GetPluginTemplateOverrideFileName(const UClass* InClass)
894+
{
895+
return GetPluginTemplateOverrideDirectory() /
896+
FString::Printf(TEXT(
897+
"%s%s"
898+
),
899+
*InClass->GetName(),
900+
*CSHARP_SUFFIX);
901+
}
902+
903+
FString FUnrealCSharpFunctionLibrary::GetPluginTemplateDynamicDirectory()
904+
{
905+
return GetPluginTemplateDirectory() / PLUGIN_TEMPLATE_DYNAMIC;
906+
}
907+
908+
FString FUnrealCSharpFunctionLibrary::GetPluginTemplateDynamicFileName(const UClass* InClass)
909+
{
910+
return GetPluginTemplateDynamicDirectory() /
911+
FString::Printf(TEXT(
912+
"%s%s%s"
913+
),
914+
*DYNAMIC_CLASS_DEFAULT_PREFIX,
915+
*InClass->GetName(),
916+
*CSHARP_SUFFIX);
917+
}
872918
#endif
873919

874920
bool FUnrealCSharpFunctionLibrary::IsGameField(const UField* InField)

Source/UnrealCSharpCore/Private/Delegate/FUnrealCSharpCoreModuleDelegates.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ FUnrealCSharpCoreModuleDelegates::FOnBeginGenerator FUnrealCSharpCoreModuleDeleg
1010

1111
FUnrealCSharpCoreModuleDelegates::FOnEndGenerator FUnrealCSharpCoreModuleDelegates::OnEndGenerator;
1212

13+
#if WITH_EDITOR
1314
FUnrealCSharpCoreModuleDelegates::FOnDynamicClassUpdated FUnrealCSharpCoreModuleDelegates::OnDynamicClassUpdated;
1415

15-
#if WITH_EDITOR
1616
FUnrealCSharpCoreModuleDelegates::FOnCompile FUnrealCSharpCoreModuleDelegates::OnCompile;
1717
#endif

Source/UnrealCSharpCore/Public/Common/FUnrealCSharpFunctionLibrary.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ class UNREALCSHARPCORE_API FUnrealCSharpFunctionLibrary
126126
#endif
127127

128128
#if WITH_EDITOR
129+
static TArray<FString> GetCustomProjectsName();
130+
129131
static TArray<FString> GetCustomProjectsDirectory();
130132
#endif
131133

@@ -153,6 +155,14 @@ class UNREALCSHARPCORE_API FUnrealCSharpFunctionLibrary
153155

154156
#if WITH_EDITOR
155157
static FString GetPluginTemplateDirectory();
158+
159+
static FString GetPluginTemplateOverrideDirectory();
160+
161+
static FString GetPluginTemplateOverrideFileName(const UClass* InClass);
162+
163+
static FString GetPluginTemplateDynamicDirectory();
164+
165+
static FString GetPluginTemplateDynamicFileName(const UClass* InClass);
156166
#endif
157167

158168
static bool IsGameField(const UField* InField);

Source/UnrealCSharpCore/Public/CoreMacro/Macro.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@
66

77
#define PLUGIN_TEMPLATE_PATH FString(TEXT("Template"))
88

9+
#define PLUGIN_TEMPLATE_OVERRIDE FString(TEXT("Override"))
10+
11+
#define PLUGIN_TEMPLATE_DYNAMIC FString(TEXT("Dynamic"))
12+
13+
#define DYNAMIC_CLASS_DEFAULT_PREFIX FString(TEXT("Dynamic"))
14+
15+
#define DYNAMIC_CLASS_DEFAULT_NAMESPACE UObject::StaticClass()->GetPackage()->GetName().RightChop(1).Replace(TEXT("/"), TEXT("."))
16+
917
#define SOLUTION_NAME FString(TEXT("Script"))
1018

1119
#define DEFAULT_UE_NAME FString(TEXT("UE"))

Source/UnrealCSharpCore/Public/Delegate/FUnrealCSharpCoreModuleDelegates.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ class UNREALCSHARPCORE_API FUnrealCSharpCoreModuleDelegates
1515

1616
DECLARE_MULTICAST_DELEGATE(FOnEndGenerator);
1717

18+
#if WITH_EDITOR
1819
DECLARE_MULTICAST_DELEGATE(FOnDynamicClassUpdated);
1920

20-
#if WITH_EDITOR
2121
DECLARE_MULTICAST_DELEGATE_OneParam(FOnCompile, const TArray<FFileChangeData>& InFileChangeData);
2222
#endif
2323

@@ -29,9 +29,9 @@ class UNREALCSHARPCORE_API FUnrealCSharpCoreModuleDelegates
2929

3030
static FOnEndGenerator OnEndGenerator;
3131

32+
#if WITH_EDITOR
3233
static FOnDynamicClassUpdated OnDynamicClassUpdated;
3334

34-
#if WITH_EDITOR
3535
static FOnCompile OnCompile;
3636
#endif
3737
};

Source/UnrealCSharpCore/Public/Dynamic/FDynamicClassGenerator.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ class FDynamicClassGenerator
2626

2727
static void Generator(MonoClass* InMonoClass, EDynamicClassGeneratorType InDynamicClassGeneratorType);
2828

29-
static bool IsDynamicClass(const UClass* InClass);
29+
static UNREALCSHARPCORE_API bool IsDynamicClass(const UClass* InClass);
3030

31-
static bool IsDynamicBlueprintGeneratedClass(const UField* InField);
31+
static UNREALCSHARPCORE_API bool IsDynamicBlueprintGeneratedClass(const UField* InField);
3232

3333
static bool IsDynamicBlueprintGeneratedClass(const UBlueprintGeneratedClass* InClass);
3434

Source/UnrealCSharpEditor/Private/ContentBrowser/DynamicDataSource.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,15 @@
66
#if UE_F_NAME_PERMISSION_LIST
77
#include "Misc/NamePermissionList.h"
88
#endif
9+
#include "ContentBrowserDataMenuContexts.h"
10+
#include "ToolMenuDelegates.h"
11+
#include "ToolMenus.h"
912
#include "Delegate/FUnrealCSharpCoreModuleDelegates.h"
1013
#include "CoreMacro/Macro.h"
1114
#include "Dynamic/FDynamicGenerator.h"
15+
#include "Common/FUnrealCSharpFunctionLibrary.h"
16+
#include "ContentBrowser/DynamicNewClassContextMenu.h"
17+
#include "NewClass/DynamicNewClassUtils.h"
1218

1319
#ifdef UE_INLINE_GENERATED_CPP_BY_NAME
1420
#include UE_INLINE_GENERATED_CPP_BY_NAME(DynamicDataSource)
@@ -28,6 +34,22 @@ void UDynamicDataSource::Initialize(const bool InAutoRegister)
2834

2935
CollectionManager = &FCollectionManagerModule::GetModule().Get();
3036

37+
if (const auto Menu = UToolMenus::Get()->ExtendMenu("ContentBrowser.AddNewContextMenu"))
38+
{
39+
Menu->AddDynamicSection(*FString::Printf(TEXT(
40+
"DynamicSection_DataSource_%s"),
41+
*GetName()
42+
),
43+
FNewToolMenuDelegate::CreateLambda(
44+
[WeakThis = TWeakObjectPtr<UDynamicDataSource>(this)](UToolMenu* InMenu)
45+
{
46+
if (WeakThis.IsValid())
47+
{
48+
WeakThis->PopulateAddNewContextMenu(InMenu);
49+
}
50+
}));
51+
}
52+
3153
BuildRootPathVirtualTree();
3254
}
3355

@@ -577,6 +599,45 @@ void UDynamicDataSource::BuildRootPathVirtualTree()
577599
RootPathAdded(FNameBuilder(*DYNAMIC_ROOT_INTERNAL_PATH));
578600
}
579601

602+
void UDynamicDataSource::OnNewClassRequested(const FName& InSelectedPath)
603+
{
604+
if (auto SelectedFileSystemPath = FDynamicHierarchy::ConvertInternalPathToFileSystemPath(InSelectedPath.ToString());
605+
!SelectedFileSystemPath.IsEmpty())
606+
{
607+
if (SelectedFileSystemPath.EndsWith(FUnrealCSharpFunctionLibrary::GetScriptDirectory()))
608+
{
609+
SelectedFileSystemPath = FUnrealCSharpFunctionLibrary::GetGameDirectory();
610+
}
611+
612+
FDynamicNewClassUtils::OpenAddDynamicClassToProjectDialog(SelectedFileSystemPath);
613+
}
614+
}
615+
616+
void UDynamicDataSource::PopulateAddNewContextMenu(UToolMenu* InMenu)
617+
{
618+
const auto ContextObject = InMenu->FindContext<UContentBrowserDataMenuContext_AddNewMenu>();
619+
620+
TArray<FName> SelectedClassPaths;
621+
622+
for (const auto& SelectedPath : ContextObject->SelectedPaths)
623+
{
624+
if (FName InternalPath; TryConvertVirtualPathToInternal(SelectedPath, InternalPath))
625+
{
626+
SelectedClassPaths.Add(InternalPath);
627+
}
628+
}
629+
630+
FDynamicNewClassContextMenu::FOnOpenNewDynamicClassRequested OnOpenNewDynamicClassRequested;
631+
632+
if (!SelectedClassPaths.IsEmpty())
633+
{
634+
OnOpenNewDynamicClassRequested = FDynamicNewClassContextMenu::FOnOpenNewDynamicClassRequested::CreateStatic(
635+
&UDynamicDataSource::OnNewClassRequested);
636+
}
637+
638+
FDynamicNewClassContextMenu::MakeContextMenu(InMenu, SelectedClassPaths, OnOpenNewDynamicClassRequested);
639+
}
640+
580641
void UDynamicDataSource::OnDynamicClassUpdated()
581642
{
582643
UpdateHierarchy();

Source/UnrealCSharpEditor/Private/ContentBrowser/DynamicHierarchy.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,30 @@ TArray<UClass*> FDynamicHierarchy::GetMatchingClasses(const FName& InPath, const
127127
return MatchingClasses;
128128
}
129129

130+
FString FDynamicHierarchy::ConvertInternalPathToFileSystemPath(const FString& InInternalPath)
131+
{
132+
auto FileSystemPath = InInternalPath;
133+
134+
if (FileSystemPath.IsEmpty() ||
135+
!FileSystemPath.StartsWith(TEXT("/")))
136+
{
137+
return FString();
138+
}
139+
140+
const auto SecondSlashIndex = FileSystemPath.Find(TEXT("/"), ESearchCase::IgnoreCase, ESearchDir::FromStart, 1);
141+
142+
FileSystemPath = SecondSlashIndex != INDEX_NONE
143+
? FileSystemPath.RightChop(SecondSlashIndex)
144+
: TEXT("");
145+
146+
if (FileSystemPath.IsEmpty())
147+
{
148+
return FString();
149+
}
150+
151+
return FUnrealCSharpFunctionLibrary::GetFullScriptDirectory() / FileSystemPath;
152+
}
153+
130154
bool FDynamicHierarchy::EnumeratePath(const FString& InPath, const TFunctionRef<bool(const FName&)>& InCallback)
131155
{
132156
const auto Path = *InPath;
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#include "ContentBrowser/DynamicNewClassContextMenu.h"
2+
#include "ToolMenus.h"
3+
#include "UnrealCSharpEditorStyle.h"
4+
5+
#define LOCTEXT_NAMESPACE "DynamicNewClassContextMenu"
6+
7+
void FDynamicNewClassContextMenu::MakeContextMenu(UToolMenu* InMenu, const TArray<FName>& InSelectedClassPaths,
8+
const FOnOpenNewDynamicClassRequested&
9+
InOnOpenNewDynamicClassRequested)
10+
{
11+
if (InSelectedClassPaths.IsEmpty())
12+
{
13+
return;
14+
}
15+
16+
const auto FirstSelectedPath = InSelectedClassPaths[0];
17+
18+
const auto bHasSinglePathSelected = InSelectedClassPaths.Num() == 1;
19+
20+
auto CanExecuteClassActions = [bHasSinglePathSelected]()
21+
{
22+
return bHasSinglePathSelected;
23+
};
24+
25+
const FCanExecuteAction CanExecuteClassActionsDelegate =
26+
FCanExecuteAction::CreateLambda(CanExecuteClassActions);
27+
28+
if (InOnOpenNewDynamicClassRequested.IsBound())
29+
{
30+
const auto ClassCreationPath = FirstSelectedPath;
31+
32+
FText NewClassToolTip;
33+
34+
if (bHasSinglePathSelected)
35+
{
36+
NewClassToolTip = FText::Format(
37+
LOCTEXT("NewClassTooltip_CreateIn", "Create a new dynamic class in {0}."),
38+
FText::FromName(ClassCreationPath));
39+
}
40+
else
41+
{
42+
NewClassToolTip = LOCTEXT("NewClassTooltip_SelectedInvalidNumberOfPaths",
43+
"Can only create classes when there is a single path selected.");
44+
}
45+
46+
auto& Section = InMenu->AddSection("ContentBrowserNewClass",
47+
LOCTEXT("ClassMenuHeading", "New Dynamic Class"));
48+
Section.AddMenuEntry(
49+
"NewClass",
50+
LOCTEXT("NewClassLabel", "New dynamic Class..."),
51+
NewClassToolTip,
52+
FSlateIcon(FUnrealCSharpEditorStyle::GetStyleSetName(), "UnrealCSharpEditor.PluginAction"),
53+
FUIAction(
54+
FExecuteAction::CreateStatic(&FDynamicNewClassContextMenu::ExecuteNewClass, ClassCreationPath,
55+
InOnOpenNewDynamicClassRequested),
56+
CanExecuteClassActionsDelegate
57+
)
58+
);
59+
}
60+
}
61+
62+
void FDynamicNewClassContextMenu::ExecuteNewClass(const FName InPath,
63+
FOnOpenNewDynamicClassRequested InOnOpenNewDynamicClassRequested)
64+
{
65+
(void)InOnOpenNewDynamicClassRequested.ExecuteIfBound(InPath);
66+
}
67+
68+
#undef LOCTEXT_NAMESPACE

0 commit comments

Comments
 (0)