跳轉至

UE 使用路徑形式擴展選單

記錄在 UE 中實現路徑形式擴展菜單的方法。

如果對UE擴展選單不熟悉,建議先簡短地查看一下:UE擴展編輯器菜單

本篇文章的程式碼是基於這個插件:UE.EditorPlus

節點管理

以樹狀結構管理菜單,父節點可以包含子項目:

class EDITORPLUS_API FEditorPlusMenuBase: public TSharedFromThis<FEditorPlusMenuBase>
{
protected:
    // sub menus
    TArray<TSharedRef<FEditorPlusMenuBase>> Children;
}

在建立父節點的同時建立子節點:

void FEditorPlusMenuBase::Register(FMenuBuilder& MenuBuilder)
{
    for (const auto Menu: Children)
    {
        Menu->Register(MenuBuilder);
    }
}

當然每個節點的具體創建行為會有點不同,覆寫虛函數來實現:

// Menubar
void FEditorPlusMenuBar::Register(FMenuBarBuilder& MenuBarBuilder)
{
    MenuBarBuilder.AddPullDownMenu(
        GetFriendlyName(),
        GetFriendlyTips(),
        // Delegate to call Register
        FEditorPlusMenuManager::GetDelegate<FNewMenuDelegate>(GetUniqueId()),       
        Hook);
}

// Section
void FEditorPlusSection::Register(FMenuBuilder& MenuBuilder)
{
    MenuBuilder.BeginSection(Hook, GetFriendlyName());
    FEditorPlusMenuBase::Register(MenuBuilder);
    MenuBuilder.EndSection();
}

// Separator
void FEditorPlusSeparator::Register(FMenuBuilder& MenuBuilder)
{
    MenuBuilder.AddMenuSeparator(Hook);
    FEditorPlusMenuBase::Register(MenuBuilder);
}

// SubMenu
void FEditorPlusSubMenu::Register(FMenuBuilder& MenuBuilder)
{
    MenuBuilder.AddSubMenu(
        GetFriendlyName(),
        GetFriendlyTips(),
        FNewMenuDelegate::CreateSP(this, &FEditorPlusSubMenu::MakeSubMenu),
        false,
        FSlateIcon(),
        true,
        Hook
    );
}

// Command
void FEditorPlusCommand::Register(FMenuBuilder& MenuBuilder)
{
    MenuBuilder.AddMenuEntry(
        CommandInfo->Label, CommandInfo->Tips, CommandInfo->Icon,
        CommandInfo->ExecuteAction, CommandInfo->Hook, CommandInfo->Type);
}

// ......

透過路徑生成節點

按照樹形結構組織好菜單,路徑格式就可以定義一條菜單的樹形結構:

"/<Hook>Help/<MenuBar>BarTest/<SubMenu>SubTest/<Command>Action"

以上路径可以定義一系列菜單的創建:

  • <Hook>Help:位置在 Hook 名稱為 Help 的選單後 <MenuBar>BarTest:建立名為 BarTest 的 MenuBar 菜單类型。
  • <SubMenu>SubTest:建立子節點,類型為 SubMenu,名稱為 SubTest
  • <Command>Action:最後創建一個命令

接口調用的形式可以非常簡潔:

const FString Path = "/<Hook>Help/<MenuBar>BarTest/<SubMenu>SubTest/<Command>Action";
FEditorPlusPath::RegisterPathAction(
    Path, 
    FExecuteAction::CreateLambda([]
    {
        // do action
    })
);

自訂格式生成節點。

我們依然保留了笨重的方式來創建菜單,笨重的方式可以允許有更詳細的設定,代碼的組織形式跟 UE 的 SlateUI 寫法有些像:

EP_NEW_MENU(FEditorPlusMenuBar)("BarTest")
->RegisterPath()
->Content({
    EP_NEW_MENU(FEditorPlusSubMenu)("SubTest")
    ->Content({
        EP_NEW_MENU(FEditorPlusCommand)("Action")
        ->BindAction(FExecuteAction::CreateLambda([]
            {
                // do action
            })),
    })
});

混合形式

當然,原生的路徑形式和自訂生成的選單,兩者是相同的,可以互相混用,具有極大的靈活性:

FEditorPlusPath::RegisterPath(
    "/<MenuBar>BarTest/<SubMenu>SubMenu/<Command>Action1", 
    EP_NEW_MENU(FEditorPlusCommand)("Action1")
    ->BindAction(FExecuteAction::CreateLambda([]
        {
            // do action
        })),
);

FEditorPlusPath::RegisterPath(
    "/<MenuBar>BarTest/<SubMenu>SubMenu/<Command>Action2", 
    EP_NEW_MENU(FEditorPlusCommand)("Action2")
    ->BindAction(FExecuteAction::CreateLambda([]
        {
            // do action
        })),
);

多個地方定義的菜單,會合併到同一個樹狀結構中,名字相同的節點會認為是同一個節點。換言之,路徑是唯一的,同一個路徑可以唯一確定一個菜單節點。 於是我們也可以把節點找出來,重新做一些設置和修改:

// set Name and Tips
FEditorPlusPath::GetNodeByPath("/<MenuBar>BarTest")->SetFriendlyName(LOCTEXT("MenuTest", "MenuTest"))->SetFriendlyTips(LOCTEXT("MenuTestTips", "MenuTestTips"));

Original: https://wiki.disenone.site/tc

This post is protected by CC BY-NC-SA 4.0 agreement, should be reproduced with attribution.

此帖是使用 ChatGPT 翻譯的,請在反饋指出任何遺漏之處。