【UE5】新建Editor Standalone Window插件,之前(或当前)创建插件的按钮消失(被顶掉/占用)的问题

问题描述

当你在同一个项目里先后新建两个Editor Standalone Window的插件模板:

然后你会发现,菜单栏里并不会出现两个插件入口。先前创建的插件入口消失了。后一个插件把前一个的入口按钮给顶掉了:

之前:

新建一个TestToolBarWiondow2后:

此时发现TestToolBarWindow的按钮消失了(但是该插件并没有被我禁用)

或者新建的按钮消失了(如果没有指定依赖顺序和配置加载阶段之类的,那么插件会按照字典序先后加载。最终留下的按钮是字典序最后面的那个。。。)

原因分析

先来看下这个插件模板的按钮注册大致流程,以TestToolBarWindow 为例,注册菜单的核心逻辑在TestToolBarWindow.cpp里的这个函数:

cpp 复制代码
void FTestToolbarWindowModule::RegisterMenus()
{
	// Owner will be used for cleanup in call to UToolMenus::UnregisterOwner
	FToolMenuOwnerScoped OwnerScoped(this);

	{
		UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("LevelEditor.MainMenu.Window");
		{
			FToolMenuSection& Section = Menu->FindOrAddSection("WindowLayout");
			Section.AddMenuEntryWithCommandList(FTestToolbarWindowCommands::Get().OpenPluginWindow, PluginCommands);
		}
	}

	{
		UToolMenu* ToolbarMenu = UToolMenus::Get()->ExtendMenu("LevelEditor.LevelEditorToolBar");
		{
			FToolMenuSection& Section = ToolbarMenu->FindOrAddSection("Settings");
			{
				FToolMenuEntry& Entry = Section.AddEntry(FToolMenuEntry::InitToolBarButton(FTestToolbarWindowCommands::Get().OpenPluginWindow));
				Entry.SetCommandList(PluginCommands);
			}
		}
	}
}

它这边直接写死了获取的"LevelEditor.MainMenu.Window "这个Menu 下的名为"WindowLayout "的Section

cpp 复制代码
FToolMenuSection& Section = Menu->FindOrAddSection("WindowLayout");

所以可以得知,我们创建的两个插件的按钮都是默认写到同一个Menu的同一个Section下的。

然而:

一个ToolMenu下会有多个Section(可以当成是逻辑分组),然后每个Section里会有Entry(可以直观理解为按钮入口);当你在同一个Section里Add一个已经有同名Entry的Entry,那么会把之前的同名的Entry顶替掉。

而我们注册的按钮的Entry的名字是由啥决定的呢?

点进AddMenuEntryWithCommandList ,一路查看定义(这边就不贴全代码了),会发现Entry使用的名字,如果没有额外指定的话,实际上就是这个const TSharedPtr< const FUICommandInfo >& InCommandCommandName字段:

cpp 复制代码
FToolMenuEntry& FToolMenuSection::AddMenuEntryWithCommandList(const TSharedPtr< const FUICommandInfo >& InCommand, const TSharedPtr< const FUICommandList >& InCommandList, const TAttribute<FText>& InLabelOverride, const TAttribute<FText>& InToolTipOverride, const TAttribute<FSlateIcon>& InIconOverride, const FName InTutorialHighlightName, const TOptional<FName> InNameOverride)
{
	return AddEntry(FToolMenuEntry::InitMenuEntryWithCommandList(InCommand, InCommandList, InLabelOverride, InToolTipOverride, InIconOverride, InTutorialHighlightName, InNameOverride));
}

即我们的FTestToolbarWindowCommandsOpenPluginWindowCommandName字段。

然后我们来追溯下这个字段是怎么设置的。

来到TestToolbarWindowCommands.cpp这里:

cpp 复制代码
void FTestToolbarWindowCommands::RegisterCommands()
{
	UI_COMMAND(OpenPluginWindow, "TestToolbarWindow", "Bring up TestToolbarWindow window", EUserInterfaceActionType::Button, FInputChord());
}

看看UI_COMMAND宏的定义:

cpp 复制代码
#define UI_COMMAND( CommandId, FriendlyName, InDescription, CommandType, InDefaultChord, ... ) \
	MakeUICommand_InternalUseOnly( this, CommandId, TEXT(LOCTEXT_NAMESPACE), TEXT(#CommandId), TEXT(#CommandId) TEXT("_ToolTip"), "." #CommandId, TEXT(FriendlyName), TEXT(InDescription), CommandType, InDefaultChord, ## __VA_ARGS__ );

再进去一层看MakeUICommand_InternalUseOnly的定义:

cpp 复制代码
void MakeUICommand_InternalUseOnly( FBindingContext* This, TSharedPtr< FUICommandInfo >& OutCommand, const TCHAR* InSubNamespace, const TCHAR* InCommandName, const TCHAR* InCommandNameUnderscoreTooltip, const ANSICHAR* DotCommandName, const TCHAR* FriendlyName, const TCHAR* InDescription, const EUserInterfaceActionType CommandType, const FInputChord& InDefaultChord, const FInputChord& InAlternateDefaultChord)
{
	static const FString UICommandsStr(TEXT("UICommands"));
	const FString Namespace = InSubNamespace && FCString::Strlen(InSubNamespace) > 0 ? UICommandsStr + TEXT(".") + InSubNamespace : UICommandsStr;

	FUICommandInfo::MakeCommandInfo(
		This->AsShared(),
		OutCommand,
		InCommandName,
		FText::AsLocalizable_Advanced( Namespace, InCommandName, FriendlyName ),
		FText::AsLocalizable_Advanced( Namespace, InCommandNameUnderscoreTooltip, InDescription ),
		FSlateIcon( This->GetStyleSetName(), ISlateStyle::Join( This->GetContextName(), DotCommandName ) ),
		CommandType,
		InDefaultChord,
		InAlternateDefaultChord
	);
}

再进去一层看看MakeCommandInfo的定义,即可看到CommandName的赋值:

cpp 复制代码
void FUICommandInfo::MakeCommandInfo( const TSharedRef<class FBindingContext>& InContext, TSharedPtr< FUICommandInfo >& OutCommand, const FName InCommandName, const FText& InCommandLabel, const FText& InCommandDesc, const FSlateIcon& InIcon, const EUserInterfaceActionType InUserInterfaceType, const FInputChord& InDefaultChord, const FInputChord& InAlternateDefaultChord, const FName InBundle)
{
	ensureMsgf( !InCommandLabel.IsEmpty(), TEXT("Command labels cannot be empty") );

	LLM_SCOPE_BYTAG(UI_Slate);

	OutCommand = MakeShareable( new FUICommandInfo( InContext->GetContextName() ) );
	OutCommand->CommandName = InCommandName;
	OutCommand->Label = InCommandLabel;
	OutCommand->Description = InCommandDesc;
	OutCommand->Icon = InIcon;
	OutCommand->UserInterfaceType = InUserInterfaceType;
	OutCommand->DefaultChords[static_cast<uint8>(EMultipleKeyBindingIndex::Primary)] = InDefaultChord;
	OutCommand->DefaultChords[static_cast<uint8>(EMultipleKeyBindingIndex::Secondary)] = InAlternateDefaultChord;
	OutCommand->Bundle = InBundle;
	FInputBindingManager::Get().CreateInputCommand( InContext, OutCommand.ToSharedRef() );
}

那么真相已经浮出水面。 从MakeCommandInfo 回溯可以得知,一个CommandCommandName 就是UI_COMMAND 宏的第一个变量的这个变量名!比如这里:

cpp 复制代码
UI_COMMAND(OpenPluginWindow, "TestToolbarWindow", "Bring up TestToolbarWindow window", EUserInterfaceActionType::Button, FInputChord());

那这个Command的CommandNanme就是"OpenPluginWindow"

那怪不得会串号。。。。。。

解决方案

建议创建菜单插件的时候给你自己的插件建立一个独有的Section名字,来替换掉这里的"WindowLayout"

cpp 复制代码
FToolMenuSection& Section = Menu->FindOrAddSection("WindowLayout");
相关推荐
Unity打怪升级12 小时前
【Unity精品源码】Ultimate Character Controller:高级角色控制器完整解决方案
游戏·unity·ue5·游戏引擎·godot·游戏程序·cocos2d
zhangzhangkeji1 天前
UE5 蓝图-游老师-18-蓝图的封装,19-继承,20-多态:父类中的变量,函数、事件,分发器,实现的接口,可以被子类直接使用,也可以被子类重写重定义
ue5
每天回答3个问题2 天前
UE教程|unlua知识地图
ue5·腾讯·lua5.4
皇族崛起2 天前
【3D标注】- Unreal Engine 5.7 与 Python 交互基础
python·3d·ue5
一眼万里*e3 天前
UE中的UObject创建,销毁
ue5
吴梓穆6 天前
UE5 Perforce使用完全手册
ue5
zhangzhangkeji6 天前
UE5 蓝图-游老师-13-事件、函数、宏、事件分发器:在自定义蓝图(包括 UI 控件蓝图)中就可以创建事件分发器
ue5
Zhichao_976 天前
【UE5.3】小白人动画重定向
ue5
Zhichao_976 天前
【UE5.3】为人形角色建立Contrl Rig
ue5
竹欣6 天前
UE杂项(Mass 崩溃排查)
ue5