skills/ue-toolbar-extension/SKILL.md
Generate boilerplate code for adding custom toolbar buttons to UE5.7 asset editors
npx skillsauth add sipherxyz/universal-ue-skills ue-toolbar-extensionInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Generate boilerplate code for adding custom toolbar buttons to Unreal Engine 5.7 asset editors (Animation Editor, Material Editor, Blueprint Editor, etc.). Uses the UToolMenus system with dynamic entries for context-aware buttons.
/ue-toolbar-extension
When invoked, ask the user these questions in order:
Ask: "Which asset editor do you want to extend with a toolbar button?"
Options:
Ask: "What should the toolbar button do?"
Gather:
Ask: "Which plugin/module should this be added to?"
Options:
After gathering inputs, generate these files:
// Copyright [Company]. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
class U{TargetAssetClass};
struct FToolMenuSection;
/**
* Toolbar extension for {EditorName}.
* Adds a "{DisplayLabel}" button to the asset editor toolbar.
*
* Uses UToolMenus to extend "{MenuPath}"
*/
class {MODULENAME}_API F{ToolbarClassName}
{
public:
F{ToolbarClassName}();
~F{ToolbarClassName}();
/** Initialize and register toolbar extension */
void Initialize();
/** Cleanup and unregister */
void Shutdown();
private:
/** Register menus with UToolMenus */
void RegisterMenus();
/** Build the toolbar entry dynamically based on editing context */
void BuildToolbarEntry(FToolMenuSection& Section);
/** Execute action when button is clicked */
void On{ButtonName}Clicked(TWeakObjectPtr<U{TargetAssetClass}> WeakAsset);
/** Whether we're initialized */
bool bIsInitialized = false;
};
// Copyright [Company]. All Rights Reserved.
#include "{ToolbarClassName}.h"
#include "{TargetAssetHeader}"
#include "Editor.h"
#include "Styling/AppStyle.h"
#include "ToolMenus.h"
#include "Toolkits/AssetEditorToolkitMenuContext.h"
#define LOCTEXT_NAMESPACE "{ToolbarClassName}"
F{ToolbarClassName}::F{ToolbarClassName}()
{
}
F{ToolbarClassName}::~F{ToolbarClassName}()
{
Shutdown();
}
void F{ToolbarClassName}::Initialize()
{
if (bIsInitialized)
{
return;
}
// Register toolbar extension using UToolMenus
UToolMenus::RegisterStartupCallback(
FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &F{ToolbarClassName}::RegisterMenus));
bIsInitialized = true;
}
void F{ToolbarClassName}::Shutdown()
{
if (!bIsInitialized)
{
return;
}
// Unregister from UToolMenus
UToolMenus::UnRegisterStartupCallback(this);
UToolMenus::UnregisterOwner(this);
bIsInitialized = false;
}
void F{ToolbarClassName}::RegisterMenus()
{
FToolMenuOwnerScoped OwnerScoped(this);
// Extend the asset editor toolbar
UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("{MenuPath}");
if (Menu)
{
FToolMenuSection& Section = Menu->FindOrAddSection("{SectionName}");
Section.Label = LOCTEXT("{SectionName}Label", "{SectionDisplayName}");
// Use dynamic entry to get access to the editing context
Section.AddDynamicEntry("{ButtonName}Button",
FNewToolMenuSectionDelegate::CreateRaw(this, &F{ToolbarClassName}::BuildToolbarEntry));
}
}
void F{ToolbarClassName}::BuildToolbarEntry(FToolMenuSection& Section)
{
// Get the context to access the asset being edited
UAssetEditorToolkitMenuContext* Context = Section.FindContext<UAssetEditorToolkitMenuContext>();
if (!Context)
{
return;
}
// Get the editing objects
TArray<UObject*> EditingObjects = Context->GetEditingObjects();
if (EditingObjects.Num() == 0)
{
return;
}
// Check if we're editing the target asset type
U{TargetAssetClass}* Asset = Cast<U{TargetAssetClass}>(EditingObjects[0]);
if (!Asset)
{
return;
}
// Create a weak pointer for the lambda (prevents dangling references)
TWeakObjectPtr<U{TargetAssetClass}> WeakAsset = Asset;
// Add the toolbar button
Section.AddEntry(FToolMenuEntry::InitToolBarButton(
"{ButtonName}",
FUIAction(
FExecuteAction::CreateRaw(this, &F{ToolbarClassName}::On{ButtonName}Clicked, WeakAsset),
FCanExecuteAction::CreateLambda([WeakAsset]() { return WeakAsset.IsValid(); })
),
LOCTEXT("{ButtonName}", "{DisplayLabel}"),
LOCTEXT("{ButtonName}Tooltip", "{Tooltip}"),
FSlateIcon(FAppStyle::GetAppStyleSetName(), "{IconName}")
));
}
void F{ToolbarClassName}::On{ButtonName}Clicked(TWeakObjectPtr<U{TargetAssetClass}> WeakAsset)
{
U{TargetAssetClass}* Asset = WeakAsset.Get();
if (!Asset)
{
return;
}
// TODO: Implement your button action here
// Example: Open a dialog, perform an operation, etc.
UE_LOG(LogTemp, Log, TEXT("{ButtonName} clicked for asset: %s"), *Asset->GetName());
}
#undef LOCTEXT_NAMESPACE
Add to your editor module's header:
// Forward declaration
class F{ToolbarClassName};
// In class definition, add member:
TUniquePtr<F{ToolbarClassName}> {ToolbarMemberName};
Add to your editor module's StartupModule():
// Initialize toolbar extension
{ToolbarMemberName} = MakeUnique<F{ToolbarClassName}>();
{ToolbarMemberName}->Initialize();
Add to your editor module's ShutdownModule():
// Shutdown toolbar extension
if ({ToolbarMemberName})
{
{ToolbarMemberName}->Shutdown();
{ToolbarMemberName}.Reset();
}
| Editor Type | Menu Path | Common Asset Classes |
|-------------|-----------|---------------------|
| Animation Editor | AssetEditor.AnimationEditor.ToolBar | UAnimSequence, UAnimMontage, UBlendSpace |
| Material Editor | AssetEditor.MaterialEditor.ToolBar | UMaterial, UMaterialInstance |
| Blueprint Editor | AssetEditor.BlueprintEditor.ToolBar | UBlueprint |
| Static Mesh Editor | AssetEditor.StaticMeshEditor.ToolBar | UStaticMesh |
| Skeletal Mesh Editor | AssetEditor.SkeletalMeshEditor.ToolBar | USkeletalMesh |
| Skeleton Editor | AssetEditor.SkeletonEditor.ToolBar | USkeleton |
| Texture Editor | AssetEditor.TextureEditor.ToolBar | UTexture2D |
| Sound Wave Editor | AssetEditor.SoundWaveEditor.ToolBar | USoundWave |
| Physics Asset Editor | AssetEditor.PhysicsAssetEditor.ToolBar | UPhysicsAsset |
| Niagara Editor | AssetEditor.NiagaraSystemEditor.ToolBar | UNiagaraSystem |
Icons.Adjust - Settings/tweak iconIcons.Edit - Pencil/edit iconIcons.Plus - Add/plus iconIcons.Delete - Delete/trash iconIcons.Refresh - Refresh/sync iconIcons.Import - Import arrow iconIcons.Export - Export arrow iconIcons.Check - Checkmark iconIcons.Warning - Warning triangle iconIcons.Info - Info circle iconAdd to your .Build.cs:
PrivateDependencyModuleNames.AddRange(new string[]
{
"ToolMenus",
"UnrealEd",
"Slate",
"SlateCore",
"EditorStyle",
// Add asset-specific modules as needed:
// "AnimationEditor",
// "MaterialEditor",
// etc.
});
// In BuildToolbarEntry(), use InitComboButton instead:
Section.AddEntry(FToolMenuEntry::InitComboButton(
"{ButtonName}",
FUIAction(),
FOnGetContent::CreateRaw(this, &F{ToolbarClassName}::GenerateDropdownMenu, WeakAsset),
LOCTEXT("{ButtonName}", "{DisplayLabel}"),
LOCTEXT("{ButtonName}Tooltip", "{Tooltip}"),
FSlateIcon(FAppStyle::GetAppStyleSetName(), "{IconName}")
));
// Add method to generate dropdown content:
TSharedRef<SWidget> F{ToolbarClassName}::GenerateDropdownMenu(TWeakObjectPtr<U{TargetAssetClass}> WeakAsset)
{
FMenuBuilder MenuBuilder(true, nullptr);
MenuBuilder.AddMenuEntry(
LOCTEXT("Option1", "Option 1"),
LOCTEXT("Option1Tooltip", "Description of option 1"),
FSlateIcon(),
FUIAction(FExecuteAction::CreateLambda([WeakAsset]()
{
// Handle option 1
}))
);
return MenuBuilder.MakeWidget();
}
See SMontageTemplateSyncDialog in MontageSynchronizerToolbar.cpp for a complete example of:
GEditor->EditorAddModalWindow()Reference implementation:
invoke: /editor-tools:ue-toolbar-extension
development
This skill should be used when implementing features in isolation using git worktrees. Triggers on "create worktree", "isolated workspace", "parallel development", or when starting implementation that should not affect main workspace.
testing
Manage VFX team issues on GitHub Projects - timeline scheduling, status updates, member commit checks, bulk assign. Use when managing VFX team project board, adding issues to timeline, checking member progress, or bulk-updating issue fields.
tools
Generate C++ validation rules from JSON definitions. Use when team updates ValidationRules.json or asks to add/modify validation rules.
development
Check codebase for Microsoft Xbox XR (Xbox Requirements) compliance issues. Scans for account picker, cloud saves, achievements, Quick Resume, and Xbox certification requirements. Use before console submission or when preparing for Microsoft certification. Triggers on "XR", "Xbox certification", "Microsoft compliance", "Xbox cert", "Xbox requirements", "GDK compliance".