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

news2024/9/28 19:18:49

前言

本文依旧来自笔者的语雀知识库。基础内容写于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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1485852.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java-ssm-jsp-宠物常规护理知识管理系统设计与实现

java-ssm-jsp-宠物常规护理知识管理系统设计与实现 获取源码——》公主号:计算机专业毕设大全

Ubuntu20.04: UE4.27 中 Source Code 的编辑器下拉框没有 Rider选项

问题描述 最近想用 Rider 作为 UE4 开发的 IDE,但安装好 Rider 后,发现编辑器下拉框中没有 Rider 的选项,我检查了 UE4 的插件,发现 Rider Integration 插件已经安装且启用的。 环境:Ubuntu 20.04 UE4.27 Rider2023…

设计模式(十四)中介者模式

请直接看原文: 原文链接:设计模式(十四)中介者模式_设计模式之中介模式-CSDN博客 -------------------------------------------------------------------------------------------------------------------------------- 前言 写了很多篇设计模式的…

洛谷 B3620 x 进制转 10 进制

题目描述 给一个小整数 x 和一个 x 进制的数 S。将 S 转为 10 进制数。对于超过十进制的数码,用 A,B,…… 表示。 输入格式 第一行一个整数 x; 第二行一个字符串 S。 输出格式 输出仅包含一个整数,表示答案。 输入输出样例…

《TCP/IP详解 卷一》第12章 TCP初步介绍

目录 12.1 引言 12.1.1 ARQ和重传 12.1.2 滑动窗口 12.1.3 变量窗口:流量控制和拥塞控制 12.1.4 设置重传的超时值 12.2 TCP的引入 12.2.1 TCP服务模型 12.2.2 TCP可靠性 12.3 TCP头部和封装 12.4 总结 12.1 引言 关于TCP详细内容,原书有5个章…

CSS 自测题 -- 用 flex 布局绘制骰子(一、二、三【含斜三点】、四、五、六点)

一点 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>css flex布局-画骰子</title><sty…

Day13:信息打点-JS架构框架识别泄漏提取API接口枚举FUZZ爬虫插件项目

目录 JS前端架构-识别&分析 JS前端架构-开发框架分析 前端架构-半自动Burp分析 前端架构-自动化项目分析 思维导图 章节知识点 Web&#xff1a;语言/CMS/中间件/数据库/系统/WAF等 系统&#xff1a;操作系统/端口服务/网络环境/防火墙等 应用&#xff1a;APP对象/API接…

OJ_重复者

题干 C实现 #include <stdio.h> #include <string.h> using namespace std; void square(int curSize, int patSize, char pattern[3000][3000], char picture[3000][3000], char last[3000][3000]) {if (curSize 1) {for (int i 0; i < patSize; i) {for (i…

《高性能MYSQL》-架构,锁,事务

MYSQL的逻辑架构 第一层&#xff0c;客户端层&#xff1a;包含了连接处理&#xff0c;身份验证&#xff0c;确保安全性等 第二层&#xff0c;包含了mysql大部分的核心功能&#xff0c;查询解析&#xff0c;分析&#xff0c;优化&#xff0c;以及所有的内置函数&#xff08;例如…

Redis--线程模型详解

Redis线程模型 Redis内部使用的文件事件处理器&#xff08;基于Reactor模式开发的&#xff09;file event handler是单线程的&#xff0c;所以Redis线程模型才叫单线程模型&#xff0c;它采用IO多路复用机制同时监听多个socket&#xff0c;当被监听的socket准备好执行accep、r…

深度相机xyz点云文件三维坐标和jpg图像文件二维坐标的相互变换函数

深度相机同时拍摄xyz点云文件和jpg图像文件。xyz文件里面包含三维坐标[x,y,z]和jpg图像文件包含二维坐标[x&#xff0c;y],但是不能直接进行变换&#xff0c;需要一定的步骤来推演。 下面函数是通过box二维框[xmin, ymin, xmax, ymax, _, _ ]去截取xyz文件中对应box里面的点云…

洛谷P5717 三角形分类

给出三条线段a,b,c 的长度&#xff0c;均是不大于 1000010000 的正整数。打算把这三条线段拼成一个三角形&#xff0c;它可以是什么三角形呢&#xff1f; 如果三条线段不能组成一个三角形&#xff0c;输出Not triangle&#xff1b;如果是直角三角形&#xff0c;输出Right tria…

图的简单介绍

定义及术语 G(V,E)&#xff1a;图G的顶点集为V&#xff0c;边集为E。分为有向图和无向图两类。 顶点的度&#xff1a;与该结点相连的边的条数。 出度&#xff1a;顶点的出边条数 入度&#xff1a;顶点的入边条数 顶点的权值称为点权&#xff0c;边的权值称为边权。 存储 1.邻…

Day14:信息打点-主机架构蜜罐识别WAF识别端口扫描协议识别服务安全

目录 Web服务器&应用服务器差异性 WAF防火墙&安全防护&识别技术 蜜罐平台&安全防护&识别技术 思维导图 章节知识点 Web&#xff1a;语言/CMS/中间件/数据库/系统/WAF等 系统&#xff1a;操作系统/端口服务/网络环境/防火墙等 应用&#xff1a;APP对象/…

c++基础知识补充6

new相比malloc可以操作自定义类型 开空间的同时调用构造函数初始化 new,delete和malloc,free要分组使用&#xff0c;否则结果不确定 new和delete本质是malloc和free的封装

香港服务器选择指南(挑选香港服务器的几个标准)

​  随着全球化的加速和互联网的普及&#xff0c;跨境访问和外贸活动越来越频繁。在这个背景下&#xff0c;香港服务器作为一种国际化的基础设施&#xff0c;受到了广泛欢迎。本文将探讨企业在选择香港服务器时应关注的几个标准事项。 1.可靠性和正常运行时间 停机可能会给企…

代码随想录算法训练营第46天| Leetcode 139.单词拆分、卡码网 56. 携带矿石资源(附带多重背包的基本解法和优化)

文章目录 Leetcode 139.单词拆分卡码网 56. 携带矿石资源方法一&#xff1a; 分组转化成01背包方法二&#xff1a; 转化成01背包完全背包&#xff08;基于方法一的小优化&#xff09;方法三&#xff1a; 二进制优化&#xff08;优化了方法一的分组方式&#xff09; Leetcode 13…

基于springboot的宠物咖啡馆平台的设计与实现论文

基于Spring Boot的宠物咖啡馆平台的设计与实现 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了基于Spring Boot的宠物咖啡馆平台的设计与实现的开发全过程。通过分析基于Spring Boot的宠物咖啡馆平台的设计与…

【Scratch画图100例】图49-scratch绘制直角风车 少儿编程 scratch编程画图案例教程 考级比赛画图集训案例

目录 scratch绘制直角风车 一、题目要求 1、准备工作 2、功能实现 二、案例分析 1、角色分析 2、背景分析 3、前期准备 三、实现流程 1、案例分析 2、详细过程 四、程序编写 五、考点分析 六、推荐资料 1、入门基础 2、蓝桥杯比赛 3、考级资料 4、视频课程 …

leetcode第 387 场周赛总结

很久没打周赛了&#xff0c;这周开始恢复 这次周赛&#xff0c;题目比较简单&#xff0c;第三道题有点浪费了时间&#xff0c;思路是对的&#xff0c;但是被我把问题复杂化了。 晚上来写题解和总结