阅读量:3
概述
HTML和XML采用类似的标签形式。
之前在Godot中以函数库形式实现了网页标签和内容生成。能用,但是缺点也很明显。函数之间没有从属关系,但是多有依赖,而且没有划分出各种对象和类型。
如果以完全的面向对象形式来设计标签类或者元素类,将可以更贴近HTML或XML的本来面目。也更容易生成。
整体思路是设计如下的类继承结构:
实现之后,将可以充分的定义和生成HTML、XML和SVG标签,并用于内容生成或文档解析。
通用标签类
因为HTML和XML标签的语法格式和要素是类似的,因此可以通过创建一个通用的标签类,来生成HTML或XML标签。
MLtag
就是这个通用的标签类,它定义了几个核心的属性:
tag_name
:标签名称attrs
:包含标签所有属性的字典is_single
:是否为单标签,默认为false
content
:标签的子内容,可以直接赋值,也可以使用append()
方法追加。单标签(is_single
为true
)时被忽略get_end_tag()
:获取结束标签,单标签时返回空字符串append()
:追加当前标签的子内容,可以是字符串形式,也可以是SVG标签实例(会自动调用to_string()
方法转化为字符串)
# ============================================= # 名称:MLtag # 类型:类 # 描述:HTML、XML通用标签类,用于定义和生成HTML、XML标签字符串 # 作者:巽星石 # 创建时间:2024年7月16日17:48:01 # 最后修改时间:2024年7月16日22: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标签基类 # 作者:巽星石 # 创建时间:2024年7月16日19:05:46 # 最后修改时间:2024年7月16日19: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>标签类 # 作者:巽星石 # 创建时间:2024年7月16日19:32:47 # 最后修改时间:2024年7月16日22: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>标签类 # 作者:巽星石 # 创建时间:2024年7月16日21:37:24 # 最后修改时间:2024年7月16日21: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所有形状和路径标签类的基类,承载共同属性 # 作者:巽星石 # 创建时间:2024年7月16日21:51:00 # 最后修改时间:2024年7月16日23: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>标签类 # 作者:巽星石 # 创建时间:2024年7月16日21:47:43 # 最后修改时间:2024年7月16日22: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预览如下:
@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>
预览如下:
总结
- 纯面向对象的设计,尤其是基于继承的多个类组成的体系在某些方面还是很有用的。比如HTML、XML、SVG这样本身就很明显的继承设计。
- 基于继承或许会写出一堆类,但是辅以类图和文档,还是能让别人比较轻松的理解和掌握的。
- 静态函数库则完全是一个很差的设计形式,很有可能会被一堆函数搞得特别臃肿。
- 适当的用类和类的继承形式取代静态函数库形式是我正在做的尝试