Claude
Skills
Sign in
Back

ue-editor-tools

Included with Lifetime
$97 forever

Use when extending the Unreal Editor with editor tool, editor utility widget, Blutility, detail customization, property customization, editor mode, asset editor, editor subsystem, editor extension, UToolMenus, or scripted asset operations. For Slate fundamentals, see ue-ui-umg-slate. For module build config, see ue-module-build-system.

Design

What this skill does


# UE Editor Tools

You are an expert in extending the Unreal Editor with custom tools and workflows.

## Context

Read `.agents/ue-project-context.md` for editor module structure, engine version, team workflows, and project-specific conventions before providing guidance.

## Before You Start

Ask which area the user needs if not clear:
- **Editor Utility Widget** — UMG panel run from editor right-click
- **Blutility** — UAssetActionUtility or UActorActionUtility scripted actions
- **Detail Customization** — Custom property panel (IDetailCustomization / IPropertyTypeCustomization)
- **Custom Editor Mode** — Viewport mode with specialized interaction (FEdMode)
- **Asset Type Actions** — Content Browser integration for a custom asset type
- **Editor Subsystem** — Editor-lifetime singleton (UEditorSubsystem)
- **Menu / Toolbar Extension** — UToolMenus additions to main menu, toolbars, context menus

---

## Editor Module Setup

All editor-extending code must live in a module with `"Type": "Editor"`. Never put `UnrealEd` / `PropertyEditor` includes in a Runtime module without `#if WITH_EDITOR` guards.

```json
{ "Name": "MyGameEditor", "Type": "Editor", "LoadingPhase": "PostEngineInit" }
```

```csharp
// MyGameEditor.Build.cs — key dependencies
PrivateDependencyModuleNames.AddRange(new string[] {
    "Core", "CoreUObject", "Engine", "UnrealEd",
    "Slate", "SlateCore", "EditorStyle",
    "PropertyEditor",   // IDetailCustomization, IPropertyTypeCustomization
    "EditorSubsystem",  // UEditorSubsystem
    "Blutility",        // UEditorUtilityWidget, UAssetActionUtility
    "ToolMenus",        // UToolMenus
    "AssetTools",       // FAssetTypeActions_Base
    "MyGame"
});
```
Module skeleton — every registration in `StartupModule` must be mirrored in `ShutdownModule`:
```cpp
IMPLEMENT_MODULE(FMyGameEditorModule, MyGameEditor)

void FMyGameEditorModule::StartupModule()
{
    FPropertyEditorModule& PropMod =
        FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
    PropMod.RegisterCustomClassLayout(
        UMyDataAsset::StaticClass()->GetFName(),
        FOnGetDetailCustomizationInstance::CreateStatic(
            &FMyDataAssetCustomization::MakeInstance));
    PropMod.NotifyCustomizationModuleChanged();

    UToolMenus::RegisterStartupCallback(
        FSimpleMulticastDelegate::FDelegate::CreateRaw(
            this, &FMyGameEditorModule::RegisterMenus));
}

void FMyGameEditorModule::ShutdownModule()
{
    if (FModuleManager::Get().IsModuleLoaded("PropertyEditor"))
    {
        FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor")
            .UnregisterCustomClassLayout(UMyDataAsset::StaticClass()->GetFName());
    }
    UToolMenus::UnRegisterStartupCallback(this);
    if (UToolMenus* TM = UToolMenus::TryGet()) { TM->UnregisterOwner(this); }
}
```

> Full boilerplate with asset type actions, editor modes, and factory: `references/editor-module-setup.md`

---

## Editor Utility Widgets

`UEditorUtilityWidget` (from `EditorUtilityWidget.h`) extends `UUserWidget` for editor-only UMG panels. Create as a Blueprint subclass (right-click Content Browser > Editor Utilities > Editor Utility Widget). **Run**: right-click the widget asset > Run Editor Utility Widget. Or subclass in C++:

```cpp
#pragma once
#include "EditorUtilityWidget.h"
#include "MyEditorUtilityWidget.generated.h"

UCLASS()
class MYGAMEEDITOR_API UMyEditorUtilityWidget : public UEditorUtilityWidget
{
    GENERATED_BODY()
public:
    UFUNCTION(BlueprintCallable, Category = "My Tools")
    void BatchRenameSelectedAssets(const FString& Prefix);
};

// .cpp
void UMyEditorUtilityWidget::BatchRenameSelectedAssets(const FString& Prefix)
{
    for (UObject* Asset : UEditorUtilityLibrary::GetSelectedAssets())
    {
        if (Asset)
        {
            const FString Src = Asset->GetOutermost()->GetName(); // e.g. /Game/Folder/OldName
            UEditorAssetLibrary::RenameAsset(Src, FPaths::GetPath(Src) / Prefix + TEXT("_") + Asset->GetName());
        }
    }
}
```

Key `UEditorUtilityLibrary` functions (from `EditorUtilityLibrary.h`):

| Function | Purpose |
|---|---|
| `GetSelectedAssets()` | Currently selected Content Browser assets |
| `GetSelectedAssetsOfClass(UClass*)` | Filter selection by class |
| `UEditorAssetLibrary::DeleteAsset(Path)` | Delete asset; `RenameAsset` for move/rename |
| `GetSelectionSet()` | Selected level actors |
| `SyncBrowserToFolders(TArray<FString>)` | Sync content browser view |

**Content browser filters**: Use `FARFilter` with `IAssetRegistry::GetAssets` for programmatic asset queries by class, path, or tags.

Open a widget programmatically:

```cpp
GEditor->GetEditorSubsystem<UEditorUtilitySubsystem>()
    ->SpawnAndRegisterTab(LoadObject<UEditorUtilityWidgetBlueprint>(
        nullptr, TEXT("/Game/EditorWidgets/BP_MyTool")));
```

---

## Blutility: Scripted Actions

### UAssetActionUtility — Asset Right-Click Actions

Any `UFUNCTION(CallInEditor)` on a `UAssetActionUtility` subclass appears in the Content Browser context menu. Set `SupportedClasses` in Class Defaults to filter by asset type.

```cpp
#pragma once
#include "AssetActionUtility.h"   // Engine/Source/Editor/Blutility/Classes/AssetActionUtility.h
#include "MyAssetActionUtility.generated.h"

UCLASS()
class MYGAMEEDITOR_API UMyAssetActionUtility : public UAssetActionUtility
{
    GENERATED_BODY()
public:
    UFUNCTION(CallInEditor, Category = "My Tools")
    void SetTextureCompressionToUI()
    {
        for (UObject* Asset : UEditorUtilityLibrary::GetSelectedAssets())
        {
            if (UTexture2D* Tex = Cast<UTexture2D>(Asset))
            {
                Tex->CompressionSettings = TC_EditorIcon;
                Tex->MarkPackageDirty();
                Tex->PostEditChange();
            }
        }
    }
};
```

### UActorActionUtility — Actor Right-Click Actions

Same pattern for level actors. Both `UAssetActionUtility` and `UActorActionUtility` inherit from `UEditorUtilityObject`, the base class for all Blutility actions.

---

## Detail Customizations

### Class Customization — IDetailCustomization

```cpp
class FMyDataAssetCustomization : public IDetailCustomization
{
public:
    static TSharedRef<IDetailCustomization> MakeInstance();
    virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
    // Override the TSharedPtr overload too — store WeakBuilder for async ForceRefreshDetails
    virtual void CustomizeDetails(const TSharedPtr<IDetailLayoutBuilder>& DetailBuilder) override;
private:
    TWeakPtr<IDetailLayoutBuilder> WeakBuilder;
};
// In CustomizeDetails: EditCategory, AddProperty, AddCustomRow, HideCategory
// Register: PropMod.RegisterCustomClassLayout(UMyClass::StaticClass()->GetFName(), ...MakeInstance)
// Unregister in ShutdownModule: PropMod.UnregisterCustomClassLayout(...)
```

### Struct Customization — IPropertyTypeCustomization

```cpp
class FMyStructCustomization : public IPropertyTypeCustomization
{
public:
    static TSharedRef<IPropertyTypeCustomization> MakeInstance();
    virtual void CustomizeHeader(TSharedRef<IPropertyHandle> Handle,
        FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& Utils) override;
    virtual void CustomizeChildren(TSharedRef<IPropertyHandle> Handle,
        IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& Utils) override;
};
// Register: PropMod.RegisterCustomPropertyTypeLayout(FMyStruct::StaticStruct()->GetFName(), ...MakeInstance)
```

> Full implementations, Slate row patterns, IPropertyHandle read/write, NameContent/ValueContent: `references/detail-customization-patterns.md`

---

## Custom Editor Modes

`FEdMode` (from `EdMode.h`) provides specialized viewport interaction. Register globally; only one active per viewport at a time.

```cpp
// MyEditorMode.h
class FMyEditorMode : public FEdMode
{
public:
    static const FEditorModeID EM_MyMode;
    FMyEditorMode();
    virtual void Enter() override;
    virtual void Exit(

Related in Design