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

news2024/9/24 13:22:58

前言

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

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

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

相关文章

C++ 快速排序快速选择

目录 1、75. 颜色分类 2、912. 排序数组 3、 215. 数组中的第K个最大元素 4、LCR 159. 库存管理 III 1、75. 颜色分类 思路:利用快速排序思路,使用三指针分块进行优化。 [0,left]——小于key[left1,right-1]——等于key[right,nums.size()]——大于k…

解决Mybatis报Type interface *.*Mapper is not known to the MapperRegis

解决Mybatis报Type interface *.*Mapper is not known to the MapperRegis 问题发现问题解决方法一:检查Mapper文件的namespace路径是否正确方法二:使用其他方法是否正确 问题发现 在学习MyBatis框架的时候,不使用 XML 构建 SqlSessionFacto…

2023年NOC大赛软件创意编程(学而思)赛道图形化小高组初赛试题,包含答案

2023NOC初赛试题-小高-A卷(平行讲义) 一、判断 1、如果想要编程演奏乐曲需要添加下面的拓展模块 2、运行下面的程序,我们看不到角色位置在舞台上发生变化 3、运行下面的程序,我们会在舞台上看到一个正方形。 4、运行下面的程序,结果一定为true。 5、运行下面的程序,…

顶易海关数据怎么做获客?功能详解看这里!

顶易海关数据怎么做获客呢?详解看这里! 海关数据系统登录:hg.smtso.com/?iEF6DCB 如果对开发国外优质客户感兴趣的话,关注Felicia外贸说,一键开发客户不是问题。 海关数据主要功能: 报关单详情查询&#…

YOLO v9训练自己数据集

原以为RT-DETR可以真的干翻YOLO家族,结果,!!!! 究竟能否让卷积神经网络重获新生? 1.数据准备 代码地址:https://github.com/WongKinYiu/yolov9 不能科学上网的评论区留言 数据集…

【JavaEE】_Spring MVC项目之建立连接

目录 1. Spring MVC程序编写流程 2. 建立连接 2.1 RequestMapping注解介绍 2.2 RequestMapping注解使用 2.2.1 仅修饰方法 2.2.2 修饰类与方法 2.3 关于POST请求与GET请求 2.3.1 GET请求 2.3.2 POST请求 2.3.3 限制请求方法 1. Spring MVC程序编写流程 1. 建立连接&…

【重温设计模式】装饰模式及其Java示例

装饰模式的介绍 在众多设计模式中,有一种叫做装饰模式,它以一种独特的方式赋予了代码更多的灵活性。 装饰模式是一种结构型设计模式,它允许我们在运行时动态地为对象添加新的行为。这就像是我们在装饰一个房间时,可以随意添加或更…

学习网络编程No.11【传输层协议之UDP】

引言: 北京时间:2023/11/20/9:17,昨天成功更文,上周实现了更文两篇,所以这周再接再厉。当然做题任在继续,而目前做题给我的感觉以套路和技巧偏多,还是那句话很多东西不经历你就是不懂&#xff…

C#入门:简单数据类型和强制类型转换

本文由 简悦 SimpRead 转码, 原文地址 mp.weixin.qq.com 本期来讲讲 unity 的脚本语言 —C#,C# 的简单数据类型及范围和强制类型转化的方法。这可是 unity 游戏开发必备技能。 1. 简单数据类型 各个类型的范围: byte -> System.Byte (字节…

roslaunch 报错 numpy与python版本冲突

报错&#xff1a; File "/usr/lib/python3/dist-packages/numpy/core/__init__.py", line 17, in <module> from . import multiarray File "/usr/lib/python3/dist-packages/numpy/core/multiarray.py", line 14, in <module> from . import…

蓝桥杯练习系统(算法训练)ALGO-992 士兵杀敌(二)

资源限制 内存限制&#xff1a;256.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述 南将军手下有N个士兵&#xff0c;分别编号1到N&#xff0c;这些士兵的杀敌数都是已知的。   小工是南将军手下的军师&…

gif闪图如何在线生成?仅需三步在线制作gif闪图

Gif动态图片是一种通过连续播放的一系列图片来展示的。Gif动图的优势是体积小、传播速度快且不受限制。当我们想要将多张图片变成带有闪动效果的gif动图时应该怎么操作呢&#xff1f;这时候&#xff0c;只需要使用在线闪图制作&#xff08;https://www.gif.cn/&#xff09;网站…

【C++初阶】内存管理

目录 一.C语言中的动态内存管理方式 二.C中的内存管理方式 1.new/delete操作内置类型 2.new和delete操作自定义类型 3.浅识抛异常 &#xff08;内存申请失败&#xff09; 4.new和delete操作自定义类型 三.new和delete的实现原理 1.内置类型 2.自定义类型 一.C语…

全部免费!抖音,牛逼了!

相比于百度文心、清华智谱和讯飞星火这些在国内有一定市场知名度的AI工具&#xff0c;字节跳动多少显得有点低调了。 国内的AI工具用了不少&#xff0c;但要是说哪家最有前景&#xff0c;那最看好的还是字节跳动。 倒不是说字节的云雀大模型比上面这几个更牛逼&#xff0c;而…

uniapp_微信小程序_说下动态class(实用性很强)

// 以上图片为例,里面的参数是后端传给你的&#xff0c;你不知道有多少东西&#xff0c;更不可能一个一个改&#xff0c;所以引入动态class 代码实现 <view :class"{ pay: item.paymoneystatus 未支付,inpay: item.paymoneystatus 已支付}">{{item.paymon…

Python之Flask框架~四大内置对象

1.g global全局对象 g对象是专门用来保存用户的数据的 g对象在一次请求中的所有的代码的地方, 都是可以使用的 突破变量存储位置限制,为数据传递添加了新的方式,比如我们在before_request产生一个数据在后面需要使 用, 可以保存在g对象中, 在其他视图西数中就可以使用这个数据…

YOLOv6、YOLOv7、YOLOv8网络结构图(清晰版)

承接上一篇博客&#xff1a;YOLOv3、YOLOv4、YOLOv5、YOLOx的网络结构图(清晰版)_yolox网络结构图-CSDN博客 1. YOLOv6网络结构图 2. YOLOv7网络结构图 3. YOLOv8网络结构图

中文地址命名实体识别预测写入csv格式

模型训练参数 中文地址命名实体识别训练和预测 效果 输入数据 输出数据 脚本 import os import numpy as np import torch import json from utils import commonUtils, metricsUtils, decodeUtils, trainUtils import b

《Linux C编程实战》笔记:共享内存

共享内存是分配一块能被其他进程访问的内存。每个共享内存段在内核中维护一个内部数据结构shmid_ds(和消息队列、信号量一样)&#xff0c;该结构定义在头文件linux/shm.h中,这是我从源码里抄的 #include<linux/shm.h> struct shmid_ds {struct ipc_perm shm_perm; /* 操…

pytorch基础2-数据集与归一化

专题链接&#xff1a;https://blog.csdn.net/qq_33345365/category_12591348.html 本教程翻译自微软教程&#xff1a;https://learn.microsoft.com/en-us/training/paths/pytorch-fundamentals/ 初次编辑&#xff1a;2024/3/2&#xff1b;最后编辑&#xff1a;2024/3/2 本教程…