【Godot4.2】MLTag类:HTML、XML通用标签类

概述

HTML和XML采用类似的标签形式。

之前在Godot中以函数库形式实现了网页标签和内容生成。能用,但是缺点也很明显。函数之间没有从属关系,但是多有依赖,而且没有划分出各种对象和类型。

如果以完全的面向对象形式来设计标签类或者元素类,将可以更贴近HTML或XML的本来面目。也更容易生成。

整体思路是设计如下的类继承结构:
在这里插入图片描述

实现之后,将可以充分的定义和生成HTML、XML和SVG标签,并用于内容生成或文档解析。

通用标签类

因为HTML和XML标签的语法格式和要素是类似的,因此可以通过创建一个通用的标签类,来生成HTML或XML标签。

MLtag就是这个通用的标签类,它定义了几个核心的属性:

  • tag_name :标签名称
  • attrs:包含标签所有属性的字典
  • is_single:是否为单标签,默认为false
  • content :标签的子内容,可以直接赋值,也可以使用append()方法追加。单标签(is_singletrue)时被忽略
  • get_end_tag():获取结束标签,单标签时返回空字符串
  • append():追加当前标签的子内容,可以是字符串形式,也可以是SVG标签实例(会自动调用to_string()方法转化为字符串)
# =============================================
# 名称:MLtag
# 类型:类
# 描述:HTMLXML通用标签类,用于定义和生成HTMLXML标签字符串
# 作者:巽星石
# 创建时间:202471617:48:01
# 最后修改时间:202471622:27:53
# =============================================
class_name MLTag

# ====================== 属性 ======================
var tag_name = ""          # 标签名称
var attrs:Dictionary = {}  # 属性字典
var is_single = false      # 是否单标签
var content = ""           # 子内容  

# ====================== 方法 ======================
# 获取结束标签
func get_end_tag() -> String:
	return "" if is_single else "</%s>" % tag_name

# 追加子内容
func append(new_content) -> void:
	if new_content is String:
		content += "\n" + new_content
	else:
		content += "\n" + new_content.to_string()

# ====================== 虚函数 ======================
# 转化为字符串
func _to_string() -> String:
	var tag_str:String
	
	# 获取属性字典的字符串
	var attr = ""
	for key in attrs.keys():
		if attrs[key] != "":
			attr += "%s=\"%s\" " % [key,attrs[key]]
	tag_str = "<%s%s/>" % [tag_name," " + attr] if is_single else "<%s%s>%s</%s>" % [tag_name," " + attr,content,tag_name]
	return tag_str

测试:

@tool
extends EditorScript

func _run() -> void:
	var tag = MLTag.new()    # 创建一个标签
	tag.name = "a"           # 名称
	tag.attrs["id"] = "a1"   # 设定属性
	tag.attrs["href"] = "https://www.runoob.com/"
	tag.content = "菜鸟教程"
	
	print(tag)

默认是双标签,也就是带有起始标签和结束标签的标签对。

<a id="a1" href="https://www.runoob.com/" >菜鸟教程</a>

is_single设为true后,就被认为是单标签:

@tool
extends EditorScript

func _run() -> void:
	var tag = MLTag.new()    # 创建一个标签
	tag.name = "a"           # 名称
	tag.attrs["id"] = "a1"   # 设定属性
	tag.attrs["href"] = "1.com"
	tag.innerHTML = "hahah"
	tag.is_single = true     # 设为标签
	
	print(tag)

打印输出的结果也就变了:

<a id="a1" href="https://www.runoob.com/" />

可以看到没有了结束标签,子内容部分也自动省略。

HTML标签基类

# =============================================
# 名称:HTMLtag
# 类型:类
# 描述:HTML标签基类
# 作者:巽星石
# 创建时间:202471619:05:46
# 最后修改时间:202471619:29:34
# =============================================
class_name HTMLTag extends MLTag

# ====================== 属性 ======================
# ID
var id:String:
	set(val):
		attrs["id"] = val
	get:
		return attrs["id"]
# name
var name:String:
	set(val):
		attrs["name"] = val
	get:
		return attrs["name"]
# css类名
var className:String:
	set(val):
		attrs["class"] = val
	get:
		return attrs["class"]
# 行内CSS样式
var style:String:
	set(val):
		attrs["style"] = val
	get:
		return attrs["style"]
# ====================== 初始化 ======================
func _init() -> void:
	# HTML标签通用属性
	attrs = {
		"id" = "",    # ID
		"name" = "",  # name属性
		"class" = "", # css类名
		"style" = "", # 行内样式
	}

测试代码:

@tool
extends EditorScript

func _run() -> void:
	var tag = HTMLTag.new()  # 创建一个标签
	tag.tag_name = "a"           # 名称
	# 设定HTML标签通用属性
	tag.id = "a2"
	tag.name = "a2"
	tag.className = "url"
	tag.style = "font-size:16px;"
	tag.innerHTML = "一个超链接"
	
	print(tag)

输出:

<a id="a2" name="a2" class="url" style="font-size:16px;" >一个超链接</a>

HTML<a>标签

# =============================================
# 名称:HTMLtagA
# 类型:类
# 描述:HTML<a>标签类
# 作者:巽星石
# 创建时间:202471619:32:47
# 最后修改时间:202471622:30:57
# =============================================
class_name HTMLTagA extends HTMLTag

# ====================== 属性 ======================
# 链接地址
var href:String:
	set(val):
		attrs["href"] = val
	get:
		return attrs["href"]
# 指定链接如何在浏览器中打开
var target:String:
	set(val):
		attrs["target"] = val
	get:
		return attrs["target"]
# 鼠标提示文本
var title:String:
	set(val):
		attrs["title"] = val
	get:
		return attrs["title"]
# 指定与链接目标的关系,如 nofollow、noopener 等
var rel:String:
	set(val):
		attrs["rel"] = val
	get:
		return attrs["rel"]

# ====================== 初始化 ======================
func _init() -> void:
	super()   # 初始化父类,否则无法继承相关的默认属性
	# 确定标签名称以及是否单标签
	tag_name = "a"
	is_single = false
	# <a>标签可用属性
	attrs["href"] = ""     # 链接地址
	attrs["target"] = ""   # 指定链接如何在浏览器中打开
	attrs["title"] = ""    # 鼠标提示文本
	attrs["rel"] = ""      # 指定与链接目标的关系,如 nofollow、noopener 等

测试代码:

@tool
extends EditorScript

func _run() -> void:
	var link = HTMLTagA.new()  # 创建一个标签
	# 设定<a>标签属性
	link.href = "https://www.runoob.com/"
	link.innerHTML = "菜鸟教程"
	
	print(link)

输出:

<a href="https://www.runoob.com/" >菜鸟教程</a>

SVG标签类

在这里插入图片描述

我发现专门做一个XML标签基类是没有必要的,XML本身就没有固定的标签,所以由MLTag定义完全可以解决XML标签定义和生成的功能。

<svg>本身既可以算HTML标签,也可以看做是XML标签。这里为了简化,我创建SVG类,代表<svg>标签,并直接继承自MLTag类型。

# =============================================
# 名称:SVG
# 类型:类
# 描述:SVG<svg>标签类
# 作者:巽星石
# 创建时间:202471621:37:24
# 最后修改时间:202471621:44:04
# =============================================
class_name SVG extends MLTag

# ====================== 属性 ======================
# SVG画布宽度
var width:String:
	set(val):
		attrs["width"] = val
	get:
		return attrs["width"]
# SVG画布高度
var height:String:
	set(val):
		attrs["height"] = val
	get:
		return attrs["height"]

# ====================== 初始化 ======================
func _init() -> void:
	tag_name = "svg"
	# HTML标签通用属性
	attrs = {
		"version"="1.1",
		"baseProfile"="full",
		"width"="200",
		"height"="200",
		"xmlns"="http://www.w3.org/2000/svg",
	}

SVGShape

SVG的形状和路径有许多共同的属性,所以抽象出一个SVGShape类型,用来承载共同属性。

# =============================================
# 名称:SVGShape
# 类型:类
# 描述:SVG所有形状和路径标签类的基类,承载共同属性
# 作者:巽星石
# 创建时间:202471621:51:00
# 最后修改时间:202471623:07:46
# =============================================
class_name SVGShape extends MLTag

# ====================== 属性 ======================
# 填充颜色
var fill:String:
	set(val):
		attrs["fill"] = val
	get:
		return attrs["fill"]

# 描边颜色
var stroke:String:
	set(val):
		attrs["stroke"] = val
	get:
		return attrs["stroke"]

# 描边宽度
var stroke_width:int:
	set(val):
		attrs["stroke-width"] = str(val)
	get:
		return int(attrs["stroke-width"])

# ====================== 初始化 ======================
func _init() -> void:
	tag_name = "svg"
	# svg形状和路径标签通用属性
	attrs["fill"] = "#fff"
	attrs["stroke"] = "#000"
	attrs["stroke-width"] = "1"

矩形

基于SVGShape类型,我们可以创建SVG具体的形状和路径类型。SVGRect就是SVG矩形 标签对应的类。

# =============================================
# 名称:SVGRect
# 类型:类
# 描述:SVG<rect>标签类
# 作者:巽星石
# 创建时间:202471621:47:43
# 最后修改时间:202471622:16:54
# =============================================
class_name SVGRect extends SVGShape

# ====================== 属性 ======================

# ---------------------- 位置 ---------------------- 
# 矩形左上角x坐标
var x:String:
	set(val):
		attrs["x"] = val
	get:
		return attrs["x"]

# 矩形左上角y坐标
var y:String:
	set(val):
		attrs["y"] = val
	get:
		return attrs["y"]
# ---------------------- 尺寸 ---------------------- 
# 矩形宽度
var width:String:
	set(val):
		attrs["width"] = val
	get:
		return attrs["width"]

# 矩形高度
var height:String:
	set(val):
		attrs["height"] = val
	get:
		return attrs["height"]
# ---------------------- 圆角 ---------------------- 
# 矩形圆角半径(水平方向)
var rx:String:
	set(val):
		attrs["rx"] = val
	get:
		return attrs["rx"]

# 矩形圆角半径(垂直方向)
var ry:String:
	set(val):
		attrs["ry"] = val
	get:
		return attrs["ry"]

# ====================== 初始化 ======================
func _init() -> void:
	super()   # 初始化父类,否则无法继承相关的默认属性
	tag_name = "rect"
	# HTML标签通用属性
	attrs["x"] = "0"
	attrs["y"] = "0"
	attrs["width"] = "100"
	attrs["height"] = "100"
	attrs["rx"] = "0"
	attrs["ry"] = "0"

测试代码:

@tool
extends EditorScript

func _run() -> void:
	var svg = SVG.new()          # 创建SVG标签
	var rect  = SVGRect.new()    # 创建矩形
	svg.append(rect)
	print(svg)

获得的SVG代码:

<svg version="1.1" baseProfile="full" width="200" height="200" xmlns="http://www.w3.org/2000/svg" >
<rect fill="#fff" stroke="#000" stroke-width="1" x="0" y="0" width="100" height="100" rx="0" ry="0" ></rect></svg>

SVG预览如下:
image.png

@tool
extends EditorScript

func _run() -> void:
	var svg = SVG.new(100,200)                    # 创建SVG标签
	var rect  = SVGRect.new(0,0,100,200,12,12)    # 创建矩形
	# 设定矩形属性
	rect.stroke = "red"
	rect.stroke_width = 2
	# 将矩形追加到svg内容的末尾
	svg.append(rect)
	print(svg)           # 打印SVG代码

获得的SVG代码:

<svg version="1.1" baseProfile="full" width="100" height="200" xmlns="http://www.w3.org/2000/svg" >
<rect fill="#fff" stroke="red" stroke-width="2" x="0" y="0" width="100" height="200" rx="12" ry="12" ></rect></svg>

预览如下:
image.png

总结

  • 纯面向对象的设计,尤其是基于继承的多个类组成的体系在某些方面还是很有用的。比如HTML、XML、SVG这样本身就很明显的继承设计。
  • 基于继承或许会写出一堆类,但是辅以类图和文档,还是能让别人比较轻松的理解和掌握的。
  • 静态函数库则完全是一个很差的设计形式,很有可能会被一堆函数搞得特别臃肿。
  • 适当的用类和类的继承形式取代静态函数库形式是我正在做的尝试

相关推荐

  1. Qt | QLabel (标签)

    2024-07-17 06:06:04       26 阅读
  2. EFCore通用仓储

    2024-07-17 06:06:04       33 阅读
  3. Okhttp通用工具

    2024-07-17 06:06:04       27 阅读
  4. 【.Net 6.0--通用帮助--FileHelper】

    2024-07-17 06:06:04       49 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-17 06:06:04       66 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-17 06:06:04       70 阅读
  3. 在Django里面运行非项目文件

    2024-07-17 06:06:04       57 阅读
  4. Python语言-面向对象

    2024-07-17 06:06:04       68 阅读

热门阅读

  1. Springboot 3.x - Reactive programming (2)

    2024-07-17 06:06:04       25 阅读
  2. C++基础语法:STL之容器(1)--容器概述和序列概述

    2024-07-17 06:06:04       31 阅读
  3. 【前端】原生实现图片的放大与缩放

    2024-07-17 06:06:04       22 阅读
  4. Meta Llama - Model Cards & Prompt formats

    2024-07-17 06:06:04       22 阅读
  5. 后端开发面试题

    2024-07-17 06:06:04       22 阅读
  6. 自动化回滚的艺术:Conda包依赖的智能管理策略

    2024-07-17 06:06:04       26 阅读
  7. 探索Dubbo的服务引用:XML配置方式

    2024-07-17 06:06:04       25 阅读
  8. 单例模式 饿汉式和懒汉式的区别

    2024-07-17 06:06:04       21 阅读
  9. 【云原生CI/CD工具GitOps】GitOps工作流程和工具链

    2024-07-17 06:06:04       27 阅读