PART 1 · 강의 3/3

메뉴와 툴바 확장

FExtender와 UToolMenus를 사용하여 에디터 메뉴와 툴바에 커스텀 버튼 및 메뉴를 추가합니다

01

UToolMenus 시스템

UE5의 새로운 메뉴 시스템으로 에디터 메뉴를 확장하기

UE5에서는 UToolMenus가 에디터 메뉴 시스템의 표준입니다. 기존 FExtender 방식보다 더 유연하고 안전한 메뉴 등록을 지원합니다.

UToolMenus vs FExtender

UToolMenus는 UE 4.24+에서 도입된 새로운 메뉴 시스템으로, 이름 기반 메뉴 등록을 지원합니다. FExtender는 레거시 방식이지만 여전히 일부 에디터 확장점에서 사용됩니다. UE5.5+에서는 가능하면 UToolMenus를 우선 사용하세요.

메뉴 등록 기본 패턴

C++ - UToolMenus 메뉴 등록 void FMyGameEditorModule::StartupModule() { // ToolMenus가 초기화된 후에 메뉴를 등록 UToolMenus::RegisterStartupCallback( FSimpleMulticastDelegate::FDelegate::CreateRaw( this, &FMyGameEditorModule::RegisterMenus)); } void FMyGameEditorModule::RegisterMenus() { // 메인 메뉴바의 "Tools" 메뉴 확장 UToolMenu* Menu = UToolMenus::Get()->ExtendMenu( "LevelEditor.MainMenu.Tools"); FToolMenuSection& Section = Menu->AddSection( "MyGameTools", LOCTEXT("MyGameTools", "My Game Tools")); Section.AddMenuEntry( "OpenMyTool", FToolMenuEntry::InitMenuEntry( "OpenMyTool", LOCTEXT("OpenMyTool", "Open My Tool"), LOCTEXT("OpenMyToolTip", "Opens the custom tool panel"), FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.GameSettings"), FUIAction(FExecuteAction::CreateLambda([]() { FGlobalTabmanager::Get()->TryInvokeTab( FName("MyToolTab")); })) )); }

주요 메뉴 이름

메뉴 이름 위치
LevelEditor.MainMenu 메인 메뉴바 전체
LevelEditor.MainMenu.File 파일 메뉴
LevelEditor.MainMenu.Edit 편집 메뉴
LevelEditor.MainMenu.Tools 도구 메뉴
LevelEditor.LevelEditorToolBar.PlayToolBar 플레이 툴바
ContentBrowser.AssetContextMenu 에셋 우클릭 메뉴
LevelEditor.ActorContextMenu 액터 우클릭 메뉴
02

툴바 버튼 추가

레벨 에디터 툴바에 커스텀 버튼 배치하기

C++ - 툴바 버튼 등록 void FMyGameEditorModule::RegisterMenus() { // 레벨 에디터 툴바 확장 UToolMenu* ToolbarMenu = UToolMenus::Get()->ExtendMenu( "LevelEditor.LevelEditorToolBar.PlayToolBar"); FToolMenuSection& Section = ToolbarMenu->FindOrAddSection( "MyToolSection"); // 단일 버튼 Section.AddEntry( FToolMenuEntry::InitToolBarButton( "MyToolButton", FUIAction(FExecuteAction::CreateRaw( this, &FMyGameEditorModule::OnToolButtonClicked)), LOCTEXT("ToolBtn", "My Tool"), LOCTEXT("ToolBtnTip", "Open custom tool"), FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.GameSettings") )); // 드롭다운 콤보 버튼 Section.AddEntry( FToolMenuEntry::InitComboButton( "MyComboButton", FUIAction(), FOnGetContent::CreateRaw( this, &FMyGameEditorModule::GenerateDropdownMenu), LOCTEXT("ComboLabel", "Tools"), LOCTEXT("ComboTip", "Additional tools"), FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.OpenLevelBlueprint") )); } TSharedRef<SWidget> FMyGameEditorModule::GenerateDropdownMenu() { FMenuBuilder MenuBuilder(true, nullptr); MenuBuilder.AddMenuEntry( LOCTEXT("Action1", "Action 1"), LOCTEXT("Action1Tip", "First action"), FSlateIcon(), FUIAction(FExecuteAction::CreateLambda([]() { // Action 1 로직 }))); MenuBuilder.AddMenuEntry( LOCTEXT("Action2", "Action 2"), LOCTEXT("Action2Tip", "Second action"), FSlateIcon(), FUIAction(FExecuteAction::CreateLambda([]() { // Action 2 로직 }))); return MenuBuilder.MakeWidget(); }
03

UI Commands와 단축키

TCommands로 키보드 단축키를 지원하는 명령 체계 구축

에디터 명령에 키보드 단축키를 바인딩하려면 TCommands 시스템을 사용합니다.

FMyToolCommands.h #pragma once #include "Framework/Commands/Commands.h" class FMyToolCommands : public TCommands<FMyToolCommands> { public: FMyToolCommands() : TCommands<FMyToolCommands>( TEXT("MyToolCommands"), // 컨텍스트 이름 LOCTEXT("Ctx", "My Tool"), // 표시 이름 NAME_None, // 부모 컨텍스트 FAppStyle::GetAppStyleSetName() // 스타일 셋 ) {} virtual void RegisterCommands() override; // 커맨드 정의 TSharedPtr<FUICommandInfo> OpenToolPanel; TSharedPtr<FUICommandInfo> RunBatchProcess; TSharedPtr<FUICommandInfo> RefreshAssets; };
FMyToolCommands.cpp #include "FMyToolCommands.h" void FMyToolCommands::RegisterCommands() { UI_COMMAND( OpenToolPanel, "Open Tool Panel", "Opens the custom tool panel", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control | EModifierKey::Shift, EKeys::T)); UI_COMMAND( RunBatchProcess, "Run Batch Process", "Runs the batch processing tool", EUserInterfaceActionType::Button, FInputChord()); UI_COMMAND( RefreshAssets, "Refresh Assets", "Refreshes all managed assets", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control | EModifierKey::Shift, EKeys::R)); }

커맨드 바인딩 및 메뉴 연결

C++ - 모듈에서 커맨드 바인딩 void FMyGameEditorModule::StartupModule() { // 1. 커맨드 등록 FMyToolCommands::Register(); // 2. 커맨드 리스트 생성 및 매핑 CommandList = MakeShareable(new FUICommandList); CommandList->MapAction( FMyToolCommands::Get().OpenToolPanel, FExecuteAction::CreateRaw( this, &FMyGameEditorModule::OnOpenToolPanel)); CommandList->MapAction( FMyToolCommands::Get().RunBatchProcess, FExecuteAction::CreateRaw( this, &FMyGameEditorModule::OnRunBatchProcess)); // 3. UToolMenus 메뉴에 커맨드 연결 UToolMenus::RegisterStartupCallback( FSimpleMulticastDelegate::FDelegate::CreateRaw( this, &FMyGameEditorModule::RegisterMenus)); } void FMyGameEditorModule::ShutdownModule() { FMyToolCommands::Unregister(); UToolMenus::UnregisterOwner(this); }
단축키 설정 UI

등록된 커맨드는 에디터의 Edit > Editor Preferences > Keyboard Shortcuts에서 사용자가 직접 변경할 수 있습니다. 컨텍스트 이름으로 분류되어 표시됩니다.

04

FExtender (레거시 방식)

기존 코드베이스와의 호환을 위한 FExtender 패턴

UToolMenus 이전에 사용되던 FExtender 방식입니다. 기존 프로젝트 유지보수나 특정 확장점에서 여전히 필요할 수 있습니다.

C++ - FExtender 방식 TSharedPtr<FExtender> MenuExtender; void FMyGameEditorModule::StartupModule() { MenuExtender = MakeShareable(new FExtender); // 메뉴바 확장 MenuExtender->AddMenuBarExtension( "Help", // 기준 메뉴 이름 EExtensionHook::After, // 위치: 뒤에 추가 CommandList, // UI 커맨드 리스트 FMenuBarExtensionDelegate::CreateRaw( this, &FMyGameEditorModule::AddMenuBarExtension)); // 툴바 확장 MenuExtender->AddToolBarExtension( "Settings", EExtensionHook::After, CommandList, FToolBarExtensionDelegate::CreateRaw( this, &FMyGameEditorModule::AddToolBarExtension)); // 레벨 에디터에 Extender 등록 FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>( "LevelEditor"); LevelEditorModule.GetMenuExtensibilityManager()-> AddExtender(MenuExtender); LevelEditorModule.GetToolBarExtensibilityManager()-> AddExtender(MenuExtender); } void FMyGameEditorModule::AddMenuBarExtension( FMenuBarBuilder& Builder) { Builder.AddPullDownMenu( LOCTEXT("MyMenu", "My Tools"), LOCTEXT("MyMenuTip", "Custom tool menu"), FNewMenuDelegate::CreateRaw( this, &FMyGameEditorModule::FillMyMenu)); } void FMyGameEditorModule::FillMyMenu( FMenuBuilder& Builder) { Builder.AddMenuEntry( FMyToolCommands::Get().OpenToolPanel); Builder.AddMenuSeparator(); Builder.AddMenuEntry( FMyToolCommands::Get().RunBatchProcess); }
FExtender 사용 시 주의사항
  • ShutdownModule()에서 반드시 Extender를 제거해야 합니다
  • Extension hook 이름은 실제 존재하는 이름이어야 합니다
  • 새 프로젝트에서는 UToolMenus를 우선 사용하세요
05

컨텍스트 메뉴 확장

콘텐츠 브라우저, 뷰포트 우클릭 메뉴에 항목 추가

C++ - 콘텐츠 브라우저 컨텍스트 메뉴 void FMyGameEditorModule::RegisterMenus() { // 에셋 우클릭 메뉴에 항목 추가 UToolMenu* AssetMenu = UToolMenus::Get()->ExtendMenu( "ContentBrowser.AssetContextMenu"); FToolMenuSection& AssetSection = AssetMenu->FindOrAddSection( "MyGameAssetActions"); AssetSection.AddMenuEntry( "ProcessAssets", FToolMenuEntry::InitMenuEntry( "ProcessAssets", LOCTEXT("Process", "Process Selected Assets"), LOCTEXT("ProcessTip", "Run custom processing on selected assets"), FSlateIcon(), FUIAction(FExecuteAction::CreateLambda([]() { // 선택된 에셋 가져오기 TArray<FAssetData> SelectedAssets; GEditor->GetContentBrowserSelections( SelectedAssets); for (const FAssetData& Asset : SelectedAssets) { UE_LOG(LogTemp, Log, TEXT("Processing: %s"), *Asset.GetFullName()); } })) )); // 액터 우클릭 메뉴에 항목 추가 UToolMenu* ActorMenu = UToolMenus::Get()->ExtendMenu( "LevelEditor.ActorContextMenu"); FToolMenuSection& ActorSection = ActorMenu->FindOrAddSection( "MyGameActorActions"); ActorSection.AddMenuEntry( "AlignActors", FToolMenuEntry::InitMenuEntry( "AlignActors", LOCTEXT("Align", "Align to Grid"), LOCTEXT("AlignTip", "Aligns selected actors to the grid"), FSlateIcon(), FUIAction(FExecuteAction::CreateLambda([]() { // 선택된 액터 정렬 로직 })) )); }
디버깅 팁: 메뉴 이름 확인

에디터 콘솔에서 ToolMenus.Edit 명령을 입력하면 현재 등록된 모든 메뉴의 이름과 구조를 확인할 수 있습니다. 또는 Widget Reflector에서 메뉴를 선택하면 메뉴 이름을 찾을 수 있습니다.

SUMMARY

핵심 요약

  • UToolMenus는 UE5의 표준 메뉴 시스템으로, 이름 기반으로 메뉴/툴바를 확장합니다. RegisterStartupCallback으로 안전한 등록 타이밍을 보장합니다
  • TCommands로 키보드 단축키를 지원하는 명령 체계를 구축하고, FUICommandList로 실행 로직을 바인딩합니다
  • FExtender는 레거시 방식이지만 특정 확장점에서 여전히 유용합니다. 새 코드에서는 UToolMenus를 우선 사용하세요
  • 컨텍스트 메뉴 확장으로 콘텐츠 브라우저 에셋, 뷰포트 액터의 우클릭 메뉴에 커스텀 작업을 추가할 수 있습니다
  • 에디터 콘솔의 ToolMenus.EditWidget Reflector를 활용하여 메뉴 이름과 구조를 확인하세요
PRACTICE

도전 과제

배운 내용을 직접 실습해보세요

실습 1: 메인 메뉴에 커스텀 메뉴 추가

UToolMenus::RegisterStartupCallback()에서 'LevelEditor.MainMenu' 메뉴를 확장하여 커스텀 메뉴 항목을 추가하세요. FUIAction으로 클릭 핸들러를 바인딩하고, 메뉴 아이콘과 단축키도 설정하세요.

실습 2: 툴바 버튼 추가

'LevelEditor.LevelEditorToolBar.PlayToolBar' 섹션에 커스텀 툴바 버튼을 추가하세요. FSlateIcon으로 에디터 아이콘을 지정하고, ComboButton 스타일로 드롭다운 메뉴가 있는 툴바 버튼을 만드세요.

심화 과제: 컨텍스트 메뉴 확장 시스템

콘텐츠 브라우저의 에셋 컨텍스트 메뉴와 레벨 에디터의 Actor 컨텍스트 메뉴를 확장하세요. 선택된 에셋/Actor의 타입에 따라 동적으로 메뉴 항목이 변하는 컨텍스트 인식 메뉴 시스템을 구현하세요.