Editor plugins in UE4 #3 – Toolbar button
In this tutorial, I’m going to show you, how to add a toolbar button and how to add a functionality to this button. The final result will look like this:
If you’ve created an empty plugin, you should have two files in your source folder: YourPluginName.h and YourPluginName.cpp. Inside you’ll find FYourPluginNameModule class that extends IModuleInterface. There will also be two overridden functions: StartupModule and ShutdownModule. These are called when the module is loaded and unloaded. SturtupModule is where you are going to do most of the work (at least for now).
Before you start adding buttons, you should create commands (more on commands in later tutorials). Create a new class derived from TCommand like this:
#pragma once #include "SlateBasics.h" #include "EditorStyleSet.h" class FTutorialPluginEditorCommands : public TCommands<FTutorialPluginEditorCommands> { public: FTutorialPluginEditorCommands() : TCommands<FTutorialPluginEditorCommands> ( TEXT("TutorialPluginEditor"), NSLOCTEXT("Contexts", "TutorialPluginEditor", "TutorialPluginEditor Plugin"), NAME_None, FEditorStyle::GetStyleSetName() ){} virtual void RegisterCommands() override; public: TSharedPtr<FUICommandInfo> TestCommand; };
Override function RegisterCommands and for each command add TSharedPtr<FUICommandInfo>. Inside cpp file add all commands using UI_COMMAND macro like this:
#include "TutorialPluginEditor.h" #include "TutorialPluginEditorCommands.h" #define LOCTEXT_NAMESPACE "FTutorialPluginEditorModule" void FTutorialPluginEditorCommands::RegisterCommands() { UI_COMMAND(TestCommand, "TestCommand", "This is test command", EUserInterfaceActionType::Button, FInputGesture()); } #undef LOCTEXT_NAMESPACE
Inside header file of FYourPluginNameModule add TSharedPtr<FUICommandList> PluginCommands. Then inside StartupModule call FYourPluginNameCommands::Register() and initialize PluginCommands. The last thing you need to do is to map your commands (in your case one command). For that use MapAction where the first parameter is the command and second one is the action, that should be performed. For now use FExecuteAction::CreateRaw(this, &FYourPluginName::TestAction) and create the TestAction function which will contain UE_LOG with a message, so you can see it works.
FTutorialPluginEditorCommands::Register(); PluginCommands = MakeShareable(new FUICommandList); PluginCommands->MapAction( FTutorialPluginEditorCommands::Get().TestCommand, FExecuteAction::CreateRaw(this, &FTutorialPluginEditorModule::TestAction) );
void FTutorialPluginEditorModule::TestAction() { UE_LOG(LogTemp, Warning, TEXT("It Works!!!")); }
Now you can finally add the toolbar button. And it is surprisingly easy. First, you need to get editor module of the editor, where you want to add the button. In this case, let’s use LevelEditor. You can get it by calling FModuleManager::LoadModuleChecked<FLevelEditorModule>(“LevelEditor”). Next step would be to create a FExtender and call AddToolBarExtension (comments of that function explain each parameter very well). After you’ve added the toolbar extension it’s time to add the extender to the toolbar. For that purpose, you are going to use GetToolBarExtensibilityManager.
FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor"); { TSharedPtr<FExtender> NewToolbarExtender = MakeShareable(new FExtender); /** * Extends a tool bar at the specified extension point * * @param ExtensionHook Part of the menu to extend. You can extend the same point multiple times, and extensions will be applied in the order they were registered. * @param HookPosition Where to apply hooks in relation to the extension hook * @param CommandList The UI command list responsible for handling actions for the toolbar items you'll be extending the menu with * @param ToolbarExtensionDelegate Called to populate the part of the toolbar you're extending * * @return Pointer to the new extension object. You can use this later to remove the extension. */ NewToolbarExtender->AddToolBarExtension("Content", EExtensionHook::Before, PluginCommands, FToolBarExtensionDelegate::CreateRaw(this, &FTutorialPluginEditorModule::AddToolbarButton)); LevelEditorModule.GetToolBarExtensibilityManager()->AddExtender(NewToolbarExtender); }
The last thing that remains is to implement the AddToolbarButton function.
void FTutorialPluginEditorModule::AddToolbarButton(FToolBarBuilder& Builder) { Builder.AddToolBarButton(FTutorialPluginEditorCommands::Get().TestCommand); }
There is one more important thing. You need to add “LevelEditor” and “EditorStyle” to the Build.cs file of the module. After that, compile and fire up the editor. You should have a new toolbar button that says “Test Command” and when clicked prints out “It Works!!!” into the log output.
Some extra tips
- FBlueprintEditorModule doesn’t have GetToolBarExtensibilityManager even though it clearly has a toolbar. But it has GetMenuExtensibilityManager and if you look into the source code, you’ll see, that the comment says “Gets the extensibility managers for outside entities to extend blueprint editor’s menus and toolbars“. That means, that in this case, you can use GetMenuExtensibilityManager to extend toolbar.
- Other than the case I just mentioned, you can’t use GetMenuExtensibilityManager to extend toolbars or GetToolBarExtensibilityManager to extend menus. It will compile just fine and the editor will start, but you won’t see the buttons you’ve added.
- If you try to do FBlueprintEditorModule& BlueprintEditorModule = FModuleManager::LoadModuleChecked<FBlueprintEditorModule>(“Kismet”), the code will compile, but the engine will crash when starting up. One solution I found was to change LoadingPhase in .uplugin file to PostEngineInit.
- In the tutorial, I used CreateRaw to create function delegate. However, there are other options. You can use for instance CreateStatic with static function or CreateLambda with anonymous functions.
Source code
#pragma once #include "ModuleManager.h" class FToolBarBuilder; 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 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) ); 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); } } void FTutorialPluginEditorModule::ShutdownModule() { FTutorialPluginEditorCommands::Unregister(); } void FTutorialPluginEditorModule::AddToolbarButton(FToolBarBuilder& Builder) { Builder.AddToolBarButton(FTutorialPluginEditorCommands::Get().TestCommand); } void FTutorialPluginEditorModule::TestAction() { UE_LOG(LogTemp, Warning, TEXT("It Works!!!")); } #undef LOCTEXT_NAMESPACE IMPLEMENT_MODULE(FTutorialPluginEditorModule, TutorialPluginEditor)
Leave a Reply