前言
本文依旧来自笔者的语雀知识库。基础内容写于2023年8月份。当时写的比较随意,本篇将在其基础上扩充和修改。
概述
Godot本身提供了丰富的控件和容器来实现UI布局,但是这个过程往往需要复杂的手动操作和配置,使用代码生成方式时也会需要一大堆代码来创建控件、容器实例,并设置相关的属性。尤其是在编写自定义控件时,不依赖场景文件.tscn,一个.gd文件就是一个自定义控件的全部,所有的节点结构都需要用代码生成。
- 简单的自定义控件:只是扩展自Godot的内置控件或容器类型,或者只涉及一些简单的动态生成。
- 复杂的自定义控件:往往需要涉及复杂的容器布局和控件嵌套,如果完全用普通代码创建容器布局和新建控件,并设置属性,将是一种灾难。我们将看到一个自定义控件用了大量(几十行甚至上百行)的代码用于生成容器和控件,并将它们用
add_child()
联系起来。
为了简化控件和容器实例的创建,并快速创建特定的控件布局,本人(Bilibili@巽星石)编写了这套控件节点生成和布局函数库并起名叫Ctl
。(后期可能会改名)
这是一个基于Godot4.1的静态函数库(目前基于4.2.1),并以全局类注册,所以你可以单独复制Ctl.gd
到你的项目中,并快速的开始使用它。
整个函数库分为以下几部分内容:
- 控件生成函数
- 布局函数
- 属性设置函数
控件生成函数设计的来由
简化控件生成代码,比较好的一种思路就是将控件生成函数化,也就是编写控件生成函数,调用函数并传入几个简单参数,就可以生成并返回一个控件实例。
比如我们编写了一个如下的Label
控件生成函数:
func label(text:String,tool_tip:String="",name:String = "label") -> Label:
var ctl = Label.new()
ctl.name = name
ctl.text = text
ctl.tooltip_text = tool_tip
return ctl
那么我们就可以调用这个函数来创建一个Label
控件实例:
var lab = label("测试")
等价的普通写法:
var lab = Label.new()
lab.text = "测试"
lab.tooltip_text = ""
lab.name = "label"
因为设计了带默认值的参数,所以,调用生成函数,比普通写法省下了3行。
如果将几乎所有的控件生成都编写为函数,在大量、频繁的动态控件生成中,将省下巨大的代码量。
布局描述
Godot用容器和容器嵌套来实现复杂UI布局。这里我用一个VBoxContainer
嵌套多个HBoxContainer
形式,实现了一种相对使用GridContainer
更自由的多行布局。这种布局有点类似于古早网页设计中的表格布局方式。
实际结构类似于下面这样:
VBox
HBox
Lab
Lab
Lab
HBox
Texture
HBox
Lab
- 最外面的
VBoxContainer
负责纵向排列。 VBoxContainer
的一级子节点都是HBoxContainer
,代表一行- 每一行中可以出现1个或多个控件,它们将自动水平排列,并共同占满一行的空间
这种布局比较适合描述简单的表单,卡片布局。具体用法可以参考文章后半段的实例。
除了这种自创的特殊布局之外,我还添加了描述左右或上下分割、多选项卡之类的布局,并使其能够方便嵌套和使用。
题外话: 关于节点嵌套结构的描述和动态生成,有多种不同的方案,我自己就搞过两三种。
这里的函数和函数嵌套方式只是其中的一种。
其他方式后续会陆续发布文章阐述。
源码
# ========================================================
# 名称:Ctl
# 类型:静态函数库
# 简介:控件节点生成函数库
# 作者:巽星石
# Godot版本:4.1-stable (official)
# 创建时间:2023-08-04 01:37:41
# 最后修改时间:2023年8月6日23:37:45
# ========================================================
class_name Ctl
# 选择框
static func select(items:PackedStringArray,name:String = "select") -> OptionButton:
var ctl = OptionButton.new()
ctl.name = name
for item in items:
if item == "---":
ctl.add_separator()
elif item.begins_with("---") and item != "---":
ctl.add_separator(item.replace("---",""))
else:
ctl.add_item(item)
return ctl
# 数字微调框
static func spin(min:float = 0.0,max:float = 100.0,step:float =1.0,name:String = "spin") -> SpinBox:
var ctl = SpinBox.new()
ctl.name = name
ctl.min_value = min
ctl.max_value = max
ctl.step = step
return ctl
# 单行文本框
static func text(placeholder:String="",text:String = "",name:String = "text") -> LineEdit:
var ctl = LineEdit.new()
ctl.name = name
ctl.text = text
ctl.placeholder_text = placeholder
return ctl
# 多行文本框
static func textArea(placeholder:String="",text:String = "",name:String = "textArea") -> TextEdit:
var ctl = TextEdit.new()
ctl.name = name
ctl.text = text
ctl.placeholder_text = placeholder
return ctl
# 标签
static func label(text:String,h_align:int = HORIZONTAL_ALIGNMENT_LEFT,v_align:int = VERTICAL_ALIGNMENT_TOP,tool_tip:String="",name:String = "label") -> Label:
var ctl = Label.new()
ctl.name = name
ctl.text = text
ctl.tooltip_text = tool_tip
# 水平和垂直对齐
ctl.horizontal_alignment = h_align
ctl.vertical_alignment = v_align
return ctl
# 标签
static func rich_label(text:String,tool_tip:String="",name:String = "rtxlab") -> RichTextLabel:
var ctl = RichTextLabel.new()
ctl.name = name
ctl.text = text
ctl.tooltip_text = tool_tip
return ctl
# 按钮
static func button(text:String,tool_tip:String="",name:String = "button") -> Button:
var ctl = Button.new()
ctl.name = name
ctl.text = text
ctl.tooltip_text = tool_tip
return ctl
# 超链接
static func link(text:String,url:String,tool_tip:String="",name:String = "link") -> LinkButton:
var ctl = LinkButton.new()
ctl.name = name
ctl.text = text
ctl.tooltip_text = tool_tip
ctl.set_meta("url",url)
return ctl
# 复选框
static func checkBox(text:String,tool_tip:String="",name:String = "ckeck") -> CheckBox:
var ctl = CheckBox.new()
ctl.name = name
ctl.text = text
ctl.tooltip_text = tool_tip
return ctl
# 开关按钮
static func onOff(text:String,tool_tip:String="",name:String = "onoff") -> CheckButton:
var ctl = CheckButton.new()
ctl.name = name
ctl.text = text
ctl.tooltip_text = tool_tip
return ctl
# 颜色选择器按钮
static func colorButton(color:Color = Color.WHITE,tool_tip:String="",name:String = "colorBtn") -> ColorPickerButton:
var ctl = ColorPickerButton.new()
ctl.name = name
ctl.color = color
ctl.tooltip_text = tool_tip
return ctl
# 纯色块
static func colorRect(color:Color = Color.WHITE,tool_tip:String="",name:String = "colorRect") -> ColorRect:
var ctl = ColorRect.new()
ctl.name = name
ctl.color = color
ctl.tooltip_text = tool_tip
return ctl
# 图片
static func picture(src:String,tool_tip:String="",name:String = "pic") -> TextureRect:
var ctl = TextureRect.new()
ctl.name = name
ctl.texture = load(src)
ctl.tooltip_text = tool_tip
return ctl
# 树
static func tree(tool_tip:String="",name:String = "tree") -> Tree:
var ctl = Tree.new()
ctl.name = name
ctl.tooltip_text = tool_tip
return ctl
# 列表
static func itemList(items:PackedStringArray,icon:Texture2D,tool_tip:String="",name:String = "list") -> ItemList:
var ctl = ItemList.new()
ctl.name = name
ctl.tooltip_text = tool_tip
for item in items:
if icon:
ctl.add_item(item,icon)
else:
ctl.add_item(item)
return ctl
# 进度条
static func progress(min:float = 0.0,max:float = 100.0,step:float =1.0,tool_tip:String="",name:String = "progress") -> ProgressBar:
var ctl = ProgressBar.new()
ctl.name = name
ctl.tooltip_text = tool_tip
ctl.min_value = min
ctl.max_value = max
ctl.step = step
return ctl
# 水平滑动条
static func hslider(min:float = 0.0,max:float = 100.0,step:float =1.0,tool_tip:String="",name:String = "hslider") -> HSlider:
var ctl = HSlider.new()
ctl.name = name
ctl.tooltip_text = tool_tip
ctl.min_value = min
ctl.max_value = max
ctl.step = step
return ctl
# 垂直滑动条
static func vslider(min:float = 0.0,max:float = 100.0,step:float =1.0,tool_tip:String="",name:String = "vslider") -> VSlider:
var ctl = VSlider.new()
ctl.name = name
ctl.tooltip_text = tool_tip
ctl.min_value = min
ctl.max_value = max
ctl.step = step
return ctl
# 水平分割线
static func hline(name:String = "hline") -> HSeparator:
var ctl = HSeparator.new()
ctl.name = name
return ctl
# 垂直分割线
static func vline(name:String = "vline") -> VSeparator:
var ctl = VSeparator.new()
ctl.name = name
return ctl
# =================================== 布局 ===================================
# 布局函数
# 默认使用VBox+HBox的形式构造行列布局
# 每行超过一个元素,就使用HBox布局
# 类似于一种简单的表格布局
static func layout(ctls:Array,scroll:bool = false,name:String = "layout"):
var root_ctl:Control
if scroll:
root_ctl = ScrollContainer.new()
root_ctl.name = "scroll"
fill(root_ctl)
# VBox
var vbox = VBoxContainer.new()
vbox.name = name
fill_x(vbox)
# 加载行
for row in ctls:
if row.size() == 1:
vbox.add_child(row[0],true)
fill_x(row[0])
elif row.size() > 1:
var hbox = HBoxContainer.new()
fill_x(hbox)
for ct in row:
hbox.add_child(ct,true)
fill_x(ct)
vbox.add_child(hbox,true)
if scroll:
root_ctl.add_child(vbox,true)
else:
root_ctl = vbox
return root_ctl
# 左右可调布局
static func hsplit(left_ctl:Node,right_ctl:Node,name:String = "hsplit") -> HSplitContainer:
var h_split = HSplitContainer.new()
h_split.name = name
fill(h_split)
h_split.add_child(left_ctl,true)
h_split.add_child(right_ctl,true)
return h_split
# 上下可调布局
static func vsplit(up_ctl:Node,bottom_ctl:Node,name:String = "vsplit") -> VSplitContainer:
var v_split = VSplitContainer.new()
v_split.name = name
fill(v_split)
v_split.add_child(up_ctl,true)
v_split.add_child(bottom_ctl,true)
return v_split
# 左右固定布局
static func LRBox(left_ctl:Node,right_ctl:Node,name:String = "LRBox") -> HBoxContainer:
var ctl = HBoxContainer.new()
ctl.name = name
fill(ctl)
ctl.add_child(left_ctl,true)
ctl.add_child(right_ctl,true)
return ctl
# 上下固定布局
static func UDBox(up_ctl:Node,bottom_ctl:Node,name:String = "UDBox") -> VBoxContainer:
var ctl = VBoxContainer.new()
ctl.name = name
fill(ctl)
ctl.add_child(up_ctl,true)
ctl.add_child(bottom_ctl,true)
return ctl
# 水平盒子
static func hbox(items:Array[Control],name:String = "HBox") -> HBoxContainer:
var ctl = HBoxContainer.new()
ctl.name = name
for item in items:
ctl.add_child(item,true)
return ctl
# 垂直盒子
static func vbox(items:Array[Control],name:String = "HBox") -> VBoxContainer:
var ctl = VBoxContainer.new()
ctl.name = name
for item in items:
ctl.add_child(item,true)
return ctl
# 网格布局
static func grid(items:Array[Control],cols:int =1,name:String = "grid") -> GridContainer:
var grid = GridContainer.new()
grid.name = name
grid.columns = cols
fill(grid)
for item in items:
grid.add_child(item,true)
return grid
# 选项卡布局
static func tabs(items:Array[Control],name:String = "tabs") -> TabContainer:
var tab = TabContainer.new()
tab.name = name
fill(tab)
for item in items:
tab.add_child(item,true)
return tab
# 水平流式布局
static func hflow(items:Array[Control],name:String = "hflow") -> HFlowContainer:
var ctl = HFlowContainer.new()
ctl.name = name
fill(ctl)
for item in items:
ctl.add_child(item,true)
return ctl
# 垂直流式布局
static func vflow(items:Array[Control],name:String = "vflow") -> VFlowContainer:
var ctl = VFlowContainer.new()
ctl.name = name
fill(ctl)
for item in items:
ctl.add_child(item,true)
return ctl
# 设置layout及其所有子结点的owner - 用于插件或EditorScript添加
static func set_layout_owner(layout:Node,owner:Node) -> void:
layout.owner = owner
for child in layout.get_children():
child.owner = owner
set_layout_owner(child,owner)
# =================================== 撑开 ===================================
# 水平撑开
static func fill_x(ctl:Control) -> void:
ctl.size_flags_horizontal = Control.SIZE_EXPAND_FILL
# 垂直撑开
static func fill_y(ctl:Control) -> void:
ctl.size_flags_vertical = Control.SIZE_EXPAND_FILL
# 水平+垂直撑开
static func fill(ctl:Control) -> void:
ctl.size_flags_horizontal = Control.SIZE_EXPAND_FILL
ctl.size_flags_vertical = Control.SIZE_EXPAND_FILL
# =================================== 最小尺寸 ===================================
# 最小宽度
static func min_width(ctl:Control,val:float) -> void:
ctl.custom_minimum_size.x = val
# 最小高度
static func min_height(ctl:Control,val:float) -> void:
ctl.custom_minimum_size.y = val
# 最小尺寸
static func min_size(ctl:Control,val:Vector2) -> void:
ctl.custom_minimum_size = val
# =================================== 尺寸 ===================================
# 宽度
static func width(ctl:Control,val:float) -> void:
ctl.size.x = val
# 高度
static func height(ctl:Control,val:float) -> void:
ctl.size.y = val
# 尺寸
static func size(ctl:Control,val:Vector2) -> void:
ctl.size = val
控件生成方法
select(items:PackedStringArray,name:String) -> OptionButton
生成并返回一个由items
定义的选项所组成,名称为name
(默认为“select”)的下拉选框控件(OptionButton
)实例。
比如在EditorScript
中可以如下使用:
@tool
extends EditorScript
func _run():
var sel = Ctl.select(["男","女"])
get_scene().add_child(sel)
Ctl.set_layout_owner(sel,get_scene())
运行后添加如下节点:
在普通场景和节点中可以如下使用:
extends Control
func _ready():
var sel = Ctl.select(["男","女"])
add_child(sel)
spin(min:float,max:float,step:float,name:String) -> SpinBox
参数 | 类型 | 默认值 | 描述 |
---|---|---|---|
min | float | 0.0 | 最小值 |
max | float | 100.0 | 最大值 |
step | float | 1.0 | 单次调整步幅 |
name | String | “spin” | 节点名称 |
生成并返回一个名称为name
的数字微调框控件(SpinBox
)实例。
@tool
extends EditorScript
func _run():
var ctl = Ctl.spin()
get_scene().add_child(ctl)
Ctl.set_layout_owner(ctl,get_scene())
text(placeholder:String,text:String,name:String) -> LineEdit
参数 | 类型 | 默认值 | 描述 |
---|---|---|---|
placeholder | String | “” | 占位文本 |
text | String | “” | 文本 |
name | String | “text” | 节点名称 |
生成并返回一个名称为name
的单行文本框控件(LineEdit
)实例。
@tool
extends EditorScript
func _run():
var ctl = Ctl.text("这里是占位文本")
get_scene().add_child(ctl)
Ctl.set_layout_owner(ctl,get_scene())
textArea(placeholder:String,text:String,name:String) -> TextEdit
参数 | 类型 | 默认值 | 描述 |
---|---|---|---|
placeholder | String | “” | 占位文本 |
text | String | “” | 文本 |
name | String | “textArea” | 节点名称 |
生成并返回一个名称为name
的多行文本框控件(TextEdit
)实例。
@tool
extends EditorScript
func _run():
var ctl = Ctl.textArea("这里是占位文本")
get_scene().add_child(ctl)
Ctl.set_layout_owner(ctl,get_scene())
label(text:String,h_align:int,v_align:int,tool_tip:String,name:String) -> Label
参数 | 类型 | 默认值 | 描述 |
---|---|---|---|
text | String | ||
文本 | |||
h_align | int | HORIZONTAL_ALIGNMENT_LEFT | 水平对齐方式 |
v_align | int | VERTICAL_ALIGNMENT_TOP | 垂直对齐方式 |
tool_tip | String | “” | 鼠标提示文本 |
name | String | “label” | 节点名称 |
生成并返回一个名称为name
的标签控件(Label
)实例。
@tool
extends EditorScript
func _run():
var ctl = Ctl.label("这是一个Label")
get_scene().add_child(ctl)
Ctl.set_layout_owner(ctl,get_scene())
rich_label(text:String,tool_tip:String,name:String) -> RichTextLabel
参数 | 类型 | 默认值 | 描述 |
---|---|---|---|
text | String | ||
文本 | |||
tool_tip | String | “” | 鼠标提示文本 |
name | String | “rtxlab” | 节点名称 |
生成并返回一个名称为name
的富文本标签控件(RichTextLabel
)实例。
@tool
extends EditorScript
func _run():
var ctl = Ctl.rich_label("这是一个[b]RichTextLabel[/b]")
get_scene().add_child(ctl)
Ctl.set_layout_owner(ctl,get_scene())
注意:
暂时未实现对BBcode的支持。(2023年8月6日21:47:56)
button(text:String,tool_tip:String,name:String) -> Button
参数 | 类型 | 默认值 | 描述 |
---|---|---|---|
text | String | ||
按钮文本 | |||
tool_tip | String | “” | 鼠标提示文本 |
name | String | “button” | 节点名称 |
生成并返回一个名称为name
的按钮控件(Button
)实例。
@tool
extends EditorScript
func _run():
var ctl = Ctl.button("确定")
get_scene().add_child(ctl)
Ctl.set_layout_owner(ctl,get_scene())
link(text:String,url:String,tool_tip:String,name:String) -> LinkButton
参数 | 类型 | 默认值 | 描述 |
---|---|---|---|
text | String | ||
按钮文本 | |||
url | String | ||
链接地址 | |||
tool_tip | String | “” | 鼠标提示文本 |
name | String | “link” | 节点名称 |
生成并返回一个名称为name
的超链接按钮控件(LinkButton
)实例。
@tool
extends EditorScript
func _run():
var ctl = Ctl.link("B站","https://www.bilibili.com/")
get_scene().add_child(ctl)
Ctl.set_layout_owner(ctl,get_scene())
其中url参数信息被作为元数据添加到控件实例。可以通过get_meta("url")
,获取链接地址。
通过连接和处理pressed()
信号,使用OS.shell_open()
传入url,就可以用系统浏览器打开网址了。
extends Control
@onready var link = $link
func _on_link_pressed():
OS.shell_open(link.get_meta("url"))
checkBox(text:String,tool_tip:String,name:String) -> CheckBox
@tool
extends EditorScript
func _run():
var ctl = Ctl.checkBox("删除所有游戏存档")
get_scene().add_child(ctl)
Ctl.set_layout_owner(ctl,get_scene())
onOff(text:String,tool_tip:String,name:String) -> CheckButton
@tool
extends EditorScript
func _run():
var ctl = Ctl.onOff("高分辨率")
get_scene().add_child(ctl)
Ctl.set_layout_owner(ctl,get_scene())
colorButton(color:Color,tool_tip:String,name:String) -> ColorPickerButton
@tool
extends EditorScript
func _run():
var ctl = Ctl.colorButton()
get_scene().add_child(ctl)
Ctl.set_layout_owner(ctl,get_scene())
colorRect(color:Color,tool_tip:String,name:String) -> ColorRect
@tool
extends EditorScript
func _run():
var ctl = Ctl.colorRect()
get_scene().add_child(ctl)
Ctl.set_layout_owner(ctl,get_scene())
picture(src:String,tool_tip:String,name:String) -> TextureRect
@tool
extends EditorScript
func _run():
var ctl = Ctl.picture("res://icon.svg")
get_scene().add_child(ctl)
Ctl.set_layout_owner(ctl,get_scene())
:::warning
暂定:需要改进。
:::
tree(tool_tip:String,name:String) -> Tree
@tool
extends EditorScript
func _run():
var ctl = Ctl.tree()
ctl.size = Vector2(100,200)
get_scene().add_child(ctl)
Ctl.set_layout_owner(ctl,get_scene())
:::warning
暂定:需要改进。
:::
itemList(items:PackedStringArray,icon:Object,tool_tip:String,name:String) -> ItemList
@tool
extends EditorScript
func _run():
var ctl = Ctl.itemList(["itm1","itm2","itm3"],preload("res://icon.svg"))
ctl.size = Vector2(450,450)
get_scene().add_child(ctl)
Ctl.set_layout_owner(ctl,get_scene())
:::warning
暂定:需要改进。
:::
progress(min:float,max:float,step:float,tool_tip:String,name:String) -> ProgressBar
var ctl = Ctl.progress()
ctl.size = Vector2(100,30)
hslider(min:float,max:float,step:float,tool_tip:String,name:String) -> HSlider
var ctl = Ctl.hslider()
ctl.size = Vector2(100,30)
vslider(min:float,max:float,step:float,tool_tip:String,name:String) -> VSlider
var ctl = Ctl.vslider()
ctl.size = Vector2(30,100)
hline(name:String) -> HSeparator
var ctl = Ctl.hline()
ctl.size = Vector2(100,10)
vline(name:String) -> VSeparator
var ctl = Ctl.vline()
ctl.size = Vector2(10,100)
布局和容器生成方法
layout(ctls:Array,scroll:bool,name:String) -> void
布局函数,本函数库最核心和重要的函数之一。其原理是通过传入二维控件数组,来定义布局:
- 整个布局是存放在一个
VBoxContainer
中 - 二维数组的每行,如果只有一个控件,则直接放入
VBoxContainer
中,如果有两个及以上控件,则添加一个HboxContainer
,然后将他们全部添加为子节点。 - 通过
VBoxContainer
和HboxContainer
,实现一种类似于HTML表格的布局
示例:
@tool
extends EditorScript
func _run():
var layout = Ctl.layout([
[Ctl.label("测试")],
[Ctl.label("姓名"),Ctl.text("请输入姓名")],
[Ctl.label("性别"),Ctl.select(["男","女"])],
[Ctl.button("提交"),Ctl.button("清除")]
])
get_scene().add_child(layout)
Ctl.set_layout_owner(layout,get_scene())
上面的EditorScript运行后就会为当前场景根节点添加如下的控件结构,它看起来就是一个简单的表单。
layout嵌套
layout()
方法可以嵌套,这就意味着你可以用它和控件创建函数,以及下方的其他容器函数,轻松的用一个数组来描述一个复杂的UI嵌套结构。
@tool
extends EditorScript
func _run():
var layout = Ctl.layout([
[Ctl.label("测试")],
[Ctl.label("姓名"),Ctl.text("请输入姓名")],
[Ctl.label("性别"),Ctl.select(["男","女"])],
[Ctl.layout([
[Ctl.label("布局嵌套")],
[Ctl.label("试试"),Ctl.text("请输入123")]
])],
[Ctl.button("提交"),Ctl.button("清除")],
[Ctl.link("在线说明","https://www.bilibili.com/")]
])
get_scene().add_child(layout)
Ctl.set_layout_owner(layout,get_scene())
显示滚动条
scroll
参数用于控制布局的外部是否包裹一个ScrollContainer
,用于在窗体缩放等时,显示滚动条。
@tool
extends EditorScript
func _run():
var layout = Ctl.layout([
[Ctl.label("测试")],
[Ctl.label("姓名"),Ctl.text("请输入姓名")],
[Ctl.label("性别"),Ctl.select(["男","女"])],
[Ctl.button("提交"),Ctl.button("清除")]
],true)
get_scene().add_child(layout)
Ctl.set_layout_owner(layout,get_scene())
hsplit(left_ctl:Object,right_ctl:Object,name:String) -> HSplitContainer
左右可调节布局。
@tool
extends EditorScript
func _run():
var right = Ctl.layout([
[Ctl.label("测试",HORIZONTAL_ALIGNMENT_CENTER)],
[Ctl.label("姓名"),Ctl.text("请输入姓名")],
[Ctl.label("性别"),Ctl.select(["男","女"])],
[Ctl.button("提交"),Ctl.button("清除")],
[Ctl.link("在线说明","https://www.bilibili.com/")]
],true,"right")
var tree = Ctl.tree()
tree.custom_minimum_size.x = 200
var layout_hsplit = Ctl.hsplit(tree,right)
get_scene().add_child(layout_hsplit)
Ctl.set_layout_owner(layout_hsplit,get_scene())
vsplit(up_ctl:Object,bottom_ctl:Object,name:String) -> VSplitContainer
上下可调节布局。
LRBox(left_ctl:Object,right_ctl:Object,name:String) -> HBoxContainer
左右固定布局。
UDBox(up_ctl:Object,bottom_ctl:Object,name:String) -> VBoxContainer
上下固定布局。
hbox(items:Array,name:String) -> HBoxContainer
不受个数限制的HBoxContainer
布局。
@tool
extends EditorScript
func _run():
var main = Ctl.layout([
[Ctl.label("测试",HORIZONTAL_ALIGNMENT_CENTER)],
[Ctl.label("姓名"),Ctl.text("请输入姓名")],
[Ctl.label("性别"),Ctl.select(["男","女"])],
[Ctl.button("提交"),Ctl.button("清除")],
[Ctl.link("在线说明","https://www.bilibili.com/")]
],true,"right")
var tree = Ctl.tree()
tree.custom_minimum_size.x = 200
var tree2 = Ctl.tree()
tree2.custom_minimum_size.x = 200
var layout = Ctl.hbox([tree,main,tree2])
get_scene().add_child(layout)
Ctl.set_layout_owner(layout,get_scene())
vbox(items:Array,name:String) -> VBoxContainer
@tool
extends EditorScript
func _run():
# =================================== 顶部 ===================================
var top = Ctl.colorRect()
Ctl.min_height(top,100)
Ctl.fill_x(top)
# =================================== 中间 ===================================
# ===== 左侧
var tree = Ctl.tree()
Ctl.min_width(tree,200)
# ===== 中间
var main = Ctl.layout([
[Ctl.label("测试",HORIZONTAL_ALIGNMENT_CENTER)],
[Ctl.label("姓名"),Ctl.text("请输入姓名")],
[Ctl.label("性别"),Ctl.select(["男","女"])],
[Ctl.button("提交"),Ctl.button("清除")],
[Ctl.link("在线说明","https://www.bilibili.com/")]
],true,"right")
# ===== 右侧
var tree2 = Ctl.tree()
Ctl.min_width(tree2,200)
var main_center = Ctl.hbox([tree,main,tree2])
Ctl.fill(main_center)
# =================================== 底部 ===================================
var bottom = Ctl.colorRect(Color.GREEN_YELLOW)
Ctl.min_height(bottom,30)
Ctl.fill_x(bottom)
var layout = Ctl.vbox([top,main_center,bottom]) # 完整布局
get_scene().add_child(layout)
Ctl.set_layout_owner(layout,get_scene())
grid(items:Array,name:String) -> GridContainer
@tool
extends EditorScript
func _run():
var ctls:Array[Control]
for i in range(10):
var btn = Ctl.button("button%d" % i)
Ctl.fill_x(btn)
ctls.append(btn)
var ctl = Ctl.grid(ctls,2)
get_scene().add_child(ctl)
Ctl.set_layout_owner(ctl,get_scene())
tabs(items:Array,name:String) -> TabContainer
@tool
extends EditorScript
func _run():
var ctls:Array[Control]
for i in range(10):
var btn = Ctl.button("button%d" % i)
Ctl.fill_x(btn)
ctls.append(btn)
var ctl = Ctl.tabs(ctls)
Ctl.size(ctl,Vector2(200,200))
get_scene().add_child(ctl)
Ctl.set_layout_owner(ctl,get_scene())
hflow(items:Array,name:String) -> HFlowContainer
这是是描述。
vflow(items:Array,name:String) -> VFlowContainer
这是是描述。
布局设置
set_layout_owner(layout:Node,owner:Node) -> void
设置layout
及其所有子结点的owner
- 用于插件或EditorScript添加时使用。
控件layout设置属性
fill_x(ctl:Control) -> void
将ctl
控件水平撑开,也就是设置ctl
的size_flags_horizontal
等于SIZE_EXPAND_FILL
。
fill_y(ctl:Control) -> void
将ctl
控件垂直撑开,也就是设置ctl
的size_flags_vertical
等于SIZE_EXPAND_FILL
。
fill(ctl:Control) -> void
将ctl
控件水平和垂直撑开,也就是设置ctl
的size_flags_horizontal
和size_flags_vertical
都等于SIZE_EXPAND_FILL
。
min_width(ctl:Control,val:float) -> void
设定ctl
控件的最小宽度(custom_minimum_size.x
)为val
。
min_height(ctl:Control,val:float) -> void
设定ctl
控件的最小宽高度(custom_minimum_size.y
)为val
。
min_size(ctl:Control,val:Vector2) -> void
设定ctl
控件的最小尺寸(custom_minimum_size
)为val
。
width(ctl:Control,val:float) -> void
设定ctl
控件的宽度(size.x
)为val
。
height(ctl:Control,val:float) -> void
设定ctl
控件的高度(size.y
)为val
。
size(ctl:Control,val:Vector2) -> void
设定ctl
控件的尺寸(size
)为val
。