概述
关于Godot的手动菜单栏制作,我已经在之前的文章中有所描述。
但是对于一些场景,手动制作菜单仍然是一个比较低效的做法。所以我将MenuBar
以及基于HBoxContainer
+MenuButton
创建菜单栏写成了一个静态函数库。
利用此函数库我们可以用函数形式构造PopupMenu
和MenuButton
、MenuBar
以及基于HBoxContainer
+MenuButton
创建的菜单栏。
另外我自定义3种资源,来定义和存储单个菜单栏、单个菜单和单个菜单项所需的数据:
MenuBarData
:存储定义单个MenuBar所需的数据MenuData
:存储定义单个菜单所需的数据PopupMenuItem
:存储单个菜单项所需的数据
当然我的本意是基于这个函数库构建更简单的菜单栏自定义控件。
函数库代码
# ========================================
# 名称:menuDB
# 类型:静态函数库
# 描述:专用于【生成菜单栏】的静态函数库
# 作者:巽星石
# Godot版本:v4.2.1.stable.official [b09f793f5]
# 创建时间:2024年2月27日18:45:28
# 最后修改时间:2024年2月27日21:51:11
# ========================================
class_name menuDB
# -------------- 构造MenuBar或基于HBoxContainer+MenuButton的水平菜单栏 --------------
# 构造并返回有完整结构的MenuBar
static func menu_bar(menu_bar_data:MenuBarData) -> MenuBar:
var bar = MenuBar.new()
# 遍历MenuBar数据,创建PopUp
for menu_data in menu_bar_data.menus:
var menu = popup_menu(menu_data.text,menu_data.items,menu_bar_data.icon_max_width)
bar.add_child(menu)
return bar
# 构造并返回多个MenuButton水平排列的HBoxContainer
static func hbox_menu_bar(menu_bar_data:MenuBarData) -> HBoxContainer:
var bar = HBoxContainer.new()
# 遍历MenuBar数据,创建PopUp
for menu_data in menu_bar_data.menus:
var menu = menu_button(menu_data.text,menu_data.icon,menu_data.items,menu_bar_data.icon_max_width)
bar.add_child(menu)
return bar
# -------------- 构造 PopupMenu、MenuButton --------------
# 构造并返回一个带有菜单项的PopupMenu
static func popup_menu(
text:String = "MenuButton", # 按钮文本
items:Array[PopupMenuItem] = [], # 菜单项数据集
items_icon_max_width = 16 # 菜单项图标的最大宽度
) -> PopupMenu:
# --------------- 创建MenuButton ---------------
var menu = PopupMenu.new()
menu.name = text
# --------------- 遍历items,为PopupMenu添加菜单项 ---------------
for item in items:
popup_menu_add_item(menu,item,items_icon_max_width)
return menu
# 构造并返回带有菜单项的MenuButton
static func menu_button(
text:String = "MenuButton", # 按钮文本
icon:Texture2D = null, # 按钮图标
items:Array[PopupMenuItem] = [], # 菜单项数据集
items_icon_max_width = 16 # 菜单项图标的最大宽度
) -> MenuButton:
# --------------- 创建MenuButton ---------------
var btn = MenuButton.new()
btn.text = text
btn.icon = icon
# --------------- 获取其PopupMenu ---------------
var menu:PopupMenu = btn.get_popup()
# --------------- 遍历items,为PopupMenu添加菜单项 ---------------
for item in items:
popup_menu_add_item(menu,item,items_icon_max_width)
return btn
# -------------- PopupMenu菜单项添加 --------------
# 通过PopupMenuItem资源数据为PopupMenu添加一个菜单项
static func popup_menu_add_item(menu:PopupMenu,item:PopupMenuItem,icon_max_width = 16) -> void:
var index = menu.item_count # 当前项索引 = 已有菜单项总数
# 创建纯文本菜单项
menu.add_item(item.text)
# 设置附加选项
if item.icon: # 图标
menu.set_item_icon(index,item.icon)
menu.set_item_icon_max_width(index,icon_max_width) # 图标最大宽度
if item.checkable: # 复选框 形式
menu.set_item_as_checkable(index,item.checkable)
if item.radio_checkable: # 单选框 形式
menu.set_item_as_radio_checkable(index,item.radio_checkable)
if item.separator: # 带分割线 形式
menu.set_item_as_separator(index,item.separator)
if item.shortcut: # 快捷键 + 是否全局
menu.set_item_shortcut(index,item.shortcut,item.shortcut_global)
menu.set_item_shortcut_disabled(index,item.shortcut_disabled) # 快捷键是否禁用
menu.set_item_checked(index,item.checked) # 是否选中(仅在单选框或复选框形式下)
menu.set_item_disabled(index,item.disabled) # 是否禁用
menu.set_item_tooltip(index,item.tooltip) # 鼠标提示文本
if item.sub_menu.size() > 0: # 如果菜单项存在子菜单项
var sub_popup = PopupMenu.new() # 创建子菜单
sub_popup.name = "%d_%s_sub_popup" % [index,item.text] # 创建唯一名称
# 添加子菜单项
for sub_item in item.sub_menu:
popup_menu_add_item(sub_popup,sub_item,icon_max_width)
# 添加为当前PopupMenu的子节点
menu.add_child(sub_popup)
menu.set_item_submenu(index,sub_popup.name) # 为当前项指定子菜单的名称
3种自定义数据结构
MenuBar
@tool
# 单个MenuBar数据
class_name MenuBarData extends Resource
@export var menus:Array[MenuData] # 菜单列表
@export var icon_max_width = 16 # 菜单项图标的最大宽度
MenuData
@tool
# 单个菜单数据
class_name MenuData extends Resource
@export var text:String # 按钮文本
@export var icon:Texture2D # 按钮图标
@export var items:Array[PopupMenuItem] # 菜单项
PopupMenuItem
@tool
# PopupMenu单个菜单项数据
class_name PopupMenuItem extends Resource
# 基础设置
@export var text:String # 文本
@export var icon:Texture2D # 图标
# 特殊形式
@export var checkable:bool # 复选框 形式
@export var radio_checkable:bool # 单选框 形式
@export var separator:bool # 带分割线 形式
# 快捷键
@export var shortcut:Shortcut # 快捷键
@export var shortcut_global:bool # 快捷键是否全局
@export var shortcut_disabled:bool # 快捷键是否禁用
# 选中与禁用
@export var checked:bool # 选中
@export var disabled:bool # 禁用
# 鼠标提示文本
@export var tooltip:String # 鼠标提示文本
# 子菜单
@export var sub_menu:Array[PopupMenuItem] # 子菜单
基于函数库创建自定义MenuBar节点
# ========================================
# 基于menuDB函数库的自定义MenuBar节点
# ========================================
@tool
class_name myMenuBar extends MenuBar
@export var data:MenuBarData:
set(val):
data = val
create()
# 根据MenuBarData创建菜单和菜单项
func create() -> void:
# 遍历MenuBar数据,创建PopUp
for menu_data in data.menus:
var menu = menuDB.popup_menu(menu_data.text,menu_data.items,data.icon_max_width)
add_child(menu)
我们可以看到myMenuBar
类型有一个Data
参数,我们需要为其设定MenuBarData
类型的资源。
运行后的效果如下:
可以看到这种自定义资源多层嵌套形式在Godot检视器面板上就是一种灾难。
所以更好的做法是基于字典的解析。