Editor plugins in UE4 #4 – Menu

In this tutorial, I’m going to show you, how to create a menu item and submenu. This is the end result:

Creating menu item is very similar to creating a toolbar button and a lot of steps will be identical. I recommend you to read the previous tutorial about toolbar buttons.

First, add new commands to your command class. Add as many as you want, but 3 or 4 would be enough. Once that is done, you need to map the actions. The process is the same as in the previous tutorial. You can use TestAction function created in the previous tutorial. In the example, I used anonymous functions (lambda) to create different output for each menu item.

PluginCommands->MapAction(
	FTutorialPluginEditorCommands::Get().MenuTestCommand,
	FExecuteAction::CreateRaw(this, &FTutorialPluginEditorModule::TestAction)
);
PluginCommands->MapAction(
	FTutorialPluginEditorCommands::Get().SubmenuTestCommand01,
	FExecuteAction::CreateLambda([](){UE_LOG(LogTemp, Warning, TEXT("SUBMENU 01 works!!!"));})
);
PluginCommands->MapAction(
	FTutorialPluginEditorCommands::Get().SubmenuTestCommand02,
	FExecuteAction::CreateLambda([](){UE_LOG(LogTemp, Warning, TEXT("SUBMENU 02 works!!!"));})
);
PluginCommands->MapAction(
	FTutorialPluginEditorCommands::Get().SubmenuTestCommand03,
	FExecuteAction::CreateLambda([](){UE_LOG(LogTemp, Warning, TEXT("SUBMENU 03 works!!!"));})
);

Adding the menu item is almost identical to adding a toolbar button. Instead of AddToolBarExtension you are going to use AddMenuExtension, and instead of GetToolBarExtensibilityManager you are going to use GetMenuExtensibilityManager. Also, remember to change extension hook to one of the menu extension hooks (for instance LevelEditor). There will also be a change in the signature of the function, that adds the menu item. Instead of ToolBarBuilder, the parameter will be MenuBuilder.

{
	TSharedPtr<FExtender> NewMenuExtender = MakeShareable(new FExtender);
	NewMenuExtender->AddMenuExtension("LevelEditor", 
										EExtensionHook::After, 
										PluginCommands, 
										FMenuExtensionDelegate::CreateRaw(this, &FTutorialPluginEditorModule::AddMenuEntry));

	LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(NewMenuExtender);
}

To add submenu you need to use AddSubMenu function and create yet another function, that will add individual menu items to the submenu. You can also create a custom section in the menu. This will create a separation from other section and a new extension hook. The second parameter in BeginSection is optional. If you don’t provide it, the separator will be just a line.

void FTutorialPluginEditorModule::AddMenuEntry(FMenuBuilder& MenuBuilder)
{
	MenuBuilder.BeginSection("CustomMenu", TAttribute<FText>(FText::FromString("TestMenu")));

	MenuBuilder.AddMenuEntry(FTutorialPluginEditorCommands::Get().MenuTestCommand);
	MenuBuilder.AddSubMenu(FText::FromString("My Submenu"), 
							FText::FromString("My submenu toolkit "), 
							FNewMenuDelegate::CreateRaw(this, &FTutorialPluginEditorModule::FillSubmenu));

	MenuBuilder.EndSection();
}

void FTutorialPluginEditorModule::FillSubmenu(FMenuBuilder& MenuBuilder)
{
	MenuBuilder.AddMenuEntry(FTutorialPluginEditorCommands::Get().SubmenuTestCommand01);
	MenuBuilder.AddMenuEntry(FTutorialPluginEditorCommands::Get().SubmenuTestCommand02);
	MenuBuilder.AddMenuEntry(FTutorialPluginEditorCommands::Get().SubmenuTestCommand03);
}

The same result can be achieved using lambda expressions. In my opinion in cases like this, where the functions for adding menu entry or submenu aren’t too complicated, it makes the code more readable.

{
	TSharedPtr<FExtender> NewMenuExtender = MakeShareable(new FExtender);
	NewMenuExtender->AddMenuExtension("LevelEditor", EExtensionHook::After, PluginCommands, FMenuExtensionDelegate::CreateLambda([](class FMenuBuilder& Builder)
	{
		Builder.BeginSection("CustomMenu", TAttribute<FText>(FText::FromString("TestMenu")));
		
		Builder.AddMenuEntry(FTutorialPluginEditorCommands::Get().MenuTestCommand);
		Builder.AddSubMenu(FText::FromString("My Submenu"), FText::FromString("My submenu toolkit "), FNewMenuDelegate::CreateLambda([](class FMenuBuilder& MenuBuilder)
		{
			MenuBuilder.AddMenuEntry(FTutorialPluginEditorCommands::Get().SubmenuTestCommand01);
			MenuBuilder.AddMenuEntry(FTutorialPluginEditorCommands::Get().SubmenuTestCommand02);
			MenuBuilder.AddMenuEntry(FTutorialPluginEditorCommands::Get().SubmenuTestCommand03);
		}));
		
		Builder.EndSection();
	}));

	LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(NewMenuExtender);
}

 

Source code

#pragma once
#include "ModuleManager.h"

class FToolBarBuilder;
class FMenuBuilder;
class FReply;

class FTutorialPluginEditorModule : public IModuleInterface
{
public:

	/** IModuleInterface implementation */
	virtual void StartupModule() override;
	virtual void ShutdownModule() override;

private:

	TSharedPtr<class FUICommandList> PluginCommands;

	void AddToolbarButton(FToolBarBuilder& Builder);
	
	void AddMenuEntry(FMenuBuilder& MenuBuilder);
	void FillSubmenu(FMenuBuilder& MenuBuilder);

	void TestAction();
};
#include "TutorialPluginEditor.h"
#include "TutorialPluginEditorCommands.h"
#include "LevelEditor.h"

#define LOCTEXT_NAMESPACE "FTutorialPluginEditorModule"


void FTutorialPluginEditorModule::StartupModule()
{
	FTutorialPluginEditorCommands::Register();
	
	PluginCommands = MakeShareable(new FUICommandList);
	PluginCommands->MapAction(
		FTutorialPluginEditorCommands::Get().TestCommand,
		FExecuteAction::CreateRaw(this, &FTutorialPluginEditorModule::TestAction)
	);

	PluginCommands->MapAction(
		FTutorialPluginEditorCommands::Get().MenuTestCommand,
		FExecuteAction::CreateRaw(this, &FTutorialPluginEditorModule::TestAction)
	);
	PluginCommands->MapAction(
		FTutorialPluginEditorCommands::Get().SubmenuTestCommand01,
		FExecuteAction::CreateLambda([](){UE_LOG(LogTemp, Warning, TEXT("SUBMENU 01 works!!!"));})
	);
	PluginCommands->MapAction(
		FTutorialPluginEditorCommands::Get().SubmenuTestCommand02,
		FExecuteAction::CreateLambda([](){UE_LOG(LogTemp, Warning, TEXT("SUBMENU 02 works!!!"));})
	);
	PluginCommands->MapAction(
		FTutorialPluginEditorCommands::Get().SubmenuTestCommand03,
		FExecuteAction::CreateLambda([](){UE_LOG(LogTemp, Warning, TEXT("SUBMENU 03 works!!!"));})
	);

	FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");

	{
		TSharedPtr<FExtender> NewToolbarExtender = MakeShareable(new FExtender);
		NewToolbarExtender->AddToolBarExtension("Content", 
												EExtensionHook::Before, 
												PluginCommands, 
												FToolBarExtensionDelegate::CreateRaw(this, &FTutorialPluginEditorModule::AddToolbarButton));

		LevelEditorModule.GetToolBarExtensibilityManager()->AddExtender(NewToolbarExtender);
	}

	{
		TSharedPtr<FExtender> NewMenuExtender = MakeShareable(new FExtender);
		NewMenuExtender->AddMenuExtension("LevelEditor", 
											EExtensionHook::After, 
											PluginCommands, 
											FMenuExtensionDelegate::CreateRaw(this, &FTutorialPluginEditorModule::AddMenuEntry));

		LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(NewMenuExtender);
	}

}

void FTutorialPluginEditorModule::ShutdownModule()
{
	FTutorialPluginEditorCommands::Unregister();
}

void FTutorialPluginEditorModule::AddToolbarButton(FToolBarBuilder& Builder)
{
	Builder.AddToolBarButton(FTutorialPluginEditorCommands::Get().TestCommand);
}

void FTutorialPluginEditorModule::AddMenuEntry(FMenuBuilder& MenuBuilder)
{
	MenuBuilder.BeginSection("CustomMenu", TAttribute<FText>(FText::FromString("TestMenu")));

	MenuBuilder.AddMenuEntry(FTutorialPluginEditorCommands::Get().MenuTestCommand);
	MenuBuilder.AddSubMenu(FText::FromString("My Submenu"), 
							FText::FromString("My submenu toolkit "), 
							FNewMenuDelegate::CreateRaw(this, &FTutorialPluginEditorModule::FillSubmenu));

	MenuBuilder.EndSection();
}

void FTutorialPluginEditorModule::FillSubmenu(FMenuBuilder& MenuBuilder)
{
	MenuBuilder.AddMenuEntry(FTutorialPluginEditorCommands::Get().SubmenuTestCommand01);
	MenuBuilder.AddMenuEntry(FTutorialPluginEditorCommands::Get().SubmenuTestCommand02);
	MenuBuilder.AddMenuEntry(FTutorialPluginEditorCommands::Get().SubmenuTestCommand03);
}

void FTutorialPluginEditorModule::TestAction()
{
	UE_LOG(LogTemp, Warning, TEXT("It Works!!!"));
}

#undef LOCTEXT_NAMESPACE
	
IMPLEMENT_MODULE(FTutorialPluginEditorModule, TutorialPluginEditor)

 

Leave a Reply

Your email address will not be published. Required fields are marked *