In our C++ editor plugin, I am trying to add a custom button to the Blueprint editor’s toolbar with no luck. I can add a custom button to the LevelEditor by hooking it into FLevelEditorModule::GetToolBarExtensibilityManager()
and it works great. But FBlueprintEditorModule
has no function GetToolBarExtensibilityManager()
, only GetMenuExtensibilityManager()
- however in its comment it says “Gets the extensibility managers for outside entities to extend blueprint editor’s menus and toolbars” so apparently this function was meant to extend toolbars as well. But when I do the same as I do for the LevelEditor no button appears. The extension delegate doesn’t even get called. Here is what I do:
// Add to blueprint tool bar
m_blueprint_tool_bar_extender = MakeShareable(new FExtender);
m_blueprint_tool_bar_extension = m_blueprint_tool_bar_extender->AddToolBarExtension("Asset", EExtensionHook::After, m_button_commands, FToolBarExtensionDelegate::CreateRaw(this, &FSkookumScriptEditor::add_skookum_button_to_blueprint_tool_bar));
FBlueprintEditorModule & asset_editor_module = FModuleManager::LoadModuleChecked<FBlueprintEditorModule>("Kismet");
m_blueprint_extension_manager = asset_editor_module.GetMenuExtensibilityManager();
m_blueprint_extension_manager->AddExtender(m_blueprint_tool_bar_extender);
and
void FSkookumScriptEditor::add_skookum_button_to_blueprint_tool_bar(FToolBarBuilder & builder)
{
#define LOCTEXT_NAMESPACE "BlueprintEditorToolBar"
FSlateIcon icon_brush = FSlateIcon(FSkookumStyles::GetStyleSetName(), "SkookumScriptEditor.ShowIDE", "SkookumScriptEditor.ShowIDE.Small");
builder.AddToolBarButton(FSkookumScriptEditorCommands::Get().m_skookum_button, NAME_None, LOCTEXT("SkookumIDEButton_Override", "Skookum IDE"), LOCTEXT("SkookumIDEButton_ToolTipOverride", "Show Skookum IDE"), icon_brush, NAME_None);
#undef LOCTEXT_NAMESPACE
}
Thanks for your help!
Ok apparently the above code actually works just fine, but only in UE4.9 and up. UE4.8 and below do not look for tool bar extensions in FBlueprintEditorModule::GetMenuExtensibilityManager()
but UE4.9 and above do.
It took some hunting to find a way to do this for our needs:
- Add a button to the blueprint editor’s toolbar for a specific native class - not all blueprint editors
- The function that button calls needs a pointer to the actual object being edited.
I wish that there was a simpler way, but here is our solution with names replaced to make it more generic.
This solution works on 4.13.
We are assuming here that you are extending MyClass to DoMyThing.
MyClassEditorToolbar.h
#pragma once
#include "MyEditorModule.h"
#include "UnrealEd.h"
class FMyClassEditorToolbar
{
public:
static void Initialize();
protected:
static void OnGatherExtensions(TSharedPtr<FExtender> Extender, UBlueprint* Blueprint);
static void ExtendToolBar(class FToolBarBuilder& Builder);
static void OnDoMyThing(class UBlueprint* Blueprint);
};
class FMyClassEditorToolbarCommands : public TCommands<FMyClassEditorToolbarCommands>
{
public:
FMyClassEditorToolbarCommands()
: TCommands<FMyClassEditorToolbarCommands>(
TEXT("MyClass"), // Context name for fast lookup
NSLOCTEXT("Contexts", "MyClass", "MyClss Editor"), // Localized context name for displaying
NAME_None, // Parent
FEditorStyle::GetStyleSetName()
) { }
// TCommand<> interface
virtual void RegisterCommands() override;
// End of TCommand<> interface
TSharedPtr<FUICommandInfo> DoMyThing;
};
MyClassEditorToolbar.cpp
#include "MyModuleHeader.h"
#include "MyClassEditorToolbar.h"
#include "BlueprintEditorModule.h"
#include "MyClass.h"
#define LOCTEXT_NAMESPACE "MyClassEditorToolbar"
// This needs to be called early - like in StartupModule()
void FMyClassEditorToolbar::Initialize()
{
FMyClassEditorToolbarCommands::Register();
auto& BlueprintModule = FModuleManager::LoadModuleChecked<FBlueprintEditorModule>("Kismet");
BlueprintModule.OnGatherBlueprintMenuExtensions().AddStatic(&FMyClassEditorToolbar::OnGatherExtensions);
}
void FMyClassEditorToolbar::ExtendToolBar(class FToolBarBuilder& Builder)
{
FSlateIcon IconBrush = FSlateIcon(
FEditorStyle::GetStyleSetName(),
"MyClassEditor.DoMyThing",
"MyClassEditor.DoMyThing.Small");
const FMyClassEditorToolbarCommands& Commands = FMyClassEditorToolbarCommands::Get();
Builder.AddToolBarButton(
Commands.DoMyThing,
NAME_None,
TAttribute<FText>(),
TAttribute<FText>(),
IconBrush,
NAME_None);
}
void FMyClassEditorToolbar::OnGatherExtensions(TSharedPtr<FExtender> Extender, UBlueprint* Blueprint)
{
// This is called for all blueprint editors.
// So first make sure we want to modify the toolbar for this one.
if (Blueprint == nullptr)
return;
if (Blueprint->ParentClass == nullptr)
return;
if (Blueprint->ParentClass->IsChildOf(UMyClass::StaticClass()) == false)
return;
const FMyClassEditorToolbarCommands& Commands = FMyClassEditorToolbarCommands::Get();
// This specific editor needs its own Command List with delegates that include the blueprint pointer
TSharedPtr<FUICommandList> CommandList = MakeShareable(new FUICommandList);
CommandList->MapAction(
Commands.DoMyThing,
FExecuteAction::CreateStatic(
&FMyClassEditorToolbar::OnDoMyThing,
Blueprint));
Extender->AddToolBarExtension(
"Asset",
EExtensionHook::After,
CommandList,
FToolBarExtensionDelegate::CreateStatic(&FMyClassEditorToolbar::ExtendToolBar));
}
void FMyClassEditorToolbar::OnDoMyThing(UBlueprint* Blueprint)
{
UMyClass* MyClass;
{
if (Blueprint == nullptr)
return;
UClass* MyClassClass = Blueprint->GeneratedClass;
if (MyClassClass == nullptr)
return;
if (MyClassClass->IsChildOf(UMyClass::StaticClass()) == false)
return;
MyClass = Cast<UMyClass>(MyClassClass->GetDefaultObject());
}
UWorld* World = TLEditorUtilities::GetEditorWorld();
if (World == nullptr)
return;
// This is where we pull it all together and do whatever the button does
FMyClassSomewhere::DoMyThing(World, MyClass);
}
//////////////////////////////////////////////////////////////////////////
// FMyClassEditorToolbarCommands
void FMyClassEditorToolbarCommands::RegisterCommands() {
// Show toggles
UI_COMMAND(GenerateMyClass, "Do My Thing", "Explain what Do My Thing does in more words", EUserInterfaceActionType::Button, FInputChord());
}
#undef LOCTEXT_NAMESPACE