Godot自定义控件样式语法解析

前言

本篇原始文章写于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等需要在运行后查看。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

链表OJ刷题(二)

制作不易,三连支持一下呗!!! 文章目录 前言一、链表的回文结构二、相交链表三、链表中倒数第k个节点四、环形链表Ⅰ和Ⅱ总结 前言 一、链表的回文结构 链表的回文结构_牛客题霸_牛客网 这里我们需要先了解一下什么叫做回文&#…

Rocky Linux 运维工具 dnf

一、dnf的简介 dnf​是用于在基于RPM包管理系统的包管理工具。用户可以通过 ​yum​来搜索、安装、更新和删除软件包,自动处理依赖关系,它是yum的继任者,旨在提供更快速、更现代化的软件包管理体验。。 二、dnf 的参数说明 序号参数描述1in…

django项目 法律法规管理系统

1.项目结构 2.项目需求 1.用户管理模块 2.数据采集模块 3.知识管理模块 4.智能匹配模块 5.个人收藏模块 6.数据分析模块 7.页面展示模块 3.知识点 1.智能匹配模块推荐算法的实现原理 TF (Term Frequency):词频,表示一个词在文档中出现的频…

LeetCode --- 长度最小的子数组(滑动窗口)

前言 滑动窗口算法是一种用于解决数组或者列表中子数组或者字串问题的方法,通常用于在给定数据上执行连续区间的操作,算法基本思想是维护一个固定大小或不定大小的窗口,通过移动窗口的起始位置和结束位置来遍历整个数据。在每个窗口位置&…

重拾前端基础知识:JavaScript

重拾前端基础知识:JavaScript 前言使用JavaScript输出语法运算符条件语句循环数据类型字符串数字数组对象日期函数 数学正则表达式异常处理类集合模块JSON闭包异步调试DOM(文档对象模型)事件事件监听器表单 BOM(浏览器对象模型&am…

【Linux杂货铺】调试工具gdb的使用

目录 🌈前言🌈 📁背景介绍 📁 使用 list [行号] / [函数名] run/r break/b [行号] / [函数名] info break disable break enable break delete break [断点编号] next/n step/s continue/c finish print/p [变量…

旧的Spring Security OAuth已停止维护,全面拥抱新解决方案Spring SAS

Spring Authorization Server 替换 Shiro 指引 背景 Spring 团队正式宣布 Spring Security OAuth 停止维护,该项目将不会再进行任何的迭代 目前 Spring 生态中的 OAuth2 授权服务器是 Spring Authorization Server 已经可以正式生产使用作为 SpringBoot 3.0 的最新…

Redis--事务机制的详解及应用

Redis事务的概念: Redis事务就是将一系列命令包装成一个队列,在执行时候按照添加的顺序依次执行,中间不会被打断或者干扰,在执行事务中,其他客户端提交的命令不可以插入到执行事务的队列中,简单来说Redis事…

Springboot接口参数校验

在设计接口时我们通常需要对接口中的非法参数做校验,以降低在程序运行时因为一些非法参数而导致程序发生异常的风险,例如登录的时候需要校验用户名密码是否为空,创建用户的时候需要校验邮件、手机号码格式是否准确。如果在代码中对接口参数一…

AOP案例(黑马学习笔记)

需求 需求:将案例中增、删、改相关接口的操作日志记录到数据库表中 ● 就是当访问部门管理和员工管理当中的增、删、改相关功能接口时,需要详细的操作日志,并保存在数据表中,便于后期数据追踪。 操作日志信息包含: ●…

基于HT32的智能家居demo(蓝牙上位机)

参加合泰杯作品的部分展示,基于HT32的智能家居,这里展示灯光的相关控制,是用蓝牙进行的数据透传,参考了一些资料,美化封装了一下之前的上位机界面。 成果展示 点击主界面的蓝牙设置,进行连接,下…

Android和Linux的嵌入式开发差异

最近开始投入Android的怀抱。说来惭愧,08年就听说这东西,当时也有同事投入去看,因为恶心Java,始终对这玩意无感,没想到现在不会这个嵌入式都快要没法搞了。为了不中年失业,所以只能回过头又来学。 首先还是…

编码规则转换

思考: 如何将一个机内码转换为区内码? 只要将机内码减去 A0A0 就可以啦 如果只让我们用加法器来解决呢? 注意我们的数据占用了 32 位,如果想用补码进行减法运算的话,符号位怎么办??&#xf…

了解Spring中Bean:配置与作用域

作为一名对技术充满热情的学习者,我一直以来都深刻地体会到知识的广度和深度。在这个不断演变的数字时代,我远非专家,而是一位不断追求进步的旅行者。通过这篇博客,我想分享我在某个领域的学习经验,与大家共同探讨、共…

Linux和Windows集群中部署HTCondor

目录 1、集群架构 2、HTCondor版本 3、Linux系统安装 3.1、HTCondor安装 3.2、中央管理节点配置 3.3、其他节点配置 4、Windwos系统安装 5、安全配置 6、参考 1、集群架构 操作系统IP地址1*Ubuntu22.04192.168.1.742Ubuntu22.04192.168.1.603Ubuntu22.04192.168.1.6…

python3装饰器

装饰器 它允许你修改函数或类的行为,而不更改其源代码。实质上,装饰器是接受另一个函数作为参数并返回一个包装原始函数的新函数。这样,你可以在不修改原始函数的情况下,添加一些额外的功能或逻辑。 def time_cost(func):"…

Java 数组(详细)

目录 一、数组的概述 1. 数组的理解: 2. 数组相关的概念: 3. 数组的特点: 4. 数组的分类: 5.数据结构: 二、一维数组 1. 一维数组的声明与初始化 2. 一维数组元素的引用: 3. 数组的属性&#xff1…

期货开户金融期货的种类

金融期货概念及其种类有哪些?期货种类分为商品期货、金融期货、和期货期权。金融期货是期货的其中一个种类,它是以证券;货币、汇率,利率等金融产品作为买卖标的的期货品种。金融期货交易产生于本世纪70年代的美国市场,…

项目解决方案: 实时视频拼接方案介绍(中)

目 录 1.实时视频拼接概述 2.适用场景 3.系统介绍 4. 拼接方案介绍 4.1基于4K摄像机的拼接方案 4.2采用1080P平台3.0 横向拼接 4.2.1系统架构 4.2.2系统功能 4.2.3方案特色 4.2.4适用场景 4.2.5设备选型 4.3纵横兼顾,竖屏拼接 4.3.1系统…

从下一代车规MCU厘清存储器的发展(2)

目录 1.概述 2.MCU大厂的选择 2.1 瑞萨自研STT-MRAM 2.2 ST专注PCM 2.3 英飞凌和台积电联手RRAM 2.4 NXP如何计划eNVM 3.小结 1.概述 上篇文章,我们简述了当前主流的存储器技术,现在我们来讲讲各大MCU大厂的技术选择 2.MCU大厂的选择 瑞萨日…