概述
在编写Godot插件和应用时,通常都需要设计一个导航结构,除了Tree
控件的树形结构之外,也可以使用分组按钮的形式,相当于一个简化的二级树形导航结构,这种形式我在自己编写的Godot插件中广泛使用。
这个自定义控件,也适用于编写Godot应用程序时,进行功能导航。本次基于Godot4.2重新编写,并通过解析自定义数据形式简化使用。
Godot4.2自定义控件系列目录
类图结构
动态生成的结构
GroupButtons
本质是根据简易数据或方法,动态生成一个嵌套的分组和基于网格容器布局的多按钮形式。
使用展示
代码
同样只需要拷贝下面的代码到你的插件或程序项目(Godot4.2或以上)中,可以命名为“GroupButtons.gd”。
# =============================================
# 名称:GroupButtons
# 类型:自定义节点(扩展控件)
# 描述:专用于显示分组按钮
# 作者:巽星石
# Godot版本:v4.2.1.stable.official [b09f793f5]
# 创建时间:2024年2月7日23:59:18
# 最后修改时间:2024年2月8日01:30:44
# =============================================
@tool
extends PanelContainer
class_name GroupButtons
## 按钮点击时触发
signal button_clicked(group_title:String,title:String)
## 包含分组和具体按钮文本的自定义数据,格式如下:[br]
## 分组标题==按钮名称||按钮名称...[br]
## 分组标题==按钮名称||按钮名称...[br]
## ...
@export_multiline var data:String = "":
set(val):
data = val
reload()
## 展开图标,显示在分组按钮右侧
@export var expand_icon:Texture2D:
set(val):
expand_icon = val
reload()
## 收起图标,显示在分组按钮右侧
@export var fold_icon:Texture2D:
set(val):
fold_icon = val
reload()
var root:VBoxContainer # 添加分组的VBox容器
# 实例化时进行初始化构建
func _init():
# 创建最基础的容器框架
var scroll = ScrollContainer.new()
var vbox = VBoxContainer.new()
vbox.size_flags_horizontal=Control.SIZE_EXPAND_FILL
scroll.add_child(vbox)
add_child(scroll)
root = vbox
# 根据data重新加载整个分组按钮列表
func reload():
# 清空原有分组
for child in root.get_children():
child.queue_free()
# 加载新分组
var datas = data.split("\n")
for dt in datas:
add_group(dt)
# 根据分组数据(形如“分组标题==按钮名称||按钮名称”)添加一个分组
func add_group(gup_data:String):
var gup_name = gup_data
var datas = gup_data.split("==")
if datas.size()>0:
gup_name = datas[0]
# 创建分组和分组标题按钮
var vbox = vbox(group_button(gup_name))
var grid = grid()
grid.columns = 2
# 创建具体的按钮
if datas.size()>1:
var btns = datas[1].split("||")
for bt in btns:
var btn = button(bt)
btn.size_flags_horizontal=Control.SIZE_EXPAND_FILL
## 按钮点击处理
btn.connect("pressed",func():
emit_signal("button_clicked",gup_name,bt)
)
grid.add_child(btn)
vbox.add_child(grid)
root.add_child(vbox)
# ============================ 控件生成函数 ============================
# ============== 说明:以下函数仅用于生成控件或容器,用于简化代码
# 创建并返回一个VBoxContainer实例,并添加child为子节点
func vbox(child:Control = null) -> VBoxContainer:
var v_box = VBoxContainer.new()
if child:
v_box.add_child(child)
return v_box
# 创建并返回一个GridContainer实例,并添加child为子节点
func grid(child:Control = null) -> GridContainer:
var g = GridContainer.new()
if child:
g.add_child(child)
return g
# 创建并返回一个按钮实例
func button(title:String) -> Button:
var btn = Button.new()
btn.text = title
return btn
# 创建并返回一个分组标题按钮实例
func group_button(gup_name:String) -> Button:
var gup_btn = button(gup_name)
gup_btn.icon = fold_icon
gup_btn.expand_icon = true
gup_btn.icon_alignment=HORIZONTAL_ALIGNMENT_RIGHT
gup_btn.flat = true
# 分组按钮点击处理
gup_btn.connect("pressed",func():
var parent = gup_btn.get_parent()
if parent is VBoxContainer:
var grid = parent.get_child(1)
if grid is GridContainer:
grid.visible = not grid.visible
if gup_btn.icon == fold_icon:
gup_btn.icon = expand_icon
else:
gup_btn.icon = fold_icon
)
return gup_btn
使用方法
添加自定义控件实例
将脚本拷贝和创建到项目中后,在具体场景中添加GroupButtons
控件实例。
设定data快速生成分组和分组下按钮
将控件放大至合适大小,在检视器面板设定参数,其中data
属性采用如下自定义形式:
分组标题==按钮名称||按钮名称...
分组标题==按钮名称||按钮名称...
...
其中:
- 每一行描述一个分组
==
之前是分组标题,==
之后是分组下的按钮标题,每个按钮标题之间用||
作为分隔
设定折叠或展开图标
expand_icon
和fold_icon
用于在分组标题的最右侧显示图标,表示分组发热折叠或展开状态。
自定义控件样式
因为本质上GroupButtons
的根节点是PanelContainer
类型,所以它本身就继承自PanelContainer
。因此你可以用自定义的StyleBox
资源灯修改它的样式。通过自定义背景颜色等,获得更好的样式:
以下是调整后的样式:
- 在制作编辑器插件时,我更倾向于不去调整原始的控件和容器样式,因为一切都可以直接跟随编辑器的样式发生变化。
处理按钮点击
- 通过连接自定义信号
button_clicked
,可以获取每个按钮的分组标题及其自身的标题,通过match
之类的分支结构,可以进行具体的逻辑处理。
信号处理代码如下:
func _on_group_button_button_clicked(group_title, title):
print(group_title,":",title)
pass
它将打印出点击的按钮所在的分组的标题及其自身的标题。