Implement IModelingToolsExtension
for example in your editor module class.
GetExtensionName
GetToolSectionName
GetExtensionTools
Implement a Style class and a Commands class - these are mostly boilerplate which you can copy from below. Implement the tool classes and tool builders, basic examples below.
See also
http://www.gradientspace.com/tutorials/2022/5/27/modeling-mode-extension-plugins-in-ue5 https://github.com/gradientspace/UE5ModelingModeExtensionDemo/blob/main/Plugins/SampleModelingModeExtension/Source/SampleModelingModeExtension/Private/SampleModelingModeExtensionModule.cpp
Examples
Module class
void FBillysEditorModule::StartupModule()
{
FBillysModelingStyle::Initialize();
FBillysModelingCommands::Register();
IModularFeatures::Get().RegisterModularFeature(GetModularFeatureName(), this);
}
FText FBillysEditorModule::GetExtensionName()
{
return INVTEXT("BillysModelingTools");
}
FText FBillysEditorModule::GetToolSectionName()
{
return INVTEXT("Billys");
}
void FBillysEditorModule::GetExtensionTools(const FExtensionToolQueryInfo& QueryInfo, TArray<FExtensionToolDescription>& ToolsOut)
{
FExtensionToolDescription FlipUVs;
FlipUVs.ToolName = INVTEXT("Flip UVs");
FlipUVs.ToolCommand = FBillysModelingCommands::Get().FlipUVsTool;
FlipUVs.ToolBuilder = NewObject<UBillysFlipUVsToolBuilder>();
ToolsOut.Add(FlipUVs);
}
Style class
Largely boilerplate that you can copypaste
See also: there is at least one engine plugin which does this. It shows how you can use the plugin content directory for assets like icons.
#pragma once
#include "CoreMinimal.h"
#include "Styling/ISlateStyle.h"
#include "Styling/SlateStyle.h"
/**
*
*/
class BILLYSEDITOR_API FBillysModelingStyle
{
public:
static void Initialize();
static void Shutdown();
static TSharedPtr< class ISlateStyle > Get();
static FName GetStyleSetName();
private:
static FString InContent(const FString& RelativePath, const ANSICHAR* Extension);
static TSharedPtr< class FSlateStyleSet > StyleSet;
};
// Copyright Billys Boys 2022
#include "BillysModelingStyle.h"
#include "Styling/SlateStyleRegistry.h"
#include "Styling/SlateTypes.h"
#include "Styling/CoreStyle.h"
#include "Styling/AppStyle.h"
#include "SlateOptMacros.h"
#include "Styling/SlateStyleMacros.h"
// This is to fix the issue that SlateStyleMacros like IMAGE_BRUSH look for RootToContentDir but StyleSet->RootToContentDir is how this style is set up
#define RootToContentDir StyleSet->RootToContentDir
TSharedPtr< FSlateStyleSet > FBillysModelingStyle::StyleSet = nullptr;
TSharedPtr< class ISlateStyle > FBillysModelingStyle::Get() { return StyleSet; }
FName FBillysModelingStyle::GetStyleSetName()
{
static FName ModelingToolsStyleName(TEXT("BillysModelingStyle"));
return ModelingToolsStyleName;
}
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void FBillysModelingStyle::Initialize()
{
// Const icon sizes
const FVector2D Icon20x20(20.0f, 20.0f);
// Only register once
if (StyleSet.IsValid())
{
return;
}
StyleSet = MakeShareable(new FSlateStyleSet(GetStyleSetName()));
StyleSet->SetContentRoot(FPaths::EngineContentDir() / TEXT("Editor/Slate/"));
StyleSet->SetCoreContentRoot(FPaths::EngineContentDir() / TEXT("Slate"));
{
StyleSet->Set("BillysModelingCommands.FlipUVsTool", new IMAGE_BRUSH("Icons/icon_Blueprint_Sequence_16x", Icon20x20));
}
FSlateStyleRegistry::RegisterSlateStyle(*StyleSet.Get());
};
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
#undef IMAGE_PLUGIN_BRUSH
#undef IMAGE_BRUSH
#undef BOX_BRUSH
#undef DEFAULT_FONT
void FBillysModelingStyle::Shutdown()
{
if (StyleSet.IsValid())
{
FSlateStyleRegistry::UnRegisterSlateStyle(*StyleSet.Get());
ensure(StyleSet.IsUnique());
StyleSet.Reset();
}
}
Commands class
/**
*
*/
class BILLYSEDITOR_API FBillysModelingCommands : public TCommands<FBillysModelingCommands>
{
public:
FBillysModelingCommands();
TSharedPtr<FUICommandInfo> FlipUVsTool;
/**
* Initialize commands
*/
virtual void RegisterCommands() override;
};
#include "BillysModelingCommands.h"
#include "BillysModelingStyle.h"
#define LOCTEXT_NAMESPACE "BillysModelingCommands"
FBillysModelingCommands::FBillysModelingCommands()
: TCommands<FBillysModelingCommands>(
"BillysModelingCommands", // Context name for fast lookup
INVTEXT("Billys Modeling Commands"), // Localized context name for displaying
NAME_None, // Parent
FBillysModelingStyle::Get()->GetStyleSetName() // Icon Style Set
)
{
}
void FBillysModelingCommands::RegisterCommands()
{
UI_COMMAND(FlipUVsTool, "FlipUVs", "Flip UVs for target face", EUserInterfaceActionType::ToggleButton, FInputChord());
}
#undef LOCTEXT_NAMESPACE
Tool class and tool builder class
#include "CoreMinimal.h"
#include "BaseTools/BaseMeshProcessingTool.h"
#include "BillysFlipUVsTool.generated.h"
/**
*
*/
UCLASS()
class BILLYSEDITOR_API UBillysFlipUVsTool : public UBaseMeshProcessingTool
{
GENERATED_BODY()
public:
UBillysFlipUVsTool();
};
UCLASS()
class BILLYSEDITOR_API UBillysFlipUVsToolBuilder : public UBaseMeshProcessingToolBuilder
{
GENERATED_BODY()
public:
virtual UBaseMeshProcessingTool* MakeNewToolInstance(UObject* Outer) const override
{
return NewObject<UBillysFlipUVsTool>(Outer);
}
};
#include "BillysFlipUVsTool.h"
UBillysFlipUVsTool::UBillysFlipUVsTool()
{
UInteractiveTool::SetToolDisplayName(INVTEXT("Flip UVs"));
}