前言
本篇原始文章写于2023年8月7日,存储在我的语雀文档中。但是语雀分享有诸多不便,为了让更多Godoter更轻松的搜到和看到,就转过来了。
这个项目我上传了Github,后续会贴上链接。
概述
Godot控件体系存在的问题之一就是样式无法用纯文本形式简洁而清晰的定义,一切都要靠主题编辑器或检视器面板那一套手动的东西。4.x提供了一些样式属性和方法,但仍然算不上简洁。
在样式定义方面,前端的CSS样式表可谓是最佳实践之一。
如果能够以类似CSS一样的纯文本形式解析和控制Godot的控件样式,那么样式定义就能更轻松。
基于这样的想法,笔者尝试建立了一个类CSS样式的样式书写和解析机制。让使用者可以基于一个简单的导出变量,用纯文本的方式定义控件的样式。
样式解析函数库
ConfigFile
提供了一个名叫parse()
的方法,可以将符合ConfigFile
风格和书写规则的字符串直接解析到ConfigFile
实例。进而可以使用其提供的方法便捷的遍历节、键和值。
基于此我创建了一个名为Sty
的静态函数库。
下面是初期的一个效果,已经可以解析按钮多个状态下的一些简单样式。
# ========================================================
# 名称:Sty
# 类型:静态函数库
# 简介:用于解析和应用控件样式
# 作者:巽星石
# Godot版本:4.1.1-stable (official)
# 创建时间:2023-08-07 23:11:57
# 最后修改时间:2023-08-07 23:11:57
# ========================================================
class_name Sty
# 样式解析
static func parse_style(ctl:Control,style_str:String):
var cfg = ConfigFile.new()
var err = cfg.parse(style_str.replace(":","=\"").replace(";","\""))
if err == OK: # 解析成功
for section in cfg.get_sections():
for key in cfg.get_section_keys(section):
var val = cfg.get_value(section,key)
match key:
"font_size":
pass
"color":
match section:
"normal":
ctl.add_theme_color_override("font_color",Color(val))
"hover","pressed","disabled","focus":
ctl.add_theme_color_override("font_%s_color" % section,Color(val))
"bg_color":
match section:
"normal","hover","pressed","disabled","focus":
var stylebox:StyleBoxFlat = get_stylebox(ctl,section)
stylebox.bg_color = Color(val)
"radius":
match section:
"normal","hover","pressed","disabled","focus":
var stylebox:StyleBoxFlat = get_stylebox(ctl,section)
var vals = val.split(",")
stylebox.corner_radius_top_left = int(vals[0])
stylebox.corner_radius_top_right = int(vals[1])
stylebox.corner_radius_bottom_left = int(vals[2])
stylebox.corner_radius_bottom_right = int(vals[3])
"border_width":
match section:
"normal","hover","pressed","disabled","focus":
var stylebox:StyleBoxFlat = get_stylebox(ctl,section)
var vals = val.split(",")
stylebox.border_width_left = int(vals[0])
stylebox.border_width_top = int(vals[1])
stylebox.border_width_right = int(vals[2])
stylebox.border_width_bottom = int(vals[3])
"border_color":
match section:
"normal","hover","pressed","disabled","focus":
var stylebox:StyleBoxFlat = get_stylebox(ctl,section)
stylebox.border_color = Color(val)
"padding":
match section:
"normal","hover","pressed","disabled","focus":
var stylebox:StyleBoxFlat = get_stylebox(ctl,section)
var vals = val.split(",")
stylebox.content_margin_left = int(vals[0])
stylebox.content_margin_top = int(vals[1])
stylebox.content_margin_right = int(vals[2])
stylebox.content_margin_bottom = int(vals[3])
"margin":
match section:
"normal","hover","pressed","disabled","focus":
var stylebox:StyleBoxFlat = get_stylebox(ctl,section)
var vals = val.split(",")
stylebox.expand_margin_left = int(vals[0])
stylebox.expand_margin_top = int(vals[1])
stylebox.expand_margin_right = int(vals[2])
stylebox.expand_margin_bottom = int(vals[3])
"shadow_color":
match section:
"normal","hover","pressed","disabled","focus":
var stylebox:StyleBoxFlat = get_stylebox(ctl,section)
stylebox.shadow_color = Color(val)
"shadow_size":
match section:
"normal","hover","pressed","disabled","focus":
var stylebox:StyleBoxFlat = get_stylebox(ctl,section)
stylebox.shadow_size = int(val)
"shadow_offset":
match section:
"normal","hover","pressed","disabled","focus":
var stylebox:StyleBoxFlat = get_stylebox(ctl,section)
var vals = val.split(",")
stylebox.shadow_offset = Vector2(float(vals[0]),float(vals[1]))
"skew":
match section:
"normal","hover","pressed","disabled","focus":
var stylebox:StyleBoxFlat = get_stylebox(ctl,section)
var vals = val.split(",")
stylebox.skew = Vector2(float(vals[0]),float(vals[1]))
# 获取控件对应名称的样式盒
static func get_stylebox(ctl:Control,name:String) -> StyleBoxFlat:
var stylebox:StyleBoxFlat
if ctl.has_theme_stylebox_override(name):
stylebox = ctl.get_theme_stylebox(name)
else:
stylebox= StyleBoxFlat.new()
ctl.add_theme_stylebox_override(name, stylebox)
return stylebox
实际使用
为普通节点添加style属性
我们创建一个UI场景,添加一个Button
。
为Button
添加如下代码:
@tool
extends Button
@export_multiline var style:String = "":
set(val):
style = val
Sty.parse_style(self,val)
接着我们在检视器面板的style
变量中,定义如下的样式:
[normal]
font_size :64;
color:#FF4400;
bg_color:yellow;
radius:55,5,56,45;
skew:0.1,0;
border_width:5,2,5,2;
border_color:#444;
[disable]
color:#00FF00;
[hover]
color:#00FF00;
bg_color:#ccc;
radius:55,5,5,45;
关于语法
因为我是以Button控件为模板进行初期的样式语法测试,所以以Button为例的话,我们可以看到一个按钮的样式其实是可以分为几个状态的:正常(normal),禁用(disable),鼠标经过(hover),按下(pressed),获得焦点(focus)。
所以我采用了状态优先,属性名称简化和重用的设计,并且采用了Godot的ConfigFile
格式。
将按钮的不同状态作为配置文件的section
,但是为了简化书写,让其更像是CSS风格,所以采用了冒号和封号,而不是等号来设定键值对。在解析时冒号和封号会被替换。
然后对应的按钮样式被定义为如下图:
因为加了@tool关键字,所以在normal状态中定义的样式都会被实时的显示在编辑器中,而其他的诸如hover等需要在运行后查看。