【Godot4.2】控件节点生成与布局函数库Ctl

前言

本文依旧来自笔者的语雀知识库。基础内容写于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
# 最后修改时间:20238623: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())

运行后添加如下节点:
image.png
在普通场景和节点中可以如下使用:

extends Control

func _ready():
	var sel = Ctl.select(["男","女"])
	add_child(sel)

spin(min:float,max:float,step:float,name:String) -> SpinBox

参数类型默认值描述
minfloat0.0最小值
maxfloat100.0最大值
stepfloat1.0单次调整步幅
nameString“spin”节点名称

生成并返回一个名称为name的数字微调框控件(SpinBox)实例。

@tool
extends EditorScript

func _run():
	var ctl = Ctl.spin()
	
	get_scene().add_child(ctl)
	Ctl.set_layout_owner(ctl,get_scene())

image.png

text(placeholder:String,text:String,name:String) -> LineEdit

参数类型默认值描述
placeholderString“”占位文本
textString“”文本
nameString“text”节点名称

生成并返回一个名称为name的单行文本框控件(LineEdit)实例。

@tool
extends EditorScript

func _run():
	var ctl = Ctl.text("这里是占位文本") 
	get_scene().add_child(ctl)
	Ctl.set_layout_owner(ctl,get_scene())

image.png

textArea(placeholder:String,text:String,name:String) -> TextEdit

参数类型默认值描述
placeholderString“”占位文本
textString“”文本
nameString“textArea”节点名称

生成并返回一个名称为name的多行文本框控件(TextEdit)实例。

@tool
extends EditorScript

func _run():
	var ctl = Ctl.textArea("这里是占位文本") 
	get_scene().add_child(ctl)
	Ctl.set_layout_owner(ctl,get_scene())

image.png

label(text:String,h_align:int,v_align:int,tool_tip:String,name:String) -> Label

参数类型默认值描述
textString
文本
h_alignintHORIZONTAL_ALIGNMENT_LEFT水平对齐方式
v_alignintVERTICAL_ALIGNMENT_TOP垂直对齐方式
tool_tipString“”鼠标提示文本
nameString“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())

image.png

rich_label(text:String,tool_tip:String,name:String) -> RichTextLabel

参数类型默认值描述
textString
文本
tool_tipString“”鼠标提示文本
nameString“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())

image.png


注意:
暂时未实现对BBcode的支持。(2023年8月6日21:47:56)


button(text:String,tool_tip:String,name:String) -> Button

参数类型默认值描述
textString
按钮文本
tool_tipString“”鼠标提示文本
nameString“button”节点名称

生成并返回一个名称为name的按钮控件(Button)实例。

@tool
extends EditorScript

func _run():
	var ctl = Ctl.button("确定") 
	get_scene().add_child(ctl)
	Ctl.set_layout_owner(ctl,get_scene())

image.png

link(text:String,url:String,tool_tip:String,name:String) -> LinkButton

参数类型默认值描述
textString
按钮文本
urlString
链接地址
tool_tipString“”鼠标提示文本
nameString“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())

image.png
其中url参数信息被作为元数据添加到控件实例。可以通过get_meta("url"),获取链接地址。
image.png
通过连接和处理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())

image.png

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())

image.png

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())

image.png

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())

image.png

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())

image.png
:::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())

image.png
:::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())

image.png
:::warning
暂定:需要改进。
:::

progress(min:float,max:float,step:float,tool_tip:String,name:String) -> ProgressBar

var ctl = Ctl.progress() 
ctl.size = Vector2(100,30)

image.png

hslider(min:float,max:float,step:float,tool_tip:String,name:String) -> HSlider

var ctl = Ctl.hslider() 
ctl.size = Vector2(100,30)

image.png

vslider(min:float,max:float,step:float,tool_tip:String,name:String) -> VSlider

var ctl = Ctl.vslider() 
ctl.size = Vector2(30,100)

image.png

hline(name:String) -> HSeparator

var ctl = Ctl.hline() 
ctl.size = Vector2(100,10)

image.png

vline(name:String) -> VSeparator

var ctl = Ctl.vline() 
ctl.size = Vector2(10,100)

image.png

布局和容器生成方法

layout(ctls:Array,scroll:bool,name:String) -> void

布局函数,本函数库最核心和重要的函数之一。其原理是通过传入二维控件数组,来定义布局:

  • 整个布局是存放在一个VBoxContainer
  • 二维数组的每行,如果只有一个控件,则直接放入VBoxContainer中,如果有两个及以上控件,则添加一个HboxContainer,然后将他们全部添加为子节点。
  • 通过VBoxContainerHboxContainer,实现一种类似于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运行后就会为当前场景根节点添加如下的控件结构,它看起来就是一个简单的表单。
image.png

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())

在布局外包裹ScrollContainer

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())

image.png

vsplit(up_ctl:Object,bottom_ctl:Object,name:String) -> VSplitContainer

上下可调节布局。
image.png

LRBox(left_ctl:Object,right_ctl:Object,name:String) -> HBoxContainer

左右固定布局。
image.png

UDBox(up_ctl:Object,bottom_ctl:Object,name:String) -> VBoxContainer

上下固定布局。
image.png

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())

image.png

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())

image.png

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控件水平撑开,也就是设置ctlsize_flags_horizontal等于SIZE_EXPAND_FILL

fill_y(ctl:Control) -> void

ctl控件垂直撑开,也就是设置ctlsize_flags_vertical等于SIZE_EXPAND_FILL

fill(ctl:Control) -> void

ctl控件水平和垂直撑开,也就是设置ctlsize_flags_horizontalsize_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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巽星石

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值