redhat9i 发表于 2023-3-10 08:14

UIElements 开发指南 (三)

(翻译)
接上篇: UIElements 开发指南 (二)
这篇翻译主要介绍样式和Unity样式表。
Styles and Unity style sheets

每一个VisualElement包含样式属性,用于设置元素的外观尺寸,元素是怎么在屏幕上绘制的,等等,比如: backgroundColor 和borderColor。
样式属性可以在C#中设置或者从style sheet中设置。样式属性用它们自己的数据结构组合在一起(IStyle 接口)。
UIElements支持用USS(Unity style sheet)写的样式表。USS文件是文本文件,受到HTML中层叠样式表Cascading Style Sheets (CSS)的启发。USS格式和CSS相似,但USS中包含一些重写和自定义,以便 在Unity中更好的工作。
本篇翻译包括关于USS的一些细节,它的语法,以及和CSS的区别。
Definition of a Unity style sheet

Unity样式表(USS)的基本构件如下:

[*]一个USS是一个文本文件,被当作一个asset。这个文本文件必须有一个.uss扩展。
[*]一个USS仅仅支持样式规则(style rule)。
[*]一个样式规则由一个选择器和一个声明块组成。
[*]选择器标识样式规则影响哪一个可视元素。
[*]由花括号括起来的声明块包含一个或多个样式声明。每一个样式声明由一个属性和一个值构成。每一个样式声明以一个分号;结束。
[*]每一个样式属性的值是一个文字常量,当被解析时,必须匹配目标属性的名字。
样式规则的通用语法:
css selector {   property1:value;   property2:value; }
Attaching USS to visual elements

把USS添加到可视化元素。
你可以把一个Unity样式表(USS)添加到任意可视元素。样式规则会应用到这个可视元素及其子孙元素。在必要的时候,要自动重新应用一遍样式表。
用标准的Unity API如 AssetDatabase.Load() 和 Resources.Load()加载StyleSheet 对象。利用VisualElement.styleSheets.Add()方法把样式表添加到可视化元素。
如果当一个EditorWindow在运行时,你修改了一个USS文件,样式改变会立即应用到相应的可视元素上。
对于使用UIElements的开发者来说,应用样式过程是透明的。当需要时样式表被重新应用。
Style matching with rules

一旦一个样式表被定义,它可以被应用到一个可视元素的UIElements树。
在这个过程中,选择器会匹配元素来解定从USS文件中应用哪一个属性。如果一个选择器匹配一个元素,则会对这个元素使用样式声明。
例如,下面的代码匹配所有Button对象:
Button {
width: 200px;
}VisualElement matching

UIElements使用以下标准来用样式规则来匹配一个可视元素:

[*] 它的C#类名(最直接的派生类的名字,而不是父类的名字)
[*] 一个name属性(是一个字符串)
[*] 一个表示为字符串集合的class列表
[*] VisualElement 在可视化树中的位置及祖先结点
这些特征可以在样式表中的选择器中使用。
如果你熟悉CSS,你会发现id属性和class属性与HTML标签名的相似性。
USS selectors

为了说明选择器,下面的讨论中,我们使用下面的可视化树作为例子:


Simple selectors
一个简单地选择器可以是通配符,任意类型、名称或类名的组合。基于上图中的可视化树,这里给出了一些有效的简单的选择器:

[*]#container1
[*]VisualElement
[*]VisualElement#container1
[*]VisualElement.yellow
[*]Button#OK.yellow:hover
Type
TypeName { ... }使用类型选择器(Type selector)来基于一个元素的C#类型来匹配该元素。例如,Button匹配到两个按钮(buttons)。
当使用类型选择器时,仅指定具体的对象类型。不要包含类名中的命名空间。
Name
#name { ... }使用名字选择器(Name selector)来基于一个元素的VisualElement.name属性来匹配该元素。例如,#Cancel匹配第二个按钮。
在一个面板(panel)中,元素名称应该是唯一的。该建议并不是强制要求的,但是不唯一的名字可能导致意想不到的匹配。
当给一个元素设置名字时,不要包含#。
Class
.class { ... }使用类选择器(Class selector)来匹配具有一个特定类的元素。
为了匹配一个元素,选择器不必指定该元素的所有类。指定该元素具有的一个类名就能匹配该元素。例如,.yellow匹配名字为container2 的元素和名字为OK的按钮元素。
如果你在选择器中指定多个类名,则只有一个元素具有所有这些类名时,才能被匹配到。当你给一个元素添加类名时,不要包含.。
类名不能以数字开关。
Wildcard
通匹配符。
* { ... }上面这行代码匹配任意元素。
Pseudo-states
伪状态。
:pseudo-state { ... }为实现当一个元素进入到某个特定状态时,才匹配到该元素,可以使用伪状态(pseudo-states)。例如,Button:hover只有当用户把鼠标的光标停留在一个Button上时,才会匹配一个类型的Button的可视元素。
支持的伪状态:

[*]hover : 光标停留在一个可视元素时。
[*]active : 可视元素正在参与交互。
[*]inactive :可视元素不在参与交互。
[*]focus : 可视元素具有焦点。
[*]selected : 未被使用。
[*]disabled : 可视元素设置了 enabled == false。
[*]enabled : 可视元素设置了 enabled == true。
[*]checked : 可视元素是一个 Toggle 并且被勾选了。
[*]root : 可视化树中最高层的元素。
伪状态在其它简单选择器之后被指定。伪状态不能被扩展,只支持预定义的这几个伪状态。
Complex selectors
复杂选择器是简单选择器使用分隔符组合的结果。复杂选择器也包括一个选择器列表,它提供了一个简短的方式,用于把相同的样式施加到许多元素上。
Delimiters
UIElements支持以下分隔符:

[*]empty或者space 分隔符匹配一个元素的所有后代元素
[*]大于号>匹配一个已经被之前的选择器匹配到的元素的直接后代(direct descendant)。
例如:

[*]#container1 .yellow : 匹配内部元素(inner element)(应该是指container2)和第一个Button。
[*]#container2 > .yellow : 只匹配内部元素(container2)
Selector List
使用一个选择器列表来把相同的样式定义施加到大量元素上。每一个选择器用逗号分隔,每一个选择器可以是一个简单选择器或一个复杂选择器。
例如:
#container1, Button { padding-top:10 }等价于
#container1 { padding-top: 10 } Button { padding-top: 10}Selector precedence
选择器优先级。
如果多个选择器匹配一个相同元素,则具有最高专一性(specificity)的元素优先。对于简单选择器,基本的专一性规则(专一性由高到低):
Name > Class > Type > 通配符*。
如果在同一个样式表中两个选择器是相等的(应该是指专一性相等吧),则在文件中最后出现的选择器优先。
为了决定不同文件中的选择器的专一性,Unity会把用户定义的样式表的优先级设置成高于Unity提供的默认样式表。
添加到具有最高深度和子索引的元素上的样式表获得优先仅。
Style sheets attached to elements with higher depth and sibling index take precedence.
注意!important关键字是被忽略的。
最后,在C#中设置的值总是具有最高的专一性,将覆盖来自USS中的任意样式。
USS Properties types

Built-in vs Custom properties(内置vs自定义属性)
当使用USS时,你可以在UI代码中指定内置的VisualElement 的属性值或自定义属性的值。
除了从USS文件中读取它们的值,内置属性值可以赋给VisualElement的C#属性。赋值给C#的值会覆盖来自USS的值。
你可以用自定义属性API扩展USS。自定义USS属性要求 -- 前缀。
Property values
接下来列举出支持的类型。
Length(长度)
UIElements支持以pixels(px)和百分比(%)作为长度的度量单位。Pixel值是绝对的,而百分比通常是相对于父元素的。
举例:

[*]width:200px;表示宽度为200像素
[*]width:50%;表示宽度是父元素宽度的一半
指定度量单位很重要。如果你不指定度量单位,UIElements会认为属性值是以像素为单位的。
注意: 0是一个特殊的值,不要求有度量单位
Numeric(数值)
数值要么用浮点数表示,要么用整数表示。例如:flex:1.0。
Keywords
某些内置的属性支持一些特定的关键字。关键字提供了一个描述性的名字而不是一个数字。例如:position:absolute。所有属性支持initial全局关键字,它会把一个属性设置为它的默认值。这里可以看到USS所支持的关键字列表:USS supported properties
Color
UIElements支持以下常量颜色值和函数:

[*]一个十六进制的值#FFFF00, #0F0(rgb)
[*]RGB函数:rgb(255, 255, 0)
[*]RGBA函数:rgba(255, 255, 0, 1.0)
[*]颜色关键字: blue,transparent
Assets
你可以用resource()或url()函数来引用资源。例如,用代码background-image: resource("Images/img.png") 来指定以Images目录中的img.png图片为背景图片。在导入期间决定被引用的资源的是什么。
resource()函数接收位于Resources文件夹下或者 Editor Default Resources文件夹下的文件。注意以下几点:

[*]如果文件位于Resources文件夹下,不要包含文件扩展名。例如:background-image: resource("Images/my-image")
[*]如果文件位于 Editor Default Resources文件夹下,必须要包含文件扩展名。例如:background-image: resource("Images/default-image.png")。
另外,当加载纹理时,resource()提供了一个方便的方法来处理高DPI/视网膜屏(High DPI/Retina screens)。举例,如果有一个纹理名字为myimage.png,在相同的位置(比如Resources下),有另一个名字相同但是有一个后缀@2x的纹理myimage@2x.png,则在USS中使用resource("myimage")时,Unity会自动根据screen DPI,来加载Resources/myimage.png 或者Resources/myimage@2x.png。(和Android的机制挺像)
url()函数会认文件路径(即参数)是相对于项目的根目录或者包含当前USS文件的目录的。你必须包含文件扩展名。在下面的例子中,USS文件位于Assets\Editor\USS路径下,背景图片thumb.png位于Assets\Editor\Resources:

[*]相对于USS文件:url("../Resources/thumb.png");
[*]相对于项目根目录:url("/Assets/Editor/Resources/thumb.png");或者url("project:/Assets/Editor/Resources/thumb.png"); 或者url("project:///Assets/Editor/Resources/thumb.png");
url()使用举例:
background-image: url("Images/my-image.png").Strings(字符串)
使用双引号来指定一个字符串值。例如: --my-property: "foo"。
USS Writing style sheets

用USS写样式表。
为了组织事物,UIElements采用BEM的方式来修改元素的样式。使用BEM不是强制的,但是推荐的。
关于BEM
BEM表示Block Element Modifier。BEM是一个简单的系统,帮助你写出结构化的、没有歧义的、易于维护的选择器。使用BEM,你可以把类(classes)赋给元素,然后在样式表中用这些类作为选择器。
BEM类有最多三个组成部分:

[*]Block name: 一个block是一个有意义的、独立的实体或控件。例如,menu,button,list-view。
[*]Element name: 一个block的一部分,没有独立意义,在语意上和其所属的block绑定在一块。Element name附加到block name后面(用两个下划线连接)。例如, menu__item, button__icon, 和 list-view__item
[*]Modifier:修饰符,是一个标志,用于改变一个block和element的外观或行为。用两个短杠(dashes)来把一个modifier添加到block或者element名字后面。例如: menu--disabled, menu__item--disabled, button--small, 和 list-view__item--selected。
举例,下面是一个menu的XML代码(即UXML文件):
<VisualElement class="menu">
    <Label class="menu__item" text="Banana" />
    <Label class="menu__item" text="Apple" />
    <Label class="menu__item menu__item--disabled" text="Orange" />
</VisualElement>Selectors
选择器。
因为每个元素都有classes,来描述它的外观,你可以仅用一个类名来写大部分选择器。
.menu {
}

.menu__item {
}

.menu__item--disabled {
}你可以用单个类名来修改一个元素的风格。然而,在某些情形下,你可能需要使用复杂的选择器。例如,当一个元素的风格依赖于它的block的modifier时:
.button {
}

.button__icon {
}

.button--small {
}

.button--small .button__icon {
}Making VisualElement BEM-friendly
使用VisualElement对BEM友好。
UIElements支持BEM。每一个可视元素附加了一些必要的类名。例如,TextElement有 unity-text-element类。每一个Button(继承自TextElement),同时有 unity-button 和unity-text-element 类。
如果你继承VisualElement或者其子类而创建一个新的元素,需要遵守下面这些指导方针,以确保你的元素容易用BEM来修改风格。

[*]在构造函数中,用 AddToClassList() 向你的元素实例添加相关的USS类。
[*]如果你的新元素在构造函数中实例化了子元素,把相关的类赋给子元素。例如,my-block__first-child, my-block__other-child。
[*]如果你的元素支持多个状态或变体,例如,small和large,则当元素状态变化时或者当元素变体被选择时,添加或移除相关的类。
[*]如果你的元素会被别的项目使用,则应当考虑给类添加前缀,以避免和别的项目中以存在的类名产生冲突。
References:
页: [1]
查看完整版本: UIElements 开发指南 (三)