#はじめに
この記事は以下の記事の続きになりますので、こちらの記事を読んでいる前提で書きますのでご了承ください...。
UnrealC++でレベルエディタに独自メニューを追加する
前回の記事ではレベルエディタ上部に独自のメニューを追加し、そこから機能を呼び出す方法をご紹介しました。
今回は、WindowsのメニューにあるWorld OutlinerやWorld Settingsのタブの様にメニューのボタンを押したらタブが開き、タブが開いている時はメニューの左側にチェックマークが付くようにする方法をご紹介します。
今回の記事で使用するプロジェクトはUE4.27ですが、UE5でも同様に動作します。
#やり方
まずは今回作成する機能で使うモジュールを追加します。
public SamplePlugin(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
"LevelEditor",
"WorkspaceMenuStructure", // 追加.
}
);
}
続いて先程追加したモジュールの必要なヘッダーをインクルードします。
#include "WorkspaceMenuStructure.h"
#include "Widgets/Docking/SDockTab.h"
次に今回追加する独自タブの登録をしていきます。
タブ固有のIDをFName
で定義します。今回はサンプルということでタブのID = タブの名前
で作成していきます。
namespace SamplePluginDefine
{
static const FName LevelEditorName = TEXT("LevelEditor");
static const FName TabName = TEXT("TestTab");
}
とりあえず、それっぽいnamespaceで囲いタブのID兼名前となるFName
の変数を定義します。
(前回の記事ではLevelEditorの名前をマクロで定義していましたが、タブのIDと同じスタイルに修正しました)
次にGlobalTabManagerにタブを登録します。
先程定義したタブのIDとタブのSlateを生成する関数を指定します。
また、戻り値でタブのスポナーの情報を示すFTabSpawnerEntry
が貰えるのでそこにタブの左上に表示される名前やツールチップなどを設定していきます。
void FSamplePluginModule::StartupModule()
{
// ~~~
const TSharedRef<FGlobalTabmanager> GlobalTabManager = FGlobalTabmanager::Get();
GlobalTabManager->RegisterTabSpawner(
SamplePluginDefine::TabName,
FOnSpawnTab::CreateRaw(this, &FSamplePluginModule::HandleRegisterTabSpawner)
)
.SetDisplayName(FText::FromName(SamplePluginDefine::TabName))
.SetGroup(WorkspaceMenu::GetMenuStructure().GetLevelEditorCategory())
// .SetIcon(< const FSlateBrush*の形でアイコン画像を指定 >)
.SetTooltipText(LOCTEXT("TabTooltipText", "This is a test tab."));
}
余談ですが、コメントにもある通りタブの左上のアイコンを変更したい場合はSetIconでアイコンの画像を指定します。
ここで重要なのがSetGroup
で設定しているワークスペースのカテゴリです。
後述する間違った方法で実装する場合はこの設定が無くても動作しますが、今回ご紹介する正規?の実装では必ず設定する必要があります。
こちらはFWorkspaceMenuStructureModule
のコメントでも触れられています。
Engine\Source\Editor\WorkspaceMenuStructure\Public\WorkspaceMenuStructureModule.h
/**
* The module that defines a structure of the workspace menu.
* Tab spawners place themselves into one of the categories/groups
* in this structure upon registration.
*/
class FWorkspaceMenuStructureModule : public IModuleInterface
タブの登録を行ったので当然登録解除もペアで実装する必要があります。
特に難しいことをする必要はなく、タブのIDを指定して登録の解除を行います。
void FSamplePluginModule::ShutdownModule()
{
const TSharedRef<FGlobalTabmanager> GlobalTabManager = FGlobalTabmanager::Get();
GlobalTabManager->UnregisterTabSpawner(SamplePluginDefine::TabName);
// ~~~
}
次にタブを生成する部分を実装していきます。
タブを登録する部分でデリゲートに指定していた関数を追加していきます。
// タブが生成されるときに呼ばれる.
TSharedRef<SDockTab> HandleRegisterTabSpawner(const FSpawnTabArgs& TabSpawnArgs);
TSharedRef<SDockTab> FSamplePluginModule::HandleRegisterTabSpawner(const FSpawnTabArgs& TabSpawnArgs)
{
TSharedRef<SDockTab> SpawnedTab = SNew(SDockTab)
.TabRole(ETabRole::NomadTab)
//.Icon(< const FSlateBrush*の形でアイコン画像を指定 >)
[
SNew(STextBlock)
.Text(LOCTEXT("TestTabText", "This is a test tab."))
];
return SpawnedTab;
}
SDockTab
がタブの実体?でこの子ウィジェットに表示したいウィジェットを設定します。(もちろんSlateで)
TablRoleにはこのタブをどのように扱うかを指定します。今回はどのタブともドッキングできるようにNomadTab
を指定します。
タブの左上のアイコンを変更したい場合はコメントにある通りアイコン画像をconst FSlateBrush*
の型で指定します。
この時点でタブの登録自体は完了しているので、FGlobalTabmanager::TryInvokeTab
にタブのIDを指定して呼び出せばタブを表示することはできるようになりました。
最後に登録されたタブをメニューに追加する部分を実装します。
こちらもやることは簡単でFGlobalTabmanager::PopulateTabSpawnerMenu
を拡張対象のMenuBuilderとタブのIDを指定して呼び出すだけでメニューへの登録は完了です。
void FSamplePluginModule::OnPulldownMenuExtension(FMenuBuilder& MenuBuilder)
{
// ~~~
MenuBuilder.BeginSection(TEXT("SectionHook"), LOCTEXT("SectionNameC", "Section C"));
const TSharedRef<FGlobalTabmanager> GlobalTabManager = FGlobalTabmanager::Get();
GlobalTabManager->PopulateTabSpawnerMenu(MenuBuilder, SamplePluginDefine::TabName);
MenuBuilder.EndSection();
}
ここまで実装してビルドし、エディタを起動すると画像のように独自のタブをエディタメニューから呼び出せるようになりました。
(ちゃんとタブが開いている時はメニューの横にチェックマークが付きます!!)
#よくやる間違い?
MenuBuilder.AddMenuEntry(
LOCTEXT("MenuTitle1_1", "Sample Command 1"),
LOCTEXT("ToolTip1_1", "A description of this command is given here."),
FSlateIcon(),
FUIAction(FExecuteAction::CreateLambda([]()
{
const TSharedRef<FGlobalTabmanager> GlobalTabManager = FGlobalTabmanager::Get();
GlobalTabManager->TryInvokeTab(SamplePluginDefine::TabName);
}))
);
このように前回の記事でご紹介した方法でGlobalTabManagerからタブを呼び出しても同じような機能を作成できますが、タブが出ている時にメニューの左側にチェックマークがでません。
#おわりに
長々と実装方法をご紹介しましたが、この記事を要約するとチェックマークを出すのがめちゃくちゃめんどくさいということです。
エディタメニューの実装とタブの登録方法を知っていればタブの呼び出し機能自体は実装でき、おそらく使用者目線ではチェックマークがあるかないかの違いしかありません。(内部的には色々と不都合がありそう...)
意外とこのことについて触れている記事が少なかったので、今後チェックマークがでなくて困っている人のお役に立てれば幸いです。
この記事で紹介したプロジェクトは以下でダウンロードできます。
https://github.com/Naotsun19B/ExtendMenuSample
明日は、@ntenten_Qさんの「Niagaraの"Age"について」です!