Skip to content

Commit e1b4799

Browse files
committed
Fix Content Menu Entry Generation
1 parent aeba4ee commit e1b4799

File tree

5 files changed

+117
-165
lines changed

5 files changed

+117
-165
lines changed

Source/Linter/Linter.Build.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public Linter(ReadOnlyTargetRules Target) : base(Target) {
1010
PrivateDependencyModuleNames.AddRange(new[] {
1111
"CoreUObject", "Engine", "Slate", "SlateCore", "AppFramework",
1212
"InputCore", "UnrealEd", "GraphEditor", "AssetTools", "EditorStyle", "BlueprintGraph", "PropertyEditor",
13-
"LauncherPlatform", "Projects", "DesktopPlatform", "Json", "UATHelper"
13+
"LauncherPlatform", "Projects", "DesktopPlatform", "Json", "UATHelper", "ToolMenus", "ContentBrowser", "ContentBrowserData"
1414
});
1515

1616
PublicIncludePathModuleNames.Add("Launch");

Source/Linter/Private/Linter.cpp

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
#include "Linter.h"
44
#include "ISettingsModule.h"
55
#include "Framework/Docking/TabManager.h"
6-
#include "LevelEditor.h"
76
#include "Styling/SlateStyle.h"
87
#include "PropertyEditorModule.h"
98
#include "LinterStyle.h"
@@ -16,8 +15,10 @@
1615

1716
#define LOCTEXT_NAMESPACE "FLinterModule"
1817

18+
1919
static const FName LinterTabName = "LinterTab";
2020

21+
2122
void FLinterModule::StartupModule() {
2223
// Load the asset registry module
2324
const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(FName("AssetRegistry"));
@@ -43,12 +44,10 @@ void FLinterModule::StartupModule() {
4344
}
4445

4546
// Install UI Hooks
46-
FLinterContentBrowserExtensions::InstallHooks(this, &ContentBrowserExtenderDelegateHandle, &AssetExtenderDelegateHandle);
47+
FLinterContentBrowserExtensions::InstallHooks();
4748

4849
//Register our UI
49-
FGlobalTabmanager::Get()->RegisterNomadTabSpawner(
50-
LinterTabName,
51-
FOnSpawnTab::CreateStatic(&FLinterModule::SpawnTab, StyleSetPtr))
50+
FGlobalTabmanager::Get()->RegisterNomadTabSpawner(LinterTabName, FOnSpawnTab::CreateStatic(&FLinterModule::SpawnTab, StyleSetPtr))
5251
.SetDisplayName(LOCTEXT("LinterTabName", "Linter"))
5352
.SetTooltipText(LOCTEXT("LinterTabToolTip", "Linter"))
5453
.SetMenuType(ETabSpawnerMenuType::Hidden);
@@ -67,13 +66,8 @@ void FLinterModule::ShutdownModule() {
6766
FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
6867
PropertyModule.UnregisterCustomClassLayout(ULinterNamingConvention::StaticClass()->GetFName());
6968

70-
FLinterContentBrowserExtensions::RemoveHooks(this, &ContentBrowserExtenderDelegateHandle, &AssetExtenderDelegateHandle);
71-
72-
if (FModuleManager::Get().IsModuleLoaded(TEXT("LevelEditor"))) {
73-
FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked<FLevelEditorModule>(TEXT("LevelEditor"));
74-
LevelEditorModule.OnTabManagerChanged().Remove(LevelEditorTabManagerChangedHandle);
75-
}
76-
69+
// Remove Hooks and Tabs
70+
FLinterContentBrowserExtensions::RemoveHooks();
7771
FGlobalTabmanager::Get()->UnregisterTabSpawner(LinterTabName);
7872

7973
// Unregister slate style overrides
@@ -113,7 +107,7 @@ void FLinterModule::TryToLoadAllLintRuleSets() {
113107
// Attempt to get all RuleSets in memory so that linting tools are better aware of them
114108
for (const FAssetData& RuleSetData : FoundRuleSets) {
115109
if (!RuleSetData.IsAssetLoaded()) {
116-
RuleSetData.GetAsset();
110+
(void)RuleSetData.GetAsset();
117111
}
118112
}
119113
}
Lines changed: 104 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -1,178 +1,138 @@
11
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
22

33
#include "LinterContentBrowserExtensions.h"
4-
#include "Modules/ModuleManager.h"
5-
#include "Framework/MultiBox/MultiBoxBuilder.h"
64
#include "LinterStyle.h"
75
#include "ContentBrowserModule.h"
86
#include "Linter.h"
97
#include "BatchRenameTool/BatchRenameTool.h"
10-
#include "Framework/MultiBox/MultiBoxExtender.h"
11-
#include "Framework/Commands/UIAction.h"
12-
#include "Delegates/IDelegateInstance.h"
138
#include "TooltipEditor/TooltipTool.h"
149
#include "Misc/EngineVersionComparison.h"
1510

11+
1612
#define LOCTEXT_NAMESPACE "Linter"
1713

1814
DEFINE_LOG_CATEGORY_STATIC(LinterContentBrowserExtensions, Log, All);
1915

20-
void FLinterContentBrowserExtensions::InstallHooks(FLinterModule* LinterModule, FDelegateHandle* pContentBrowserExtenderDelegateHandle, class FDelegateHandle* pAssetExtenderDelegateHandle) {
21-
struct Local {
22-
// Path extensions
23-
24-
static TSharedRef<FExtender> OnExtendContentBrowserAssetSelectionMenu(const TArray<FString>& SelectedPaths) {
25-
TSharedRef<FExtender> Extender = MakeShared<FExtender>();
26-
Extender->AddMenuExtension(
27-
"PathContextSourceControl",
28-
EExtensionHook::After,
29-
TSharedPtr<FUICommandList>(),
30-
FMenuExtensionDelegate::CreateStatic(&Local::ContentBrowserExtenderFunc, SelectedPaths)
31-
);
32-
return Extender;
33-
}
3416

35-
static void ContentBrowserExtenderFunc(FMenuBuilder& MenuBuilder, const TArray<FString> SelectedPaths) {
36-
MenuBuilder.BeginSection("LinterContentBrowserContext", LOCTEXT("CB_LinterHeader", "Linter"));
37-
{
38-
MenuBuilder.AddMenuEntry(
39-
LOCTEXT("CB_ScanProjectWithLinter", "Scan with Linter"),
40-
LOCTEXT("CB_ScanProjectWithLinter_Tooltip", "Scan project content with Linter"),
41-
FSlateIcon(FLinterStyle::GetStyleSetName(), "Linter.Toolbar.Icon"),
42-
FUIAction(FExecuteAction::CreateLambda([SelectedPaths]() {
43-
if (FLinterModule* Linter = FModuleManager::GetModulePtr<FLinterModule>("Linter")) {
44-
if (Linter != nullptr) {
45-
Linter->SetDesiredLintPaths(SelectedPaths);
46-
}
47-
#if UE_VERSION_NEWER_THAN(4, 26, 0)
48-
FGlobalTabmanager::Get()->TryInvokeTab(FName("LinterTab"));
49-
#else
50-
FGlobalTabmanager::Get()->InvokeTab(FName("LinterTab"));
51-
#endif
52-
}
53-
})),
54-
NAME_None,
55-
EUserInterfaceActionType::Button);
56-
}
57-
MenuBuilder.EndSection();
58-
}
17+
namespace {
5918

60-
// Asset extensions
61-
62-
static TSharedRef<FExtender> OnExtendAssetSelectionMenu(const TArray<FAssetData>& SelectedAssets) {
63-
TSharedRef<FExtender> Extender = MakeShared<FExtender>();
64-
Extender->AddMenuExtension(
65-
"CommonAssetActions",
66-
EExtensionHook::After,
67-
nullptr,
68-
FMenuExtensionDelegate::CreateStatic(&Local::AssetExtenderFunc, SelectedAssets)
69-
);
70-
return Extender;
71-
}
19+
void RunLinterForAssets(const TArray<FString>& SelectedPaths) {
20+
// Set Paths to Linter
21+
if (FLinterModule* Linter = FModuleManager::GetModulePtr<FLinterModule>("Linter")) {
22+
Linter->SetDesiredLintPaths(SelectedPaths);
23+
}
7224

73-
static void AssetExtenderFunc(FMenuBuilder& MenuBuilder, const TArray<FAssetData> SelectedAssets) {
74-
MenuBuilder.BeginSection("LinterAssetContext", LOCTEXT("CB_LinterHeader", "Linter"));
75-
{
76-
// Run through the assets to determine if any are blueprints
77-
bool bAnyBlueprintsSelected = false;
78-
for (auto AssetIt = SelectedAssets.CreateConstIterator(); AssetIt; ++AssetIt) {
79-
const FAssetData& Asset = *AssetIt;
80-
// Cannot rename redirectors or classes or cooked packages
81-
#if UE_VERSION_NEWER_THAN(5, 1, 0)
82-
if (!Asset.IsRedirector() && Asset.AssetClassPath.GetAssetName() != NAME_Class && !(Asset.PackageFlags & PKG_FilterEditorOnly))
25+
// Execute Linter
26+
#if UE_VERSION_NEWER_THAN(4, 26, 0)
27+
FGlobalTabmanager::Get()->TryInvokeTab(FName("LinterTab"));
8328
#else
84-
if (!Asset.IsRedirector() && Asset.AssetClass != NAME_Class && !(Asset.PackageFlags & PKG_FilterEditorOnly))
29+
FGlobalTabmanager::Get()->InvokeTab(FName("LinterTab"));
8530
#endif
86-
{
87-
if (Asset.GetClass()->IsChildOf(UBlueprint::StaticClass())) {
88-
bAnyBlueprintsSelected = true;
89-
break;
90-
}
91-
}
92-
}
31+
}
9332

94-
// If we have blueprints selected, enable blueprint tools
95-
if (bAnyBlueprintsSelected) {
96-
// Add Tooltip Editor
97-
MenuBuilder.AddMenuEntry(
98-
LOCTEXT("CB_EditBlueprintTooltips", "Edit Blueprint Tooltips (Experimental)"),
99-
LOCTEXT("CB_EditBlueprintTooltips_Tooltip", "Edit selected blueprints' templates definitions"),
100-
FSlateIcon(FLinterStyle::GetStyleSetName(), "Linter.Toolbar.Icon"),
101-
FUIAction(FExecuteAction::CreateLambda([SelectedAssets]() {
102-
UE_LOG(LinterContentBrowserExtensions, Display, TEXT("Opening Tooltip Tool window."));
103-
FTooltipTool AssetDlg(SelectedAssets);
104-
if (AssetDlg.ShowModal() == FTooltipTool::Confirm) {
105-
UE_LOG(LinterContentBrowserExtensions, Display, TEXT("Tooltip Tool did the thing."));
106-
}
107-
})),
108-
NAME_None,
109-
EUserInterfaceActionType::Button);
110-
}
33+
void EditBlueprintTooltips(const TArray<FAssetData> SelectedAssets) {
34+
UE_LOG(LinterContentBrowserExtensions, Display, TEXT("Opening Tooltip Tool window."));
35+
FTooltipTool{SelectedAssets}.ShowModal();
36+
}
37+
38+
void BatchRenameAssets(const TArray<FAssetData> SelectedAssets) {
39+
UE_LOG(LinterContentBrowserExtensions, Display, TEXT("Starting batch rename."));
40+
FDlgBatchRenameTool{SelectedAssets}.ShowModal();
41+
}
11142

112-
// Run through the assets to see if any can be renamed
113-
bool bAnyAssetCanBeRenamed = false;
114-
for (auto AssetIt = SelectedAssets.CreateConstIterator(); AssetIt; ++AssetIt) {
115-
const FAssetData& Asset = *AssetIt;
116-
// Cannot rename redirectors or classes or cooked packages
43+
// Asset Context Menu is dynamic -> some options will only be displayed if
44+
// you have certain types of Blueprints or Assets selected
45+
void CreateDynamicAssetSelectionMenu(UToolMenu* InMenu) {
46+
const auto* Context = InMenu->FindContext<UContentBrowserDataMenuContext_FileMenu>();
47+
FToolMenuSection& Section = InMenu->AddSection("Linter", LOCTEXT("LinterSection", "Linter"));
48+
49+
// Convert BrowserItems to their AssetData
50+
TArray<FAssetData> Assets;
51+
for (const auto& Asset : Context->SelectedItems) {
52+
FAssetData AssetData;
53+
Asset.Legacy_TryGetAssetData(AssetData);
54+
Assets.Add(AssetData);
55+
}
56+
57+
// Run through the assets to determine if any are blueprints or can be renamed
58+
bool bAnyBlueprintsSelected = false;
59+
bool bAnyAssetCanBeRenamed = false;
60+
61+
for (const auto& Asset : Assets) {
62+
// Cannot rename redirectors or classes or cooked packages
11763
#if UE_VERSION_NEWER_THAN(5, 1, 0)
118-
if (!Asset.IsRedirector() && Asset.AssetClassPath.GetAssetName() != NAME_Class && !(Asset.PackageFlags & PKG_FilterEditorOnly))
64+
if (!Asset.IsRedirector() && Asset.AssetClassPath.GetAssetName() != NAME_Class && !(Asset.PackageFlags & PKG_FilterEditorOnly))
11965
#else
120-
if (!Asset.IsRedirector() && Asset.AssetClass != NAME_Class && !(Asset.PackageFlags & PKG_FilterEditorOnly))
66+
if (!Asset.IsRedirector() && Asset.AssetClass != NAME_Class && !(Asset.PackageFlags & PKG_FilterEditorOnly))
12167
#endif
122-
{
123-
bAnyAssetCanBeRenamed = true;
124-
break;
125-
}
126-
}
127-
128-
if (bAnyAssetCanBeRenamed) {
129-
// Add Tooltip Editor
130-
MenuBuilder.AddMenuEntry(
131-
LOCTEXT("CB_BatchRenameAssets", "Batch Rename Assets (Experimental)"),
132-
LOCTEXT("CB_BatchRenameAssets_Tooltip", "Perform a bulk rename operation on all of the selected assets"),
133-
FSlateIcon(FLinterStyle::GetStyleSetName(), "Linter.Toolbar.Icon"),
134-
FUIAction(FExecuteAction::CreateLambda([SelectedAssets]() {
135-
UE_LOG(LinterContentBrowserExtensions, Display, TEXT("Starting batch rename."));
136-
FDlgBatchRenameTool AssetDlg(SelectedAssets);
137-
AssetDlg.ShowModal();
138-
})),
139-
NAME_None,
140-
EUserInterfaceActionType::Button);
141-
}
68+
{
69+
bAnyAssetCanBeRenamed = true;
70+
71+
if (Asset.GetClass()->IsChildOf(UBlueprint::StaticClass())) {
72+
bAnyBlueprintsSelected = true;
73+
break;
14274
}
143-
MenuBuilder.EndSection();
14475
}
145-
};
76+
}
77+
78+
// If we have blueprints selected, add Tooltip Editor
79+
if (bAnyBlueprintsSelected) {
80+
Section.AddMenuEntry(
81+
"EditBlueprintTooltips",
82+
LOCTEXT("EditBlueprintTooltips", "Edit Blueprint Tooltips (Experimental)"),
83+
LOCTEXT("EditBlueprintTooltips_Tooltip", "Edit selected blueprints' templates definitions"),
84+
FSlateIcon(FLinterStyle::GetStyleSetName(), "Linter.Toolbar.Icon"),
85+
FExecuteAction::CreateStatic(&EditBlueprintTooltips, Assets)
86+
);
87+
}
88+
89+
// If blueprints can be renamed, add the batch rename option
90+
if (bAnyAssetCanBeRenamed) {
91+
Section.AddMenuEntry(
92+
"BatchRename",
93+
LOCTEXT("BatchRenameAssets", "Batch Rename Assets (Experimental)"),
94+
LOCTEXT("BatchRenameAssets_Tooltip", "Perform a bulk rename operation on all of the selected assets"),
95+
FSlateIcon(FLinterStyle::GetStyleSetName(), "Linter.Toolbar.Icon"),
96+
FExecuteAction::CreateStatic(&BatchRenameAssets, Assets)
97+
);
98+
}
99+
}
100+
101+
}
146102

147-
FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>(TEXT("ContentBrowser"));
148103

149-
// Path view extenders
150-
TArray<FContentBrowserMenuExtender_SelectedPaths>& CBMenuPathExtenderDelegates = ContentBrowserModule.GetAllPathViewContextMenuExtenders();
151-
CBMenuPathExtenderDelegates.Add(FContentBrowserMenuExtender_SelectedPaths::CreateStatic(&Local::OnExtendContentBrowserAssetSelectionMenu));
152-
*pContentBrowserExtenderDelegateHandle = CBMenuPathExtenderDelegates.Last().GetHandle();
104+
void FLinterContentBrowserExtensions::InstallHooks() {
105+
// Run Linter on Selected Folder(s)
106+
UToolMenu* ContentBrowserMenu = UToolMenus::Get()->ExtendMenu("ContentBrowser.FolderContextMenu");
107+
ContentBrowserMenu->AddSection("Linter", LOCTEXT("LinterSection", "Linter"))
108+
.AddMenuEntry(
109+
"LintAssets",
110+
LOCTEXT("ScanWithLinter", "Scan with Linter"),
111+
LOCTEXT("ScanWithLinter_Tooltip", "Scan project content with Linter"),
112+
FSlateIcon(FLinterStyle::GetStyleSetName(), "Linter.Toolbar.Icon"),
113+
FToolMenuExecuteAction::CreateLambda([](const FToolMenuContext& InContext) {
114+
if (const UContentBrowserDataMenuContext_FolderMenu* Context = InContext.FindContext<UContentBrowserDataMenuContext_FolderMenu>()) {
115+
TArray<FString> SelectedPaths;
116+
for (const auto& Asset : Context->SelectedItems) {
117+
SelectedPaths.Add(Asset.GetVirtualPath().ToString());
118+
}
119+
120+
RunLinterForAssets(SelectedPaths);
121+
}
122+
})
123+
);
153124

154-
// Asset extenders
155-
TArray<FContentBrowserMenuExtender_SelectedAssets>& CBMenuAssetExtenderDelegates = ContentBrowserModule.GetAllAssetViewContextMenuExtenders();
156-
CBMenuAssetExtenderDelegates.Add(FContentBrowserMenuExtender_SelectedAssets::CreateStatic(&Local::OnExtendAssetSelectionMenu));
157-
*pAssetExtenderDelegateHandle = CBMenuAssetExtenderDelegates.Last().GetHandle();
125+
// BatchRename and TooltipEditor
126+
UToolMenu* AssetMenu = UToolMenus::Get()->ExtendMenu("ContentBrowser.AssetContextMenu");
127+
AssetMenu->AddDynamicSection("Linter", FNewToolMenuDelegate::CreateStatic(&CreateDynamicAssetSelectionMenu), {"AssetContextExploreMenuOptions", EToolMenuInsertType::After});
158128
}
159129

160-
void FLinterContentBrowserExtensions::RemoveHooks(FLinterModule* LinterModule, FDelegateHandle* pContentBrowserExtenderDelegateHandle, FDelegateHandle* pAssetExtenderDelegateHandle) {
161-
if (FModuleManager::Get().IsModuleLoaded("ContentBrowser")) {
162-
FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>(TEXT("ContentBrowser"));
163-
164-
// Path view extenders
165-
TArray<FContentBrowserMenuExtender_SelectedPaths>& CBMenuExtenderDelegates = ContentBrowserModule.GetAllAssetContextMenuExtenders();
166-
CBMenuExtenderDelegates.RemoveAll([pContentBrowserExtenderDelegateHandle](const FContentBrowserMenuExtender_SelectedPaths& Delegate) {
167-
return Delegate.GetHandle() == *pContentBrowserExtenderDelegateHandle;
168-
});
169-
170-
// Asset extenders
171-
TArray<FContentBrowserMenuExtender_SelectedAssets>& CBMenuAssetExtenderDelegates = ContentBrowserModule.GetAllAssetViewContextMenuExtenders();
172-
CBMenuAssetExtenderDelegates.RemoveAll([pAssetExtenderDelegateHandle](const FContentBrowserMenuExtender_SelectedAssets& Delegate) {
173-
return Delegate.GetHandle() == *pAssetExtenderDelegateHandle;
174-
});
175-
}
130+
void FLinterContentBrowserExtensions::RemoveHooks() {
131+
UToolMenu* ContentBrowserMenu = UToolMenus::Get()->ExtendMenu("ContentBrowser.FolderContextMenu");
132+
ContentBrowserMenu->RemoveSection("Linter");
133+
134+
UToolMenu* AssetMenu = UToolMenus::Get()->ExtendMenu("ContentBrowser.AssetContextMenu");
135+
AssetMenu->RemoveSection("Linter");
176136
}
177137

178138
#undef LOCTEXT_NAMESPACE

Source/Linter/Public/Linter.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
#include "Widgets/Docking/SDockTab.h"
66
#include "Styling/SlateStyle.h"
77

8+
89
class FLinterManagerBase;
910

10-
DECLARE_LOG_CATEGORY_EXTERN(LogLinter, Verbose, All);
1111

12+
DECLARE_LOG_CATEGORY_EXTERN(LogLinter, Verbose, All);
1213
DECLARE_LOG_CATEGORY_EXTERN(LogCommandlet, All, All);
1314

15+
1416
class LINTER_API FLinterModule : public IModuleInterface {
1517
public:
1618
/** IModuleInterface implementation */
@@ -39,10 +41,6 @@ class LINTER_API FLinterModule : public IModuleInterface {
3941
}
4042

4143
private:
42-
FDelegateHandle LevelEditorTabManagerChangedHandle;
43-
FDelegateHandle ContentBrowserExtenderDelegateHandle;
44-
FDelegateHandle AssetExtenderDelegateHandle;
45-
4644
TArray<FString> DesiredLintPaths;
4745

4846
public:

Source/Linter/Public/LinterContentBrowserExtensions.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ class FLinterModule;
77
// Integrate Linter actions into the Content Browser
88
class FLinterContentBrowserExtensions {
99
public:
10-
static void InstallHooks(FLinterModule* LinterModule, class FDelegateHandle* pContentBrowserExtenderDelegateHandle, class FDelegateHandle* pAssetExtenderDelegateHandle);
11-
static void RemoveHooks(FLinterModule* LinterModule, class FDelegateHandle* pContentBrowserExtenderDelegateHandle, class FDelegateHandle* pAssetExtenderDelegateHandle);
10+
static void InstallHooks();
11+
static void RemoveHooks();
1212
};

0 commit comments

Comments
 (0)