From b62d7d75de6445b05e188b3d79f17738135f0e2b Mon Sep 17 00:00:00 2001 From: PHILIP WHITE Date: Fri, 26 Apr 2024 03:30:26 +0100 Subject: [PATCH] Add Recent Blueprint Plugin --- EndlessVendetta/EndlessVendetta.uproject | 5 ++ .../RecentBlueprintMenu.uplugin | 28 +++++++ .../RecentBlueprintMenu/Resources/Icon128.png | 3 + .../Private/RecentBlueprintMenu.cpp | 83 +++++++++++++++++++ .../Private/RecentBlueprintMenuUtils.cpp | 57 +++++++++++++ .../Public/RecentBlueprintMenu.h | 28 +++++++ .../Public/RecentBlueprintMenuSettings.h | 25 ++++++ .../Public/RecentBlueprintMenuUtils.h | 23 +++++ .../RecentBlueprintMenu.Build.cs | 55 ++++++++++++ 9 files changed, 307 insertions(+) create mode 100644 EndlessVendetta/Plugins/RecentBlueprintMenu/RecentBlueprintMenu.uplugin create mode 100644 EndlessVendetta/Plugins/RecentBlueprintMenu/Resources/Icon128.png create mode 100644 EndlessVendetta/Plugins/RecentBlueprintMenu/Source/RecentBlueprintMenu/Private/RecentBlueprintMenu.cpp create mode 100644 EndlessVendetta/Plugins/RecentBlueprintMenu/Source/RecentBlueprintMenu/Private/RecentBlueprintMenuUtils.cpp create mode 100644 EndlessVendetta/Plugins/RecentBlueprintMenu/Source/RecentBlueprintMenu/Public/RecentBlueprintMenu.h create mode 100644 EndlessVendetta/Plugins/RecentBlueprintMenu/Source/RecentBlueprintMenu/Public/RecentBlueprintMenuSettings.h create mode 100644 EndlessVendetta/Plugins/RecentBlueprintMenu/Source/RecentBlueprintMenu/Public/RecentBlueprintMenuUtils.h create mode 100644 EndlessVendetta/Plugins/RecentBlueprintMenu/Source/RecentBlueprintMenu/RecentBlueprintMenu.Build.cs diff --git a/EndlessVendetta/EndlessVendetta.uproject b/EndlessVendetta/EndlessVendetta.uproject index 3db11051..0d59c0a8 100644 --- a/EndlessVendetta/EndlessVendetta.uproject +++ b/EndlessVendetta/EndlessVendetta.uproject @@ -68,6 +68,11 @@ "Name": "AsyncLoadingScreen", "Enabled": true, "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/01f39767dc6b4290877f38365787cbf8" + }, + { + "Name": "RecentBlueprintMenu", + "Enabled": true, + "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/484f72297c144cf9b5c35fea3359500c" } ] } \ No newline at end of file diff --git a/EndlessVendetta/Plugins/RecentBlueprintMenu/RecentBlueprintMenu.uplugin b/EndlessVendetta/Plugins/RecentBlueprintMenu/RecentBlueprintMenu.uplugin new file mode 100644 index 00000000..cf985b2e --- /dev/null +++ b/EndlessVendetta/Plugins/RecentBlueprintMenu/RecentBlueprintMenu.uplugin @@ -0,0 +1,28 @@ +{ + "FileVersion": 3, + "Version": 101, + "VersionName": "1.01", + "FriendlyName": "Recent Blueprints Menu", + "Description": "A simple but time saving plugin that adds a \"Recent Blueprints\" menu to the blueprint editor. Giving you one click access to blueprints recently opened, and avoid having to constantly search for them.", + "Category": "Blueprint Editor", + "CreatedBy": "The Tool Shed", + "CreatedByURL": "https://unrealengine.com/marketplace/en-US/profile/The%20Tool%20Shed", + "DocsURL": "https://docs.google.com/presentation/d/10Gp1y-EPkBW8myvR-Lzd3viMDU5xovd0fSzJLOFm4NI/edit?usp=sharing", + "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/484f72297c144cf9b5c35fea3359500c", + "SupportURL": "https://forums.unrealengine.com/t/support-recent-blueprints-menu/532808", + "EngineVersion": "5.1.0", + "CanContainContent": false, + "Installed": true, + "Modules": [ + { + "Name": "RecentBlueprintMenu", + "Type": "Editor", + "LoadingPhase": "PostEngineInit", + "PlatformAllowList": [ + "Win64", + "Mac", + "Linux" + ] + } + ] +} \ No newline at end of file diff --git a/EndlessVendetta/Plugins/RecentBlueprintMenu/Resources/Icon128.png b/EndlessVendetta/Plugins/RecentBlueprintMenu/Resources/Icon128.png new file mode 100644 index 00000000..45f17dff --- /dev/null +++ b/EndlessVendetta/Plugins/RecentBlueprintMenu/Resources/Icon128.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:073d2412f964cc3713085b7ebb8edb6510a54bc4c8eaf5f01435e5cfb6f58318 +size 4930 diff --git a/EndlessVendetta/Plugins/RecentBlueprintMenu/Source/RecentBlueprintMenu/Private/RecentBlueprintMenu.cpp b/EndlessVendetta/Plugins/RecentBlueprintMenu/Source/RecentBlueprintMenu/Private/RecentBlueprintMenu.cpp new file mode 100644 index 00000000..aeb25d51 --- /dev/null +++ b/EndlessVendetta/Plugins/RecentBlueprintMenu/Source/RecentBlueprintMenu/Private/RecentBlueprintMenu.cpp @@ -0,0 +1,83 @@ +// Copyright 2022 The Tool Shed (Chris Garnier) + +#include "RecentBlueprintMenu.h" +#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION < 24 +#include "Toolkits/AssetEditorManager.h" +#endif +#include "BlueprintEditor.h" +#include "RecentBlueprintMenuUtils.h" +#include "RecentBlueprintMenuSettings.h" +#include "EditorStyleSet.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" +#include "Engine/LevelScriptBlueprint.h" + +#define LOCTEXT_NAMESPACE "FRecentBlueprintMenuModule" + +void FRecentBlueprintMenuModule::StartupModule() +{ + ///ADD NEW BLUEPRINT MENU + FBlueprintEditorModule& BlueprintEditorModule = FModuleManager::LoadModuleChecked("Kismet"); + if (const TSharedPtr MenuExtender = BlueprintEditorModule.GetMenuExtensibilityManager()) + { + BlueprintMenuExtender = MakeShareable(new FExtender); + BlueprintMenuExtender->AddMenuExtension("FileBlueprint", + EExtensionHook::Before, + nullptr, + FMenuExtensionDelegate::CreateRaw(this, &FRecentBlueprintMenuModule::OnBuildBlueprintMenu)); + MenuExtender->AddExtender(BlueprintMenuExtender); + } + + //SUBSCRIBE TO BLUEPRINT OPEN EVENTS +#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION < 24 + FAssetEditorManager::Get().OnAssetEditorRequestedOpen().AddLambda([](UObject* Asset) +#else + GEditor->GetEditorSubsystem()->OnAssetEditorRequestedOpen().AddLambda([](UObject* Asset) +#endif + { + if (Asset && Asset->GetClass() == UBlueprint::StaticClass() && Asset->GetClass() != ULevelScriptBlueprint::StaticClass()) + { + FRecentBlueprintMenuUtils::OnBlueprintOpened(Asset->GetPathName()); + } + }); +} + +void FRecentBlueprintMenuModule::ShutdownModule() +{ +} + +void FRecentBlueprintMenuModule::OnBuildBlueprintMenu(FMenuBuilder& MenuBuilder) +{ + MenuBuilder.BeginSection("RecentBlueprintMenuSection", FText::FromString("Quick Access")); + MenuBuilder.AddSubMenu(FText::FromString("Recent Blueprints..."), + FText::FromString("Select a Blueprint to edit from a list of recently opened ones."), + FNewMenuDelegate::CreateRaw(this, &FRecentBlueprintMenuModule::OnBuildRecentSubMenu), + false, + FSlateIcon(FEditorStyle::GetStyleSetName(),"GraphEditor.Timeline_16x")); + MenuBuilder.EndSection(); +} + +void FRecentBlueprintMenuModule::OnBuildRecentSubMenu(FMenuBuilder& MenuBuilder) +{ + //purge first to ensure we have a list with no bad assets + FRecentBlueprintMenuUtils::PurgeList(); + + TArray RecentList = GetDefault()->RecentBlueprints; + for (FString Entry : RecentList) + { + MenuBuilder.AddMenuEntry(FText::FromString(FPackageName::ObjectPathToObjectName(Entry)), + FText::FromString(FPackageName::ObjectPathToPackageName(Entry)), + FSlateIcon(), + FUIAction(FExecuteAction::CreateLambda([Entry]() + { +#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION < 24 + FAssetEditorManager::Get().OpenEditorForAsset(Entry); +#else + GEditor->GetEditorSubsystem()->OpenEditorForAsset(Entry); +#endif + }))); + } +} + +#undef LOCTEXT_NAMESPACE + +IMPLEMENT_MODULE(FRecentBlueprintMenuModule, RecentBlueprintMenu) \ No newline at end of file diff --git a/EndlessVendetta/Plugins/RecentBlueprintMenu/Source/RecentBlueprintMenu/Private/RecentBlueprintMenuUtils.cpp b/EndlessVendetta/Plugins/RecentBlueprintMenu/Source/RecentBlueprintMenu/Private/RecentBlueprintMenuUtils.cpp new file mode 100644 index 00000000..ecb56876 --- /dev/null +++ b/EndlessVendetta/Plugins/RecentBlueprintMenu/Source/RecentBlueprintMenu/Private/RecentBlueprintMenuUtils.cpp @@ -0,0 +1,57 @@ +// Copyright 2022 The Tool Shed (Chris Garnier) + + +#include "RecentBlueprintMenuUtils.h" +#include "RecentBlueprintMenuSettings.h" + +void FRecentBlueprintMenuUtils::OnBlueprintOpened(FString BlueprintPath) +{ + + TArray& RecentList = GetMutableDefault()->RecentBlueprints; + const int32 Index = RecentList.Find(BlueprintPath); + if (Index != INDEX_NONE) //If it's already in the list we just need to move it to the top + { + if (Index == 0) + { + return; //already first + } + RecentList.RemoveAt(Index); //removes so we can re-add at the end + } + RecentList.EmplaceAt(0, BlueprintPath); + + //Now that we've added, trim entries + const uint8 MaxBP = GetDefault()->MaxRecentBP; + if (RecentList.Num() > MaxBP) + { + TArray TrimmedList; + for (int i=0; i < RecentList.Num(); i++) + { + if (i < MaxBP) + { + TrimmedList.Add(RecentList[i]); + } + } + RecentList = TrimmedList; + } + + GetMutableDefault()->SaveConfig(); + +} + +void FRecentBlueprintMenuUtils::PurgeList() +{ + TArray& List = GetMutableDefault()->RecentBlueprints; + + TArray NewList; + + for (int32 i = 0; i < List.Num(); i++) + { + if (FPackageName::DoesPackageExist(List[i])) + { + NewList.Add(List[i]); + } + } + + List = NewList; + GetMutableDefault()->SaveConfig(); +} diff --git a/EndlessVendetta/Plugins/RecentBlueprintMenu/Source/RecentBlueprintMenu/Public/RecentBlueprintMenu.h b/EndlessVendetta/Plugins/RecentBlueprintMenu/Source/RecentBlueprintMenu/Public/RecentBlueprintMenu.h new file mode 100644 index 00000000..1aa626b4 --- /dev/null +++ b/EndlessVendetta/Plugins/RecentBlueprintMenu/Source/RecentBlueprintMenu/Public/RecentBlueprintMenu.h @@ -0,0 +1,28 @@ +// Copyright 2022 The Tool Shed (Chris Garnier) + +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleManager.h" + +class FMenuBuilder; +class FExtender; + +class FRecentBlueprintMenuModule : public IModuleInterface +{ +public: + + /** IModuleInterface implementation */ + virtual void StartupModule() override; + virtual void ShutdownModule() override; + + private: + //Delegate to generate new menu in BP + void OnBuildBlueprintMenu(FMenuBuilder&); + + //Delegate to generate new menu in BP + void OnBuildRecentSubMenu(FMenuBuilder&); + + //Pointer to the extender used for the new custom blueprint menu + TSharedPtr BlueprintMenuExtender; +}; diff --git a/EndlessVendetta/Plugins/RecentBlueprintMenu/Source/RecentBlueprintMenu/Public/RecentBlueprintMenuSettings.h b/EndlessVendetta/Plugins/RecentBlueprintMenu/Source/RecentBlueprintMenu/Public/RecentBlueprintMenuSettings.h new file mode 100644 index 00000000..94dc766a --- /dev/null +++ b/EndlessVendetta/Plugins/RecentBlueprintMenu/Source/RecentBlueprintMenu/Public/RecentBlueprintMenuSettings.h @@ -0,0 +1,25 @@ +// Copyright 2022 The Tool Shed (Chris Garnier) + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/Object.h" +#include "RecentBlueprintMenuSettings.generated.h" + +/** + * Settings class for RecentBlueprintMenu to save recent BPs and other info + */ +UCLASS(config=EditorPerProjectUserSettings) +class RECENTBLUEPRINTMENU_API URecentBlueprintMenuSettings : public UObject +{ + GENERATED_BODY() + +public: + //List of blueprints that were recently open to show in the Recent menu + UPROPERTY(Config) + TArray RecentBlueprints; + + //How many Blueprints to remember max + UPROPERTY(Config) //not presented in a menu but users can modify in ini + uint8 MaxRecentBP = 15; +}; diff --git a/EndlessVendetta/Plugins/RecentBlueprintMenu/Source/RecentBlueprintMenu/Public/RecentBlueprintMenuUtils.h b/EndlessVendetta/Plugins/RecentBlueprintMenu/Source/RecentBlueprintMenu/Public/RecentBlueprintMenuUtils.h new file mode 100644 index 00000000..ffd383a3 --- /dev/null +++ b/EndlessVendetta/Plugins/RecentBlueprintMenu/Source/RecentBlueprintMenu/Public/RecentBlueprintMenuUtils.h @@ -0,0 +1,23 @@ +// Copyright 2022 The Tool Shed (Chris Garnier) + +#pragma once + +#include "CoreMinimal.h" + +/** + * RecentBlueprintMenu class containing functions to use by the menus + */ + + +class RECENTBLUEPRINTMENU_API FRecentBlueprintMenuUtils +{ + friend class FRecentBlueprintMenuModule; //TODO: Only friend the necessary classes + +private: + //Called when a blueprint opens, so it can get saved to the recent list + static void OnBlueprintOpened(FString BlueprintPath); + + //Remove files that do not exist anymore + static void PurgeList(); + +}; diff --git a/EndlessVendetta/Plugins/RecentBlueprintMenu/Source/RecentBlueprintMenu/RecentBlueprintMenu.Build.cs b/EndlessVendetta/Plugins/RecentBlueprintMenu/Source/RecentBlueprintMenu/RecentBlueprintMenu.Build.cs new file mode 100644 index 00000000..0060ea25 --- /dev/null +++ b/EndlessVendetta/Plugins/RecentBlueprintMenu/Source/RecentBlueprintMenu/RecentBlueprintMenu.Build.cs @@ -0,0 +1,55 @@ +// Copyright 2022 The Tool Shed (Chris Garnier) + +using UnrealBuildTool; + +public class RecentBlueprintMenu : ModuleRules +{ + public RecentBlueprintMenu(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicIncludePaths.AddRange( + new string[] { + // ... add public include paths required here ... + } + ); + + + PrivateIncludePaths.AddRange( + new string[] { + // ... add other private include paths required here ... + } + ); + + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + // ... add other public dependencies that you statically link with here ... + } + ); + + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "CoreUObject", + "Engine", + "Slate", + "SlateCore", + "UnrealEd", + "EditorStyle" + // ... add private dependencies that you statically link with here ... + } + ); + + + DynamicallyLoadedModuleNames.AddRange( + new string[] + { + // ... add any modules that your module loads dynamically here ... + } + ); + } +}